diff --git a/README.md b/README.md index 6482c5a..e40e019 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Unicity Java State Transition SDK -A Java SDK for interacting with the Unicity network, enabling state transitions and token operations with cross-platform support for JVM and Android 12+. +A Java SDK for interacting with the Unicity network, enabling state transitions and token operations +with cross-platform support for JVM and Android 12+. ## Features @@ -22,6 +23,7 @@ A Java SDK for interacting with the Unicity network, enabling state transitions ### Using JitPack Add JitPack repository: + ```groovy repositories { maven { url 'https://jitpack.io' } @@ -29,6 +31,7 @@ repositories { ``` #### For Android Projects: + ```groovy dependencies { implementation 'com.github.unicitynetwork:java-state-transition-sdk:1.1:android' @@ -36,6 +39,7 @@ dependencies { ``` #### For JVM Projects: + ```groovy dependencies { implementation 'com.github.unicitynetwork:java-state-transition-sdk:1.1:jvm' @@ -52,179 +56,245 @@ dependencies { ## Quick Start -### Initialize the Client +### Util methods ```java -import org.unicitylabs.sdk.StateTransitionClient; -import org.unicitylabs.sdk.api.AggregatorClient; +private static final SecureRandom RANDOM = new SecureRandom(); + +/** + * Generate random bytes of specified length. + */ +public static byte[] randomBytes(int length) { + byte[] bytes = new byte[length]; + RANDOM.nextBytes(bytes); + return bytes; +} +``` + +### Initialize the Client +```java // Connect to the Unicity test network String aggregatorUrl = "https://gateway-test.unicity.network"; -AggregatorClient aggregatorClient = new AggregatorClient(aggregatorUrl); +DefaultAggregatorClient aggregatorClient = new DefaultAggregatorClient(aggregatorUrl); StateTransitionClient client = new StateTransitionClient(aggregatorClient); + +// Create root trust base from classpath +RootTrustBase trustbase = RootTrustBase.fromJson( + new String(getClass().getResourceAsStream("/trust-base.json").readAllBytes()) +); ``` ### Mint a Token ```java -import org.unicitylabs.sdk.token.*; -import org.unicitylabs.sdk.token.fungible.*; -import org.unicitylabs.sdk.transaction.*; -import org.unicitylabs.sdk.predicate.*; -import org.unicitylabs.sdk.signing.SigningService; -import org.unicitylabs.sdk.hash.HashAlgorithm; - -// Create signing service from secret -byte[] secret = "your-secret-key".getBytes(); -byte[] nonce = new byte[32]; // Generate random nonce -SigningService signingService = SigningService.createFromSecret(secret, nonce).get(); - -// Create token parameters -TokenId tokenId = TokenId.create(randomBytes(32)); -TokenType tokenType = TokenType.create(randomBytes(32)); - -// Create predicate for ownership +byte[] secret = "minter_secret".getBytes(StandardCharsets.UTF_8); +// Generate data for token +TokenId tokenId = new TokenId(randomBytes(32)); +TokenType tokenType = new TokenType(randomBytes(32)); +byte[] tokenData = "token immutable data".getBytes(StandardCharsets.UTF_8); +TokenCoinData coinData = new TokenCoinData( + Map.of( + new CoinId("coin".getBytes()), BigInteger.valueOf(100), + new CoinId("second coin".getBytes()), BigInteger.valueOf(5) + ) +); + +// Create predicate for initial state and use its reference as address +byte[] nonce = randomBytes(32); +// Create key pair from nonce and secret +SigningService signingService = SigningService.createFromMaskedSecret(secret, nonce); MaskedPredicate predicate = MaskedPredicate.create( tokenId, tokenType, signingService, HashAlgorithm.SHA256, nonce -).get(); - -// Create token data -TestTokenData tokenData = new TestTokenData(randomBytes(32)); - -// Create coins -Map coins = new HashMap<>(); -coins.put(new CoinId(randomBytes(32)), BigInteger.valueOf(100)); -coins.put(new CoinId(randomBytes(32)), BigInteger.valueOf(50)); -TokenCoinData coinData = new TokenCoinData(coins); +); -// Create mint transaction -MintTransactionData mintData = new MintTransactionData<>( - tokenId, - tokenType, - predicate, - tokenData, - coinData, - null, // dataHash (optional) - randomBytes(32) // salt +byte[] salt = randomBytes(32); +MintCommitment commitment = MintCommitment.create( + new MintTransaction.Data<>( + tokenId, + tokenType, + tokenData, + coinData, + predicate.getReference().toAddress(), + salt, + null, + null + ) ); -// Submit transaction -Commitment> commitment = - client.submitMintTransaction(mintData).get(); +// Submit mint transaction using StateTransitionClient +SubmitCommitmentResponse response = client + .submitCommitment(commitment) + .get(); + +if (response.getStatus() != SubmitCommitmentStatus.SUCCESS) { + throw new Exception( + String.format( + "Failed to submit mint commitment: %s", + response.getStatus() + ) + ); +} // Wait for inclusion proof InclusionProof inclusionProof = InclusionProofUtils.waitInclusionProof( - client, - commitment, - Duration.ofSeconds(30), - Duration.ofSeconds(1) + client, + trustBase, + commitment ).get(); -// Create transaction with proof -Transaction> mintTransaction = - client.createTransaction(commitment, inclusionProof).get(); - -// Create token -TokenState tokenState = TokenState.create(predicate, tokenData.getData()); -Token>> token = - new Token<>(tokenState, (Transaction) mintTransaction); - -System.out.println("Token minted with ID: " + token.getId().toJSON()); +// Create mint transaction +Token token = Token.create( + trustBase, + // Create initial state with transaction data + new TokenState(predicate, null), + commitment.toTransaction(inclusionProof) +); ``` ### Get Block Height ```java Long blockHeight = client.getAggregatorClient().getBlockHeight().get(); -System.out.println("Current block height: " + blockHeight); +System.out.println("Current block height: "+blockHeight); ``` ### Transfer a Token ```java -// Create transfer transaction data -TransactionData transferData = TransactionData.create( - token.getState(), - recipientAddress.toString(), - randomBytes(32), // salt - dataHash, // optional data hash - messageBytes // optional message -).get(); +byte[] senderSecret = secret; +byte[] senderNonce = nonce; -// Submit transfer -Commitment transferCommitment = - client.submitTransaction(transferData, signingService).get(); +String recipientNametag = "RECIPIENT"; +byte[] recipientData = "Custom data".getBytes(StandardCharsets.UTF_8); +DataHash recipientDataHash = new DataHasher(HashAlgorithm.SHA256) + .update(recipientData) + .digest(); -// Wait for inclusion proof and create transaction -InclusionProof transferProof = InclusionProofUtils.waitInclusionProof( - client, transferCommitment -).get(); +byte[] salt = randomBytes(32); -Transaction transferTransaction = - client.createTransaction(transferCommitment, transferProof).get(); -``` - -### Offline Token Transfer +// Submit transfer transaction +TransferCommitment transferCommitment = TransferCommitment.create( + token, + ProxyAddress.create(recipientNametag), + salt, + recipientDataHash, + null, + SigningService.createFromMaskedSecret(senderSecret, senderNonce) +); -The SDK supports offline token transfers, where the transaction is prepared by the sender and can be transferred offline (e.g., via NFC, QR code, or file) to the recipient. +SubmitCommitmentResponse transferResponse = this.client.submitCommitment(transferCommitment).get(); +if (transferResponse.getStatus() != SubmitCommitmentStatus.SUCCESS) { + throw new Exception( + String.format( + "Failed to submit transfer commitment: %s", + transferResponse.getStatus() + ) + ); +} -```java -// Step 1: Sender creates offline commitment -TransactionData transactionData = TransactionData.create( - token.getState(), - recipientAddress.toString(), - randomBytes(32), // salt - dataHash, // optional data hash - messageBytes // optional message -).get(); +// Create transfer transaction +TransferTransaction transferTransaction = transferCommitment.toTransaction( + InclusionProofUtils.waitInclusionProof( + client, + trustBase, + transferCommitment + ).get() +); -// Create authenticator (signed by sender) -Authenticator authenticator = Authenticator.create( - senderSigningService, - transactionData.getHash(), - transactionData.getSourceState().getHash() -).get(); +// Prepare info for sending to recipient +String transferTransactionJson = transferTransaction.toJson(); +String tokenJson = token.toJson(); +``` -// Create request ID -RequestId requestId = RequestId.create( - senderSigningService.getPublicKey(), - transactionData.getSourceState().getHash() -).get(); +### Receive the token -// Create commitment -Commitment commitment = new Commitment<>( - requestId, - transactionData, - authenticator +```java +String recipientNametag = "RECIPIENT"; +byte[] receiverSecret = "RECEIVER_SECRET".getBytes(); + +Token token = Token.fromJson("TOKEN JSON"); +TransferTransaction transaction = TransferTransaction.fromJson("TRANSFER TRANSACTION JSON"); + +// Create nametag token +TokenType nametagType = new TokenType(randomBytes(32)); +byte[] nametagNonce = randomBytes(32); +byte[] nametagSalt = randomBytes(32); + +MintCommitment nametagMintCommitment = MintCommitment.create( + new MintTransaction.NametagData( + recipientNametag, + nametagType, + MaskedPredicateReference.create( + nametagType, + SigningService.createFromMaskedSecret(receiverSecret, nametagNonce), + HashAlgorithm.SHA256, + nametagNonce + ).toAddress(), + nametagSalt, + UnmaskedPredicateReference.create( + token.getType(), + SigningService.createFromSecret(receiverSecret), + HashAlgorithm.SHA256 + ).toAddress() + ) ); -// Step 2: Transfer commitment data offline (NFC, QR code, file, etc.) -// In a real scenario, serialize commitment and token data for transfer - -// Step 3: Recipient submits the commitment online -SubmitCommitmentResponse response = client.submitCommitment(commitment).get(); +// Submit nametag mint transaction using StateTransitionClient +SubmitCommitmentResponse nametagMintResponse = client.submitCommitment(nametagMintCommitment).get(); +if (nametagMintResponse.getStatus() != SubmitCommitmentStatus.SUCCESS) { + throw new Exception( + String.format( + "Failed to submit nametag mint commitment: %s", + nametagMintResponse.getStatus() + ) + ); +} // Wait for inclusion proof InclusionProof inclusionProof = InclusionProofUtils.waitInclusionProof( - client, - commitment + client, + trustBase, + nametagMintCommitment ).get(); -// Create transaction with proof -Transaction confirmedTransaction = - client.createTransaction(commitment, inclusionProof).get(); +Token nametagToken = Token.create( + trustBase, + new TokenState( + MaskedPredicate.create( + nametagMintCommitment.getTransactionData().getTokenId(), + nametagMintCommitment.getTransactionData().getTokenType(), + SigningService.createFromMaskedSecret(receiverSecret, nametagNonce), + HashAlgorithm.SHA256, + nametagNonce + ), + null + ), + nametagMintCommitment.toTransaction(inclusionProof) +); -// Finish transaction with recipient's state -TokenState recipientTokenState = TokenState.create(recipientPredicate, recipientData); -Token updatedToken = client.finishTransaction( +// Receiver finalizes the token +Token finalizedToken = client.finalizeTransaction( + trustBase, token, - recipientTokenState, - confirmedTransaction -).get(); + new TokenState( + UnmaskedPredicate.create( + token.getId(), + token.getType(), + SigningService.createFromSecret(receiverSecret), + HashAlgorithm.SHA256, + transaction.getData().getSalt() + ), + null + ), + transaction, + List.of(nametagToken) +); + ``` ## Building from Source @@ -260,6 +330,7 @@ AGGREGATOR_URL=https://gateway-test.unicity.network ./gradlew integrationTest ### Android Compatibility The SDK is compatible with Android 12+ (API level 31+). It uses: + - OkHttp for HTTP operations instead of Java 11's HttpClient - Android-compatible Guava version - Animal Sniffer plugin to ensure API compatibility @@ -267,6 +338,7 @@ The SDK is compatible with Android 12+ (API level 31+). It uses: ### JVM Compatibility The standard JVM version uses: + - Java 11 APIs - Full Guava JRE version - All Java 11 features @@ -275,39 +347,26 @@ The standard JVM version uses: The SDK follows a modular architecture under `org.unicitylabs.sdk`: -- **`api`**: Core API interfaces and aggregator client -- **`address`**: Address schemes and implementations (DirectAddress, ProxyAddress) -- **`hash`**: Cryptographic hashing (SHA256, SHA224, SHA384, SHA512, RIPEMD160) -- **`jsonrpc`**: JSON-RPC transport layer with OkHttp +- **api**: Core API interfaces and aggregator client +- **address**: Address schemes and implementations (DirectAddress, ProxyAddress) +- **bft**: BFT layer data, trustbase, certificates, seals +- **hash**: Cryptographic hashing (SHA256, SHA224, SHA384, SHA512, RIPEMD160) +- **jsonrpc**: JSON-RPC transport layer - **`mtree`**: Merkle tree implementations - - `plain`: Sparse Merkle Tree (SMT) - - `sum`: Sparse Merkle Sum Tree (SMST) -- **`predicate`**: Ownership predicates (Masked, Unmasked, Burn, Default) -- **`serializer`**: CBOR and JSON serializers hierarchy - - `cbor/`: CBOR serializers for all domain objects - - `json/`: JSON serializers for all domain objects -- **`signing`**: Digital signature support (ECDSA secp256k1) -- **`token`**: Token types including fungible tokens and nametags - - `fungible`: Fungible token support with CoinId and TokenCoinData -- **`transaction`**: Transaction types and builders - - `split`: Token splitting functionality with TokenSplitBuilder -- **`util`**: Utilities including BitString and HexConverter - -## Error Handling - -All async operations return `CompletableFuture` which can be handled with standard Java patterns: - -```java -client.submitMintTransaction(mintData) - .thenApply(commitment -> { - System.out.println("Transaction submitted: " + commitment.getRequestId()); - return commitment; - }) - .exceptionally(error -> { - System.err.println("Transaction failed: " + error.getMessage()); - return null; - }); -``` + - `plain`: Sparse Merkle Tree (SMT) + - `sum`: Sparse Merkle Sum Tree (SMST) +- **predicate**: Ownership predicates +- **serializer**: CBOR/JSON serializer utilities +- **signing**: Digital signature support (ECDSA secp256k1) +- **token**: Token types + - `fungible`: Fungible token support with CoinId and TokenCoinData + **`transaction`**: Transaction types and builders + - `split`: Token splitting functionality with TokenSplitBuilder +- **util**: Utilities +- **verification**: Verification rules + +All certificate and transaction verification is handled internally by the SDK, requiring only the +trustbase as input from the user. ## Contributing @@ -330,10 +389,13 @@ client.submitMintTransaction(mintData) The SDK includes comprehensive test suites: ### Unit Tests + Located in `src/test/java`, these test individual components in isolation. ### Integration Tests + Located in `src/test/java/org/unicitylabs/sdk/`: + - `integration/TokenIntegrationTest`: Tests against Docker-based local aggregator - `e2e/TokenE2ETest`: E2E tests using CommonTestFlow (requires `AGGREGATOR_URL` env var) - `e2e/BasicE2ETest`: Basic connectivity and performance tests @@ -357,7 +419,8 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file ## Support -For issues and feature requests, please use the [GitHub issue tracker](https://github.com/unicitynetwork/java-state-transition-sdk/issues). +For issues and feature requests, please use +the [GitHub issue tracker](https://github.com/unicitynetwork/java-state-transition-sdk/issues). For questions about the Unicity Labs, visit [unicity-labs.com](https://unicity-labs.com). diff --git a/build.gradle.kts b/build.gradle.kts index 01d46c3..0a1987b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,9 +27,9 @@ configurations { dependencies { // Core dependencies that work on both platforms - implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.19.2") - implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.19.2") - implementation("com.fasterxml.jackson.core:jackson-databind:2.19.2") + implementation(platform("com.fasterxml.jackson:jackson-bom:2.20.0")) + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8") + implementation("com.fasterxml.jackson.core:jackson-databind") implementation("org.bouncycastle:bcprov-jdk18on:1.81") implementation("org.slf4j:slf4j-api:2.0.13") implementation("com.squareup.okhttp3:okhttp:4.12.0") diff --git a/src/main/java/org/unicitylabs/sdk/Hashable.java b/src/main/java/org/unicitylabs/sdk/Hashable.java deleted file mode 100644 index 5dd4036..0000000 --- a/src/main/java/org/unicitylabs/sdk/Hashable.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.unicitylabs.sdk; - -import org.unicitylabs.sdk.hash.DataHash; - -public interface Hashable { - DataHash getHash(); -} diff --git a/src/main/java/org/unicitylabs/sdk/StateTransitionClient.java b/src/main/java/org/unicitylabs/sdk/StateTransitionClient.java index 9599d4e..40287e3 100644 --- a/src/main/java/org/unicitylabs/sdk/StateTransitionClient.java +++ b/src/main/java/org/unicitylabs/sdk/StateTransitionClient.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; -import org.unicitylabs.sdk.api.IAggregatorClient; +import org.unicitylabs.sdk.api.AggregatorClient; import org.unicitylabs.sdk.api.InclusionProofResponse; import org.unicitylabs.sdk.api.RequestId; import org.unicitylabs.sdk.api.SubmitCommitmentResponse; @@ -12,26 +12,43 @@ import org.unicitylabs.sdk.predicate.PredicateEngineService; import org.unicitylabs.sdk.token.Token; import org.unicitylabs.sdk.token.TokenState; -import org.unicitylabs.sdk.verification.VerificationException; import org.unicitylabs.sdk.transaction.Commitment; import org.unicitylabs.sdk.transaction.InclusionProofVerificationStatus; import org.unicitylabs.sdk.transaction.MintCommitment; -import org.unicitylabs.sdk.transaction.MintTransactionData; -import org.unicitylabs.sdk.transaction.Transaction; +import org.unicitylabs.sdk.transaction.MintTransactionReason; import org.unicitylabs.sdk.transaction.TransferCommitment; -import org.unicitylabs.sdk.transaction.TransferTransactionData; +import org.unicitylabs.sdk.transaction.TransferTransaction; +import org.unicitylabs.sdk.verification.VerificationException; +/** + * Client for handling state transitions of tokens, including submitting commitments and finalizing + * transactions. + */ public class StateTransitionClient { - protected final IAggregatorClient client; + /** + * The aggregator client used for submitting commitments and retrieving inclusion proofs. + */ + protected final AggregatorClient client; - public StateTransitionClient(IAggregatorClient client) { + /** + * Creates a new StateTransitionClient with the specified aggregator client. + * + * @param client The aggregator client to use for communication. + */ + public StateTransitionClient(AggregatorClient client) { this.client = client; } - public > CompletableFuture submitCommitment( - MintCommitment commitment - ) { + /** + * Submits a mint commitment to the aggregator. + * + * @param commitment The mint commitment to submit. + * @param The type of mint transaction data. + * @return A CompletableFuture that resolves to the response from the aggregator. + */ + public + CompletableFuture submitCommitment(MintCommitment commitment) { return this.client.submitCommitment( commitment.getRequestId(), commitment.getTransactionData().calculateHash(), @@ -39,6 +56,13 @@ public > CompletableFuture submitCommitment( TransferCommitment commitment ) { @@ -55,20 +79,45 @@ public CompletableFuture submitCommitment( .calculateHash(), commitment.getAuthenticator()); } - public > Token finalizeTransaction( + /** + * Finalizes a transaction by updating the token state based on the provided transaction data + * without nametags. + * + * @param trustBase The root trust base for inclusion proof verification. + * @param token The token to be updated. + * @param state The current state of the token. + * @param transaction The transaction containing transfer data. + * @param The type of mint transaction data. + * @return The updated token after applying the transaction. + * @throws VerificationException if verification fails during the update process. + */ + public Token finalizeTransaction( RootTrustBase trustBase, - Token token, + Token token, TokenState state, - Transaction transaction + TransferTransaction transaction ) throws VerificationException { return this.finalizeTransaction(trustBase, token, state, transaction, List.of()); } - public > Token finalizeTransaction( + /** + * Finalizes a transaction by updating the token state based on the provided transaction data and + * nametags. + * + * @param trustBase The root trust base for inclusion proof verification. + * @param token The token to be updated. + * @param state The current state of the token. + * @param transaction The transaction containing transfer data. + * @param nametags A list of tokens used as nametags in the transaction. + * @param The type of mint transaction data of token. + * @return The updated token after applying the transaction. + * @throws VerificationException if verification fails during the update process. + */ + public Token finalizeTransaction( RootTrustBase trustBase, - Token token, + Token token, TokenState state, - Transaction transaction, + TransferTransaction transaction, List> nametags ) throws VerificationException { Objects.requireNonNull(token, "Token is null"); @@ -76,8 +125,17 @@ public > Token finalizeTransaction( return token.update(trustBase, state, transaction, nametags); } + /** + * Retrieves the inclusion proof for a token and verifies its status against the provided public + * key and trust base. + * + * @param token The token for which to retrieve the inclusion proof. + * @param publicKey The public key associated with the token. + * @param trustBase The root trust base for verification. + * @return A CompletableFuture that resolves to the inclusion proof verification status. + */ public CompletableFuture getTokenStatus( - Token> token, + Token token, byte[] publicKey, RootTrustBase trustBase ) { @@ -86,6 +144,12 @@ public CompletableFuture getTokenStatus( .thenApply(response -> response.getInclusionProof().verify(requestId, trustBase)); } + /** + * Retrieves the inclusion proof for a given commitment. + * + * @param commitment The commitment for which to retrieve the inclusion proof. + * @return A CompletableFuture that resolves to the inclusion proof response from the aggregator. + */ public CompletableFuture getInclusionProof(Commitment commitment) { return this.client.getInclusionProof(commitment.getRequestId()); } diff --git a/src/main/java/org/unicitylabs/sdk/address/Address.java b/src/main/java/org/unicitylabs/sdk/address/Address.java index af963d7..78a8a5f 100644 --- a/src/main/java/org/unicitylabs/sdk/address/Address.java +++ b/src/main/java/org/unicitylabs/sdk/address/Address.java @@ -1,7 +1,27 @@ package org.unicitylabs.sdk.address; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +/** + * Address interface. + */ +@JsonSerialize(using = AddressJson.Serializer.class) +@JsonDeserialize(using = AddressJson.Deserializer.class) public interface Address { - AddressScheme getScheme(); - String getAddress(); + + /** + * Get the address scheme. + * + * @return address scheme + */ + AddressScheme getScheme(); + + /** + * Get the address as a string. + * + * @return address string + */ + String getAddress(); } diff --git a/src/main/java/org/unicitylabs/sdk/address/AddressFactory.java b/src/main/java/org/unicitylabs/sdk/address/AddressFactory.java index f577645..10fd298 100644 --- a/src/main/java/org/unicitylabs/sdk/address/AddressFactory.java +++ b/src/main/java/org/unicitylabs/sdk/address/AddressFactory.java @@ -1,13 +1,25 @@ package org.unicitylabs.sdk.address; +import java.util.Arrays; +import java.util.Objects; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.util.HexConverter; -import java.util.Arrays; -import java.util.Objects; +/** + * Factory for creating Address instances from string representations. + */ public class AddressFactory { + /** + * Create an Address from its string representation. + * + * @param address The address string. + * @return The corresponding Address instance. + * @throws IllegalArgumentException if the address format is invalid or does not match the + * expected format. + * @throws NullPointerException if the address is null. + */ public static Address createAddress(String address) { Objects.requireNonNull(address, "Address cannot be null"); diff --git a/src/main/java/org/unicitylabs/sdk/address/AddressJson.java b/src/main/java/org/unicitylabs/sdk/address/AddressJson.java new file mode 100644 index 0000000..ed91a20 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/address/AddressJson.java @@ -0,0 +1,90 @@ +package org.unicitylabs.sdk.address; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import org.unicitylabs.sdk.predicate.EncodedPredicate; + +/** + * Address serializer and deserializer implementation. + */ +public class AddressJson { + + private AddressJson() { + } + + /** + * Address serializer. + */ + public static class Serializer extends StdSerializer
{ + + /** + * Create serializer. + */ + public Serializer() { + super(Address.class); + } + + /** + * Serialize address. + * + * @param value addess + * @param gen json generator + * @param serializers serializer provider + * @throws IOException on serialization failure + */ + @Override + public void serialize(Address value, JsonGenerator gen, + SerializerProvider serializers) + throws IOException { + gen.writeObject(value.toString()); + } + } + + /** + * Address deserializer. + */ + public static class Deserializer extends StdDeserializer
{ + + /** + * Create deserializer. + */ + public Deserializer() { + super(Address.class); + } + + /** + * Deserialize address. + * + * @param p Parser used for reading JSON content + * @param ctx Context that can be used to access information about this deserialization + * activity. + * @return address + * @throws IOException on deserialization failure + */ + @Override + public Address deserialize(JsonParser p, DeserializationContext ctx) + throws IOException { + if (p.getCurrentToken() != JsonToken.VALUE_STRING) { + throw MismatchedInputException.from( + p, + EncodedPredicate.class, + "Expected string value" + ); + } + + try { + return AddressFactory.createAddress(p.readValueAs(String.class)); + } catch (Exception e) { + throw MismatchedInputException.from(p, EncodedPredicate.class, "Expected bytes"); + } + } + } +} + diff --git a/src/main/java/org/unicitylabs/sdk/address/AddressScheme.java b/src/main/java/org/unicitylabs/sdk/address/AddressScheme.java index 55b0a48..ee7ee97 100644 --- a/src/main/java/org/unicitylabs/sdk/address/AddressScheme.java +++ b/src/main/java/org/unicitylabs/sdk/address/AddressScheme.java @@ -1,6 +1,9 @@ package org.unicitylabs.sdk.address; +/** + * Address scheme. + */ public enum AddressScheme { DIRECT, PROXY diff --git a/src/main/java/org/unicitylabs/sdk/address/DirectAddress.java b/src/main/java/org/unicitylabs/sdk/address/DirectAddress.java index 4d47eed..6b29dfd 100644 --- a/src/main/java/org/unicitylabs/sdk/address/DirectAddress.java +++ b/src/main/java/org/unicitylabs/sdk/address/DirectAddress.java @@ -1,15 +1,14 @@ package org.unicitylabs.sdk.address; +import java.util.Arrays; +import java.util.Objects; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.util.HexConverter; -import java.util.Arrays; -import java.util.Objects; - /** - * Direct address implementation + * Direct address implementation. */ public class DirectAddress implements Address { @@ -21,6 +20,12 @@ private DirectAddress(DataHash data, byte[] checksum) { this.checksum = Arrays.copyOf(checksum, checksum.length); } + /** + * Create a direct address from a predicate reference. + * + * @param reference the data hash to create the address from + * @return the direct address + */ public static DirectAddress create(DataHash reference) { DataHash checksum = new DataHasher(HashAlgorithm.SHA256).update(reference.getImprint()) .digest(); diff --git a/src/main/java/org/unicitylabs/sdk/address/ProxyAddress.java b/src/main/java/org/unicitylabs/sdk/address/ProxyAddress.java index c95de61..8132f32 100644 --- a/src/main/java/org/unicitylabs/sdk/address/ProxyAddress.java +++ b/src/main/java/org/unicitylabs/sdk/address/ProxyAddress.java @@ -1,20 +1,20 @@ package org.unicitylabs.sdk.address; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.hash.DataHasher; -import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.token.TokenId; -import org.unicitylabs.sdk.util.HexConverter; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.hash.DataHasher; +import org.unicitylabs.sdk.hash.HashAlgorithm; +import org.unicitylabs.sdk.token.Token; +import org.unicitylabs.sdk.token.TokenId; +import org.unicitylabs.sdk.util.HexConverter; /** - * Proxy address implementation + * Proxy address implementation. */ public class ProxyAddress implements Address { @@ -26,10 +26,22 @@ private ProxyAddress(TokenId data, byte[] checksum) { this.checksum = Arrays.copyOf(checksum, checksum.length); } + /** + * Create a proxy address from a nametag string. + * + * @param name the nametag + * @return the proxy address + */ public static ProxyAddress create(String name) { return ProxyAddress.create(TokenId.fromNameTag(name)); } + /** + * Create a proxy address from a token ID. + * + * @param tokenId the token ID + * @return the proxy address + */ public static ProxyAddress create(TokenId tokenId) { DataHash checksum = new DataHasher(HashAlgorithm.SHA256).update(tokenId.getBytes()) .digest(); @@ -46,6 +58,15 @@ public String getAddress() { return this.toString(); } + /** + * Resolve a proxy address to a direct address using a list of nametag tokens. + * + * @param inputAddress the input address to resolve + * @param nametags the list of nametag tokens + * @return the resolved direct address, or null if resolution fails + * @throws IllegalArgumentException if the nametags list contains null elements or duplicate + * addresses + */ public static Address resolve(Address inputAddress, List> nametags) { Map> nametagMap = new HashMap<>(); for (Token token : nametags) { diff --git a/src/main/java/org/unicitylabs/sdk/api/AggregatorClient.java b/src/main/java/org/unicitylabs/sdk/api/AggregatorClient.java index 0a9e96f..8808cdb 100644 --- a/src/main/java/org/unicitylabs/sdk/api/AggregatorClient.java +++ b/src/main/java/org/unicitylabs/sdk/api/AggregatorClient.java @@ -1,52 +1,39 @@ + package org.unicitylabs.sdk.api; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; import java.util.concurrent.CompletableFuture; import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.jsonrpc.JsonRpcHttpTransport; - -import static com.google.common.net.HttpHeaders.AUTHORIZATION; - -public class AggregatorClient implements IAggregatorClient { - - private final JsonRpcHttpTransport transport; - private final String apiKey; - - public AggregatorClient(String url) { - this(url, null); - } - public AggregatorClient(String url, String apiKey) { - this.transport = new JsonRpcHttpTransport(url); - this.apiKey = apiKey; - } - - public CompletableFuture submitCommitment( +/** + * Aggregator client structure. + */ +public interface AggregatorClient { + + /** + * Submit commitment. + * + * @param requestId request id + * @param transactionHash transaction hash + * @param authenticator authenticator + * @return submit commitment response + */ + CompletableFuture submitCommitment( RequestId requestId, DataHash transactionHash, - Authenticator authenticator) { - - SubmitCommitmentRequest request = new SubmitCommitmentRequest(requestId, transactionHash, - authenticator, false); - Map> headers = new LinkedHashMap<>(); - if (apiKey != null) { - headers.put(AUTHORIZATION, Collections.singletonList("Bearer " + apiKey)); - } - return this.transport.request("submit_commitment", request, SubmitCommitmentResponse.class, headers); - } - - public CompletableFuture getInclusionProof(RequestId requestId) { - InclusionProofRequest request = new InclusionProofRequest(requestId); - - return this.transport.request("get_inclusion_proof", request, InclusionProofResponse.class); - } - - public CompletableFuture getBlockHeight() { - return this.transport.request("get_block_height", Collections.emptyMap(), - BlockHeightResponse.class) - .thenApply(BlockHeightResponse::getBlockNumber); - } -} \ No newline at end of file + Authenticator authenticator); + + /** + * Get inclusion proof for request id. + * + * @param requestId request id + * @return inclusion / non inclusion proof + */ + CompletableFuture getInclusionProof(RequestId requestId); + + /** + * Get block height. + * + * @return block height + */ + CompletableFuture getBlockHeight(); +} diff --git a/src/main/java/org/unicitylabs/sdk/api/Authenticator.java b/src/main/java/org/unicitylabs/sdk/api/Authenticator.java index 953c884..1a5c9a2 100644 --- a/src/main/java/org/unicitylabs/sdk/api/Authenticator.java +++ b/src/main/java/org/unicitylabs/sdk/api/Authenticator.java @@ -1,14 +1,22 @@ package org.unicitylabs.sdk.api; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; import org.unicitylabs.sdk.signing.Signature; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.util.HexConverter; -import java.util.Arrays; -import java.util.Objects; /** - * Authenticator for transaction submission + * Authenticator for transaction submission. */ public class Authenticator { @@ -17,52 +25,150 @@ public class Authenticator { private final DataHash stateHash; private final byte[] publicKey; - public Authenticator(String algorithm, byte[] publicKey, Signature signature, - DataHash stateHash) { + @JsonCreator + private Authenticator( + @JsonProperty("algorithm") String algorithm, + @JsonProperty("publicKey") byte[] publicKey, + @JsonProperty("signature") Signature signature, + @JsonProperty("stateHash") DataHash stateHash + ) { this.algorithm = algorithm; - this.publicKey = publicKey; + this.publicKey = Arrays.copyOf(publicKey, publicKey.length); this.signature = signature; this.stateHash = stateHash; } + /** + * Create authenticator from signing service. + * + * @param signingService signing service + * @param transactionHash transaction hash + * @param stateHash state hash + * @return authenticator + */ public static Authenticator create( SigningService signingService, DataHash transactionHash, - DataHash stateHash) { - + DataHash stateHash + ) { return new Authenticator(signingService.getAlgorithm(), signingService.getPublicKey(), signingService.sign(transactionHash), stateHash); } + /** + * Get signature. + * + * @return signature + */ public Signature getSignature() { return this.signature; } + /** + * Get algorithm. + * + * @return algorithm + */ public String getAlgorithm() { return this.algorithm; } + /** + * Get state hash. + * + * @return state hash + */ public DataHash getStateHash() { return this.stateHash; } + /** + * Get public key. + * + * @return public key + */ public byte[] getPublicKey() { - return this.publicKey; + return Arrays.copyOf(this.publicKey, this.publicKey.length); } + /** + * Verify if signature and data are correct. + * + * @param hash data hash + * @return true if successful + */ public boolean verify(DataHash hash) { return SigningService.verifyWithPublicKey(hash, this.signature.getBytes(), this.publicKey); } + /** + * Create authenticator from CBOR bytes. + * + * @param bytes CBOR bytes + * @return authenticator + */ + public static Authenticator fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new Authenticator( + CborDeserializer.readTextString(data.get(0)), + CborDeserializer.readByteString(data.get(1)), + Signature.decode(CborDeserializer.readByteString(data.get(2))), + DataHash.fromCbor(data.get(3)) + ); + } + + /** + * Convert authenticator to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + CborSerializer.encodeTextString(this.algorithm), + CborSerializer.encodeByteString(this.publicKey), + CborSerializer.encodeByteString(this.signature.encode()), + this.stateHash.toCbor() + ); + } + + /** + * Create authenticator from JSON string. + * + * @param input JSON string + * @return authenticator + */ + public static Authenticator fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, Authenticator.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(Authenticator.class, e); + } + } + + /** + * Convert authenticator to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(Authenticator.class, e); + } + } + @Override public boolean equals(Object o) { if (!(o instanceof Authenticator)) { return false; } Authenticator that = (Authenticator) o; - return Objects.equals(this.algorithm, that.algorithm) && Objects.equals(this.signature, - that.signature) && Objects.equals(this.stateHash, that.stateHash) && Objects.deepEquals( - this.publicKey, that.publicKey); + return Objects.equals(this.algorithm, that.algorithm) + && Objects.equals(this.signature, that.signature) + && Objects.equals(this.stateHash, that.stateHash) + && Objects.deepEquals(this.publicKey, that.publicKey); } @Override diff --git a/src/main/java/org/unicitylabs/sdk/api/BlockHeightResponse.java b/src/main/java/org/unicitylabs/sdk/api/BlockHeightResponse.java index dcf5d17..d815d69 100644 --- a/src/main/java/org/unicitylabs/sdk/api/BlockHeightResponse.java +++ b/src/main/java/org/unicitylabs/sdk/api/BlockHeightResponse.java @@ -1,19 +1,62 @@ package org.unicitylabs.sdk.api; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; import java.util.Objects; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; +/** + * Block height response. + */ public class BlockHeightResponse { private final long blockNumber; - public BlockHeightResponse(long blockNumber) { + @JsonCreator + private BlockHeightResponse( + @JsonProperty("blockNumber") long blockNumber + ) { this.blockNumber = blockNumber; } + /** + * Get block height. + * + * @return block height + */ public long getBlockNumber() { return this.blockNumber; } + /** + * Create response from JSON string. + * + * @param input JSON string + * @return block height response + */ + public static BlockHeightResponse fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, BlockHeightResponse.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(BlockHeightResponse.class, e); + } + } + + /** + * Convert response to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(BlockHeightResponse.class, e); + } + } + @Override public boolean equals(Object o) { if (!(o instanceof BlockHeightResponse)) { diff --git a/src/main/java/org/unicitylabs/sdk/api/IAggregatorClient.java b/src/main/java/org/unicitylabs/sdk/api/IAggregatorClient.java deleted file mode 100644 index db53333..0000000 --- a/src/main/java/org/unicitylabs/sdk/api/IAggregatorClient.java +++ /dev/null @@ -1,17 +0,0 @@ - -package org.unicitylabs.sdk.api; - -import java.util.concurrent.CompletableFuture; -import org.unicitylabs.sdk.hash.DataHash; - -public interface IAggregatorClient { - - CompletableFuture submitCommitment( - RequestId requestId, - DataHash transactionHash, - Authenticator authenticator); - - CompletableFuture getInclusionProof(RequestId requestId); - - CompletableFuture getBlockHeight(); -} diff --git a/src/main/java/org/unicitylabs/sdk/api/InclusionProofRequest.java b/src/main/java/org/unicitylabs/sdk/api/InclusionProofRequest.java index 697185c..663ac20 100644 --- a/src/main/java/org/unicitylabs/sdk/api/InclusionProofRequest.java +++ b/src/main/java/org/unicitylabs/sdk/api/InclusionProofRequest.java @@ -1,14 +1,63 @@ package org.unicitylabs.sdk.api; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; + +/** + * Inclusion proof request. + */ public class InclusionProofRequest { private final RequestId requestId; - public InclusionProofRequest(RequestId requestId) { + /** + * Create inclusion proof request. + * + * @param requestId request id + */ + @JsonCreator + public InclusionProofRequest( + @JsonProperty("requestId") RequestId requestId + ) { this.requestId = requestId; } + /** + * Get request id. + * + * @return request id + */ public RequestId getRequestId() { return this.requestId; } + + /** + * Create request from JSON string. + * + * @param input JSON string + * @return inclusion proof request + */ + public static InclusionProofRequest fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, InclusionProofRequest.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(InclusionProofRequest.class, e); + } + } + + /** + * Convert request to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(InclusionProofRequest.class, e); + } + } } diff --git a/src/main/java/org/unicitylabs/sdk/api/InclusionProofResponse.java b/src/main/java/org/unicitylabs/sdk/api/InclusionProofResponse.java index 0f9b7f0..21b5d8c 100644 --- a/src/main/java/org/unicitylabs/sdk/api/InclusionProofResponse.java +++ b/src/main/java/org/unicitylabs/sdk/api/InclusionProofResponse.java @@ -1,16 +1,65 @@ package org.unicitylabs.sdk.api; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; import org.unicitylabs.sdk.transaction.InclusionProof; +/** + * Inclusion proof response. + */ public class InclusionProofResponse { private final InclusionProof inclusionProof; - public InclusionProofResponse(InclusionProof inclusionProof) { + /** + * Create inclison proof response. + * + * @param inclusionProof inclusion proof + */ + @JsonCreator + public InclusionProofResponse( + @JsonProperty("inclusionProof") + InclusionProof inclusionProof + ) { this.inclusionProof = inclusionProof; } + /** + * Get inclusion proof. + * + * @return inclusion proof + */ public InclusionProof getInclusionProof() { return this.inclusionProof; } + + /** + * Create response from JSON string. + * + * @param input JSON string + * @return inclusion proof response + */ + public static InclusionProofResponse fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, InclusionProofResponse.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(InclusionProofResponse.class, e); + } + } + + /** + * Convert response to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(InclusionProofResponse.class, e); + } + } } diff --git a/src/main/java/org/unicitylabs/sdk/api/JsonRpcAggregatorClient.java b/src/main/java/org/unicitylabs/sdk/api/JsonRpcAggregatorClient.java new file mode 100644 index 0000000..ce4680c --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/api/JsonRpcAggregatorClient.java @@ -0,0 +1,96 @@ +package org.unicitylabs.sdk.api; + +import static com.google.common.net.HttpHeaders.AUTHORIZATION; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.jsonrpc.JsonRpcHttpTransport; + +/** + * Default aggregator client. + */ +public class JsonRpcAggregatorClient implements AggregatorClient { + + private final JsonRpcHttpTransport transport; + private final String apiKey; + + /** + * Create aggregator client for destination url. + * + * @param url destination url + */ + public JsonRpcAggregatorClient(String url) { + this(url, null); + } + + + /** + * Create aggregator client for destination url with api key. + * + * @param url destination url + * @param apiKey api key + * + */ + public JsonRpcAggregatorClient(String url, String apiKey) { + this.transport = new JsonRpcHttpTransport(url); + this.apiKey = apiKey; + } + + /** + * Submit commitment. + * + * @param requestId request id + * @param transactionHash transaction hash + * @param authenticator authenticator + * @return submit commitment response + */ + public CompletableFuture submitCommitment( + RequestId requestId, + DataHash transactionHash, + Authenticator authenticator + ) { + SubmitCommitmentRequest request = new SubmitCommitmentRequest( + requestId, + transactionHash, + authenticator, + false + ); + + Map> headers = this.apiKey == null + ? Map.of() + : Map.of(AUTHORIZATION, List.of(String.format("Bearer %s", this.apiKey))); + + return this.transport.request( + "submit_commitment", + request, + SubmitCommitmentResponse.class, + headers + ); + } + + /** + * Get inclusion proof for request id. + * + * @param requestId request id + * @return inclusion / non inclusion proof + */ + public CompletableFuture getInclusionProof(RequestId requestId) { + InclusionProofRequest request = new InclusionProofRequest(requestId); + + return this.transport.request("get_inclusion_proof", request, InclusionProofResponse.class); + } + + /** + * Get block height. + * + * @return block height + */ + public CompletableFuture getBlockHeight() { + return this.transport.request("get_block_height", Collections.emptyMap(), + BlockHeightResponse.class) + .thenApply(BlockHeightResponse::getBlockNumber); + } +} \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/api/LeafValue.java b/src/main/java/org/unicitylabs/sdk/api/LeafValue.java index a57e563..8c2966b 100644 --- a/src/main/java/org/unicitylabs/sdk/api/LeafValue.java +++ b/src/main/java/org/unicitylabs/sdk/api/LeafValue.java @@ -1,17 +1,14 @@ package org.unicitylabs.sdk.api; -import com.fasterxml.jackson.core.JsonProcessingException; +import java.util.Arrays; +import java.util.Objects; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; import org.unicitylabs.sdk.util.HexConverter; -import java.util.Arrays; -import java.util.Objects; /** - * Leaf value for merkle tree + * Leaf value for merkle tree. */ public class LeafValue { @@ -21,19 +18,27 @@ private LeafValue(byte[] bytes) { this.bytes = Arrays.copyOf(bytes, bytes.length); } + /** + * Create leaf value from authenticator and transaction hash. + * + * @param authenticator authenticator + * @param transactionHash transaction hash + * @return leaf value + */ public static LeafValue create(Authenticator authenticator, DataHash transactionHash) { - try { - DataHash hash = new DataHasher(HashAlgorithm.SHA256) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(authenticator)) - .update(transactionHash.getImprint()) - .digest(); - - return new LeafValue(hash.getImprint()); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + DataHash hash = new DataHasher(HashAlgorithm.SHA256) + .update(authenticator.toCbor()) + .update(transactionHash.getImprint()) + .digest(); + + return new LeafValue(hash.getImprint()); } + /** + * Get leaf value as bytes. + * + * @return bytes + */ public byte[] getBytes() { return Arrays.copyOf(this.bytes, this.bytes.length); } diff --git a/src/main/java/org/unicitylabs/sdk/api/RequestId.java b/src/main/java/org/unicitylabs/sdk/api/RequestId.java index 9d0cc85..dcd99a7 100644 --- a/src/main/java/org/unicitylabs/sdk/api/RequestId.java +++ b/src/main/java/org/unicitylabs/sdk/api/RequestId.java @@ -1,25 +1,28 @@ package org.unicitylabs.sdk.api; -import org.unicitylabs.sdk.Hashable; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; import org.unicitylabs.sdk.util.BitString; +import org.unicitylabs.sdk.util.HexConverter; /** * Represents a unique request identifier derived from a public key and state hash. */ -public class RequestId implements Hashable { - - private final DataHash hash; +@JsonDeserialize(using = RequestIdJson.Deserializer.class) +public class RequestId extends DataHash { /** * Constructs a RequestId instance. * * @param hash The DataHash representing the request ID. */ - public RequestId(DataHash hash) { - this.hash = hash; + protected RequestId(DataHash hash) { + super(hash.getAlgorithm(), hash.getData()); } /** @@ -49,39 +52,39 @@ public static RequestId createFromImprint(byte[] id, byte[] hashImprint) { } /** - * Converts the RequestId to a BitString. + * Create a request id from JSON string. * - * @return The BitString representation of the RequestId. + * @param input JSON string + * @return request id */ - public BitString toBitString() { - return BitString.fromDataHash(this.hash); + public static RequestId fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, RequestId.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(RequestId.class, e); + } } /** - * Gets the underlying DataHash. + * Converts the request id to a JSON string. * - * @return The DataHash. + * @return JSON string */ - public DataHash getHash() { - return this.hash; + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(RequestId.class, e); + } } /** - * Checks if this RequestId is equal to another. + * Converts the RequestId to a BitString. * - * @param obj The object to compare. - * @return True if equal, false otherwise. + * @return The BitString representation of the RequestId. */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - RequestId requestId = (RequestId) obj; - return this.hash.equals(requestId.hash); + public BitString toBitString() { + return BitString.fromDataHash(this); } /** @@ -91,6 +94,6 @@ public boolean equals(Object obj) { */ @Override public String toString() { - return String.format("RequestId[%s]", this.hash.toString()); + return String.format("RequestId[%s]", HexConverter.encode(this.getImprint())); } } \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/api/RequestIdJson.java b/src/main/java/org/unicitylabs/sdk/api/RequestIdJson.java new file mode 100644 index 0000000..2ee0bf5 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/api/RequestIdJson.java @@ -0,0 +1,43 @@ +package org.unicitylabs.sdk.api; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import java.io.IOException; +import org.unicitylabs.sdk.hash.DataHash; + +/** + * Request ID deserializer implementation. + */ +public class RequestIdJson { + + private RequestIdJson() { + } + + /** + * Request ID deserializer. + */ + public static class Deserializer extends StdDeserializer { + + /** + * Create deserializer. + */ + public Deserializer() { + super(RequestId.class); + } + + /** + * Deserialize request id. + * + * @param p Parser used for reading JSON content + * @param ctx Context that can be used to access information about this deserialization + * activity. + * @return request id + * @throws IOException on deserialization failure + */ + @Override + public RequestId deserialize(JsonParser p, DeserializationContext ctx) throws IOException { + return new RequestId(p.readValueAs(DataHash.class)); + } + } +} diff --git a/src/main/java/org/unicitylabs/sdk/api/SubmitCommitmentRequest.java b/src/main/java/org/unicitylabs/sdk/api/SubmitCommitmentRequest.java index 2bc1c67..957a45d 100644 --- a/src/main/java/org/unicitylabs/sdk/api/SubmitCommitmentRequest.java +++ b/src/main/java/org/unicitylabs/sdk/api/SubmitCommitmentRequest.java @@ -1,7 +1,15 @@ package org.unicitylabs.sdk.api; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; +/** + * Submit commitment request. + */ public class SubmitCommitmentRequest { private final RequestId requestId; @@ -9,27 +17,86 @@ public class SubmitCommitmentRequest { private final Authenticator authenticator; private final Boolean receipt; - public SubmitCommitmentRequest(RequestId requestId, DataHash transactionHash, - Authenticator authenticator, Boolean receipt) { + /** + * Create submit commitment request. + * + * @param requestId request id + * @param transactionHash transaction hash + * @param authenticator authenticator + * @param receipt get receipt + */ + @JsonCreator + public SubmitCommitmentRequest( + @JsonProperty("requestId") RequestId requestId, + @JsonProperty("transactionHash") DataHash transactionHash, + @JsonProperty("authenticator") Authenticator authenticator, + @JsonProperty("receipt") Boolean receipt) { this.requestId = requestId; this.transactionHash = transactionHash; this.authenticator = authenticator; this.receipt = receipt; } + /** + * Get request id. + * + * @return request id + */ public RequestId getRequestId() { return this.requestId; } + /** + * Get transaction hash. + * + * @return transaction hash + */ public DataHash getTransactionHash() { return this.transactionHash; } + /** + * Get authenticator. + * + * @return authenticator + */ public Authenticator getAuthenticator() { return this.authenticator; } + /** + * Is getting receipt from unicity service. + * + * @return true if receipt unicity service should return receipt + */ public Boolean getReceipt() { return this.receipt; } + + /** + * Create submit commitment request from JSON string. + * + * @param input JSON string + * @return submit commitment request + */ + public static SubmitCommitmentRequest fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, SubmitCommitmentRequest.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(SubmitCommitmentRequest.class, e); + } + } + + /** + * Convert submit commitment request to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(SubmitCommitmentRequest.class, e); + } + } } diff --git a/src/main/java/org/unicitylabs/sdk/api/SubmitCommitmentResponse.java b/src/main/java/org/unicitylabs/sdk/api/SubmitCommitmentResponse.java index cbf5fc4..39d88c8 100644 --- a/src/main/java/org/unicitylabs/sdk/api/SubmitCommitmentResponse.java +++ b/src/main/java/org/unicitylabs/sdk/api/SubmitCommitmentResponse.java @@ -1,17 +1,66 @@ package org.unicitylabs.sdk.api; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; + +/** + * Submit commitment response. + */ public class SubmitCommitmentResponse { private final SubmitCommitmentStatus status; - public SubmitCommitmentResponse(SubmitCommitmentStatus status) { + /** + * Create submit commitment response. + * + * @param status status + */ + @JsonCreator + public SubmitCommitmentResponse( + @JsonProperty("status") SubmitCommitmentStatus status + ) { this.status = status; } + /** + * Get status. + * + * @return status + */ public SubmitCommitmentStatus getStatus() { return this.status; } + /** + * Create submit commitment response from JSON string. + * + * @param input JSON string + * @return submit commitment response + */ + public static SubmitCommitmentResponse fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, SubmitCommitmentResponse.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(SubmitCommitmentResponse.class, e); + } + } + + /** + * Convert submit commitment response to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(SubmitCommitmentResponse.class, e); + } + } + @Override public String toString() { return String.format("SubmitCommitmentResponse{status=%s}", this.status); diff --git a/src/main/java/org/unicitylabs/sdk/api/SubmitCommitmentStatus.java b/src/main/java/org/unicitylabs/sdk/api/SubmitCommitmentStatus.java index 8564357..f0f926c 100644 --- a/src/main/java/org/unicitylabs/sdk/api/SubmitCommitmentStatus.java +++ b/src/main/java/org/unicitylabs/sdk/api/SubmitCommitmentStatus.java @@ -4,31 +4,50 @@ * Status codes for submit commitment response. */ public enum SubmitCommitmentStatus { - /** The commitment was accepted and stored. */ - SUCCESS("SUCCESS"), - /** Signature verification failed. */ - AUTHENTICATOR_VERIFICATION_FAILED("AUTHENTICATOR_VERIFICATION_FAILED"), - /** Request identifier did not match the payload. */ - REQUEST_ID_MISMATCH("REQUEST_ID_MISMATCH"), - /** A commitment with the same request id already exists. */ - REQUEST_ID_EXISTS("REQUEST_ID_EXISTS"); + /** + * The commitment was accepted and stored. + */ + SUCCESS("SUCCESS"), + /** + * Signature verification failed. + */ + AUTHENTICATOR_VERIFICATION_FAILED("AUTHENTICATOR_VERIFICATION_FAILED"), + /** + * Request identifier did not match the payload. + */ + REQUEST_ID_MISMATCH("REQUEST_ID_MISMATCH"), + /** + * A commitment with the same request id already exists. + */ + REQUEST_ID_EXISTS("REQUEST_ID_EXISTS"); - private final String value; + private final String value; - SubmitCommitmentStatus(String value) { - this.value = value; - } + SubmitCommitmentStatus(String value) { + this.value = value; + } - public String getValue() { - return value; - } - - public static SubmitCommitmentStatus fromString(String value) { - for (SubmitCommitmentStatus status : SubmitCommitmentStatus.values()) { - if (status.value.equalsIgnoreCase(value)) { - return status; - } - } - throw new IllegalArgumentException("Unknown status: " + value); + /** + * Get string value of the status. + * + * @return string value + */ + public String getValue() { + return value; + } + + /** + * Create status from string value. + * + * @param value string value + * @return status + */ + public static SubmitCommitmentStatus fromString(String value) { + for (SubmitCommitmentStatus status : SubmitCommitmentStatus.values()) { + if (status.value.equalsIgnoreCase(value)) { + return status; + } } + throw new IllegalArgumentException("Unknown status: " + value); + } } \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/bft/InputRecord.java b/src/main/java/org/unicitylabs/sdk/bft/InputRecord.java index bca9f7c..c5725fc 100644 --- a/src/main/java/org/unicitylabs/sdk/bft/InputRecord.java +++ b/src/main/java/org/unicitylabs/sdk/bft/InputRecord.java @@ -1,9 +1,17 @@ package org.unicitylabs.sdk.bft; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Arrays; +import java.util.List; import java.util.Objects; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer.CborTag; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.util.HexConverter; +/** + * Input record for UnicityCertificate. + */ public class InputRecord { private final int version; @@ -17,7 +25,7 @@ public class InputRecord { private final long sumOfEarnedFees; private final byte[] executedTransactionsHash; - public InputRecord( + InputRecord( int version, long roundNumber, long epoch, @@ -27,7 +35,7 @@ public InputRecord( long timestamp, byte[] blockHash, long sumOfEarnedFees, - byte[] executedTransactionsHash + @JsonProperty("executedTransactionsHash") byte[] executedTransactionsHash ) { Objects.requireNonNull(hash, "Hash cannot be null"); Objects.requireNonNull(summaryValue, "Summary value cannot be null"); @@ -44,44 +52,143 @@ public InputRecord( this.executedTransactionsHash = executedTransactionsHash; } + /** + * Get version. + * + * @return version + */ public int getVersion() { return this.version; } + /** + * Get round number. + * + * @return round number + */ public long getRoundNumber() { return this.roundNumber; } + /** + * Get epoch. + * + * @return epoch + */ public long getEpoch() { return this.epoch; } + /** + * Get previous hash. + * + * @return previous hash or null if not set + */ public byte[] getPreviousHash() { - return this.previousHash != null ? Arrays.copyOf(this.previousHash, this.previousHash.length) : null; + return this.previousHash != null ? Arrays.copyOf(this.previousHash, this.previousHash.length) + : null; } + /** + * Get hash. + * + * @return hash + */ public byte[] getHash() { - return this.hash != null ? Arrays.copyOf(this.hash, this.hash.length) : null; + return Arrays.copyOf(this.hash, this.hash.length); } + /** + * Get summary value. + * + * @return summary value + */ public byte[] getSummaryValue() { return Arrays.copyOf(this.summaryValue, this.summaryValue.length); } + /** + * Get timestamp. + * + * @return timestamp + */ public long getTimestamp() { return this.timestamp; } + /** + * Get block hash. + * + * @return block hash or null if not set + */ public byte[] getBlockHash() { return this.blockHash != null ? Arrays.copyOf(this.blockHash, this.blockHash.length) : null; } + /** + * Get sum of earned fees. + * + * @return sum of earned fees + */ public long getSumOfEarnedFees() { return this.sumOfEarnedFees; } + /** + * Get executed transactions hash. + * + * @return executed transactions hash or null if not set + */ public byte[] getExecutedTransactionsHash() { - return this.executedTransactionsHash != null ? Arrays.copyOf(this.executedTransactionsHash, this.executedTransactionsHash.length) : null; + return this.executedTransactionsHash != null ? Arrays.copyOf(this.executedTransactionsHash, + this.executedTransactionsHash.length) : null; + } + + /** + * Create InputRecord from CBOR bytes. + * + * @param bytes CBOR bytes + * @return input record + */ + public static InputRecord fromCbor(byte[] bytes) { + CborTag tag = CborDeserializer.readTag(bytes); + List data = CborDeserializer.readArray(tag.getData()); + + return new InputRecord( + CborDeserializer.readUnsignedInteger(data.get(0)).asInt(), + CborDeserializer.readUnsignedInteger(data.get(1)).asLong(), + CborDeserializer.readUnsignedInteger(data.get(2)).asLong(), + CborDeserializer.readOptional(data.get(3), CborDeserializer::readByteString), + CborDeserializer.readByteString(data.get(4)), + CborDeserializer.readByteString(data.get(5)), + CborDeserializer.readUnsignedInteger(data.get(6)).asLong(), + CborDeserializer.readOptional(data.get(7), CborDeserializer::readByteString), + CborDeserializer.readUnsignedInteger(data.get(8)).asLong(), + CborDeserializer.readOptional(data.get(9), CborDeserializer::readByteString) + ); + } + + /** + * Convert InputRecord to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeTag( + 1008, + CborSerializer.encodeArray( + CborSerializer.encodeUnsignedInteger(this.version), + CborSerializer.encodeUnsignedInteger(this.roundNumber), + CborSerializer.encodeUnsignedInteger(this.epoch), + CborSerializer.encodeOptional(this.previousHash, CborSerializer::encodeByteString), + CborSerializer.encodeByteString(this.hash), + CborSerializer.encodeByteString(this.summaryValue), + CborSerializer.encodeUnsignedInteger(this.timestamp), + CborSerializer.encodeOptional(this.blockHash, CborSerializer::encodeByteString), + CborSerializer.encodeUnsignedInteger(this.sumOfEarnedFees), + CborSerializer.encodeOptional(this.executedTransactionsHash, + CborSerializer::encodeByteString) + )); } @Override @@ -112,18 +219,19 @@ public int hashCode() { @Override public String toString() { return String.format("InputRecord{version=%s, roundNumber=%s, epoch=%s, previousHash=%s, " - + "hash=%s, summaryValue=%s, timestamp=%s, blockHash=%s, sumOfEarnedFees=%s, " - + "executedTransactionsHash=%s}", + + "hash=%s, summaryValue=%s, timestamp=%s, blockHash=%s, sumOfEarnedFees=%s, " + + "executedTransactionsHash=%s}", this.version, this.roundNumber, this.epoch, this.previousHash != null ? HexConverter.encode(this.previousHash) : null, - this.hash != null ? HexConverter.encode(this.hash) : null, + HexConverter.encode(this.hash), HexConverter.encode(this.summaryValue), this.timestamp, this.blockHash != null ? HexConverter.encode(this.blockHash) : null, this.sumOfEarnedFees, - this.executedTransactionsHash != null ? HexConverter.encode(this.executedTransactionsHash) : null + this.executedTransactionsHash != null ? HexConverter.encode(this.executedTransactionsHash) + : null ); } } diff --git a/src/main/java/org/unicitylabs/sdk/bft/RootTrustBase.java b/src/main/java/org/unicitylabs/sdk/bft/RootTrustBase.java index dcbc225..d221089 100644 --- a/src/main/java/org/unicitylabs/sdk/bft/RootTrustBase.java +++ b/src/main/java/org/unicitylabs/sdk/bft/RootTrustBase.java @@ -1,11 +1,19 @@ package org.unicitylabs.sdk.bft; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; import java.util.Arrays; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; +/** + * Root trust base information. + */ public class RootTrustBase { private final long version; @@ -19,17 +27,18 @@ public class RootTrustBase { private final byte[] previousEntryHash; private final Map signatures; - public RootTrustBase( - long version, - int networkId, - long epoch, - long epochStartRound, - Set rootNodes, - long quorumThreshold, - byte[] stateHash, - byte[] changeRecordHash, - byte[] previousEntryHash, - Map signatures + @JsonCreator + RootTrustBase( + @JsonProperty("version") long version, + @JsonProperty("networkId") int networkId, + @JsonProperty("epoch") long epoch, + @JsonProperty("epochStartRound") long epochStartRound, + @JsonProperty("rootNodes") Set rootNodes, + @JsonProperty("quorumThreshold") long quorumThreshold, + @JsonProperty("stateHash") byte[] stateHash, + @JsonProperty("changeRecordHash") byte[] changeRecordHash, + @JsonProperty("previousEntryHash") byte[] previousEntryHash, + @JsonProperty("signatures") Map signatures ) { this.version = version; this.networkId = networkId; @@ -53,46 +62,96 @@ public RootTrustBase( ); } + /** + * Get version. + * + * @return version + */ public long getVersion() { return this.version; } + /** + * Get network id. + * + * @return network id + */ public int getNetworkId() { return this.networkId; } + /** + * Get current epoch. + * + * @return epoch + */ public long getEpoch() { return this.epoch; } + /** + * Get epoch start round. + * + * @return epoch start round + */ public long getEpochStartRound() { return this.epochStartRound; } + /** + * Get root nodes. + * + * @return root nodes + */ public Set getRootNodes() { return this.rootNodes; } + /** + * Get quorum threshold. + * + * @return quorum threshold + */ public long getQuorumThreshold() { return this.quorumThreshold; } + /** + * Get state hash. + * + * @return state hash + */ public byte[] getStateHash() { return Arrays.copyOf(this.stateHash, this.stateHash.length); } + /** + * Get change record hash. + * + * @return change record hash + */ public byte[] getChangeRecordHash() { return this.changeRecordHash == null ? null : Arrays.copyOf(this.changeRecordHash, this.changeRecordHash.length); } + /** + * Get previous entry hash. + * + * @return previous entry hash + */ public byte[] getPreviousEntryHash() { return this.previousEntryHash == null ? null : Arrays.copyOf(this.previousEntryHash, this.previousEntryHash.length); } + /** + * Get signatures. + * + * @return signatures + */ public Map getSignatures() { return this.signatures.entrySet().stream() .collect( @@ -103,26 +162,78 @@ public Map getSignatures() { ); } + /** + * Create a root trust base from JSON string. + * + * @param input JSON string + * @return root trust base + */ + public static RootTrustBase fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, RootTrustBase.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(RootTrustBase.class, e); + } + } + + /** + * Convert root trust base to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(RootTrustBase.class, e); + } + } + + /** + * Node information. + */ public static class NodeInfo { private final String nodeId; private final byte[] signingKey; private final long stakedAmount; - public NodeInfo(String nodeId, byte[] signingKey, long stakedAmount) { + @JsonCreator + NodeInfo( + @JsonProperty("nodeId") String nodeId, + @JsonProperty("sigKey") byte[] signingKey, + @JsonProperty("stake") long stakedAmount + ) { this.nodeId = nodeId; this.signingKey = Arrays.copyOf(signingKey, signingKey.length); this.stakedAmount = stakedAmount; } + /** + * Get node ID. + * + * @return node ID + */ public String getNodeId() { return this.nodeId; } + /** + * Get signing key. + * + * @return signing key + */ + @JsonProperty("sigKey") public byte[] getSigningKey() { return Arrays.copyOf(this.signingKey, this.signingKey.length); } + /** + * Get staked amount. + * + * @return staked amount + */ + @JsonProperty("stake") public long getStakedAmount() { return this.stakedAmount; } diff --git a/src/main/java/org/unicitylabs/sdk/bft/ShardTreeCertificate.java b/src/main/java/org/unicitylabs/sdk/bft/ShardTreeCertificate.java index 8e48576..19ee062 100644 --- a/src/main/java/org/unicitylabs/sdk/bft/ShardTreeCertificate.java +++ b/src/main/java/org/unicitylabs/sdk/bft/ShardTreeCertificate.java @@ -1,17 +1,28 @@ package org.unicitylabs.sdk.bft; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.util.HexConverter; +/** + * Shard tree certificate. + */ public class ShardTreeCertificate { private final byte[] shard; private final List siblingHashList; - public ShardTreeCertificate(byte[] shard, List siblingHashList) { + @JsonCreator + ShardTreeCertificate( + @JsonProperty("shard") byte[] shard, + @JsonProperty("siblingHashList") List siblingHashList + ) { Objects.requireNonNull(shard, "Shard cannot be null"); Objects.requireNonNull(siblingHashList, "Sibling hash list cannot be null"); @@ -21,16 +32,59 @@ public ShardTreeCertificate(byte[] shard, List siblingHashList) { .collect(Collectors.toList()); } + /** + * Get shard. + * + * @return shard + */ public byte[] getShard() { return Arrays.copyOf(this.shard, this.shard.length); } + /** + * Get sibling hash list. + * + * @return sibling hash list + */ public List getSiblingHashList() { return this.siblingHashList.stream() .map(hash -> Arrays.copyOf(hash, hash.length)) .collect(Collectors.toList()); } + /** + * Create shard tree certificate from CBOR bytes. + * + * @param bytes CBOR bytes + * @return shard tree certificate + */ + public static ShardTreeCertificate fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new ShardTreeCertificate( + CborDeserializer.readByteString(data.get(0)), + CborDeserializer.readArray(data.get(1)).stream() + .map(CborDeserializer::readByteString) + .collect(Collectors.toList()) + ); + } + + /** + * Convert shard tree certificate to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + CborSerializer.encodeByteString(this.shard), + CborSerializer.encodeArray( + this.siblingHashList.stream() + .map(CborSerializer::encodeByteString) + .toArray(byte[][]::new) + ) + ); + } + @Override public boolean equals(Object o) { if (!(o instanceof ShardTreeCertificate)) { diff --git a/src/main/java/org/unicitylabs/sdk/bft/UnicityCertificate.java b/src/main/java/org/unicitylabs/sdk/bft/UnicityCertificate.java index 62646e0..1fc8761 100644 --- a/src/main/java/org/unicitylabs/sdk/bft/UnicityCertificate.java +++ b/src/main/java/org/unicitylabs/sdk/bft/UnicityCertificate.java @@ -1,16 +1,23 @@ package org.unicitylabs.sdk.bft; -import java.io.IOException; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.util.Arrays; import java.util.List; import java.util.Objects; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer.CborTag; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.util.HexConverter; +/** + * Unicity certificate. + */ +@JsonSerialize(using = UnicityCertificateJson.Serializer.class) +@JsonDeserialize(using = UnicityCertificateJson.Deserializer.class) public class UnicityCertificate { private final int version; @@ -21,7 +28,7 @@ public class UnicityCertificate { private final UnicityTreeCertificate unicityTreeCertificate; private final UnicitySeal unicitySeal; - public UnicityCertificate( + UnicityCertificate( int version, InputRecord inputRecord, byte[] technicalRecordHash, @@ -48,68 +55,150 @@ public UnicityCertificate( this.unicitySeal = unicitySeal; } + /** + * Get the certificate version. + * + * @return certificate version + */ public int getVersion() { return this.version; } + /** + * Get the input record. + * + * @return input record + */ public InputRecord getInputRecord() { return this.inputRecord; } + /** + * Get the technical record hash. + * + * @return technical record hash + */ public byte[] getTechnicalRecordHash() { return Arrays.copyOf(this.technicalRecordHash, this.technicalRecordHash.length); } + /** + * Get the shard configuration hash. + * + * @return shard configuration hash + */ public byte[] getShardConfigurationHash() { return Arrays.copyOf(this.shardConfigurationHash, this.shardConfigurationHash.length); } + /** + * Get the shard tree certificate. + * + * @return shard tree certificate + */ public ShardTreeCertificate getShardTreeCertificate() { return this.shardTreeCertificate; } + /** + * Get the unicity tree certificate. + * + * @return unicity tree certificate + */ public UnicityTreeCertificate getUnicityTreeCertificate() { return this.unicityTreeCertificate; } + /** + * Get the unicity seal. + * + * @return unicity seal + */ public UnicitySeal getUnicitySeal() { return this.unicitySeal; } + /** + * Calculate the root hash of the shard tree certificate. + * + * @param inputRecord input record + * @param technicalRecordHash technical record hash + * @param shardConfigurationHash shard configuration hash + * @param shardTreeCertificate shard tree certificate + * @return root hash + */ public static DataHash calculateShardTreeCertificateRootHash( InputRecord inputRecord, byte[] technicalRecordHash, byte[] shardConfigurationHash, ShardTreeCertificate shardTreeCertificate ) { - try { - DataHash rootHash = new DataHasher(HashAlgorithm.SHA256) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(inputRecord)) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(technicalRecordHash)) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(shardConfigurationHash)) - .digest(); - - byte[] shardId = shardTreeCertificate.getShard(); - List siblingHashes = shardTreeCertificate.getSiblingHashList(); - for (int i = 0; i < siblingHashes.size(); i++) { - boolean isRight = shardId[(shardId.length - 1) - (i / 8)] == 1; - if (isRight) { - rootHash = new DataHasher(HashAlgorithm.SHA256) - .update(siblingHashes.get(i)) - .update(rootHash.getData()) - .digest(); - } else { - rootHash = new DataHasher(HashAlgorithm.SHA256) - .update(rootHash.getData()) - .update(siblingHashes.get(i)) - .digest(); - } - } - return rootHash; - } catch (IOException e) { - throw new CborSerializationException(e); + DataHash rootHash = new DataHasher(HashAlgorithm.SHA256) + .update(inputRecord.toCbor()) + .update(CborSerializer.encodeByteString(technicalRecordHash)) + .update(CborSerializer.encodeByteString(shardConfigurationHash)) + .digest(); + + byte[] shardId = shardTreeCertificate.getShard(); + List siblingHashes = shardTreeCertificate.getSiblingHashList(); + for (int i = 0; i < siblingHashes.size(); i++) { + boolean isRight = shardId[(shardId.length - 1) - (i / 8)] == 1; + if (isRight) { + rootHash = new DataHasher(HashAlgorithm.SHA256) + .update(siblingHashes.get(i)) + .update(rootHash.getData()) + .digest(); + } else { + rootHash = new DataHasher(HashAlgorithm.SHA256) + .update(rootHash.getData()) + .update(siblingHashes.get(i)) + .digest(); + } } + + return rootHash; + + } + + /** + * Create unicity certificate from CBOR bytes. + * + * @param bytes CBOR bytes + * @return unicity certificate + */ + public static UnicityCertificate fromCbor(byte[] bytes) { + CborTag tag = CborDeserializer.readTag(bytes); + List data = CborDeserializer.readArray(tag.getData()); + + return new UnicityCertificate( + CborDeserializer.readUnsignedInteger(data.get(0)).asInt(), + InputRecord.fromCbor(data.get(1)), + CborDeserializer.readByteString(data.get(2)), + CborDeserializer.readByteString(data.get(3)), + ShardTreeCertificate.fromCbor(data.get(4)), + UnicityTreeCertificate.fromCbor(data.get(5)), + UnicitySeal.fromCbor(data.get(6)) + ); + } + + /** + * Convert unicity certificate to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeTag( + 1007, + CborSerializer.encodeArray( + CborSerializer.encodeUnsignedInteger(this.version), + this.inputRecord.toCbor(), + CborSerializer.encodeByteString(this.technicalRecordHash), + CborSerializer.encodeByteString(this.shardConfigurationHash), + this.shardTreeCertificate.toCbor(), + this.unicityTreeCertificate.toCbor(), + this.unicitySeal.toCbor() + )); } @Override diff --git a/src/main/java/org/unicitylabs/sdk/bft/UnicityCertificateJson.java b/src/main/java/org/unicitylabs/sdk/bft/UnicityCertificateJson.java new file mode 100644 index 0000000..298e286 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/bft/UnicityCertificateJson.java @@ -0,0 +1,74 @@ +package org.unicitylabs.sdk.bft; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; + +/** + * Unicity certificate serializer and deserializer implementation. + */ +public class UnicityCertificateJson { + + private UnicityCertificateJson() { + } + + /** + * Unicity certificate serializer. + */ + public static class Serializer extends StdSerializer { + + /** + * Create serializer. + */ + public Serializer() { + super(UnicityCertificate.class); + } + + /** + * Serialize unicity certificate. + * + * @param value unicity certificate + * @param gen json generator + * @param serializers serializer provider + * @throws IOException on serialization failure + */ + @Override + public void serialize(UnicityCertificate value, JsonGenerator gen, + SerializerProvider serializers) + throws IOException { + gen.writeObject(value.toCbor()); + } + } + + /** + * Unicity certificate deserializer. + */ + public static class Deserializer extends StdDeserializer { + + /** + * Create deserializer. + */ + public Deserializer() { + super(UnicityCertificate.class); + } + + /** + * Deserialize unicity certificate. + * + * @param p json parser + * @param ctx deserialization context + * @return unicity certificate + * @throws IOException on deserialization failure + */ + @Override + public UnicityCertificate deserialize(JsonParser p, DeserializationContext ctx) + throws IOException { + return UnicityCertificate.fromCbor(p.readValueAs(byte[].class)); + } + } +} + diff --git a/src/main/java/org/unicitylabs/sdk/bft/UnicitySeal.java b/src/main/java/org/unicitylabs/sdk/bft/UnicitySeal.java index 3f74ee2..7222206 100644 --- a/src/main/java/org/unicitylabs/sdk/bft/UnicitySeal.java +++ b/src/main/java/org/unicitylabs/sdk/bft/UnicitySeal.java @@ -1,15 +1,20 @@ package org.unicitylabs.sdk.bft; -import java.io.IOException; import java.util.Arrays; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer.CborTag; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer.CborMap; import org.unicitylabs.sdk.util.HexConverter; +/** + * UnicitySeal represents a seal in the Unicity BFT system, containing metadata and signatures. + */ public class UnicitySeal { private final int version; @@ -21,7 +26,7 @@ public class UnicitySeal { private final byte[] hash; private final LinkedHashMap signatures; - public UnicitySeal( + UnicitySeal( int version, short networkId, long rootChainRoundNumber, @@ -58,19 +63,12 @@ public UnicitySeal( ); } - public static UnicitySeal fromUnicitySealWithoutSignatures(UnicitySeal seal) { - return new UnicitySeal( - seal.version, - seal.networkId, - seal.rootChainRoundNumber, - seal.epoch, - seal.timestamp, - seal.previousHash, - seal.hash, - null - ); - } - + /** + * Create a new UnicitySeal instance with the provided signatures. + * + * @param signatures the signatures to include in the new UnicitySeal + * @return a new UnicitySeal instance with the specified signatures + */ public UnicitySeal withSignatures(Map signatures) { return new UnicitySeal( this.version, @@ -84,35 +82,75 @@ public UnicitySeal withSignatures(Map signatures) { ); } + /** + * Get the version. + * + * @return version + */ public int getVersion() { return this.version; } + /** + * Get the network ID. + * + * @return network ID + */ public short getNetworkId() { return this.networkId; } + /** + * Get the root chain round number. + * + * @return root chain round number + */ public long getRootChainRoundNumber() { return this.rootChainRoundNumber; } + /** + * Get the epoch. + * + * @return epoch + */ public long getEpoch() { return this.epoch; } + /** + * Get the timestamp. + * + * @return timestamp + */ public long getTimestamp() { return this.timestamp; } + /** + * Get the previous hash. + * + * @return previous hash or null if not set + */ public byte[] getPreviousHash() { return this.previousHash != null ? Arrays.copyOf(this.previousHash, this.previousHash.length) : null; } + /** + * Get the hash. + * + * @return hash + */ public byte[] getHash() { return Arrays.copyOf(this.hash, this.hash.length); } + /** + * Get the signatures. + * + * @return signatures + */ public Map getSignatures() { return this.signatures == null ? null @@ -132,12 +170,85 @@ public Map getSignatures() { ); } - public byte[] encode() { - try { - return UnicityObjectMapper.CBOR.writeValueAsBytes(this); - } catch (IOException e) { - throw new CborSerializationException(e); - } + /** + * Create unicity seal from CBOR bytes. + * + * @param bytes CBOR bytes + * @return unicity seal + */ + public static UnicitySeal fromCbor(byte[] bytes) { + CborTag tag = CborDeserializer.readTag(bytes); + List data = CborDeserializer.readArray(tag.getData()); + + return new UnicitySeal( + CborDeserializer.readUnsignedInteger(data.get(0)).asInt(), + CborDeserializer.readUnsignedInteger(data.get(1)).asShort(), + CborDeserializer.readUnsignedInteger(data.get(2)).asLong(), + CborDeserializer.readUnsignedInteger(data.get(3)).asLong(), + CborDeserializer.readUnsignedInteger(data.get(4)).asLong(), + CborDeserializer.readOptional(data.get(5), CborDeserializer::readByteString), + CborDeserializer.readByteString(data.get(6)), + CborDeserializer.readMap(data.get(7)).stream() + .collect( + Collectors.toMap( + entry -> CborDeserializer.readTextString(entry.getKey()), + entry -> CborDeserializer.readByteString(entry.getValue() + ) + ) + ) + ); + } + + /** + * Convert unicity seal to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeTag( + 1001, + CborSerializer.encodeArray( + CborSerializer.encodeUnsignedInteger(this.version), + CborSerializer.encodeUnsignedInteger(this.networkId), + CborSerializer.encodeUnsignedInteger(this.rootChainRoundNumber), + CborSerializer.encodeUnsignedInteger(this.epoch), + CborSerializer.encodeUnsignedInteger(this.timestamp), + CborSerializer.encodeOptional(this.previousHash, CborSerializer::encodeByteString), + CborSerializer.encodeByteString(this.hash), + CborSerializer.encodeOptional( + this.signatures, + (signatures) -> CborSerializer.encodeMap( + new CborMap( + signatures.entrySet().stream() + .map(entry -> new CborMap.Entry( + CborSerializer.encodeTextString(entry.getKey()), + CborSerializer.encodeByteString(entry.getValue()) + ) + ) + .collect(Collectors.toSet()) + ) + ) + ) + ) + ); + } + + /** + * Convert unicity seal to CBOR bytes without signatures. + * + * @return CBOR bytes without signatures + */ + public byte[] toCborWithoutSignatures() { + return new UnicitySeal( + this.version, + this.networkId, + this.rootChainRoundNumber, + this.epoch, + this.timestamp, + this.previousHash, + this.hash, + null + ).toCbor(); } @Override diff --git a/src/main/java/org/unicitylabs/sdk/bft/UnicityTreeCertificate.java b/src/main/java/org/unicitylabs/sdk/bft/UnicityTreeCertificate.java index 4b0938d..afff5da 100644 --- a/src/main/java/org/unicitylabs/sdk/bft/UnicityTreeCertificate.java +++ b/src/main/java/org/unicitylabs/sdk/bft/UnicityTreeCertificate.java @@ -3,15 +3,22 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer.CborTag; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.util.HexConverter; +/** + * Unicity tree certificate. + */ public class UnicityTreeCertificate { private final int version; private final int partitionIdentifier; private final List steps; - public UnicityTreeCertificate( + UnicityTreeCertificate( int version, int partitionIdentifier, List steps @@ -23,18 +30,69 @@ public UnicityTreeCertificate( this.steps = List.copyOf(steps); } + /** + * Get certificate version. + * + * @return version + */ public int getVersion() { return this.version; } + /** + * Get partition identifier. + * + * @return partition identifier + */ public int getPartitionIdentifier() { return this.partitionIdentifier; } + /** + * Get hash steps. + * + * @return hash steps + */ public List getSteps() { return this.steps; } + /** + * Create certificate from CBOR bytes. + * + * @param bytes CBOR bytes + * @return certificate + */ + public static UnicityTreeCertificate fromCbor(byte[] bytes) { + CborTag tag = CborDeserializer.readTag(bytes); + List data = CborDeserializer.readArray(tag.getData()); + + return new UnicityTreeCertificate( + CborDeserializer.readUnsignedInteger(data.get(0)).asInt(), + CborDeserializer.readUnsignedInteger(data.get(1)).asInt(), + CborDeserializer.readArray(data.get(2)).stream() + .map(HashStep::fromCbor) + .collect(Collectors.toList()) + ); + } + + /** + * Convert certificate to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeTag( + 1014, + CborSerializer.encodeArray( + CborSerializer.encodeUnsignedInteger(this.version), + CborSerializer.encodeUnsignedInteger(this.partitionIdentifier), + CborSerializer.encodeArray(this.steps.stream() + .map(HashStep::toCbor) + .toArray(byte[][]::new)) + )); + } + @Override public boolean equals(Object o) { if (!(o instanceof UnicityTreeCertificate)) { @@ -57,25 +115,65 @@ public String toString() { this.version, this.partitionIdentifier, this.steps); } + /** + * Hash step in the certificate. + */ public static class HashStep { + private final int key; private final byte[] hash; - public HashStep(int key, byte[] hash) { + HashStep(int key, byte[] hash) { Objects.requireNonNull(hash, "Hash cannot be null"); this.key = key; this.hash = Arrays.copyOf(hash, hash.length); } + /** + * Get key. + * + * @return key + */ public int getKey() { return this.key; } + /** + * Get hash. + * + * @return hash + */ public byte[] getHash() { return Arrays.copyOf(this.hash, this.hash.length); } + /** + * Create hash step from CBOR bytes. + * + * @param bytes CBOR bytes + * @return hash step + */ + public static HashStep fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new HashStep( + CborDeserializer.readUnsignedInteger(data.get(0)).asInt(), + CborDeserializer.readByteString(data.get(1)) + ); + } + + /** + * Convert hash step to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + CborSerializer.encodeUnsignedInteger(this.key), + CborSerializer.encodeByteString(this.hash) + ); + } @Override public boolean equals(Object o) { diff --git a/src/main/java/org/unicitylabs/sdk/bft/verification/UnicityCertificateVerificationContext.java b/src/main/java/org/unicitylabs/sdk/bft/verification/UnicityCertificateVerificationContext.java index 58bd201..07d77ee 100644 --- a/src/main/java/org/unicitylabs/sdk/bft/verification/UnicityCertificateVerificationContext.java +++ b/src/main/java/org/unicitylabs/sdk/bft/verification/UnicityCertificateVerificationContext.java @@ -3,14 +3,25 @@ import org.unicitylabs.sdk.bft.RootTrustBase; import org.unicitylabs.sdk.bft.UnicityCertificate; import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.verification.VerificationContext; -public class UnicityCertificateVerificationContext { +/** + * Unicity certificate verification context. + */ +public class UnicityCertificateVerificationContext implements VerificationContext { private final DataHash inputHash; private final UnicityCertificate unicityCertificate; private final RootTrustBase trustBase; + /** + * Create unicity certificate verification context. + * + * @param inputHash input record hash + * @param unicityCertificate unicity certificate + * @param trustBase root trust base + */ public UnicityCertificateVerificationContext( DataHash inputHash, UnicityCertificate unicityCertificate, @@ -21,14 +32,29 @@ public UnicityCertificateVerificationContext( this.trustBase = trustBase; } + /** + * Get input record hash. + * + * @return input record hash + */ public DataHash getInputHash() { return this.inputHash; } + /** + * Get unicity certificate. + * + * @return unicity certificate + */ public UnicityCertificate getUnicityCertificate() { return this.unicityCertificate; } + /** + * Get root trust base. + * + * @return root trust base + */ public RootTrustBase getTrustBase() { return this.trustBase; } diff --git a/src/main/java/org/unicitylabs/sdk/bft/verification/UnicityCertificateVerificationRule.java b/src/main/java/org/unicitylabs/sdk/bft/verification/UnicityCertificateVerificationRule.java index d610481..8024f36 100644 --- a/src/main/java/org/unicitylabs/sdk/bft/verification/UnicityCertificateVerificationRule.java +++ b/src/main/java/org/unicitylabs/sdk/bft/verification/UnicityCertificateVerificationRule.java @@ -5,9 +5,15 @@ import org.unicitylabs.sdk.bft.verification.rule.UnicitySealQuorumSignaturesVerificationRule; import org.unicitylabs.sdk.verification.CompositeVerificationRule; +/** + * Unicity certificate verification rule. + */ public class UnicityCertificateVerificationRule extends CompositeVerificationRule { + /** + * Create unicity certificate verification rule. + */ public UnicityCertificateVerificationRule() { super("Verify unicity certificate", new InputRecordCurrentHashVerificationRule( diff --git a/src/main/java/org/unicitylabs/sdk/bft/verification/rule/InputRecordCurrentHashVerificationRule.java b/src/main/java/org/unicitylabs/sdk/bft/verification/rule/InputRecordCurrentHashVerificationRule.java index ce5bb42..ffe9baf 100644 --- a/src/main/java/org/unicitylabs/sdk/bft/verification/rule/InputRecordCurrentHashVerificationRule.java +++ b/src/main/java/org/unicitylabs/sdk/bft/verification/rule/InputRecordCurrentHashVerificationRule.java @@ -5,13 +5,25 @@ import org.unicitylabs.sdk.verification.VerificationResult; import org.unicitylabs.sdk.verification.VerificationRule; +/** + * Input record current hash verification rule. + */ public class InputRecordCurrentHashVerificationRule extends VerificationRule { + /** + * Create the rule without any subsequent rules. + */ public InputRecordCurrentHashVerificationRule() { this(null, null); } + /** + * Create the rule with subsequent rules for success and failure. + * + * @param onSuccessRule rule to execute on success + * @param onFailureRule rule to execute on failure + */ public InputRecordCurrentHashVerificationRule( VerificationRule onSuccessRule, VerificationRule onFailureRule diff --git a/src/main/java/org/unicitylabs/sdk/bft/verification/rule/UnicitySealHashMatchesWithRootHashRule.java b/src/main/java/org/unicitylabs/sdk/bft/verification/rule/UnicitySealHashMatchesWithRootHashRule.java index 127976d..2f9b22c 100644 --- a/src/main/java/org/unicitylabs/sdk/bft/verification/rule/UnicitySealHashMatchesWithRootHashRule.java +++ b/src/main/java/org/unicitylabs/sdk/bft/verification/rule/UnicitySealHashMatchesWithRootHashRule.java @@ -1,6 +1,5 @@ package org.unicitylabs.sdk.bft.verification.rule; -import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; @@ -10,17 +9,29 @@ import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.verification.VerificationResult; import org.unicitylabs.sdk.verification.VerificationRule; +/** + * Rule to verify that the UnicitySeal hash matches the root hash of the UnicityTreeCertificate. + */ public class UnicitySealHashMatchesWithRootHashRule extends VerificationRule { + /** + * Create the rule without any subsequent rules. + */ public UnicitySealHashMatchesWithRootHashRule() { this(null, null); } + /** + * Create the rule with subsequent rules for success and failure. + * + * @param onSuccessRule rule to execute on success + * @param onFailureRule rule to execute on failure + */ public UnicitySealHashMatchesWithRootHashRule( VerificationRule onSuccessRule, VerificationRule onFailureRule @@ -34,12 +45,13 @@ public UnicitySealHashMatchesWithRootHashRule( @Override public VerificationResult verify(UnicityCertificateVerificationContext context) { - DataHash shardTreeCertificateRootHash = UnicityCertificate.calculateShardTreeCertificateRootHash( - context.getUnicityCertificate().getInputRecord(), - context.getUnicityCertificate().getTechnicalRecordHash(), - context.getUnicityCertificate().getShardConfigurationHash(), - context.getUnicityCertificate().getShardTreeCertificate() - ); + DataHash shardTreeCertificateRootHash = UnicityCertificate + .calculateShardTreeCertificateRootHash( + context.getUnicityCertificate().getInputRecord(), + context.getUnicityCertificate().getTechnicalRecordHash(), + context.getUnicityCertificate().getShardConfigurationHash(), + context.getUnicityCertificate().getShardTreeCertificate() + ); if (shardTreeCertificateRootHash == null) { return VerificationResult.fail("Could not calculate shard tree certificate root hash."); @@ -52,55 +64,48 @@ public VerificationResult verify(UnicityCertificateVerificationContext context) .putInt(unicityTreeCertificate.getPartitionIdentifier()) .array(); - try { - DataHash result = new DataHasher(HashAlgorithm.SHA256) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(new byte[]{(byte) 0x01})) // LEAF - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(key)) - .update( - UnicityObjectMapper.CBOR.writeValueAsBytes( - new DataHasher(HashAlgorithm.SHA256) - .update( - UnicityObjectMapper.CBOR.writeValueAsBytes( - shardTreeCertificateRootHash.getData() - ) - ) - .digest() - .getData() - ) - ) - .digest(); - - for (UnicityTreeCertificate.HashStep step : unicityTreeCertificate.getSteps()) { - byte[] stepKey = ByteBuffer.allocate(4) - .order(ByteOrder.BIG_ENDIAN) - .putInt(step.getKey()) - .array(); + DataHash result = new DataHasher(HashAlgorithm.SHA256) + .update(CborSerializer.encodeByteString(new byte[]{(byte) 0x01})) // LEAF + .update(CborSerializer.encodeByteString(key)) + .update( + CborSerializer.encodeByteString( + new DataHasher(HashAlgorithm.SHA256) + .update( + CborSerializer.encodeByteString(shardTreeCertificateRootHash.getData()) + ) + .digest() + .getData() + ) + ) + .digest(); - DataHasher hasher = new DataHasher(HashAlgorithm.SHA256) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(new byte[]{(byte) 0x00})) // NODE - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(stepKey)); + for (UnicityTreeCertificate.HashStep step : unicityTreeCertificate.getSteps()) { + byte[] stepKey = ByteBuffer.allocate(4) + .order(ByteOrder.BIG_ENDIAN) + .putInt(step.getKey()) + .array(); - if (Arrays.compare(key, stepKey) > 0) { - hasher - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(step.getHash())) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(result.getData())); - } else { - hasher - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(result.getData())) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(step.getHash())); - } + DataHasher hasher = new DataHasher(HashAlgorithm.SHA256) + .update(CborSerializer.encodeByteString(new byte[]{(byte) 0x00})) // NODE + .update(CborSerializer.encodeByteString(stepKey)); - result = hasher.digest(); + if (Arrays.compare(key, stepKey) > 0) { + hasher + .update(CborSerializer.encodeByteString(step.getHash())) + .update(CborSerializer.encodeByteString(result.getData())); + } else { + hasher + .update(CborSerializer.encodeByteString(result.getData())) + .update(CborSerializer.encodeByteString(step.getHash())); } - byte[] unicitySealHash = context.getUnicityCertificate().getUnicitySeal().getHash(); + result = hasher.digest(); + } - if (Arrays.compare(unicitySealHash, result.getData()) != 0) { - return VerificationResult.fail("Unicity seal hash does not match tree root."); - } - } catch (IOException e) { - // TODO: Fix message - return VerificationResult.fail(e.getMessage()); + byte[] unicitySealHash = context.getUnicityCertificate().getUnicitySeal().getHash(); + + if (Arrays.compare(unicitySealHash, result.getData()) != 0) { + return VerificationResult.fail("Unicity seal hash does not match tree root."); } return VerificationResult.success(); diff --git a/src/main/java/org/unicitylabs/sdk/bft/verification/rule/UnicitySealQuorumSignaturesVerificationRule.java b/src/main/java/org/unicitylabs/sdk/bft/verification/rule/UnicitySealQuorumSignaturesVerificationRule.java index 28effcb..9fde5d2 100644 --- a/src/main/java/org/unicitylabs/sdk/bft/verification/rule/UnicitySealQuorumSignaturesVerificationRule.java +++ b/src/main/java/org/unicitylabs/sdk/bft/verification/rule/UnicitySealQuorumSignaturesVerificationRule.java @@ -14,13 +14,25 @@ import org.unicitylabs.sdk.verification.VerificationResult; import org.unicitylabs.sdk.verification.VerificationRule; +/** + * Rule to verify that the UnicitySeal contains valid quorum signatures. + */ public class UnicitySealQuorumSignaturesVerificationRule extends VerificationRule { + /** + * Create the rule without any subsequent rules. + */ public UnicitySealQuorumSignaturesVerificationRule() { this(null, null); } + /** + * Create the rule with subsequent rules for success and failure. + * + * @param onSuccessRule rule to execute on success + * @param onFailureRule rule to execute on failure + */ public UnicitySealQuorumSignaturesVerificationRule( VerificationRule onSuccessRule, VerificationRule onFailureRule @@ -39,7 +51,7 @@ public VerificationResult verify(UnicityCertificateVerificationContext context) List results = new ArrayList<>(); DataHash hash = new DataHasher(HashAlgorithm.SHA256) - .update(UnicitySeal.fromUnicitySealWithoutSignatures(unicitySeal).encode()) + .update(unicitySeal.toCborWithoutSignatures()) .digest(); int successful = 0; for (Map.Entry entry : unicitySeal.getSignatures().entrySet()) { diff --git a/src/main/java/org/unicitylabs/sdk/hash/DataHash.java b/src/main/java/org/unicitylabs/sdk/hash/DataHash.java index 66436e0..dd5dd9a 100644 --- a/src/main/java/org/unicitylabs/sdk/hash/DataHash.java +++ b/src/main/java/org/unicitylabs/sdk/hash/DataHash.java @@ -1,14 +1,22 @@ package org.unicitylabs.sdk.hash; -import org.unicitylabs.sdk.util.HexConverter; - +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.util.Arrays; import java.util.Objects; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; +import org.unicitylabs.sdk.util.HexConverter; /** * DataHash represents a hash of data using a specific hash algorithm. */ +@JsonSerialize(using = DataHashJson.Serializer.class) +@JsonDeserialize(using = DataHashJson.Deserializer.class) public class DataHash { private final byte[] data; @@ -83,23 +91,64 @@ public byte[] getImprint() { return imprint; } + /** + * Create data hash from JSON string. + * + * @param input json string + * @return data hash + */ + public static DataHash fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, DataHash.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(DataHash.class, e); + } + } + + /** + * Convert data hash to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(DataHash.class, e); + } + } + + /** + * Create data hash from CBOR bytes. + * + * @param bytes CBOR bytes + * @return data hash + */ + public static DataHash fromCbor(byte[] bytes) { + return DataHash.fromImprint(CborDeserializer.readByteString(bytes)); + } + + /** + * Convert data hash to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeByteString(this.getImprint()); + } + @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { + if (!(o instanceof DataHash)) { return false; } - DataHash dataHash = (DataHash) o; - return Arrays.equals(this.data, dataHash.data) && this.algorithm == dataHash.algorithm; + DataHash that = (DataHash) o; + return Arrays.equals(this.data, that.data) && this.algorithm == that.algorithm; } @Override public int hashCode() { - int result = Arrays.hashCode(this.data); - result = 31 * result + (this.algorithm != null ? this.algorithm.hashCode() : 0); - return result; + return Objects.hash(this.algorithm, Arrays.hashCode(this.data)); } @Override diff --git a/src/main/java/org/unicitylabs/sdk/hash/DataHashJson.java b/src/main/java/org/unicitylabs/sdk/hash/DataHashJson.java new file mode 100644 index 0000000..0682fc4 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/hash/DataHashJson.java @@ -0,0 +1,73 @@ +package org.unicitylabs.sdk.hash; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; + +/** + * Data hash serializer and deserializer implementation. + */ +public class DataHashJson { + + private DataHashJson() { + } + + /** + * Data hash serializer. + */ + public static class Serializer extends StdSerializer { + + /** + * Create serializer. + */ + public Serializer() { + super(DataHash.class); + } + + /** + * Serialize data hash. + * + * @param value data hash + * @param gen json generator + * @param serializers serializer provider + * @throws IOException on serialization failure + */ + @Override + public void serialize(DataHash value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + gen.writeObject(value.getImprint()); + } + } + + /** + * Data hash deserializer. + */ + public static class Deserializer extends StdDeserializer { + + /** + * Create deserializer. + */ + public Deserializer() { + super(DataHash.class); + } + + /** + * Deserialize data hash. + * + * @param p Parser used for reading JSON content + * @param ctx Context that can be used to access information about this deserialization + * activity. + * @return data hash + * @throws IOException on deserialization failure + */ + @Override + public DataHash deserialize(JsonParser p, DeserializationContext ctx) throws IOException { + return DataHash.fromImprint(p.readValueAs(byte[].class)); + } + } +} + diff --git a/src/main/java/org/unicitylabs/sdk/hash/DataHasher.java b/src/main/java/org/unicitylabs/sdk/hash/DataHasher.java index 7bd1982..c48c280 100644 --- a/src/main/java/org/unicitylabs/sdk/hash/DataHasher.java +++ b/src/main/java/org/unicitylabs/sdk/hash/DataHasher.java @@ -4,49 +4,54 @@ import java.security.NoSuchAlgorithmException; /** - * DataHasher is a utility class for hashing data using a specified hash algorithm. - * It provides methods to update the hash with data and to retrieve the final hash. + * DataHasher is a utility class for hashing data using a specified hash algorithm. It provides + * methods to update the hash with data and to retrieve the final hash. */ public class DataHasher { - private final HashAlgorithm algorithm; - private final MessageDigest messageDigest; - /** - * Creates a DataHasher instance with the specified hash algorithm. - * @param algorithm the hash algorithm to use - */ - public DataHasher(HashAlgorithm algorithm) { - this.algorithm = algorithm; - try { - this.messageDigest = MessageDigest.getInstance(algorithm.getAlgorithm()); - } catch (NoSuchAlgorithmException e) { - throw new UnsupportedHashAlgorithmError(algorithm); - } - } + private final HashAlgorithm algorithm; + private final MessageDigest messageDigest; - /** - * Returns the hash algorithm used by this DataHasher. - * @return the hash algorithm - */ - public HashAlgorithm getAlgorithm() { - return algorithm; + /** + * Creates a DataHasher instance with the specified hash algorithm. + * + * @param algorithm the hash algorithm to use + */ + public DataHasher(HashAlgorithm algorithm) { + this.algorithm = algorithm; + try { + this.messageDigest = MessageDigest.getInstance(algorithm.getAlgorithm()); + } catch (NoSuchAlgorithmException e) { + throw new UnsupportedHashAlgorithmException(algorithm); } + } - /** - * Updates the digest with the given byte array. - * @param data the byte array - * @return this DataHasher instance for method chaining - */ - public DataHasher update(byte[] data) { - this.messageDigest.update(data); - return this; - } + /** + * Returns the hash algorithm used by this DataHasher. + * + * @return the hash algorithm + */ + public HashAlgorithm getAlgorithm() { + return algorithm; + } - /** - * Gets the final hash digest. - * @return the final hash as a DataHash object - */ - public DataHash digest() { - return new DataHash(this.algorithm, this.messageDigest.digest()); - } + /** + * Updates the digest with the given byte array. + * + * @param data the byte array + * @return this DataHasher instance for method chaining + */ + public DataHasher update(byte[] data) { + this.messageDigest.update(data); + return this; + } + + /** + * Gets the final hash digest. + * + * @return the final hash as a DataHash object + */ + public DataHash digest() { + return new DataHash(this.algorithm, this.messageDigest.digest()); + } } \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/hash/HashAlgorithm.java b/src/main/java/org/unicitylabs/sdk/hash/HashAlgorithm.java index db15164..c3c55bc 100644 --- a/src/main/java/org/unicitylabs/sdk/hash/HashAlgorithm.java +++ b/src/main/java/org/unicitylabs/sdk/hash/HashAlgorithm.java @@ -1,40 +1,55 @@ package org.unicitylabs.sdk.hash; +/** + * Hash algorithm representation. + */ public enum HashAlgorithm { - SHA256(0, "SHA-256"), - SHA224(1, "SHA-224"), - SHA384(2, "SHA-384"), - SHA512(3, "SHA-512"), - RIPEMD160(4, "RIPEMD160"),; + SHA256(0, "SHA-256"), + SHA224(1, "SHA-224"), + SHA384(2, "SHA-384"), + SHA512(3, "SHA-512"), + RIPEMD160(4, "RIPEMD160"), + ; - private final int value; - private final String algorithm; + private final int value; + private final String algorithm; - HashAlgorithm(int value, String algorithm) { - this.value = value; - this.algorithm = algorithm; - } + HashAlgorithm(int value, String algorithm) { + this.value = value; + this.algorithm = algorithm; + } - public int getValue() { - return value; - } + /** + * Hash algorithm value in imprint. + * + * @return value + */ + public int getValue() { + return value; + } - public String getAlgorithm() { - return this.algorithm; - } - - /** - * Get HashAlgorithm from its numeric value. - * @param value The numeric value - * @return The corresponding HashAlgorithm - * @throws IllegalArgumentException if value is not valid - */ - public static HashAlgorithm fromValue(int value) { - for (HashAlgorithm algorithm : HashAlgorithm.values()) { - if (algorithm.getValue() == value) { - return algorithm; - } - } - throw new IllegalArgumentException("Invalid HashAlgorithm value: " + value); + /** + * Hash algorithm string representation. + * + * @return algorithm + */ + public String getAlgorithm() { + return this.algorithm; + } + + /** + * Get HashAlgorithm from its numeric value. + * + * @param value The numeric value + * @return The corresponding HashAlgorithm + * @throws IllegalArgumentException if value is not valid + */ + public static HashAlgorithm fromValue(int value) { + for (HashAlgorithm algorithm : HashAlgorithm.values()) { + if (algorithm.getValue() == value) { + return algorithm; + } } + throw new IllegalArgumentException("Invalid HashAlgorithm value: " + value); + } } \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/hash/UnsupportedHashAlgorithmError.java b/src/main/java/org/unicitylabs/sdk/hash/UnsupportedHashAlgorithmError.java deleted file mode 100644 index 3b4129c..0000000 --- a/src/main/java/org/unicitylabs/sdk/hash/UnsupportedHashAlgorithmError.java +++ /dev/null @@ -1,20 +0,0 @@ - -package org.unicitylabs.sdk.hash; - -public class UnsupportedHashAlgorithmError extends RuntimeException { - private final HashAlgorithm algorithm; - - public UnsupportedHashAlgorithmError(String message) { - super(message); - this.algorithm = null; - } - - public UnsupportedHashAlgorithmError(HashAlgorithm algorithm) { - super("Unsupported hash algorithm: " + algorithm); - this.algorithm = algorithm; - } - - public HashAlgorithm getAlgorithm() { - return algorithm; - } -} diff --git a/src/main/java/org/unicitylabs/sdk/hash/UnsupportedHashAlgorithmException.java b/src/main/java/org/unicitylabs/sdk/hash/UnsupportedHashAlgorithmException.java new file mode 100644 index 0000000..7584d05 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/hash/UnsupportedHashAlgorithmException.java @@ -0,0 +1,29 @@ + +package org.unicitylabs.sdk.hash; + +/** + * Throw given error when hash algorithm is not supported by data hasher. + */ +public class UnsupportedHashAlgorithmException extends RuntimeException { + + private final HashAlgorithm algorithm; + + /** + * Create exception for given hash algorithm. + * + * @param algorithm algorithm + */ + public UnsupportedHashAlgorithmException(HashAlgorithm algorithm) { + super("Unsupported hash algorithm: " + algorithm); + this.algorithm = algorithm; + } + + /** + * Get algorithm which was not supported. + * + * @return algorithm + */ + public HashAlgorithm getAlgorithm() { + return algorithm; + } +} diff --git a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcDataError.java b/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcDataError.java deleted file mode 100644 index 720f088..0000000 --- a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcDataError.java +++ /dev/null @@ -1,20 +0,0 @@ - -package org.unicitylabs.sdk.jsonrpc; - -public class JsonRpcDataError extends Exception { - - private final JsonRpcError error; - - public JsonRpcDataError(JsonRpcError error) { - super(error.getMessage()); - this.error = error; - } - - public int getCode() { - return error.getCode(); - } - - public JsonRpcError getError() { - return error; - } -} diff --git a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcError.java b/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcError.java index e0dd13a..8ae66bf 100644 --- a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcError.java +++ b/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcError.java @@ -1,19 +1,39 @@ package org.unicitylabs.sdk.jsonrpc; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * JSON RPC response error. + */ public class JsonRpcError { private final int code; private final String message; - public JsonRpcError(int code, String message) { + @JsonCreator + JsonRpcError( + @JsonProperty("code") int code, + @JsonProperty("message") String message + ) { this.code = code; this.message = message; } + /** + * Get error code. + * + * @return error code + */ public int getCode() { return code; } + /** + * Get error message. + * + * @return error message + */ public String getMessage() { return message; } diff --git a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcHttpTransport.java b/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcHttpTransport.java index 8fbb236..26ffc9e 100644 --- a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcHttpTransport.java +++ b/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcHttpTransport.java @@ -20,9 +20,9 @@ */ public class JsonRpcHttpTransport { - private static final MediaType MEDIA_TYPE_JSON = MediaType.get("application/json; charset=utf-8"); + private static final MediaType MEDIA_TYPE_JSON = MediaType.get("application/json; charset=utf-8"); - private final String url; + private final String url; private final OkHttpClient httpClient; /** @@ -43,7 +43,12 @@ public CompletableFuture request(String method, Object params, Class r /** * Send a JSON-RPC request with optional API key. */ - public CompletableFuture request(String method, Object params, Class resultType, Map> headers) { + public CompletableFuture request( + String method, + Object params, + Class resultType, + Map> headers + ) { CompletableFuture future = new CompletableFuture<>(); try { @@ -51,13 +56,15 @@ public CompletableFuture request(String method, Object params, Class r .url(this.url) .post( RequestBody.create( - UnicityObjectMapper.JSON.writeValueAsString(new JsonRpcRequest(method, params)), + UnicityObjectMapper.JSON.writeValueAsString( + new JsonRpcRequest(method, params) + ), JsonRpcHttpTransport.MEDIA_TYPE_JSON) ); - + headers.forEach((header, values) -> - values.forEach(value -> - requestBuilder.addHeader(header, value))); + values.forEach(value -> + requestBuilder.addHeader(header, value))); Request request = requestBuilder.build(); @@ -72,18 +79,23 @@ public void onResponse(Call call, Response response) { try (ResponseBody body = response.body()) { if (!response.isSuccessful()) { String error = body != null ? body.string() : ""; - future.completeExceptionally(new JsonRpcNetworkError(response.code(), error)); + future.completeExceptionally(new JsonRpcNetworkException(response.code(), error)); return; } JsonRpcResponse data = UnicityObjectMapper.JSON.readValue( body != null ? body.string() : "", UnicityObjectMapper.JSON.getTypeFactory() - .constructParametricType(JsonRpcResponse.class, resultType)); + .constructParametricType(JsonRpcResponse.class, resultType) + ); if (data.getError() != null) { - - future.completeExceptionally(new JsonRpcDataError(data.getError())); + future.completeExceptionally( + new JsonRpcNetworkException( + data.getError().getCode(), + data.getError().getMessage() + ) + ); return; } diff --git a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcNetworkError.java b/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcNetworkError.java deleted file mode 100644 index f995203..0000000 --- a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcNetworkError.java +++ /dev/null @@ -1,22 +0,0 @@ - -package org.unicitylabs.sdk.jsonrpc; - -public class JsonRpcNetworkError extends Exception { - - private final int status; - private final String errorMessage; - - public JsonRpcNetworkError(int status, String message) { - super(String.format("Network error [%s] occurred: %s", status, message)); - this.status = status; - this.errorMessage = message; - } - - public int getStatus() { - return this.status; - } - - public String getErrorMessage() { - return this.errorMessage; - } -} diff --git a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcNetworkException.java b/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcNetworkException.java new file mode 100644 index 0000000..c00c0bf --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcNetworkException.java @@ -0,0 +1,41 @@ + +package org.unicitylabs.sdk.jsonrpc; + +/** + * JSON RPC network exception. + */ +public class JsonRpcNetworkException extends Exception { + + private final int status; + private final String errorMessage; + + /** + * Create exception from http code and error message. + * + * @param status status code + * @param message error message + */ + public JsonRpcNetworkException(int status, String message) { + super(String.format("Network error [%s] occurred: %s", status, message)); + this.status = status; + this.errorMessage = message; + } + + /** + * Get status code. + * + * @return status code + */ + public int getStatus() { + return this.status; + } + + /** + * Get error message. + * + * @return error message + */ + public String getErrorMessage() { + return this.errorMessage; + } +} diff --git a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcRequest.java b/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcRequest.java index 9df01f4..4e06a39 100644 --- a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcRequest.java +++ b/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcRequest.java @@ -1,35 +1,80 @@ package org.unicitylabs.sdk.jsonrpc; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.UUID; +/** + * JSON RPC request. + */ public class JsonRpcRequest { private final UUID id; private final String method; private final Object params; + /** + * Create JSON RPC request. + * + * @param method request method + * @param params request parameters + */ public JsonRpcRequest(String method, Object params) { this(UUID.randomUUID(), method, params); } - public JsonRpcRequest(UUID id, String method, Object params) { + /** + * Create JSON RPC request. + * + * @param id request id + * @param method request method + * @param params request error + */ + @JsonCreator + public JsonRpcRequest( + @JsonProperty("id") UUID id, + @JsonProperty("method") String method, + @JsonProperty("params") Object params + ) { this.id = id; this.method = method; this.params = params; } + /** + * Get request ID. + * + * @return id + */ public UUID getId() { return this.id; } + /** + * Get request version. + * + * @return version + */ + @JsonGetter("jsonrpc") public String getVersion() { return "2.0"; } + /** + * Get request method. + * + * @return method + */ public String getMethod() { return this.method; } + /** + * Get request parameters. + * + * @return parameters + */ public Object getParams() { return this.params; } diff --git a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcResponse.java b/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcResponse.java index 3b34f45..89df5ba 100644 --- a/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcResponse.java +++ b/src/main/java/org/unicitylabs/sdk/jsonrpc/JsonRpcResponse.java @@ -1,8 +1,15 @@ package org.unicitylabs.sdk.jsonrpc; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; import java.util.UUID; +/** + * JSON RPC response structure. + * + * @param response type + */ public class JsonRpcResponse { private final String version; @@ -10,29 +17,55 @@ public class JsonRpcResponse { private final JsonRpcError error; private final UUID id; - public JsonRpcResponse( - String version, - T result, - JsonRpcError error, - UUID id) { + @JsonCreator + JsonRpcResponse( + @JsonProperty("jsonrpc") String version, + @JsonProperty("result") T result, + @JsonProperty("error") JsonRpcError error, + @JsonProperty("id") UUID id + ) { + if (!"2.0".equals(version)) { + throw new IllegalArgumentException("Invalid JSON-RPC version: " + version); + } + this.version = version; this.result = result; this.error = error; this.id = id; } + /** + * Get JSON RPC version. + * + * @return version + */ public String getVersion() { return this.version; } + /** + * Get result if exists. + * + * @return result + */ public T getResult() { return this.result; } + /** + * Get error if exists. + * + * @return error + */ public JsonRpcError getError() { return this.error; } + /** + * Get id. + * + * @return id + */ public UUID getId() { return this.id; } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/BranchExistsException.java b/src/main/java/org/unicitylabs/sdk/mtree/BranchExistsException.java index 3804229..b5694b7 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/BranchExistsException.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/BranchExistsException.java @@ -1,7 +1,14 @@ package org.unicitylabs.sdk.mtree; +/** + * Exception thrown when a branch already exists at a given path in the merkle tree. + */ public class BranchExistsException extends Exception { - public BranchExistsException() { - super("Branch already exists at this path."); - } + + /** + * Create exception indicating that a branch already exists at the specified path. + */ + public BranchExistsException() { + super("Branch already exists at this path."); + } } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/CommonPath.java b/src/main/java/org/unicitylabs/sdk/mtree/CommonPath.java index 3582b85..177438b 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/CommonPath.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/CommonPath.java @@ -3,20 +3,33 @@ import java.math.BigInteger; import java.util.Objects; +/** + * Common path for two nodes in a merkle tree. + */ public class CommonPath { private final BigInteger path; private final int length; - public CommonPath(BigInteger path, int length) { + CommonPath(BigInteger path, int length) { this.path = path; this.length = length; } + /** + * Get common path. + * + * @return common path + */ public BigInteger getPath() { return this.path; } + /** + * Get length of the common path. + * + * @return length of the common path + */ public int getLength() { return this.length; } @@ -35,6 +48,13 @@ public int hashCode() { return Objects.hash(path, length); } + /** + * Create common path for two paths. + * + * @param path1 first path + * @param path2 second path + * @return common path + */ public static CommonPath create(BigInteger path1, BigInteger path2) { BigInteger path = BigInteger.ONE; BigInteger mask = BigInteger.ONE; diff --git a/src/main/java/org/unicitylabs/sdk/mtree/LeafOutOfBoundsException.java b/src/main/java/org/unicitylabs/sdk/mtree/LeafOutOfBoundsException.java index b816a3e..d392a44 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/LeafOutOfBoundsException.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/LeafOutOfBoundsException.java @@ -1,7 +1,14 @@ package org.unicitylabs.sdk.mtree; +/** + * Exception when leaf is out of bounds. + */ public class LeafOutOfBoundsException extends Exception { - public LeafOutOfBoundsException() { - super("Cannot extend tree through leaf."); - } + + /** + * Create exception. + */ + public LeafOutOfBoundsException() { + super("Cannot extend tree through leaf."); + } } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/MerkleTreePathVerificationResult.java b/src/main/java/org/unicitylabs/sdk/mtree/MerkleTreePathVerificationResult.java index b2a2781..2613d0a 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/MerkleTreePathVerificationResult.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/MerkleTreePathVerificationResult.java @@ -2,26 +2,50 @@ import java.util.Objects; +/** + * Merkle tree path verification result. + */ public class MerkleTreePathVerificationResult { private final boolean pathValid; private final boolean pathIncluded; + /** + * Create merkle tree path verification result. + * + * @param pathValid is path valid + * @param pathIncluded is path included for given state id + */ public MerkleTreePathVerificationResult(boolean pathValid, boolean pathIncluded) { this.pathValid = pathValid; this.pathIncluded = pathIncluded; } + /** + * Is path valid. + * + * @return true if path is valid + */ public boolean isPathValid() { - return pathValid; + return this.pathValid; } + /** + * Is path included for given state id. + * + * @return true if is included + */ public boolean isPathIncluded() { - return pathIncluded; + return this.pathIncluded; } - public boolean isValid() { - return pathValid && pathIncluded; + /** + * Is verification successful. + * + * @return true if successful + */ + public boolean isSuccessful() { + return this.pathValid && this.pathIncluded; } @Override diff --git a/src/main/java/org/unicitylabs/sdk/mtree/PathVerificationResult.java b/src/main/java/org/unicitylabs/sdk/mtree/PathVerificationResult.java index 28f0bf8..aed9ca5 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/PathVerificationResult.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/PathVerificationResult.java @@ -1,6 +1,9 @@ package org.unicitylabs.sdk.mtree; +/** + * Result of path verification in a merkle tree. + */ public enum PathVerificationResult { OK, PATH_NOT_INCLUDED, diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/Branch.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/Branch.java index 0431634..2cb6f43 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/Branch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/Branch.java @@ -1,9 +1,25 @@ package org.unicitylabs.sdk.mtree.plain; -import org.unicitylabs.sdk.hash.HashAlgorithm; import java.math.BigInteger; +import org.unicitylabs.sdk.hash.HashAlgorithm; +/** + * Sparse merkle tree branch structure. + */ interface Branch { - BigInteger getPath(); - FinalizedBranch finalize(HashAlgorithm hashAlgorithm); + + /** + * Get branch path from leaf to root. + * + * @return path + */ + BigInteger getPath(); + + /** + * Finalize current branch. + * + * @param hashAlgorithm hash algorithm + * @return finalized branch + */ + FinalizedBranch finalize(HashAlgorithm hashAlgorithm); } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/FinalizedBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/FinalizedBranch.java index 5483ab7..a6c884f 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/FinalizedBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/FinalizedBranch.java @@ -2,6 +2,15 @@ import org.unicitylabs.sdk.hash.DataHash; +/** + * Finalized branch in sparse merkle tree. + */ interface FinalizedBranch extends Branch { - DataHash getHash(); + + /** + * Get hash of the branch. + * + * @return hash + */ + DataHash getHash(); } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/FinalizedLeafBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/FinalizedLeafBranch.java index e19b578..b25602a 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/FinalizedLeafBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/FinalizedLeafBranch.java @@ -1,13 +1,16 @@ package org.unicitylabs.sdk.mtree.plain; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Objects; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.util.BigIntegerConverter; -import java.math.BigInteger; -import java.util.Arrays; -import java.util.Objects; +/** + * Finalized leaf branch in a sparse merkle tree. + */ class FinalizedLeafBranch implements LeafBranch, FinalizedBranch { private final BigInteger path; @@ -20,6 +23,14 @@ private FinalizedLeafBranch(BigInteger path, byte[] value, DataHash hash) { this.hash = hash; } + /** + * Create a finalized leaf branch. + * + * @param path path of the branch + * @param value value stored in the leaf + * @param hashAlgorithm hash algorithm to use + * @return finalized leaf branch + */ public static FinalizedLeafBranch create(BigInteger path, byte[] value, HashAlgorithm hashAlgorithm) { DataHash hash = new DataHasher(hashAlgorithm) @@ -29,18 +40,22 @@ public static FinalizedLeafBranch create(BigInteger path, byte[] value, return new FinalizedLeafBranch(path, value, hash); } + @Override public BigInteger getPath() { return this.path; } + @Override public byte[] getValue() { return Arrays.copyOf(this.value, this.value.length); } + @Override public DataHash getHash() { return this.hash; } + @Override public FinalizedLeafBranch finalize(HashAlgorithm hashAlgorithm) { return this; // Already finalized } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/FinalizedNodeBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/FinalizedNodeBranch.java index 605c5dc..30c2050 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/FinalizedNodeBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/FinalizedNodeBranch.java @@ -1,12 +1,15 @@ package org.unicitylabs.sdk.mtree.plain; +import java.math.BigInteger; +import java.util.Objects; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.util.BigIntegerConverter; -import java.math.BigInteger; -import java.util.Objects; +/** + * Finalized node branch in a sparse merkle tree. + */ class FinalizedNodeBranch implements NodeBranch, FinalizedBranch { private final BigInteger path; @@ -24,6 +27,15 @@ private FinalizedNodeBranch(BigInteger path, FinalizedBranch left, FinalizedBran this.hash = hash; } + /** + * Create a finalized node branch. + * + * @param path path of the branch + * @param left left branch + * @param right right branch + * @param hashAlgorithm hash algorithm to use + * @return finalized node branch + */ public static FinalizedNodeBranch create(BigInteger path, FinalizedBranch left, FinalizedBranch right, HashAlgorithm hashAlgorithm) { DataHash childrenHash = new DataHasher(hashAlgorithm) @@ -39,26 +51,36 @@ public static FinalizedNodeBranch create(BigInteger path, FinalizedBranch left, return new FinalizedNodeBranch(path, left, right, childrenHash, hash); } + @Override public BigInteger getPath() { return this.path; } + @Override public FinalizedBranch getLeft() { return this.left; } + @Override public FinalizedBranch getRight() { return this.right; } + /** + * Get hash of the children (left and right). + * + * @return children hash + */ public DataHash getChildrenHash() { return this.childrenHash; } + @Override public DataHash getHash() { return this.hash; } + @Override public FinalizedNodeBranch finalize(HashAlgorithm hashAlgorithm) { return this; // Already finalized } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/LeafBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/LeafBranch.java index 488e5ad..f8650e5 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/LeafBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/LeafBranch.java @@ -1,5 +1,14 @@ package org.unicitylabs.sdk.mtree.plain; +/** + * Leaf branch in a sparse merkle tree. + */ interface LeafBranch extends Branch { - byte[] getValue(); + + /** + * Get value stored in the leaf. + * + * @return value stored in the leaf + */ + byte[] getValue(); } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/NodeBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/NodeBranch.java index 74c7164..2c32323 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/NodeBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/NodeBranch.java @@ -1,7 +1,21 @@ package org.unicitylabs.sdk.mtree.plain; +/** + * Node branch in merkle tree. + */ interface NodeBranch extends Branch { - Branch getLeft(); - Branch getRight(); + /** + * Get left branch. + * + * @return left branch + */ + Branch getLeft(); + + /** + * Get right branch. + * + * @return right branch + */ + Branch getRight(); } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/PendingLeafBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/PendingLeafBranch.java index ed35e62..88022ab 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/PendingLeafBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/PendingLeafBranch.java @@ -1,28 +1,40 @@ package org.unicitylabs.sdk.mtree.plain; -import org.unicitylabs.sdk.hash.HashAlgorithm; import java.math.BigInteger; import java.util.Arrays; import java.util.Objects; +import org.unicitylabs.sdk.hash.HashAlgorithm; +/** + * Pending leaf branch in a sparse merkle tree. + */ class PendingLeafBranch implements LeafBranch { private final BigInteger path; private final byte[] value; + /** + * Create a pending leaf branch. + * + * @param path path of the branch + * @param value value stored in the leaf + */ public PendingLeafBranch(BigInteger path, byte[] value) { this.path = path; this.value = value; } + @Override public BigInteger getPath() { return this.path; } + @Override public byte[] getValue() { return Arrays.copyOf(this.value, this.value.length); } + @Override public FinalizedLeafBranch finalize(HashAlgorithm hashAlgorithm) { return FinalizedLeafBranch.create(this.path, this.value, hashAlgorithm); } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/PendingNodeBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/PendingNodeBranch.java index d8af437..5198e78 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/PendingNodeBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/PendingNodeBranch.java @@ -1,33 +1,47 @@ package org.unicitylabs.sdk.mtree.plain; -import org.unicitylabs.sdk.hash.HashAlgorithm; import java.math.BigInteger; import java.util.Objects; +import org.unicitylabs.sdk.hash.HashAlgorithm; +/** + * Pending node branch in a sparse merkle tree. + */ class PendingNodeBranch implements NodeBranch { private final BigInteger path; private final Branch left; private final Branch right; + /** + * Create a pending node branch. + * + * @param path path of the branch + * @param left left branch + * @param right right branch + */ public PendingNodeBranch(BigInteger path, Branch left, Branch right) { this.path = path; this.left = left; this.right = right; } + @Override public BigInteger getPath() { return this.path; } + @Override public Branch getLeft() { return this.left; } + @Override public Branch getRight() { return this.right; } + @Override public FinalizedNodeBranch finalize(HashAlgorithm hashAlgorithm) { return FinalizedNodeBranch.create(this.path, this.left.finalize(hashAlgorithm), this.right.finalize(hashAlgorithm), hashAlgorithm); diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTree.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTree.java index 5798e48..96abb35 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTree.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTree.java @@ -1,12 +1,15 @@ package org.unicitylabs.sdk.mtree.plain; +import java.math.BigInteger; +import java.util.Arrays; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.mtree.BranchExistsException; import org.unicitylabs.sdk.mtree.CommonPath; import org.unicitylabs.sdk.mtree.LeafOutOfBoundsException; -import java.math.BigInteger; -import java.util.Arrays; +/** + * Sparse Merkle tree implementation. + */ public class SparseMerkleTree { private Branch left = null; @@ -14,10 +17,24 @@ public class SparseMerkleTree { private final HashAlgorithm hashAlgorithm; + /** + * Create sparse Merkle tree with given hash algorithm. + * + * @param hashAlgorithm hash algorithm + */ public SparseMerkleTree(HashAlgorithm hashAlgorithm) { this.hashAlgorithm = hashAlgorithm; } + /** + * Add leaf to the tree at given path. + * + * @param path path of the leaf + * @param data data of the leaf + * @throws BranchExistsException if branch already exists at the path + * @throws LeafOutOfBoundsException if leaf is out of bounds + * @throws IllegalArgumentException if path is less than 1 + */ public synchronized void addLeaf(BigInteger path, byte[] data) throws BranchExistsException, LeafOutOfBoundsException { if (path.compareTo(BigInteger.ONE) < 0) { @@ -37,6 +54,11 @@ public synchronized void addLeaf(BigInteger path, byte[] data) } } + /** + * Calculate root of the tree. + * + * @return root node and its state + */ public synchronized SparseMerkleTreeRootNode calculateRoot() { FinalizedBranch left = this.left != null ? this.left.finalize(this.hashAlgorithm) : null; FinalizedBranch right = this.right != null ? this.right.finalize(this.hashAlgorithm) : null; diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePath.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePath.java index 8c01fa0..9a1af96 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePath.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePath.java @@ -1,20 +1,38 @@ package org.unicitylabs.sdk.mtree.plain; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import java.math.BigInteger; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.mtree.MerkleTreePathVerificationResult; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; import org.unicitylabs.sdk.util.BigIntegerConverter; -import java.math.BigInteger; -import java.util.List; -import java.util.Objects; +/** + * Sparse merkle tree path for selected path. + */ public class SparseMerkleTreePath { private final DataHash rootHash; private final List steps; - public SparseMerkleTreePath(DataHash rootHash, List steps) { + @JsonCreator + SparseMerkleTreePath( + @JsonProperty("root") + DataHash rootHash, + @JsonProperty("steps") + List steps + ) { Objects.requireNonNull(rootHash, "rootHash cannot be null"); Objects.requireNonNull(steps, "steps cannot be null"); @@ -22,14 +40,31 @@ public SparseMerkleTreePath(DataHash rootHash, List st this.steps = List.copyOf(steps); } + /** + * Get root hash. + * + * @return root hash + */ + @JsonGetter("root") public DataHash getRootHash() { return this.rootHash; } + /** + * Get steps to root. + * + * @return steps + */ public List getSteps() { return this.steps; } + /** + * Verify merkle tree path against given path. + * + * @param requestId path + * @return true if successful + */ public MerkleTreePathVerificationResult verify(BigInteger requestId) { BigInteger currentPath = BigInteger.ONE; // Root path is always 1 DataHash currentHash = null; @@ -55,7 +90,8 @@ public MerkleTreePathVerificationResult verify(BigInteger requestId) { .or(step.getPath().and(BigInteger.ONE.shiftLeft(length).subtract(BigInteger.ONE))); } - byte[] siblingHash = step.getSibling().map(SparseMerkleTreePathStep.Branch::getValue).orElse(new byte[]{0}); + byte[] siblingHash = step.getSibling().map(SparseMerkleTreePathStep.Branch::getValue) + .orElse(new byte[]{0}); boolean isRight = step.getPath().testBit(0); currentHash = new DataHasher(HashAlgorithm.SHA256).update(isRight ? siblingHash : hash) .update(isRight ? hash : siblingHash).digest(); @@ -65,6 +101,66 @@ public MerkleTreePathVerificationResult verify(BigInteger requestId) { currentPath.equals(requestId)); } + /** + * Create sparse merkle tree path from CBOR bytes. + * + * @param bytes CBOR bytes + * @return path + */ + public static SparseMerkleTreePath fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new SparseMerkleTreePath( + DataHash.fromCbor(data.get(0)), + CborDeserializer.readArray(data.get(1)).stream() + .map(SparseMerkleTreePathStep::fromCbor) + .collect(Collectors.toList()) + ); + } + + /** + * Convert sparse merkle tree path to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + this.rootHash.toCbor(), + CborSerializer.encodeArray( + this.steps.stream() + .map(SparseMerkleTreePathStep::toCbor) + .toArray(byte[][]::new) + ) + ); + } + + /** + * Create sparse merkle tree path from JSON string. + * + * @param input JSON string + * @return path + */ + public static SparseMerkleTreePath fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, SparseMerkleTreePath.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(SparseMerkleTreePath.class, e); + } + } + + /** + * Convert sparse merkle tree path to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(SparseMerkleTreePath.class, e); + } + } + @Override public boolean equals(Object o) { if (!(o instanceof SparseMerkleTreePath)) { diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePathStep.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePathStep.java index d670c63..720b77b 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePathStep.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePathStep.java @@ -1,19 +1,29 @@ package org.unicitylabs.sdk.mtree.plain; -import org.unicitylabs.sdk.util.HexConverter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.math.BigInteger; import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.Optional; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.util.BigIntegerConverter; +import org.unicitylabs.sdk.util.HexConverter; +/** + * Sparse Merkle tree path step. + */ +@JsonSerialize(using = SparseMerkleTreePathStepJson.Serializer.class) +@JsonDeserialize(using = SparseMerkleTreePathStepJson.Deserializer.class) public class SparseMerkleTreePathStep { private final BigInteger path; private final Branch sibling; private final Branch branch; - SparseMerkleTreePathStep(BigInteger path, FinalizedBranch sibling, - FinalizedLeafBranch branch) { + SparseMerkleTreePathStep(BigInteger path, FinalizedBranch sibling, FinalizedLeafBranch branch) { this( path, sibling, @@ -41,7 +51,7 @@ public class SparseMerkleTreePathStep { ); } - public SparseMerkleTreePathStep(BigInteger path, Branch sibling, Branch branch) { + SparseMerkleTreePathStep(BigInteger path, Branch sibling, Branch branch) { Objects.requireNonNull(path, "path cannot be null"); this.path = path; @@ -49,18 +59,62 @@ public SparseMerkleTreePathStep(BigInteger path, Branch sibling, Branch branch) this.branch = branch; } + /** + * Get path. + * + * @return path + */ public BigInteger getPath() { return this.path; } + /** + * Get sibling branch. + * + * @return sibling branch + */ public Optional getSibling() { return Optional.ofNullable(this.sibling); } + /** + * Get branch. + * + * @return branch + */ public Optional getBranch() { return Optional.ofNullable(this.branch); } + /** + * Create sparse Merkle tree path step from CBOR bytes. + * + * @param bytes CBOR bytes + * @return sparse Merkle tree path step + */ + public static SparseMerkleTreePathStep fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new SparseMerkleTreePathStep( + BigIntegerConverter.decode(CborDeserializer.readByteString(data.get(0))), + Branch.fromCbor(data.get(1)), + Branch.fromCbor(data.get(2)) + ); + } + + /** + * Convert sparse Merkle tree path step to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + CborSerializer.encodeByteString(BigIntegerConverter.encode(this.path)), + CborSerializer.encodeOptional(this.sibling, Branch::toCbor), + CborSerializer.encodeOptional(this.branch, Branch::toCbor) + ); + } + @Override public boolean equals(Object o) { if (!(o instanceof SparseMerkleTreePathStep)) { @@ -82,18 +136,51 @@ public String toString() { this.path.toString(2), this.sibling, this.branch); } + /** + * Sparse Merkle tree branch. + */ public static class Branch { private final byte[] value; - - public Branch(byte[] value) { + + Branch(byte[] value) { this.value = value == null ? null : Arrays.copyOf(value, value.length); } + /** + * Get branch value. + * + * @return value + */ public byte[] getValue() { return this.value == null ? null : Arrays.copyOf(this.value, this.value.length); } + /** + * Create branch from CBOR bytes. + * + * @param bytes CBOR bytes + * @return branch + */ + public static Branch fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new Branch( + CborDeserializer.readByteString(data.get(0)) + ); + } + + /** + * Convert branch to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + CborSerializer.encodeByteString(this.value) + ); + } + @Override public boolean equals(Object o) { if (!(o instanceof Branch)) { diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePathStepJson.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePathStepJson.java new file mode 100644 index 0000000..1de95f8 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePathStepJson.java @@ -0,0 +1,148 @@ +package org.unicitylabs.sdk.mtree.plain; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Sparse merkle tree path step serializer and deserializer implementation. + */ +public class SparseMerkleTreePathStepJson { + + private SparseMerkleTreePathStepJson() { + } + + /** + * Sparse merkle tree path step serializer. + */ + public static class Serializer extends StdSerializer { + + /** + * Create serializer. + */ + public Serializer() { + super(SparseMerkleTreePathStep.class); + } + + /** + * Serialize sparse merkle tree path step. + * + * @param value sparse merkle tree path step + * @param gen json generator + * @param serializers serializer provider + * @throws IOException on serialization failure + */ + @Override + public void serialize(SparseMerkleTreePathStep value, JsonGenerator gen, + SerializerProvider serializers) + throws IOException { + gen.writeStartArray(); + gen.writeObject(value.getPath().toString()); + if (value.getSibling().isPresent()) { + gen.writeStartArray(); + gen.writeObject(value.getSibling().get().getValue()); + gen.writeEndArray(); + } else { + gen.writeNull(); + } + if (value.getBranch().isPresent()) { + gen.writeStartArray(); + gen.writeObject(value.getBranch().get().getValue()); + gen.writeEndArray(); + } else { + gen.writeNull(); + } + gen.writeEndArray(); + } + } + + /** + * Sparse merkle tree path step deserializer. + */ + public static class Deserializer extends StdDeserializer { + + /** + * Create deserializer. + */ + public Deserializer() { + super(SparseMerkleTreePathStep.class); + } + + private static SparseMerkleTreePathStep.Branch parseBranch(JsonParser p) throws IOException { + p.nextToken(); + + if (p.currentToken() == JsonToken.VALUE_NULL) { + return null; + } + + if (p.currentToken() != JsonToken.START_ARRAY) { + throw MismatchedInputException.from( + p, + SparseMerkleTreePathStep.Branch.class, + "Expected start of array" + ); + } + p.nextToken(); + SparseMerkleTreePathStep.Branch branch = new SparseMerkleTreePathStep.Branch( + p.readValueAs(byte[].class) + ); + + if (p.nextToken() != JsonToken.END_ARRAY) { + throw MismatchedInputException.from( + p, + SparseMerkleTreePathStep.Branch.class, + "Expected end of array" + ); + } + + return branch; + } + + /** + * Deserialize sparse merkle tree path step. + * + * @param p Parser used for reading JSON content + * @param ctx Context that can be used to access information about this deserialization + * activity. + * @return sparse merkle tree path step + * @throws IOException on deserialization failure + */ + @Override + public SparseMerkleTreePathStep deserialize(JsonParser p, DeserializationContext ctx) + throws IOException { + if (p.getCurrentToken() != JsonToken.START_ARRAY) { + throw MismatchedInputException.from( + p, + SparseMerkleTreePathStep.class, + "Expected start of array" + ); + } + p.nextToken(); + + SparseMerkleTreePathStep step = new SparseMerkleTreePathStep( + new BigInteger(p.readValueAs(String.class)), + SparseMerkleTreePathStepJson + .Deserializer.parseBranch(p), + SparseMerkleTreePathStepJson + .Deserializer.parseBranch(p)); + + if (p.nextToken() != JsonToken.END_ARRAY) { + throw MismatchedInputException.from( + p, + SparseMerkleTreePathStep.class, + "Expected end of array" + ); + } + + return step; + } + } +} + diff --git a/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreeRootNode.java b/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreeRootNode.java index 3b1456e..a498c74 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreeRootNode.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreeRootNode.java @@ -1,15 +1,18 @@ package org.unicitylabs.sdk.mtree.plain; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.hash.DataHasher; -import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.mtree.CommonPath; import java.math.BigInteger; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.hash.DataHasher; +import org.unicitylabs.sdk.hash.HashAlgorithm; +import org.unicitylabs.sdk.mtree.CommonPath; +/** + * Sparse merkle tree state for given root. + */ public class SparseMerkleTreeRootNode { private final BigInteger path = BigInteger.ONE; // Root path is always 0 @@ -34,10 +37,21 @@ static SparseMerkleTreeRootNode create( return new SparseMerkleTreeRootNode(left, right, rootHash); } + /** + * Get root hash. + * + * @return root hash + */ public DataHash getRootHash() { return this.rootHash; } + /** + * Get merkle tree path for requested path. + * + * @param path path + * @return merkle tree path + */ public SparseMerkleTreePath getPath(BigInteger path) { return new SparseMerkleTreePath(this.rootHash, SparseMerkleTreeRootNode.generatePath(path, this.left, this.right)); diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/Branch.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/Branch.java index a37821b..1818db3 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/Branch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/Branch.java @@ -1,9 +1,25 @@ package org.unicitylabs.sdk.mtree.sum; -import org.unicitylabs.sdk.hash.HashAlgorithm; import java.math.BigInteger; +import org.unicitylabs.sdk.hash.HashAlgorithm; +/** + * Branch in a sparse merkle sum tree. + */ interface Branch { - BigInteger getPath(); - FinalizedBranch finalize(HashAlgorithm hashAlgorithm); + + /** + * Get path of the branch. + * + * @return path + */ + BigInteger getPath(); + + /** + * Finalize the branch by computing its hash. + * + * @param hashAlgorithm hash algorithm to use + * @return finalized branch + */ + FinalizedBranch finalize(HashAlgorithm hashAlgorithm); } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedBranch.java index c229285..ad8475a 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedBranch.java @@ -1,9 +1,24 @@ package org.unicitylabs.sdk.mtree.sum; -import org.unicitylabs.sdk.hash.DataHash; import java.math.BigInteger; +import org.unicitylabs.sdk.hash.DataHash; +/** + * Finalized branch in sparse merkle sum tree. + */ interface FinalizedBranch extends Branch { - DataHash getHash(); - BigInteger getCounter(); + + /** + * Get hash of the branch. + * + * @return hash + */ + DataHash getHash(); + + /** + * Get counter of the branch. + * + * @return counter + */ + BigInteger getCounter(); } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedLeafBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedLeafBranch.java index d0e9285..50ba112 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedLeafBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedLeafBranch.java @@ -1,16 +1,17 @@ package org.unicitylabs.sdk.mtree.sum; -import com.fasterxml.jackson.core.JsonProcessingException; +import java.math.BigInteger; +import java.util.Objects; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTree.LeafValue; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.util.BigIntegerConverter; -import java.math.BigInteger; -import java.util.Objects; +/** + * Finalized leaf branch in a sparse merkle sum tree. + */ class FinalizedLeafBranch implements LeafBranch, FinalizedBranch { private final BigInteger path; @@ -23,39 +24,52 @@ private FinalizedLeafBranch(BigInteger path, LeafValue value, DataHash hash) { this.hash = hash; } - public static FinalizedLeafBranch create(BigInteger path, LeafValue value, - HashAlgorithm hashAlgorithm) { - try { - DataHash hash = new DataHasher(hashAlgorithm) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes( - UnicityObjectMapper.CBOR.createArrayNode() - .add(BigIntegerConverter.encode(path)) - .add(value.getValue()) - .add(BigIntegerConverter.encode(value.getCounter())) - )) - .digest(); - return new FinalizedLeafBranch(path, value, hash); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + /** + * Create a finalized leaf branch. + * + * @param path path of the branch + * @param value value stored in the leaf + * @param hashAlgorithm hash algorithm to use + * @return finalized leaf branch + */ + public static FinalizedLeafBranch create( + BigInteger path, + LeafValue value, + HashAlgorithm hashAlgorithm + ) { + DataHash hash = new DataHasher(hashAlgorithm) + .update( + CborSerializer.encodeArray( + CborSerializer.encodeByteString(BigIntegerConverter.encode(path)), + CborSerializer.encodeByteString(value.getValue()), + CborSerializer.encodeByteString(BigIntegerConverter.encode(value.getCounter())) + ) + ) + .digest(); + return new FinalizedLeafBranch(path, value, hash); } + @Override public BigInteger getPath() { return this.path; } + @Override public LeafValue getValue() { return this.value; } + @Override public BigInteger getCounter() { return this.value.getCounter(); } + @Override public DataHash getHash() { return this.hash; } + @Override public FinalizedLeafBranch finalize(HashAlgorithm hashAlgorithm) { return this; // Already finalized } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedNodeBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedNodeBranch.java index 3827930..e3301dd 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedNodeBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedNodeBranch.java @@ -1,15 +1,16 @@ package org.unicitylabs.sdk.mtree.sum; -import com.fasterxml.jackson.core.JsonProcessingException; +import java.math.BigInteger; +import java.util.Objects; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.util.BigIntegerConverter; -import java.math.BigInteger; -import java.util.Objects; +/** + * Finalized node branch in a sparse merkle sum tree. + */ class FinalizedNodeBranch implements NodeBranch, FinalizedBranch { private final BigInteger path; @@ -29,66 +30,86 @@ private FinalizedNodeBranch(BigInteger path, FinalizedBranch left, FinalizedBran this.hash = hash; } - public static FinalizedNodeBranch create(BigInteger path, FinalizedBranch left, - FinalizedBranch right, HashAlgorithm hashAlgorithm) { - try { - DataHash childrenHash = new DataHasher(hashAlgorithm) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes( - UnicityObjectMapper.CBOR.createArrayNode() - .add( - UnicityObjectMapper.CBOR.createArrayNode() - .add(left.getHash().getImprint()) - .add(BigIntegerConverter.encode(left.getCounter())) - ) - .add( - UnicityObjectMapper.CBOR.createArrayNode() - .add(right.getHash().getImprint()) - .add(BigIntegerConverter.encode(right.getCounter())) - ) - ) - ) - .digest(); - - BigInteger counter = left.getCounter().add(right.getCounter()); - DataHash hash = new DataHasher(hashAlgorithm) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes( - UnicityObjectMapper.CBOR.createArrayNode() - .add(BigIntegerConverter.encode(path)) - .add(childrenHash.getImprint()) - .add(BigIntegerConverter.encode(counter)) - )) - .digest(); - - return new FinalizedNodeBranch(path, left, right, counter, childrenHash, hash); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + /** + * Create a finalized node branch. + * + * @param path path of the branch + * @param left left branch + * @param right right branch + * @param hashAlgorithm hash algorithm to use + * @return finalized node branch + */ + public static FinalizedNodeBranch create( + BigInteger path, + FinalizedBranch left, + FinalizedBranch right, + HashAlgorithm hashAlgorithm + ) { + DataHash childrenHash = new DataHasher(hashAlgorithm) + .update( + CborSerializer.encodeArray( + CborSerializer.encodeArray( + CborSerializer.encodeByteString(left.getHash().getImprint()), + CborSerializer.encodeByteString(BigIntegerConverter.encode(left.getCounter())) + ), + CborSerializer.encodeArray( + CborSerializer.encodeByteString(right.getHash().getImprint()), + CborSerializer.encodeByteString( + BigIntegerConverter.encode(right.getCounter())) + ) + ) + ) + .digest(); + + BigInteger counter = left.getCounter().add(right.getCounter()); + DataHash hash = new DataHasher(hashAlgorithm) + .update( + CborSerializer.encodeArray( + CborSerializer.encodeByteString(BigIntegerConverter.encode(path)), + CborSerializer.encodeByteString(childrenHash.getImprint()), + CborSerializer.encodeByteString(BigIntegerConverter.encode(counter)) + ) + ) + .digest(); + + return new FinalizedNodeBranch(path, left, right, counter, childrenHash, hash); } + @Override public BigInteger getPath() { return this.path; } + @Override public FinalizedBranch getLeft() { return this.left; } + @Override public FinalizedBranch getRight() { return this.right; } + @Override public BigInteger getCounter() { return this.counter; } + /** + * Get hash of the children (left and right). + * + * @return children hash + */ public DataHash getChildrenHash() { return this.childrenHash; } + @Override public DataHash getHash() { return this.hash; } + @Override public FinalizedNodeBranch finalize(HashAlgorithm hashAlgorithm) { return this; // Already finalized } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/LeafBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/LeafBranch.java index 4b11c39..b57e43a 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/LeafBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/LeafBranch.java @@ -2,6 +2,15 @@ import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTree.LeafValue; +/** + * Leaf branch in a sparse merkle sum tree. + */ interface LeafBranch extends Branch { - LeafValue getValue(); + + /** + * Get value stored in the leaf. + * + * @return value stored in the leaf + */ + LeafValue getValue(); } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/NodeBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/NodeBranch.java index 9f58af7..1ba9a2b 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/NodeBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/NodeBranch.java @@ -1,7 +1,21 @@ package org.unicitylabs.sdk.mtree.sum; +/** + * Node branch in sparse merkle sum tree. + */ interface NodeBranch extends Branch { - Branch getLeft(); - Branch getRight(); + /** + * Get left branch. + * + * @return left branch + */ + Branch getLeft(); + + /** + * Get right branch. + * + * @return right branch + */ + Branch getRight(); } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/PendingLeafBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/PendingLeafBranch.java index e384c41..bbdeec0 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/PendingLeafBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/PendingLeafBranch.java @@ -1,28 +1,40 @@ package org.unicitylabs.sdk.mtree.sum; -import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTree.LeafValue; import java.math.BigInteger; import java.util.Objects; +import org.unicitylabs.sdk.hash.HashAlgorithm; +import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTree.LeafValue; +/** + * Pending leaf branch in a sparse merkle sum tree. + */ class PendingLeafBranch implements LeafBranch { private final BigInteger path; private final LeafValue value; + /** + * Create a pending leaf branch. + * + * @param path path of the branch + * @param value value stored in the leaf + */ public PendingLeafBranch(BigInteger path, LeafValue value) { this.path = path; this.value = value; } + @Override public BigInteger getPath() { return this.path; } + @Override public LeafValue getValue() { return this.value; } + @Override public FinalizedLeafBranch finalize(HashAlgorithm hashAlgorithm) { return FinalizedLeafBranch.create(this.path, this.value, hashAlgorithm); } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/PendingNodeBranch.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/PendingNodeBranch.java index 502e3e2..f17cded 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/PendingNodeBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/PendingNodeBranch.java @@ -1,33 +1,47 @@ package org.unicitylabs.sdk.mtree.sum; -import org.unicitylabs.sdk.hash.HashAlgorithm; import java.math.BigInteger; import java.util.Objects; +import org.unicitylabs.sdk.hash.HashAlgorithm; +/** + * Pending node branch in a sparse merkle sum tree. + */ class PendingNodeBranch implements NodeBranch { private final BigInteger path; private final Branch left; private final Branch right; + /** + * Create a pending node branch. + * + * @param path path of the branch + * @param left left branch + * @param right right branch + */ public PendingNodeBranch(BigInteger path, Branch left, Branch right) { this.path = path; this.left = left; this.right = right; } + @Override public BigInteger getPath() { return this.path; } + @Override public Branch getLeft() { return this.left; } + @Override public Branch getRight() { return this.right; } + @Override public FinalizedNodeBranch finalize(HashAlgorithm hashAlgorithm) { return FinalizedNodeBranch.create(this.path, this.left.finalize(hashAlgorithm), this.right.finalize(hashAlgorithm), hashAlgorithm); diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTree.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTree.java index 7b0c30c..565ed09 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTree.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTree.java @@ -1,13 +1,16 @@ package org.unicitylabs.sdk.mtree.sum; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Objects; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.mtree.BranchExistsException; import org.unicitylabs.sdk.mtree.CommonPath; import org.unicitylabs.sdk.mtree.LeafOutOfBoundsException; -import java.math.BigInteger; -import java.util.Arrays; -import java.util.Objects; +/** + * Sparse Merkle Sum Tree implementation. + */ public class SparseMerkleSumTree { private Branch left = null; @@ -15,10 +18,26 @@ public class SparseMerkleSumTree { private final HashAlgorithm hashAlgorithm; + /** + * Create a sparse merkle sum tree. + * + * @param hashAlgorithm hash algorithm to use + */ public SparseMerkleSumTree(HashAlgorithm hashAlgorithm) { this.hashAlgorithm = hashAlgorithm; } + /** + * Add a leaf to the tree. + * + * @param path path of the leaf (must be greater than 0) + * @param value value stored in the leaf + * @throws BranchExistsException if a branch already exists at the given path + * @throws LeafOutOfBoundsException if a leaf already exists at the given path + * @throws IllegalArgumentException if the path is less than or equal to 0 or if the counter is + * negative + * @throws NullPointerException if the path or value is null + */ public synchronized void addLeaf(BigInteger path, LeafValue value) throws BranchExistsException, LeafOutOfBoundsException { Objects.requireNonNull(path, "Path cannot be null"); @@ -45,6 +64,11 @@ public synchronized void addLeaf(BigInteger path, LeafValue value) } } + /** + * Calculate the root of the tree and its state. + * + * @return root node of the tree + */ public synchronized SparseMerkleSumTreeRootNode calculateRoot() { FinalizedBranch left = this.left != null ? this.left.finalize(this.hashAlgorithm) : null; FinalizedBranch right = this.right != null ? this.right.finalize(this.hashAlgorithm) : null; @@ -102,11 +126,21 @@ private static Branch buildTree(Branch branch, BigInteger remainingPath, LeafVal remainingPath.shiftRight(commonPath.getLength()), value), nodeBranch.getRight()); } + /** + * Value stored in a leaf of the sparse merkle sum tree. + */ public static class LeafValue { private final byte[] value; private final BigInteger counter; + /** + * Create a leaf value. + * + * @param value byte array value + * @param counter unsigned counter + * @throws NullPointerException if the value or counter is null + */ public LeafValue(byte[] value, BigInteger counter) { Objects.requireNonNull(value, "Value cannot be null"); Objects.requireNonNull(counter, "Counter cannot be null"); @@ -115,10 +149,20 @@ public LeafValue(byte[] value, BigInteger counter) { this.counter = counter; } + /** + * Get a copy of leaf byte value. + * + * @return bytes + */ public byte[] getValue() { return Arrays.copyOf(this.value, this.value.length); } + /** + * Get the counter. + * + * @return counter + */ public BigInteger getCounter() { return this.counter; } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePath.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePath.java index 2230a9a..ef9284a 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePath.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePath.java @@ -1,24 +1,32 @@ package org.unicitylabs.sdk.mtree.sum; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigInteger; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.mtree.MerkleTreePathVerificationResult; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.util.BigIntegerConverter; -import java.math.BigInteger; -import java.util.List; -import java.util.Objects; +/** + * Path in a sparse merkle sum tree. + */ public class SparseMerkleSumTreePath { private final Root root; private final List steps; - public SparseMerkleSumTreePath(Root root, List steps) { + @JsonCreator + SparseMerkleSumTreePath( + @JsonProperty("root") Root root, + @JsonProperty("steps") List steps + ) { Objects.requireNonNull(root, "root cannot be null"); Objects.requireNonNull(steps, "steps cannot be null"); @@ -26,16 +34,31 @@ public SparseMerkleSumTreePath(Root root, List step this.steps = List.copyOf(steps); } + /** + * Get root of the path. + * + * @return root + */ public Root getRoot() { return this.root; } + /** + * Get steps of the path from leaf to the root. + * + * @return steps + */ public List getSteps() { return this.steps; } - // TODO: Make it possible to use other hash algorithms - public MerkleTreePathVerificationResult verify(BigInteger requestId) { + /** + * Verify the path against the given state ID. + * + * @param stateId state ID to verify against + * @return result of the verification + */ + public MerkleTreePathVerificationResult verify(BigInteger stateId) { BigInteger currentPath = BigInteger.ONE; DataHash currentHash = null; BigInteger currentCounter = this.steps.isEmpty() @@ -45,7 +68,6 @@ public MerkleTreePathVerificationResult verify(BigInteger requestId) { .map(SparseMerkleSumTreePathStep.Branch::getCounter) .orElse(BigInteger.ZERO); - for (int i = 0; i < this.steps.size(); i++) { SparseMerkleSumTreePathStep step = this.steps.get(i); DataHash hash = null; @@ -54,50 +76,46 @@ public MerkleTreePathVerificationResult verify(BigInteger requestId) { byte[] bytes = i == 0 ? step.getBranch().get().getValue() : (currentHash != null ? currentHash.getImprint() : null); - try { - hash = new DataHasher(HashAlgorithm.SHA256) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes( - UnicityObjectMapper.CBOR.createArrayNode() - .add(BigIntegerConverter.encode(step.getPath())) - .add(bytes) - .add(BigIntegerConverter.encode(currentCounter)) - )) - .digest(); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + hash = new DataHasher(HashAlgorithm.SHA256) + .update( + CborSerializer.encodeArray( + CborSerializer.encodeByteString( + BigIntegerConverter.encode(step.getPath()) + ), + CborSerializer.encodeByteString(bytes), + CborSerializer.encodeByteString(BigIntegerConverter.encode(currentCounter)) + ) + ) + .digest(); int length = step.getPath().bitLength() - 1; currentPath = currentPath.shiftLeft(length) .or(step.getPath().and(BigInteger.ONE.shiftLeft(length).subtract(BigInteger.ONE))); - } boolean isRight = step.getPath().testBit(0); - ArrayNode sibling = step.getSibling() - .map(data -> UnicityObjectMapper.CBOR.createArrayNode() - .add(data.getValue()) - .add(BigIntegerConverter.encode(data.getCounter()))) - .orElse(null); - ArrayNode branch = hash == null - ? null - : UnicityObjectMapper.CBOR.createArrayNode() - .add(hash.getImprint()) - .add(BigIntegerConverter.encode(currentCounter)); - - try { - currentHash = new DataHasher(HashAlgorithm.SHA256) - .update( - UnicityObjectMapper.CBOR.writeValueAsBytes( - UnicityObjectMapper.CBOR.createArrayNode() - .add(isRight ? sibling : branch) - .add(isRight ? branch : sibling) - ) - ) - .digest(); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + byte[] sibling = step.getSibling() + .map( + data -> CborSerializer.encodeArray( + CborSerializer.encodeByteString(data.getValue()), + CborSerializer.encodeByteString(BigIntegerConverter.encode(data.getCounter())) + ) + ).orElse(CborSerializer.encodeNull()); + byte[] branch = hash == null + ? CborSerializer.encodeNull() + : CborSerializer.encodeArray( + CborSerializer.encodeByteString(hash.getImprint()), + CborSerializer.encodeByteString(BigIntegerConverter.encode(currentCounter)) + ); + + currentHash = new DataHasher(HashAlgorithm.SHA256) + .update( + CborSerializer.encodeArray( + isRight ? sibling : branch, + isRight ? branch : sibling + ) + ) + .digest(); currentCounter = currentCounter.add( step.getSibling() .map(SparseMerkleSumTreePathStep.Branch::getCounter) @@ -107,7 +125,40 @@ public MerkleTreePathVerificationResult verify(BigInteger requestId) { return new MerkleTreePathVerificationResult( this.root.hash.equals(currentHash) && this.root.counter.equals(currentCounter), - currentPath.equals(requestId)); + currentPath.equals(stateId)); + } + + /** + * Create path from CBOR bytes. + * + * @param bytes CBOR bytes + * @return path + */ + public static SparseMerkleSumTreePath fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new SparseMerkleSumTreePath( + SparseMerkleSumTreePath.Root.fromCbor(data.get(0)), + CborDeserializer.readArray(data.get(1)).stream() + .map(SparseMerkleSumTreePathStep::fromCbor) + .collect(Collectors.toList()) + ); + } + + /** + * Convert path to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + this.root.toCbor(), + CborSerializer.encodeArray( + this.steps.stream() + .map(SparseMerkleSumTreePathStep::toCbor) + .toArray(byte[][]::new) + ) + ); } @Override @@ -129,24 +180,68 @@ public String toString() { return String.format("MerkleTreePath{root=%s, steps=%s}", this.root, this.steps); } + /** + * Root of the sparse merkle sum tree path. + */ public static class Root { private final DataHash hash; private final BigInteger counter; - public Root(DataHash hash, BigInteger counter) { + @JsonCreator + Root( + @JsonProperty("hash") DataHash hash, + @JsonProperty("counter") BigInteger counter + ) { this.hash = Objects.requireNonNull(hash, "hash cannot be null"); this.counter = Objects.requireNonNull(counter, "counter cannot be null"); } + /** + * Get hash of the root. + * + * @return hash + */ public DataHash getHash() { return this.hash; } + /** + * Get the counter of the root. + * + * @return counter + */ public BigInteger getCounter() { return this.counter; } + /** + * Create root from CBOR bytes. + * + * @param bytes CBOR bytes + * @return root + */ + public static Root fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new Root( + DataHash.fromCbor(data.get(0)), + BigIntegerConverter.decode(CborDeserializer.readByteString(data.get(1))) + ); + } + + /** + * Convert root to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + this.hash.toCbor(), + CborSerializer.encodeByteString(BigIntegerConverter.encode(this.counter)) + ); + } + @Override public String toString() { return String.format("Root{hash=%s, counter=%s}", this.hash, this.counter); diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStep.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStep.java index 6641e9d..a120676 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStep.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStep.java @@ -1,11 +1,18 @@ package org.unicitylabs.sdk.mtree.sum; -import org.unicitylabs.sdk.util.HexConverter; import java.math.BigInteger; import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.Optional; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.util.BigIntegerConverter; +import org.unicitylabs.sdk.util.HexConverter; +/** + * Step in a sparse merkle sum tree path. + */ public class SparseMerkleSumTreePathStep { private final BigInteger path; @@ -49,7 +56,7 @@ public class SparseMerkleSumTreePathStep { ); } - public SparseMerkleSumTreePathStep(BigInteger path, Branch sibling, Branch branch) { + SparseMerkleSumTreePathStep(BigInteger path, Branch sibling, Branch branch) { Objects.requireNonNull(path, "path cannot be null"); this.path = path; @@ -57,18 +64,62 @@ public SparseMerkleSumTreePathStep(BigInteger path, Branch sibling, Branch branc this.branch = branch; } + /** + * Get path of the step. + * + * @return path + */ public BigInteger getPath() { return this.path; } + /** + * Get sibling branch. + * + * @return sibling branch + */ public Optional getSibling() { return Optional.ofNullable(this.sibling); } + /** + * Get branch at this step (can be null for non-leaf steps). + * + * @return branch + */ public Optional getBranch() { return Optional.ofNullable(this.branch); } + /** + * Create a step from CBOR bytes. + * + * @param bytes CBOR bytes + * @return step + */ + public static SparseMerkleSumTreePathStep fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new SparseMerkleSumTreePathStep( + BigIntegerConverter.decode(CborDeserializer.readByteString(data.get(0))), + Branch.fromCbor(data.get(1)), + Branch.fromCbor(data.get(2)) + ); + } + + /** + * Convert step to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + CborSerializer.encodeByteString(BigIntegerConverter.encode(this.path)), + CborSerializer.encodeOptional(this.sibling, Branch::toCbor), + CborSerializer.encodeOptional(this.branch, Branch::toCbor) + ); + } + @Override public boolean equals(Object o) { if (!(o instanceof SparseMerkleSumTreePathStep)) { @@ -90,26 +141,66 @@ public String toString() { this.path.toString(2), this.sibling, this.branch); } + /** + * A branch in the sparse merkle sum tree. + */ public static class Branch { private final byte[] value; private final BigInteger counter; - public Branch(byte[] value, BigInteger counter) { + Branch(byte[] value, BigInteger counter) { Objects.requireNonNull(counter, "counter cannot be null"); this.value = value == null ? null : Arrays.copyOf(value, value.length); this.counter = counter; } + /** + * Get branch value. + * + * @return value + */ public byte[] getValue() { return this.value == null ? null : Arrays.copyOf(this.value, this.value.length); } + /** + * Get the counter. + * + * @return counter + */ public BigInteger getCounter() { return this.counter; } + /** + * Create branch from CBOR bytes. + * + * @param bytes CBOR bytes + * @return branch + */ + public static Branch fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new Branch( + CborDeserializer.readByteString(data.get(0)), + BigIntegerConverter.decode(CborDeserializer.readByteString(data.get(1))) + ); + } + + /** + * Convert branch to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + CborSerializer.encodeOptional(this.value, CborSerializer::encodeByteString), + CborSerializer.encodeByteString(BigIntegerConverter.encode(this.counter)) + ); + } + @Override public boolean equals(Object o) { if (!(o instanceof Branch)) { diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStepJson.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStepJson.java new file mode 100644 index 0000000..ff4e0c0 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStepJson.java @@ -0,0 +1,162 @@ +package org.unicitylabs.sdk.mtree.sum; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.math.BigInteger; +import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep; + +/** + * Sparse merkle sum tree path step serializer and deserializer implementation. + */ +public class SparseMerkleSumTreePathStepJson { + + private SparseMerkleSumTreePathStepJson() { + } + + /** + * Sparse merkle sum tree path step serializer. + */ + public static class Serializer extends StdSerializer { + + /** + * Create serializer. + */ + public Serializer() { + super(SparseMerkleSumTreePathStep.class); + } + + /** + * Serialize sparse merkle sum tree path step. + * + * @param value path step + * @param gen json generator + * @param serializers serializer provider + * @throws IOException on serialization failure + */ + @Override + public void serialize(SparseMerkleSumTreePathStep value, JsonGenerator gen, + SerializerProvider serializers) + throws IOException { + gen.writeStartArray(); + gen.writeObject(value.getPath().toString()); + SparseMerkleSumTreePathStep.Branch sibling = value.getSibling().orElse(null); + if (sibling != null) { + gen.writeStartArray(); + gen.writeObject(sibling.getValue()); + gen.writeObject(sibling.getCounter().toString()); + gen.writeEndArray(); + } else { + gen.writeNull(); + } + + SparseMerkleSumTreePathStep.Branch branch = value.getBranch().orElse(null); + if (branch != null) { + gen.writeStartArray(); + gen.writeObject(branch.getValue()); + gen.writeObject(branch.getCounter().toString()); + gen.writeEndArray(); + } else { + gen.writeNull(); + } + gen.writeEndArray(); + } + } + + /** + * Sparse merkle sum tree path step deserializer. + */ + public static class Deserializer extends StdDeserializer { + + /** + * Create deserializer. + */ + public Deserializer() { + super(SparseMerkleSumTreePathStep.class); + } + + /** + * Deserialize sparse merkle sum tree path step branch. + * + * @param p Parser used for reading JSON content + * @return branch + * @throws IOException on deserialization failure + */ + public static SparseMerkleSumTreePathStep.Branch parseBranch(JsonParser p) throws IOException { + p.nextToken(); + + if (p.currentToken() == JsonToken.VALUE_NULL) { + return null; + } + + if (p.currentToken() != JsonToken.START_ARRAY) { + throw MismatchedInputException.from( + p, + SparseMerkleSumTreePathStep.Branch.class, + "Expected start of array" + ); + } + p.nextToken(); + SparseMerkleSumTreePathStep.Branch branch = new SparseMerkleSumTreePathStep.Branch( + p.readValueAs(byte[].class), + new BigInteger(p.readValueAs(String.class)) + ); + + if (p.nextToken() != JsonToken.END_ARRAY) { + throw MismatchedInputException.from( + p, + SparseMerkleSumTreePathStep.Branch.class, + "Expected end of array" + ); + } + + return branch; + } + + /** + * Deserialize sparse merkle sum tree path step. + * + * @param p Parser used for reading JSON content + * @param ctx Context that can be used to access information about this deserialization + * activity. + * @return path step + * @throws IOException on deserialization failure + */ + @Override + public SparseMerkleSumTreePathStep deserialize(JsonParser p, DeserializationContext ctx) + throws IOException { + if (p.getCurrentToken() != JsonToken.START_ARRAY) { + throw MismatchedInputException.from( + p, + SparseMerkleTreePathStep.class, + "Expected start of array" + ); + } + p.nextToken(); + + SparseMerkleSumTreePathStep step = new SparseMerkleSumTreePathStep( + new BigInteger(p.readValueAs(String.class)), + SparseMerkleSumTreePathStepJson + .Deserializer.parseBranch(p), + SparseMerkleSumTreePathStepJson + .Deserializer.parseBranch(p)); + + if (p.nextToken() != JsonToken.END_ARRAY) { + throw MismatchedInputException.from( + p, + SparseMerkleTreePathStep.class, + "Expected end of array" + ); + } + + return step; + } + } +} + diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreeRootNode.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreeRootNode.java index 3edaebc..b71aa02 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreeRootNode.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreeRootNode.java @@ -1,20 +1,21 @@ package org.unicitylabs.sdk.mtree.sum; -import com.fasterxml.jackson.core.JsonProcessingException; +import java.math.BigInteger; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.mtree.CommonPath; import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePath.Root; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.util.BigIntegerConverter; -import java.math.BigInteger; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; +/** + * Sparse Merkle Sum Tree root node. + */ public class SparseMerkleSumTreeRootNode { private final BigInteger path = BigInteger.ONE; // Root path is always 0 @@ -30,44 +31,56 @@ private SparseMerkleSumTreeRootNode( } static SparseMerkleSumTreeRootNode create( - FinalizedBranch left, FinalizedBranch right, - HashAlgorithm hashAlgorithm) { - try { - DataHash rootHash = new DataHasher(hashAlgorithm) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes( - UnicityObjectMapper.CBOR.createArrayNode() - .add( - left == null - ? null - : UnicityObjectMapper.CBOR.createArrayNode() - .add(left.getHash().getImprint()) - .add(BigIntegerConverter.encode(left.getCounter())) - ) - .add( - right == null - ? null - : UnicityObjectMapper.CBOR.createArrayNode() - .add(right.getHash().getImprint()) - .add(BigIntegerConverter.encode(right.getCounter())) - ) - )) - .digest(); - - BigInteger counter = BigInteger.ZERO - .add(left == null ? BigInteger.ZERO : left.getCounter()) - .add(right == null ? BigInteger.ZERO : right.getCounter()); - Root root = new Root(rootHash, counter); - - return new SparseMerkleSumTreeRootNode(left, right, root); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + FinalizedBranch left, + FinalizedBranch right, + HashAlgorithm hashAlgorithm + ) { + DataHash rootHash = new DataHasher(hashAlgorithm) + .update( + CborSerializer.encodeArray( + left == null + ? CborSerializer.encodeNull() + : CborSerializer.encodeArray( + CborSerializer.encodeByteString(left.getHash().getImprint()), + CborSerializer.encodeByteString( + BigIntegerConverter.encode(left.getCounter()) + ) + ), + right == null + ? CborSerializer.encodeNull() + : CborSerializer.encodeArray( + CborSerializer.encodeByteString(right.getHash().getImprint()), + CborSerializer.encodeByteString( + BigIntegerConverter.encode(right.getCounter()) + ) + ) + ) + ) + .digest(); + + BigInteger counter = BigInteger.ZERO + .add(left == null ? BigInteger.ZERO : left.getCounter()) + .add(right == null ? BigInteger.ZERO : right.getCounter()); + Root root = new Root(rootHash, counter); + + return new SparseMerkleSumTreeRootNode(left, right, root); } + /** + * Get root of the tree. + * + * @return root + */ public Root getRoot() { return this.root; } + /** + * Get path from the leaf to root. + * + * @param path path to the leaf + * @return path to the root + */ public SparseMerkleSumTreePath getPath(BigInteger path) { return new SparseMerkleSumTreePath( this.root, diff --git a/src/main/java/org/unicitylabs/sdk/predicate/EncodedPredicate.java b/src/main/java/org/unicitylabs/sdk/predicate/EncodedPredicate.java index 4ac9a76..af598d8 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/EncodedPredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/EncodedPredicate.java @@ -1,15 +1,22 @@ package org.unicitylabs.sdk.predicate; import java.util.Arrays; +import java.util.List; import java.util.Objects; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; import org.unicitylabs.sdk.util.HexConverter; +/** + * Predicate structure before passing it to predicate engine. + */ public class EncodedPredicate implements SerializablePredicate { + private final PredicateEngineType engine; private final byte[] code; private final byte[] parameters; - public EncodedPredicate(PredicateEngineType engine, byte[] code, byte[] parameters) { + + EncodedPredicate(PredicateEngineType engine, byte[] code, byte[] parameters) { Objects.requireNonNull(code, "Code must not be null"); Objects.requireNonNull(parameters, "Parameters must not be null"); @@ -18,20 +25,51 @@ public EncodedPredicate(PredicateEngineType engine, byte[] code, byte[] paramete this.parameters = Arrays.copyOf(parameters, parameters.length); } + /** + * Get predicate engine. + * + * @return predicate engine + */ public PredicateEngineType getEngine() { return this.engine; } + /** + * Encode predicate code. + * + * @return encoded code + */ @Override public byte[] encode() { return Arrays.copyOf(this.code, this.code.length); } + /** + * Encode predicate parameters. + * + * @return encoded parameters + */ @Override public byte[] encodeParameters() { return Arrays.copyOf(this.parameters, this.parameters.length); } + /** + * Create encoded predicate from CBOR bytes. + * + * @param bytes CBOR bytes + * @return encoded predicate + */ + public static EncodedPredicate fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new EncodedPredicate( + PredicateEngineType.values()[CborDeserializer.readUnsignedInteger(data.get(0)).asInt()], + CborDeserializer.readByteString(data.get(1)), + CborDeserializer.readByteString(data.get(2)) + ); + } + @Override public boolean equals(Object o) { if (!(o instanceof EncodedPredicate)) { diff --git a/src/main/java/org/unicitylabs/sdk/predicate/IPredicateFactory.java b/src/main/java/org/unicitylabs/sdk/predicate/IPredicateFactory.java deleted file mode 100644 index 36f04cb..0000000 --- a/src/main/java/org/unicitylabs/sdk/predicate/IPredicateFactory.java +++ /dev/null @@ -1,20 +0,0 @@ - -package org.unicitylabs.sdk.predicate; - -import org.unicitylabs.sdk.token.TokenId; -import org.unicitylabs.sdk.token.TokenType; - -/** - * Factory capable of reconstructing predicates from their CBOR/JSON form. - */ -public interface IPredicateFactory { - /** - * Create a predicate instance for the given token. - * - * @param tokenId Token identifier - * @param tokenType Token type - * @param data Predicate data - * @return CompletableFuture resolving to the predicate instance - */ - Predicate create(TokenId tokenId, TokenType tokenType, Object data); -} diff --git a/src/main/java/org/unicitylabs/sdk/predicate/Predicate.java b/src/main/java/org/unicitylabs/sdk/predicate/Predicate.java index 36912e2..2a9b95d 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/Predicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/Predicate.java @@ -3,16 +3,43 @@ import org.unicitylabs.sdk.bft.RootTrustBase; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.transaction.Transaction; -import org.unicitylabs.sdk.transaction.TransferTransactionData; +import org.unicitylabs.sdk.transaction.TransferTransaction; +/** + * Predicate structure. + */ public interface Predicate extends SerializablePredicate { + + /** + * Calculate predicate hash representation. + * + * @return predicate hash + */ DataHash calculateHash(); + /** + * Get predicate as reference. + * + * @return predicate reference + */ PredicateReference getReference(); + /** + * Is given public key owner of current predicate. + * + * @param publicKey public key of potential owner + * @return true if is owner + */ boolean isOwner(byte[] publicKey); - boolean verify(Token token, Transaction transaction, RootTrustBase trustBase); + /** + * Verify if predicate is valid for given token state. + * + * @param token current token state + * @param transaction current transaction + * @param trustBase trust base to verify against. + * @return true if successful + */ + boolean verify(Token token, TransferTransaction transaction, RootTrustBase trustBase); } diff --git a/src/main/java/org/unicitylabs/sdk/predicate/PredicateEngine.java b/src/main/java/org/unicitylabs/sdk/predicate/PredicateEngine.java index f700d55..54ee980 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/PredicateEngine.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/PredicateEngine.java @@ -1,5 +1,15 @@ package org.unicitylabs.sdk.predicate; +/** + * Predicate engine structure. + */ public interface PredicateEngine { + + /** + * Create predicate from serializable predicate. + * + * @param predicate serializable predicate. + * @return parsed predicate + */ Predicate create(SerializablePredicate predicate); } diff --git a/src/main/java/org/unicitylabs/sdk/predicate/PredicateEngineService.java b/src/main/java/org/unicitylabs/sdk/predicate/PredicateEngineService.java index 46a9f9e..66de301 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/PredicateEngineService.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/PredicateEngineService.java @@ -3,6 +3,9 @@ import java.util.HashMap; import org.unicitylabs.sdk.predicate.embedded.EmbeddedPredicateEngine; +/** + * Predefined predicate engines service to create predicates. + */ public class PredicateEngineService { private static final HashMap ENGINES = new HashMap<>() { @@ -11,6 +14,12 @@ public class PredicateEngineService { } }; + /** + * Create predicate from serializable predicate. + * + * @param predicate serializable predicate + * @return parsed predicate + */ public static Predicate createPredicate(SerializablePredicate predicate) { PredicateEngine engine = PredicateEngineService.ENGINES.get(predicate.getEngine()); if (engine == null) { diff --git a/src/main/java/org/unicitylabs/sdk/predicate/PredicateEngineType.java b/src/main/java/org/unicitylabs/sdk/predicate/PredicateEngineType.java index afef11a..f27d72f 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/PredicateEngineType.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/PredicateEngineType.java @@ -1,6 +1,8 @@ - package org.unicitylabs.sdk.predicate; +/** + * Predicate engine type. + */ public enum PredicateEngineType { EMBEDDED, } diff --git a/src/main/java/org/unicitylabs/sdk/predicate/PredicateReference.java b/src/main/java/org/unicitylabs/sdk/predicate/PredicateReference.java index 6ede73e..682b8f7 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/PredicateReference.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/PredicateReference.java @@ -3,7 +3,22 @@ import org.unicitylabs.sdk.address.Address; import org.unicitylabs.sdk.hash.DataHash; +/** + * Predicate reference interface. + */ public interface PredicateReference { - DataHash getHash(); - Address toAddress(); + + /** + * Get predicate reference as hash. + * + * @return reference hash + */ + DataHash getHash(); + + /** + * Get predicate reference as address. + * + * @return reference address + */ + Address toAddress(); } diff --git a/src/main/java/org/unicitylabs/sdk/predicate/SerializablePredicate.java b/src/main/java/org/unicitylabs/sdk/predicate/SerializablePredicate.java index 0fdc4b8..7adc34f 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/SerializablePredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/SerializablePredicate.java @@ -1,9 +1,33 @@ package org.unicitylabs.sdk.predicate; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +/** + * Serializable predicate structure. + */ +@JsonSerialize(using = SerializablePredicateJson.Serializer.class) +@JsonDeserialize(using = SerializablePredicateJson.Deserializer.class) public interface SerializablePredicate { + + /** + * Get predicate engine. + * + * @return predicate engine + */ PredicateEngineType getEngine(); + /** + * Get predicate code as bytes. + * + * @return predicate code bytes + */ byte[] encode(); + /** + * Get predicate parameters as bytes. + * + * @return parameters bytes + */ byte[] encodeParameters(); } diff --git a/src/main/java/org/unicitylabs/sdk/predicate/SerializablePredicateJson.java b/src/main/java/org/unicitylabs/sdk/predicate/SerializablePredicateJson.java new file mode 100644 index 0000000..50b53db --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/predicate/SerializablePredicateJson.java @@ -0,0 +1,96 @@ +package org.unicitylabs.sdk.predicate; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; + +/** + * Predicate serializer and deserializer implementation. + */ +public class SerializablePredicateJson { + + private SerializablePredicateJson() { + } + + /** + * Predicate serializer. + */ + public static class Serializer extends StdSerializer { + + /** + * Create predicate serializer. + */ + public Serializer() { + super(SerializablePredicate.class); + } + + /** + * Serialize predicate. + * + * @param value predicate + * @param gen json generator + * @param serializers serializer provider + * @throws IOException on serialization failure + */ + @Override + public void serialize(SerializablePredicate value, JsonGenerator gen, + SerializerProvider serializers) + throws IOException { + gen.writeObject( + CborSerializer.encodeArray( + CborSerializer.encodeUnsignedInteger(value.getEngine().ordinal()), + CborSerializer.encodeByteString(value.encode()), + CborSerializer.encodeByteString(value.encodeParameters()) + ) + ); + } + } + + /** + * Predicate deserializer. + */ + public static class Deserializer extends StdDeserializer { + + /** + * Create predicate deserializer. + */ + public Deserializer() { + super(SerializablePredicate.class); + } + + /** + * Deserialize predicate. + * + * @param p Parser used for reading JSON content + * @param ctx Context that can be used to access information about this deserialization + * activity. + * @return predicate + * @throws IOException on deserialization failure + */ + @Override + public SerializablePredicate deserialize(JsonParser p, DeserializationContext ctx) + throws IOException { + if (p.getCurrentToken() != JsonToken.VALUE_STRING) { + throw MismatchedInputException.from( + p, + EncodedPredicate.class, + "Expected string value" + ); + } + + try { + return EncodedPredicate.fromCbor(p.readValueAs(byte[].class)); + } catch (Exception e) { + throw MismatchedInputException.from(p, EncodedPredicate.class, "Expected bytes"); + } + } + } +} + diff --git a/src/main/java/org/unicitylabs/sdk/predicate/embedded/BurnPredicate.java b/src/main/java/org/unicitylabs/sdk/predicate/embedded/BurnPredicate.java index b35fd21..ad3a1f9 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/BurnPredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/BurnPredicate.java @@ -1,7 +1,6 @@ package org.unicitylabs.sdk.predicate.embedded; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.ArrayNode; +import java.util.List; import java.util.Objects; import org.unicitylabs.sdk.bft.RootTrustBase; import org.unicitylabs.sdk.hash.DataHash; @@ -9,20 +8,29 @@ import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.predicate.Predicate; import org.unicitylabs.sdk.predicate.PredicateEngineType; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.token.Token; import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenType; -import org.unicitylabs.sdk.transaction.Transaction; -import org.unicitylabs.sdk.transaction.TransferTransactionData; +import org.unicitylabs.sdk.transaction.TransferTransaction; +/** + * Burn predicate implementation. + */ public class BurnPredicate implements Predicate { private final TokenId tokenId; private final TokenType tokenType; private final DataHash burnReason; + /** + * Create burn predicate. + * + * @param tokenId token id + * @param tokenType token type + * @param reason burn reason as coin aggregation tree hash + */ public BurnPredicate(TokenId tokenId, TokenType tokenType, DataHash reason) { Objects.requireNonNull(tokenId, "Token id cannot be null"); Objects.requireNonNull(tokenType, "Token type cannot be null"); @@ -33,14 +41,29 @@ public BurnPredicate(TokenId tokenId, TokenType tokenType, DataHash reason) { this.burnReason = reason; } + /** + * Get token id. + * + * @return token id + */ public TokenId getTokenId() { return this.tokenId; } + /** + * Get token type. + * + * @return token type + */ public TokenType getTokenType() { return this.tokenType; } + /** + * Get burn reason. + * + * @return burn reason + */ public DataHash getReason() { return this.burnReason; } @@ -51,27 +74,36 @@ public boolean isOwner(byte[] publicKey) { } @Override - public boolean verify( - Token token, - Transaction transaction, - RootTrustBase trustBase - ) { + public boolean verify(Token token, TransferTransaction transaction, RootTrustBase trustBase) { return false; } @Override public DataHash calculateHash() { - ArrayNode node = UnicityObjectMapper.CBOR.createArrayNode(); - node.addPOJO(this.getReference().getHash()); - node.addPOJO(this.tokenId); - - try { - return new DataHasher(HashAlgorithm.SHA256) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(node)) - .digest(); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + return new DataHasher(HashAlgorithm.SHA256) + .update( + CborSerializer.encodeArray( + this.getReference().getHash().toCbor(), + this.tokenId.toCbor() + ) + ) + .digest(); + } + + /** + * Create burn predicate from CBOR bytes. + * + * @param bytes CBOR bytes + * @return burn predicate + */ + public static BurnPredicate fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new BurnPredicate( + TokenId.fromCbor(data.get(0)), + TokenType.fromCbor(data.get(1)), + DataHash.fromCbor(data.get(2)) + ); } @Override @@ -91,11 +123,11 @@ public byte[] encode() { @Override public byte[] encodeParameters() { - try { - return UnicityObjectMapper.CBOR.writeValueAsBytes(this); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + return CborSerializer.encodeArray( + this.tokenId.toCbor(), + this.tokenType.toCbor(), + this.burnReason.toCbor() + ); } @Override diff --git a/src/main/java/org/unicitylabs/sdk/predicate/embedded/BurnPredicateReference.java b/src/main/java/org/unicitylabs/sdk/predicate/embedded/BurnPredicateReference.java index d211310..675e876 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/BurnPredicateReference.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/BurnPredicateReference.java @@ -1,16 +1,17 @@ package org.unicitylabs.sdk.predicate.embedded; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.ArrayNode; +import java.util.Objects; import org.unicitylabs.sdk.address.DirectAddress; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.predicate.PredicateReference; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.token.TokenType; +/** + * Burn predicate reference. + */ public class BurnPredicateReference implements PredicateReference { private final DataHash hash; @@ -19,27 +20,44 @@ private BurnPredicateReference(DataHash hash) { this.hash = hash; } + /** + * Get burn predicate reference hash. + * + * @return reference hash + */ public DataHash getHash() { return this.hash; } + /** + * Create burn predicate reference. + * + * @param tokenType token type + * @param burnReason burn reason + * @return predicate reference + */ public static BurnPredicateReference create(TokenType tokenType, DataHash burnReason) { - ArrayNode node = UnicityObjectMapper.CBOR.createArrayNode(); - node.add(EmbeddedPredicateType.BURN.name()); - node.addPOJO(tokenType); - node.addPOJO(burnReason); - - try { - DataHash hash = new DataHasher(HashAlgorithm.SHA256) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(node)) - .digest(); - - return new BurnPredicateReference(hash); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + Objects.requireNonNull(tokenType, "Token type cannot be null"); + Objects.requireNonNull(burnReason, "Burn reason cannot be null"); + + return new BurnPredicateReference( + new DataHasher(HashAlgorithm.SHA256) + .update( + CborSerializer.encodeArray( + CborSerializer.encodeByteString(EmbeddedPredicateType.BURN.getBytes()), + CborSerializer.encodeByteString(tokenType.toCbor()), + CborSerializer.encodeByteString(burnReason.getImprint()) + ) + ) + .digest() + ); } + /** + * Convert predicate reference to address. + * + * @return predicate address + */ public DirectAddress toAddress() { return DirectAddress.create(this.hash); } diff --git a/src/main/java/org/unicitylabs/sdk/predicate/embedded/DefaultPredicate.java b/src/main/java/org/unicitylabs/sdk/predicate/embedded/DefaultPredicate.java index 5d1fea5..4948a33 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/DefaultPredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/DefaultPredicate.java @@ -1,7 +1,5 @@ package org.unicitylabs.sdk.predicate.embedded; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.ArrayNode; import java.util.Arrays; import java.util.Objects; import org.unicitylabs.sdk.api.Authenticator; @@ -13,18 +11,16 @@ import org.unicitylabs.sdk.predicate.Predicate; import org.unicitylabs.sdk.predicate.PredicateEngineType; import org.unicitylabs.sdk.predicate.PredicateReference; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.token.Token; import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenType; import org.unicitylabs.sdk.transaction.InclusionProofVerificationStatus; -import org.unicitylabs.sdk.transaction.Transaction; -import org.unicitylabs.sdk.transaction.TransferTransactionData; +import org.unicitylabs.sdk.transaction.TransferTransaction; import org.unicitylabs.sdk.util.HexConverter; /** - * Base class for unmasked and masked predicates + * Base class for unmasked and masked predicates. */ public abstract class DefaultPredicate implements Predicate { @@ -36,6 +32,17 @@ public abstract class DefaultPredicate implements Predicate { private final HashAlgorithm hashAlgorithm; private final byte[] nonce; + /** + * Create default functionality for masked and unmasked predicate. + * + * @param type predicate type + * @param tokenId token id + * @param tokenType token type + * @param publicKey public key + * @param signingAlgorithm signing algorithm + * @param hashAlgorithm hash algorithm + * @param nonce predicate nonce + */ protected DefaultPredicate( EmbeddedPredicateType type, TokenId tokenId, @@ -61,50 +68,87 @@ protected DefaultPredicate( this.nonce = Arrays.copyOf(nonce, nonce.length); } + /** + * Get predicate type. + * + * @return predicate type + */ public EmbeddedPredicateType getType() { return this.type; } + /** + * Get token id. + * + * @return token id + */ public TokenId getTokenId() { return this.tokenId; } + /** + * Get token type. + * + * @return token type + */ public TokenType getTokenType() { return this.tokenType; } + /** + * Get public key associated with predicate. + * + * @return public key + */ public byte[] getPublicKey() { return Arrays.copyOf(this.publicKey, this.publicKey.length); } + /** + * Get signing algorithm used with predicate. + * + * @return signing algorithm + */ public String getSigningAlgorithm() { return this.signingAlgorithm; } + /** + * Get hash algorithm used with predicate. + * + * @return hash algorithm + */ public HashAlgorithm getHashAlgorithm() { return this.hashAlgorithm; } + /** + * Get predicate nonce. + * + * @return predicate nonce + */ public byte[] getNonce() { return Arrays.copyOf(this.nonce, this.nonce.length); } @Override public DataHash calculateHash() { - ArrayNode node = UnicityObjectMapper.CBOR.createArrayNode(); - node.addPOJO(this.getReference().getHash()); - node.addPOJO(this.tokenId); - node.add(this.getNonce()); - - try { - return new DataHasher(HashAlgorithm.SHA256) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(node)) - .digest(); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - + return new DataHasher(HashAlgorithm.SHA256) + .update( + CborSerializer.encodeArray( + this.getReference().getHash().toCbor(), + this.tokenId.toCbor(), + CborSerializer.encodeByteString(this.getNonce()) + ) + ) + .digest(); + } + + /** + * Get predicate reference. + * + * @return predicate reference + */ public abstract PredicateReference getReference(); @Override @@ -113,8 +157,7 @@ public boolean isOwner(byte[] publicKey) { } @Override - public boolean verify(Token token, Transaction transaction, - RootTrustBase trustBase) { + public boolean verify(Token token, TransferTransaction transaction, RootTrustBase trustBase) { if (!this.tokenId.equals(token.getId()) || !this.tokenType.equals(token.getType())) { return false; } @@ -156,11 +199,14 @@ public byte[] encode() { @Override public byte[] encodeParameters() { - try { - return UnicityObjectMapper.CBOR.writeValueAsBytes(this); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + return CborSerializer.encodeArray( + this.tokenId.toCbor(), + this.tokenType.toCbor(), + CborSerializer.encodeByteString(this.publicKey), + CborSerializer.encodeTextString(this.signingAlgorithm), + CborSerializer.encodeUnsignedInteger(this.hashAlgorithm.getValue()), + CborSerializer.encodeByteString(this.nonce) + ); } @Override @@ -186,7 +232,14 @@ public int hashCode() { @Override public String toString() { return String.format( - "DefaultPredicate{type=%s, tokenId=%s, tokenType=%s, publicKey=%s, algorithm=%s, hashAlgorithm=%s, nonce=%s}", + "DefaultPredicate{" + + "type=%s, " + + "tokenId=%s, " + + "tokenType=%s, " + + "publicKey=%s, " + + "algorithm=%s, " + + "hashAlgorithm=%s, " + + "nonce=%s}", this.type, this.tokenId, this.tokenType, diff --git a/src/main/java/org/unicitylabs/sdk/predicate/embedded/EmbeddedPredicateEngine.java b/src/main/java/org/unicitylabs/sdk/predicate/embedded/EmbeddedPredicateEngine.java index 316eeff..6f535d7 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/EmbeddedPredicateEngine.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/EmbeddedPredicateEngine.java @@ -1,49 +1,43 @@ package org.unicitylabs.sdk.predicate.embedded; -import java.io.IOException; import org.unicitylabs.sdk.predicate.Predicate; import org.unicitylabs.sdk.predicate.PredicateEngine; import org.unicitylabs.sdk.predicate.SerializablePredicate; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +/** + * Embedded predicate engine implementation. + */ public class EmbeddedPredicateEngine implements PredicateEngine { + /** + * Create predicate from embedded predicate engine. + * + * @param predicate serializable predicate. + * @return predicate + */ public Predicate create(SerializablePredicate predicate) { - try { - EmbeddedPredicateType type = EmbeddedPredicateType.fromBytes(predicate.encode()); - switch (type) { - case MASKED: - if (predicate instanceof MaskedPredicate) { - return (MaskedPredicate) predicate; - } + EmbeddedPredicateType type = EmbeddedPredicateType.fromBytes(predicate.encode()); + switch (type) { + case MASKED: + if (predicate instanceof MaskedPredicate) { + return (MaskedPredicate) predicate; + } - return UnicityObjectMapper.CBOR.readValue( - predicate.encodeParameters(), - MaskedPredicate.class - ); - case UNMASKED: - if (predicate instanceof UnmaskedPredicate) { - return (UnmaskedPredicate) predicate; - } + return MaskedPredicate.fromCbor(predicate.encodeParameters()); + case UNMASKED: + if (predicate instanceof UnmaskedPredicate) { + return (UnmaskedPredicate) predicate; + } - return UnicityObjectMapper.CBOR.readValue( - predicate.encodeParameters(), - UnmaskedPredicate.class - ); - case BURN: - if (predicate instanceof BurnPredicate) { - return (BurnPredicate) predicate; - } + return UnmaskedPredicate.fromCbor(predicate.encodeParameters()); + case BURN: + if (predicate instanceof BurnPredicate) { + return (BurnPredicate) predicate; + } - return UnicityObjectMapper.CBOR.readValue( - predicate.encodeParameters(), - BurnPredicate.class - ); - default: - throw new IllegalArgumentException("Unknown predicate type: " + type); - } - } catch (IOException e) { - throw new RuntimeException("Failed to create predicate", e); + return BurnPredicate.fromCbor(predicate.encodeParameters()); + default: + throw new IllegalArgumentException("Unknown predicate type: " + type); } } } diff --git a/src/main/java/org/unicitylabs/sdk/predicate/embedded/EmbeddedPredicateType.java b/src/main/java/org/unicitylabs/sdk/predicate/embedded/EmbeddedPredicateType.java index f8a794f..a7b89f8 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/EmbeddedPredicateType.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/EmbeddedPredicateType.java @@ -3,10 +3,13 @@ import java.util.Arrays; +/** + * Embedded predicate types. + */ public enum EmbeddedPredicateType { - UNMASKED(new byte[] {0x0}), - MASKED(new byte[] {0x1}), - BURN(new byte[] {0x2}); + UNMASKED(new byte[]{0x0}), + MASKED(new byte[]{0x1}), + BURN(new byte[]{0x2}); private final byte[] bytes; @@ -14,10 +17,21 @@ public enum EmbeddedPredicateType { this.bytes = bytes; } + /** + * Get embedded predicate encoded code bytes. + * + * @return encoded code bytes + */ public byte[] getBytes() { return Arrays.copyOf(this.bytes, this.bytes.length); } + /** + * Create embedded predicate type from bytes. + * + * @param bytes predicate type bytes + * @return predicate type + */ public static EmbeddedPredicateType fromBytes(byte[] bytes) { for (EmbeddedPredicateType type : EmbeddedPredicateType.values()) { if (Arrays.equals(bytes, type.getBytes())) { diff --git a/src/main/java/org/unicitylabs/sdk/predicate/embedded/MaskedPredicate.java b/src/main/java/org/unicitylabs/sdk/predicate/embedded/MaskedPredicate.java index 96b5116..3e62247 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/MaskedPredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/MaskedPredicate.java @@ -1,12 +1,27 @@ package org.unicitylabs.sdk.predicate.embedded; +import java.util.List; import org.unicitylabs.sdk.hash.HashAlgorithm; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenType; +/** + * Masked predicate. + */ public class MaskedPredicate extends DefaultPredicate { + /** + * Create masked predicate. + * + * @param tokenId token id + * @param tokenType token type + * @param publicKey predicate public key + * @param signingAlgorithm signing algorithm + * @param hashAlgorithm hash algorithm + * @param nonce predicate nonce + */ public MaskedPredicate( TokenId tokenId, TokenType tokenType, @@ -25,6 +40,16 @@ public MaskedPredicate( ); } + /** + * Create masked predicate from signing service. + * + * @param tokenId token id + * @param tokenType token type + * @param signingService signing service + * @param hashAlgorithm hash algorithm + * @param nonce predicate nonce + * @return predicate + */ public static MaskedPredicate create( TokenId tokenId, TokenType tokenType, @@ -35,6 +60,25 @@ public static MaskedPredicate create( signingService.getAlgorithm(), hashAlgorithm, nonce); } + /** + * Create masked predicate from CBOR bytes. + * + * @param bytes CBOR bytes. + * @return predicate + */ + public static MaskedPredicate fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new MaskedPredicate( + TokenId.fromCbor(data.get(0)), + TokenType.fromCbor(data.get(1)), + CborDeserializer.readByteString(data.get(2)), + CborDeserializer.readTextString(data.get(3)), + HashAlgorithm.fromValue(CborDeserializer.readUnsignedInteger(data.get(4)).asInt()), + CborDeserializer.readByteString(data.get(5)) + ); + } + @Override public MaskedPredicateReference getReference() { return MaskedPredicateReference.create( diff --git a/src/main/java/org/unicitylabs/sdk/predicate/embedded/MaskedPredicateReference.java b/src/main/java/org/unicitylabs/sdk/predicate/embedded/MaskedPredicateReference.java index 27be0a9..b2ad5b1 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/MaskedPredicateReference.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/MaskedPredicateReference.java @@ -1,17 +1,18 @@ package org.unicitylabs.sdk.predicate.embedded; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.ArrayNode; +import java.util.Objects; import org.unicitylabs.sdk.address.DirectAddress; -import org.unicitylabs.sdk.predicate.PredicateReference; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.predicate.PredicateReference; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.token.TokenType; +/** + * Masked predicate reference. + */ public class MaskedPredicateReference implements PredicateReference { private final DataHash hash; @@ -20,37 +21,79 @@ private MaskedPredicateReference(DataHash hash) { this.hash = hash; } + /** + * Get predicate hash. + * + * @return predicate hash + */ public DataHash getHash() { return this.hash; } - public static MaskedPredicateReference create(TokenType tokenType, String signingAlgorithm, - byte[] publicKey, HashAlgorithm hashAlgorithm, byte[] nonce) { - ArrayNode node = UnicityObjectMapper.CBOR.createArrayNode(); - node.add(EmbeddedPredicateType.MASKED.name()); - node.addPOJO(tokenType); - node.add(signingAlgorithm); - node.addPOJO(hashAlgorithm); - node.add(publicKey); - node.add(nonce); - - try { - DataHash hash = new DataHasher(HashAlgorithm.SHA256) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(node)) - .digest(); - - return new MaskedPredicateReference(hash); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + /** + * Create masked predicate reference. + * + * @param tokenType token type + * @param signingAlgorithm signing algorithm + * @param publicKey predicate public key + * @param hashAlgorithm hash algorithm + * @param nonce predicate nonce + * @return predicate reference + */ + public static MaskedPredicateReference create( + TokenType tokenType, + String signingAlgorithm, + byte[] publicKey, + HashAlgorithm hashAlgorithm, + byte[] nonce + ) { + Objects.requireNonNull(tokenType, "Token type cannot be null"); + Objects.requireNonNull(signingAlgorithm, "Signing algorithm cannot be null"); + Objects.requireNonNull(publicKey, "Public key cannot be null"); + Objects.requireNonNull(hashAlgorithm, "Hash algorithm cannot be null"); + Objects.requireNonNull(nonce, "Nonce cannot be null"); + + return new MaskedPredicateReference( + new DataHasher(HashAlgorithm.SHA256) + .update( + CborSerializer.encodeArray( + CborSerializer.encodeByteString(EmbeddedPredicateType.MASKED.getBytes()), + CborSerializer.encodeByteString(tokenType.toCbor()), + CborSerializer.encodeTextString(signingAlgorithm), + CborSerializer.encodeUnsignedInteger(hashAlgorithm.getValue()), + CborSerializer.encodeByteString(publicKey), + CborSerializer.encodeByteString(nonce) + ) + ) + .digest() + ); } + /** + * Create predicate reference from signing service. + * + * @param tokenType token type + * @param signingService signing service + * @param hashAlgorithm hash algorithm + * @param nonce predicate nonce + * @return predicate reference + */ public static MaskedPredicateReference create(TokenType tokenType, SigningService signingService, HashAlgorithm hashAlgorithm, byte[] nonce) { - return MaskedPredicateReference.create(tokenType, signingService.getAlgorithm(), - signingService.getPublicKey(), hashAlgorithm, nonce); + return MaskedPredicateReference.create( + tokenType, + signingService.getAlgorithm(), + signingService.getPublicKey(), + hashAlgorithm, + nonce + ); } + /** + * Convert predicate reference to address. + * + * @return predicate address + */ public DirectAddress toAddress() { return DirectAddress.create(this.hash); } diff --git a/src/main/java/org/unicitylabs/sdk/predicate/embedded/UnmaskedPredicate.java b/src/main/java/org/unicitylabs/sdk/predicate/embedded/UnmaskedPredicate.java index 6473972..375c9cc 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/UnmaskedPredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/UnmaskedPredicate.java @@ -5,17 +5,20 @@ import org.unicitylabs.sdk.bft.RootTrustBase; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; import org.unicitylabs.sdk.signing.Signature; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.token.Token; import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenType; -import org.unicitylabs.sdk.transaction.Transaction; -import org.unicitylabs.sdk.transaction.TransferTransactionData; +import org.unicitylabs.sdk.transaction.TransferTransaction; +/** + * Unmasked predicate. + */ public class UnmaskedPredicate extends DefaultPredicate { - public UnmaskedPredicate( + UnmaskedPredicate( TokenId tokenId, TokenType tokenType, byte[] publicKey, @@ -27,6 +30,15 @@ public UnmaskedPredicate( hashAlgorithm, nonce); } + /** + * Create unmasked predicate. + * + * @param tokenId token id + * @param tokenType token type + * @param signingService signing service + * @param hashAlgorithm hash algorithm + * @param salt received transaction salt + */ public static UnmaskedPredicate create( TokenId tokenId, TokenType tokenType, @@ -46,13 +58,21 @@ public static UnmaskedPredicate create( nonce.getBytes()); } + /** + * Verify token state for current transaction. + * + * @param token current token state + * @param transaction current transaction + * @param trustBase trust base to verify against. + * @return true if successful + */ @Override public boolean verify( Token token, - Transaction transaction, + TransferTransaction transaction, RootTrustBase trustBase ) { - List> transactions = token.getTransactions(); + List transactions = token.getTransactions(); return super.verify(token, transaction, trustBase) && SigningService.verifyWithPublicKey( new DataHasher(HashAlgorithm.SHA256) @@ -67,6 +87,30 @@ public boolean verify( ); } + /** + * Create predicate from CBOR bytes. + * + * @param bytes CBOR bytes + * @return predicate + */ + public static UnmaskedPredicate fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new UnmaskedPredicate( + TokenId.fromCbor(data.get(0)), + TokenType.fromCbor(data.get(1)), + CborDeserializer.readByteString(data.get(2)), + CborDeserializer.readTextString(data.get(3)), + HashAlgorithm.fromValue(CborDeserializer.readUnsignedInteger(data.get(4)).asInt()), + CborDeserializer.readByteString(data.get(5)) + ); + } + + /** + * Convert predicate to CBOR bytes. + * + * @return CBOR bytes + */ public UnmaskedPredicateReference getReference() { return UnmaskedPredicateReference.create( this.getTokenType(), diff --git a/src/main/java/org/unicitylabs/sdk/predicate/embedded/UnmaskedPredicateReference.java b/src/main/java/org/unicitylabs/sdk/predicate/embedded/UnmaskedPredicateReference.java index 9aa0719..3041222 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/UnmaskedPredicateReference.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/UnmaskedPredicateReference.java @@ -1,17 +1,18 @@ package org.unicitylabs.sdk.predicate.embedded; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.ArrayNode; +import java.util.Objects; import org.unicitylabs.sdk.address.DirectAddress; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.predicate.PredicateReference; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.token.TokenType; +/** + * Unmasked predicate reference. + */ public class UnmaskedPredicateReference implements PredicateReference { private final DataHash hash; @@ -20,36 +21,76 @@ private UnmaskedPredicateReference(DataHash hash) { this.hash = hash; } + /** + * Get predicate hash. + * + * @return predicate hash + */ public DataHash getHash() { return this.hash; } - public static UnmaskedPredicateReference create(TokenType tokenType, String signingAlgorithm, - byte[] publicKey, HashAlgorithm hashAlgorithm) { - ArrayNode node = UnicityObjectMapper.CBOR.createArrayNode(); - node.add(EmbeddedPredicateType.UNMASKED.name()); - node.addPOJO(tokenType); - node.add(signingAlgorithm); - node.addPOJO(hashAlgorithm); - node.add(publicKey); - - try { - DataHash hash = new DataHasher(HashAlgorithm.SHA256) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(node)) - .digest(); - - return new UnmaskedPredicateReference(hash); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + /** + * Create predicate reference. + * + * @param tokenType token type + * @param signingAlgorithm signing algorithm + * @param publicKey predicate public key + * @param hashAlgorithm hash algorithm + * @return predicate reference + */ + public static UnmaskedPredicateReference create( + TokenType tokenType, + String signingAlgorithm, + byte[] publicKey, + HashAlgorithm hashAlgorithm + ) { + Objects.requireNonNull(tokenType, "Token type cannot be null"); + Objects.requireNonNull(signingAlgorithm, "Signing algorithm cannot be null"); + Objects.requireNonNull(publicKey, "Public key cannot be null"); + Objects.requireNonNull(hashAlgorithm, "Hash algorithm cannot be null"); + + return new UnmaskedPredicateReference( + new DataHasher(HashAlgorithm.SHA256) + .update( + CborSerializer.encodeArray( + CborSerializer.encodeByteString(EmbeddedPredicateType.UNMASKED.getBytes()), + CborSerializer.encodeByteString(tokenType.toCbor()), + CborSerializer.encodeTextString(signingAlgorithm), + CborSerializer.encodeUnsignedInteger(hashAlgorithm.getValue()), + CborSerializer.encodeByteString(publicKey) + ) + ) + .digest() + ); } - public static UnmaskedPredicateReference create(TokenType tokenType, - SigningService signingService, HashAlgorithm hashAlgorithm) { - return UnmaskedPredicateReference.create(tokenType, signingService.getAlgorithm(), - signingService.getPublicKey(), hashAlgorithm); + /** + * Create predicate reference from signing service. + * + * @param tokenType token type + * @param signingService signing service + * @param hashAlgorithm hash algorithm + * @return predicate reference + */ + public static UnmaskedPredicateReference create( + TokenType tokenType, + SigningService signingService, + HashAlgorithm hashAlgorithm + ) { + return UnmaskedPredicateReference.create( + tokenType, + signingService.getAlgorithm(), + signingService.getPublicKey(), + hashAlgorithm + ); } + /** + * Convert predicate reference to address. + * + * @return predicate address + */ public DirectAddress toAddress() { return DirectAddress.create(this.hash); } diff --git a/src/main/java/org/unicitylabs/sdk/serializer/InvalidFieldValueException.java b/src/main/java/org/unicitylabs/sdk/serializer/InvalidFieldValueException.java deleted file mode 100644 index 0d3b8af..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/InvalidFieldValueException.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.unicitylabs.sdk.serializer; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.JsonMappingException; - -public class InvalidFieldValueException extends JsonMappingException { - private InvalidFieldValueException(JsonParser p, String fieldName, Throwable cause) { - super(p, String.format("Invalid value on field '%s'", fieldName), cause); - } - - public static InvalidFieldValueException from(JsonParser p, String field) { - return new InvalidFieldValueException(p, field, null); - } - - public static InvalidFieldValueException from(JsonParser p, String field, Throwable cause) { - return new InvalidFieldValueException(p, field, cause); - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/MissingFieldException.java b/src/main/java/org/unicitylabs/sdk/serializer/MissingFieldException.java deleted file mode 100644 index 17c833b..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/MissingFieldException.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.unicitylabs.sdk.serializer; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.JsonMappingException; - -public class MissingFieldException extends JsonMappingException { - public MissingFieldException(JsonParser p, String fieldName) { - super(p, String.format("Missing required field '%s'", fieldName)); - } - - public static MissingFieldException from(JsonParser p, String field) { - return new MissingFieldException(p, field); - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/UnicityObjectMapper.java b/src/main/java/org/unicitylabs/sdk/serializer/UnicityObjectMapper.java index b326c20..73253d6 100644 --- a/src/main/java/org/unicitylabs/sdk/serializer/UnicityObjectMapper.java +++ b/src/main/java/org/unicitylabs/sdk/serializer/UnicityObjectMapper.java @@ -2,344 +2,28 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import org.unicitylabs.sdk.address.Address; -import org.unicitylabs.sdk.api.Authenticator; -import org.unicitylabs.sdk.api.BlockHeightResponse; -import org.unicitylabs.sdk.api.InclusionProofRequest; -import org.unicitylabs.sdk.api.InclusionProofResponse; -import org.unicitylabs.sdk.api.RequestId; -import org.unicitylabs.sdk.api.SubmitCommitmentRequest; -import org.unicitylabs.sdk.api.SubmitCommitmentResponse; -import org.unicitylabs.sdk.bft.InputRecord; -import org.unicitylabs.sdk.bft.RootTrustBase; -import org.unicitylabs.sdk.bft.ShardTreeCertificate; -import org.unicitylabs.sdk.bft.UnicityCertificate; -import org.unicitylabs.sdk.bft.UnicitySeal; -import org.unicitylabs.sdk.bft.UnicityTreeCertificate; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.jsonrpc.JsonRpcError; -import org.unicitylabs.sdk.jsonrpc.JsonRpcRequest; -import org.unicitylabs.sdk.jsonrpc.JsonRpcResponse; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePath; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePathStep; -import org.unicitylabs.sdk.predicate.SerializablePredicate; -import org.unicitylabs.sdk.predicate.embedded.BurnPredicate; -import org.unicitylabs.sdk.predicate.embedded.DefaultPredicate; -import org.unicitylabs.sdk.predicate.embedded.MaskedPredicate; -import org.unicitylabs.sdk.predicate.embedded.UnmaskedPredicate; -import org.unicitylabs.sdk.serializer.cbor.address.AddressCbor; -import org.unicitylabs.sdk.serializer.cbor.api.AuthenticatorCbor; -import org.unicitylabs.sdk.serializer.cbor.bft.InputRecordCbor; -import org.unicitylabs.sdk.serializer.cbor.bft.ShardTreeCertificateCbor; -import org.unicitylabs.sdk.serializer.cbor.bft.UnicityCertificateCbor; -import org.unicitylabs.sdk.serializer.cbor.bft.UnicitySealCbor; -import org.unicitylabs.sdk.serializer.cbor.bft.UnicityTreeCertificateCbor; -import org.unicitylabs.sdk.serializer.cbor.bft.UnicityTreeCertificateHashStepCbor; -import org.unicitylabs.sdk.serializer.cbor.hash.DataHashCbor; -import org.unicitylabs.sdk.serializer.cbor.mtree.plain.SparseMerkleTreePathCbor; -import org.unicitylabs.sdk.serializer.cbor.mtree.plain.SparseMerkleTreePathStepBranchCbor; -import org.unicitylabs.sdk.serializer.cbor.mtree.plain.SparseMerkleTreePathStepCbor; -import org.unicitylabs.sdk.serializer.cbor.mtree.sum.SparseMerkleSumTreePathCbor; -import org.unicitylabs.sdk.serializer.cbor.mtree.sum.SparseMerkleSumTreePathStepBranchCbor; -import org.unicitylabs.sdk.serializer.cbor.mtree.sum.SparseMerkleSumTreePathStepCbor; -import org.unicitylabs.sdk.serializer.cbor.predicate.BurnPredicateCbor; -import org.unicitylabs.sdk.serializer.cbor.predicate.DefaultPredicateCbor; -import org.unicitylabs.sdk.serializer.cbor.predicate.MaskedPredicateCbor; -import org.unicitylabs.sdk.serializer.cbor.predicate.SerializablePredicateCbor; -import org.unicitylabs.sdk.serializer.cbor.predicate.UnmaskedPredicateCbor; -import org.unicitylabs.sdk.serializer.cbor.token.TokenCbor; -import org.unicitylabs.sdk.serializer.cbor.token.TokenIdCbor; -import org.unicitylabs.sdk.serializer.cbor.token.TokenStateCbor; -import org.unicitylabs.sdk.serializer.cbor.token.TokenTypeCbor; -import org.unicitylabs.sdk.serializer.cbor.token.fungible.TokenCoinDataCbor; -import org.unicitylabs.sdk.serializer.cbor.transaction.InclusionProofCbor; -import org.unicitylabs.sdk.serializer.cbor.transaction.MintTransactionDataCbor; -import org.unicitylabs.sdk.serializer.cbor.transaction.MintTransactionReasonCbor; -import org.unicitylabs.sdk.serializer.cbor.transaction.TransactionCbor; -import org.unicitylabs.sdk.serializer.cbor.transaction.TransferTransactionDataCbor; -import org.unicitylabs.sdk.serializer.cbor.transaction.split.SplitMintReasonCbor; -import org.unicitylabs.sdk.serializer.cbor.transaction.split.SplitMintReasonProofCbor; -import org.unicitylabs.sdk.serializer.json.address.AddressJson; -import org.unicitylabs.sdk.serializer.json.api.AuthenticatorJson; -import org.unicitylabs.sdk.serializer.json.api.BlockHeightResponseJson; -import org.unicitylabs.sdk.serializer.json.api.InclusionProofRequestJson; -import org.unicitylabs.sdk.serializer.json.api.InclusionProofResponseJson; -import org.unicitylabs.sdk.serializer.json.api.RequestIdJson; -import org.unicitylabs.sdk.serializer.json.api.SubmitCommitmentRequestJson; -import org.unicitylabs.sdk.serializer.json.api.SubmitCommitmentResponseJson; -import org.unicitylabs.sdk.serializer.json.bft.RootTrustBaseJson; -import org.unicitylabs.sdk.serializer.json.bft.RootTrustBaseNodeInfoJson; -import org.unicitylabs.sdk.serializer.json.hash.DataHashJson; -import org.unicitylabs.sdk.serializer.json.jsonrpc.JsonRpcErrorJson; -import org.unicitylabs.sdk.serializer.json.jsonrpc.JsonRpcRequestJson; -import org.unicitylabs.sdk.serializer.json.jsonrpc.JsonRpcResponseJson; -import org.unicitylabs.sdk.serializer.json.mtree.plain.SparseMerkleTreePathJson; -import org.unicitylabs.sdk.serializer.json.mtree.plain.SparseMerkleTreePathStepBranchJson; -import org.unicitylabs.sdk.serializer.json.mtree.plain.SparseMerkleTreePathStepJson; -import org.unicitylabs.sdk.serializer.json.mtree.sum.SparseMerkleSumTreePathJson; -import org.unicitylabs.sdk.serializer.json.mtree.sum.SparseMerkleSumTreePathStepBranchJson; -import org.unicitylabs.sdk.serializer.json.mtree.sum.SparseMerkleSumTreePathStepJson; -import org.unicitylabs.sdk.serializer.json.predicate.SerializablePredicateJson; -import org.unicitylabs.sdk.serializer.json.token.TokenIdJson; -import org.unicitylabs.sdk.serializer.json.token.TokenJson; -import org.unicitylabs.sdk.serializer.json.token.TokenStateJson; -import org.unicitylabs.sdk.serializer.json.token.TokenTypeJson; -import org.unicitylabs.sdk.serializer.json.token.fungible.TokenCoinDataJson; -import org.unicitylabs.sdk.serializer.json.transaction.CommitmentJson; -import org.unicitylabs.sdk.serializer.json.transaction.InclusionProofJson; -import org.unicitylabs.sdk.serializer.json.transaction.MintCommitmentJson; -import org.unicitylabs.sdk.serializer.json.transaction.MintTransactionDataJson; -import org.unicitylabs.sdk.serializer.json.transaction.MintTransactionReasonJson; -import org.unicitylabs.sdk.serializer.json.transaction.TransactionJson; -import org.unicitylabs.sdk.serializer.json.transaction.TransferCommitmentJson; -import org.unicitylabs.sdk.serializer.json.transaction.TransferTransactionDataJson; -import org.unicitylabs.sdk.serializer.json.transaction.split.SplitMintReasonJson; -import org.unicitylabs.sdk.serializer.json.transaction.split.SplitMintReasonProofJson; -import org.unicitylabs.sdk.serializer.json.util.ByteArrayHexJson; -import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.token.TokenId; -import org.unicitylabs.sdk.token.TokenState; -import org.unicitylabs.sdk.token.TokenType; -import org.unicitylabs.sdk.token.fungible.TokenCoinData; -import org.unicitylabs.sdk.transaction.Commitment; -import org.unicitylabs.sdk.transaction.InclusionProof; -import org.unicitylabs.sdk.transaction.MintCommitment; -import org.unicitylabs.sdk.transaction.MintTransactionData; -import org.unicitylabs.sdk.transaction.MintTransactionReason; -import org.unicitylabs.sdk.transaction.Transaction; -import org.unicitylabs.sdk.transaction.TransferCommitment; -import org.unicitylabs.sdk.transaction.TransferTransactionData; -import org.unicitylabs.sdk.transaction.split.SplitMintReason; -import org.unicitylabs.sdk.transaction.split.SplitMintReasonProof; +import org.unicitylabs.sdk.serializer.json.ByteArrayJson; + +/** + * Unicity object mapper with byte array and JDK8 module attached. + */ public class UnicityObjectMapper { - public static final ObjectMapper CBOR = createCborObjectMapper(); + /** + * JSON object mapper with correct modules attached. + */ public static final ObjectMapper JSON = createJsonObjectMapper(); - private static ObjectMapper createCborObjectMapper() { - SimpleModule module = new SimpleModule(); - - module.addSerializer(Address.class, new AddressCbor.Serializer()); - module.addDeserializer(Address.class, new AddressCbor.Deserializer()); - - module.addSerializer(DataHash.class, new DataHashCbor.Serializer()); - module.addDeserializer(DataHash.class, new DataHashCbor.Deserializer()); - - module.addSerializer(SparseMerkleTreePath.class, new SparseMerkleTreePathCbor.Serializer()); - module.addDeserializer(SparseMerkleTreePath.class, new SparseMerkleTreePathCbor.Deserializer()); - module.addSerializer(SparseMerkleTreePathStep.class, - new SparseMerkleTreePathStepCbor.Serializer()); - module.addDeserializer(SparseMerkleTreePathStep.class, - new SparseMerkleTreePathStepCbor.Deserializer()); - module.addSerializer(SparseMerkleTreePathStep.Branch.class, - new SparseMerkleTreePathStepBranchCbor.Serializer()); - module.addDeserializer(SparseMerkleTreePathStep.Branch.class, - new SparseMerkleTreePathStepBranchCbor.Deserializer()); - - module.addSerializer(SparseMerkleSumTreePath.class, - new SparseMerkleSumTreePathCbor.Serializer()); - module.addDeserializer(SparseMerkleSumTreePath.class, - new SparseMerkleSumTreePathCbor.Deserializer()); - module.addSerializer(SparseMerkleSumTreePathStep.class, - new SparseMerkleSumTreePathStepCbor.Serializer()); - module.addDeserializer(SparseMerkleSumTreePathStep.class, - new SparseMerkleSumTreePathStepCbor.Deserializer()); - module.addSerializer(SparseMerkleSumTreePathStep.Branch.class, - new SparseMerkleSumTreePathStepBranchCbor.Serializer()); - module.addDeserializer(SparseMerkleSumTreePathStep.Branch.class, - new SparseMerkleSumTreePathStepBranchCbor.Deserializer()); - - module.addSerializer(Authenticator.class, new AuthenticatorCbor.Serializer()); - module.addDeserializer(Authenticator.class, new AuthenticatorCbor.Deserializer()); - - module.addSerializer(InclusionProof.class, new InclusionProofCbor.Serializer()); - module.addDeserializer(InclusionProof.class, new InclusionProofCbor.Deserializer()); - - module.addSerializer(Transaction.class, new TransactionCbor.Serializer()); - module.addDeserializer(Transaction.class, new TransactionCbor.Deserializer()); - - module.addSerializer(MintTransactionData.class, new MintTransactionDataCbor.Serializer()); - module.addDeserializer(MintTransactionData.class, new MintTransactionDataCbor.Deserializer()); - - module.addDeserializer(MintTransactionReason.class, - new MintTransactionReasonCbor.Deserializer()); - - module.addSerializer(TransferTransactionData.class, - new TransferTransactionDataCbor.Serializer()); - module.addDeserializer(TransferTransactionData.class, - new TransferTransactionDataCbor.Deserializer()); - - module.addSerializer(SplitMintReason.class, new SplitMintReasonCbor.Serializer()); - module.addDeserializer(SplitMintReason.class, new SplitMintReasonCbor.Deserializer()); - - module.addSerializer(SplitMintReasonProof.class, new SplitMintReasonProofCbor.Serializer()); - module.addDeserializer(SplitMintReasonProof.class, new SplitMintReasonProofCbor.Deserializer()); - - module.addSerializer(TokenId.class, new TokenIdCbor.Serializer()); - module.addDeserializer(TokenId.class, new TokenIdCbor.Deserializer()); - - module.addSerializer(TokenType.class, new TokenTypeCbor.Serializer()); - module.addDeserializer(TokenType.class, new TokenTypeCbor.Deserializer()); - - module.addSerializer(TokenCoinData.class, new TokenCoinDataCbor.Serializer()); - module.addDeserializer(TokenCoinData.class, new TokenCoinDataCbor.Deserializer()); - - module.addSerializer(Token.class, new TokenCbor.Serializer()); - module.addDeserializer(Token.class, new TokenCbor.Deserializer()); - - module.addSerializer(TokenState.class, new TokenStateCbor.Serializer()); - module.addDeserializer(TokenState.class, new TokenStateCbor.Deserializer()); - - module.addSerializer(SerializablePredicate.class, new SerializablePredicateCbor.Serializer()); - module.addDeserializer(SerializablePredicate.class, - new SerializablePredicateCbor.Deserializer()); - - module.addSerializer(DefaultPredicate.class, new DefaultPredicateCbor.Serializer()); - module.addSerializer(BurnPredicate.class, new BurnPredicateCbor.Serializer()); - module.addDeserializer(MaskedPredicate.class, new MaskedPredicateCbor.Deserializer()); - module.addDeserializer(UnmaskedPredicate.class, new UnmaskedPredicateCbor.Deserializer()); - module.addDeserializer(BurnPredicate.class, new BurnPredicateCbor.Deserializer()); - - // BFT - UnicityCertificate - module.addSerializer(UnicityCertificate.class, new UnicityCertificateCbor.Serializer()); - module.addDeserializer(UnicityCertificate.class, new UnicityCertificateCbor.Deserializer()); - module.addSerializer(InputRecord.class, new InputRecordCbor.Serializer()); - module.addDeserializer(InputRecord.class, new InputRecordCbor.Deserializer()); - module.addSerializer(ShardTreeCertificate.class, new ShardTreeCertificateCbor.Serializer()); - module.addDeserializer(ShardTreeCertificate.class, new ShardTreeCertificateCbor.Deserializer()); - module.addSerializer(UnicityTreeCertificate.class, new UnicityTreeCertificateCbor.Serializer()); - module.addDeserializer(UnicityTreeCertificate.class, - new UnicityTreeCertificateCbor.Deserializer()); - module.addSerializer(UnicityTreeCertificate.HashStep.class, - new UnicityTreeCertificateHashStepCbor.Serializer()); - module.addDeserializer(UnicityTreeCertificate.HashStep.class, - new UnicityTreeCertificateHashStepCbor.Deserializer()); - module.addSerializer(UnicitySeal.class, new UnicitySealCbor.Serializer()); - module.addDeserializer(UnicitySeal.class, new UnicitySealCbor.Deserializer()); - - ObjectMapper objectMapper = new CBORMapper(); - objectMapper.registerModule(new Jdk8Module()); - objectMapper.registerModule(module); - return objectMapper; - } - private static ObjectMapper createJsonObjectMapper() { SimpleModule module = new SimpleModule(); - module.addSerializer(byte[].class, new ByteArrayHexJson.Serializer()); - module.addDeserializer(byte[].class, new ByteArrayHexJson.Deserializer()); - - module.addSerializer(Address.class, new AddressJson.Serializer()); - module.addDeserializer(Address.class, new AddressJson.Deserializer()); - - module.addSerializer(TokenId.class, new TokenIdJson.Serializer()); - module.addDeserializer(TokenId.class, new TokenIdJson.Deserializer()); - - module.addSerializer(TokenType.class, new TokenTypeJson.Serializer()); - module.addDeserializer(TokenType.class, new TokenTypeJson.Deserializer()); - - module.addSerializer(TokenCoinData.class, new TokenCoinDataJson.Serializer()); - module.addDeserializer(TokenCoinData.class, new TokenCoinDataJson.Deserializer()); - - module.addSerializer(DataHash.class, new DataHashJson.Serializer()); - module.addDeserializer(DataHash.class, new DataHashJson.Deserializer()); - - module.addSerializer(InclusionProof.class, new InclusionProofJson.Serializer()); - module.addDeserializer(InclusionProof.class, new InclusionProofJson.Deserializer()); - - module.addSerializer(SparseMerkleTreePath.class, new SparseMerkleTreePathJson.Serializer()); - module.addDeserializer(SparseMerkleTreePath.class, new SparseMerkleTreePathJson.Deserializer()); - - module.addSerializer(SparseMerkleTreePathStep.class, - new SparseMerkleTreePathStepJson.Serializer()); - module.addDeserializer(SparseMerkleTreePathStep.class, - new SparseMerkleTreePathStepJson.Deserializer()); - - module.addSerializer(SparseMerkleTreePathStep.Branch.class, - new SparseMerkleTreePathStepBranchJson.Serializer()); - module.addDeserializer(SparseMerkleTreePathStep.Branch.class, - new SparseMerkleTreePathStepBranchJson.Deserializer()); - - module.addSerializer(SparseMerkleSumTreePath.class, - new SparseMerkleSumTreePathJson.Serializer()); - module.addDeserializer(SparseMerkleSumTreePath.class, - new SparseMerkleSumTreePathJson.Deserializer()); - - module.addSerializer(SparseMerkleSumTreePathStep.class, - new SparseMerkleSumTreePathStepJson.Serializer()); - module.addDeserializer(SparseMerkleSumTreePathStep.class, - new SparseMerkleSumTreePathStepJson.Deserializer()); - - module.addSerializer(SparseMerkleSumTreePathStep.Branch.class, - new SparseMerkleSumTreePathStepBranchJson.Serializer()); - module.addDeserializer(SparseMerkleSumTreePathStep.Branch.class, - new SparseMerkleSumTreePathStepBranchJson.Deserializer()); - - module.addSerializer(Authenticator.class, new AuthenticatorJson.Serializer()); - module.addDeserializer(Authenticator.class, new AuthenticatorJson.Deserializer()); - - module.addSerializer(RequestId.class, new RequestIdJson.Serializer()); - module.addDeserializer(RequestId.class, new RequestIdJson.Deserializer()); - - module.addSerializer(Commitment.class, new CommitmentJson.Serializer()); - module.addDeserializer(MintCommitment.class, new MintCommitmentJson.Deserializer()); - module.addDeserializer(TransferCommitment.class, new TransferCommitmentJson.Deserializer()); - - module.addSerializer(Token.class, new TokenJson.Serializer()); - module.addDeserializer(Token.class, new TokenJson.Deserializer()); - - module.addSerializer(TokenState.class, new TokenStateJson.Serializer()); - module.addDeserializer(TokenState.class, new TokenStateJson.Deserializer()); - - module.addSerializer(SerializablePredicate.class, new SerializablePredicateJson.Serializer()); - module.addDeserializer(SerializablePredicate.class, - new SerializablePredicateJson.Deserializer()); - - module.addSerializer(Transaction.class, new TransactionJson.Serializer()); - module.addDeserializer(Transaction.class, new TransactionJson.Deserializer()); - - module.addSerializer(MintTransactionData.class, new MintTransactionDataJson.Serializer()); - module.addDeserializer(MintTransactionData.class, new MintTransactionDataJson.Deserializer()); - - module.addSerializer(TransferTransactionData.class, - new TransferTransactionDataJson.Serializer()); - module.addDeserializer(TransferTransactionData.class, - new TransferTransactionDataJson.Deserializer()); - - module.addDeserializer(MintTransactionReason.class, - new MintTransactionReasonJson.Deserializer()); - - module.addSerializer(SplitMintReason.class, new SplitMintReasonJson.Serializer()); - module.addDeserializer(SplitMintReason.class, new SplitMintReasonJson.Deserializer()); - - module.addSerializer(SplitMintReasonProof.class, new SplitMintReasonProofJson.Serializer()); - module.addDeserializer(SplitMintReasonProof.class, new SplitMintReasonProofJson.Deserializer()); - - module.addSerializer(JsonRpcRequest.class, new JsonRpcRequestJson.Serializer()); - module.addSerializer(SubmitCommitmentRequest.class, - new SubmitCommitmentRequestJson.Serializer()); - module.addSerializer(InclusionProofRequest.class, new InclusionProofRequestJson.Serializer()); - - module.addDeserializer(JsonRpcResponse.class, new JsonRpcResponseJson.Deserializer()); - module.addDeserializer(JsonRpcError.class, new JsonRpcErrorJson.Deserializer()); - module.addDeserializer(BlockHeightResponse.class, new BlockHeightResponseJson.Deserializer()); - module.addDeserializer(InclusionProofResponse.class, - new InclusionProofResponseJson.Deserializer()); - module.addDeserializer(SubmitCommitmentResponse.class, - new SubmitCommitmentResponseJson.Deserializer()); - - // BFT - module.addDeserializer(RootTrustBase.NodeInfo.class, - new RootTrustBaseNodeInfoJson.Deserializer()); - module.addDeserializer(RootTrustBase.class, new RootTrustBaseJson.Deserializer()); + module.addSerializer(byte[].class, new ByteArrayJson.Serializer()); + module.addDeserializer(byte[].class, new ByteArrayJson.Deserializer()); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new Jdk8Module()); objectMapper.registerModule(module); return objectMapper; } -} +} \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborDeserializer.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborDeserializer.java index d10a806..b5fea25 100644 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborDeserializer.java +++ b/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborDeserializer.java @@ -10,11 +10,22 @@ import org.unicitylabs.sdk.serializer.cbor.CborSerializer.CborMap; import org.unicitylabs.sdk.serializer.cbor.CborSerializer.CborMap.Entry; +/** + * CBOR deserialization utilities. + */ public class CborDeserializer { - private final static byte MAJOR_TYPE_MASK = (byte) 0b11100000; - private final static byte ADDITIONAL_INFORMATION_MASK = (byte) 0b00011111; - + private static final byte MAJOR_TYPE_MASK = (byte) 0b11100000; + private static final byte ADDITIONAL_INFORMATION_MASK = (byte) 0b00011111; + + /** + * Read optional value from CBOR bytes. + * + * @param data bytes + * @param reader parse method + * @param parsed value type + * @return parsed value + */ public static T readOptional(byte[] data, Function reader) { if (Byte.compareUnsigned(new CborReader(data).readByte(), (byte) 0xf6) == 0) { return null; @@ -23,55 +34,96 @@ public static T readOptional(byte[] data, Function reader) { return reader.apply(data); } - public static long readUnsignedInteger(byte[] data) { + /** + * Read unsigned integer from CBOR bytes. + * + * @param data bytes + * @return unsigned number + */ + public static CborNumber readUnsignedInteger(byte[] data) { CborReader reader = new CborReader(data); - return reader.readLength(CborMajorType.UNSIGNED_INTEGER); + return new CborNumber(reader.readLength(CborMajorType.UNSIGNED_INTEGER)); } + /** + * Read byte string from CBOR bytes. + * + * @param data bytes + * @return bytes + */ public static byte[] readByteString(byte[] data) { CborReader reader = new CborReader(data); return reader.read((int) reader.readLength(CborMajorType.BYTE_STRING)); } + /** + * Read text string from CBOR bytes. + * + * @param data bytes + * @return text + */ public static String readTextString(byte[] data) { CborReader reader = new CborReader(data); return new String( reader.read((int) reader.readLength(CborMajorType.TEXT_STRING))); } - + /** + * Read elements as raw CBOR element array from CBOR bytes. + * + * @param data bytes + * @return CBOR element array + */ public static List readArray(byte[] data) { CborReader reader = new CborReader(data); long length = reader.readLength(CborMajorType.ARRAY); ArrayList result = new ArrayList<>(); for (int i = 0; i < length; i++) { - result.add(reader.readRawCBOR()); + result.add(reader.readRawCbor()); } return result; } + /** + * Read elements as raw CBOR element map from CBOR bytes. + * + * @param data bytes + * @return CBOR element map + */ public static Set readMap(byte[] data) { CborReader reader = new CborReader(data); long length = (int) reader.readLength(CborMajorType.MAP); Set result = new LinkedHashSet<>(); for (int i = 0; i < length; i++) { - byte[] key = reader.readRawCBOR(); - byte[] value = reader.readRawCBOR(); + byte[] key = reader.readRawCbor(); + byte[] value = reader.readRawCbor(); result.add(new CborMap.Entry(key, value)); } return result; } + /** + * Read tag from CBOR bytes. + * + * @param data bytes + * @return CBOR tag + */ public static CborTag readTag(byte[] data) { CborReader reader = new CborReader(data); long tag = (int) reader.readLength(CborMajorType.TAG); - return new CborTag(tag, reader.readRawCBOR()); + return new CborTag(tag, reader.readRawCbor()); } + /** + * Read boolean from CBOR bytes. + * + * @param data bytes + * @return boolean + */ public static boolean readBoolean(byte[] data) { byte byteValue = new CborReader(data).readByte(); if (byteValue == (byte) 0xf5) { @@ -84,6 +136,7 @@ public static boolean readBoolean(byte[] data) { } private static class CborReader { + final byte[] data; int position = 0; @@ -133,6 +186,8 @@ public long readLength(CborMajorType majorType) { if (Byte.compareUnsigned(additionalInformation, (byte) 31) == 0) { throw new CborSerializationException("Indefinite length array not supported."); } + break; + default: } if (Byte.compareUnsigned(additionalInformation, (byte) 27) > 0) { @@ -148,12 +203,13 @@ public long readLength(CborMajorType majorType) { return t; } - public byte[] readRawCBOR() { + public byte[] readRawCbor() { if (this.position >= this.data.length) { throw new CborSerializationException("Premature end of data."); } - CborMajorType majorType = CborMajorType.fromType(this.data[this.position] & CborDeserializer.MAJOR_TYPE_MASK); + CborMajorType majorType = CborMajorType.fromType( + this.data[this.position] & CborDeserializer.MAJOR_TYPE_MASK); int position = this.position; int length = (int) this.readLength(majorType); switch (majorType) { @@ -163,17 +219,19 @@ public byte[] readRawCBOR() { break; case ARRAY: for (int i = 0; i < length; i++) { - this.readRawCBOR(); + this.readRawCbor(); } break; case MAP: for (int i = 0; i < length; i++) { - this.readRawCBOR(); - this.readRawCBOR(); + this.readRawCbor(); + this.readRawCbor(); } break; case TAG: - this.readRawCBOR(); + this.readRawCbor(); + break; + default: break; } @@ -181,7 +239,11 @@ public byte[] readRawCBOR() { } } + /** + * CBOR Tag implementation. + */ public static class CborTag { + private final long tag; private final byte[] data; @@ -190,12 +252,81 @@ private CborTag(long tag, byte[] data) { this.data = data; } + /** + * Get tag number. + * + * @return tag + */ public long getTag() { return this.tag; } + /** + * Get data associated with tag. + * + * @return tag data + */ public byte[] getData() { return Arrays.copyOf(this.data, this.data.length); } } + + /** + * CBOR number implementation. + */ + public static class CborNumber { + + private final long value; + + private CborNumber(long value) { + this.value = value; + } + + /** + * Get number as long. + * + * @return number + */ + public long asLong() { + return this.value; + } + + /** + * Get number as int, throw error if does not fit. + * + * @return number + */ + public int asInt() { + if (Long.compareUnsigned(this.value, 0xFFFFFFFFL) > 0) { + throw new ArithmeticException("Value too large"); + } + return (int) this.value; + } + + /** + * Get number as byte, throw error if does not fit. + * + * @return number + */ + public byte asByte() { + if (Long.compareUnsigned(this.value, 0xFFL) > 0) { + throw new ArithmeticException("Value too large"); + } + + return (byte) this.value; + } + + /** + * Get number as short, throw error if does not fit. + * + * @return number + */ + public short asShort() { + if (Long.compareUnsigned(this.value, 0xFFFFL) > 0) { + throw new ArithmeticException("Value too large"); + } + + return (short) this.value; + } + } } diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborMajorType.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborMajorType.java index 5262d17..ba43d81 100644 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborMajorType.java +++ b/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborMajorType.java @@ -1,5 +1,8 @@ package org.unicitylabs.sdk.serializer.cbor; +/** + * CBOR major types. + */ public enum CborMajorType { UNSIGNED_INTEGER(0b00000000), NEGATIVE_INTEGER(0b00100000), @@ -16,10 +19,21 @@ public enum CborMajorType { this.type = type; } + /** + * Get CBOR major type. + * + * @return type + */ public int getType() { return this.type; } + /** + * Get CBOR major type from value. + * + * @param type value + * @return type + */ public static CborMajorType fromType(int type) { for (CborMajorType majorType : CborMajorType.values()) { if (Integer.compareUnsigned(majorType.getType(), type & 0xFF) == 0) { diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborSerializationException.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborSerializationException.java index 6dc8311..ba2b9f5 100644 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborSerializationException.java +++ b/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborSerializationException.java @@ -1,14 +1,15 @@ package org.unicitylabs.sdk.serializer.cbor; +/** + * CBOR serialization exception, when something goes wrong with deserializing or serializing. + */ public class CborSerializationException extends RuntimeException { - public CborSerializationException(String message, Throwable cause) { - super(message, cause); - } - - public CborSerializationException(Throwable cause) { - super(cause); - } + /** + * Create CBOR serialization exception. + * + * @param message error message + */ public CborSerializationException(String message) { super(message); } diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborSerializer.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborSerializer.java index f4389c5..588d605 100644 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborSerializer.java +++ b/src/main/java/org/unicitylabs/sdk/serializer/cbor/CborSerializer.java @@ -10,8 +10,19 @@ import java.util.Set; import java.util.function.Function; +/** + * CBOR serialization utilities. + */ public class CborSerializer { + /** + * Encode value to CBOR, if null encode null bytes. + * + * @param data value to be encoded + * @param encoder encode method + * @param value type + * @return bytes + */ public static byte[] encodeOptional(T data, Function encoder) { if (data == null) { return new byte[]{(byte) 0xf6}; @@ -19,6 +30,12 @@ public static byte[] encodeOptional(T data, Function encoder) { return encoder.apply(data); } + /** + * Encode unsigned integer to CBOR bytes. + * + * @param input unsigned integer + * @return bytes + */ public static byte[] encodeUnsignedInteger(long input) { if (Long.compareUnsigned(input, 24) < 0) { return new byte[]{(byte) (CborMajorType.UNSIGNED_INTEGER.getType() | input)}; @@ -27,14 +44,18 @@ public static byte[] encodeUnsignedInteger(long input) { byte[] bytes = CborSerializer.getUnsignedLongAsPaddedBytes(input); byte[] result = new byte[1 + bytes.length]; System.arraycopy(bytes, 0, result, 1, bytes.length); - result[0] = (byte) ( - CborMajorType.UNSIGNED_INTEGER.getType() - | CborSerializer.getAdditionalInformationBits(bytes.length) - ); + result[0] = (byte) (CborMajorType.UNSIGNED_INTEGER.getType() + | CborSerializer.getAdditionalInformationBits(bytes.length)); return result; } + /** + * Encode byte string to CBOR bytes. + * + * @param input bytes + * @return bytes + */ public static byte[] encodeByteString(byte[] input) { if (input == null) { throw new CborSerializationException("Input byte array cannot be null."); @@ -43,6 +64,12 @@ public static byte[] encodeByteString(byte[] input) { return CborSerializer.encodeRawArray(input, input.length, CborMajorType.BYTE_STRING); } + /** + * Encode text string as CBOR bytes. + * + * @param input text + * @return bytes + */ public static byte[] encodeTextString(String input) { if (input == null) { throw new CborSerializationException("Input string cannot be null."); @@ -52,7 +79,13 @@ public static byte[] encodeTextString(String input) { return CborSerializer.encodeRawArray(bytes, bytes.length, CborMajorType.TEXT_STRING); } - public static byte[] encodeArray(List input) { + /** + * Encode array of CBOR elements to single CBOR byte array. + * + * @param input elements + * @return bytes + */ + public static byte[] encodeArray(byte[]... input) { if (input == null) { throw new CborSerializationException("Input byte array list cannot be null."); } @@ -69,13 +102,14 @@ public static byte[] encodeArray(List input) { length += bytes.length; } - return CborSerializer.encodeRawArray(data, input.size(), CborMajorType.ARRAY); + return CborSerializer.encodeRawArray(data, input.length, CborMajorType.ARRAY); } /** + * Encode map of CBOR elements to single CBOR byte array. * * @param input map with hex converted keys - * @return cbor representation of the map + * @return CBOR representation of the map */ public static byte[] encodeMap(CborMap input) { if (input == null) { @@ -99,6 +133,13 @@ public static byte[] encodeMap(CborMap input) { return CborSerializer.encodeRawArray(data, input.entries.size(), CborMajorType.MAP); } + /** + * Encode CBOR tag with CBOR encoded element to single CBOR byte array. + * + * @param tag CBOR tag + * @param input element + * @return bytes + */ public static byte[] encodeTag(long tag, byte[] input) { if (Long.compareUnsigned(tag, 24) < 0) { byte[] result = new byte[1 + input.length]; @@ -110,20 +151,29 @@ public static byte[] encodeTag(long tag, byte[] input) { byte[] bytes = CborSerializer.getUnsignedLongAsPaddedBytes(tag); byte[] result = new byte[1 + bytes.length + input.length]; - result[0] = (byte) ( - CborMajorType.TAG.getType() - | CborSerializer.getAdditionalInformationBits(bytes.length) - ); + result[0] = (byte) (CborMajorType.TAG.getType() + | CborSerializer.getAdditionalInformationBits(bytes.length)); System.arraycopy(bytes, 0, result, 1, bytes.length); System.arraycopy(input, 0, result, 1 + bytes.length, input.length); return result; } + /** + * Encode boolean to CBOR bytes. + * + * @param input boolean + * @return bytes + */ public static byte[] encodeBoolean(boolean input) { return new byte[]{(byte) (input ? 0xf5 : 0xf4)}; } + /** + * Encode null to CBOR bytes. + * + * @return bytes + */ public static byte[] encodeNull() { return new byte[]{(byte) 0xf6}; } @@ -139,9 +189,8 @@ private static byte[] encodeRawArray(byte[] input, int length, CborMajorType typ byte[] lengthBytes = CborSerializer.getUnsignedLongAsPaddedBytes(length); byte[] result = new byte[1 + lengthBytes.length + input.length]; - result[0] = (byte) ( - type.getType() | CborSerializer.getAdditionalInformationBits(lengthBytes.length) - ); + result[0] = (byte) (type.getType() + | CborSerializer.getAdditionalInformationBits(lengthBytes.length)); System.arraycopy(lengthBytes, 0, result, 1, lengthBytes.length); System.arraycopy(input, 0, result, 1 + lengthBytes.length, input.length); @@ -174,10 +223,18 @@ private static byte[] getUnsignedLongAsPaddedBytes(long input) { return buffer.array(); } + /** + * CBOR map to order elements for canonicalization. + */ public static final class CborMap { private final ArrayList entries; + /** + * Create map from set of CBOR elements. + * + * @param entries elements + */ public CborMap(Set entries) { this.entries = new ArrayList<>(entries); this.entries.sort((a, b) -> { @@ -195,15 +252,29 @@ public CborMap(Set entries) { }); } + /** + * Get CBOR element list. + * + * @return element list + */ public List getEntries() { return List.copyOf(this.entries); } + /** + * CBOR entry for map. + */ public static final class Entry { private final byte[] key; private final byte[] value; + /** + * Create new CBOR element map entry. + * + * @param key CBOR bytes + * @param value CBOR bytes + */ public Entry(byte[] key, byte[] value) { Objects.requireNonNull(key, "Key cannot be null."); Objects.requireNonNull(value, "Value cannot be null."); @@ -212,10 +283,20 @@ public Entry(byte[] key, byte[] value) { this.value = Arrays.copyOf(value, value.length); } + /** + * Get entry key CBOR bytes element. + * + * @return bytes + */ public byte[] getKey() { return Arrays.copyOf(this.key, this.key.length); } + /** + * Get entry value CBOR bytes element. + * + * @return bytes + */ public byte[] getValue() { return Arrays.copyOf(this.value, this.value.length); } diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/address/AddressCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/address/AddressCbor.java deleted file mode 100644 index 2fe80e8..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/address/AddressCbor.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.address; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.address.Address; -import org.unicitylabs.sdk.address.AddressFactory; -import java.io.IOException; - -public class AddressCbor { - - private AddressCbor() { - } - - public static class Serializer extends JsonSerializer
{ - - public Serializer() { - } - - @Override - public void serialize(Address value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeString(value.getAddress()); - } - } - - public static class Deserializer extends JsonDeserializer
{ - - public Deserializer() { - } - - @Override - public Address deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (p.getCurrentToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, Address.class, - "Expected string value"); - } - - try { - return AddressFactory.createAddress(p.readValueAs(String.class)); - } catch (Exception e) { - throw MismatchedInputException.from(p, Address.class, - String.format("Invalid address: %s", e.getMessage())); - } - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/api/AuthenticatorCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/api/AuthenticatorCbor.java deleted file mode 100644 index 4a7793d..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/api/AuthenticatorCbor.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.api; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.api.Authenticator; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.signing.Signature; -import java.io.IOException; - -public class AuthenticatorCbor { - - private AuthenticatorCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(Authenticator value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 4); - gen.writeObject(value.getAlgorithm()); - gen.writeObject(value.getPublicKey()); - gen.writeObject(value.getSignature().encode()); - gen.writeObject(value.getStateHash()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public Authenticator deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, Authenticator.class, "Expected array value"); - } - - return new Authenticator( - p.readValueAs(String.class), - p.readValueAs(byte[].class), - Signature.decode(p.readValueAs(byte[].class)), - p.readValueAs(DataHash.class) - ); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/InputRecordCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/InputRecordCbor.java deleted file mode 100644 index 24163a5..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/InputRecordCbor.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.bft; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import com.fasterxml.jackson.dataformat.cbor.CBORGenerator; -import java.io.IOException; -import org.unicitylabs.sdk.bft.InputRecord; - -public class InputRecordCbor { - - private InputRecordCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(InputRecord value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - ((CBORGenerator) gen).writeTag(1008); - gen.writeStartArray(value, 10); - gen.writeObject(value.getVersion()); - gen.writeObject(value.getRoundNumber()); - gen.writeObject(value.getEpoch()); - gen.writeObject(value.getPreviousHash()); - gen.writeObject(value.getHash()); - gen.writeObject(value.getSummaryValue()); - gen.writeObject(value.getTimestamp()); - gen.writeObject(value.getBlockHash()); - gen.writeObject(value.getSumOfEarnedFees()); - gen.writeObject(value.getExecutedTransactionsHash()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public InputRecord deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, InputRecord.class, "Expected array value"); - } - p.nextToken(); - - int version = p.readValueAs(int.class); - long roundNumber = p.readValueAs(long.class); - long epoch = p.readValueAs(long.class); - byte[] previousHash = p.readValueAs(byte[].class); - byte[] hash = p.readValueAs(byte[].class); - byte[] summaryValue = p.readValueAs(byte[].class); - long timestamp = p.readValueAs(long.class); - byte[] blockHash = p.readValueAs(byte[].class); - long sumOfEarnedFees = p.readValueAs(long.class); - byte[] executedTransactionsHash = p.readValueAs(byte[].class); - - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, InputRecord.class, "Expected end of array"); - } - - return new InputRecord( - version, - roundNumber, - epoch, - previousHash, - hash, - summaryValue, - timestamp, - blockHash, - sumOfEarnedFees, - executedTransactionsHash - ); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/ShardTreeCertificateCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/ShardTreeCertificateCbor.java deleted file mode 100644 index fd36440..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/ShardTreeCertificateCbor.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.bft; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import org.unicitylabs.sdk.bft.ShardTreeCertificate; - -public class ShardTreeCertificateCbor { - - private ShardTreeCertificateCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(ShardTreeCertificate value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 2); - gen.writeObject(value.getShard()); - gen.writeObject(value.getSiblingHashList()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public ShardTreeCertificate deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, ShardTreeCertificate.class, "Expected array value"); - } - p.nextToken(); - byte[] shard = p.readValueAs(byte[].class); - if (p.nextToken() != JsonToken.START_ARRAY) { - throw MismatchedInputException.from(p, ShardTreeCertificate.class, "Expected sibling hash list"); - } - - List siblings = new ArrayList<>(); - while (p.nextToken() != JsonToken.END_ARRAY) { - siblings.add(p.readValueAs(byte[].class)); - } - - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, ShardTreeCertificate.class, "Expected end of array"); - } - - return new ShardTreeCertificate(shard, siblings); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/UnicityCertificateCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/UnicityCertificateCbor.java deleted file mode 100644 index 8a76122..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/UnicityCertificateCbor.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.bft; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import com.fasterxml.jackson.dataformat.cbor.CBORGenerator; -import java.io.IOException; -import org.unicitylabs.sdk.bft.InputRecord; -import org.unicitylabs.sdk.bft.ShardTreeCertificate; -import org.unicitylabs.sdk.bft.UnicityCertificate; -import org.unicitylabs.sdk.bft.UnicitySeal; -import org.unicitylabs.sdk.bft.UnicityTreeCertificate; - -public class UnicityCertificateCbor { - - private UnicityCertificateCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(UnicityCertificate value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - ((CBORGenerator) gen).writeTag(1007); - gen.writeStartArray(value, 7); - gen.writeObject(value.getVersion()); - gen.writeObject(value.getInputRecord()); - gen.writeObject(value.getTechnicalRecordHash()); - gen.writeObject(value.getShardConfigurationHash()); - gen.writeObject(value.getShardTreeCertificate()); - gen.writeObject(value.getUnicityTreeCertificate()); - gen.writeObject(value.getUnicitySeal()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public UnicityCertificate deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, UnicityCertificate.class, "Expected array value"); - } - p.nextToken(); - - UnicityCertificate result = new UnicityCertificate( - p.readValueAs(int.class), - p.readValueAs(InputRecord.class), - p.readValueAs(byte[].class), - p.readValueAs(byte[].class), - p.readValueAs(ShardTreeCertificate.class), - p.readValueAs(UnicityTreeCertificate.class), - p.readValueAs(UnicitySeal.class) - ); - - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, UnicityCertificate.class, "Expected end of array"); - } - - return result; - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/UnicitySealCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/UnicitySealCbor.java deleted file mode 100644 index cfe8b67..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/UnicitySealCbor.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.bft; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import com.fasterxml.jackson.dataformat.cbor.CBORGenerator; -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Map; -import org.unicitylabs.sdk.bft.InputRecord; -import org.unicitylabs.sdk.bft.UnicitySeal; - -public class UnicitySealCbor { - - private UnicitySealCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(UnicitySeal value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - ((CBORGenerator) gen).writeTag(1001); - gen.writeStartArray(value, 8); - gen.writeObject(value.getVersion()); - gen.writeObject(value.getNetworkId()); - gen.writeObject(value.getRootChainRoundNumber()); - gen.writeObject(value.getEpoch()); - gen.writeObject(value.getTimestamp()); - gen.writeObject(value.getPreviousHash()); - gen.writeObject(value.getHash()); - if (value.getSignatures() == null) { - gen.writeNull(); - } else { - gen.writeStartObject(value.getSignatures(), value.getSignatures().size()); - for (Map.Entry entry : value.getSignatures().entrySet()) { - gen.writeFieldName(entry.getKey()); - gen.writeObject(entry.getValue()); - } - gen.writeEndObject(); - } - - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public UnicitySeal deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, UnicitySeal.class, "Expected array value"); - } - p.nextToken(); - - int version = p.readValueAs(int.class); - short networkId = p.readValueAs(short.class); - long rootChainRoundNumber = p.readValueAs(long.class); - long epoch = p.readValueAs(long.class); - long timestamp = p.readValueAs(long.class); - byte[] previousHash = p.readValueAs(byte[].class); - byte[] hash = p.readValueAs(byte[].class); - - if (p.nextToken() != JsonToken.START_OBJECT) { - throw MismatchedInputException.from(p, UnicitySeal.class, "Expected map value"); - } - - Map signatures = new LinkedHashMap<>(); - while (p.nextToken() != JsonToken.END_OBJECT) { - String name = p.currentName(); - p.nextToken(); - signatures.put(name, p.readValueAs(byte[].class)); - } - - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, InputRecord.class, "Expected end of array"); - } - - return new UnicitySeal( - version, - networkId, - rootChainRoundNumber, - epoch, - timestamp, - previousHash, - hash, - signatures - ); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/UnicityTreeCertificateCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/UnicityTreeCertificateCbor.java deleted file mode 100644 index c2961e9..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/UnicityTreeCertificateCbor.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.bft; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import com.fasterxml.jackson.dataformat.cbor.CBORGenerator; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import org.unicitylabs.sdk.bft.UnicityTreeCertificate; - -public class UnicityTreeCertificateCbor { - - private UnicityTreeCertificateCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(UnicityTreeCertificate value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - ((CBORGenerator) gen).writeTag(1014); - gen.writeStartArray(value, 3); - gen.writeObject(value.getVersion()); - gen.writeObject(value.getPartitionIdentifier()); - gen.writeObject(value.getSteps()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public UnicityTreeCertificate deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, UnicityTreeCertificate.class, "Expected array value"); - } - p.nextToken(); - - int version = p.readValueAs(int.class); - int partitionIdentifier = p.readValueAs(int.class); - - if (p.nextToken() != JsonToken.START_ARRAY) { - throw MismatchedInputException.from(p, UnicityTreeCertificate.class, "Expected hash step list"); - } - - List steps = new ArrayList<>(); - while (p.nextToken() != JsonToken.END_ARRAY) { - steps.add(ctx.readValue(p, UnicityTreeCertificate.HashStep.class)); - } - - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, UnicityTreeCertificate.HashStep.class, "Expected end of array"); - } - - return new UnicityTreeCertificate(version, partitionIdentifier, steps); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/UnicityTreeCertificateHashStepCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/UnicityTreeCertificateHashStepCbor.java deleted file mode 100644 index 7fecbd4..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/bft/UnicityTreeCertificateHashStepCbor.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.bft; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import java.io.IOException; -import org.unicitylabs.sdk.bft.InputRecord; -import org.unicitylabs.sdk.bft.UnicityTreeCertificate; - -public class UnicityTreeCertificateHashStepCbor { - - private UnicityTreeCertificateHashStepCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(UnicityTreeCertificate.HashStep value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 2); - gen.writeObject(value.getKey()); - gen.writeObject(value.getHash()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public UnicityTreeCertificate.HashStep deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, UnicityTreeCertificate.class, "Expected array value"); - } - p.nextToken(); - - UnicityTreeCertificate.HashStep result = new UnicityTreeCertificate.HashStep( - p.readValueAs(int.class), - p.readValueAs(byte[].class) - ); - - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, InputRecord.class, "Expected end of array"); - } - - return result; - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/hash/DataHashCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/hash/DataHashCbor.java deleted file mode 100644 index cc93281..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/hash/DataHashCbor.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.hash; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.hash.DataHash; - -import java.io.IOException; - -public class DataHashCbor { - - private DataHashCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(DataHash value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeBinary(value.getImprint()); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public DataHash deserialize(JsonParser p, DeserializationContext cxt) throws IOException { - byte[] value = p.getBinaryValue(); - if (value == null) { - throw MismatchedInputException.from(p, DataHash.class, "Expected byte value"); - } - - try { - return DataHash.fromImprint(value); - } catch (Exception e) { - throw MismatchedInputException.from(p, DataHash.class, "Expected byte value"); - } - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/plain/SparseMerkleTreePathCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/plain/SparseMerkleTreePathCbor.java deleted file mode 100644 index b24034d..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/plain/SparseMerkleTreePathCbor.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.mtree.plain; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep; -import java.io.IOException; -import java.util.List; - -public class SparseMerkleTreePathCbor { - - private SparseMerkleTreePathCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(SparseMerkleTreePath value, JsonGenerator gen, - SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 2); - gen.writeObject(value.getRootHash()); - gen.writeObject(value.getSteps()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public SparseMerkleTreePath deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, SparseMerkleTreePath.class, "Expected object value"); - } - - return new SparseMerkleTreePath( - p.readValueAs(DataHash.class), - ctx.readValue(p, ctx.getTypeFactory() - .constructCollectionType(List.class, SparseMerkleTreePathStep.class))); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/plain/SparseMerkleTreePathStepBranchCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/plain/SparseMerkleTreePathStepBranchCbor.java deleted file mode 100644 index e6081f1..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/plain/SparseMerkleTreePathStepBranchCbor.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.mtree.plain; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep.Branch; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePathStep; -import java.io.IOException; - -public class SparseMerkleTreePathStepBranchCbor { - - private SparseMerkleTreePathStepBranchCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(Branch value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 1); - gen.writeObject(value.getValue()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public Branch deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, SparseMerkleSumTreePathStep.Branch.class, - "Expected array"); - } - if (p.nextToken() == JsonToken.END_ARRAY) { - return new Branch(null); - } - - Branch branch = new Branch(p.readValueAs(byte[].class)); - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, SparseMerkleSumTreePathStep.Branch.class, - "Expected end of array"); - } - - return branch; - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/plain/SparseMerkleTreePathStepCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/plain/SparseMerkleTreePathStepCbor.java deleted file mode 100644 index 44b72ad..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/plain/SparseMerkleTreePathStepCbor.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.mtree.plain; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep; -import org.unicitylabs.sdk.util.BigIntegerConverter; -import java.io.IOException; - -public class SparseMerkleTreePathStepCbor { - - private SparseMerkleTreePathStepCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(SparseMerkleTreePathStep value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 3); - gen.writeObject(BigIntegerConverter.encode(value.getPath())); - gen.writeObject(value.getSibling()); - gen.writeObject(value.getBranch()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public SparseMerkleTreePathStep deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, SparseMerkleTreePathStep.class, - "Expected array value"); - } - p.nextToken(); - - return new SparseMerkleTreePathStep( - BigIntegerConverter.decode(p.readValueAs(byte[].class)), - p.readValueAs(SparseMerkleTreePathStep.Branch.class), - p.readValueAs(SparseMerkleTreePathStep.Branch.class) - ); - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/sum/SparseMerkleSumTreePathCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/sum/SparseMerkleSumTreePathCbor.java deleted file mode 100644 index 6a836d2..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/sum/SparseMerkleSumTreePathCbor.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.mtree.sum; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePath; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePathStep; -import org.unicitylabs.sdk.util.BigIntegerConverter; -import java.io.IOException; -import java.util.List; - -public class SparseMerkleSumTreePathCbor { - - private SparseMerkleSumTreePathCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(SparseMerkleSumTreePath value, JsonGenerator gen, - SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 3); - gen.writeObject(value.getRoot().getHash()); - gen.writeObject(BigIntegerConverter.encode(value.getRoot().getCounter())); - gen.writeObject(value.getSteps()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public SparseMerkleSumTreePath deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, SparseMerkleSumTreePath.class, - "Expected object value"); - } - - return new SparseMerkleSumTreePath( - new SparseMerkleSumTreePath.Root( - p.readValueAs(DataHash.class), - BigIntegerConverter.decode(p.readValueAs(byte[].class)) - ), - ctx.readValue(p, ctx.getTypeFactory() - .constructCollectionType(List.class, SparseMerkleSumTreePathStep.class))); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/sum/SparseMerkleSumTreePathStepBranchCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/sum/SparseMerkleSumTreePathStepBranchCbor.java deleted file mode 100644 index 3a81394..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/sum/SparseMerkleSumTreePathStepBranchCbor.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.mtree.sum; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePathStep.Branch; -import org.unicitylabs.sdk.util.BigIntegerConverter; -import java.io.IOException; - -public class SparseMerkleSumTreePathStepBranchCbor { - - private SparseMerkleSumTreePathStepBranchCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(Branch value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 2); - gen.writeObject(value.getValue()); - gen.writeObject(BigIntegerConverter.encode(value.getCounter())); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public Branch deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, Branch.class, - "Expected array"); - } - p.nextToken(); - - Branch branch = new Branch( - p.readValueAs(byte[].class), - BigIntegerConverter.decode(p.readValueAs(byte[].class)) - ); - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, Branch.class, - "Expected end of array"); - } - - return branch; - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/sum/SparseMerkleSumTreePathStepCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/sum/SparseMerkleSumTreePathStepCbor.java deleted file mode 100644 index 08ec1e8..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/mtree/sum/SparseMerkleSumTreePathStepCbor.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.mtree.sum; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePathStep; -import org.unicitylabs.sdk.util.BigIntegerConverter; -import java.io.IOException; - -public class SparseMerkleSumTreePathStepCbor { - - private SparseMerkleSumTreePathStepCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(SparseMerkleSumTreePathStep value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 3); - gen.writeObject(BigIntegerConverter.encode(value.getPath())); - gen.writeObject(value.getSibling()); - gen.writeObject(value.getBranch()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public SparseMerkleSumTreePathStep deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, SparseMerkleSumTreePathStep.class, - "Expected array"); - } - p.nextToken(); - - return new SparseMerkleSumTreePathStep( - BigIntegerConverter.decode(p.readValueAs(byte[].class)), - p.readValueAs(SparseMerkleSumTreePathStep.Branch.class), - p.readValueAs(SparseMerkleSumTreePathStep.Branch.class) - ); - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/BurnPredicateCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/BurnPredicateCbor.java deleted file mode 100644 index aab9113..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/BurnPredicateCbor.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.predicate; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import java.io.IOException; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.predicate.embedded.BurnPredicate; -import org.unicitylabs.sdk.token.TokenId; -import org.unicitylabs.sdk.token.TokenType; - - -public class BurnPredicateCbor { - private BurnPredicateCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(BurnPredicate value, JsonGenerator gen, - SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 3); - gen.writeObject(value.getTokenId()); - gen.writeObject(value.getTokenType()); - gen.writeObject(value.getReason()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends - JsonDeserializer { - - @Override - public BurnPredicate deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, BurnPredicate.class, "Expected array value"); - } - p.nextToken(); - - TokenId tokenId = p.readValueAs(TokenId.class); - TokenType tokenType = p.readValueAs(TokenType.class); - DataHash reason = p.readValueAs(DataHash.class); - - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, BurnPredicate.class, "Expected end of array"); - } - - return new BurnPredicate(tokenId, tokenType, reason); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/DefaultPredicateCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/DefaultPredicateCbor.java deleted file mode 100644 index 143e2e6..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/DefaultPredicateCbor.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.predicate; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; -import org.unicitylabs.sdk.predicate.embedded.DefaultPredicate; - -public class DefaultPredicateCbor { - private DefaultPredicateCbor() { - } - - public static class Serializer extends JsonSerializer { - @Override - public void serialize(DefaultPredicate value, JsonGenerator gen, - SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 6); - gen.writeObject(value.getTokenId()); - gen.writeObject(value.getTokenType()); - gen.writeObject(value.getPublicKey()); - gen.writeObject(value.getSigningAlgorithm()); - gen.writeObject(value.getHashAlgorithm()); - gen.writeObject(value.getNonce()); - gen.writeEndArray(); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/MaskedPredicateCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/MaskedPredicateCbor.java deleted file mode 100644 index c253e67..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/MaskedPredicateCbor.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.predicate; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import java.io.IOException; -import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.predicate.embedded.MaskedPredicate; -import org.unicitylabs.sdk.token.TokenId; -import org.unicitylabs.sdk.token.TokenType; - - -public class MaskedPredicateCbor { - - private MaskedPredicateCbor() { - } - - public static class Deserializer extends - JsonDeserializer { - - @Override - public MaskedPredicate deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, MaskedPredicate.class, "Expected array value"); - } - p.nextToken(); - - TokenId tokenId = p.readValueAs(TokenId.class); - TokenType tokenType = p.readValueAs(TokenType.class); - byte[] publicKey = p.readValueAs(byte[].class); - String signingAlgorithm = p.readValueAs(String.class); - HashAlgorithm hashAlgorithm = p.readValueAs(HashAlgorithm.class); - byte[] nonce = p.readValueAs(byte[].class); - - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, MaskedPredicate.class, "Expected end of array"); - } - - return new MaskedPredicate( - tokenId, - tokenType, - publicKey, - signingAlgorithm, - hashAlgorithm, - nonce - ); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/SerializablePredicateCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/SerializablePredicateCbor.java deleted file mode 100644 index b1731ec..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/SerializablePredicateCbor.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.predicate; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.predicate.EncodedPredicate; -import org.unicitylabs.sdk.predicate.PredicateEngineType; -import org.unicitylabs.sdk.predicate.SerializablePredicate; -import org.unicitylabs.sdk.predicate.Predicate; -import java.io.IOException; - -public class SerializablePredicateCbor { - - private SerializablePredicateCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(SerializablePredicate value, JsonGenerator gen, - SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 3); - gen.writeObject(value.getEngine().ordinal()); - gen.writeObject(value.encode()); - gen.writeObject(value.encodeParameters()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public SerializablePredicate deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, Predicate.class, "Expected array value"); - } - - p.nextToken(); - PredicateEngineType engine = p.readValueAs(PredicateEngineType.class); - byte[] code = p.readValueAs(byte[].class); - byte[] parameters = p.readValueAs(byte[].class); - - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, Predicate.class, "Expected end of array"); - } - - return new EncodedPredicate(engine, code, parameters); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/UnmaskedPredicateCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/UnmaskedPredicateCbor.java deleted file mode 100644 index 852221f..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/predicate/UnmaskedPredicateCbor.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.predicate; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import java.io.IOException; -import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.predicate.embedded.UnmaskedPredicate; -import org.unicitylabs.sdk.token.TokenId; -import org.unicitylabs.sdk.token.TokenType; - - -public class UnmaskedPredicateCbor { - - private UnmaskedPredicateCbor() { - } - - public static class Deserializer extends - JsonDeserializer { - - @Override - public UnmaskedPredicate deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, UnmaskedPredicate.class, "Expected array value"); - } - p.nextToken(); - - TokenId tokenId = p.readValueAs(TokenId.class); - TokenType tokenType = p.readValueAs(TokenType.class); - byte[] publicKey = p.readValueAs(byte[].class); - String signingAlgorithm = p.readValueAs(String.class); - HashAlgorithm hashAlgorithm = p.readValueAs(HashAlgorithm.class); - byte[] nonce = p.readValueAs(byte[].class); - - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, UnmaskedPredicate.class, "Expected end of array"); - } - - return new UnmaskedPredicate( - tokenId, - tokenType, - publicKey, - signingAlgorithm, - hashAlgorithm, - nonce - ); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/TokenCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/TokenCbor.java deleted file mode 100644 index 1f13c50..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/TokenCbor.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.token; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.token.TokenState; -import org.unicitylabs.sdk.transaction.MintTransactionData; -import org.unicitylabs.sdk.transaction.Transaction; -import org.unicitylabs.sdk.transaction.TransferTransactionData; -import java.io.IOException; -import java.util.List; - -public class TokenCbor { - - private TokenCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(Token value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 5); - gen.writeObject(value.getVersion()); - gen.writeObject(value.getGenesis()); - List> transactions = value.getTransactions(); - gen.writeStartArray(value, transactions.size()); - for (Transaction transaction : transactions) { - gen.writeObject(transaction); - } - gen.writeEndArray(); - gen.writeObject(value.getState()); - gen.writeObject(value.getNametags()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends - JsonDeserializer>> { - - @Override - public Token> deserialize(JsonParser p, - DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, Token.class, "Expected array value"); - } - - String version = p.readValueAs(String.class); - if (!Token.TOKEN_VERSION.equals(version)) { - throw MismatchedInputException.from(p, Token.class, - "Expected version to be " + Token.TOKEN_VERSION); - } - - return new Token<>( - p.readValueAs(TokenState.class), - ctx.readValue(p, ctx.getTypeFactory().constructParametricType(Transaction.class, MintTransactionData.class)), - ctx.readValue(p, ctx.getTypeFactory().constructCollectionType(List.class, ctx.getTypeFactory().constructParametricType(Transaction.class, TransferTransactionData.class))), - ctx.readValue(p, ctx.getTypeFactory().constructCollectionType(List.class, Token.class)) - ); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/TokenIdCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/TokenIdCbor.java deleted file mode 100644 index 4f582d8..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/TokenIdCbor.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.token; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.token.TokenId; -import java.io.IOException; - -public class TokenIdCbor { - - private TokenIdCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(TokenId value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeBinary(value.getBytes()); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public TokenId deserialize(JsonParser p, DeserializationContext cxt) throws IOException { - byte[] value = p.getBinaryValue(); - if (value == null) { - throw MismatchedInputException.from(p, TokenId.class, "Expected byte value"); - } - - try { - return new TokenId(value); - } catch (Exception e) { - throw MismatchedInputException.from(p, TokenId.class, "Expected byte value"); - } - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/TokenStateCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/TokenStateCbor.java deleted file mode 100644 index 750b792..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/TokenStateCbor.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.token; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.predicate.Predicate; -import org.unicitylabs.sdk.token.TokenState; -import java.io.IOException; - -public class TokenStateCbor { - - private TokenStateCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(TokenState value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 2); - gen.writeObject(value.getPredicate()); - gen.writeObject(value.getData()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends - JsonDeserializer { - - @Override - public TokenState deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, TokenState.class, "Expected array"); - } - - return new TokenState(p.readValueAs(Predicate.class), p.readValueAs(byte[].class)); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/TokenTypeCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/TokenTypeCbor.java deleted file mode 100644 index d6f0bb1..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/TokenTypeCbor.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.token; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.token.TokenType; - -import java.io.IOException; - -public class TokenTypeCbor { - - private TokenTypeCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(TokenType value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeBinary(value.getBytes()); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public TokenType deserialize(JsonParser p, DeserializationContext cxt) throws IOException { - byte[] value = p.getBinaryValue(); - if (value == null) { - throw MismatchedInputException.from(p, TokenType.class, "Expected byte value"); - } - - try { - return new TokenType(value); - } catch (Exception e) { - throw MismatchedInputException.from(p, TokenType.class, "Expected byte value"); - } - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/fungible/TokenCoinDataCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/fungible/TokenCoinDataCbor.java deleted file mode 100644 index 0d30eb4..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/token/fungible/TokenCoinDataCbor.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.token.fungible; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.token.fungible.CoinId; -import org.unicitylabs.sdk.token.fungible.TokenCoinData; -import org.unicitylabs.sdk.util.BigIntegerConverter; -import java.io.IOException; -import java.math.BigInteger; -import java.util.LinkedHashMap; -import java.util.Map; - -public class TokenCoinDataCbor { - - private TokenCoinDataCbor() { - } - - - public static class Serializer extends JsonSerializer { - - public Serializer() { - } - - @Override - public void serialize(TokenCoinData value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - Map coins = value.getCoins(); - gen.writeStartArray(value, coins.size()); - for (Map.Entry entry : coins.entrySet()) { - gen.writeStartArray(entry, 2); - gen.writeObject(entry.getKey().getBytes()); - gen.writeObject(BigIntegerConverter.encode(entry.getValue())); - gen.writeEndArray(); - } - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - public Deserializer() { - } - - @Override - public TokenCoinData deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, TokenCoinData.class, "Expected array value"); - } - - Map coins = new LinkedHashMap<>(); - while (p.nextToken() != JsonToken.END_ARRAY) { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, TokenCoinData.class, - "Expected array value for coin entry"); - } - - CoinId coinId = new CoinId(p.readValueAs(byte[].class)); - if (coins.containsKey(coinId)) { - throw MismatchedInputException.from(p, TokenCoinData.class, - "Duplicate coin ID in coin data: " + coinId); - } - - BigInteger amount = BigIntegerConverter.decode(p.readValueAs(byte[].class)); - coins.put(coinId, amount); - } - - return new TokenCoinData(coins); - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/InclusionProofCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/InclusionProofCbor.java deleted file mode 100644 index 8cc9d59..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/InclusionProofCbor.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.transaction; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.api.Authenticator; -import org.unicitylabs.sdk.bft.UnicityCertificate; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; -import org.unicitylabs.sdk.transaction.InclusionProof; -import java.io.IOException; - -public class InclusionProofCbor { - - private InclusionProofCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(InclusionProof value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 4); - gen.writeObject(value.getMerkleTreePath()); - gen.writeObject(value.getAuthenticator()); - gen.writeObject(value.getTransactionHash()); - gen.writeObject(value.getUnicityCertificate()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public InclusionProof deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, InclusionProof.class, "Expected array value"); - } - - return new InclusionProof( - p.readValueAs(SparseMerkleTreePath.class), - p.readValueAs(Authenticator.class), - p.readValueAs(DataHash.class), - p.readValueAs(UnicityCertificate.class) - ); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/MintTransactionDataCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/MintTransactionDataCbor.java deleted file mode 100644 index 0de220c..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/MintTransactionDataCbor.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.transaction; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.address.Address; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.token.TokenId; -import org.unicitylabs.sdk.token.TokenType; -import org.unicitylabs.sdk.token.fungible.TokenCoinData; -import org.unicitylabs.sdk.transaction.MintTransactionData; -import org.unicitylabs.sdk.transaction.MintTransactionReason; -import java.io.IOException; - -public class MintTransactionDataCbor { - - private MintTransactionDataCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(MintTransactionData value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 8); - gen.writeObject(value.getTokenId()); - gen.writeObject(value.getTokenType()); - gen.writeObject(value.getTokenData()); - gen.writeObject(value.getCoinData()); - gen.writeObject(value.getRecipient()); - gen.writeObject(value.getSalt()); - gen.writeObject(value.getDataHash()); - gen.writeObject(value.getReason()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends - JsonDeserializer> { - - @Override - public MintTransactionData deserialize(JsonParser p, - DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, MintTransactionData.class, "Expected array value"); - } - - return new MintTransactionData<>( - p.readValueAs(TokenId.class), - p.readValueAs(TokenType.class), - p.readValueAs(byte[].class), - p.readValueAs(TokenCoinData.class), - p.readValueAs(Address.class), - p.readValueAs(byte[].class), - p.readValueAs(DataHash.class), - p.readValueAs(MintTransactionReason.class) - ); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/MintTransactionReasonCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/MintTransactionReasonCbor.java deleted file mode 100644 index a042b7c..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/MintTransactionReasonCbor.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.transaction; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import org.unicitylabs.sdk.transaction.MintTransactionReason; -import org.unicitylabs.sdk.transaction.split.SplitMintReason; -import java.io.IOException; - -public class MintTransactionReasonCbor { - - private MintTransactionReasonCbor() { - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public MintTransactionReason deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - return p.readValueAs(SplitMintReason.class); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/TransactionCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/TransactionCbor.java deleted file mode 100644 index 97c263a..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/TransactionCbor.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.transaction; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.BeanProperty; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.transaction.InclusionProof; -import org.unicitylabs.sdk.transaction.Transaction; -import java.io.IOException; - -public class TransactionCbor { - - private TransactionCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(Transaction value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 2); - gen.writeObject(value.getData()); - gen.writeObject(value.getInclusionProof()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer> implements - ContextualDeserializer { - - private final JavaType transactionType; - - public Deserializer() { - this.transactionType = null; - } - - private Deserializer(JavaType valueType) { - this.transactionType = valueType; - } - - @Override - public JsonDeserializer createContextual(DeserializationContext ctx, - BeanProperty property) { - JavaType wrapperType = ctx.getContextualType(); - JavaType valueType = wrapperType != null ? wrapperType.containedType(0) : null; - return new TransactionCbor.Deserializer(valueType); - } - - @Override - public Transaction deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, Transaction.class, "Expected array value"); - } - - return new Transaction<>( - p.getCodec().readValue(p, this.transactionType), - p.readValueAs(InclusionProof.class) - ); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/TransferTransactionDataCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/TransferTransactionDataCbor.java deleted file mode 100644 index c16055c..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/TransferTransactionDataCbor.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.transaction; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.address.Address; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.token.TokenState; -import org.unicitylabs.sdk.transaction.TransferTransactionData; -import java.io.IOException; -import java.util.List; - -public class TransferTransactionDataCbor { - private TransferTransactionDataCbor() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(TransferTransactionData value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 6); - gen.writeObject(value.getSourceState()); - gen.writeObject(value.getRecipient()); - gen.writeObject(value.getSalt()); - gen.writeObject(value.getDataHash()); - gen.writeObject(value.getMessage()); - gen.writeObject(value.getNametags()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public TransferTransactionData deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, TransferTransactionData.class, "Expected array value"); - } - - return new TransferTransactionData( - p.readValueAs(TokenState.class), - p.readValueAs(Address.class), - p.readValueAs(byte[].class), - p.readValueAs(DataHash.class), - p.readValueAs(byte[].class), - ctx.readValue(p, ctx.getTypeFactory().constructCollectionType(List.class, Token.class)) - ); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/split/SplitMintReasonCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/split/SplitMintReasonCbor.java deleted file mode 100644 index 0a5888c..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/split/SplitMintReasonCbor.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.transaction.split; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.transaction.MintReasonType; -import org.unicitylabs.sdk.transaction.split.SplitMintReason; -import org.unicitylabs.sdk.transaction.split.SplitMintReasonProof; -import java.io.IOException; -import java.util.List; - -public class SplitMintReasonCbor { - - private SplitMintReasonCbor() { - } - - - public static class Serializer extends JsonSerializer { - - public Serializer() { - } - - @Override - public void serialize(SplitMintReason value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 3); - gen.writeObject(value.getType()); - gen.writeObject(value.getToken()); - gen.writeObject(value.getProofs()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - public Deserializer() { - } - - @Override - public SplitMintReason deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, SplitMintReason.class, "Expected array value"); - } - - String type = p.readValueAs(String.class); - - if (!MintReasonType.TOKEN_SPLIT.name().equals(type)) { - throw MismatchedInputException.from(p, SplitMintReason.class, - "Expected type to be " + MintReasonType.TOKEN_SPLIT); - } - - return new SplitMintReason( - p.readValueAs(Token.class), - ctx.readValue(p, - ctx.getTypeFactory().constructCollectionType(List.class, SplitMintReasonProof.class)) - ); - } - } - -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/split/SplitMintReasonProofCbor.java b/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/split/SplitMintReasonProofCbor.java deleted file mode 100644 index eadc3fe..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/cbor/transaction/split/SplitMintReasonProofCbor.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.unicitylabs.sdk.serializer.cbor.transaction.split; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePath; -import org.unicitylabs.sdk.token.fungible.CoinId; -import org.unicitylabs.sdk.transaction.split.SplitMintReasonProof; -import java.io.IOException; - -public class SplitMintReasonProofCbor { - - private SplitMintReasonProofCbor() { - } - - - public static class Serializer extends JsonSerializer { - - public Serializer() { - } - - @Override - public void serialize(SplitMintReasonProof value, JsonGenerator gen, - SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 3); - gen.writeObject(value.getCoinId().getBytes()); - gen.writeObject(value.getAggregationPath()); - gen.writeObject(value.getCoinTreePath()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - public Deserializer() { - } - - @Override - public SplitMintReasonProof deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, SplitMintReasonProof.class, "Expected array"); - } - - return new SplitMintReasonProof( - new CoinId(p.readValueAs(byte[].class)), - p.readValueAs(SparseMerkleTreePath.class), - p.readValueAs(SparseMerkleSumTreePath.class) - ); - } - } - -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/ByteArrayJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/ByteArrayJson.java new file mode 100644 index 0000000..a65444a --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/serializer/json/ByteArrayJson.java @@ -0,0 +1,85 @@ +package org.unicitylabs.sdk.serializer.json; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import org.unicitylabs.sdk.util.HexConverter; + +/** + * Byte array serializer and deserializer implementation. + */ +public class ByteArrayJson { + + private ByteArrayJson() { + } + + /** + * Byte array serializer. + */ + public static class Serializer extends StdSerializer { + + /** + * Create serializer. + */ + public Serializer() { + super(byte[].class); + } + + /** + * Serialize byte array. + * + * @param value byte array + * @param gen json generator + * @param serializers serializer provider + * @throws IOException on serialization failure + */ + @Override + public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + gen.writeString(HexConverter.encode(value)); + } + } + + /** + * Byte array deserializer. + */ + public static class Deserializer extends StdDeserializer { + + /** + * Create deserializer. + */ + public Deserializer() { + super(byte[].class); + } + + /** + * Deserialize byte array. + * + * @param p Parser used for reading JSON content + * @param ctx Context that can be used to access information about this deserialization + * activity. + * @return bytes + * @throws IOException on deserialization failure + */ + @Override + public byte[] deserialize(JsonParser p, DeserializationContext ctx) throws IOException { + if (p.getCurrentToken() != JsonToken.VALUE_STRING) { + throw MismatchedInputException.from(p, byte[].class, + "Expected hex string value"); + } + + try { + return HexConverter.decode(p.readValueAs(String.class)); + } catch (Exception e) { + throw MismatchedInputException.from(p, byte[].class, "Expected hex string value"); + } + } + } +} + diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/JsonSerializationException.java b/src/main/java/org/unicitylabs/sdk/serializer/json/JsonSerializationException.java new file mode 100644 index 0000000..1594c80 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/serializer/json/JsonSerializationException.java @@ -0,0 +1,35 @@ +package org.unicitylabs.sdk.serializer.json; + +/** + * Json serialization exception for json errors. + */ +public class JsonSerializationException extends RuntimeException { + + /** + * Create serialization exception from class and cause. + * + * @param c class + * @param cause cause + */ + public JsonSerializationException(Class c, Throwable cause) { + super(c.getName(), cause); + } + + /** + * Create serialization exception from cause. + * + * @param cause cause + */ + public JsonSerializationException(Throwable cause) { + super(cause); + } + + /** + * Create serialization exception from message. + * + * @param message error + */ + public JsonSerializationException(String message) { + super(message); + } +} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/address/AddressJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/address/AddressJson.java deleted file mode 100644 index 7b63e9e..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/address/AddressJson.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.address; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.address.Address; -import org.unicitylabs.sdk.address.AddressFactory; -import java.io.IOException; - -public class AddressJson { - - private AddressJson() { - } - - public static class Serializer extends JsonSerializer
{ - - public Serializer() { - } - - @Override - public void serialize(Address value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeString(value.getAddress()); - } - } - - public static class Deserializer extends JsonDeserializer
{ - - public Deserializer() { - } - - @Override - public Address deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (p.getCurrentToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, Address.class, - "Expected string value"); - } - - try { - return AddressFactory.createAddress(p.readValueAs(String.class)); - } catch (Exception e) { - throw MismatchedInputException.from(p, Address.class, - String.format("Invalid address: %s", e.getMessage())); - } - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/api/AuthenticatorJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/api/AuthenticatorJson.java deleted file mode 100644 index 47dfd82..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/api/AuthenticatorJson.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.api; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.api.Authenticator; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.signing.Signature; -import org.unicitylabs.sdk.util.HexConverter; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class AuthenticatorJson { - - private static final String ALGORITHM_FIELD = "algorithm"; - private static final String PUBLIC_KEY_FIELD = "publicKey"; - private static final String SIGNATURE_FIELD = "signature"; - private static final String STATE_HASH_FIELD = "stateHash"; - - private AuthenticatorJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(Authenticator value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(ALGORITHM_FIELD, value.getAlgorithm()); - gen.writeObjectField(PUBLIC_KEY_FIELD, HexConverter.encode(value.getPublicKey())); - gen.writeObjectField(SIGNATURE_FIELD, HexConverter.encode(value.getSignature().encode())); - gen.writeObjectField(STATE_HASH_FIELD, value.getStateHash()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public Authenticator deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - String algorithm = null; - byte[] publicKey = null; - Signature signature = null; - DataHash stateHash = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, Authenticator.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, Authenticator.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - if (p.getCurrentToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, Authenticator.class, "Expected string value"); - } - - try { - switch (fieldName) { - case ALGORITHM_FIELD: - algorithm = p.readValueAs(String.class); - break; - case PUBLIC_KEY_FIELD: - publicKey = p.readValueAs(byte[].class); - break; - case SIGNATURE_FIELD: - signature = Signature.decode(p.readValueAs(byte[].class)); - break; - case STATE_HASH_FIELD: - stateHash = p.readValueAs(DataHash.class); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, Authenticator.class, fieldName); - } - } - - Set missingFields = new HashSet<>( - Set.of(ALGORITHM_FIELD, PUBLIC_KEY_FIELD, SIGNATURE_FIELD, STATE_HASH_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, Authenticator.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new Authenticator(algorithm, publicKey, signature, stateHash); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/api/BlockHeightResponseJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/api/BlockHeightResponseJson.java deleted file mode 100644 index 6b16602..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/api/BlockHeightResponseJson.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.api; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.api.BlockHeightResponse; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class BlockHeightResponseJson { - - private static final String BLOCK_NUMBER_FIELD = "blockNumber"; - - private BlockHeightResponseJson() { - } - - public static class Deserializer extends JsonDeserializer{ - - public Deserializer() { - } - - - @Override - public BlockHeightResponse deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - Long blockNumber = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, BlockHeightResponse.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, BlockHeightResponse.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case BLOCK_NUMBER_FIELD: - if (p.getCurrentToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, BlockHeightResponse.class, - "Expected string value"); - } - blockNumber = p.readValueAs(Long.class); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, BlockHeightResponse.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of(BLOCK_NUMBER_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, BlockHeightResponse.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new BlockHeightResponse(blockNumber); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/api/InclusionProofRequestJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/api/InclusionProofRequestJson.java deleted file mode 100644 index 51c9974..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/api/InclusionProofRequestJson.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.api; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import org.unicitylabs.sdk.api.InclusionProofRequest; -import java.io.IOException; - -public class InclusionProofRequestJson { - - private static final String REQUEST_ID_FIELD = "requestId"; - - private InclusionProofRequestJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(InclusionProofRequest value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(REQUEST_ID_FIELD, value.getRequestId()); - gen.writeEndObject(); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/api/InclusionProofResponseJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/api/InclusionProofResponseJson.java deleted file mode 100644 index 4c94a82..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/api/InclusionProofResponseJson.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.api; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; -import org.unicitylabs.sdk.api.InclusionProofResponse; -import org.unicitylabs.sdk.transaction.InclusionProof; - -public class InclusionProofResponseJson { - - private static final String INCLUSION_PROOF_FIELD = "inclusionProof"; - - private InclusionProofResponseJson() { - } - - public static class Deserializer extends JsonDeserializer { - - public Deserializer() { - } - - - @Override - public InclusionProofResponse deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - InclusionProof inclusionProof = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, InclusionProofResponse.class, - "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, InclusionProofResponse.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case INCLUSION_PROOF_FIELD: - inclusionProof = p.readValueAs(InclusionProof.class); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, InclusionProofResponse.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of(INCLUSION_PROOF_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, InclusionProofResponse.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new InclusionProofResponse(inclusionProof); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/api/RequestIdJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/api/RequestIdJson.java deleted file mode 100644 index 0052fd6..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/api/RequestIdJson.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.api; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import org.unicitylabs.sdk.api.RequestId; -import org.unicitylabs.sdk.hash.DataHash; -import java.io.IOException; - -public class RequestIdJson { - - private RequestIdJson() { - } - - public static class Serializer extends JsonSerializer { - - public Serializer() { - } - - @Override - public void serialize(RequestId value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeObject(value.getHash()); - } - } - - public static class Deserializer extends JsonDeserializer { - - public Deserializer() { - } - - @Override - public RequestId deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - return new RequestId(p.readValueAs(DataHash.class)); - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/api/SubmitCommitmentRequestJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/api/SubmitCommitmentRequestJson.java deleted file mode 100644 index 124f664..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/api/SubmitCommitmentRequestJson.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.api; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import org.unicitylabs.sdk.api.SubmitCommitmentRequest; -import java.io.IOException; - -public class SubmitCommitmentRequestJson { - - private static final String REQUEST_ID_FIELD = "requestId"; - private static final String TRANSACTION_HASH_FIELD = "transactionHash"; - private static final String AUTHENTICATOR_FIELD = "authenticator"; - private static final String RECEIPT_FIELD = "receipt"; - - private SubmitCommitmentRequestJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(SubmitCommitmentRequest value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(REQUEST_ID_FIELD, value.getRequestId()); - gen.writeObjectField(TRANSACTION_HASH_FIELD, value.getTransactionHash()); - gen.writeObjectField(AUTHENTICATOR_FIELD, value.getAuthenticator()); - gen.writeObjectField(RECEIPT_FIELD, value.getReceipt()); - gen.writeEndObject(); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/api/SubmitCommitmentResponseJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/api/SubmitCommitmentResponseJson.java deleted file mode 100644 index ff33616..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/api/SubmitCommitmentResponseJson.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.api; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.api.SubmitCommitmentResponse; -import org.unicitylabs.sdk.api.SubmitCommitmentStatus; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class SubmitCommitmentResponseJson { - - private static final String STATUS_FIELD = "status"; - - private SubmitCommitmentResponseJson() { - } - - public static class Deserializer extends JsonDeserializer { - - public Deserializer() { - } - - - @Override - public SubmitCommitmentResponse deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - SubmitCommitmentStatus status = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, SubmitCommitmentResponse.class, - "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, SubmitCommitmentResponse.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case STATUS_FIELD: - if (p.getCurrentToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, SubmitCommitmentResponse.class, - "Expected string value"); - } - status = SubmitCommitmentStatus.valueOf(p.readValueAs(String.class)); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, SubmitCommitmentResponse.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of(STATUS_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, SubmitCommitmentResponse.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new SubmitCommitmentResponse(status); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/bft/RootTrustBaseJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/bft/RootTrustBaseJson.java deleted file mode 100644 index 8d3ce39..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/bft/RootTrustBaseJson.java +++ /dev/null @@ -1,159 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.bft; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import java.io.IOException; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import org.unicitylabs.sdk.bft.RootTrustBase; - -public class RootTrustBaseJson { - private static final String VERSION_FIELD = "version"; - private static final String NETWORK_ID_FIELD = "networkId"; - private static final String EPOCH_FIELD = "epoch"; - private static final String EPOCH_START_ROUND_FIELD = "epochStartRound"; - private static final String ROOT_NODES_FIELD = "rootNodes"; - private static final String QUORUM_THRESHOLD_FIELD = "quorumThreshold"; - private static final String STATE_HASH_FIELD = "stateHash"; - private static final String CHANGE_RECORD_HASH_FIELD = "changeRecordHash"; - private static final String PREVIOUS_ENTRY_HASH_FIELD = "previousEntryHash"; - private static final String SIGNATURES_FIELD = "signatures"; - - private RootTrustBaseJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(RootTrustBase value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(VERSION_FIELD, value.getVersion()); - gen.writeObjectField(NETWORK_ID_FIELD, value.getNetworkId()); - gen.writeObjectField(EPOCH_FIELD, value.getEpoch()); - gen.writeObjectField(EPOCH_START_ROUND_FIELD, value.getEpochStartRound()); - gen.writeObjectField(ROOT_NODES_FIELD, value.getRootNodes()); - gen.writeObjectField(QUORUM_THRESHOLD_FIELD, value.getQuorumThreshold()); - gen.writeObjectField(STATE_HASH_FIELD, value.getStateHash()); - gen.writeObjectField(CHANGE_RECORD_HASH_FIELD, value.getChangeRecordHash()); - gen.writeObjectField(PREVIOUS_ENTRY_HASH_FIELD, value.getPreviousEntryHash()); - gen.writeObjectField(SIGNATURES_FIELD, value.getSignatures()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public RootTrustBase deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - Long version = null; - Integer networkId = null; - Long epoch = null; - Long epochStartRound = null; - Set rootNodes = null; - Integer quorumThreshold = null; - byte[] stateHash = null; - byte[] changeRecordHash = null; - byte[] previousEntryHash = null; - Map signatures = null; - - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, RootTrustBase.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, RootTrustBase.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - - try { - switch (fieldName) { - case VERSION_FIELD: - version = p.readValueAs(Long.class); - break; - case NETWORK_ID_FIELD: - networkId = p.readValueAs(Integer.class); - break; - case EPOCH_FIELD: - epoch = p.readValueAs(Long.class); - break; - case EPOCH_START_ROUND_FIELD: - epochStartRound = p.readValueAs(Long.class); - break; - case ROOT_NODES_FIELD: - rootNodes = ctx.readValue( - p, - ctx.getTypeFactory().constructCollectionType(Set.class, RootTrustBase.NodeInfo.class) - ); - break; - case QUORUM_THRESHOLD_FIELD: - quorumThreshold = p.readValueAs(Integer.class); - break; - case STATE_HASH_FIELD: - stateHash = p.readValueAs(byte[].class); - break; - case CHANGE_RECORD_HASH_FIELD: - changeRecordHash = p.readValueAs(byte[].class); - break; - case PREVIOUS_ENTRY_HASH_FIELD: - previousEntryHash = p.readValueAs(byte[].class); - break; - case SIGNATURES_FIELD: - signatures = ctx.readValue( - p, - ctx.getTypeFactory().constructMapType(Map.class, String.class, byte[].class) - ); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, RootTrustBase.class, fieldName); - } - } - - Set missingFields = new HashSet<>( - Set.of(VERSION_FIELD, NETWORK_ID_FIELD, EPOCH_FIELD, EPOCH_START_ROUND_FIELD, - ROOT_NODES_FIELD, QUORUM_THRESHOLD_FIELD, STATE_HASH_FIELD, CHANGE_RECORD_HASH_FIELD, - PREVIOUS_ENTRY_HASH_FIELD, SIGNATURES_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, RootTrustBase.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new RootTrustBase( - version, - networkId, - epoch, - epochStartRound, - rootNodes, - quorumThreshold, - stateHash, - changeRecordHash, - previousEntryHash, - signatures - ); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/bft/RootTrustBaseNodeInfoJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/bft/RootTrustBaseNodeInfoJson.java deleted file mode 100644 index 05edc08..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/bft/RootTrustBaseNodeInfoJson.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.bft; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; -import org.unicitylabs.sdk.bft.RootTrustBase; - -public class RootTrustBaseNodeInfoJson { - private static final String NODE_ID_FIELD = "nodeId"; - private static final String SIGNING_KEY_FIELD = "sigKey"; - private static final String STAKED_AMOUNT_FIELD = "stake"; - - private RootTrustBaseNodeInfoJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(RootTrustBase.NodeInfo value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(NODE_ID_FIELD, value.getNodeId()); - gen.writeObjectField(SIGNING_KEY_FIELD, value.getSigningKey()); - gen.writeObjectField(STAKED_AMOUNT_FIELD, value.getStakedAmount()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public RootTrustBase.NodeInfo deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - String nodeId = null; - byte[] signingKey = null; - long stakedAmount = 0; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, RootTrustBase.NodeInfo.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, RootTrustBase.NodeInfo.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - - try { - switch (fieldName) { - case NODE_ID_FIELD: - nodeId = p.readValueAs(String.class); - break; - case SIGNING_KEY_FIELD: - signingKey = p.readValueAs(byte[].class); - break; - case STAKED_AMOUNT_FIELD: - stakedAmount = p.readValueAs(Long.class); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, RootTrustBase.NodeInfo.class, fieldName); - } - } - - Set missingFields = new HashSet<>( - Set.of(NODE_ID_FIELD, SIGNING_KEY_FIELD, STAKED_AMOUNT_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, RootTrustBase.NodeInfo.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new RootTrustBase.NodeInfo(nodeId, signingKey, stakedAmount); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/hash/DataHashJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/hash/DataHashJson.java deleted file mode 100644 index 81f32b5..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/hash/DataHashJson.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.hash; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.util.HexConverter; - -import java.io.IOException; - -/** - * DataHashJson provides JSON serialization and deserialization for DataHash objects. It uses Hex - * encoding to represent the hash imprint as a string in JSON. - */ -public class DataHashJson { - - private DataHashJson() { - } - - /** - * Serializer for DataHash objects. Serializes the DataHash imprint as a Hex-encoded string. - */ - public static class Serializer extends JsonSerializer { - - /** - * Default constructor for the serializer. - */ - public Serializer() { - } - - @Override - public void serialize(DataHash value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeString(HexConverter.encode(value.getImprint())); - } - } - - /** - * Deserializer for DataHash objects. Expects a Hex-encoded string and converts it to a DataHash - * object. - */ - public static class Deserializer extends JsonDeserializer { - - /** - * Default constructor for the deserializer. - */ - public Deserializer() { - } - - @Override - public DataHash deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (p.getCurrentToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, DataHash.class, - "Expected string value"); - } - - try { - return DataHash.fromImprint(p.readValueAs(byte[].class)); - } catch (Exception e) { - throw MismatchedInputException.from(p, DataHash.class, "Expected bytes"); - } - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/jsonrpc/JsonRpcErrorJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/jsonrpc/JsonRpcErrorJson.java deleted file mode 100644 index 07bd3f8..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/jsonrpc/JsonRpcErrorJson.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.jsonrpc; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.jsonrpc.JsonRpcError; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class JsonRpcErrorJson { - - private static final String CODE_FIELD = "code"; - private static final String MESSAGE_FIELD = "message"; - - private JsonRpcErrorJson() { - } - - public static class Deserializer extends JsonDeserializer { - - - public Deserializer() { - } - - @Override - public JsonRpcError deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - Integer code = null; - String message = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, JsonRpcError.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, JsonRpcError.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - - try { - switch (fieldName) { - case CODE_FIELD: - // TODO: Check field type? - code = p.readValueAs(Integer.class); - break; - case MESSAGE_FIELD: - if (p.getCurrentToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, JsonRpcError.class, - "Expected string value"); - } - message = p.readValueAs(String.class); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, JsonRpcError.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of(CODE_FIELD, MESSAGE_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, JsonRpcError.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new JsonRpcError(code, message); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/jsonrpc/JsonRpcRequestJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/jsonrpc/JsonRpcRequestJson.java deleted file mode 100644 index e195bf8..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/jsonrpc/JsonRpcRequestJson.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.jsonrpc; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import org.unicitylabs.sdk.jsonrpc.JsonRpcRequest; -import java.io.IOException; - -public class JsonRpcRequestJson { - - private static final String ID_FIELD = "id"; - private static final String VERSION_FIELD = "jsonrpc"; - private static final String METHOD_FIELD = "method"; - private static final String PARAMS_FIELD = "params"; - - private JsonRpcRequestJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(JsonRpcRequest value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(ID_FIELD, value.getId()); - gen.writeObjectField(VERSION_FIELD, value.getVersion()); - gen.writeObjectField(METHOD_FIELD, value.getMethod()); - gen.writeObjectField(PARAMS_FIELD, value.getParams()); - gen.writeEndObject(); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/jsonrpc/JsonRpcResponseJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/jsonrpc/JsonRpcResponseJson.java deleted file mode 100644 index bd560ee..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/jsonrpc/JsonRpcResponseJson.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.jsonrpc; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.BeanProperty; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.jsonrpc.JsonRpcError; -import org.unicitylabs.sdk.jsonrpc.JsonRpcResponse; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - -public class JsonRpcResponseJson { - - private static final String VERSION_FIELD = "jsonrpc"; - private static final String RESULT_FIELD = "result"; - private static final String ERROR_FIELD = "error"; - private static final String ID_FIELD = "id"; - - private JsonRpcResponseJson() { - } - - public static class Deserializer extends JsonDeserializer> implements - ContextualDeserializer { - - private final JavaType resultType; - - public Deserializer() { - this.resultType = null; - } - - private Deserializer(JavaType valueType) { - this.resultType = valueType; - } - - @Override - public JsonDeserializer createContextual(DeserializationContext ctxt, - BeanProperty property) { - JavaType wrapperType = ctxt.getContextualType(); - JavaType valueType = wrapperType != null ? wrapperType.containedType(0) : null; - return new Deserializer(valueType); - } - - @Override - public JsonRpcResponse deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - String version = null; - Object result = null; - JsonRpcError error = null; - UUID id = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, JsonRpcResponse.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, JsonRpcResponse.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case VERSION_FIELD: - if (p.getCurrentToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, JsonRpcResponse.class, - "Expected string value"); - } - version = p.readValueAs(String.class); - break; - case RESULT_FIELD: - result = p.getCodec().readValue(p, this.resultType); - break; - case ERROR_FIELD: - error = p.readValueAs(JsonRpcError.class); - break; - case ID_FIELD: - id = p.readValueAs(UUID.class); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, JsonRpcResponse.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of(VERSION_FIELD, ID_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, JsonRpcResponse.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new JsonRpcResponse<>(version, result, error, id); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/plain/SparseMerkleTreePathJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/plain/SparseMerkleTreePathJson.java deleted file mode 100644 index 669e6ea..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/plain/SparseMerkleTreePathJson.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.mtree.plain; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.hash.DataHash; - -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class SparseMerkleTreePathJson { - - private static final String ROOT_HASH_FIELD = "root"; - private static final String STEPS_FIELD = "steps"; - - private SparseMerkleTreePathJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(SparseMerkleTreePath value, JsonGenerator gen, - SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(ROOT_HASH_FIELD, value.getRootHash()); - gen.writeObjectField(STEPS_FIELD, value.getSteps()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public SparseMerkleTreePath deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - DataHash rootHash = null; - List steps = new ArrayList<>(); - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, SparseMerkleTreePath.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, SparseMerkleTreePath.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case ROOT_HASH_FIELD: - rootHash = p.readValueAs(DataHash.class); - break; - case STEPS_FIELD: - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, SparseMerkleTreePath.class, - "Expected array value"); - } - - while (p.nextToken() != JsonToken.END_ARRAY) { - steps.add(p.readValueAs(SparseMerkleTreePathStep.class)); - } - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, SparseMerkleTreePath.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of(ROOT_HASH_FIELD, STEPS_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, SparseMerkleTreePath.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new SparseMerkleTreePath(rootHash, steps); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/plain/SparseMerkleTreePathStepBranchJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/plain/SparseMerkleTreePathStepBranchJson.java deleted file mode 100644 index 57fbd66..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/plain/SparseMerkleTreePathStepBranchJson.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.mtree.plain; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep.Branch; -import java.io.IOException; - -public class SparseMerkleTreePathStepBranchJson { - - private SparseMerkleTreePathStepBranchJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(Branch value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(); - gen.writeObject(value.getValue()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public Branch deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, SparseMerkleTreePathStep.Branch.class, - "Expected array"); - } - if (p.nextToken() == JsonToken.END_ARRAY) { - return new Branch(null); - } - - Branch branch = new Branch(p.readValueAs(byte[].class)); - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, SparseMerkleTreePathStep.Branch.class, - "Expected end of array"); - } - - return branch; - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/plain/SparseMerkleTreePathStepJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/plain/SparseMerkleTreePathStepJson.java deleted file mode 100644 index 55f37a8..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/plain/SparseMerkleTreePathStepJson.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.mtree.plain; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep; -import java.io.IOException; -import java.math.BigInteger; -import java.util.HashSet; -import java.util.Set; - -public class SparseMerkleTreePathStepJson { - - private static final String PATH_FIELD = "path"; - private static final String SIBLING_FIELD = "sibling"; - private static final String BRANCH_FIELD = "branch"; - - private SparseMerkleTreePathStepJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(SparseMerkleTreePathStep value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeStringField(PATH_FIELD, value.getPath().toString()); - gen.writeObjectField(SIBLING_FIELD, value.getSibling()); - gen.writeObjectField(BRANCH_FIELD, value.getBranch()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public SparseMerkleTreePathStep deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - BigInteger path = null; - SparseMerkleTreePathStep.Branch sibling = null; - SparseMerkleTreePathStep.Branch branch = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, SparseMerkleTreePathStep.class, - "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, SparseMerkleTreePathStep.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case PATH_FIELD: - if (p.currentToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, SparseMerkleTreePathStep.class, - "Expected string value"); - } - path = new BigInteger(p.readValueAs(String.class)); - break; - case SIBLING_FIELD: - sibling = p.readValueAs(SparseMerkleTreePathStep.Branch.class); - break; - case BRANCH_FIELD: - branch = p.readValueAs(SparseMerkleTreePathStep.Branch.class); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, SparseMerkleTreePathStep.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of(PATH_FIELD, SIBLING_FIELD, BRANCH_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, SparseMerkleTreePathStep.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new SparseMerkleTreePathStep(path, sibling, branch); - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/sum/SparseMerkleSumTreePathJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/sum/SparseMerkleSumTreePathJson.java deleted file mode 100644 index a9b8c90..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/sum/SparseMerkleSumTreePathJson.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.mtree.sum; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePath; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePathStep; -import java.io.IOException; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class SparseMerkleSumTreePathJson { - - private static final String ROOT_HASH_FIELD = "root"; - private static final String ROOT_COUNTER_FIELD = "sum"; - private static final String STEPS_FIELD = "steps"; - - private SparseMerkleSumTreePathJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(SparseMerkleSumTreePath value, JsonGenerator gen, - SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(ROOT_HASH_FIELD, value.getRoot().getHash()); - gen.writeObjectField(ROOT_COUNTER_FIELD, value.getRoot().getCounter()); - gen.writeObjectField(STEPS_FIELD, value.getSteps()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public SparseMerkleSumTreePath deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - DataHash hash = null; - BigInteger counter = null; - List steps = new ArrayList<>(); - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, SparseMerkleSumTreePath.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, SparseMerkleSumTreePath.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case ROOT_HASH_FIELD: - hash = p.readValueAs(DataHash.class); - break; - case ROOT_COUNTER_FIELD: - counter = new BigInteger(p.readValueAs(String.class)); - break; - case STEPS_FIELD: - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, SparseMerkleSumTreePath.class, - "Expected array value"); - } - - while (p.nextToken() != JsonToken.END_ARRAY) { - steps.add(p.readValueAs(SparseMerkleSumTreePathStep.class)); - } - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, SparseMerkleSumTreePath.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of(ROOT_HASH_FIELD, STEPS_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, SparseMerkleSumTreePath.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new SparseMerkleSumTreePath(new SparseMerkleSumTreePath.Root(hash, counter), steps); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/sum/SparseMerkleSumTreePathStepBranchJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/sum/SparseMerkleSumTreePathStepBranchJson.java deleted file mode 100644 index 70503fb..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/sum/SparseMerkleSumTreePathStepBranchJson.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.mtree.sum; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePathStep.Branch; -import java.io.IOException; -import java.math.BigInteger; - -public class SparseMerkleSumTreePathStepBranchJson { - - private SparseMerkleSumTreePathStepBranchJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(Branch value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(); - gen.writeObject(value.getValue()); - gen.writeObject(value.getCounter().toString()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public Branch deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, Branch.class, - "Expected array"); - } - p.nextToken(); - - Branch branch = new Branch( - p.readValueAs(byte[].class), - new BigInteger(p.readValueAs(String.class)) - ); - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, Branch.class, - "Expected end of array"); - } - - return branch; - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/sum/SparseMerkleSumTreePathStepJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/sum/SparseMerkleSumTreePathStepJson.java deleted file mode 100644 index 663280f..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/mtree/sum/SparseMerkleSumTreePathStepJson.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.mtree.sum; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePathStep; -import java.io.IOException; -import java.math.BigInteger; -import java.util.HashSet; -import java.util.Set; - -public class SparseMerkleSumTreePathStepJson { - - private static final String PATH_FIELD = "path"; - private static final String SIBLING_FIELD = "sibling"; - private static final String BRANCH_FIELD = "branch"; - - private SparseMerkleSumTreePathStepJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(SparseMerkleSumTreePathStep value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeStringField(PATH_FIELD, value.getPath().toString()); - gen.writeObjectField(SIBLING_FIELD, value.getSibling()); - gen.writeObjectField(BRANCH_FIELD, value.getBranch()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public SparseMerkleSumTreePathStep deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - BigInteger path = null; - SparseMerkleSumTreePathStep.Branch sibling = null; - SparseMerkleSumTreePathStep.Branch branch = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, SparseMerkleSumTreePathStep.class, - "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, SparseMerkleSumTreePathStep.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case PATH_FIELD: - if (p.currentToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, SparseMerkleSumTreePathStep.class, - "Expected string value"); - } - path = new BigInteger(p.readValueAs(String.class)); - break; - case SIBLING_FIELD: - sibling = p.readValueAs(SparseMerkleSumTreePathStep.Branch.class); - break; - case BRANCH_FIELD: - branch = p.readValueAs(SparseMerkleSumTreePathStep.Branch.class); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, SparseMerkleSumTreePathStep.class, - fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of(PATH_FIELD, SIBLING_FIELD, BRANCH_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, SparseMerkleSumTreePathStep.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new SparseMerkleSumTreePathStep(path, sibling, branch); - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/predicate/SerializablePredicateJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/predicate/SerializablePredicateJson.java deleted file mode 100644 index 58a36c3..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/predicate/SerializablePredicateJson.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.predicate; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import java.io.IOException; -import org.unicitylabs.sdk.predicate.EncodedPredicate; -import org.unicitylabs.sdk.predicate.Predicate; -import org.unicitylabs.sdk.predicate.PredicateEngineType; -import org.unicitylabs.sdk.predicate.SerializablePredicate; - -public class SerializablePredicateJson { - private SerializablePredicateJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(SerializablePredicate value, JsonGenerator gen, - SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartArray(value, 3); - gen.writeObject(value.getEngine().ordinal()); - gen.writeObject(value.encode()); - gen.writeObject(value.encodeParameters()); - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public SerializablePredicate deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, Predicate.class, "Expected array value"); - } - - p.nextToken(); - PredicateEngineType engine = p.readValueAs(PredicateEngineType.class); - byte[] code = p.readValueAs(byte[].class); - byte[] parameters = p.readValueAs(byte[].class); - - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, Predicate.class, "Expected end of array"); - } - - return new EncodedPredicate(engine, code, parameters); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/token/TokenIdJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/token/TokenIdJson.java deleted file mode 100644 index 629f9ef..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/token/TokenIdJson.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.token; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.token.TokenId; -import java.io.IOException; - -public class TokenIdJson { - - private TokenIdJson() { - } - - - public static class Serializer extends JsonSerializer { - - public Serializer() { - } - - @Override - public void serialize(TokenId value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeObject(value.getBytes()); - } - } - - public static class Deserializer extends JsonDeserializer { - - public Deserializer() { - } - - @Override - public TokenId deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - try { - return new TokenId(p.readValueAs(byte[].class)); - } catch (Exception e) { - throw MismatchedInputException.from(p, TokenId.class, "Expected bytes"); - } - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/token/TokenJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/token/TokenJson.java deleted file mode 100644 index 3dbd44b..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/token/TokenJson.java +++ /dev/null @@ -1,136 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.token; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.token.TokenState; -import org.unicitylabs.sdk.transaction.MintTransactionData; -import org.unicitylabs.sdk.transaction.Transaction; -import org.unicitylabs.sdk.transaction.TransferTransactionData; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class TokenJson { - - private static final String VERSION_FIELD = "version"; - private static final String STATE_FIELD = "state"; - private static final String GENESIS_FIELD = "genesis"; - private static final String TRANSACTIONS_FIELD = "transactions"; - private static final String NAMETAG_FIELD = "nametags"; - - private TokenJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(Token value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(VERSION_FIELD, value.getVersion()); - gen.writeObjectField(STATE_FIELD, value.getState()); - gen.writeObjectField(GENESIS_FIELD, value.getGenesis()); - gen.writeObjectField(TRANSACTIONS_FIELD, value.getTransactions()); - gen.writeObjectField(NAMETAG_FIELD, value.getNametags()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends - JsonDeserializer>> { - - @Override - public Token> deserialize(JsonParser p, - DeserializationContext ctx) - throws IOException { - String version = null; - TokenState state = null; - Transaction> genesis = null; - List> transactions = new ArrayList<>(); - List> nametags = new ArrayList<>(); - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, Token.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, Token.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case VERSION_FIELD: - version = p.readValueAs(String.class); - if (!Token.TOKEN_VERSION.equals(version)) { - throw MismatchedInputException.from(p, Token.class, - String.format("Unsupported token version: %s", version)); - } - break; - case STATE_FIELD: - state = p.readValueAs(TokenState.class); - break; - case GENESIS_FIELD: - genesis = ctx.readValue(p, ctx.getTypeFactory() - .constructParametricType(Transaction.class, MintTransactionData.class)); - break; - case TRANSACTIONS_FIELD: - if (p.currentToken() != JsonToken.START_ARRAY) { - throw MismatchedInputException.from(p, Token.class, "Expected array value"); - } - - while (p.nextToken() != JsonToken.END_ARRAY) { - transactions.add( - ctx.readValue(p, ctx.getTypeFactory() - .constructParametricType(Transaction.class, TransferTransactionData.class)) - ); - } - break; - case NAMETAG_FIELD: - if (p.currentToken() != JsonToken.START_ARRAY) { - throw MismatchedInputException.from(p, Token.class, "Expected array value"); - } - - while (p.nextToken() != JsonToken.END_ARRAY) { - nametags.add(p.readValueAs(Token.class)); - } - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, Token.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of( - VERSION_FIELD, STATE_FIELD, GENESIS_FIELD, TRANSACTIONS_FIELD, NAMETAG_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, Token.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new Token<>(state, genesis, transactions, nametags); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/token/TokenStateJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/token/TokenStateJson.java deleted file mode 100644 index 6edad82..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/token/TokenStateJson.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.token; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; -import org.unicitylabs.sdk.predicate.SerializablePredicate; -import org.unicitylabs.sdk.token.TokenState; - -public class TokenStateJson { - - private static final String UNLOCK_PREDICATE_FIELD = "unlockPredicate"; - private static final String DATA_FIELD = "data"; - - private TokenStateJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(TokenState value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(UNLOCK_PREDICATE_FIELD, value.getPredicate()); - gen.writeObjectField(DATA_FIELD, value.getData()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends - JsonDeserializer { - - @Override - public TokenState deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - SerializablePredicate predicate = null; - byte[] data = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, TokenState.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, TokenState.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case UNLOCK_PREDICATE_FIELD: - predicate = p.readValueAs(SerializablePredicate.class); - break; - case DATA_FIELD: - data = - p.getCurrentToken() != JsonToken.VALUE_NULL ? p.readValueAs(byte[].class) : null; - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, TokenState.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of(UNLOCK_PREDICATE_FIELD, DATA_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, TokenState.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new TokenState(predicate, data); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/token/TokenTypeJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/token/TokenTypeJson.java deleted file mode 100644 index 49ca513..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/token/TokenTypeJson.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.token; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.token.TokenType; -import java.io.IOException; - -public class TokenTypeJson { - - private TokenTypeJson() { - } - - - public static class Serializer extends JsonSerializer { - - public Serializer() { - } - - @Override - public void serialize(TokenType value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeObject(value.getBytes()); - } - } - - public static class Deserializer extends JsonDeserializer { - - public Deserializer() { - } - - @Override - public TokenType deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - try { - return new TokenType(p.readValueAs(byte[].class)); - } catch (Exception e) { - throw MismatchedInputException.from(p, TokenType.class, "Expected bytes"); - } - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/token/fungible/TokenCoinDataJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/token/fungible/TokenCoinDataJson.java deleted file mode 100644 index ead143a..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/token/fungible/TokenCoinDataJson.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.token.fungible; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.token.fungible.CoinId; -import org.unicitylabs.sdk.token.fungible.TokenCoinData; -import java.io.IOException; -import java.math.BigInteger; -import java.util.LinkedHashMap; -import java.util.Map; - -public class TokenCoinDataJson { - - private TokenCoinDataJson() { - } - - - public static class Serializer extends JsonSerializer { - - public Serializer() { - } - - @Override - public void serialize(TokenCoinData value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - Map coins = value.getCoins(); - gen.writeStartArray(value, coins.size()); - for (Map.Entry entry : coins.entrySet()) { - gen.writeStartArray(entry, 2); - gen.writeObject(entry.getKey().getBytes()); - gen.writeString(entry.getValue().toString()); - gen.writeEndArray(); - } - gen.writeEndArray(); - } - } - - public static class Deserializer extends JsonDeserializer { - - public Deserializer() { - } - - @Override - public TokenCoinData deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, TokenCoinData.class, "Expected array value"); - } - - Map result = new LinkedHashMap<>(); - while (p.nextToken() != JsonToken.END_ARRAY) { - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, TokenCoinData.class, - "Expected array of coin data"); - } - - if (p.nextToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, TokenCoinData.class, "Expected bytes of coin id"); - } - CoinId id = new CoinId(p.readValueAs(byte[].class)); - if (p.nextToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, TokenCoinData.class, "Expected value as string"); - } - BigInteger value = new BigInteger(p.readValueAs(String.class)); - if (p.nextToken() != JsonToken.END_ARRAY) { - throw MismatchedInputException.from(p, TokenCoinData.class, - "Expected end of coin data array"); - } - if (result.containsKey(id)) { - throw MismatchedInputException.from(p, TokenCoinData.class, - "Duplicate coin id: " + id); - } - - result.put(id, value); - } - - return new TokenCoinData(result); - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/CommitmentJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/CommitmentJson.java deleted file mode 100644 index 425058d..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/CommitmentJson.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.transaction; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.api.Authenticator; -import org.unicitylabs.sdk.api.RequestId; -import org.unicitylabs.sdk.transaction.Commitment; -import org.unicitylabs.sdk.transaction.TransactionData; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class CommitmentJson { - - private static final String REQUEST_ID_FIELD = "requestId"; - private static final String TRANSACTION_DATA_FIELD = "transactionData"; - private static final String AUTHENTICATOR_FIELD = "authenticator"; - - private CommitmentJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(Commitment value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(REQUEST_ID_FIELD, value.getRequestId()); - gen.writeObjectField(TRANSACTION_DATA_FIELD, value.getTransactionData()); - gen.writeObjectField(AUTHENTICATOR_FIELD, value.getAuthenticator()); - gen.writeEndObject(); - } - } - - public static abstract class Deserializer, U extends Commitment> extends JsonDeserializer { - - protected abstract T createTransactionData(JsonParser p, DeserializationContext ctx) throws IOException; - - protected abstract U createCommitment( - RequestId requestId, T transactionData, Authenticator authenticator); - - @Override - public U deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - RequestId requestId = null; - T transactionData = null; - Authenticator authenticator = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, Commitment.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, Commitment.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case REQUEST_ID_FIELD: - requestId = p.readValueAs(RequestId.class); - break; - case TRANSACTION_DATA_FIELD: - transactionData = this.createTransactionData(p, ctx); - break; - case AUTHENTICATOR_FIELD: - authenticator = p.readValueAs(Authenticator.class); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, Commitment.class, fieldName); - } - } - - Set missingFields = new HashSet<>( - Set.of(REQUEST_ID_FIELD, TRANSACTION_DATA_FIELD, AUTHENTICATOR_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, Commitment.class, - String.format("Missing required fields: %s", missingFields)); - } - - return this.createCommitment(requestId, transactionData, authenticator); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/InclusionProofJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/InclusionProofJson.java deleted file mode 100644 index b89b0bf..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/InclusionProofJson.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.transaction; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.api.Authenticator; -import org.unicitylabs.sdk.bft.UnicityCertificate; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.transaction.InclusionProof; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class InclusionProofJson { - - private static final String MERKLE_TREE_PATH_FIELD = "merkleTreePath"; - private static final String AUTHENTICATOR_FIELD = "authenticator"; - private static final String TRANSACTION_HASH_FIELD = "transactionHash"; - private static final String UNICITY_CERTIFICATE_FIELD = "unicityCertificate"; - - private InclusionProofJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(InclusionProof value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(MERKLE_TREE_PATH_FIELD, value.getMerkleTreePath()); - gen.writeObjectField(AUTHENTICATOR_FIELD, value.getAuthenticator()); - gen.writeObjectField(TRANSACTION_HASH_FIELD, value.getTransactionHash()); - gen.writeObjectField( - UNICITY_CERTIFICATE_FIELD, - UnicityObjectMapper.CBOR.writeValueAsBytes(value.getUnicityCertificate()) - ); - gen.writeEndObject(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public InclusionProof deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - SparseMerkleTreePath merkleTreePath = null; - Authenticator authenticator = null; - DataHash transactionHash = null; - UnicityCertificate unicityCertificate = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, InclusionProof.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, InclusionProof.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case MERKLE_TREE_PATH_FIELD: - merkleTreePath = p.readValueAs(SparseMerkleTreePath.class); - break; - case AUTHENTICATOR_FIELD: - authenticator = - p.currentToken() != JsonToken.VALUE_NULL ? p.readValueAs(Authenticator.class) - : null; - break; - case TRANSACTION_HASH_FIELD: - transactionHash = - p.currentToken() != JsonToken.VALUE_NULL ? p.readValueAs(DataHash.class) : null; - break; - case UNICITY_CERTIFICATE_FIELD: - byte[] bytes = p.readValueAs(byte[].class); - unicityCertificate = UnicityObjectMapper.CBOR.readValue( - bytes, - UnicityCertificate.class - ); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, InclusionProof.class, fieldName); - } - } - - Set missingFields = new HashSet<>( - Set.of(MERKLE_TREE_PATH_FIELD, UNICITY_CERTIFICATE_FIELD) - ); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, Token.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new InclusionProof(merkleTreePath, authenticator, transactionHash, unicityCertificate); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/MintCommitmentJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/MintCommitmentJson.java deleted file mode 100644 index 9c96d6e..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/MintCommitmentJson.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.transaction; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import org.unicitylabs.sdk.api.Authenticator; -import org.unicitylabs.sdk.api.RequestId; -import org.unicitylabs.sdk.transaction.MintCommitment; -import org.unicitylabs.sdk.transaction.MintTransactionData; -import java.io.IOException; - -public class MintCommitmentJson { - - private MintCommitmentJson() {} - - public static class Deserializer extends CommitmentJson.Deserializer, MintCommitment>> { - @Override - protected MintTransactionData createTransactionData(JsonParser p, DeserializationContext ctx) throws IOException { - return p.readValueAs(MintTransactionData.class); - } - - @Override - protected MintCommitment> createCommitment( - RequestId requestId, MintTransactionData transactionData, Authenticator authenticator) { - return new MintCommitment<>(requestId, transactionData, authenticator); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/MintTransactionDataJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/MintTransactionDataJson.java deleted file mode 100644 index 94a7e14..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/MintTransactionDataJson.java +++ /dev/null @@ -1,136 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.transaction; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.address.Address; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.token.TokenId; -import org.unicitylabs.sdk.token.TokenType; -import org.unicitylabs.sdk.token.fungible.TokenCoinData; -import org.unicitylabs.sdk.transaction.MintTransactionData; -import org.unicitylabs.sdk.transaction.MintTransactionReason; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class MintTransactionDataJson { - - private static final String TOKEN_ID_FIELD = "tokenId"; - private static final String TOKEN_TYPE_FIELD = "tokenType"; - private static final String TOKEN_DATA_FIELD = "tokenData"; - private static final String COIN_DATA_FIELD = "coins"; - private static final String RECIPIENT_FIELD = "recipient"; - private static final String SALT_FIELD = "salt"; - private static final String DATA_HASH_FIELD = "dataHash"; - private static final String REASON_FIELD = "reason"; - - private MintTransactionDataJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(MintTransactionData value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(TOKEN_ID_FIELD, value.getTokenId()); - gen.writeObjectField(TOKEN_TYPE_FIELD, value.getTokenType()); - gen.writeObjectField(TOKEN_DATA_FIELD, value.getTokenData()); - gen.writeObjectField(COIN_DATA_FIELD, value.getCoinData()); - gen.writeObjectField(RECIPIENT_FIELD, value.getRecipient()); - gen.writeObjectField(SALT_FIELD, value.getSalt()); - gen.writeObjectField(DATA_HASH_FIELD, value.getDataHash()); - gen.writeObjectField(REASON_FIELD, value.getReason()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends - JsonDeserializer> { - - @Override - public MintTransactionData deserialize(JsonParser p, - DeserializationContext ctx) - throws IOException { - TokenId tokenId = null; - TokenType tokenType = null; - byte[] tokenData = null; - TokenCoinData coinData = null; - Address recipient = null; - byte[] salt = null; - DataHash dataHash = null; - MintTransactionReason reason = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, MintTransactionData.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, MintTransactionData.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case TOKEN_ID_FIELD: - tokenId = p.readValueAs(TokenId.class); - break; - case TOKEN_TYPE_FIELD: - tokenType = p.readValueAs(TokenType.class); - break; - case TOKEN_DATA_FIELD: - tokenData = p.readValueAs(byte[].class); - break; - case COIN_DATA_FIELD: - coinData = p.readValueAs(TokenCoinData.class); - break; - case RECIPIENT_FIELD: - recipient = p.readValueAs(Address.class); - break; - case SALT_FIELD: - salt = p.readValueAs(byte[].class); - break; - case DATA_HASH_FIELD: - dataHash = p.readValueAs(DataHash.class); - break; - case REASON_FIELD: - reason = p.readValueAs(MintTransactionReason.class); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, MintTransactionData.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of( - TOKEN_ID_FIELD, TOKEN_TYPE_FIELD, TOKEN_DATA_FIELD, RECIPIENT_FIELD, SALT_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, MintTransactionData.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new MintTransactionData<>(tokenId, tokenType, tokenData, coinData, recipient, salt, - dataHash, reason); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/MintTransactionReasonJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/MintTransactionReasonJson.java deleted file mode 100644 index 68163fb..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/MintTransactionReasonJson.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.transaction; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import org.unicitylabs.sdk.transaction.split.SplitMintReason; -import org.unicitylabs.sdk.transaction.MintTransactionReason; -import java.io.IOException; - -public class MintTransactionReasonJson { - - private MintTransactionReasonJson() { - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public MintTransactionReason deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - return p.readValueAs(SplitMintReason.class); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/TransactionJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/TransactionJson.java deleted file mode 100644 index c9ef2e5..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/TransactionJson.java +++ /dev/null @@ -1,114 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.transaction; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.BeanProperty; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.transaction.InclusionProof; -import org.unicitylabs.sdk.transaction.Transaction; -import org.unicitylabs.sdk.transaction.TransactionData; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class TransactionJson { - - private static final String DATA_FIELD = "data"; - private static final String INCLUSION_PROOF_FIELD = "inclusionProof"; - - private TransactionJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(Transaction value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(DATA_FIELD, value.getData()); - gen.writeObjectField(INCLUSION_PROOF_FIELD, value.getInclusionProof()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends JsonDeserializer> implements - ContextualDeserializer { - - private final JavaType transactionType; - - public Deserializer() { - this.transactionType = null; - } - - private Deserializer(JavaType valueType) { - this.transactionType = valueType; - } - - @Override - public JsonDeserializer createContextual(DeserializationContext ctx, - BeanProperty property) { - JavaType wrapperType = ctx.getContextualType(); - JavaType valueType = wrapperType != null ? wrapperType.containedType(0) : null; - return new TransactionJson.Deserializer(valueType); - } - - @Override - public Transaction deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - InclusionProof inclusionProof = null; - TransactionData data = null; - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, Transaction.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, Transaction.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case INCLUSION_PROOF_FIELD: - inclusionProof = p.readValueAs(InclusionProof.class); - break; - case DATA_FIELD: - data = p.getCodec().readValue(p, this.transactionType); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, Transaction.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of(INCLUSION_PROOF_FIELD, DATA_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, Transaction.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new Transaction<>(data, inclusionProof); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/TransferCommitmentJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/TransferCommitmentJson.java deleted file mode 100644 index 2dcee61..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/TransferCommitmentJson.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.transaction; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import org.unicitylabs.sdk.api.Authenticator; -import org.unicitylabs.sdk.api.RequestId; -import org.unicitylabs.sdk.transaction.TransferCommitment; -import org.unicitylabs.sdk.transaction.TransferTransactionData; -import java.io.IOException; - -public class TransferCommitmentJson { - - private TransferCommitmentJson() {} - - public static class Deserializer extends CommitmentJson.Deserializer { - @Override - protected TransferTransactionData createTransactionData(JsonParser p, DeserializationContext ctx) throws IOException { - return p.readValueAs(TransferTransactionData.class); - } - - @Override - protected TransferCommitment createCommitment( - RequestId requestId, TransferTransactionData transactionData, Authenticator authenticator) { - return new TransferCommitment(requestId, transactionData, authenticator); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/TransferTransactionDataJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/TransferTransactionDataJson.java deleted file mode 100644 index ed5bc0b..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/TransferTransactionDataJson.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.transaction; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.address.Address; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.token.TokenState; -import org.unicitylabs.sdk.transaction.TransferTransactionData; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -public class TransferTransactionDataJson { - - // TODO: Include token id and type within each transfer transaction data so it would be easier to handle hashing and parsing - private static final String SOURCE_STATE_FIELD = "state"; - private static final String RECIPIENT_FIELD = "recipient"; - private static final String SALT_FIELD = "salt"; - private static final String DATA_HASH_FIELD = "dataHash"; - private static final String MESSAGE_FIELD = "message"; - private static final String NAMETAG_FIELD = "nametags"; - - private TransferTransactionDataJson() { - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(TransferTransactionData value, JsonGenerator gen, - SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(DATA_HASH_FIELD, value.getDataHash()); - gen.writeObjectField(MESSAGE_FIELD, value.getMessage()); - gen.writeObjectField(RECIPIENT_FIELD, value.getRecipient()); - gen.writeObjectField(SALT_FIELD, value.getSalt()); - gen.writeObjectField(SOURCE_STATE_FIELD, value.getSourceState()); - gen.writeObjectField(NAMETAG_FIELD, value.getNametags()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends JsonDeserializer { - - @Override - public TransferTransactionData deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, TransferTransactionData.class, - "Expected object value"); - } - - TokenState tokenState = null; - Address recipient = null; - byte[] salt = null; - DataHash dataHash = null; - byte[] message = null; - List> nametags = new ArrayList<>(); - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - p.nextToken(); // Move to the value - - switch (fieldName) { - case SOURCE_STATE_FIELD: - tokenState = p.readValueAs(TokenState.class); - break; - case RECIPIENT_FIELD: - recipient = p.readValueAs(Address.class); - break; - case SALT_FIELD: - salt = p.readValueAs(byte[].class); - break; - case DATA_HASH_FIELD: - dataHash = p.readValueAs(DataHash.class); - break; - case MESSAGE_FIELD: - message = p.readValueAs(byte[].class); - break; - case NAMETAG_FIELD: - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, Token.class, "Expected array value"); - } - - while (p.nextToken() != JsonToken.END_ARRAY) { - nametags.add(p.readValueAs(Token.class)); - } - break; - default: - p.skipChildren(); // Skip unknown fields - } - } - - return new TransferTransactionData(tokenState, recipient, salt, dataHash, message, nametags); - } - } -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/split/SplitMintReasonJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/split/SplitMintReasonJson.java deleted file mode 100644 index d49e86a..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/split/SplitMintReasonJson.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.transaction.split; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.transaction.MintReasonType; -import org.unicitylabs.sdk.transaction.split.SplitMintReason; -import org.unicitylabs.sdk.transaction.split.SplitMintReasonProof; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class SplitMintReasonJson { - private static final String TYPE_FIELD = "type"; - private static final String TOKEN_FIELD = "token"; - private static final String PROOFS_FIELD = "proofs"; - - private SplitMintReasonJson() { - } - - - public static class Serializer extends JsonSerializer { - public Serializer() { - } - - @Override - public void serialize(SplitMintReason value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(TYPE_FIELD, value.getType()); - gen.writeObjectField(TOKEN_FIELD, value.getToken()); - gen.writeObjectField(PROOFS_FIELD, value.getProofs()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends JsonDeserializer { - - public Deserializer() { - } - - @Override - public SplitMintReason deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - String type = null; - Token token = null; - List proofs = new ArrayList<>(); - - Set fields = new HashSet<>(); - - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, SplitMintReason.class, "Expected object value"); - } - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, SplitMintReason.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case TYPE_FIELD: - type = p.getValueAsString(); - if (!MintReasonType.TOKEN_SPLIT.name().equals(type)) { - throw MismatchedInputException.from(p, SplitMintReason.class, - String.format("Invalid type: %s", type)); - } - break; - case TOKEN_FIELD: - token = p.readValueAs(Token.class); - break; - case PROOFS_FIELD: - if (!p.isExpectedStartArrayToken()) { - throw MismatchedInputException.from(p, SplitMintReason.class, "Expected array value"); - } - - while (p.nextToken() != JsonToken.END_ARRAY) { - proofs.add(p.readValueAs(SplitMintReasonProof.class)); - } - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, SplitMintReason.class, fieldName); - } - } - - Set missingFields = new HashSet<>(Set.of(TYPE_FIELD, TOKEN_FIELD, PROOFS_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, SplitMintReason.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new SplitMintReason(token, proofs); - } - } - -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/split/SplitMintReasonProofJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/split/SplitMintReasonProofJson.java deleted file mode 100644 index a3162ac..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/transaction/split/SplitMintReasonProofJson.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.transaction.split; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePath; -import org.unicitylabs.sdk.token.fungible.CoinId; -import org.unicitylabs.sdk.transaction.split.SplitMintReasonProof; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class SplitMintReasonProofJson { - - private static final String COIN_ID_FIELD = "coinId"; - private static final String AGGREGATION_PATH_FIELD = "aggregationPath"; - private static final String COIN_TREE_PATH_FIELD = "coinTreePath"; - - private SplitMintReasonProofJson() { - } - - - public static class Serializer extends JsonSerializer { - - public Serializer() { - } - - @Override - public void serialize(SplitMintReasonProof value, JsonGenerator gen, - SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeStartObject(); - gen.writeObjectField(COIN_ID_FIELD, value.getCoinId().getBytes()); - gen.writeObjectField(AGGREGATION_PATH_FIELD, value.getAggregationPath()); - gen.writeObjectField(COIN_TREE_PATH_FIELD, value.getCoinTreePath()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends JsonDeserializer { - - public Deserializer() { - } - - @Override - public SplitMintReasonProof deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (!p.isExpectedStartObjectToken()) { - throw MismatchedInputException.from(p, SplitMintReasonProof.class, "Expected object value"); - } - - CoinId coinId = null; - SparseMerkleTreePath aggregationPath = null; - SparseMerkleSumTreePath coinTreePath = null; - - Set fields = new HashSet<>(); - - while (p.nextToken() != JsonToken.END_OBJECT) { - String fieldName = p.currentName(); - - if (!fields.add(fieldName)) { - throw MismatchedInputException.from(p, SplitMintReasonProof.class, - String.format("Duplicate field: %s", fieldName)); - } - - p.nextToken(); - try { - switch (fieldName) { - case COIN_ID_FIELD: - coinId = new CoinId(p.readValueAs(byte[].class)); - break; - case AGGREGATION_PATH_FIELD: - aggregationPath = p.readValueAs(SparseMerkleTreePath.class); - break; - case COIN_TREE_PATH_FIELD: - coinTreePath = p.readValueAs(SparseMerkleSumTreePath.class); - break; - default: - p.skipChildren(); - } - } catch (Exception e) { - throw MismatchedInputException.wrapWithPath(e, SplitMintReasonProof.class, fieldName); - } - } - - Set missingFields = new HashSet<>( - Set.of(COIN_ID_FIELD, AGGREGATION_PATH_FIELD, COIN_TREE_PATH_FIELD)); - missingFields.removeAll(fields); - if (!missingFields.isEmpty()) { - throw MismatchedInputException.from(p, SplitMintReasonProof.class, - String.format("Missing required fields: %s", missingFields)); - } - - return new SplitMintReasonProof(coinId, aggregationPath, coinTreePath); - } - } - -} diff --git a/src/main/java/org/unicitylabs/sdk/serializer/json/util/ByteArrayHexJson.java b/src/main/java/org/unicitylabs/sdk/serializer/json/util/ByteArrayHexJson.java deleted file mode 100644 index e8b13ac..0000000 --- a/src/main/java/org/unicitylabs/sdk/serializer/json/util/ByteArrayHexJson.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.unicitylabs.sdk.serializer.json.util; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import org.unicitylabs.sdk.util.HexConverter; -import java.io.IOException; - -public class ByteArrayHexJson { - - private ByteArrayHexJson() { - } - - public static class Serializer extends JsonSerializer { - - public Serializer() { - } - - @Override - public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - gen.writeString(HexConverter.encode(value)); - } - } - - public static class Deserializer extends JsonDeserializer { - - public Deserializer() { - } - - @Override - public byte[] deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - if (p.getCurrentToken() != JsonToken.VALUE_STRING) { - throw MismatchedInputException.from(p, byte[].class, - "Expected hex string value"); - } - - try { - return HexConverter.decode(p.readValueAs(String.class)); - } catch (Exception e) { - throw MismatchedInputException.from(p, byte[].class, "Expected hex string value"); - } - } - } -} - diff --git a/src/main/java/org/unicitylabs/sdk/signing/Signature.java b/src/main/java/org/unicitylabs/sdk/signing/Signature.java index 9855828..677d767 100644 --- a/src/main/java/org/unicitylabs/sdk/signing/Signature.java +++ b/src/main/java/org/unicitylabs/sdk/signing/Signature.java @@ -1,24 +1,40 @@ - package org.unicitylabs.sdk.signing; -import org.unicitylabs.sdk.util.HexConverter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.util.Arrays; import java.util.Objects; +import org.unicitylabs.sdk.util.HexConverter; +/** + * Signature implementation for signing service, this contains public key recovery byte as well. + */ +@JsonSerialize(using = SignatureJson.Serializer.class) +@JsonDeserialize(using = SignatureJson.Deserializer.class) public class Signature { private final byte[] bytes; private final int recovery; - public Signature(byte[] bytes, int recovery) { + Signature(byte[] bytes, int recovery) { this.bytes = Arrays.copyOf(bytes, bytes.length); this.recovery = recovery; } + /** + * Get signature bytes. + * + * @return bytes + */ public byte[] getBytes() { return Arrays.copyOf(this.bytes, this.bytes.length); } + /** + * Get recovery byte for recovering public key. + * + * @return byte + */ public int getRecovery() { return this.recovery; } @@ -53,9 +69,9 @@ public static Signature decode(byte[] input) { @Override public boolean equals(Object o) { - if (!(o instanceof Signature)) { - return false; - } + if (!(o instanceof Signature)) { + return false; + } Signature signature = (Signature) o; return this.recovery == signature.recovery && Objects.deepEquals(this.bytes, signature.bytes); } diff --git a/src/main/java/org/unicitylabs/sdk/signing/SignatureJson.java b/src/main/java/org/unicitylabs/sdk/signing/SignatureJson.java new file mode 100644 index 0000000..bdcd698 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/signing/SignatureJson.java @@ -0,0 +1,75 @@ +package org.unicitylabs.sdk.signing; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; + +/** + * Signature serializer and deserializer implementation. + */ +public class SignatureJson { + + private SignatureJson() { + } + + /** + * Signature serializer. + */ + public static class Serializer extends StdSerializer { + + /** + * Create serializer. + */ + public Serializer() { + super(Signature.class); + } + + /** + * Serialize signature. + * + * @param value signature + * @param gen json generator + * @param serializers serializer provider + * @throws IOException on serialization failure + */ + @Override + public void serialize(Signature value, JsonGenerator gen, + SerializerProvider serializers) + throws IOException { + gen.writeObject(value.encode()); + } + } + + /** + * Signature deserializer. + */ + public static class Deserializer extends StdDeserializer { + + /** + * Create deserializer. + */ + public Deserializer() { + super(Signature.class); + } + + /** + * Deserialize signature. + * + * @param p Parser used for reading JSON content + * @param ctx Context that can be used to access information about this deserialization + * activity. + * @return signature + * @throws IOException on deserialization failure + */ + @Override + public Signature deserialize(JsonParser p, DeserializationContext ctx) + throws IOException { + return Signature.decode(p.readValueAs(byte[].class)); + } + } +} + diff --git a/src/main/java/org/unicitylabs/sdk/signing/SigningService.java b/src/main/java/org/unicitylabs/sdk/signing/SigningService.java index 3d7bd0b..9116c19 100644 --- a/src/main/java/org/unicitylabs/sdk/signing/SigningService.java +++ b/src/main/java/org/unicitylabs/sdk/signing/SigningService.java @@ -1,8 +1,8 @@ package org.unicitylabs.sdk.signing; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.hash.DataHasher; -import org.unicitylabs.sdk.hash.HashAlgorithm; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Arrays; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; @@ -13,10 +13,9 @@ import org.bouncycastle.jce.spec.ECParameterSpec; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; - -import java.math.BigInteger; -import java.security.SecureRandom; -import java.util.Arrays; +import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.hash.DataHasher; +import org.unicitylabs.sdk.hash.HashAlgorithm; /** * Default signing service. @@ -52,8 +51,8 @@ public SigningService(byte[] privateKey) { } // Calculate public key - ECPoint Q = EC_SPEC.getG().multiply(privateKeyAsBigInt); - this.publicKey = Q.getEncoded(true); // compressed format + ECPoint q = EC_SPEC.getG().multiply(privateKeyAsBigInt); + this.publicKey = q.getEncoded(true); // compressed format this.privateKey = new ECPrivateKeyParameters( privateKeyAsBigInt, EC_DOMAIN_PARAMETERS @@ -98,6 +97,12 @@ public static SigningService createFromMaskedSecret(byte[] secret, byte[] nonce) return new SigningService(hasher.digest().getData()); } + /** + * Sign data hash. + * + * @param hash data hash + * @return signature + */ public Signature sign(DataHash hash) { ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())); signer.init(true, this.privateKey); @@ -133,19 +138,36 @@ public Signature sign(DataHash hash) { return new Signature(signatureBytes, recoveryId); } + /** + * Verify signature and data hash. + * + * @param hash data hash + * @param signature signature + * @return true if successful + */ public boolean verify(DataHash hash, Signature signature) { return verifyWithPublicKey(hash, signature.getBytes(), this.publicKey); } /** - * Verify signature with public key and hash. + * Verify signature with public key. + * + * @param hash data hash + * @param signature signature bytes + * @param publicKey public key + * @return true if successful */ public static boolean verifyWithPublicKey(DataHash hash, byte[] signature, byte[] publicKey) { return SigningService.verifyWithPublicKey(hash.getData(), signature, publicKey); } /** - * Verify signature with public key and hash bytes. + * Verify signature with public key and data hash bytes. + * + * @param hash hash bytes + * @param signature signature bytes + * @param publicKey public key + * @return true if successful */ public static boolean verifyWithPublicKey(byte[] hash, byte[] signature, byte[] publicKey) { ECPoint pubPoint = EC_SPEC.getCurve().decodePoint(publicKey); @@ -182,10 +204,13 @@ private byte[] toFixedLength(BigInteger value, int length) { /** * Recover public key from signature for a specific recovery ID. */ - private static ECPoint recoverFromSignature(int recId, BigInteger r, BigInteger s, - byte[] message) { + private static ECPoint recoverFromSignature( + int recId, + BigInteger r, + BigInteger s, + byte[] message + ) { BigInteger n = EC_DOMAIN_PARAMETERS.getN(); - BigInteger e = new BigInteger(1, message); BigInteger x = r; if (recId >= 2) { @@ -195,36 +220,39 @@ private static ECPoint recoverFromSignature(int recId, BigInteger r, BigInteger ECCurve curve = EC_DOMAIN_PARAMETERS.getCurve(); // Calculate y from x - ECPoint R = SigningService.decompressKey(x, (recId & 1) == 1, curve); - if (R == null) { + ECPoint y = SigningService.decompressKey(x, (recId & 1) == 1, curve); + if (y == null) { return null; } // Verify R is on curve and has order n - if (!R.isValid() || !R.multiply(n).isInfinity()) { + if (!y.isValid() || !y.multiply(n).isInfinity()) { return null; } + BigInteger e = new BigInteger(1, message); + // Calculate public key: Q = r^-1 * (s*R - e*G) - BigInteger rInv = r.modInverse(n); - ECPoint point1 = R.multiply(s); + ECPoint point1 = y.multiply(s); ECPoint point2 = EC_DOMAIN_PARAMETERS.getG().multiply(e); - return point1.subtract(point2).multiply(rInv); + return point1 + .subtract(point2) + .multiply(r.modInverse(n)); } /** * Decompress a compressed public key point. */ - private static ECPoint decompressKey(BigInteger x, boolean yBit, ECCurve curve) { + private static ECPoint decompressKey(BigInteger x, boolean ybit, ECCurve curve) { try { byte[] compEnc = new byte[33]; - compEnc[0] = (byte) (yBit ? 0x03 : 0x02); - byte[] xBytes = x.toByteArray(); - if (xBytes.length > 32) { - System.arraycopy(xBytes, xBytes.length - 32, compEnc, 1, 32); + compEnc[0] = (byte) (ybit ? 0x03 : 0x02); + byte[] xbytes = x.toByteArray(); + if (xbytes.length > 32) { + System.arraycopy(xbytes, xbytes.length - 32, compEnc, 1, 32); } else { - System.arraycopy(xBytes, 0, compEnc, 33 - xBytes.length, xBytes.length); + System.arraycopy(xbytes, 0, compEnc, 33 - xbytes.length, xbytes.length); } return curve.decodePoint(compEnc); } catch (Exception e) { diff --git a/src/main/java/org/unicitylabs/sdk/token/Token.java b/src/main/java/org/unicitylabs/sdk/token/Token.java index e6204dc..d256b3b 100644 --- a/src/main/java/org/unicitylabs/sdk/token/Token.java +++ b/src/main/java/org/unicitylabs/sdk/token/Token.java @@ -1,44 +1,65 @@ package org.unicitylabs.sdk.token; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import org.unicitylabs.sdk.address.Address; import org.unicitylabs.sdk.address.ProxyAddress; import org.unicitylabs.sdk.api.RequestId; import org.unicitylabs.sdk.bft.RootTrustBase; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.hash.DataHasher; -import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.predicate.Predicate; import org.unicitylabs.sdk.predicate.PredicateEngineService; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.token.fungible.TokenCoinData; import org.unicitylabs.sdk.transaction.InclusionProofVerificationStatus; import org.unicitylabs.sdk.transaction.MintCommitment; -import org.unicitylabs.sdk.transaction.MintTransactionData; +import org.unicitylabs.sdk.transaction.MintTransaction; +import org.unicitylabs.sdk.transaction.MintTransactionReason; import org.unicitylabs.sdk.transaction.MintTransactionState; import org.unicitylabs.sdk.transaction.Transaction; -import org.unicitylabs.sdk.transaction.TransferTransactionData; +import org.unicitylabs.sdk.transaction.TransferTransaction; import org.unicitylabs.sdk.verification.VerificationException; import org.unicitylabs.sdk.verification.VerificationResult; -public class Token> { +/** + * Token representation. + * + * @param mint transaction reason for current token. + */ +public class Token { + /** + * Current token representation version. + */ public static final String TOKEN_VERSION = "2.0"; private final TokenState state; - private final Transaction genesis; - private final List> transactions; + private final MintTransaction genesis; + private final List transactions; private final List> nametags; - public Token( + @JsonCreator + Token( + @JsonProperty("state") TokenState state, - Transaction genesis, - List> transactions, + @JsonProperty("genesis") + MintTransaction genesis, + @JsonProperty("transactions") + List transactions, + @JsonProperty("nametags") List> nametags ) { Objects.requireNonNull(state, "State cannot be null"); @@ -52,54 +73,126 @@ public Token( this.nametags = List.copyOf(nametags); } + /** + * Get token id from genesis. + * + * @return token id + */ + @JsonIgnore public TokenId getId() { return this.genesis.getData().getTokenId(); } + /** + * Get token type from genesis. + * + * @return token type + */ + @JsonIgnore public TokenType getType() { return this.genesis.getData().getTokenType(); } + /** + * Get token immutable data from genesis. + * + * @return token immutable data + */ + @JsonIgnore public Optional getData() { return this.genesis.getData().getTokenData(); } + /** + * Get token coins data from genesis. + * + * @return token coins data + */ + @JsonIgnore public Optional getCoins() { return this.genesis.getData().getCoinData(); } + /** + * Get token version. + * + * @return token version + */ + @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getVersion() { return TOKEN_VERSION; } + /** + * Get token current state. + * + * @return token state + */ public TokenState getState() { return this.state; } - public Transaction getGenesis() { + /** + * Get token genesis. + * + * @return token genesis + */ + public MintTransaction getGenesis() { return this.genesis; } - public List> getTransactions() { + /** + * Get token transactions. + * + * @return token transactions + */ + public List getTransactions() { return this.transactions; } + /** + * Get token current state nametags. + * + * @return nametags + */ public List> getNametags() { return this.nametags; } - public static > Token create( + /** + * Create token from mint transaction and initial state. Also verify if state is correct. + * + * @param trustBase trust base for mint transaction verification + * @param state initial state + * @param transaction mint transaction + * @param mint transaction reason + * @return token + * @throws VerificationException if token state is invalid + */ + public static Token create( RootTrustBase trustBase, TokenState state, - Transaction transaction + MintTransaction transaction ) throws VerificationException { return Token.create(trustBase, state, transaction, List.of()); } - public static > Token create( + /** + * Create token state from mint transaction, initial state and nametags. Also verify if state is + * correct. + * + * @param trustBase trust base for mint transaction verification + * @param state initial state + * @param transaction mint transaction + * @param nametags nametags associated with transaction + * @param mint transaction reason + * @return token + * @throws VerificationException if token state is invalid + */ + public static Token create( RootTrustBase trustBase, TokenState state, - Transaction transaction, + MintTransaction transaction, List> nametags ) throws VerificationException { Objects.requireNonNull(state, "State cannot be null"); @@ -107,7 +200,7 @@ public static > Token create( Objects.requireNonNull(trustBase, "Trust base cannot be null"); Objects.requireNonNull(nametags, "Nametag tokens cannot be null"); - Token token = new Token<>(state, transaction, List.of(), nametags); + Token token = new Token<>(state, transaction, List.of(), nametags); VerificationResult result = token.verify(trustBase); if (!result.isSuccessful()) { throw new VerificationException("Token verification failed", result); @@ -116,10 +209,20 @@ public static > Token create( return token; } - public Token update( + /** + * Update token to next state with given transfer transaction. + * + * @param trustBase trust base to verify latest state + * @param state current state + * @param transaction latest transaction + * @param nametags nametags associated with transaction + * @return tokest with latest state + * @throws VerificationException if token state is invalid + */ + public Token update( RootTrustBase trustBase, TokenState state, - Transaction transaction, + TransferTransaction transaction, List> nametags ) throws VerificationException { Objects.requireNonNull(state, "State cannot be null"); @@ -133,14 +236,18 @@ public Token update( throw new VerificationException("Transaction verification failed", result); } - LinkedList> transactions = new LinkedList<>( - this.transactions - ); + LinkedList transactions = new LinkedList<>(this.transactions); transactions.add(transaction); - return new Token<>(state, this.getGenesis(), transactions, nametags); + return new Token<>(state, this.genesis, transactions, nametags); } + /** + * Verify current token state against trustbase. + * + * @param trustBase trust base to verify state against + * @return verification result + */ public VerificationResult verify(RootTrustBase trustBase) { List results = new ArrayList<>(); results.add( @@ -150,7 +257,7 @@ public VerificationResult verify(RootTrustBase trustBase) { ); for (int i = 0; i < this.transactions.size(); i++) { - Transaction transaction = this.transactions.get(i); + TransferTransaction transaction = this.transactions.get(i); results.add( VerificationResult.fromChildren( @@ -181,7 +288,7 @@ public VerificationResult verify(RootTrustBase trustBase) { private static VerificationResult verifyTransaction( Token token, - Transaction transaction, + TransferTransaction transaction, RootTrustBase trustBase ) { for (Token nametag : token.getNametags()) { @@ -202,9 +309,9 @@ private static VerificationResult verifyTransaction( return VerificationResult.fail("recipient mismatch"); } - if (!Token.transactionContainsData( - previousTransaction.getData().getDataHash().orElse(null), - token.getState().getData().orElse(null))) { + if (!previousTransaction.containsRecipientDataHash( + token.getState().getData().orElse(null)) + ) { return VerificationResult.fail("data mismatch"); } @@ -215,8 +322,8 @@ private static VerificationResult verifyTransaction( return VerificationResult.success(); } - private static > VerificationResult verifyGenesis( - Transaction transaction, + private static VerificationResult verifyGenesis( + MintTransaction transaction, RootTrustBase trustBase ) { if (transaction.getInclusionProof().getAuthenticator().isEmpty()) { @@ -256,8 +363,10 @@ private static > VerificationResult verifyGenes return reasonResult; } - RequestId requestId = RequestId.create(signingService.getPublicKey(), - transaction.getData().getSourceState().getHash()); + RequestId requestId = RequestId.create( + signingService.getPublicKey(), + transaction.getData().getSourceState() + ); if (transaction.getInclusionProof().verify(requestId, trustBase) != InclusionProofVerificationStatus.OK) { return VerificationResult.fail("Inclusion proof verification failed."); @@ -266,18 +375,79 @@ private static > VerificationResult verifyGenes return VerificationResult.success(); } - private static boolean transactionContainsData(DataHash hash, byte[] stateData) { - if ((hash == null) != (stateData == null)) { - return false; + /** + * Create token from CBOR bytes. + * + * @param bytes CBOR bytes + * @return token + */ + public static Token fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + String version = CborDeserializer.readTextString(data.get(0)); + if (!Token.TOKEN_VERSION.equals(version)) { + throw new CborSerializationException("Invalid version: " + version); } - if (hash == null) { - return true; + return new Token<>( + TokenState.fromCbor(data.get(1)), + MintTransaction.fromCbor(data.get(2)), + CborDeserializer.readArray(data.get(3)).stream() + .map(TransferTransaction::fromCbor) + .collect(Collectors.toList()), + CborDeserializer.readArray(data.get(4)).stream() + .map(Token::fromCbor) + .collect(Collectors.toList()) + ); + } + + /** + * Convert token to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + CborSerializer.encodeTextString(TOKEN_VERSION), + this.state.toCbor(), + this.genesis.toCbor(), + CborSerializer.encodeArray( + this.transactions.stream() + .map(TransferTransaction::toCbor) + .toArray(byte[][]::new) + ), + CborSerializer.encodeArray( + this.nametags.stream() + .map(Token::toCbor) + .toArray(byte[][]::new) + ) + ); + } + + /** + * Create token from JSON string. + * + * @param input JSON string + * @return token + */ + public static Token fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, Token.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(Token.class, e); } + } - DataHasher hasher = new DataHasher(HashAlgorithm.SHA256); - hasher.update(stateData); - return hasher.digest().equals(hash); + /** + * Convert token to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(Token.class, e); + } } @Override diff --git a/src/main/java/org/unicitylabs/sdk/token/TokenId.java b/src/main/java/org/unicitylabs/sdk/token/TokenId.java index 668ef8f..ed49ec2 100644 --- a/src/main/java/org/unicitylabs/sdk/token/TokenId.java +++ b/src/main/java/org/unicitylabs/sdk/token/TokenId.java @@ -1,62 +1,108 @@ - package org.unicitylabs.sdk.token; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Objects; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.util.BitString; import org.unicitylabs.sdk.util.HexConverter; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Objects; /** * Globally unique identifier of a token. */ +@JsonSerialize(using = TokenIdJson.Serializer.class) +@JsonDeserialize(using = TokenIdJson.Deserializer.class) public class TokenId { - private final byte[] bytes; - public TokenId(byte[] bytes) { - this.bytes = Arrays.copyOf(bytes, bytes.length); - } + private final byte[] bytes; - public byte[] getBytes() { - return Arrays.copyOf(this.bytes, this.bytes.length); - } + /** + * Create token id from bytes. + * + * @param bytes token id bytes + */ + public TokenId(byte[] bytes) { + this.bytes = Arrays.copyOf(bytes, bytes.length); + } - public BitString toBitString() { - return new BitString(this.bytes); - } + /** + * Get token id bytes. + * + * @return token id bytes + */ + public byte[] getBytes() { + return Arrays.copyOf(this.bytes, this.bytes.length); + } - public static TokenId fromNameTag(String name) { - Objects.requireNonNull(name, "Name cannot be null"); + /** + * Get token id as bit string. + * + * @return token id bit string + */ + public BitString toBitString() { + return new BitString(this.bytes); + } - return new TokenId( - new DataHasher(HashAlgorithm.SHA256) - .update(name.getBytes(StandardCharsets.UTF_8)) - .digest() - .getImprint() - ); - } + /** + * Create token id from nametag. + * + * @param name nametag + * @return token id + */ + public static TokenId fromNameTag(String name) { + Objects.requireNonNull(name, "Name cannot be null"); - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - TokenId tokenId = (TokenId) o; - return Arrays.equals(this.bytes, tokenId.bytes); - } + return new TokenId( + new DataHasher(HashAlgorithm.SHA256) + .update(name.getBytes(StandardCharsets.UTF_8)) + .digest() + .getImprint() + ); + } - @Override - public int hashCode() { - return Arrays.hashCode(this.bytes); - } + /** + * Create token id from CBOR bytes. + * + * @param bytes CBOR bytes + * @return token id + */ + public static TokenId fromCbor(byte[] bytes) { + return new TokenId(CborDeserializer.readByteString(bytes)); + } - @Override - public String toString() { - return String.format("TokenId[%s]", HexConverter.encode(this.bytes)); + /** + * Convert token id to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeByteString(this.bytes); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; } + TokenId tokenId = (TokenId) o; + return Arrays.equals(this.bytes, tokenId.bytes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(this.bytes); + } + + @Override + public String toString() { + return String.format("TokenId[%s]", HexConverter.encode(this.bytes)); + } } diff --git a/src/main/java/org/unicitylabs/sdk/token/TokenIdJson.java b/src/main/java/org/unicitylabs/sdk/token/TokenIdJson.java new file mode 100644 index 0000000..72f6eb9 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/token/TokenIdJson.java @@ -0,0 +1,91 @@ +package org.unicitylabs.sdk.token; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; + +/** + * Token id serializer and deserializer implementation. + */ +public class TokenIdJson { + + private TokenIdJson() { + } + + /** + * Token id serializer. + */ + public static class Serializer extends StdSerializer { + + /** + * Create token id serializer. + */ + public Serializer() { + super(TokenId.class); + } + + + /** + * Serialize token id. + * + * @param value token id + * @param gen json generator + * @param serializers serializer provider + * @throws IOException on serialization failure + */ + @Override + public void serialize(TokenId value, JsonGenerator gen, + SerializerProvider serializers) + throws IOException { + gen.writeObject(value.getBytes()); + } + } + + /** + * Token id deserializer. + */ + public static class Deserializer extends StdDeserializer { + + /** + * Create token id deserializer. + */ + public Deserializer() { + super(TokenId.class); + } + + + /** + * Deserialize token id. + * + * @param p Parser used for reading JSON content + * @param ctx Context that can be used to access information about this deserialization + * activity. + * @return token id + * @throws IOException on deserialization failure + */ + @Override + public TokenId deserialize(JsonParser p, DeserializationContext ctx) + throws IOException { + if (p.getCurrentToken() != JsonToken.VALUE_STRING) { + throw MismatchedInputException.from( + p, + TokenId.class, + "Expected string value" + ); + } + + try { + return new TokenId(p.readValueAs(byte[].class)); + } catch (Exception e) { + throw MismatchedInputException.from(p, TokenId.class, "Expected bytes"); + } + } + } +} + diff --git a/src/main/java/org/unicitylabs/sdk/token/TokenState.java b/src/main/java/org/unicitylabs/sdk/token/TokenState.java index 5cba2a6..ea0b533 100644 --- a/src/main/java/org/unicitylabs/sdk/token/TokenState.java +++ b/src/main/java/org/unicitylabs/sdk/token/TokenState.java @@ -1,54 +1,110 @@ package org.unicitylabs.sdk.token; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.Optional; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.predicate.PredicateEngineService; +import org.unicitylabs.sdk.predicate.EncodedPredicate; import org.unicitylabs.sdk.predicate.SerializablePredicate; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.predicate.SerializablePredicateJson; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.util.HexConverter; /** * Represents a snapshot of token ownership and associated data. */ -public class TokenState{ +public class TokenState { private final SerializablePredicate predicate; private final byte[] data; - public TokenState(SerializablePredicate predicate, byte[] data) { + /** + * Create token state. + * + * @param predicate current state predicate + * @param data current state data + */ + @JsonCreator + public TokenState( + @JsonSerialize(using = SerializablePredicateJson.Serializer.class) + @JsonDeserialize(using = SerializablePredicateJson.Deserializer.class) + @JsonProperty("predicate") SerializablePredicate predicate, + @JsonProperty("data") byte[] data + ) { Objects.requireNonNull(predicate, "Predicate cannot be null"); + this.predicate = predicate; this.data = data != null ? Arrays.copyOf(data, data.length) : null; } + /** + * Get current state predicate. + * + * @return state predicate + */ public SerializablePredicate getPredicate() { return this.predicate; } + /** + * Get current state data. + * + * @return state data + */ public Optional getData() { return this.data != null ? Optional.of(Arrays.copyOf(this.data, this.data.length)) : Optional.empty(); } + /** + * Calculate current state hash. + * + * @return state hash + */ public DataHash calculateHash() { - ArrayNode node = UnicityObjectMapper.CBOR.createArrayNode(); - node.addPOJO(PredicateEngineService.createPredicate(this.predicate).calculateHash()); - node.add(this.data); + return new DataHasher(HashAlgorithm.SHA256) + .update(this.toCbor()) + .digest(); + } - try { - return new DataHasher(HashAlgorithm.SHA256).update( - UnicityObjectMapper.CBOR.writeValueAsBytes(node)).digest(); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } + /** + * Create current state from CBOR bytes. + * + * @param bytes CBOR bytes + * @return current state + */ + public static TokenState fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new TokenState( + EncodedPredicate.fromCbor(data.get(0)), + CborDeserializer.readOptional(data.get(1), CborDeserializer::readByteString) + ); + } + + /** + * Convert current state to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + CborSerializer.encodeArray( + CborSerializer.encodeUnsignedInteger(this.predicate.getEngine().ordinal()), + this.predicate.encode(), + this.predicate.encodeParameters() + ), + CborSerializer.encodeOptional(this.data, CborSerializer::encodeByteString) + ); } @Override diff --git a/src/main/java/org/unicitylabs/sdk/token/TokenType.java b/src/main/java/org/unicitylabs/sdk/token/TokenType.java index 3649696..08c8b57 100644 --- a/src/main/java/org/unicitylabs/sdk/token/TokenType.java +++ b/src/main/java/org/unicitylabs/sdk/token/TokenType.java @@ -1,42 +1,78 @@ package org.unicitylabs.sdk.token; -import org.unicitylabs.sdk.util.HexConverter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.util.Arrays; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.util.HexConverter; /** * Unique identifier describing the type/category of a token. */ +@JsonSerialize(using = TokenTypeJson.Serializer.class) +@JsonDeserialize(using = TokenTypeJson.Deserializer.class) public class TokenType { - private final byte[] bytes; - public TokenType(byte[] bytes) { - this.bytes = Arrays.copyOf(bytes, bytes.length); - } + private final byte[] bytes; - public byte[] getBytes() { - return Arrays.copyOf(this.bytes, this.bytes.length); - } + /** + * Token type constructor. + * + * @param bytes type bytes + */ + public TokenType(byte[] bytes) { + this.bytes = Arrays.copyOf(bytes, bytes.length); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - TokenType tokenType = (TokenType) o; - return Arrays.equals(this.bytes, tokenType.bytes); - } + /** + * Get token type as bytes. + * + * @return type bytes + */ + public byte[] getBytes() { + return Arrays.copyOf(this.bytes, this.bytes.length); + } - @Override - public int hashCode() { - return Arrays.hashCode(this.bytes); - } + /** + * Create token type from CBOR. + * + * @param bytes CBOR bytes + * @return token type + */ + public static TokenType fromCbor(byte[] bytes) { + return new TokenType(CborDeserializer.readByteString(bytes)); + } + + /** + * Convert token type to CBOR. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeByteString(this.bytes); + } - @Override - public String toString() { - return String.format("TokenType[%s]", HexConverter.encode(this.bytes)); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; } + TokenType tokenType = (TokenType) o; + return Arrays.equals(this.bytes, tokenType.bytes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(this.bytes); + } + + @Override + public String toString() { + return String.format("TokenType[%s]", HexConverter.encode(this.bytes)); + } } diff --git a/src/main/java/org/unicitylabs/sdk/token/TokenTypeJson.java b/src/main/java/org/unicitylabs/sdk/token/TokenTypeJson.java new file mode 100644 index 0000000..8aacde5 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/token/TokenTypeJson.java @@ -0,0 +1,74 @@ +package org.unicitylabs.sdk.token; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; + +/** + * Token type serializer and deserializer implementation. + */ +public class TokenTypeJson { + + private TokenTypeJson() { + } + + /** + * Token type serializer. + */ + public static class Serializer extends StdSerializer { + + /** + * Create token type serializer. + */ + public Serializer() { + super(TokenType.class); + } + + /** + * Serialize token type. + * + * @param value token type + * @param gen json generator + * @param serializers serializer provider + * @throws IOException on serialization failure + */ + @Override + public void serialize(TokenType value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + gen.writeObject(value.getBytes()); + } + } + + /** + * Token type deserializer. + */ + public static class Deserializer extends StdDeserializer { + + /** + * Create token type deserializer. + */ + public Deserializer() { + super(TokenType.class); + } + + /** + * Deserialize token type. + * + * @param p Parser used for reading JSON content + * @param ctx Context that can be used to access information about this deserialization + * activity. + * @return token type + * @throws IOException on deserialization failure + */ + @Override + public TokenType deserialize(JsonParser p, DeserializationContext ctx) + throws IOException { + return new TokenType(p.readValueAs(byte[].class)); + } + } +} + diff --git a/src/main/java/org/unicitylabs/sdk/token/fungible/CoinId.java b/src/main/java/org/unicitylabs/sdk/token/fungible/CoinId.java index 91fa62d..f9bdb3f 100644 --- a/src/main/java/org/unicitylabs/sdk/token/fungible/CoinId.java +++ b/src/main/java/org/unicitylabs/sdk/token/fungible/CoinId.java @@ -1,22 +1,40 @@ package org.unicitylabs.sdk.token.fungible; +import java.util.Arrays; import org.unicitylabs.sdk.util.BitString; import org.unicitylabs.sdk.util.HexConverter; -import java.util.Arrays; +/** + * Coin ID representation. + */ public class CoinId { private final byte[] bytes; + /** + * Create coin ID from bytes. + * + * @param bytes coin identifier bytes. + */ public CoinId(byte[] bytes) { this.bytes = Arrays.copyOf(bytes, bytes.length); } + /** + * Get coin ID bytes. + * + * @return coin id bytes + */ public byte[] getBytes() { return Arrays.copyOf(this.bytes, this.bytes.length); } + /** + * Convert coin ID to bit string. + * + * @return coin id bitstring + */ public BitString toBitString() { return new BitString(this.bytes); } diff --git a/src/main/java/org/unicitylabs/sdk/token/fungible/TokenCoinData.java b/src/main/java/org/unicitylabs/sdk/token/fungible/TokenCoinData.java index de9b4c8..a09aad3 100644 --- a/src/main/java/org/unicitylabs/sdk/token/fungible/TokenCoinData.java +++ b/src/main/java/org/unicitylabs/sdk/token/fungible/TokenCoinData.java @@ -1,38 +1,109 @@ package org.unicitylabs.sdk.token.fungible; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.math.BigInteger; import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Objects; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.util.BigIntegerConverter; +/** + * Token coin data representation. + */ +@JsonSerialize(using = TokenCoinDataJson.Serializer.class) +@JsonDeserialize(using = TokenCoinDataJson.Deserializer.class) public class TokenCoinData { - private final Map coins; - public TokenCoinData(Map coins) { - this.coins = Collections.unmodifiableMap(coins); - } + private final Map coins; - public Map getCoins() { - return this.coins; - } + /** + * Create token coin data from coins map. + * + * @param coins map of token coins + */ + public TokenCoinData(Map coins) { + this.coins = Collections.unmodifiableMap(coins); + } - @Override - public boolean equals(Object o) { - if (!(o instanceof TokenCoinData)) { - return false; - } - TokenCoinData that = (TokenCoinData) o; - return Objects.equals(this.coins, that.coins); - } + /** + * Get token coins map. + * + * @return token coins map + */ + public Map getCoins() { + return this.coins; + } + + /** + * Create token coins data from CBOR. + * + * @param bytes CBOR bytes. + * @return token coin data + */ + public static TokenCoinData fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + Map coins = new LinkedHashMap<>(); + for (byte[] coinBytes : data) { + List coinData = CborDeserializer.readArray(coinBytes); + CoinId coinId = new CoinId(CborDeserializer.readByteString(coinData.get(0))); - @Override - public int hashCode() { - return Objects.hashCode(this.coins); + if (coins.containsKey(coinId)) { + throw new CborSerializationException( + String.format("Duplicate coin ID in coin data: %s", coinId) + ); + } + + BigInteger amount = BigIntegerConverter.decode( + CborDeserializer.readByteString(coinData.get(1)) + ); + coins.put(coinId, amount); } - @Override - public String toString() { - return String.format("TokenCoinData{%s}", this.coins); + return new TokenCoinData(coins); + } + + /** + * Convert token coins data to CBOR. + * + * @return token coins data as cbor + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + this.coins.entrySet().stream() + .map(entry -> CborSerializer.encodeArray( + CborSerializer.encodeByteString(entry.getKey().getBytes()), + CborSerializer.encodeByteString( + BigIntegerConverter.encode(entry.getValue()) + ) + )) + .toArray(byte[][]::new) + ); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof TokenCoinData)) { + return false; } + TokenCoinData that = (TokenCoinData) o; + return Objects.equals(this.coins, that.coins); + } + + @Override + public int hashCode() { + return Objects.hashCode(this.coins); + } + + @Override + public String toString() { + return String.format("TokenCoinData{%s}", this.coins); + } } diff --git a/src/main/java/org/unicitylabs/sdk/token/fungible/TokenCoinDataJson.java b/src/main/java/org/unicitylabs/sdk/token/fungible/TokenCoinDataJson.java new file mode 100644 index 0000000..0143110 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/token/fungible/TokenCoinDataJson.java @@ -0,0 +1,110 @@ +package org.unicitylabs.sdk.token.fungible; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.unicitylabs.sdk.util.HexConverter; + +/** + * Token coin data json serializer and deserializer. + */ +public class TokenCoinDataJson { + + private TokenCoinDataJson() { + } + + /** + * Token coin data serializer. + */ + public static class Serializer extends StdSerializer { + + /** + * Create token coin data serializer. + */ + public Serializer() { + super(TokenCoinData.class); + } + + /** + * Serialize token coin data. + * + * @param value token coin data. + * @param gen json generator. + * @param serializers serializer provider. + * @throws IOException on serialization failure + */ + @Override + public void serialize(TokenCoinData value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + gen.writeStartArray(); + for (Map.Entry entry : value.getCoins().entrySet()) { + gen.writeStartArray(); + gen.writeObject(HexConverter.encode(entry.getKey().getBytes())); + gen.writeObject(entry.getValue().toString()); + gen.writeEndArray(); + } + gen.writeEndArray(); + } + } + + /** + * Token coin data deserializer. + */ + public static class Deserializer extends StdDeserializer { + + /** + * Create token coin data deserializer. + */ + public Deserializer() { + super(TokenCoinData.class); + } + + /** + * Deserialize token coin data. + * + * @param p Parser used for reading JSON content + * @param ctx Context that can be used to access information about this deserialization + * activity. + * @return token coin data + * @throws IOException on deserialization failure + */ + @Override + public TokenCoinData deserialize(JsonParser p, DeserializationContext ctx) throws IOException { + List data = ctx.readValue(p, + ctx.getTypeFactory().constructCollectionType(List.class, + ctx.getTypeFactory().constructArrayType(String.class))); + + LinkedHashMap coins = new LinkedHashMap<>(); + for (String[] entry : data) { + if (entry.length != 2) { + throw MismatchedInputException.from( + p, + TokenCoinData.class, + "Each entry must be an array of two elements: [coinId, amount]" + ); + } + try { + CoinId coinId = new CoinId(HexConverter.decode(entry[0])); + if (coins.containsKey(coinId)) { + throw new IOException("Duplicate CoinId: " + coinId); + } + + coins.put(coinId, new BigInteger(entry[1])); + } catch (Exception e) { + throw MismatchedInputException.from(p, "Invalid coin data", e); + } + } + + return new TokenCoinData(coins); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/transaction/Commitment.java b/src/main/java/org/unicitylabs/sdk/transaction/Commitment.java index 38dbfaa..8a14bef 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/Commitment.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/Commitment.java @@ -6,16 +6,24 @@ import org.unicitylabs.sdk.api.RequestId; /** - * Commitment representing a submitted transaction + * Commitment representing a submitted transaction. * * @param the type of transaction data */ public abstract class Commitment> { + private final RequestId requestId; private final T transactionData; private final Authenticator authenticator; - public Commitment(RequestId requestId, T transactionData, Authenticator authenticator) { + /** + * Create commitment. + * + * @param requestId request id + * @param transactionData transaction data + * @param authenticator authenticator + */ + protected Commitment(RequestId requestId, T transactionData, Authenticator authenticator) { this.requestId = requestId; this.transactionData = transactionData; this.authenticator = authenticator; @@ -48,9 +56,13 @@ public Authenticator getAuthenticator() { return authenticator; } - public Transaction toTransaction(InclusionProof inclusionProof) { - return new Transaction<>(this.getTransactionData(), inclusionProof); - } + /** + * Convert commitment to transaction. + * + * @param inclusionProof Commitment inclusion proof + * @return transaction + */ + public abstract Transaction toTransaction(InclusionProof inclusionProof); @Override public boolean equals(Object o) { @@ -58,9 +70,9 @@ public boolean equals(Object o) { return false; } Commitment that = (Commitment) o; - return Objects.equals(this.requestId, that.requestId) && Objects.equals( - this.transactionData, that.transactionData) && Objects.equals(this.authenticator, - that.authenticator); + return Objects.equals(this.requestId, that.requestId) + && Objects.equals(this.transactionData, that.transactionData) + && Objects.equals(this.authenticator, that.authenticator); } @Override diff --git a/src/main/java/org/unicitylabs/sdk/transaction/InclusionProof.java b/src/main/java/org/unicitylabs/sdk/transaction/InclusionProof.java index 3bc215d..7bd0818 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/InclusionProof.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/InclusionProof.java @@ -1,6 +1,10 @@ package org.unicitylabs.sdk.transaction; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.Optional; import org.unicitylabs.sdk.api.Authenticator; @@ -14,7 +18,11 @@ import org.unicitylabs.sdk.mtree.MerkleTreePathVerificationResult; import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; /** * Represents a proof of inclusion or non-inclusion in a sparse merkle tree. @@ -26,11 +34,12 @@ public class InclusionProof { private final DataHash transactionHash; private final UnicityCertificate unicityCertificate; - public InclusionProof( - SparseMerkleTreePath merkleTreePath, - Authenticator authenticator, - DataHash transactionHash, - UnicityCertificate unicityCertificate + @JsonCreator + InclusionProof( + @JsonProperty("merkleTreePath") SparseMerkleTreePath merkleTreePath, + @JsonProperty("authenticator") Authenticator authenticator, + @JsonProperty("transactionHash") DataHash transactionHash, + @JsonProperty("unicityCertificate") UnicityCertificate unicityCertificate ) { Objects.requireNonNull(merkleTreePath, "Merkle tree path cannot be null."); Objects.requireNonNull(unicityCertificate, "Unicity certificate cannot be null."); @@ -45,22 +54,49 @@ public InclusionProof( this.unicityCertificate = unicityCertificate; } + /** + * Get merkle tree path. + * + * @return merkle tree path + */ public SparseMerkleTreePath getMerkleTreePath() { return this.merkleTreePath; } + /** + * Get unicity certificate. + * + * @return unicity certificate + */ public UnicityCertificate getUnicityCertificate() { return this.unicityCertificate; } + /** + * Get authenticator on inclusion proof, null on non inclusion proof. + * + * @return authenticator + */ public Optional getAuthenticator() { return Optional.ofNullable(this.authenticator); } + /** + * Get authenticator on inclusion proof, null on non inclusion proof. + * + * @return inclusion proof + */ public Optional getTransactionHash() { return Optional.ofNullable(this.transactionHash); } + /** + * Verify inclusion proof. + * + * @param requestId request id + * @param trustBase trust base for unicity certificate anchor verification + * @return inclusion proof verification status + */ public InclusionProofVerificationStatus verify(RequestId requestId, RootTrustBase trustBase) { // Check if path is valid and signed by a trusted authority if (!new UnicityCertificateVerificationRule().verify( @@ -79,7 +115,6 @@ public InclusionProofVerificationStatus verify(RequestId requestId, RootTrustBas return InclusionProofVerificationStatus.PATH_INVALID; } - if (this.authenticator != null && this.transactionHash != null) { if (!this.authenticator.verify(this.transactionHash)) { return InclusionProofVerificationStatus.NOT_AUTHENTICATED; @@ -104,6 +139,64 @@ public InclusionProofVerificationStatus verify(RequestId requestId, RootTrustBas return InclusionProofVerificationStatus.OK; } + /** + * Create inclusion proof from CBOR bytes. + * + * @param bytes CBOR bytes + * @return inclusion proof + */ + public static InclusionProof fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new InclusionProof( + SparseMerkleTreePath.fromCbor(data.get(0)), + CborDeserializer.readOptional(data.get(1), Authenticator::fromCbor), + CborDeserializer.readOptional(data.get(2), DataHash::fromCbor), + UnicityCertificate.fromCbor(data.get(3)) + ); + } + + /** + * Convert inclusion proof to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + this.merkleTreePath.toCbor(), + CborSerializer.encodeOptional(this.authenticator, Authenticator::toCbor), + CborSerializer.encodeOptional(this.transactionHash, DataHash::toCbor), + this.unicityCertificate.toCbor() + ); + } + + /** + * Get inclusion proof from JSON. + * + * @param input inclusion proof JSON string + * @return inclusion proof + */ + public static InclusionProof fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, InclusionProof.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(InclusionProof.class, e); + } + } + + /** + * Get inclusion proof as JSON. + * + * @return inclusion proof JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(InclusionProof.class, e); + } + } + @Override public boolean equals(Object o) { if (!(o instanceof InclusionProof)) { diff --git a/src/main/java/org/unicitylabs/sdk/transaction/InclusionProofVerificationStatus.java b/src/main/java/org/unicitylabs/sdk/transaction/InclusionProofVerificationStatus.java index 74fd941..aba5b45 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/InclusionProofVerificationStatus.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/InclusionProofVerificationStatus.java @@ -4,18 +4,23 @@ * Status codes for verifying an InclusionProof. */ public enum InclusionProofVerificationStatus { - NOT_AUTHENTICATED("NOT_AUTHENTICATED"), - PATH_NOT_INCLUDED("PATH_NOT_INCLUDED"), - PATH_INVALID("PATH_INVALID"), - OK("OK"); + NOT_AUTHENTICATED("NOT_AUTHENTICATED"), + PATH_NOT_INCLUDED("PATH_NOT_INCLUDED"), + PATH_INVALID("PATH_INVALID"), + OK("OK"); - private final String value; + private final String value; - InclusionProofVerificationStatus(String value) { - this.value = value; - } + InclusionProofVerificationStatus(String value) { + this.value = value; + } - public String getValue() { - return value; - } + /** + * Get inclusion proof verification status value. + * + * @return status value + */ + public String getValue() { + return value; + } } \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintCommitment.java b/src/main/java/org/unicitylabs/sdk/transaction/MintCommitment.java index 1384db7..ae164f7 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintCommitment.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintCommitment.java @@ -1,6 +1,8 @@ package org.unicitylabs.sdk.transaction; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; import org.unicitylabs.sdk.api.Authenticator; import org.unicitylabs.sdk.api.RequestId; @@ -9,36 +11,77 @@ import org.unicitylabs.sdk.util.HexConverter; /** - * Commitment representing a submitted transaction + * Commitment representing a submitted transaction. * - * @param the type of transaction data + * @param the type of transaction data */ -public class MintCommitment> extends Commitment { +public class MintCommitment extends + Commitment> { + public static final byte[] MINTER_SECRET = HexConverter.decode( "495f414d5f554e4956455253414c5f4d494e5445525f464f525f"); - public MintCommitment(RequestId requestId, T transactionData, Authenticator authenticator) { + @JsonCreator + private MintCommitment( + @JsonProperty("requestId") + RequestId requestId, + @JsonProperty("transactionData") + MintTransaction.Data transactionData, + @JsonProperty("authenticator") + Authenticator authenticator + ) { super(requestId, transactionData, authenticator); } - public static > MintCommitment create( - T transactionData + /** + * Create mint transaction from commitment. + * + * @param inclusionProof Commitment inclusion proof + * @return mint transaction + */ + @Override + public MintTransaction toTransaction(InclusionProof inclusionProof) { + return new MintTransaction<>(this.getTransactionData(), inclusionProof); + } + + /** + * Create mint commitment from transaction data. + * + * @param transactionData mint transaction data + * @param mint reason + * @return mint commitment + */ + public static MintCommitment create( + MintTransaction.Data transactionData ) { Objects.requireNonNull(transactionData, "Transaction data cannot be null"); SigningService signingService = MintCommitment.createSigningService(transactionData); - DataHash sourceStateHash = transactionData.getSourceState().getHash(); DataHash transactionHash = transactionData.calculateHash(); - RequestId requestId = RequestId.create(signingService.getPublicKey(), sourceStateHash); - Authenticator authenticator = Authenticator.create(signingService, transactionHash, - sourceStateHash); + RequestId requestId = RequestId.create( + signingService.getPublicKey(), + transactionData.getSourceState() + ); + Authenticator authenticator = Authenticator.create( + signingService, + transactionHash, + transactionData.getSourceState() + ); return new MintCommitment<>(requestId, transactionData, authenticator); } - public static SigningService createSigningService(MintTransactionData transactionData) { - return SigningService.createFromMaskedSecret(MINTER_SECRET, transactionData.getTokenId().getBytes()); + + /** + * Create signing service for initial mint. + * + * @param transactionData mint transaction data + * @return signing service + */ + public static SigningService createSigningService(MintTransaction.Data transactionData) { + return SigningService.createFromMaskedSecret(MINTER_SECRET, + transactionData.getTokenId().getBytes()); } } diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintReasonType.java b/src/main/java/org/unicitylabs/sdk/transaction/MintReasonType.java index 15d0f51..d0c5480 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintReasonType.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintReasonType.java @@ -1,5 +1,8 @@ package org.unicitylabs.sdk.transaction; +/** + * Mint reason type. + */ public enum MintReasonType { TOKEN_SPLIT, } diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintTransaction.java b/src/main/java/org/unicitylabs/sdk/transaction/MintTransaction.java new file mode 100644 index 0000000..849e10c --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintTransaction.java @@ -0,0 +1,369 @@ + +package org.unicitylabs.sdk.transaction; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import org.unicitylabs.sdk.address.Address; +import org.unicitylabs.sdk.address.AddressFactory; +import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.hash.DataHasher; +import org.unicitylabs.sdk.hash.HashAlgorithm; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; +import org.unicitylabs.sdk.token.TokenId; +import org.unicitylabs.sdk.token.TokenType; +import org.unicitylabs.sdk.token.fungible.TokenCoinData; +import org.unicitylabs.sdk.transaction.split.SplitMintReason; +import org.unicitylabs.sdk.util.HexConverter; + + +/** + * Mint transaction. + * + * @param mint reason + */ +public class MintTransaction extends + Transaction> { + + @JsonCreator + MintTransaction( + @JsonProperty("data") + Data data, + @JsonProperty("inclusionProof") + InclusionProof inclusionProof) { + super(data, inclusionProof); + } + + /** + * Create mint transaction from CBOR bytes. + * + * @param bytes CBOR bytes + * @return mint transaction + */ + public static MintTransaction fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new MintTransaction<>( + Data.fromCbor(data.get(0)), + InclusionProof.fromCbor(data.get(1)) + ); + } + + /** + * Create mint transaction from JSON string. + * + * @param input JSON string + * @return mint transaction + */ + public static MintTransaction fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, MintTransaction.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(MintTransaction.class, e); + } + } + + /** + * Mint transaction data. + * + * @param mint reason + */ + public static class Data implements + TransactionData { + + private final TokenId tokenId; + private final TokenType tokenType; + private final byte[] tokenData; + private final TokenCoinData coinData; + private final MintTransactionState sourceState; + private final Address recipient; + private final byte[] salt; + private final DataHash recipientDataHash; + private final R reason; + + /** + * Create mint transaction data. + * + * @param tokenId token id + * @param tokenType token type + * @param tokenData token immutable data + * @param coinData token coin data + * @param recipient token recipient address + * @param salt mint transaction salt + * @param recipientDataHash recipient data hash + * @param reason mint reason + */ + @JsonCreator + public Data( + @JsonProperty("tokenId") TokenId tokenId, + @JsonProperty("tokenType") TokenType tokenType, + @JsonProperty("tokenData") byte[] tokenData, + @JsonProperty("coinData") TokenCoinData coinData, + @JsonProperty("recipient") Address recipient, + @JsonProperty("salt") byte[] salt, + @JsonProperty("recipientDataHash") DataHash recipientDataHash, + @JsonProperty("reason") R reason + ) { + Objects.requireNonNull(tokenId, "Token ID cannot be null"); + Objects.requireNonNull(tokenType, "Token type cannot be null"); + Objects.requireNonNull(recipient, "Recipient cannot be null"); + Objects.requireNonNull(salt, "Salt cannot be null"); + + this.tokenId = tokenId; + this.tokenType = tokenType; + this.tokenData = tokenData == null ? null : Arrays.copyOf(tokenData, tokenData.length); + this.coinData = coinData; + this.sourceState = MintTransactionState.create(tokenId); + this.recipient = recipient; + this.salt = Arrays.copyOf(salt, salt.length); + this.recipientDataHash = recipientDataHash; + this.reason = reason; + } + + /** + * Get token id. + * + * @return token id + */ + public TokenId getTokenId() { + return this.tokenId; + } + + /** + * Get token type. + * + * @return token type + */ + public TokenType getTokenType() { + return this.tokenType; + } + + /** + * Get immutable token data. + * + * @return token data + */ + public Optional getTokenData() { + return Optional.ofNullable(this.tokenData); + } + + /** + * Get token coin data. + * + * @return token coin data + */ + public Optional getCoinData() { + return Optional.ofNullable(this.coinData); + } + + /** + * Get recipient data hash. + * + * @return recipient data hash + */ + public Optional getRecipientDataHash() { + return Optional.ofNullable(this.recipientDataHash); + } + + /** + * Get mint transaction salt. + * + * @return transaction salt + */ + public byte[] getSalt() { + return Arrays.copyOf(this.salt, this.salt.length); + } + + /** + * Get token recipient address. + * + * @return recipient address + */ + public Address getRecipient() { + return this.recipient; + } + + /** + * Get mint reason. + * + * @return mint reason + */ + public Optional getReason() { + return Optional.ofNullable(this.reason); + } + + /** + * Get mint transaction source state. + * + * @return source state + */ + @JsonIgnore + public MintTransactionState getSourceState() { + return this.sourceState; + } + + /** + * Calculate mint transaction hash. + * + * @return transaction hash. + */ + public DataHash calculateHash() { + return new DataHasher(HashAlgorithm.SHA256) + .update(this.toCbor()) + .digest(); + } + + /** + * Create mint transaction data from CBOR bytes. + * + * @param bytes CBOR bytes + * @return mint transaction data + */ + public static Data fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new Data<>( + TokenId.fromCbor(data.get(0)), + TokenType.fromCbor(data.get(1)), + CborDeserializer.readOptional(data.get(2), CborDeserializer::readByteString), + CborDeserializer.readOptional(data.get(3), TokenCoinData::fromCbor), + AddressFactory.createAddress(CborDeserializer.readTextString(data.get(4))), + CborDeserializer.readByteString(data.get(5)), + CborDeserializer.readOptional(data.get(6), DataHash::fromCbor), + CborDeserializer.readOptional(data.get(7), SplitMintReason::fromCbor) + ); + } + + /** + * Convert mint transaction data to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + this.tokenId.toCbor(), + this.tokenType.toCbor(), + CborSerializer.encodeOptional(this.tokenData, CborSerializer::encodeByteString), + CborSerializer.encodeOptional(this.coinData, TokenCoinData::toCbor), + CborSerializer.encodeTextString(this.recipient.getAddress()), + CborSerializer.encodeByteString(this.salt), + CborSerializer.encodeOptional(this.recipientDataHash, DataHash::toCbor), + CborSerializer.encodeOptional(this.reason, MintTransactionReason::toCbor) + ); + } + + /** + * Create mint transaction data from JSON string. + * + * @param input JSON string + * @return mint transaction data + */ + public static Data fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, Data.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(Data.class, e); + } + } + + /** + * Convert mint transaction data to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(Data.class, e); + } + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Data)) { + return false; + } + Data that = (Data) o; + + return Objects.equals(this.tokenId, that.tokenId) + && Objects.equals(this.tokenType, that.tokenType) + && Objects.deepEquals(this.tokenData, that.tokenData) + && Objects.equals(this.coinData, that.coinData) + && Objects.equals(this.sourceState, that.sourceState) + && Objects.equals(this.recipient, that.recipient) + && Objects.deepEquals(this.salt, that.salt) + && Objects.equals(this.recipientDataHash, that.recipientDataHash) + && Objects.equals(this.reason, that.reason); + } + + @Override + public int hashCode() { + return Objects.hash(this.tokenId, this.tokenType, Arrays.hashCode(tokenData), this.coinData, + this.sourceState, + this.recipient, Arrays.hashCode(this.salt), this.recipientDataHash, this.reason); + } + + @Override + public String toString() { + return String.format( + "Data{" + + "tokenId=%s, " + + "tokenType=%s, " + + "tokenData=%s, " + + "coinData=%s, " + + "sourceState=%s, " + + "recipient=%s, " + + "salt=%s, " + + "dataHash=%s, " + + "reason=%s" + + "}", + this.tokenId, this.tokenType, HexConverter.encode(this.tokenData), this.coinData, + this.sourceState, this.recipient, HexConverter.encode(this.salt), this.recipientDataHash, + this.reason); + } + } + + /** + * Nametag mint data. + */ + public static class NametagData extends Data { + + /** + * Create nametag mint data. + * + * @param name nametag + * @param tokenType token type + * @param recipient recipient address + * @param salt mint salt + * @param targetAddress target address + */ + public NametagData( + String name, + TokenType tokenType, + Address recipient, + byte[] salt, + Address targetAddress + ) { + super( + TokenId.fromNameTag(name), + tokenType, + targetAddress.getAddress().getBytes(StandardCharsets.UTF_8), + null, + recipient, + salt, + null, + null + ); + } + } +} diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionData.java b/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionData.java deleted file mode 100644 index 364ec42..0000000 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionData.java +++ /dev/null @@ -1,155 +0,0 @@ -package org.unicitylabs.sdk.transaction; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.ArrayNode; -import org.unicitylabs.sdk.address.Address; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.hash.DataHasher; -import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; -import org.unicitylabs.sdk.token.TokenId; -import org.unicitylabs.sdk.token.TokenType; -import org.unicitylabs.sdk.token.fungible.TokenCoinData; -import org.unicitylabs.sdk.util.HexConverter; -import java.util.Arrays; -import java.util.Objects; -import java.util.Optional; - -public class MintTransactionData implements - TransactionData { - - private final TokenId tokenId; - private final TokenType tokenType; - private final byte[] tokenData; - private final TokenCoinData coinData; - private final MintTransactionState sourceState; - private final Address recipient; - private final byte[] salt; - private final DataHash dataHash; - private final R reason; - - public MintTransactionData( - TokenId tokenId, - TokenType tokenType, - byte[] tokenData, - TokenCoinData coinData, - Address recipient, - byte[] salt, - DataHash dataHash, - R reason - ) { - Objects.requireNonNull(tokenId, "Token ID cannot be null"); - Objects.requireNonNull(tokenType, "Token type cannot be null"); - Objects.requireNonNull(recipient, "Recipient cannot be null"); - Objects.requireNonNull(salt, "Salt cannot be null"); - - this.tokenId = tokenId; - this.tokenType = tokenType; - this.tokenData = tokenData == null ? null : Arrays.copyOf(tokenData, tokenData.length); - this.coinData = coinData; - this.sourceState = MintTransactionState.create(tokenId); - this.recipient = recipient; - this.salt = Arrays.copyOf(salt, salt.length); - this.dataHash = dataHash; - this.reason = reason; - } - - public TokenId getTokenId() { - return this.tokenId; - } - - public TokenType getTokenType() { - return this.tokenType; - } - - - public Optional getTokenData() { - return Optional.ofNullable(this.tokenData); - } - - public Optional getCoinData() { - return Optional.ofNullable(this.coinData); - } - - public Optional getDataHash() { - return Optional.ofNullable(this.dataHash); - } - - public byte[] getSalt() { - return Arrays.copyOf(this.salt, this.salt.length); - } - - public Address getRecipient() { - return this.recipient; - } - - public Optional getReason() { - return Optional.ofNullable(this.reason); - } - - public MintTransactionState getSourceState() { - return this.sourceState; - } - - public DataHash calculateHash() { - ArrayNode node = UnicityObjectMapper.CBOR.createArrayNode(); - node.addPOJO(this.tokenId); - node.addPOJO(this.tokenType); - node.addPOJO(this.tokenData); - node.addPOJO(this.coinData); - node.add(this.recipient.getAddress()); - node.add(this.salt); - node.addPOJO(this.dataHash); - node.addPOJO(this.reason); - - try { - return new DataHasher(HashAlgorithm.SHA256) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(node)) - .digest(); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof MintTransactionData)) { - return false; - } - MintTransactionData that = (MintTransactionData) o; - - return Objects.equals(this.tokenId, that.tokenId) && Objects.equals(this.tokenType, - that.tokenType) && Objects.deepEquals(this.tokenData, that.tokenData) - && Objects.equals(this.coinData, that.coinData) && Objects.equals(this.sourceState, - that.sourceState) && Objects.equals(this.recipient, that.recipient) - && Objects.deepEquals(this.salt, that.salt) && Objects.equals(this.dataHash, - that.dataHash) && Objects.equals(this.reason, that.reason); - } - - @Override - public int hashCode() { - return Objects.hash(this.tokenId, this.tokenType, Arrays.hashCode(tokenData), this.coinData, - this.sourceState, - this.recipient, Arrays.hashCode(this.salt), this.dataHash, this.reason); - } - - @Override - public String toString() { - return String.format( - "MintTransactionData{" - + "tokenId=%s, " - + "tokenType=%s, " - + "tokenData=%s, " - + "coinData=%s, " - + "sourceState=%s, " - + "recipient=%s, " - + "salt=%s, " - + "dataHash=%s, " - + "reason=%s" - + "}", - this.tokenId, this.tokenType, HexConverter.encode(this.tokenData), this.coinData, - this.sourceState, this.recipient, HexConverter.encode(this.salt), this.dataHash, - this.reason); - } -} \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionReason.java b/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionReason.java index 256cada..e7718ae 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionReason.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionReason.java @@ -2,7 +2,30 @@ import org.unicitylabs.sdk.verification.VerificationResult; +/** + * Mint transaction reason. + */ public interface MintTransactionReason { + + /** + * Get mint reason type. + * + * @return reason type + */ String getType(); - VerificationResult verify(Transaction> genesis); + + /** + * Verify mint reason for genesis. + * + * @param genesis Genesis to verify against + * @return verification result + */ + VerificationResult verify(MintTransaction genesis); + + /** + * Convert mint transaction reason to CBOR bytes. + * + * @return CBOR representation of reason + */ + byte[] toCbor(); } diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionState.java b/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionState.java index f933610..9cb2c5c 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionState.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionState.java @@ -5,15 +5,25 @@ import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.util.HexConverter; -public class MintTransactionState extends RequestId { +/** + * Token mint state. + */ +public class MintTransactionState extends DataHash { + private static final byte[] MINT_SUFFIX = HexConverter.decode( "9e82002c144d7c5796c50f6db50a0c7bbd7f717ae3af6c6c71a3e9eba3022730"); private MintTransactionState(DataHash hash) { - super(hash); + super(hash.getAlgorithm(), hash.getData()); } + /** + * Create token initial state from token id. + * + * @param tokenId token id + * @return mint state + */ public static MintTransactionState create(TokenId tokenId) { - return new MintTransactionState(RequestId.createFromImprint(tokenId.getBytes(), MINT_SUFFIX).getHash()); + return new MintTransactionState(RequestId.createFromImprint(tokenId.getBytes(), MINT_SUFFIX)); } } diff --git a/src/main/java/org/unicitylabs/sdk/transaction/NametagMintTransactionData.java b/src/main/java/org/unicitylabs/sdk/transaction/NametagMintTransactionData.java deleted file mode 100644 index 6c42436..0000000 --- a/src/main/java/org/unicitylabs/sdk/transaction/NametagMintTransactionData.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.unicitylabs.sdk.transaction; - -import java.nio.charset.StandardCharsets; -import org.unicitylabs.sdk.address.Address; -import org.unicitylabs.sdk.token.TokenId; -import org.unicitylabs.sdk.token.TokenType; - -public class NametagMintTransactionData extends - MintTransactionData { - - public NametagMintTransactionData( - String name, - TokenType tokenType, - Address recipient, - byte[] salt, - Address targetAddress - ) { - super( - TokenId.fromNameTag(name), - tokenType, - targetAddress.getAddress().getBytes(StandardCharsets.UTF_8), - null, - recipient, - salt, - null, - null - ); - } -} \ No newline at end of file diff --git a/src/main/java/org/unicitylabs/sdk/transaction/Transaction.java b/src/main/java/org/unicitylabs/sdk/transaction/Transaction.java index a2dda0e..ae5269e 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/Transaction.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/Transaction.java @@ -1,17 +1,32 @@ package org.unicitylabs.sdk.transaction; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import java.util.Objects; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; -import java.util.Objects; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; -public class Transaction> { +/** + * Token transaction. + * + * @param transaction data + */ +public abstract class Transaction> { private final T data; private final InclusionProof inclusionProof; - public Transaction(T data, InclusionProof inclusionProof) { + @JsonCreator + Transaction( + @JsonProperty("data") T data, + @JsonProperty("inclusionProof") InclusionProof inclusionProof + ) { Objects.requireNonNull(data, "Transaction data cannot be null"); Objects.requireNonNull(inclusionProof, "Inclusion proof cannot be null"); @@ -19,26 +34,67 @@ public Transaction(T data, InclusionProof inclusionProof) { this.inclusionProof = inclusionProof; } + /** + * Get transaction data. + * + * @return transaction data + */ public T getData() { return data; } + /** + * Get transaction inclusion proof. + * + * @return inclusion proof + */ public InclusionProof getInclusionProof() { return inclusionProof; } - public boolean containsData(byte[] stateData) { - if (this.data.getDataHash().isPresent() == (stateData == null)) { + /** + * Verify if recipient data is added to transaction. + * + * @param stateData recipient data + * @return true if contains given data hash + */ + public boolean containsRecipientDataHash(byte[] stateData) { + if (this.data.getRecipientDataHash().isPresent() == (stateData == null)) { return false; } - if (this.data.getDataHash().isEmpty()) { + if (this.data.getRecipientDataHash().isEmpty()) { return true; } DataHasher hasher = new DataHasher(HashAlgorithm.SHA256); hasher.update(stateData); - return hasher.digest().equals(this.data.getDataHash().orElse(null)); + return hasher.digest().equals(this.data.getRecipientDataHash().orElse(null)); + } + + /** + * Convert transaction to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(Transaction.class, e); + } + } + + /** + * Convert transaction to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + this.data.toCbor(), + this.inclusionProof.toCbor() + ); } @Override diff --git a/src/main/java/org/unicitylabs/sdk/transaction/TransactionData.java b/src/main/java/org/unicitylabs/sdk/transaction/TransactionData.java index e51b78a..74c0028 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/TransactionData.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/TransactionData.java @@ -1,12 +1,48 @@ package org.unicitylabs.sdk.transaction; +import java.util.Optional; import org.unicitylabs.sdk.address.Address; import org.unicitylabs.sdk.hash.DataHash; -import java.util.Optional; +/** + * Interface representing the data of a transaction. + * + * @param the type of the transaction source state + */ public interface TransactionData { - T getSourceState(); - Address getRecipient(); - DataHash calculateHash(); - Optional getDataHash(); + + /** + * Gets the transaction source state. + * + * @return the source state + */ + T getSourceState(); + + /** + * Gets the recipient address of the transaction. + * + * @return the recipient address + */ + Address getRecipient(); + + /** + * Gets the optional recipient data hash. + * + * @return an Optional containing the data hash if present, otherwise empty + */ + Optional getRecipientDataHash(); + + /** + * Calculates the hash of the transaction data. + * + * @return the calculated DataHash + */ + DataHash calculateHash(); + + /** + * Convert transaction data to CBOR bytes. + * + * @return CBOR bytes + */ + byte[] toCbor(); } diff --git a/src/main/java/org/unicitylabs/sdk/transaction/TransferCommitment.java b/src/main/java/org/unicitylabs/sdk/transaction/TransferCommitment.java index 542754e..4784fc9 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/TransferCommitment.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/TransferCommitment.java @@ -1,29 +1,60 @@ package org.unicitylabs.sdk.transaction; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; import org.unicitylabs.sdk.address.Address; import org.unicitylabs.sdk.api.Authenticator; import org.unicitylabs.sdk.api.RequestId; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.token.Token; -import java.util.Objects; /** - * Commitment representing a transfer transaction + * Commitment representing a transfer transaction. */ -public class TransferCommitment extends Commitment { +public class TransferCommitment extends Commitment { - public TransferCommitment(RequestId requestId, TransferTransactionData transactionData, - Authenticator authenticator) { + @JsonCreator + private TransferCommitment( + @JsonProperty("requestId") + RequestId requestId, + @JsonProperty("transactionData") + TransferTransaction.Data transactionData, + @JsonProperty("authenticator") + Authenticator authenticator + ) { super(requestId, transactionData, authenticator); } + /** + * Create transfer transaction from transfer commitment. + * + * @param inclusionProof Commitment inclusion proof + * @return transfer transaction + */ + @Override + public TransferTransaction toTransaction(InclusionProof inclusionProof) { + return new TransferTransaction(this.getTransactionData(), inclusionProof); + } + + /** + * Create transfer commitment. + * + * @param token current token + * @param recipient recipient of token + * @param salt transaction salt + * @param recipientDataHash recipient data hash + * @param message transaction message + * @param signingService signing service to unlock token + * @return transfer commitment + */ public static TransferCommitment create( Token token, Address recipient, byte[] salt, - DataHash dataHash, + DataHash recipientDataHash, byte[] message, SigningService signingService ) { @@ -32,8 +63,8 @@ public static TransferCommitment create( Objects.requireNonNull(salt, "Salt cannot be null"); Objects.requireNonNull(signingService, "SigningService cannot be null"); - TransferTransactionData transactionData = new TransferTransactionData( - token.getState(), recipient, salt, dataHash, message, token.getNametags()); + TransferTransaction.Data transactionData = new TransferTransaction.Data( + token.getState(), recipient, salt, recipientDataHash, message, token.getNametags()); DataHash sourceStateHash = transactionData.getSourceState().calculateHash(); DataHash transactionHash = transactionData.calculateHash(); diff --git a/src/main/java/org/unicitylabs/sdk/transaction/TransferTransaction.java b/src/main/java/org/unicitylabs/sdk/transaction/TransferTransaction.java new file mode 100644 index 0000000..570ffa0 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/transaction/TransferTransaction.java @@ -0,0 +1,274 @@ + +package org.unicitylabs.sdk.transaction; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import org.unicitylabs.sdk.address.Address; +import org.unicitylabs.sdk.address.AddressFactory; +import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.hash.DataHasher; +import org.unicitylabs.sdk.hash.HashAlgorithm; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; +import org.unicitylabs.sdk.token.Token; +import org.unicitylabs.sdk.token.TokenState; +import org.unicitylabs.sdk.util.HexConverter; + +/** + * Token transfer transaction. + */ +public class TransferTransaction extends Transaction { + + @JsonCreator + TransferTransaction( + @JsonProperty("data") + Data data, + @JsonProperty("inclusionProof") + InclusionProof inclusionProof) { + super(data, inclusionProof); + } + + /** + * Create transfer transaction from CBOR bytes. + * + * @param bytes CBOR bytes + * @return transfer transaction + */ + public static TransferTransaction fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new TransferTransaction( + Data.fromCbor(data.get(0)), + InclusionProof.fromCbor(data.get(1)) + ); + } + + /** + * Create transfer transaction from JSON string. + * + * @param input JSON string + * @return transfer transaction + */ + public static TransferTransaction fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, TransferTransaction.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(TransferTransaction.class, e); + } + } + + /** + * Transaction data for token state transitions. + */ + public static class Data implements TransactionData { + + private final TokenState sourceState; + private final Address recipient; + private final byte[] salt; + private final DataHash recipientDataHash; + private final byte[] message; + private final List> nametags; + + @JsonCreator + Data( + @JsonProperty("sourceState") TokenState sourceState, + @JsonProperty("recipient") Address recipient, + @JsonProperty("salt") byte[] salt, + @JsonProperty("recipientDataHash") DataHash recipientDataHash, + @JsonProperty("message") byte[] message, + @JsonProperty("nametags") List> nametags + ) { + Objects.requireNonNull(sourceState, "SourceState cannot be null"); + Objects.requireNonNull(recipient, "Recipient cannot be null"); + Objects.requireNonNull(salt, "Salt cannot be null"); + Objects.requireNonNull(nametags, "Nametags cannot be null"); + + this.sourceState = sourceState; + this.recipient = recipient; + this.salt = Arrays.copyOf(salt, salt.length); + this.recipientDataHash = recipientDataHash; + this.message = message != null ? Arrays.copyOf(message, message.length) : null; + this.nametags = List.copyOf(nametags); + } + + /** + * Get transaction source state. + * + * @return source state + */ + public TokenState getSourceState() { + return this.sourceState; + } + + /** + * Get transaction recipient address. + * + * @return recipient address + */ + public Address getRecipient() { + return this.recipient; + } + + /** + * Get transaction salt. + * + * @return transaction salt + */ + public byte[] getSalt() { + return Arrays.copyOf(this.salt, this.salt.length); + } + + /** + * Get transaction recipient data hash. + * + * @return recipient data hash + */ + public Optional getRecipientDataHash() { + return Optional.ofNullable(this.recipientDataHash); + } + + /** + * Get transaction message. + * + * @return transaction message + */ + public Optional getMessage() { + return this.message != null + ? Optional.of(Arrays.copyOf(this.message, this.message.length)) + : Optional.empty(); + } + + /** + * Get transaction nametags. + * + * @return nametags + */ + public List> getNametags() { + return this.nametags; + } + + /** + * Calculate transfer transaction data hash. + * + * @return transaction data hash + */ + public DataHash calculateHash() { + return new DataHasher(HashAlgorithm.SHA256) + .update(this.toCbor()) + .digest(); + } + + /** + * Create transfer transaction data from CBOR bytes. + * + * @param bytes CBOR bytes + * @return transfer transaction + */ + public static Data fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new Data( + TokenState.fromCbor(data.get(0)), + AddressFactory.createAddress(CborDeserializer.readTextString(data.get(1))), + CborDeserializer.readByteString(data.get(2)), + CborDeserializer.readOptional(data.get(3), DataHash::fromCbor), + CborDeserializer.readOptional(data.get(4), CborDeserializer::readByteString), + CborDeserializer.readArray(data.get(5)).stream() + .map(Token::fromCbor) + .collect(Collectors.toList()) + ); + } + + /** + * Convert transfer transaction data to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + this.sourceState.toCbor(), + CborSerializer.encodeTextString(this.recipient.getAddress()), + CborSerializer.encodeByteString(this.salt), + CborSerializer.encodeOptional(this.recipientDataHash, DataHash::toCbor), + CborSerializer.encodeOptional(this.message, CborSerializer::encodeByteString), + CborSerializer.encodeArray( + this.nametags.stream() + .map(Token::toCbor) + .toArray(byte[][]::new) + ) + ); + } + + /** + * Create transfer transaction data from JSON string. + * + * @param input JSON string + * @return transfer transaction data + */ + public static Data fromJson(String input) { + try { + return UnicityObjectMapper.JSON.readValue(input, Data.class); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(Data.class, e); + } + } + + /** + * Convert transfer transaction data to JSON string. + * + * @return JSON string + */ + public String toJson() { + try { + return UnicityObjectMapper.JSON.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new JsonSerializationException(Data.class, e); + } + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Data)) { + return false; + } + Data that = (Data) o; + return Objects.equals(this.sourceState, that.sourceState) + && Objects.equals(this.recipient, that.recipient) + && Objects.deepEquals(this.salt, that.salt) + && Objects.equals(this.recipientDataHash, that.recipientDataHash) + && Objects.deepEquals(this.message, that.message) + && Objects.equals(this.nametags, that.nametags); + } + + @Override + public int hashCode() { + return Objects.hash(this.sourceState, this.recipient, Arrays.hashCode(this.salt), + this.recipientDataHash, + Arrays.hashCode(this.message), this.nametags); + } + + @Override + public String toString() { + return String.format( + "Data{" + + "sourceState=%s, " + + "recipient=%s, " + + "salt=%s, " + + "dataHash=%s, " + + "message=%s, " + + "nametags=%s" + + "}", + this.sourceState, this.recipient, HexConverter.encode(this.salt), this.recipientDataHash, + this.message != null ? HexConverter.encode(this.message) : null, this.nametags); + } + } + +} diff --git a/src/main/java/org/unicitylabs/sdk/transaction/TransferTransactionData.java b/src/main/java/org/unicitylabs/sdk/transaction/TransferTransactionData.java deleted file mode 100644 index 5dab8d0..0000000 --- a/src/main/java/org/unicitylabs/sdk/transaction/TransferTransactionData.java +++ /dev/null @@ -1,126 +0,0 @@ - -package org.unicitylabs.sdk.transaction; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.ArrayNode; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import org.unicitylabs.sdk.address.Address; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.hash.DataHasher; -import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.serializer.cbor.CborSerializationException; -import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.token.TokenState; -import org.unicitylabs.sdk.util.HexConverter; - -/** - * Transaction data for token state transitions - */ -public class TransferTransactionData implements TransactionData { - - private final TokenState sourceState; - private final Address recipient; - private final byte[] salt; - private final DataHash dataHash; - private final byte[] message; - private final List> nametags; - - public TransferTransactionData( - TokenState sourceState, - Address recipient, - byte[] salt, - DataHash dataHash, - byte[] message, - List> nametags) { - Objects.requireNonNull(sourceState, "SourceState cannot be null"); - Objects.requireNonNull(recipient, "Recipient cannot be null"); - Objects.requireNonNull(salt, "Salt cannot be null"); - Objects.requireNonNull(nametags, "Nametags cannot be null"); - - this.sourceState = sourceState; - this.recipient = recipient; - this.salt = Arrays.copyOf(salt, salt.length); - this.dataHash = dataHash; - this.message = message != null ? Arrays.copyOf(message, message.length) : null; - this.nametags = List.copyOf(nametags); - } - - public TokenState getSourceState() { - return this.sourceState; - } - - public Address getRecipient() { - return this.recipient; - } - - public byte[] getSalt() { - return Arrays.copyOf(this.salt, this.salt.length); - } - - public Optional getDataHash() { - return Optional.ofNullable(this.dataHash); - } - - public Optional getMessage() { - return this.message != null - ? Optional.of(Arrays.copyOf(this.message, this.message.length)) - : Optional.empty(); - } - - public List> getNametags() { - return this.nametags; - } - - public DataHash calculateHash() { - ArrayNode node = UnicityObjectMapper.CBOR.createArrayNode(); - node.addPOJO(this.sourceState.calculateHash()); - node.addPOJO(this.dataHash); - node.addPOJO(this.recipient); - node.add(this.salt); - node.add(this.message); - - try { - return new DataHasher(HashAlgorithm.SHA256).update( - UnicityObjectMapper.CBOR.writeValueAsBytes(node)).digest(); - } catch (JsonProcessingException e) { - throw new CborSerializationException(e); - } - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof TransferTransactionData)) { - return false; - } - TransferTransactionData that = (TransferTransactionData) o; - return Objects.equals(this.sourceState, that.sourceState) && Objects.equals( - this.recipient, that.recipient) && Objects.deepEquals(this.salt, that.salt) - && Objects.equals(this.dataHash, that.dataHash) && Objects.deepEquals(this.message, - that.message) && Objects.equals(this.nametags, that.nametags); - } - - @Override - public int hashCode() { - return Objects.hash(this.sourceState, this.recipient, Arrays.hashCode(this.salt), this.dataHash, - Arrays.hashCode(this.message), this.nametags); - } - - @Override - public String toString() { - return String.format( - "TransferTransactionData{" - + "sourceState=%s, " - + "recipient=%s, " - + "salt=%s, " - + "dataHash=%s, " - + "message=%s, " - + "nametags=%s" - + "}", - this.sourceState, this.recipient, HexConverter.encode(this.salt), this.dataHash, - this.message != null ? HexConverter.encode(this.message) : null, this.nametags); - } -} diff --git a/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReason.java b/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReason.java index bba6a15..6003ced 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReason.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReason.java @@ -1,32 +1,43 @@ package org.unicitylabs.sdk.transaction.split; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep; import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePathStep.Branch; +import org.unicitylabs.sdk.predicate.Predicate; import org.unicitylabs.sdk.predicate.PredicateEngineService; import org.unicitylabs.sdk.predicate.embedded.BurnPredicate; -import org.unicitylabs.sdk.predicate.Predicate; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.token.Token; import org.unicitylabs.sdk.token.fungible.CoinId; import org.unicitylabs.sdk.token.fungible.TokenCoinData; import org.unicitylabs.sdk.transaction.MintReasonType; -import org.unicitylabs.sdk.transaction.MintTransactionData; +import org.unicitylabs.sdk.transaction.MintTransaction; import org.unicitylabs.sdk.transaction.MintTransactionReason; -import org.unicitylabs.sdk.transaction.Transaction; import org.unicitylabs.sdk.verification.VerificationResult; -import java.math.BigInteger; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +/** + * Mint reason for splitting a token. + */ public class SplitMintReason implements MintTransactionReason { private final Token token; private final List proofs; - public SplitMintReason(Token token, List proofs) { + @JsonCreator + SplitMintReason( + @JsonProperty("token") Token token, + @JsonProperty("proofs") List proofs + ) { Objects.requireNonNull(token, "Token cannot be null"); Objects.requireNonNull(proofs, "Proofs cannot be null"); @@ -34,19 +45,40 @@ public SplitMintReason(Token token, List proofs) { this.proofs = List.copyOf(proofs); } + /** + * Get mint reason type. + * + * @return token split reason + */ public String getType() { return MintReasonType.TOKEN_SPLIT.name(); } + /** + * Get token which was burnt for split. + * + * @return burnt token + */ public Token getToken() { return this.token; } + /** + * Get proofs for currently minted token coins. + * + * @return split proofs + */ public List getProofs() { return List.copyOf(this.proofs); } - public VerificationResult verify(Transaction> transaction) { + /** + * Verify mint transaction against mint reason. + * + * @param transaction Genesis to verify against + * @return verification result + */ + public VerificationResult verify(MintTransaction transaction) { if (transaction.getData().getCoinData().isEmpty()) { return VerificationResult.fail("Coin data is missing."); } @@ -65,13 +97,13 @@ public VerificationResult verify(Transaction> t for (SplitMintReasonProof proof : this.proofs) { if (!proof.getAggregationPath().verify(proof.getCoinId().toBitString().toBigInteger()) - .isValid()) { + .isSuccessful()) { return VerificationResult.fail( "Aggregation path verification failed for coin: " + proof.getCoinId()); } if (!proof.getCoinTreePath() - .verify(transaction.getData().getTokenId().toBitString().toBigInteger()).isValid()) { + .verify(transaction.getData().getTokenId().toBitString().toBigInteger()).isSuccessful()) { return VerificationResult.fail( "Coin tree path verification failed for token"); } @@ -102,6 +134,40 @@ public VerificationResult verify(Transaction> t return VerificationResult.success(); } + /** + * Create split mint reason from CBOR bytes. + * + * @param bytes CBOR bytes + * @return mint reason + */ + public static SplitMintReason fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new SplitMintReason( + Token.fromCbor(data.get(1)), + CborDeserializer.readArray(data.get(2)).stream() + .map(SplitMintReasonProof::fromCbor) + .collect(Collectors.toList()) + ); + } + + /** + * Convert split mint reason to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + CborSerializer.encodeTextString(this.getType()), + this.token.toCbor(), + CborSerializer.encodeArray( + this.proofs.stream() + .map(SplitMintReasonProof::toCbor) + .toArray(byte[][]::new) + ) + ); + } + @Override public String toString() { return String.format("SplitMintReason{token=%s, proofs=%s}", this.token, this.proofs); diff --git a/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReasonProof.java b/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReasonProof.java index beb08c3..405a83d 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReasonProof.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReasonProof.java @@ -1,20 +1,31 @@ package org.unicitylabs.sdk.transaction.split; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.Objects; import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePath; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.token.fungible.CoinId; -import java.util.Objects; +/** + * Split mint reason proof for specific coin. + */ public class SplitMintReasonProof { private final CoinId coinId; private final SparseMerkleTreePath aggregationPath; private final SparseMerkleSumTreePath coinTreePath; - public SplitMintReasonProof( - CoinId coinId, - SparseMerkleTreePath aggregationPath, SparseMerkleSumTreePath coinTreePath) { + @JsonCreator + SplitMintReasonProof( + @JsonProperty("coinId") CoinId coinId, + @JsonProperty("aggregationPath") SparseMerkleTreePath aggregationPath, + @JsonProperty("coinTreePath") SparseMerkleSumTreePath coinTreePath + ) { Objects.requireNonNull(coinId, "coinId cannot be null"); Objects.requireNonNull(aggregationPath, "aggregationPath cannot be null"); Objects.requireNonNull(coinTreePath, "coinTreePath cannot be null"); @@ -24,15 +35,59 @@ public SplitMintReasonProof( this.coinTreePath = coinTreePath; } + /** + * Get coin ID associated with proof. + * + * @return coin id + */ public CoinId getCoinId() { return this.coinId; } + /** + * Get aggregation path for current coin in coin trees. + * + * @return aggregation path + */ public SparseMerkleTreePath getAggregationPath() { return this.aggregationPath; } + /** + * Get coin tree path for current coin. + * + * @return coin tree path + */ public SparseMerkleSumTreePath getCoinTreePath() { return this.coinTreePath; } + + /** + * Create split mint reason proof from CBOR bytes. + * + * @param bytes CBOR bytes + * @return split mint reason proof + */ + public static SplitMintReasonProof fromCbor(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + return new SplitMintReasonProof( + new CoinId(CborDeserializer.readByteString(data.get(0))), + SparseMerkleTreePath.fromCbor(data.get(1)), + SparseMerkleSumTreePath.fromCbor(data.get(2)) + ); + } + + /** + * Convert split mint reason proof to CBOR bytes. + * + * @return CBOR bytes + */ + public byte[] toCbor() { + return CborSerializer.encodeArray( + CborSerializer.encodeByteString(this.coinId.getBytes()), + this.aggregationPath.toCbor(), + this.coinTreePath.toCbor() + ); + } } diff --git a/src/main/java/org/unicitylabs/sdk/transaction/split/TokenSplitBuilder.java b/src/main/java/org/unicitylabs/sdk/transaction/split/TokenSplitBuilder.java index 6d6dbdc..e438e38 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/split/TokenSplitBuilder.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/split/TokenSplitBuilder.java @@ -1,5 +1,14 @@ package org.unicitylabs.sdk.transaction.split; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; import org.unicitylabs.sdk.address.Address; import org.unicitylabs.sdk.bft.RootTrustBase; import org.unicitylabs.sdk.hash.DataHash; @@ -18,28 +27,33 @@ import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenState; import org.unicitylabs.sdk.token.TokenType; -import org.unicitylabs.sdk.verification.VerificationException; import org.unicitylabs.sdk.token.fungible.CoinId; import org.unicitylabs.sdk.token.fungible.TokenCoinData; import org.unicitylabs.sdk.transaction.MintCommitment; -import org.unicitylabs.sdk.transaction.MintTransactionData; -import org.unicitylabs.sdk.transaction.Transaction; +import org.unicitylabs.sdk.transaction.MintTransaction; import org.unicitylabs.sdk.transaction.TransferCommitment; -import org.unicitylabs.sdk.transaction.TransferTransactionData; -import java.math.BigInteger; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; +import org.unicitylabs.sdk.transaction.TransferTransaction; +import org.unicitylabs.sdk.verification.VerificationException; +/** + * Token splitting builder. + */ public class TokenSplitBuilder { private final Map tokens = new HashMap<>(); + /** + * Create new token which will be created from selected token. + * + * @param id new token id + * @param type new token type + * @param data new token data + * @param coinData new token coin data + * @param recipient new token recipient address + * @param salt new token salt + * @param recipientDataHash new token recipient data hash + * @return current builder + */ public TokenSplitBuilder createToken( TokenId id, TokenType type, @@ -55,6 +69,14 @@ public TokenSplitBuilder createToken( return this; } + /** + * Split old token to new tokens. + * + * @param token token to be used for split + * @return token split object for submitting info + * @throws LeafOutOfBoundsException if building aggregation tree and coin tree fail + * @throws BranchExistsException if building aggregation tree and coin tree fail + */ public TokenSplit build(Token token) throws LeafOutOfBoundsException, BranchExistsException { Objects.requireNonNull(token, "Token cannot be null"); @@ -103,6 +125,9 @@ public TokenSplit build(Token token) throws LeafOutOfBoundsException, BranchE ); } + /** + * Token split request object. + */ public static class TokenSplit { private final Token token; @@ -122,6 +147,13 @@ private TokenSplit( this.tokens = tokens; } + /** + * Create burn commitment to burn token going through split. + * + * @param salt burn commitment salt + * @param signingService signing service used to unlock token + * @return transfer commitment for sending to unicity service + */ public TransferCommitment createBurnCommitment(byte[] salt, SigningService signingService) { return TransferCommitment.create( token, @@ -136,9 +168,17 @@ public TransferCommitment createBurnCommitment(byte[] salt, SigningService signi ); } - public List>> createSplitMintCommitments( + /** + * Create split mint commitments after burn transaction is received. + * + * @param trustBase trust base for burn transaction verification + * @param burnTransaction burn transaction + * @return list of mint commitments for sending to unicity service + * @throws VerificationException if token verification fails + */ + public List> createSplitMintCommitments( RootTrustBase trustBase, - Transaction burnTransaction + TransferTransaction burnTransaction ) throws VerificationException { Objects.requireNonNull(burnTransaction, "Burn transaction cannot be null"); @@ -149,9 +189,9 @@ public List>> createSplitMin this.token.getId(), this.token.getType(), this.aggregationRoot.getRootHash() - ), - null ), + null + ), burnTransaction, List.of() ); @@ -159,7 +199,7 @@ public List>> createSplitMin return List.copyOf( this.tokens.values().stream() .map(request -> MintCommitment.create( - new MintTransactionData<>( + new MintTransaction.Data<>( request.id, request.type, request.data, @@ -167,19 +207,19 @@ public List>> createSplitMin request.recipient, request.salt, request.recipientDataHash, - new SplitMintReason(burnedToken, + new SplitMintReason( + burnedToken, List.copyOf( request.coinData.getCoins().keySet().stream() .map(coinId -> new SplitMintReasonProof( coinId, - this.aggregationRoot.getPath( - coinId.toBitString().toBigInteger()), + this.aggregationRoot + .getPath(coinId.toBitString().toBigInteger()), this.coinRoots.get(coinId) .getPath(request.id.toBitString().toBigInteger()) ) ) - .collect( - Collectors.toList()) + .collect(Collectors.toList()) ) ) ) @@ -190,6 +230,9 @@ public List>> createSplitMin } } + /** + * New token request for generating it out of burnt token. + */ public static class TokenRequest { private final TokenId id; @@ -200,7 +243,7 @@ public static class TokenRequest { private final byte[] salt; private final DataHash recipientDataHash; - public TokenRequest( + TokenRequest( TokenId id, TokenType type, byte[] data, diff --git a/src/main/java/org/unicitylabs/sdk/util/BigIntegerConverter.java b/src/main/java/org/unicitylabs/sdk/util/BigIntegerConverter.java index a740c8c..9f3f0ca 100644 --- a/src/main/java/org/unicitylabs/sdk/util/BigIntegerConverter.java +++ b/src/main/java/org/unicitylabs/sdk/util/BigIntegerConverter.java @@ -2,14 +2,32 @@ import java.math.BigInteger; +/** + * BigInteger converter to bytes and back. + */ public class BigIntegerConverter { - private BigIntegerConverter() {} + private BigIntegerConverter() { + } + /** + * Decode bytes to BigInteger. + * + * @param data bytes + * @return BigInteger + */ public static BigInteger decode(byte[] data) { return BigIntegerConverter.decode(data, 0, data.length); } + /** + * Decode bytes to BigInteger for byte range. + * + * @param data bytes + * @param offset offset position + * @param length length + * @return BigInteger + */ public static BigInteger decode(byte[] data, int offset, int length) { if (offset < 0 || length < 0 || offset + length > data.length) { throw new Error("Index out of bounds"); @@ -22,6 +40,12 @@ public static BigInteger decode(byte[] data, int offset, int length) { return t; } + /** + * Encode BigInteger to bytes. + * + * @param value BigInteger + * @return bytes + */ public static byte[] encode(BigInteger value) { int length = 0; BigInteger t = value; diff --git a/src/main/java/org/unicitylabs/sdk/util/BitString.java b/src/main/java/org/unicitylabs/sdk/util/BitString.java index 9805998..ef9506e 100644 --- a/src/main/java/org/unicitylabs/sdk/util/BitString.java +++ b/src/main/java/org/unicitylabs/sdk/util/BitString.java @@ -1,7 +1,7 @@ package org.unicitylabs.sdk.util; -import org.unicitylabs.sdk.hash.DataHash; import java.math.BigInteger; +import org.unicitylabs.sdk.hash.DataHash; /** * Represents a bit string as a BigInteger. This class is used to ensure that leading zero bits are @@ -17,9 +17,10 @@ public class BitString { * @param data The input data to convert into a BitString. */ public BitString(byte[] data) { - // Create hex string with "01" prefix - String hexString = "01" + HexConverter.encode(data); - this.value = new BigInteger(hexString, 16); + byte[] dataWithPrefix = new byte[data.length + 1]; + dataWithPrefix[0] = 1; + System.arraycopy(data, 0, dataWithPrefix, 1, data.length); + this.value = new BigInteger(1, dataWithPrefix); } /** @@ -42,21 +43,6 @@ public BigInteger toBigInteger() { return value; } - /** - * Converts bit string to byte array. - * - * @return The byte array representation of the bit string - */ - public byte[] toBytes() { - // Convert to hex string, remove the "01" prefix we added, then convert back to bytes - String hex = value.toString(16); - if (hex.startsWith("1") && hex.length() > 2) { - // Remove the leading "1" from "10000..." - hex = hex.substring(1); - } - return HexConverter.decode(hex); - } - /** * Converts bit string to string. * diff --git a/src/main/java/org/unicitylabs/sdk/util/HexConverter.java b/src/main/java/org/unicitylabs/sdk/util/HexConverter.java index 395111a..81c00e0 100644 --- a/src/main/java/org/unicitylabs/sdk/util/HexConverter.java +++ b/src/main/java/org/unicitylabs/sdk/util/HexConverter.java @@ -8,7 +8,7 @@ public class HexConverter { private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); /** - * Convert byte array to hex + * Convert byte array to hex. * * @param data byte array * @return hex string @@ -24,7 +24,7 @@ public static String encode(byte[] data) { } /** - * Convert hex string to bytes + * Convert hex string to bytes. * * @param value hex string * @return byte array diff --git a/src/main/java/org/unicitylabs/sdk/util/InclusionProofUtils.java b/src/main/java/org/unicitylabs/sdk/util/InclusionProofUtils.java index 5c3610f..e4ce662 100644 --- a/src/main/java/org/unicitylabs/sdk/util/InclusionProofUtils.java +++ b/src/main/java/org/unicitylabs/sdk/util/InclusionProofUtils.java @@ -13,7 +13,7 @@ import org.unicitylabs.sdk.transaction.InclusionProofVerificationStatus; /** - * Utility class for working with inclusion proofs + * Utility class for working with inclusion proofs. */ public class InclusionProofUtils { @@ -23,7 +23,7 @@ public class InclusionProofUtils { private static final Duration DEFAULT_INTERVAL = Duration.ofMillis(1000); /** - * Wait for an inclusion proof to be available and verified + * Wait for an inclusion proof to be available and verified. */ public static CompletableFuture waitInclusionProof( StateTransitionClient client, @@ -34,7 +34,7 @@ public static CompletableFuture waitInclusionProof( } /** - * Wait for an inclusion proof to be available and verified with custom timeout + * Wait for an inclusion proof to be available and verified with custom timeout. */ public static CompletableFuture waitInclusionProof( StateTransitionClient client, @@ -49,7 +49,8 @@ public static CompletableFuture waitInclusionProof( long startTime = System.currentTimeMillis(); long timeoutMillis = timeout.toMillis(); - checkInclusionProof(client, trustBase, commitment, future, startTime, timeoutMillis, interval.toMillis()); + checkInclusionProof(client, trustBase, commitment, future, startTime, timeoutMillis, + interval.toMillis()); return future; } @@ -76,7 +77,8 @@ private static void checkInclusionProof( if (status == InclusionProofVerificationStatus.PATH_NOT_INCLUDED) { CompletableFuture.delayedExecutor(intervalMillis, TimeUnit.MILLISECONDS) - .execute(() -> checkInclusionProof(client, trustBase, commitment, future, startTime, timeoutMillis, + .execute(() -> checkInclusionProof(client, trustBase, commitment, future, startTime, + timeoutMillis, intervalMillis)); } else { future.completeExceptionally( diff --git a/src/main/java/org/unicitylabs/sdk/verification/CompositeVerificationRule.java b/src/main/java/org/unicitylabs/sdk/verification/CompositeVerificationRule.java index 150d6de..7ce7880 100644 --- a/src/main/java/org/unicitylabs/sdk/verification/CompositeVerificationRule.java +++ b/src/main/java/org/unicitylabs/sdk/verification/CompositeVerificationRule.java @@ -3,14 +3,37 @@ import java.util.ArrayList; import java.util.List; -public abstract class CompositeVerificationRule extends VerificationRule { - - private final VerificationRule firstRule; +/** + * A composite verification rule that chains multiple verification rules together. + * + *

This class allows you to create a sequence of verification rules where each rule can lead to + * another rule based on the result of the verification. The first rule in the chain is provided at + * construction, and subsequent rules can be determined dynamically based on the outcome of each + * verification step. + * + *

When the {@code verify} method is called, it starts with the first rule and continues to + * execute subsequent rules based on whether the previous rule was successful or not. The final + * result is a composite {@code VerificationResult} that includes the results of all executed + * rules. + * + * @param the type of context used for verification + */ +public abstract class CompositeVerificationRule + extends VerificationRule { + + private final VerificationRule firstRule; private final String message; + /** + * Constructs a {@code CompositeVerificationRule} with the specified message and the first rule in + * the chain. + * + * @param message a descriptive message for the composite rule + * @param firstRule the first verification rule to execute in the chain + */ public CompositeVerificationRule( String message, - VerificationRule firstRule + VerificationRule firstRule ) { super(message); @@ -18,14 +41,17 @@ public CompositeVerificationRule( this.message = message; } - public VerificationResult verify(CTX context) { - VerificationRule rule = this.firstRule; + @Override + public VerificationResult verify(C context) { + VerificationRule rule = this.firstRule; List results = new ArrayList<>(); while (rule != null) { VerificationResult result = rule.verify(context); results.add(result); - rule = rule.getNextRule(result.isSuccessful() ? VerificationResultCode.OK : VerificationResultCode.FAIL); + rule = rule.getNextRule(result.isSuccessful() + ? VerificationResultCode.OK + : VerificationResultCode.FAIL); } return VerificationResult.fromChildren(this.message, results); diff --git a/src/main/java/org/unicitylabs/sdk/verification/VerificationContext.java b/src/main/java/org/unicitylabs/sdk/verification/VerificationContext.java new file mode 100644 index 0000000..dedd139 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/verification/VerificationContext.java @@ -0,0 +1,5 @@ +package org.unicitylabs.sdk.verification; + +public interface VerificationContext { + +} diff --git a/src/main/java/org/unicitylabs/sdk/verification/VerificationException.java b/src/main/java/org/unicitylabs/sdk/verification/VerificationException.java index fd61e75..e8df76c 100644 --- a/src/main/java/org/unicitylabs/sdk/verification/VerificationException.java +++ b/src/main/java/org/unicitylabs/sdk/verification/VerificationException.java @@ -2,10 +2,19 @@ import java.util.Objects; +/** + * Exception thrown when a verification fails. + */ public class VerificationException extends Exception { private final VerificationResult verificationResult; + /** + * Create exception with message and verification result. + * + * @param message message + * @param verificationResult verification result + */ public VerificationException(String message, VerificationResult verificationResult) { super(message); Objects.requireNonNull(verificationResult, "verificationResult cannot be null"); @@ -13,6 +22,11 @@ public VerificationException(String message, VerificationResult verificationResu this.verificationResult = verificationResult; } + /** + * Get verification result. + * + * @return verification result + */ public VerificationResult getVerificationResult() { return verificationResult; } diff --git a/src/main/java/org/unicitylabs/sdk/verification/VerificationResult.java b/src/main/java/org/unicitylabs/sdk/verification/VerificationResult.java index a585b50..c2739f5 100644 --- a/src/main/java/org/unicitylabs/sdk/verification/VerificationResult.java +++ b/src/main/java/org/unicitylabs/sdk/verification/VerificationResult.java @@ -2,6 +2,9 @@ import java.util.List; +/** + * Verification result implementation. + */ public class VerificationResult { private final VerificationResultCode status; @@ -15,24 +18,57 @@ private VerificationResult(VerificationResultCode status, String message, this.status = status; } + /** + * Return successful verification result. + * + * @return verification result + */ public static VerificationResult success() { return new VerificationResult(VerificationResultCode.OK, "Verification successful", List.of()); } + /** + * Return successful verification result with child results. + * + * @param results child results + * @return verification result + */ public static VerificationResult success(List results) { return new VerificationResult(VerificationResultCode.OK, "Verification successful", results); } + /** + * Return failed verification result. + * + * @param error error message + * @return verification result + */ public static VerificationResult fail(String error) { return new VerificationResult(VerificationResultCode.FAIL, error, List.of()); } + /** + * Return failed verification result with child results. + * + * @param error error message + * @param results child results + * @return verification result + */ public static VerificationResult fail(String error, List results) { return new VerificationResult(VerificationResultCode.FAIL, error, results); } - public static VerificationResult fromChildren(String message, - List children) { + /** + * Create verification result from child results, all has to succeed. + * + * @param message message for the verification result + * @param children child results + * @return verification result + */ + public static VerificationResult fromChildren( + String message, + List children + ) { return new VerificationResult( children.stream().allMatch(VerificationResult::isSuccessful) ? VerificationResultCode.OK @@ -42,6 +78,11 @@ public static VerificationResult fromChildren(String message, ); } + /** + * Is verification successful. + * + * @return success if verification status is ok + */ public boolean isSuccessful() { return this.status == VerificationResultCode.OK; } diff --git a/src/main/java/org/unicitylabs/sdk/verification/VerificationResultCode.java b/src/main/java/org/unicitylabs/sdk/verification/VerificationResultCode.java index 4952dc5..1d97767 100644 --- a/src/main/java/org/unicitylabs/sdk/verification/VerificationResultCode.java +++ b/src/main/java/org/unicitylabs/sdk/verification/VerificationResultCode.java @@ -1,5 +1,8 @@ package org.unicitylabs.sdk.verification; +/** + * Result code for verification. + */ public enum VerificationResultCode { OK, FAIL diff --git a/src/main/java/org/unicitylabs/sdk/verification/VerificationRule.java b/src/main/java/org/unicitylabs/sdk/verification/VerificationRule.java index 9410eee..13681b4 100644 --- a/src/main/java/org/unicitylabs/sdk/verification/VerificationRule.java +++ b/src/main/java/org/unicitylabs/sdk/verification/VerificationRule.java @@ -2,20 +2,37 @@ import java.util.Objects; -public abstract class VerificationRule { +/** + * Verification rule base class. + * + * @param verification context + */ +public abstract class VerificationRule { private final String message; - private final VerificationRule onSuccessRule; - private final VerificationRule onFailureRule; + private final VerificationRule onSuccessRule; + private final VerificationRule onFailureRule; + /** + * Create the rule without any subsequent rules. + * + * @param message rule message + */ protected VerificationRule(String message) { this(message, null, null); } + /** + * Create the rule with subsequent rules for success and failure. + * + * @param message rule message + * @param onSuccessRule rule to execute on success + * @param onFailureRule rule to execute on failure + */ protected VerificationRule( String message, - VerificationRule onSuccessRule, - VerificationRule onFailureRule + VerificationRule onSuccessRule, + VerificationRule onFailureRule ) { Objects.requireNonNull(message, "Message cannot be null"); @@ -24,11 +41,22 @@ protected VerificationRule( this.onFailureRule = onFailureRule; } + /** + * Get verification rule message. + * + * @return message + */ public String getMessage() { return this.message; } - public VerificationRule getNextRule(VerificationResultCode resultCode) { + /** + * Get next verification rule based on verification result. + * + * @param resultCode result of current verification rule + * @return next rule or null if no rule exists for given result + */ + public VerificationRule getNextRule(VerificationResultCode resultCode) { switch (resultCode) { case OK: return this.onSuccessRule; @@ -39,5 +67,11 @@ public VerificationRule getNextRule(VerificationResultCode resultCode) { } } - public abstract VerificationResult verify(CTX context); + /** + * Verify context against current rule. + * + * @param context verification context + * @return verification result + */ + public abstract VerificationResult verify(C context); } \ No newline at end of file diff --git a/src/test/java/org/unicitylabs/sdk/TestAggregatorClient.java b/src/test/java/org/unicitylabs/sdk/TestAggregatorClient.java index 7c29d8a..6dfd192 100644 --- a/src/test/java/org/unicitylabs/sdk/TestAggregatorClient.java +++ b/src/test/java/org/unicitylabs/sdk/TestAggregatorClient.java @@ -7,21 +7,21 @@ import java.util.Objects; import java.util.concurrent.CompletableFuture; import org.unicitylabs.sdk.api.Authenticator; -import org.unicitylabs.sdk.api.IAggregatorClient; +import org.unicitylabs.sdk.api.AggregatorClient; import org.unicitylabs.sdk.api.InclusionProofResponse; import org.unicitylabs.sdk.api.LeafValue; import org.unicitylabs.sdk.api.RequestId; import org.unicitylabs.sdk.api.SubmitCommitmentResponse; import org.unicitylabs.sdk.api.SubmitCommitmentStatus; +import org.unicitylabs.sdk.bft.UnicityCertificateUtils; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.mtree.plain.SparseMerkleTree; import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreeRootNode; import org.unicitylabs.sdk.signing.SigningService; -import org.unicitylabs.sdk.transaction.InclusionProof; -import org.unicitylabs.sdk.utils.UnicityCertificateUtils; +import org.unicitylabs.sdk.transaction.InclusionProofFixture; -public class TestAggregatorClient implements IAggregatorClient { +public class TestAggregatorClient implements AggregatorClient { private final SparseMerkleTree tree = new SparseMerkleTree(HashAlgorithm.SHA256); private final HashMap> requests = new HashMap<>(); @@ -59,7 +59,7 @@ public CompletableFuture getInclusionProof(RequestId req SparseMerkleTreeRootNode root = tree.calculateRoot(); return CompletableFuture.completedFuture( new InclusionProofResponse( - new InclusionProof( + InclusionProofFixture.create( root.getPath(requestId.toBitString().toBigInteger()), entry.getKey(), entry.getValue(), diff --git a/src/test/java/org/unicitylabs/sdk/TestApiKeyIntegration.java b/src/test/java/org/unicitylabs/sdk/TestApiKeyIntegration.java index 02f23bc..e254820 100644 --- a/src/test/java/org/unicitylabs/sdk/TestApiKeyIntegration.java +++ b/src/test/java/org/unicitylabs/sdk/TestApiKeyIntegration.java @@ -6,12 +6,13 @@ import org.junit.jupiter.api.Test; import org.unicitylabs.sdk.api.AggregatorClient; import org.unicitylabs.sdk.api.Authenticator; +import org.unicitylabs.sdk.api.JsonRpcAggregatorClient; import org.unicitylabs.sdk.api.RequestId; import org.unicitylabs.sdk.api.SubmitCommitmentResponse; import org.unicitylabs.sdk.api.SubmitCommitmentStatus; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.jsonrpc.JsonRpcNetworkError; +import org.unicitylabs.sdk.jsonrpc.JsonRpcNetworkException; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.util.HexConverter; @@ -39,8 +40,9 @@ void setUp() throws Exception { mockServer.setExpectedApiKey(TEST_API_KEY); mockServer.start(); - clientWithApiKey = new AggregatorClient(mockServer.getUrl(), TEST_API_KEY); - clientWithoutApiKey = new AggregatorClient(mockServer.getUrl()); + clientWithApiKey = new JsonRpcAggregatorClient( + mockServer.getUrl(), TEST_API_KEY); + clientWithoutApiKey = new JsonRpcAggregatorClient(mockServer.getUrl()); SigningService signingService = new SigningService( HexConverter.decode("0000000000000000000000000000000000000000000000000000000000000001")); @@ -79,7 +81,7 @@ public void testSubmitCommitmentWithoutApiKeyThrowsUnauthorized() throws Excepti fail("Expected UnauthorizedException to be thrown"); } catch (Exception e) { assertInstanceOf(ExecutionException.class, e); - assertInstanceOf(JsonRpcNetworkError.class, e.getCause()); + assertInstanceOf(JsonRpcNetworkException.class, e.getCause()); assertEquals("Network error [401] occurred: Unauthorized", e.getCause().getMessage()); } @@ -99,7 +101,7 @@ public void testSubmitCommitmentWithWrongApiKeyThrowsUnauthorized() throws Excep fail("Expected UnauthorizedException to be thrown"); } catch (Exception e) { assertInstanceOf(ExecutionException.class, e); - assertInstanceOf(JsonRpcNetworkError.class, e.getCause()); + assertInstanceOf(JsonRpcNetworkException.class, e.getCause()); assertEquals("Network error [401] occurred: Unauthorized", e.getCause().getMessage()); } @@ -119,7 +121,7 @@ public void testRateLimitExceeded() throws Exception { fail("Expected RateLimitExceededException to be thrown"); } catch (Exception e) { assertInstanceOf(ExecutionException.class, e); - assertInstanceOf(JsonRpcNetworkError.class, e.getCause()); + assertInstanceOf(JsonRpcNetworkException.class, e.getCause()); assertTrue(e.getCause().getMessage().contains("Network error [429] occurred: Too Many Requests"), e.getCause().getMessage()); } } diff --git a/src/test/java/org/unicitylabs/sdk/api/AuthenticatorTest.java b/src/test/java/org/unicitylabs/sdk/api/AuthenticatorTest.java index 2ce351d..2aaed3a 100644 --- a/src/test/java/org/unicitylabs/sdk/api/AuthenticatorTest.java +++ b/src/test/java/org/unicitylabs/sdk/api/AuthenticatorTest.java @@ -1,14 +1,11 @@ package org.unicitylabs.sdk.api; import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.signing.Signature; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.util.HexConverter; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; public class AuthenticatorTest { @@ -16,20 +13,16 @@ public class AuthenticatorTest { public void testJsonSerialization() throws JsonProcessingException { SigningService signingService = new SigningService( HexConverter.decode("0000000000000000000000000000000000000000000000000000000000000001")); - Authenticator authenticator = new Authenticator( - signingService.getAlgorithm(), - signingService.getPublicKey(), - Signature.decode(HexConverter.decode( - "A0B37F8FBA683CC68F6574CD43B39F0343A50008BF6CCEA9D13231D9E7E2E1E411EDC8D307254296264AEBFC3DC76CD8B668373A072FD64665B50000E9FCCE5201")), - new DataHash(HashAlgorithm.SHA256, new byte[32]) + Authenticator authenticator = Authenticator.create( + signingService, + DataHash.fromImprint(new byte[34]), + DataHash.fromImprint(new byte[34]) ); Assertions.assertEquals( "8469736563703235366b3158210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817985841a0b37f8fba683cc68f6574cd43b39f0343a50008bf6ccea9d13231d9e7e2e1e411edc8d307254296264aebfc3dc76cd8b668373a072fd64665b50000e9fcce5201582200000000000000000000000000000000000000000000000000000000000000000000", - HexConverter.encode(UnicityObjectMapper.CBOR.writeValueAsBytes(authenticator))); + HexConverter.encode(authenticator.toCbor())); - UnicityObjectMapper.JSON.readValue( - "{\"algorithm\":\"secp256k1\",\"publicKey\":\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"signature\":\"a0b37f8fba683cc68f6574cd43b39f0343a50008bf6ccea9d13231d9e7e2e1e411edc8d307254296264aebfc3dc76cd8b668373a072fd64665b50000e9fcce5201\",\"stateHash\":\"00000000000000000000000000000000000000000000000000000000000000000000\"}", - Authenticator.class); + Authenticator.fromJson("{\"algorithm\":\"secp256k1\",\"publicKey\":\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"signature\":\"a0b37f8fba683cc68f6574cd43b39f0343a50008bf6ccea9d13231d9e7e2e1e411edc8d307254296264aebfc3dc76cd8b668373a072fd64665b50000e9fcce5201\",\"stateHash\":\"00000000000000000000000000000000000000000000000000000000000000000000\"}"); } } diff --git a/src/test/java/org/unicitylabs/sdk/api/RequestIdTest.java b/src/test/java/org/unicitylabs/sdk/api/RequestIdTest.java index f3e73af..993484d 100644 --- a/src/test/java/org/unicitylabs/sdk/api/RequestIdTest.java +++ b/src/test/java/org/unicitylabs/sdk/api/RequestIdTest.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import java.math.BigInteger; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -23,14 +22,14 @@ public void shouldResolveToBigInteger() { @Test public void testJsonSerialization() throws JsonProcessingException { - RequestId requestId = RequestId.create(new byte[5], - new DataHash(HashAlgorithm.SHA256, new byte[32])); + RequestId requestId = RequestId.create( + new byte[5], + new DataHash(HashAlgorithm.SHA256, new byte[32]) + ); - Assertions.assertEquals(requestId, - UnicityObjectMapper.JSON.readValue( - UnicityObjectMapper.JSON.writeValueAsString(requestId), - RequestId.class - ) + Assertions.assertEquals( + requestId, + RequestId.fromJson(requestId.toJson()) ); } } diff --git a/src/test/java/org/unicitylabs/sdk/bft/RootTrustBaseTest.java b/src/test/java/org/unicitylabs/sdk/bft/RootTrustBaseTest.java index 7e13681..6c856f4 100644 --- a/src/test/java/org/unicitylabs/sdk/bft/RootTrustBaseTest.java +++ b/src/test/java/org/unicitylabs/sdk/bft/RootTrustBaseTest.java @@ -3,15 +3,14 @@ import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; public class RootTrustBaseTest { @Test public void testRootTrustBaseDeserializationFromJson() throws IOException { - RootTrustBase trustBase = UnicityObjectMapper.JSON.readValue( - "{\"version\":1,\"networkId\":3,\"epoch\":1,\"epochStartRound\":1,\"rootNodes\":[{\"nodeId\":\"16Uiu2HAm3PaA9z8jonZzfvuT1WJgTxCpbFkV4Wq4PSSBk7VctkmG\",\"sigKey\":\"0x03982564bf661da9c048397114fab9dcfbfedb0ad7c1b1c83e13c0f9fa633f7aa6\",\"stake\":1},{\"nodeId\":\"16Uiu2HAm8918Ds2nPiVLXg55kypyoXoiweokpQxtnguZjgxz3pNE\",\"sigKey\":\"0x039a2f7f41c5583d339f31490757152b947ccb19944634a40a16a762a32a4855d4\",\"stake\":1},{\"nodeId\":\"16Uiu2HAmEEEGyvYZno7hm2Gs8FwfPejWdpvWC3HLivQD5hXFbNUh\",\"sigKey\":\"0x038cabc84fa86076277879554c277f9a0a19955fa4c3b37871fca81d5f709777f1\",\"stake\":1},{\"nodeId\":\"16Uiu2HAmNwgru7QSsVRacGqXdtfeaf1oqtznvXA6rSzKU1822kuW\",\"sigKey\":\"0x03044c0309fd0a713440da958f8c510a40a4347aa82622481655d162c227d771e3\",\"stake\":1}],\"quorumThreshold\":3,\"stateHash\":\"\",\"changeRecordHash\":\"\",\"previousEntryHash\":\"\",\"signatures\":{\"16Uiu2HAm3PaA9z8jonZzfvuT1WJgTxCpbFkV4Wq4PSSBk7VctkmG\":\"0xfe672d56ddd60e4b028b52999b4e43bcbdac9413d9e8da6f969d46c249da8f492cd719017510af8b199b94c7605b79707da56950a4888320f8cf7e07329e92da01\",\"16Uiu2HAm8918Ds2nPiVLXg55kypyoXoiweokpQxtnguZjgxz3pNE\":\"0x8d1b178f6617a6aff9e9d4a71febac6837bd2a5088f3e3b81c766065e6c7a7ad718d1e0a1c7f7e12954514e663b888337cbaa6e7c8bd5e721f4ae5520ca6f09e00\",\"16Uiu2HAmEEEGyvYZno7hm2Gs8FwfPejWdpvWC3HLivQD5hXFbNUh\":\"0x28ef1e0279fb2962149011177c173aabb7e1fad102f07c898b9de4fe71b424390dd0cbc59f75453a7573c4853218eab800431e42fd0d4a6000ef73d50170d03101\",\"16Uiu2HAmNwgru7QSsVRacGqXdtfeaf1oqtznvXA6rSzKU1822kuW\":\"0xf563d04beb3eb5bd5967cb53e6cc1d1b331cd37c03d7a34ba7f11bc0d2c4994818168e1f5caf88956b34384dd3d3685c432d7a487b5c0bee2da012fd70891bab01\"}}", - RootTrustBase.class); + RootTrustBase trustBase = RootTrustBase.fromJson( + "{\"version\":1,\"networkId\":3,\"epoch\":1,\"epochStartRound\":1,\"rootNodes\":[{\"nodeId\":\"16Uiu2HAm3PaA9z8jonZzfvuT1WJgTxCpbFkV4Wq4PSSBk7VctkmG\",\"sigKey\":\"0x03982564bf661da9c048397114fab9dcfbfedb0ad7c1b1c83e13c0f9fa633f7aa6\",\"stake\":1},{\"nodeId\":\"16Uiu2HAm8918Ds2nPiVLXg55kypyoXoiweokpQxtnguZjgxz3pNE\",\"sigKey\":\"0x039a2f7f41c5583d339f31490757152b947ccb19944634a40a16a762a32a4855d4\",\"stake\":1},{\"nodeId\":\"16Uiu2HAmEEEGyvYZno7hm2Gs8FwfPejWdpvWC3HLivQD5hXFbNUh\",\"sigKey\":\"0x038cabc84fa86076277879554c277f9a0a19955fa4c3b37871fca81d5f709777f1\",\"stake\":1},{\"nodeId\":\"16Uiu2HAmNwgru7QSsVRacGqXdtfeaf1oqtznvXA6rSzKU1822kuW\",\"sigKey\":\"0x03044c0309fd0a713440da958f8c510a40a4347aa82622481655d162c227d771e3\",\"stake\":1}],\"quorumThreshold\":3,\"stateHash\":\"\",\"changeRecordHash\":\"\",\"previousEntryHash\":\"\",\"signatures\":{\"16Uiu2HAm3PaA9z8jonZzfvuT1WJgTxCpbFkV4Wq4PSSBk7VctkmG\":\"0xfe672d56ddd60e4b028b52999b4e43bcbdac9413d9e8da6f969d46c249da8f492cd719017510af8b199b94c7605b79707da56950a4888320f8cf7e07329e92da01\",\"16Uiu2HAm8918Ds2nPiVLXg55kypyoXoiweokpQxtnguZjgxz3pNE\":\"0x8d1b178f6617a6aff9e9d4a71febac6837bd2a5088f3e3b81c766065e6c7a7ad718d1e0a1c7f7e12954514e663b888337cbaa6e7c8bd5e721f4ae5520ca6f09e00\",\"16Uiu2HAmEEEGyvYZno7hm2Gs8FwfPejWdpvWC3HLivQD5hXFbNUh\":\"0x28ef1e0279fb2962149011177c173aabb7e1fad102f07c898b9de4fe71b424390dd0cbc59f75453a7573c4853218eab800431e42fd0d4a6000ef73d50170d03101\",\"16Uiu2HAmNwgru7QSsVRacGqXdtfeaf1oqtznvXA6rSzKU1822kuW\":\"0xf563d04beb3eb5bd5967cb53e6cc1d1b331cd37c03d7a34ba7f11bc0d2c4994818168e1f5caf88956b34384dd3d3685c432d7a487b5c0bee2da012fd70891bab01\"}}" + ); Assertions.assertEquals(1, trustBase.getVersion()); Assertions.assertEquals(3, trustBase.getNetworkId()); diff --git a/src/test/java/org/unicitylabs/sdk/utils/RootTrustBaseUtils.java b/src/test/java/org/unicitylabs/sdk/bft/RootTrustBaseUtils.java similarity index 85% rename from src/test/java/org/unicitylabs/sdk/utils/RootTrustBaseUtils.java rename to src/test/java/org/unicitylabs/sdk/bft/RootTrustBaseUtils.java index 4636271..ddc75ad 100644 --- a/src/test/java/org/unicitylabs/sdk/utils/RootTrustBaseUtils.java +++ b/src/test/java/org/unicitylabs/sdk/bft/RootTrustBaseUtils.java @@ -1,8 +1,7 @@ -package org.unicitylabs.sdk.utils; +package org.unicitylabs.sdk.bft; import java.util.Map; import java.util.Set; -import org.unicitylabs.sdk.bft.RootTrustBase; public class RootTrustBaseUtils { public static RootTrustBase generateRootTrustBase(byte[] publicKey) { diff --git a/src/test/java/org/unicitylabs/sdk/bft/UnicityCertificateTest.java b/src/test/java/org/unicitylabs/sdk/bft/UnicityCertificateTest.java index 34158d5..3f2e5a1 100644 --- a/src/test/java/org/unicitylabs/sdk/bft/UnicityCertificateTest.java +++ b/src/test/java/org/unicitylabs/sdk/bft/UnicityCertificateTest.java @@ -3,7 +3,6 @@ import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import org.unicitylabs.sdk.util.HexConverter; public class UnicityCertificateTest { @@ -12,14 +11,11 @@ public class UnicityCertificateTest { public void testUnicityCertificateDeserializationFromCbor() throws IOException { byte[] data = HexConverter.decode( "d903ef8701d903f08a0118f70058220000ea2fc549e86b1f8666bd7a6d5dd0721b446856ad80c67ea958c4045dfb0b627c58220000ea2fc549e86b1f8666bd7a6d5dd0721b446856ad80c67ea958c4045dfb0b627c401a68d27af6f600f6582024bf5304778618efe3303e1ac36a5c23b7d9d64bbd2c2ae92ea1488ad59e9cfa58206a362e77353752942e9c8101511861ee7fac83ba0873ee88f34416535ae17c5c82418080d903f68301078182055820b206f60d312bf4422815c861e9330ddc3a29b0c744864d7bea41f94d1d34c669d903e988010319073b001a68d27af958204b3f7f036452271a1245ca79f6c2e35adc0b192e6eeb001dff35d70d1be3b2d55820380451b471673eb68cf750ceedb09755c08e3b0b7dea32d0101465970ca3a661a4783531365569753248416d33506141397a386a6f6e5a7a6676755431574a675478437062466b5634577134505353426b375663746b6d475841c5103160b79e7691cec53448949bbd93a870a38c89ad4c929aa1c8e77f2a1d9053d3f8f04e96bcb38ba234bf3dee52baad4b1df94be0b8a5e569918fb94e18ec00783531365569753248416d383931384473326e5069564c586735356b7970796f586f6977656f6b705178746e67755a6a67787a33704e455841fa3736ebdc729b31d6e3c964787dfcefcbd39286a1619665eac1313e6d35049b4385156f652ad9ac87a769d3bf7b8cc819bb5a6f838e595d2e2092df2592849400783531365569753248416d454545477976595a6e6f37686d3247733846776650656a57647076574333484c6976514435685846624e55685841e49ea9e0ca1bb21a37a203fef7bc64efc660ff208f4e572ad3d58ae1d5f1e86e7af1c030c66b3325f99886b1a1d0836487c16112c43c07248b490121d473287701783531365569753248416d4e776772753751537356526163477158647466656166316f71747a6e7658413672537a4b55313832326b75575841ecb36e24df58876277643117344d5329fc1bb22b51e9e6835ac587c894db06a11110e966fd97acd91659c778494d9b4dcdc1bd22ffb47a90e3bec38db8a7310f01"); - UnicityCertificate unicityCertificate = UnicityObjectMapper.CBOR.readValue(data, - UnicityCertificate.class); + UnicityCertificate unicityCertificate = UnicityCertificate.fromCbor(data); Assertions.assertEquals( "d903ef8701d903f08a0118f70058220000ea2fc549e86b1f8666bd7a6d5dd0721b446856ad80c67ea958c4045dfb0b627c58220000ea2fc549e86b1f8666bd7a6d5dd0721b446856ad80c67ea958c4045dfb0b627c401a68d27af6f600f6582024bf5304778618efe3303e1ac36a5c23b7d9d64bbd2c2ae92ea1488ad59e9cfa58206a362e77353752942e9c8101511861ee7fac83ba0873ee88f34416535ae17c5c82418080d903f68301078182055820b206f60d312bf4422815c861e9330ddc3a29b0c744864d7bea41f94d1d34c669d903e988010319073b001a68d27af958204b3f7f036452271a1245ca79f6c2e35adc0b192e6eeb001dff35d70d1be3b2d55820380451b471673eb68cf750ceedb09755c08e3b0b7dea32d0101465970ca3a661a4783531365569753248416d33506141397a386a6f6e5a7a6676755431574a675478437062466b5634577134505353426b375663746b6d475841c5103160b79e7691cec53448949bbd93a870a38c89ad4c929aa1c8e77f2a1d9053d3f8f04e96bcb38ba234bf3dee52baad4b1df94be0b8a5e569918fb94e18ec00783531365569753248416d383931384473326e5069564c586735356b7970796f586f6977656f6b705178746e67755a6a67787a33704e455841fa3736ebdc729b31d6e3c964787dfcefcbd39286a1619665eac1313e6d35049b4385156f652ad9ac87a769d3bf7b8cc819bb5a6f838e595d2e2092df2592849400783531365569753248416d454545477976595a6e6f37686d3247733846776650656a57647076574333484c6976514435685846624e55685841e49ea9e0ca1bb21a37a203fef7bc64efc660ff208f4e572ad3d58ae1d5f1e86e7af1c030c66b3325f99886b1a1d0836487c16112c43c07248b490121d473287701783531365569753248416d4e776772753751537356526163477158647466656166316f71747a6e7658413672537a4b55313832326b75575841ecb36e24df58876277643117344d5329fc1bb22b51e9e6835ac587c894db06a11110e966fd97acd91659c778494d9b4dcdc1bd22ffb47a90e3bec38db8a7310f01", - HexConverter.encode( - UnicityObjectMapper.CBOR.writeValueAsBytes(unicityCertificate) - ) + HexConverter.encode(unicityCertificate.toCbor()) ); } diff --git a/src/test/java/org/unicitylabs/sdk/bft/UnicityCertificateUtils.java b/src/test/java/org/unicitylabs/sdk/bft/UnicityCertificateUtils.java new file mode 100644 index 0000000..5d8fe61 --- /dev/null +++ b/src/test/java/org/unicitylabs/sdk/bft/UnicityCertificateUtils.java @@ -0,0 +1,87 @@ +package org.unicitylabs.sdk.bft; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.List; +import java.util.Map; +import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.hash.DataHasher; +import org.unicitylabs.sdk.hash.HashAlgorithm; +import org.unicitylabs.sdk.serializer.cbor.CborSerializer; +import org.unicitylabs.sdk.signing.SigningService; + +public class UnicityCertificateUtils { + + public static UnicityCertificate generateCertificate( + SigningService signingService, + DataHash rootHash + ) { + InputRecord inputRecord = new InputRecord(0, 0, 0, null, rootHash.getImprint(), new byte[10], + 0, + new byte[10], 0, new byte[10]); + UnicityTreeCertificate unicityTreeCertificate = new UnicityTreeCertificate(0, 0, List.of()); + byte[] technicalRecordHash = new byte[32]; + byte[] shardConfigurationHash = new byte[32]; + ShardTreeCertificate shardTreeCertificate = new ShardTreeCertificate( + new byte[32], List.of() + ); + + DataHash shardTreeCertificateRootHash = UnicityCertificate.calculateShardTreeCertificateRootHash( + inputRecord, + technicalRecordHash, + shardConfigurationHash, + shardTreeCertificate + ); + + byte[] key = ByteBuffer.allocate(4) + .order(ByteOrder.BIG_ENDIAN) + .putInt(unicityTreeCertificate.getPartitionIdentifier()) + .array(); + + DataHash unicitySealHash = new DataHasher(HashAlgorithm.SHA256) + .update(CborSerializer.encodeByteString(new byte[]{(byte) 0x01})) // LEAF + .update(CborSerializer.encodeByteString(key)) + .update( + CborSerializer.encodeByteString( + new DataHasher(HashAlgorithm.SHA256) + .update( + CborSerializer.encodeByteString( + shardTreeCertificateRootHash.getData() + ) + ) + .digest() + .getData() + ) + ) + .digest(); + + UnicitySeal seal = new UnicitySeal( + 0, + (short) 0, + 0L, + 0L, + 0L, + null, + unicitySealHash.getData(), + null + ); + + return new UnicityCertificate( + 0, + new InputRecord(0, 0, 0, null, rootHash.getImprint(), new byte[10], 0, + new byte[10], 0, new byte[10]), + technicalRecordHash, + shardConfigurationHash, + shardTreeCertificate, + new UnicityTreeCertificate(0, 0, List.of()), + seal.withSignatures( + Map.of( + "NODE", + signingService.sign( + new DataHasher(HashAlgorithm.SHA256).update(seal.toCbor()).digest() + ).encode() + ) + ) + ); + } +} diff --git a/src/test/java/org/unicitylabs/sdk/common/BaseEscrowSwapTest.java b/src/test/java/org/unicitylabs/sdk/common/BaseEscrowSwapTest.java index 4441ed6..62fd818 100644 --- a/src/test/java/org/unicitylabs/sdk/common/BaseEscrowSwapTest.java +++ b/src/test/java/org/unicitylabs/sdk/common/BaseEscrowSwapTest.java @@ -15,15 +15,13 @@ import org.unicitylabs.sdk.predicate.embedded.MaskedPredicate; import org.unicitylabs.sdk.predicate.embedded.UnmaskedPredicate; import org.unicitylabs.sdk.predicate.embedded.UnmaskedPredicateReference; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.token.Token; import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenState; import org.unicitylabs.sdk.token.TokenType; -import org.unicitylabs.sdk.transaction.Transaction; import org.unicitylabs.sdk.transaction.TransferCommitment; -import org.unicitylabs.sdk.transaction.TransferTransactionData; +import org.unicitylabs.sdk.transaction.TransferTransaction; import org.unicitylabs.sdk.util.HexConverter; import org.unicitylabs.sdk.util.InclusionProofUtils; import org.unicitylabs.sdk.utils.TokenUtils; @@ -69,16 +67,14 @@ private String[] transferToken(Token token, SigningService signingService, St } return new String[]{ - UnicityObjectMapper.JSON.writeValueAsString(token), - UnicityObjectMapper.JSON.writeValueAsString( - commitment.toTransaction( - InclusionProofUtils.waitInclusionProof( - this.client, - this.trustBase, - commitment - ).get() - ) - ) + token.toJson(), + commitment.toTransaction( + InclusionProofUtils.waitInclusionProof( + this.client, + this.trustBase, + commitment + ).get() + ).toJson() }; } @@ -99,11 +95,8 @@ private Token mintToken(byte[] secret) throws Exception { private Token receiveToken(String[] tokenInfo, SigningService signingService, Token nametagToken) throws Exception { - Token token = UnicityObjectMapper.JSON.readValue(tokenInfo[0], Token.class); - Transaction transaction = UnicityObjectMapper.JSON.readValue( - tokenInfo[1], - UnicityObjectMapper.JSON.getTypeFactory() - .constructParametricType(Transaction.class, TransferTransactionData.class)); + Token token = Token.fromJson(tokenInfo[0]); + TransferTransaction transaction = TransferTransaction.fromJson(tokenInfo[1]); TokenState state = new TokenState( UnmaskedPredicate.create( diff --git a/src/test/java/org/unicitylabs/sdk/common/CommonTestFlow.java b/src/test/java/org/unicitylabs/sdk/common/CommonTestFlow.java index 52de7cd..ba760a0 100644 --- a/src/test/java/org/unicitylabs/sdk/common/CommonTestFlow.java +++ b/src/test/java/org/unicitylabs/sdk/common/CommonTestFlow.java @@ -35,10 +35,9 @@ import org.unicitylabs.sdk.token.fungible.TokenCoinData; import org.unicitylabs.sdk.transaction.InclusionProof; import org.unicitylabs.sdk.transaction.MintCommitment; -import org.unicitylabs.sdk.transaction.MintTransactionData; -import org.unicitylabs.sdk.transaction.Transaction; +import org.unicitylabs.sdk.transaction.MintTransaction; import org.unicitylabs.sdk.transaction.TransferCommitment; -import org.unicitylabs.sdk.transaction.TransferTransactionData; +import org.unicitylabs.sdk.transaction.TransferTransaction; import org.unicitylabs.sdk.transaction.split.SplitMintReason; import org.unicitylabs.sdk.transaction.split.TokenSplitBuilder; import org.unicitylabs.sdk.transaction.split.TokenSplitBuilder.TokenSplit; @@ -106,7 +105,7 @@ public void testTransferFlow() throws Exception { ).get(); // Create transfer transaction - Transaction aliceToBobTransferTransaction = aliceToBobTransferCommitment.toTransaction( + TransferTransaction aliceToBobTransferTransaction = aliceToBobTransferCommitment.toTransaction( aliceToBobTransferInclusionProof ); @@ -183,7 +182,7 @@ public void testTransferFlow() throws Exception { this.trustBase, bobToCarolTransferCommitment ).get(); - Transaction bobToCarolTransaction = bobToCarolTransferCommitment.toTransaction( + TransferTransaction bobToCarolTransaction = bobToCarolTransferCommitment.toTransaction( bobToCarolInclusionProof ); @@ -230,7 +229,7 @@ public void testTransferFlow() throws Exception { carolToBobTransferCommitment ).get(); - Transaction carolToBobTransaction = carolToBobTransferCommitment.toTransaction( + TransferTransaction carolToBobTransaction = carolToBobTransferCommitment.toTransaction( carolToBobInclusionProof ); @@ -302,7 +301,7 @@ public void testTransferFlow() throws Exception { throw new Exception("Failed to submit burn commitment"); } - List>> splitCommitments = split.createSplitMintCommitments( + List> splitCommitments = split.createSplitMintCommitments( this.trustBase, burnCommitment.toTransaction( InclusionProofUtils.waitInclusionProof( @@ -313,8 +312,8 @@ public void testTransferFlow() throws Exception { ) ); - List>> splitTransactions = new ArrayList<>(); - for (MintCommitment> commitment : splitCommitments) { + List> splitTransactions = new ArrayList<>(); + for (MintCommitment commitment : splitCommitments) { if (client.submitCommitment(commitment).get().getStatus() != SubmitCommitmentStatus.SUCCESS) { throw new Exception("Failed to submit split mint commitment"); } diff --git a/src/test/java/org/unicitylabs/sdk/common/split/BaseTokenSplitTest.java b/src/test/java/org/unicitylabs/sdk/common/split/BaseTokenSplitTest.java index 937de54..fa0d29d 100644 --- a/src/test/java/org/unicitylabs/sdk/common/split/BaseTokenSplitTest.java +++ b/src/test/java/org/unicitylabs/sdk/common/split/BaseTokenSplitTest.java @@ -26,7 +26,6 @@ import org.unicitylabs.sdk.token.fungible.CoinId; import org.unicitylabs.sdk.token.fungible.TokenCoinData; import org.unicitylabs.sdk.transaction.MintCommitment; -import org.unicitylabs.sdk.transaction.MintTransactionData; import org.unicitylabs.sdk.transaction.TransferCommitment; import org.unicitylabs.sdk.transaction.split.SplitMintReason; import org.unicitylabs.sdk.transaction.split.TokenSplitBuilder; @@ -125,7 +124,7 @@ void testTokenSplitFullAmounts() throws Exception { burnCommitmentResponse.getStatus())); } - List>> mintCommitments = split.createSplitMintCommitments( + List> mintCommitments = split.createSplitMintCommitments( this.trustBase, burnCommitment.toTransaction( InclusionProofUtils.waitInclusionProof( @@ -136,7 +135,7 @@ void testTokenSplitFullAmounts() throws Exception { ) ); - for (MintCommitment> commitment : mintCommitments) { + for (MintCommitment commitment : mintCommitments) { SubmitCommitmentResponse response = this.client .submitCommitment(commitment) .get(); @@ -157,7 +156,7 @@ void testTokenSplitFullAmounts() throws Exception { null ); - Token> splitToken = Token.create( + Token splitToken = Token.create( this.trustBase, state, commitment.toTransaction( diff --git a/src/test/java/org/unicitylabs/sdk/e2e/BasicE2ETest.java b/src/test/java/org/unicitylabs/sdk/e2e/BasicE2ETest.java index 475c21e..99b000d 100644 --- a/src/test/java/org/unicitylabs/sdk/e2e/BasicE2ETest.java +++ b/src/test/java/org/unicitylabs/sdk/e2e/BasicE2ETest.java @@ -1,6 +1,6 @@ package org.unicitylabs.sdk.e2e; -import org.unicitylabs.sdk.api.AggregatorClient; +import org.unicitylabs.sdk.api.JsonRpcAggregatorClient; import org.unicitylabs.sdk.api.Authenticator; import org.unicitylabs.sdk.api.RequestId; import org.unicitylabs.sdk.api.SubmitCommitmentResponse; @@ -30,7 +30,7 @@ void testVerifyBlockHeight() throws Exception { String aggregatorUrl = System.getenv("AGGREGATOR_URL"); assertNotNull(aggregatorUrl, "AGGREGATOR_URL environment variable must be set"); - AggregatorClient aggregatorClient = new AggregatorClient(aggregatorUrl); + JsonRpcAggregatorClient aggregatorClient = new JsonRpcAggregatorClient(aggregatorUrl); Long blockHeight = aggregatorClient.getBlockHeight().get(); System.out.println("block height: " + blockHeight); @@ -43,7 +43,7 @@ void testCommitmentPerformance() throws Exception { String aggregatorUrl = System.getenv("AGGREGATOR_URL"); assertNotNull(aggregatorUrl, "AGGREGATOR_URL environment variable must be set"); - AggregatorClient aggregatorClient = new AggregatorClient(aggregatorUrl); + JsonRpcAggregatorClient aggregatorClient = new JsonRpcAggregatorClient(aggregatorUrl); long startTime = System.currentTimeMillis(); SecureRandom sr = new SecureRandom(); @@ -77,7 +77,7 @@ void testCommitmentPerformanceMultiThreaded() throws Exception { String aggregatorUrl = System.getenv("AGGREGATOR_URL"); assertNotNull(aggregatorUrl, "AGGREGATOR_URL environment variable must be set"); - AggregatorClient aggregatorClient = new AggregatorClient(aggregatorUrl); + JsonRpcAggregatorClient aggregatorClient = new JsonRpcAggregatorClient(aggregatorUrl); ExecutorService executor = java.util.concurrent.Executors.newFixedThreadPool(threadCount); java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(threadCount * commitmentsPerThread); diff --git a/src/test/java/org/unicitylabs/sdk/e2e/E2EEscrowSwapTest.java b/src/test/java/org/unicitylabs/sdk/e2e/E2EEscrowSwapTest.java index 778636a..8fc13e1 100644 --- a/src/test/java/org/unicitylabs/sdk/e2e/E2EEscrowSwapTest.java +++ b/src/test/java/org/unicitylabs/sdk/e2e/E2EEscrowSwapTest.java @@ -6,10 +6,9 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.unicitylabs.sdk.StateTransitionClient; -import org.unicitylabs.sdk.api.AggregatorClient; +import org.unicitylabs.sdk.api.JsonRpcAggregatorClient; import org.unicitylabs.sdk.bft.RootTrustBase; import org.unicitylabs.sdk.common.BaseEscrowSwapTest; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; @Tag("integration") @@ -20,10 +19,10 @@ void setUp() throws IOException { String aggregatorUrl = System.getenv("AGGREGATOR_URL"); Assertions.assertNotNull(aggregatorUrl, "AGGREGATOR_URL environment variable must be set"); - this.client = new StateTransitionClient(new AggregatorClient(aggregatorUrl)); - this.trustBase = UnicityObjectMapper.JSON.readValue( - getClass().getResourceAsStream("/trust-base.json"), - RootTrustBase.class + this.client = new StateTransitionClient(new JsonRpcAggregatorClient(aggregatorUrl)); + // TODO: Close the stream + this.trustBase = RootTrustBase.fromJson( + new String(getClass().getResourceAsStream("/trust-base.json").readAllBytes()) ); } } diff --git a/src/test/java/org/unicitylabs/sdk/e2e/TokenE2ETest.java b/src/test/java/org/unicitylabs/sdk/e2e/TokenE2ETest.java index ab618fc..c55c0a6 100644 --- a/src/test/java/org/unicitylabs/sdk/e2e/TokenE2ETest.java +++ b/src/test/java/org/unicitylabs/sdk/e2e/TokenE2ETest.java @@ -2,14 +2,13 @@ import java.io.IOException; import org.unicitylabs.sdk.StateTransitionClient; -import org.unicitylabs.sdk.api.AggregatorClient; +import org.unicitylabs.sdk.api.JsonRpcAggregatorClient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.unicitylabs.sdk.bft.RootTrustBase; import org.unicitylabs.sdk.common.CommonTestFlow; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -22,18 +21,18 @@ @Tag("integration") @EnabledIfEnvironmentVariable(named = "AGGREGATOR_URL", matches = ".+") public class TokenE2ETest extends CommonTestFlow { - private AggregatorClient aggregatorClient; + + private JsonRpcAggregatorClient aggregatorClient; @BeforeEach void setUp() throws IOException { String aggregatorUrl = System.getenv("AGGREGATOR_URL"); assertNotNull(aggregatorUrl, "AGGREGATOR_URL environment variable must be set"); - this.aggregatorClient = new AggregatorClient(aggregatorUrl); + this.aggregatorClient = new JsonRpcAggregatorClient(aggregatorUrl); this.client = new StateTransitionClient(this.aggregatorClient); - this.trustBase = UnicityObjectMapper.JSON.readValue( - getClass().getResourceAsStream("/trust-base.json"), - RootTrustBase.class + this.trustBase = RootTrustBase.fromJson( + new String(getClass().getResourceAsStream("/trust-base.json").readAllBytes()) ); } diff --git a/src/test/java/org/unicitylabs/sdk/functional/FunctionalCommonFlowTest.java b/src/test/java/org/unicitylabs/sdk/functional/FunctionalCommonFlowTest.java index 7f06aae..8a1b9fc 100644 --- a/src/test/java/org/unicitylabs/sdk/functional/FunctionalCommonFlowTest.java +++ b/src/test/java/org/unicitylabs/sdk/functional/FunctionalCommonFlowTest.java @@ -5,7 +5,7 @@ import org.unicitylabs.sdk.TestAggregatorClient; import org.unicitylabs.sdk.common.CommonTestFlow; import org.unicitylabs.sdk.signing.SigningService; -import org.unicitylabs.sdk.utils.RootTrustBaseUtils; +import org.unicitylabs.sdk.bft.RootTrustBaseUtils; public class FunctionalCommonFlowTest extends CommonTestFlow { diff --git a/src/test/java/org/unicitylabs/sdk/functional/FunctionalEscrowSwapTest.java b/src/test/java/org/unicitylabs/sdk/functional/FunctionalEscrowSwapTest.java index 603d74a..4940240 100644 --- a/src/test/java/org/unicitylabs/sdk/functional/FunctionalEscrowSwapTest.java +++ b/src/test/java/org/unicitylabs/sdk/functional/FunctionalEscrowSwapTest.java @@ -5,7 +5,7 @@ import org.unicitylabs.sdk.TestAggregatorClient; import org.unicitylabs.sdk.common.BaseEscrowSwapTest; import org.unicitylabs.sdk.signing.SigningService; -import org.unicitylabs.sdk.utils.RootTrustBaseUtils; +import org.unicitylabs.sdk.bft.RootTrustBaseUtils; public class FunctionalEscrowSwapTest extends BaseEscrowSwapTest { @BeforeEach diff --git a/src/test/java/org/unicitylabs/sdk/functional/FunctionalTokenSplitTest.java b/src/test/java/org/unicitylabs/sdk/functional/FunctionalTokenSplitTest.java index 4748962..0d4c7e8 100644 --- a/src/test/java/org/unicitylabs/sdk/functional/FunctionalTokenSplitTest.java +++ b/src/test/java/org/unicitylabs/sdk/functional/FunctionalTokenSplitTest.java @@ -5,7 +5,7 @@ import org.unicitylabs.sdk.common.split.BaseTokenSplitTest; import org.junit.jupiter.api.BeforeEach; import org.unicitylabs.sdk.signing.SigningService; -import org.unicitylabs.sdk.utils.RootTrustBaseUtils; +import org.unicitylabs.sdk.bft.RootTrustBaseUtils; public class FunctionalTokenSplitTest extends BaseTokenSplitTest { @BeforeEach diff --git a/src/test/java/org/unicitylabs/sdk/functional/FunctionalUnsignedPredicateDoubleSpendPreventionTest.java b/src/test/java/org/unicitylabs/sdk/functional/FunctionalUnsignedPredicateDoubleSpendPreventionTest.java index e210fd9..3d2cee9 100644 --- a/src/test/java/org/unicitylabs/sdk/functional/FunctionalUnsignedPredicateDoubleSpendPreventionTest.java +++ b/src/test/java/org/unicitylabs/sdk/functional/FunctionalUnsignedPredicateDoubleSpendPreventionTest.java @@ -18,21 +18,20 @@ import org.unicitylabs.sdk.predicate.embedded.MaskedPredicate; import org.unicitylabs.sdk.predicate.embedded.UnmaskedPredicate; import org.unicitylabs.sdk.predicate.embedded.UnmaskedPredicateReference; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.token.Token; import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenState; import org.unicitylabs.sdk.token.TokenType; -import org.unicitylabs.sdk.transaction.Transaction; import org.unicitylabs.sdk.transaction.TransferCommitment; -import org.unicitylabs.sdk.transaction.TransferTransactionData; +import org.unicitylabs.sdk.transaction.TransferTransaction; import org.unicitylabs.sdk.util.HexConverter; import org.unicitylabs.sdk.util.InclusionProofUtils; -import org.unicitylabs.sdk.utils.RootTrustBaseUtils; +import org.unicitylabs.sdk.bft.RootTrustBaseUtils; import org.unicitylabs.sdk.utils.TokenUtils; public class FunctionalUnsignedPredicateDoubleSpendPreventionTest { + protected StateTransitionClient client; protected RootTrustBase trustBase; @@ -57,12 +56,11 @@ private String[] transferToken(Token token, byte[] secret, Address address) t } return new String[]{ - UnicityObjectMapper.JSON.writeValueAsString(token), - UnicityObjectMapper.JSON.writeValueAsString( - commitment.toTransaction( - InclusionProofUtils.waitInclusionProof(this.client, this.trustBase, commitment).get() - ) - ) + token.toJson(), + commitment.toTransaction( + InclusionProofUtils.waitInclusionProof(this.client, this.trustBase, commitment) + .get() + ).toJson() }; } @@ -83,11 +81,8 @@ private Token mintToken(byte[] secret) throws Exception { } private Token receiveToken(String[] tokenInfo, byte[] secret) throws Exception { - Token token = UnicityObjectMapper.JSON.readValue(tokenInfo[0], Token.class); - Transaction transaction = UnicityObjectMapper.JSON.readValue( - tokenInfo[1], - UnicityObjectMapper.JSON.getTypeFactory() - .constructParametricType(Transaction.class, TransferTransactionData.class)); + Token token = Token.fromJson(tokenInfo[0]); + TransferTransaction transaction = TransferTransaction.fromJson(tokenInfo[1]); TokenState state = new TokenState( UnmaskedPredicate.create( diff --git a/src/test/java/org/unicitylabs/sdk/hash/DataHashTest.java b/src/test/java/org/unicitylabs/sdk/hash/DataHashTest.java index 36947b0..2737000 100644 --- a/src/test/java/org/unicitylabs/sdk/hash/DataHashTest.java +++ b/src/test/java/org/unicitylabs/sdk/hash/DataHashTest.java @@ -1,11 +1,9 @@ package org.unicitylabs.sdk.hash; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.unicitylabs.sdk.serializer.json.JsonSerializationException; public class DataHashTest { @@ -21,21 +19,21 @@ public void testInvalidDataHashArguments() { @Test public void testDataHashJsonSerialization() throws JsonProcessingException { - ObjectMapper objectMapper = UnicityObjectMapper.JSON; - Assertions.assertEquals("null", objectMapper.writeValueAsString(null)); Assertions.assertEquals( "\"00000000000000000000000000000000000000000000000000000000000000000000\"", - objectMapper.writeValueAsString(new DataHash(HashAlgorithm.SHA256, new byte[32]))); - Assertions.assertEquals("\"000200000000000000000000000000000000\"", - objectMapper.writeValueAsString(new DataHash(HashAlgorithm.SHA384, new byte[16]))); - Assertions.assertEquals(new DataHash(HashAlgorithm.SHA256, new byte[32]), - objectMapper.readValue( - "\"00000000000000000000000000000000000000000000000000000000000000000000\"", - DataHash.class)); - Assertions.assertThrows(JsonMappingException.class, - () -> objectMapper.readValue("[]", DataHash.class)); - Assertions.assertThrows(JsonMappingException.class, - () -> objectMapper.readValue("\"AABBGG\"", DataHash.class)); + new DataHash(HashAlgorithm.SHA256, new byte[32]).toJson() + ); + Assertions.assertEquals( + "\"000200000000000000000000000000000000\"", + new DataHash(HashAlgorithm.SHA384, new byte[16]).toJson() + ); + + Assertions.assertEquals( + new DataHash(HashAlgorithm.SHA256, new byte[32]), + DataHash.fromJson("\"00000000000000000000000000000000000000000000000000000000000000000000\"") + ); + Assertions.assertThrows(JsonSerializationException.class, () -> DataHash.fromJson("[]")); + Assertions.assertThrows(JsonSerializationException.class, () -> DataHash.fromJson("\"AABBGG\"")); } } diff --git a/src/test/java/org/unicitylabs/sdk/integration/TokenIntegrationTest.java b/src/test/java/org/unicitylabs/sdk/integration/TokenIntegrationTest.java index bb5a21e..2775a63 100644 --- a/src/test/java/org/unicitylabs/sdk/integration/TokenIntegrationTest.java +++ b/src/test/java/org/unicitylabs/sdk/integration/TokenIntegrationTest.java @@ -1,7 +1,7 @@ package org.unicitylabs.sdk.integration; import org.unicitylabs.sdk.StateTransitionClient; -import org.unicitylabs.sdk.api.AggregatorClient; +import org.unicitylabs.sdk.api.JsonRpcAggregatorClient; import org.junit.jupiter.api.*; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; @@ -29,7 +29,7 @@ public class TokenIntegrationTest { private GenericContainer mongoSetup; private GenericContainer aggregator; - private AggregatorClient aggregatorClient; + private JsonRpcAggregatorClient aggregatorClient; private StateTransitionClient client; @BeforeAll @@ -88,7 +88,7 @@ void setUp() throws Exception { private void initializeClient() { String aggregatorUrl = String.format("http://localhost:%d", aggregator.getMappedPort(3000)); - aggregatorClient = new AggregatorClient(aggregatorUrl); + aggregatorClient = new JsonRpcAggregatorClient(aggregatorUrl); client = new StateTransitionClient(aggregatorClient); } diff --git a/src/test/java/org/unicitylabs/sdk/jsonrpc/JsonRpcRequestTest.java b/src/test/java/org/unicitylabs/sdk/jsonrpc/JsonRpcRequestTest.java index b59b581..72723ab 100644 --- a/src/test/java/org/unicitylabs/sdk/jsonrpc/JsonRpcRequestTest.java +++ b/src/test/java/org/unicitylabs/sdk/jsonrpc/JsonRpcRequestTest.java @@ -1,21 +1,23 @@ package org.unicitylabs.sdk.jsonrpc; import com.fasterxml.jackson.core.JsonProcessingException; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import java.util.List; import java.util.UUID; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; public class JsonRpcRequestTest { @Test public void testJsonSerialization() throws JsonProcessingException { JsonRpcRequest request = new JsonRpcRequest( - UUID.fromString("42b9d83f-f984-4c81-9a53-fa1bdbd30b99"), "testMethod", - List.of("param1", "param2")); + UUID.fromString("42b9d83f-f984-4c81-9a53-fa1bdbd30b99"), + "testMethod", + List.of("param1", "param2") + ); Assertions.assertEquals( - "{\"id\":\"42b9d83f-f984-4c81-9a53-fa1bdbd30b99\",\"jsonrpc\":\"2.0\",\"method\":\"testMethod\",\"params\":[\"param1\",\"param2\"]}", + "{\"id\":\"42b9d83f-f984-4c81-9a53-fa1bdbd30b99\",\"method\":\"testMethod\",\"params\":[\"param1\",\"param2\"],\"jsonrpc\":\"2.0\"}", UnicityObjectMapper.JSON.writeValueAsString(request)); } diff --git a/src/test/java/org/unicitylabs/sdk/jsonrpc/JsonRpcResponseTest.java b/src/test/java/org/unicitylabs/sdk/jsonrpc/JsonRpcResponseTest.java index 26e5ee8..a989eed 100644 --- a/src/test/java/org/unicitylabs/sdk/jsonrpc/JsonRpcResponseTest.java +++ b/src/test/java/org/unicitylabs/sdk/jsonrpc/JsonRpcResponseTest.java @@ -1,11 +1,10 @@ package org.unicitylabs.sdk.jsonrpc; import com.fasterxml.jackson.core.JsonProcessingException; -import org.unicitylabs.sdk.api.BlockHeightResponse; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import java.util.UUID; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.unicitylabs.sdk.api.BlockHeightResponse; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; public class JsonRpcResponseTest { @@ -16,7 +15,11 @@ public void testJsonSerialization() throws JsonProcessingException { UnicityObjectMapper.JSON.getTypeFactory() .constructParametricType(JsonRpcResponse.class, BlockHeightResponse.class)); - Assertions.assertEquals(new JsonRpcResponse("2.0", new BlockHeightResponse(846973L), null, UUID.fromString("60ce8f4d-4c78-4690-a330-a92d3cf497a9")), data); + Assertions.assertEquals("60ce8f4d-4c78-4690-a330-a92d3cf497a9", data.getId().toString()); + Assertions.assertEquals("2.0", data.getVersion()); + Assertions.assertInstanceOf(BlockHeightResponse.class, data.getResult()); + Assertions.assertEquals(846973L, data.getResult().getBlockNumber()); + Assertions.assertNull(data.getError()); } } diff --git a/src/test/java/org/unicitylabs/sdk/mtree/MerkleTreePathStepBranchTest.java b/src/test/java/org/unicitylabs/sdk/mtree/MerkleTreePathStepBranchTest.java deleted file mode 100644 index 08ce650..0000000 --- a/src/test/java/org/unicitylabs/sdk/mtree/MerkleTreePathStepBranchTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.unicitylabs.sdk.mtree; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep.Branch; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class MerkleTreePathStepBranchTest { - - @Test - public void testJsonSerialization() throws JsonProcessingException { - ObjectMapper objectMapper = UnicityObjectMapper.JSON; - Assertions.assertEquals("null", objectMapper.writeValueAsString(null)); - Assertions.assertEquals("[null]", - objectMapper.writeValueAsString(new Branch(null))); - Assertions.assertEquals("[\"00000000\"]", - objectMapper.writeValueAsString(new Branch(new byte[4]))); - Assertions.assertEquals(new Branch(null), - objectMapper.readValue("[null]", Branch.class)); - Assertions.assertEquals(new Branch(new byte[]{(byte) 0x51, (byte) 0x55}), - objectMapper.readValue("[\"5155\"]", Branch.class)); - Assertions.assertThrows(JsonMappingException.class, - () -> objectMapper.readValue("\"asd\"", Branch.class)); - Assertions.assertThrows(JsonMappingException.class, - () -> objectMapper.readValue("[\"AABBGG\"]", Branch.class)); - } -} diff --git a/src/test/java/org/unicitylabs/sdk/mtree/MerkleTreePathStepTest.java b/src/test/java/org/unicitylabs/sdk/mtree/plain/MerkleTreePathStepTest.java similarity index 93% rename from src/test/java/org/unicitylabs/sdk/mtree/MerkleTreePathStepTest.java rename to src/test/java/org/unicitylabs/sdk/mtree/plain/MerkleTreePathStepTest.java index b1f0486..c1f3639 100644 --- a/src/test/java/org/unicitylabs/sdk/mtree/MerkleTreePathStepTest.java +++ b/src/test/java/org/unicitylabs/sdk/mtree/plain/MerkleTreePathStepTest.java @@ -1,4 +1,4 @@ -package org.unicitylabs.sdk.mtree; +package org.unicitylabs.sdk.mtree.plain; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -8,18 +8,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import java.math.BigInteger; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.unicitylabs.sdk.serializer.UnicityObjectMapper; public class MerkleTreePathStepTest { @Test public void testConstructorThrowsOnNullArguments() { Exception exception = assertThrows(NullPointerException.class, - () -> new SparseMerkleTreePathStep(null, null, null)); + () -> new SparseMerkleTreePathStep(null, (SparseMerkleTreePathStep.Branch) null, null)); assertEquals("path cannot be null", exception.getMessage()); } diff --git a/src/test/java/org/unicitylabs/sdk/mtree/MerkleTreePathTest.java b/src/test/java/org/unicitylabs/sdk/mtree/plain/MerkleTreePathTest.java similarity index 92% rename from src/test/java/org/unicitylabs/sdk/mtree/MerkleTreePathTest.java rename to src/test/java/org/unicitylabs/sdk/mtree/plain/MerkleTreePathTest.java index 85a5e40..40ba82a 100644 --- a/src/test/java/org/unicitylabs/sdk/mtree/MerkleTreePathTest.java +++ b/src/test/java/org/unicitylabs/sdk/mtree/plain/MerkleTreePathTest.java @@ -1,4 +1,4 @@ -package org.unicitylabs.sdk.mtree; +package org.unicitylabs.sdk.mtree.plain; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -6,10 +6,10 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.ValueInstantiationException; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathStep; +import org.unicitylabs.sdk.mtree.MerkleTreePathVerificationResult; import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import org.unicitylabs.sdk.util.HexConverter; import java.math.BigInteger; @@ -39,7 +39,7 @@ public void testJsonSerialization() throws JsonProcessingException { () -> objectMapper.readValue("{\"root\":\"00000000\"}", SparseMerkleTreePath.class)); Assertions.assertThrows(JsonMappingException.class, () -> objectMapper.readValue("{\"steps\":[]}", SparseMerkleTreePath.class)); - Assertions.assertThrows(NullPointerException.class, + Assertions.assertThrows(ValueInstantiationException.class, () -> objectMapper.readValue("{\"root\": null, \"steps\":[]}", SparseMerkleTreePath.class)); Assertions.assertThrows(JsonMappingException.class, () -> objectMapper.readValue("{\"root\": \"asd\", \"steps\":[]}", @@ -115,7 +115,11 @@ public void testShouldVerifyNonInclusionProof() { "00006c5ad75422175395b4b63390e9dea5d0a39017f4750b78cc4b89ac6451265345")), new SparseMerkleTreePathStep.Branch( HexConverter.decode("76616c75653030303030303030"))), - new SparseMerkleTreePathStep(BigInteger.valueOf(4), null, null) + new SparseMerkleTreePathStep( + BigInteger.valueOf(4), + (SparseMerkleTreePathStep.Branch) null, + null + ) ) ); diff --git a/src/test/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePathFixture.java b/src/test/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePathFixture.java new file mode 100644 index 0000000..f5b67a6 --- /dev/null +++ b/src/test/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePathFixture.java @@ -0,0 +1,51 @@ +package org.unicitylabs.sdk.mtree.plain; + +import java.util.List; +import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.hash.DataHasher; +import org.unicitylabs.sdk.hash.HashAlgorithm; +import org.unicitylabs.sdk.util.BigIntegerConverter; + +public class SparseMerkleTreePathFixture { + + public static SparseMerkleTreePath create(List steps) { + if (steps.isEmpty()) { + return new SparseMerkleTreePath( + new DataHasher(HashAlgorithm.SHA256) + .update(new byte[]{0}) + .update(new byte[]{0}) + .digest(), + List.of() + ); + } + + DataHash root = null; + for (int i = 0; i < steps.size(); i++) { + SparseMerkleTreePathStep step = steps.get(i); + byte[] hash; + + if (step.getBranch().isEmpty()) { + hash = new byte[]{0}; + } else { + byte[] bytes = i == 0 + ? step.getBranch().map(SparseMerkleTreePathStep.Branch::getValue).orElse(null) + : (root != null ? root.getData() : null); + + hash = new DataHasher(HashAlgorithm.SHA256) + .update(BigIntegerConverter.encode(step.getPath())) + .update(bytes == null ? new byte[]{0} : bytes) + .digest() + .getData(); + } + + byte[] siblingHash = step.getSibling().map(SparseMerkleTreePathStep.Branch::getValue) + .orElse(new byte[]{0}); + boolean isRight = step.getPath().testBit(0); + root = new DataHasher(HashAlgorithm.SHA256).update(isRight ? siblingHash : hash) + .update(isRight ? hash : siblingHash).digest(); + } + + return new SparseMerkleTreePath(root, steps); + } + +} diff --git a/src/test/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreeTest.java b/src/test/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreeTest.java index f9306d5..ffa2b73 100644 --- a/src/test/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreeTest.java +++ b/src/test/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreeTest.java @@ -151,25 +151,25 @@ public void shouldGetWorkingPath() throws Exception { MerkleTreePathVerificationResult result = path.verify(BigInteger.valueOf(0b11010)); Assertions.assertFalse(result.isPathIncluded()); Assertions.assertTrue(result.isPathValid()); - Assertions.assertFalse(result.isValid()); + Assertions.assertFalse(result.isSuccessful()); path = root.getPath(BigInteger.valueOf(0b110010000)); result = path.verify(BigInteger.valueOf(0b110010000)); Assertions.assertTrue(result.isPathIncluded()); Assertions.assertTrue(result.isPathValid()); - Assertions.assertTrue(result.isValid()); + Assertions.assertTrue(result.isSuccessful()); path = root.getPath(BigInteger.valueOf(0b110010000)); result = path.verify(BigInteger.valueOf(0b11010)); Assertions.assertFalse(result.isPathIncluded()); Assertions.assertTrue(result.isPathValid()); - Assertions.assertFalse(result.isValid()); + Assertions.assertFalse(result.isSuccessful()); path = root.getPath(BigInteger.valueOf(0b111100101)); result = path.verify(BigInteger.valueOf(0b111100101)); Assertions.assertTrue(result.isPathIncluded()); Assertions.assertTrue(result.isPathValid()); - Assertions.assertTrue(result.isValid()); + Assertions.assertTrue(result.isSuccessful()); SparseMerkleTree emptyTree = new SparseMerkleTree(HashAlgorithm.SHA256); SparseMerkleTreeRootNode emptyRoot = emptyTree.calculateRoot(); @@ -177,6 +177,6 @@ public void shouldGetWorkingPath() throws Exception { result = path.verify(BigInteger.valueOf(0b10)); Assertions.assertFalse(result.isPathIncluded()); Assertions.assertTrue(result.isPathValid()); - Assertions.assertFalse(result.isValid()); + Assertions.assertFalse(result.isSuccessful()); } } diff --git a/src/test/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreeTest.java b/src/test/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreeTest.java index f4a7bbf..647d093 100644 --- a/src/test/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreeTest.java +++ b/src/test/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreeTest.java @@ -33,7 +33,7 @@ void shouldBuildTreeWithNumericValues() throws Exception { var verificationResult = path.verify(entry.getKey()); Assertions.assertTrue(verificationResult.isPathIncluded()); Assertions.assertTrue(verificationResult.isPathValid()); - Assertions.assertTrue(verificationResult.isValid()); + Assertions.assertTrue(verificationResult.isSuccessful()); Assertions.assertEquals(root.getRoot().getCounter(), path.getRoot().getCounter()); Assertions.assertEquals(root.getRoot(), path.getRoot()); diff --git a/src/test/java/org/unicitylabs/sdk/predicate/MaskedPredicateReferenceTest.java b/src/test/java/org/unicitylabs/sdk/predicate/MaskedPredicateReferenceTest.java index 75234b1..49116a5 100644 --- a/src/test/java/org/unicitylabs/sdk/predicate/MaskedPredicateReferenceTest.java +++ b/src/test/java/org/unicitylabs/sdk/predicate/MaskedPredicateReferenceTest.java @@ -11,7 +11,7 @@ class MaskedPredicateReferenceTest { @Test void testReferenceAddress() { Assertions.assertEquals( - "DIRECT://000095ca469ab7a37f8be976b7da1a6023369182ba3cdd1293c07a4b2bf40aa5118d60f5bf36", + "DIRECT://000056787e7ec9ef8e70cc715f061bd83981d552c6f813f9a319153e24321ccf5195f0f78200", MaskedPredicateReference.create( new TokenType(new byte[32]), "my_algorithm", diff --git a/src/test/java/org/unicitylabs/sdk/serializer/cbor/CborDeserializerTest.java b/src/test/java/org/unicitylabs/sdk/serializer/cbor/CborDeserializerTest.java index 6eb2fe4..1cf553a 100644 --- a/src/test/java/org/unicitylabs/sdk/serializer/cbor/CborDeserializerTest.java +++ b/src/test/java/org/unicitylabs/sdk/serializer/cbor/CborDeserializerTest.java @@ -16,32 +16,32 @@ public class CborDeserializerTest { void testReadUnsignedInteger() { Assertions.assertEquals( 5, - CborDeserializer.readUnsignedInteger(HexConverter.decode("05")) + CborDeserializer.readUnsignedInteger(HexConverter.decode("05")).asLong() ); Assertions.assertEquals( 100, - CborDeserializer.readUnsignedInteger(HexConverter.decode("1864")) + CborDeserializer.readUnsignedInteger(HexConverter.decode("1864")).asLong() ); Assertions.assertEquals( 10000, - CborDeserializer.readUnsignedInteger(HexConverter.decode("192710")) + CborDeserializer.readUnsignedInteger(HexConverter.decode("192710")).asLong() ); Assertions.assertEquals( 66000, - CborDeserializer.readUnsignedInteger(HexConverter.decode("1a000101d0")) + CborDeserializer.readUnsignedInteger(HexConverter.decode("1a000101d0")).asLong() ); Assertions.assertEquals( 8147483647L, - CborDeserializer.readUnsignedInteger(HexConverter.decode("1b00000001e5a0bbff")) + CborDeserializer.readUnsignedInteger(HexConverter.decode("1b00000001e5a0bbff")).asLong() ); Assertions.assertEquals( -5, - CborDeserializer.readUnsignedInteger(HexConverter.decode("1bfffffffffffffffb")) + CborDeserializer.readUnsignedInteger(HexConverter.decode("1bfffffffffffffffb")).asLong() ); } @@ -136,23 +136,18 @@ void testReadMap() { @Test void testReadBoolean() { - Assertions.assertEquals( - true, - CborDeserializer.readBoolean(HexConverter.decode("f5")) - ); + Assertions.assertTrue(CborDeserializer.readBoolean(HexConverter.decode("f5"))); - Assertions.assertEquals( - false, - CborDeserializer.readBoolean(HexConverter.decode("f4")) - ); + Assertions.assertFalse(CborDeserializer.readBoolean(HexConverter.decode("f4"))); } @Test void testReadOptional() { - Assertions.assertEquals( - null, - CborDeserializer.readOptional(HexConverter.decode("f6"), - CborDeserializer::readUnsignedInteger) + Assertions.assertNull( + CborDeserializer.readOptional( + HexConverter.decode("f6"), + CborDeserializer::readUnsignedInteger + ) ); } diff --git a/src/test/java/org/unicitylabs/sdk/serializer/cbor/CborSerializerTest.java b/src/test/java/org/unicitylabs/sdk/serializer/cbor/CborSerializerTest.java index 4a74562..78a84cc 100644 --- a/src/test/java/org/unicitylabs/sdk/serializer/cbor/CborSerializerTest.java +++ b/src/test/java/org/unicitylabs/sdk/serializer/cbor/CborSerializerTest.java @@ -92,10 +92,8 @@ void testEncodeArray() { HexConverter.decode( "826d48656c6c6f2c20776f726c6421581900000000000000000000000000000000000000000000000000"), CborSerializer.encodeArray( - List.of( - CborSerializer.encodeTextString("Hello, world!"), - CborSerializer.encodeByteString(new byte[25]) - ) + CborSerializer.encodeTextString("Hello, world!"), + CborSerializer.encodeByteString(new byte[25]) ) ); @@ -107,7 +105,7 @@ void testEncodeArray() { Assertions.assertArrayEquals( HexConverter.decode( "98196d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c64216d48656c6c6f2c20776f726c6421"), - CborSerializer.encodeArray(list) + CborSerializer.encodeArray(list.toArray(byte[][]::new)) ); } diff --git a/src/test/java/org/unicitylabs/sdk/transaction/split/TokenSplitBuilderTest.java b/src/test/java/org/unicitylabs/sdk/token/TokenSplitBuilderTest.java similarity index 84% rename from src/test/java/org/unicitylabs/sdk/transaction/split/TokenSplitBuilderTest.java rename to src/test/java/org/unicitylabs/sdk/token/TokenSplitBuilderTest.java index bbc59c7..5ba7685 100644 --- a/src/test/java/org/unicitylabs/sdk/transaction/split/TokenSplitBuilderTest.java +++ b/src/test/java/org/unicitylabs/sdk/token/TokenSplitBuilderTest.java @@ -1,4 +1,4 @@ -package org.unicitylabs.sdk.transaction.split; +package org.unicitylabs.sdk.token; import java.io.IOException; import java.math.BigInteger; @@ -8,24 +8,21 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.unicitylabs.sdk.bft.UnicityCertificate; +import org.unicitylabs.sdk.bft.UnicityCertificateUtils; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.mtree.BranchExistsException; import org.unicitylabs.sdk.mtree.LeafOutOfBoundsException; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; +import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathFixture; import org.unicitylabs.sdk.predicate.Predicate; import org.unicitylabs.sdk.predicate.embedded.MaskedPredicate; import org.unicitylabs.sdk.signing.SigningService; -import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.token.TokenId; -import org.unicitylabs.sdk.token.TokenState; -import org.unicitylabs.sdk.token.TokenType; import org.unicitylabs.sdk.token.fungible.CoinId; import org.unicitylabs.sdk.token.fungible.TokenCoinData; -import org.unicitylabs.sdk.transaction.InclusionProof; -import org.unicitylabs.sdk.transaction.MintTransactionData; -import org.unicitylabs.sdk.transaction.Transaction; -import org.unicitylabs.sdk.utils.UnicityCertificateUtils; +import org.unicitylabs.sdk.transaction.InclusionProofFixture; +import org.unicitylabs.sdk.transaction.MintTransaction; +import org.unicitylabs.sdk.transaction.MintTransactionFixture; +import org.unicitylabs.sdk.transaction.split.TokenSplitBuilder; import org.unicitylabs.sdk.verification.VerificationException; public class TokenSplitBuilderTest { @@ -49,8 +46,8 @@ private Token createToken(TokenCoinData coinData) throws VerificationExceptio return new Token<>( new TokenState(predicate, null), - new Transaction<>( - new MintTransactionData<>( + MintTransactionFixture.create( + new MintTransaction.Data<>( tokenId, tokenType, null, @@ -60,11 +57,8 @@ private Token createToken(TokenCoinData coinData) throws VerificationExceptio null, null ), - new InclusionProof( - new SparseMerkleTreePath( - new DataHash(HashAlgorithm.SHA256, new byte[32]), - List.of() - ), + InclusionProofFixture.create( + SparseMerkleTreePathFixture.create(List.of()), null, null, unicityCertificate @@ -78,7 +72,6 @@ private Token createToken(TokenCoinData coinData) throws VerificationExceptio @Test public void testTokenSplitIntoMultipleTokens() throws LeafOutOfBoundsException, BranchExistsException, VerificationException, IOException { - Token token = this.createToken( new TokenCoinData( Map.of( @@ -125,9 +118,10 @@ public void testTokenSplitIntoMultipleTokens() null ); - Exception exception = Assertions.assertThrows(IllegalArgumentException.class, () -> { - builder.build(token); - }); + Exception exception = Assertions.assertThrows( + IllegalArgumentException.class, + () -> builder.build(token) + ); Assertions.assertEquals("Token contained 100 CoinId{bytes=636f696e31} coins, but tree has 50", exception.getMessage()); diff --git a/src/test/java/org/unicitylabs/sdk/token/TokenTest.java b/src/test/java/org/unicitylabs/sdk/token/TokenTest.java index ccd9545..a4628ce 100644 --- a/src/test/java/org/unicitylabs/sdk/token/TokenTest.java +++ b/src/test/java/org/unicitylabs/sdk/token/TokenTest.java @@ -9,20 +9,19 @@ import org.junit.jupiter.api.Test; import org.unicitylabs.sdk.address.DirectAddress; import org.unicitylabs.sdk.bft.UnicityCertificate; +import org.unicitylabs.sdk.bft.UnicityCertificateUtils; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; +import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathFixture; import org.unicitylabs.sdk.predicate.embedded.MaskedPredicate; import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.token.fungible.CoinId; import org.unicitylabs.sdk.token.fungible.TokenCoinData; -import org.unicitylabs.sdk.transaction.InclusionProof; -import org.unicitylabs.sdk.transaction.MintTransactionData; -import org.unicitylabs.sdk.transaction.NametagMintTransactionData; -import org.unicitylabs.sdk.transaction.Transaction; +import org.unicitylabs.sdk.transaction.InclusionProofFixture; +import org.unicitylabs.sdk.transaction.MintTransaction; +import org.unicitylabs.sdk.transaction.MintTransactionFixture; import org.unicitylabs.sdk.utils.TestUtils; -import org.unicitylabs.sdk.utils.UnicityCertificateUtils; import org.unicitylabs.sdk.verification.VerificationException; public class TokenTest { @@ -33,13 +32,16 @@ public void testJsonSerialization() throws IOException, VerificationException { UnicityCertificate unicityCertificate = UnicityCertificateUtils.generateCertificate( signingService, DataHash.fromImprint(new byte[34])); - MintTransactionData genesisData = new MintTransactionData<>( + MintTransaction.Data genesisData = new MintTransaction.Data<>( new TokenId(TestUtils.randomBytes(32)), new TokenType(TestUtils.randomBytes(32)), TestUtils.randomBytes(10), - new TokenCoinData(Map.of( - new CoinId(TestUtils.randomBytes(10)), BigInteger.valueOf(100), - new CoinId(TestUtils.randomBytes(4)), BigInteger.valueOf(3))), + new TokenCoinData( + Map.of( + new CoinId(TestUtils.randomBytes(10)), BigInteger.valueOf(100), + new CoinId(TestUtils.randomBytes(4)), BigInteger.valueOf(3) + ) + ), DirectAddress.create(new DataHash(HashAlgorithm.SHA256, TestUtils.randomBytes(32))), TestUtils.randomBytes(32), null, @@ -47,7 +49,7 @@ public void testJsonSerialization() throws IOException, VerificationException { ); byte[] nametagNonce = TestUtils.randomBytes(32); - MintTransactionData nametagGenesisData = new NametagMintTransactionData<>( + MintTransaction.NametagData nametagGenesisData = new MintTransaction.NametagData( UUID.randomUUID().toString(), new TokenType(TestUtils.randomBytes(32)), DirectAddress.create(new DataHash(HashAlgorithm.SHA256, TestUtils.randomBytes(32))), @@ -64,13 +66,10 @@ public void testJsonSerialization() throws IOException, VerificationException { HashAlgorithm.SHA256, nametagNonce), null), - new Transaction<>( + MintTransactionFixture.create( nametagGenesisData, - new InclusionProof( - new SparseMerkleTreePath( - new DataHash(HashAlgorithm.SHA256, TestUtils.randomBytes(32)), - List.of() - ), + InclusionProofFixture.create( + SparseMerkleTreePathFixture.create(List.of()), null, null, unicityCertificate @@ -93,13 +92,10 @@ public void testJsonSerialization() throws IOException, VerificationException { TestUtils.randomBytes(24)), null ), - new Transaction<>( + MintTransactionFixture.create( genesisData, - new InclusionProof( - new SparseMerkleTreePath( - new DataHash(HashAlgorithm.SHA256, TestUtils.randomBytes(32)), - List.of() - ), + InclusionProofFixture.create( + SparseMerkleTreePathFixture.create(List.of()), null, null, unicityCertificate diff --git a/src/test/java/org/unicitylabs/sdk/transaction/CommitmentTest.java b/src/test/java/org/unicitylabs/sdk/transaction/CommitmentTest.java index db8f253..102866b 100644 --- a/src/test/java/org/unicitylabs/sdk/transaction/CommitmentTest.java +++ b/src/test/java/org/unicitylabs/sdk/transaction/CommitmentTest.java @@ -1,22 +1,21 @@ package org.unicitylabs.sdk.transaction; -import org.unicitylabs.sdk.api.Authenticator; -import org.unicitylabs.sdk.api.RequestId; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import java.io.IOException; +import java.math.BigInteger; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.predicate.embedded.MaskedPredicateReference; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenType; import org.unicitylabs.sdk.token.fungible.CoinId; import org.unicitylabs.sdk.token.fungible.TokenCoinData; import org.unicitylabs.sdk.util.HexConverter; -import java.io.IOException; -import java.math.BigInteger; -import java.util.Map; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; public class CommitmentTest { @@ -29,7 +28,7 @@ public void testJsonSerialization() throws IOException { MaskedPredicateReference predicateReference = MaskedPredicateReference.create(tokenType, signingService.getAlgorithm(), signingService.getPublicKey(), HashAlgorithm.SHA256, nonce); - MintTransactionData transactionData = new MintTransactionData<>( + MintTransaction.Data transactionData = new MintTransaction.Data<>( new TokenId(new byte[32]), tokenType, new byte[5], @@ -42,20 +41,14 @@ public void testJsonSerialization() throws IOException { new DataHash(HashAlgorithm.SHA256, new byte[32]), null ); - Commitment> commitment = new MintCommitment<>( - new RequestId( - new DataHash(HashAlgorithm.SHA256, new byte[32]) - ), - transactionData, - Authenticator.create( - signingService, - transactionData.calculateHash(), - transactionData.getSourceState().getHash()) - ); + MintCommitment commitment = MintCommitment.create(transactionData); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new Jdk8Module()); Assertions.assertEquals(commitment, - UnicityObjectMapper.JSON.readValue( - UnicityObjectMapper.JSON.writeValueAsString(commitment), + mapper.readValue( + mapper.writeValueAsString(commitment), MintCommitment.class ) ); diff --git a/src/test/java/org/unicitylabs/sdk/transaction/InclusionProofFixture.java b/src/test/java/org/unicitylabs/sdk/transaction/InclusionProofFixture.java new file mode 100644 index 0000000..51bddf1 --- /dev/null +++ b/src/test/java/org/unicitylabs/sdk/transaction/InclusionProofFixture.java @@ -0,0 +1,17 @@ +package org.unicitylabs.sdk.transaction; + +import org.unicitylabs.sdk.api.Authenticator; +import org.unicitylabs.sdk.bft.UnicityCertificate; +import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; + +public class InclusionProofFixture { + public static InclusionProof create( + SparseMerkleTreePath path, + Authenticator authenticator, + DataHash transactionHash, + UnicityCertificate certificate + ) { + return new InclusionProof(path, authenticator, transactionHash, certificate); + } +} diff --git a/src/test/java/org/unicitylabs/sdk/transaction/InclusionProofTest.java b/src/test/java/org/unicitylabs/sdk/transaction/InclusionProofTest.java index a74b2cb..62d0e20 100644 --- a/src/test/java/org/unicitylabs/sdk/transaction/InclusionProofTest.java +++ b/src/test/java/org/unicitylabs/sdk/transaction/InclusionProofTest.java @@ -13,11 +13,10 @@ import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.mtree.plain.SparseMerkleTree; import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.util.HexConverter; -import org.unicitylabs.sdk.utils.RootTrustBaseUtils; -import org.unicitylabs.sdk.utils.UnicityCertificateUtils; +import org.unicitylabs.sdk.bft.RootTrustBaseUtils; +import org.unicitylabs.sdk.bft.UnicityCertificateUtils; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class InclusionProofTest { @@ -58,8 +57,7 @@ public void testJsonSerialization() throws Exception { transactionHash, unicityCertificate ); - Assertions.assertEquals(inclusionProof, UnicityObjectMapper.JSON.readValue( - UnicityObjectMapper.JSON.writeValueAsString(inclusionProof), InclusionProof.class)); + Assertions.assertEquals(inclusionProof, InclusionProof.fromJson(inclusionProof.toJson())); } @Test diff --git a/src/test/java/org/unicitylabs/sdk/transaction/MintTransactionFixture.java b/src/test/java/org/unicitylabs/sdk/transaction/MintTransactionFixture.java new file mode 100644 index 0000000..d0cdfe2 --- /dev/null +++ b/src/test/java/org/unicitylabs/sdk/transaction/MintTransactionFixture.java @@ -0,0 +1,15 @@ +package org.unicitylabs.sdk.transaction; + +import org.unicitylabs.sdk.api.Authenticator; +import org.unicitylabs.sdk.bft.UnicityCertificate; +import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePath; + +public class MintTransactionFixture { + public static MintTransaction create( + MintTransaction.Data data, + InclusionProof inclusionProof + ) { + return new MintTransaction<>(data, inclusionProof); + } +} diff --git a/src/test/java/org/unicitylabs/sdk/utils/TokenUtils.java b/src/test/java/org/unicitylabs/sdk/utils/TokenUtils.java index f23d781..6ccff5d 100644 --- a/src/test/java/org/unicitylabs/sdk/utils/TokenUtils.java +++ b/src/test/java/org/unicitylabs/sdk/utils/TokenUtils.java @@ -20,9 +20,7 @@ import org.unicitylabs.sdk.token.fungible.TokenCoinData; import org.unicitylabs.sdk.transaction.InclusionProof; import org.unicitylabs.sdk.transaction.MintCommitment; -import org.unicitylabs.sdk.transaction.MintTransactionData; -import org.unicitylabs.sdk.transaction.MintTransactionReason; -import org.unicitylabs.sdk.transaction.NametagMintTransactionData; +import org.unicitylabs.sdk.transaction.MintTransaction; import org.unicitylabs.sdk.util.InclusionProofUtils; public class TokenUtils { @@ -71,8 +69,8 @@ public static Token mintToken( Address address = predicate.getReference().toAddress(); TokenState tokenState = new TokenState(predicate, null); - MintCommitment> commitment = MintCommitment.create( - new MintTransactionData<>( + MintCommitment commitment = MintCommitment.create( + new MintTransaction.Data<>( tokenId, tokenType, tokenData, @@ -145,8 +143,8 @@ public static Token mintNametagToken( HashAlgorithm.SHA256, nonce).toAddress(); - MintCommitment> commitment = MintCommitment.create( - new NametagMintTransactionData<>( + MintCommitment commitment = MintCommitment.create( + new MintTransaction.NametagData( nametag, tokenType, address, diff --git a/src/test/java/org/unicitylabs/sdk/utils/UnicityCertificateUtils.java b/src/test/java/org/unicitylabs/sdk/utils/UnicityCertificateUtils.java deleted file mode 100644 index a28fdcf..0000000 --- a/src/test/java/org/unicitylabs/sdk/utils/UnicityCertificateUtils.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.unicitylabs.sdk.utils; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.List; -import java.util.Map; -import org.unicitylabs.sdk.bft.InputRecord; -import org.unicitylabs.sdk.bft.ShardTreeCertificate; -import org.unicitylabs.sdk.bft.UnicityCertificate; -import org.unicitylabs.sdk.bft.UnicitySeal; -import org.unicitylabs.sdk.bft.UnicityTreeCertificate; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.hash.DataHasher; -import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.serializer.UnicityObjectMapper; -import org.unicitylabs.sdk.signing.SigningService; - -public class UnicityCertificateUtils { - - public static UnicityCertificate generateCertificate( - SigningService signingService, - DataHash rootHash - ) { - try { - InputRecord inputRecord = new InputRecord(0, 0, 0, null, rootHash.getImprint(), new byte[10], - 0, - new byte[10], 0, new byte[10]); - UnicityTreeCertificate unicityTreeCertificate = new UnicityTreeCertificate(0, 0, List.of()); - byte[] technicalRecordHash = new byte[32]; - byte[] shardConfigurationHash = new byte[32]; - ShardTreeCertificate shardTreeCertificate = new ShardTreeCertificate( - new byte[32], List.of() - ); - - DataHash shardTreeCertificateRootHash = UnicityCertificate.calculateShardTreeCertificateRootHash( - inputRecord, - technicalRecordHash, - shardConfigurationHash, - shardTreeCertificate - ); - - byte[] key = ByteBuffer.allocate(4) - .order(ByteOrder.BIG_ENDIAN) - .putInt(unicityTreeCertificate.getPartitionIdentifier()) - .array(); - - DataHash unicitySealHash = new DataHasher(HashAlgorithm.SHA256) - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(new byte[]{(byte) 0x01})) // LEAF - .update(UnicityObjectMapper.CBOR.writeValueAsBytes(key)) - .update( - UnicityObjectMapper.CBOR.writeValueAsBytes( - new DataHasher(HashAlgorithm.SHA256) - .update( - UnicityObjectMapper.CBOR.writeValueAsBytes( - shardTreeCertificateRootHash.getData() - ) - ) - .digest() - .getData() - ) - ) - .digest(); - - UnicitySeal seal = new UnicitySeal( - 0, - (short) 0, - 0L, - 0L, - 0L, - null, - unicitySealHash.getData(), - null - ); - - return new UnicityCertificate( - 0, - new InputRecord(0, 0, 0, null, rootHash.getImprint(), new byte[10], 0, - new byte[10], 0, new byte[10]), - technicalRecordHash, - shardConfigurationHash, - shardTreeCertificate, - new UnicityTreeCertificate(0, 0, List.of()), - seal.withSignatures( - Map.of( - "NODE", - signingService.sign( - new DataHasher(HashAlgorithm.SHA256).update(seal.encode()).digest() - ).encode() - ) - ) - ); - } catch (IOException e) { - throw new RuntimeException("Failed to generate UnicityCertificate", e); - } - } -} diff --git a/src/test/java/org/unicitylabs/sdk/utils/helpers/PendingTransfer.java b/src/test/java/org/unicitylabs/sdk/utils/helpers/PendingTransfer.java index db3bcbe..bb2ac69 100644 --- a/src/test/java/org/unicitylabs/sdk/utils/helpers/PendingTransfer.java +++ b/src/test/java/org/unicitylabs/sdk/utils/helpers/PendingTransfer.java @@ -1,18 +1,17 @@ package org.unicitylabs.sdk.utils.helpers; import org.unicitylabs.sdk.token.Token; -import org.unicitylabs.sdk.transaction.Transaction; -import org.unicitylabs.sdk.transaction.TransferTransactionData; +import org.unicitylabs.sdk.transaction.TransferTransaction; public class PendingTransfer { private final Token sourceToken; - private final Transaction transaction; + private final TransferTransaction transaction; - public PendingTransfer(Token sourceToken, Transaction transaction) { + public PendingTransfer(Token sourceToken, TransferTransaction transaction) { this.sourceToken = sourceToken; this.transaction = transaction; } public Token getSourceToken() { return sourceToken; } - public Transaction getTransaction() { return transaction; } + public TransferTransaction getTransaction() { return transaction; } }