diff --git a/src/main/java/org/unicitylabs/sdk/StateTransitionClient.java b/src/main/java/org/unicitylabs/sdk/StateTransitionClient.java index 40287e3..5bba9b8 100644 --- a/src/main/java/org/unicitylabs/sdk/StateTransitionClient.java +++ b/src/main/java/org/unicitylabs/sdk/StateTransitionClient.java @@ -9,20 +9,22 @@ import org.unicitylabs.sdk.api.RequestId; import org.unicitylabs.sdk.api.SubmitCommitmentResponse; import org.unicitylabs.sdk.bft.RootTrustBase; +import org.unicitylabs.sdk.predicate.Predicate; import org.unicitylabs.sdk.predicate.PredicateEngineService; +import org.unicitylabs.sdk.signing.MintSigningService; import org.unicitylabs.sdk.token.Token; +import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenState; -import org.unicitylabs.sdk.transaction.Commitment; import org.unicitylabs.sdk.transaction.InclusionProofVerificationStatus; import org.unicitylabs.sdk.transaction.MintCommitment; import org.unicitylabs.sdk.transaction.MintTransactionReason; +import org.unicitylabs.sdk.transaction.MintTransactionState; import org.unicitylabs.sdk.transaction.TransferCommitment; 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. + * Client for handling state transitions of tokens, including submitting commitments and finalizing transactions. */ public class StateTransitionClient { @@ -80,8 +82,7 @@ public CompletableFuture submitCommitment( } /** - * Finalizes a transaction by updating the token state based on the provided transaction data - * without nametags. + * 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. @@ -101,8 +102,7 @@ public Token finalizeTransaction( } /** - * Finalizes a transaction by updating the token state based on the provided transaction data and - * nametags. + * 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. @@ -126,31 +126,77 @@ public Token finalizeTransaction( } /** - * Retrieves the inclusion proof for a token and verifies its status against the provided public - * key and trust base. + * Retrieves the inclusion proof for a given request id. * - * @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. + * @param requestId The request ID of inclusion proof to retrieve. + * @return A CompletableFuture that resolves to the inclusion proof response from the aggregator. + */ + public CompletableFuture getInclusionProof(RequestId requestId) { + return this.client.getInclusionProof(requestId); + } + + /** + * Check if state is already spent for given request id. + * + * @param requestId request id + * @param trustBase root trust base + * @return A CompletableFuture that resolves to true if state is spent, false otherwise. */ - public CompletableFuture getTokenStatus( + public CompletableFuture isStateSpent(RequestId requestId, RootTrustBase trustBase) { + return this.getInclusionProof(requestId) + .thenApply(inclusionProof -> { + InclusionProofVerificationStatus result = inclusionProof.getInclusionProof().verify(requestId, trustBase); + switch (result) { + case OK: + return true; + case PATH_NOT_INCLUDED: + return false; + default: + throw new RuntimeException( + String.format("Inclusion proof verification failed with status %s", result) + ); + } + }); + } + + + /** + * Get inclusion proof for current token state. + * + * @param token token + * @param publicKey public key + * @param trustBase trustBase + * @return A CompletableFuture that resolves to the inclusion proof response from the aggregator. + */ + public CompletableFuture isStateSpent( Token token, byte[] publicKey, RootTrustBase trustBase ) { - RequestId requestId = RequestId.create(publicKey, token.getState().calculateHash()); - return this.client.getInclusionProof(requestId) - .thenApply(response -> response.getInclusionProof().verify(requestId, trustBase)); + Predicate predicate = PredicateEngineService.createPredicate(token.getState().getPredicate()); + if (!predicate.isOwner(publicKey)) { + throw new IllegalArgumentException("Given key is not owner of the token."); + } + + return this.isStateSpent(RequestId.create(publicKey, token.getState()), trustBase); } /** - * Retrieves the inclusion proof for a given commitment. + * Check if token id is already minted. * - * @param commitment The commitment for which to retrieve the inclusion proof. - * @return A CompletableFuture that resolves to the inclusion proof response from the aggregator. + * @param tokenId token id + * @param trustBase root trust base + * @return A CompletableFuture that resolves to true if token id is spent, false otherwise. */ - public CompletableFuture getInclusionProof(Commitment commitment) { - return this.client.getInclusionProof(commitment.getRequestId()); + public CompletableFuture isMinted(TokenId tokenId, RootTrustBase trustBase) { + return this.isStateSpent( + RequestId.create( + MintSigningService.create(tokenId).getPublicKey(), + MintTransactionState.create(tokenId) + ), + trustBase + ); } + + } diff --git a/src/main/java/org/unicitylabs/sdk/api/Authenticator.java b/src/main/java/org/unicitylabs/sdk/api/Authenticator.java index 1a5c9a2..97d106c 100644 --- a/src/main/java/org/unicitylabs/sdk/api/Authenticator.java +++ b/src/main/java/org/unicitylabs/sdk/api/Authenticator.java @@ -51,8 +51,12 @@ public static Authenticator create( DataHash transactionHash, DataHash stateHash ) { - return new Authenticator(signingService.getAlgorithm(), signingService.getPublicKey(), - signingService.sign(transactionHash), stateHash); + return new Authenticator( + signingService.getAlgorithm(), + signingService.getPublicKey(), + signingService.sign(transactionHash), + stateHash + ); } /** diff --git a/src/main/java/org/unicitylabs/sdk/api/RequestId.java b/src/main/java/org/unicitylabs/sdk/api/RequestId.java index dcd99a7..fabe147 100644 --- a/src/main/java/org/unicitylabs/sdk/api/RequestId.java +++ b/src/main/java/org/unicitylabs/sdk/api/RequestId.java @@ -7,6 +7,8 @@ import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.serializer.UnicityObjectMapper; import org.unicitylabs.sdk.serializer.json.JsonSerializationException; +import org.unicitylabs.sdk.token.TokenState; +import org.unicitylabs.sdk.transaction.MintTransaction; import org.unicitylabs.sdk.util.BitString; import org.unicitylabs.sdk.util.HexConverter; @@ -26,27 +28,38 @@ protected RequestId(DataHash hash) { } /** - * Creates a RequestId from a public key and state hash. + * Creates a RequestId from public key and state. * - * @param id The public key as a byte array. - * @param stateHash The state hash. - * @return A CompletableFuture resolving to a RequestId instance. + * @param publicKey public key as a byte array. + * @param state token state. + * @return request id + */ + public static RequestId create(byte[] publicKey, TokenState state) { + return RequestId.create(publicKey, state.calculateHash()); + } + + /** + * Creates a RequestId from public key and hash. + * + * @param publicKey public key as a byte array. + * @param hash hash. + * @return request id */ - public static RequestId create(byte[] id, DataHash stateHash) { - return createFromImprint(id, stateHash.getImprint()); + public static RequestId create(byte[] publicKey, DataHash hash) { + return RequestId.create(publicKey, hash.getImprint()); } /** - * Creates a RequestId from a public key and hash imprint. + * Creates a RequestId from identifier bytes and hash imprint. * - * @param id The public key as a byte array. - * @param hashImprint The hash imprint as a byte array. - * @return A CompletableFuture resolving to a RequestId instance. + * @param id id bytes. + * @param stateBytes state bytes. + * @return request id. */ - public static RequestId createFromImprint(byte[] id, byte[] hashImprint) { + public static RequestId create(byte[] id, byte[] stateBytes) { DataHasher hasher = new DataHasher(HashAlgorithm.SHA256); hasher.update(id); - hasher.update(hashImprint); + hasher.update(stateBytes); return new RequestId(hasher.digest()); } 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 09ba196..2258986 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,10 +1,8 @@ package org.unicitylabs.sdk.bft.verification.rule; +import com.google.common.primitives.UnsignedBytes; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.Arrays; - -import com.google.common.primitives.UnsignedBytes; import org.unicitylabs.sdk.bft.UnicityCertificate; import org.unicitylabs.sdk.bft.UnicityTreeCertificate; import org.unicitylabs.sdk.bft.verification.UnicityCertificateVerificationContext; 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 6829ded..53b9012 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePath.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreePath.java @@ -71,8 +71,8 @@ public MerkleTreePathVerificationResult verify(BigInteger requestId) { SparseMerkleTreePathStep step = this.steps.get(0); byte[] currentData; - BigInteger currentPath = BigInteger.ONE; - if (step.getPath().compareTo(BigInteger.ZERO) > 0) { + BigInteger currentPath = step.getPath(); + if (step.getPath().compareTo(BigInteger.ONE) > 0) { DataHash hash = new DataHasher(this.rootHash.getAlgorithm()) .update( CborSerializer.encodeArray( @@ -86,17 +86,15 @@ public MerkleTreePathVerificationResult verify(BigInteger requestId) { .digest(); currentData = hash.getData(); - - int length = step.getPath().bitLength() - 1; - currentPath = currentPath.shiftLeft(length) - .or(step.getPath().and(BigInteger.ONE.shiftLeft(length).subtract(BigInteger.ONE))); } else { + currentPath = BigInteger.ONE; currentData = step.getData().orElse(null); } + SparseMerkleTreePathStep previousStep = step; for (int i = 1; i < this.steps.size(); i++) { step = this.steps.get(i); - boolean isRight = currentPath.testBit(0); + boolean isRight = previousStep.getPath().testBit(0); byte[] left = isRight ? step.getData().orElse(null) : currentData; byte[] right = isRight ? currentData : step.getData().orElse(null); @@ -114,8 +112,12 @@ public MerkleTreePathVerificationResult verify(BigInteger requestId) { currentData = hash.getData(); int length = step.getPath().bitLength() - 1; + if (length < 0) { + return new MerkleTreePathVerificationResult(false, false); + } currentPath = currentPath.shiftLeft(length) .or(step.getPath().and(BigInteger.ONE.shiftLeft(length).subtract(BigInteger.ONE))); + previousStep = step; } boolean pathValid = currentData != null 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 fa9351e..76cba44 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreeRootNode.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/plain/SparseMerkleTreeRootNode.java @@ -13,7 +13,6 @@ */ public class SparseMerkleTreeRootNode { - private final BigInteger path = BigInteger.ONE; // Root path is always 0 private final FinalizedNodeBranch root; private SparseMerkleTreeRootNode(FinalizedNodeBranch root) { @@ -58,12 +57,12 @@ public boolean equals(Object o) { return false; } SparseMerkleTreeRootNode that = (SparseMerkleTreeRootNode) o; - return Objects.equals(this.path, that.path) && Objects.equals(this.root, that.root); + return Objects.equals(this.root, that.root); } @Override public int hashCode() { - return Objects.hash(this.path, this.root); + return Objects.hash(this.root); } private static List generatePath( @@ -83,16 +82,16 @@ private static List generatePath( || remainingPath.compareTo(BigInteger.ONE) == 0) { return List.of( new SparseMerkleTreePathStep( - BigInteger.ONE, - node.getRight() == null + BigInteger.ZERO, + node.getLeft() == null ? null - : node.getRight().getHash().getData() + : node.getLeft().getHash().getData() ), new SparseMerkleTreePathStep( node.getPath(), - node.getLeft() == null + node.getRight() == null ? null - : node.getLeft().getHash().getData() + : node.getRight().getHash().getData() ) ); } @@ -101,30 +100,26 @@ private static List generatePath( FinalizedBranch branch = isRight ? node.getRight() : node.getLeft(); FinalizedBranch siblingBranch = isRight ? node.getLeft() : node.getRight(); + SparseMerkleTreePathStep step = new SparseMerkleTreePathStep( + node.getPath(), + siblingBranch == null ? null : siblingBranch.getHash().getData() + ); + if (branch == null) { return List.of( new SparseMerkleTreePathStep( - BigInteger.ZERO, - node.getRight() == null - ? null - : node.getRight().getHash().getData()), - new SparseMerkleTreePathStep( - BigInteger.ONE, - node.getLeft() == null - ? null - : node.getLeft().getHash().getData()) + isRight ? BigInteger.ONE : BigInteger.ZERO, + null + ), + step ); } - ArrayList list = new ArrayList<>( + List list = new ArrayList<>( SparseMerkleTreeRootNode.generatePath(remainingPath, branch) ); - list.add( - new SparseMerkleTreePathStep( - parent.getPath(), - siblingBranch == null ? null : siblingBranch.getHash().getData() - ) - ); + + list.add(step); return List.copyOf(list); } 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 e3301dd..2093a7f 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedNodeBranch.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/FinalizedNodeBranch.java @@ -18,15 +18,18 @@ class FinalizedNodeBranch implements NodeBranch, FinalizedBranch { private final FinalizedBranch right; private final BigInteger counter; private final DataHash hash; - private final DataHash childrenHash; - private FinalizedNodeBranch(BigInteger path, FinalizedBranch left, FinalizedBranch right, - BigInteger counter, DataHash childrenHash, DataHash hash) { + private FinalizedNodeBranch( + BigInteger path, + FinalizedBranch left, + FinalizedBranch right, + BigInteger counter, + DataHash hash + ) { this.path = path; this.left = left; this.right = right; this.counter = counter; - this.childrenHash = childrenHash; this.hash = hash; } @@ -45,34 +48,26 @@ public static FinalizedNodeBranch create( 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(); + byte[] leftHash = left == null ? null : left.getHash().getData(); + byte[] rightHash = right == null ? null : right.getHash().getData(); + BigInteger leftCounter = left == null ? BigInteger.ZERO : left.getCounter(); + BigInteger rightCounter = right == null ? BigInteger.ZERO : right.getCounter(); - 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)) + CborSerializer.encodeOptional(leftHash, CborSerializer::encodeByteString), + CborSerializer.encodeByteString(BigIntegerConverter.encode(leftCounter)), + CborSerializer.encodeOptional(rightHash, CborSerializer::encodeByteString), + CborSerializer.encodeByteString(BigIntegerConverter.encode(rightCounter)) ) ) .digest(); - return new FinalizedNodeBranch(path, left, right, counter, childrenHash, hash); + BigInteger counter = leftCounter.add(rightCounter); + + return new FinalizedNodeBranch(path, left, right, counter, hash); } @Override @@ -95,15 +90,6 @@ 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; 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 cfa7111..9925ee4 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePath.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePath.java @@ -1,6 +1,7 @@ package org.unicitylabs.sdk.mtree.sum; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.math.BigInteger; @@ -9,12 +10,10 @@ 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.cbor.CborDeserializer; import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.serializer.json.BigIntegerAsStringSerializer; -import org.unicitylabs.sdk.serializer.json.LongAsStringSerializer; import org.unicitylabs.sdk.util.BigIntegerConverter; /** @@ -22,28 +21,29 @@ */ public class SparseMerkleSumTreePath { - private final Root root; + private final DataHash rootHash; private final List steps; @JsonCreator SparseMerkleSumTreePath( - @JsonProperty("root") Root root, + @JsonProperty("root") DataHash rootHash, @JsonProperty("steps") List steps ) { - Objects.requireNonNull(root, "root cannot be null"); + Objects.requireNonNull(rootHash, "root cannot be null"); Objects.requireNonNull(steps, "steps cannot be null"); - this.root = root; + this.rootHash = rootHash; this.steps = List.copyOf(steps); } /** - * Get root of the path. + * Get root hash. * - * @return root + * @return root hash */ - public Root getRoot() { - return this.root; + @JsonGetter("root") + public DataHash getRootHash() { + return this.rootHash; } /** @@ -62,73 +62,73 @@ public List getSteps() { * @return result of the verification */ public MerkleTreePathVerificationResult verify(BigInteger stateId) { - BigInteger currentPath = BigInteger.ONE; - DataHash currentHash = null; - BigInteger currentCounter = this.steps.isEmpty() - ? BigInteger.ZERO - : this.steps.get(0) - .getBranch() - .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; - - if (step.getBranch().isPresent()) { - byte[] bytes = i == 0 - ? step.getBranch().get().getValue() - : (currentHash != null ? currentHash.getImprint() : null); - 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))); - } + if (this.steps.size() == 0) { + return new MerkleTreePathVerificationResult(false, false); + } - boolean isRight = step.getPath().testBit(0); - byte[] sibling = step.getSibling() - .map( - data -> CborSerializer.encodeArray( - CborSerializer.encodeByteString(data.getValue()), - CborSerializer.encodeByteString(BigIntegerConverter.encode(data.getCounter())) + SparseMerkleSumTreePathStep step = this.steps.get(0); + byte[] currentData; + BigInteger currentPath = step.getPath(); + BigInteger currentSum = step.getValue(); + if (step.getPath().compareTo(BigInteger.ONE) > 0) { + DataHash hash = new DataHasher(this.rootHash.getAlgorithm()) + .update( + CborSerializer.encodeArray( + CborSerializer.encodeByteString(BigIntegerConverter.encode(step.getPath())), + CborSerializer.encodeOptional( + step.getData().orElse(null), + CborSerializer::encodeByteString + ), + CborSerializer.encodeByteString(BigIntegerConverter.encode(step.getValue())) ) - ).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) + ) + .digest(); + + currentData = hash.getData(); + } else { + currentPath = BigInteger.ONE; + currentData = step.getData().orElse(null); + } + + SparseMerkleSumTreePathStep previousStep = step; + for (int i = 1; i < this.steps.size(); i++) { + step = this.steps.get(i); + boolean isRight = previousStep.getPath().testBit(0); + + byte[] leftHash = isRight ? step.getData().orElse(null) : currentData; + byte[] rightHash = isRight ? currentData : step.getData().orElse(null); + BigInteger leftCounter = isRight ? step.getValue() : currentSum; + BigInteger rightCounter = isRight ? currentSum : step.getValue(); + + DataHash hash = new DataHasher(this.rootHash.getAlgorithm()) .update( CborSerializer.encodeArray( - isRight ? sibling : branch, - isRight ? branch : sibling + CborSerializer.encodeByteString(BigIntegerConverter.encode(step.getPath())), + CborSerializer.encodeOptional(leftHash, CborSerializer::encodeByteString), + CborSerializer.encodeByteString(BigIntegerConverter.encode(leftCounter)), + CborSerializer.encodeOptional(rightHash, CborSerializer::encodeByteString), + CborSerializer.encodeByteString(BigIntegerConverter.encode(rightCounter)) ) ) .digest(); - currentCounter = currentCounter.add( - step.getSibling() - .map(SparseMerkleSumTreePathStep.Branch::getCounter) - .orElse(BigInteger.ZERO) - ); + + currentData = hash.getData(); + + int length = step.getPath().bitLength() - 1; + if (length < 0) { + return new MerkleTreePathVerificationResult(false, false); + } + currentPath = currentPath.shiftLeft(length) + .or(step.getPath().and(BigInteger.ONE.shiftLeft(length).subtract(BigInteger.ONE))); + currentSum = currentSum.add(step.getValue()); + previousStep = step; } - return new MerkleTreePathVerificationResult( - this.root.hash.equals(currentHash) && this.root.counter.equals(currentCounter), - currentPath.equals(stateId)); + boolean pathValid = currentData != null + && this.rootHash.equals(new DataHash(this.rootHash.getAlgorithm(), currentData)); + boolean pathIncluded = currentPath.compareTo(stateId) == 0; + + return new MerkleTreePathVerificationResult(pathValid, pathIncluded); } /** @@ -141,7 +141,7 @@ public static SparseMerkleSumTreePath fromCbor(byte[] bytes) { List data = CborDeserializer.readArray(bytes); return new SparseMerkleSumTreePath( - SparseMerkleSumTreePath.Root.fromCbor(data.get(0)), + DataHash.fromCbor(data.get(0)), CborDeserializer.readArray(data.get(1)).stream() .map(SparseMerkleSumTreePathStep::fromCbor) .collect(Collectors.toList()) @@ -155,7 +155,7 @@ public static SparseMerkleSumTreePath fromCbor(byte[] bytes) { */ public byte[] toCbor() { return CborSerializer.encodeArray( - this.root.toCbor(), + this.rootHash.toCbor(), CborSerializer.encodeArray( this.steps.stream() .map(SparseMerkleSumTreePathStep::toCbor) @@ -170,17 +170,17 @@ public boolean equals(Object o) { return false; } SparseMerkleSumTreePath that = (SparseMerkleSumTreePath) o; - return Objects.equals(this.root, that.root) && Objects.equals(this.steps, that.steps); + return Objects.equals(this.rootHash, that.rootHash) && Objects.equals(this.steps, that.steps); } @Override public int hashCode() { - return Objects.hash(this.root, this.steps); + return Objects.hash(this.rootHash, this.steps); } @Override public String toString() { - return String.format("MerkleTreePath{root=%s, steps=%s}", this.root, this.steps); + return String.format("MerkleTreePath{rootHash=%s, steps=%s}", this.rootHash, this.steps); } /** 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 504d1cb..f4cd109 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStep.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStep.java @@ -2,7 +2,6 @@ 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.math.BigInteger; import java.util.Arrays; @@ -21,57 +20,21 @@ public class SparseMerkleSumTreePathStep { private final BigInteger path; - private final Branch sibling; - private final Branch branch; - - SparseMerkleSumTreePathStep(BigInteger path, FinalizedBranch sibling, - FinalizedLeafBranch branch) { - this( - path, - sibling, - branch == null - ? null - : new Branch(branch.getValue().getValue(), branch.getValue().getCounter()) - ); - } - - SparseMerkleSumTreePathStep(BigInteger path, FinalizedBranch sibling, - FinalizedNodeBranch branch) { - this( - path, - sibling, - branch == null ? null - : new Branch(branch.getChildrenHash().getImprint(), branch.getCounter()) - ); - } - - SparseMerkleSumTreePathStep(BigInteger path, FinalizedBranch sibling) { - this( - path, - sibling, - (Branch) null - ); - } - - SparseMerkleSumTreePathStep(BigInteger path, FinalizedBranch sibling, Branch branch) { - this( - path, - sibling == null ? null : new Branch(sibling.getHash().getImprint(), sibling.getCounter()), - branch - ); - } + private final byte[] data; + private final BigInteger value; @JsonCreator SparseMerkleSumTreePathStep( @JsonProperty("path") BigInteger path, - @JsonProperty("sibling") Branch sibling, - @JsonProperty("branch") Branch branch + @JsonProperty("data") byte[] data, + @JsonProperty("value") BigInteger value ) { Objects.requireNonNull(path, "path cannot be null"); + Objects.requireNonNull(value, "value cannot be null"); this.path = path; - this.sibling = sibling; - this.branch = branch; + this.data = data; + this.value = value; } /** @@ -85,21 +48,22 @@ public BigInteger getPath() { } /** - * Get sibling branch. + * Get data of step. * - * @return sibling branch + * @return data */ - public Optional getSibling() { - return Optional.ofNullable(this.sibling); + public Optional getData() { + return Optional.ofNullable(this.data); } /** - * Get branch at this step (can be null for non-leaf steps). + * Get value of step. * - * @return branch + * @return value */ - public Optional getBranch() { - return Optional.ofNullable(this.branch); + @JsonSerialize(using = BigIntegerAsStringSerializer.class) + public BigInteger getValue() { + return this.value; } /** @@ -113,8 +77,8 @@ public static SparseMerkleSumTreePathStep fromCbor(byte[] bytes) { return new SparseMerkleSumTreePathStep( BigIntegerConverter.decode(CborDeserializer.readByteString(data.get(0))), - Branch.fromCbor(data.get(1)), - Branch.fromCbor(data.get(2)) + CborDeserializer.readOptional(data.get(1), CborDeserializer::readByteString), + BigIntegerConverter.decode(CborDeserializer.readByteString(data.get(2))) ); } @@ -126,8 +90,8 @@ public static SparseMerkleSumTreePathStep fromCbor(byte[] 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) + CborSerializer.encodeOptional(this.data, CborSerializer::encodeByteString), + CborSerializer.encodeByteString(BigIntegerConverter.encode(this.value)) ); } @@ -137,105 +101,21 @@ public boolean equals(Object o) { return false; } SparseMerkleSumTreePathStep that = (SparseMerkleSumTreePathStep) o; - return Objects.equals(this.path, that.path) && Objects.equals(this.sibling, that.sibling) - && Objects.equals(this.branch, that.branch); + return Objects.equals(this.path, that.path) && Arrays.equals(this.data, that.data) + && Objects.equals(this.value, that.value); } @Override public int hashCode() { - return Objects.hash(this.path, this.sibling, this.branch); + return Objects.hash(this.path, Arrays.hashCode(this.data), this.value); } @Override public String toString() { - return String.format("MerkleTreePathStep{path=%s, sibling=%s, branch=%s}", - this.path.toString(2), this.sibling, this.branch); - } - - /** - * A branch in the sparse merkle sum tree. - */ - @JsonSerialize(using = SparseMerkleSumTreePathStepBranchJson.Serializer.class) - @JsonDeserialize(using = SparseMerkleSumTreePathStepBranchJson.Deserializer.class) - public static class Branch { - - private final byte[] value; - private final 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)) { - return false; - } - Branch branch = (Branch) o; - return Objects.deepEquals(this.value, branch.value) && Objects.equals(this.counter, - branch.counter); - } - - @Override - public int hashCode() { - return Objects.hash(Arrays.hashCode(this.value), this.counter); - } - - @Override - public String toString() { - return String.format( - "Branch{value=%s, counter=%s}", - this.value == null ? null : HexConverter.encode(this.value), - this.counter - ); - } + return String.format("MerkleTreePathStep{path=%s, data=%s, value=%s}", + this.path.toString(2), + this.data == null ? null : HexConverter.encode(this.data), + this.value + ); } } diff --git a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStepBranchJson.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStepBranchJson.java deleted file mode 100644 index a922ed1..0000000 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStepBranchJson.java +++ /dev/null @@ -1,106 +0,0 @@ -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; - -/** - * Sparse merkle tree path step serializer and deserializer implementation. - */ -public class SparseMerkleSumTreePathStepBranchJson { - - private SparseMerkleSumTreePathStepBranchJson() { - } - - /** - * Sparse merkle tree path step serializer. - */ - public static class Serializer extends StdSerializer { - - /** - * Create serializer. - */ - public Serializer() { - super(SparseMerkleSumTreePathStep.Branch.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(SparseMerkleSumTreePathStep.Branch value, JsonGenerator gen, - SerializerProvider serializers) - throws IOException { - gen.writeStartArray(); - gen.writeObject(value.getValue()); - gen.writeObject(value.getCounter().toString()); - gen.writeEndArray(); - } - } - - /** - * Sparse merkle tree path step deserializer. - */ - public static class Deserializer extends StdDeserializer { - - /** - * Create deserializer. - */ - public Deserializer() { - super(SparseMerkleSumTreePathStep.Branch.class); - } - - /** - * Deserialize sparse merkle tree path step branch. - * - * @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 SparseMerkleSumTreePathStep.Branch deserialize(JsonParser p, DeserializationContext ctx) - throws IOException { - if (p.currentToken() != JsonToken.START_ARRAY) { - throw MismatchedInputException.from( - p, - SparseMerkleSumTreePathStep.Branch.class, - "Expected start of array" - ); - } - - if (p.nextToken() == JsonToken.END_ARRAY) { - return new SparseMerkleSumTreePathStep.Branch(null, BigInteger.ZERO); - } - - SparseMerkleSumTreePathStep.Branch branch = new SparseMerkleSumTreePathStep.Branch( - p.readValueAs(byte[].class), - p.readValueAs(BigInteger.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/mtree/sum/SparseMerkleSumTreePathStepJson.java b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStepJson.java deleted file mode 100644 index ff4e0c0..0000000 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreePathStepJson.java +++ /dev/null @@ -1,162 +0,0 @@ -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 854a123..40522f8 100644 --- a/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreeRootNode.java +++ b/src/main/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreeRootNode.java @@ -1,32 +1,21 @@ package org.unicitylabs.sdk.mtree.sum; import java.math.BigInteger; +import java.util.ArrayList; 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.cbor.CborSerializer; -import org.unicitylabs.sdk.util.BigIntegerConverter; /** * Sparse Merkle Sum Tree root node. */ public class SparseMerkleSumTreeRootNode { - private final BigInteger path = BigInteger.ONE; // Root path is always 0 - private final FinalizedBranch left; - private final FinalizedBranch right; - private final Root root; + private final FinalizedNodeBranch root; - private SparseMerkleSumTreeRootNode( - FinalizedBranch left, FinalizedBranch right, Root root) { - this.left = left; - this.right = right; + private SparseMerkleSumTreeRootNode(FinalizedNodeBranch root) { this.root = root; } @@ -35,56 +24,39 @@ static SparseMerkleSumTreeRootNode create( 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); + return new SparseMerkleSumTreeRootNode( + FinalizedNodeBranch.create(BigInteger.ONE, left, right, hashAlgorithm) + ); + } + + /** + * Get root hash. + * + * @return root hash + */ + public DataHash getRootHash() { + return this.root.getHash(); } /** - * Get root of the tree. + * Get root value. * - * @return root + * @return root value */ - public Root getRoot() { - return this.root; + public BigInteger getValue() { + return this.root.getCounter(); } /** - * Get path from the leaf to root. + * Get merkle sum tree path for requested path. * - * @param path path to the leaf - * @return path to the root + * @param path path + * @return merkle tree path */ public SparseMerkleSumTreePath getPath(BigInteger path) { return new SparseMerkleSumTreePath( - this.root, - SparseMerkleSumTreeRootNode.generatePath(path, this.left, this.right) + this.root.getHash(), + SparseMerkleSumTreeRootNode.generatePath(path, this.root) ); } @@ -94,67 +66,82 @@ public boolean equals(Object o) { return false; } SparseMerkleSumTreeRootNode that = (SparseMerkleSumTreeRootNode) o; - return Objects.equals(this.path, that.path) && Objects.equals(this.left, that.left) - && Objects.equals(this.right, that.right); + return Objects.equals(this.root, that.root); } @Override public int hashCode() { - return Objects.hash(this.path, this.left, this.right); + return Objects.hash(this.root); } private static List generatePath( BigInteger remainingPath, - FinalizedBranch left, - FinalizedBranch right + FinalizedBranch parent ) { - boolean isRight = remainingPath.testBit(0); - FinalizedBranch branch = isRight ? right : left; - FinalizedBranch siblingBranch = isRight ? left : right; - - if (branch == null) { - return List.of(new SparseMerkleSumTreePathStep(remainingPath, siblingBranch)); + if (parent instanceof LeafBranch) { + LeafBranch leaf = (LeafBranch) parent; + return List.of(new SparseMerkleSumTreePathStep( + leaf.getPath(), + leaf.getValue().getValue(), + leaf.getValue().getCounter() + )); } - CommonPath commonPath = CommonPath.create(remainingPath, branch.getPath()); - if (branch.getPath().equals(commonPath.getPath())) { - if (branch instanceof FinalizedLeafBranch) { - return List.of( - new SparseMerkleSumTreePathStep( - branch.getPath(), - siblingBranch, - (FinalizedLeafBranch) branch)); - } - - FinalizedNodeBranch nodeBranch = (FinalizedNodeBranch) branch; - - if (remainingPath.shiftRight(commonPath.getLength()).compareTo(BigInteger.ONE) == 0) { - return List.of(new SparseMerkleSumTreePathStep(branch.getPath(), siblingBranch, nodeBranch)); - } - - return List.copyOf( - Stream.concat( - SparseMerkleSumTreeRootNode.generatePath( - remainingPath.shiftRight(commonPath.getLength()), - nodeBranch.getLeft(), - nodeBranch.getRight() - ) - .stream(), - Stream.of( - new SparseMerkleSumTreePathStep(branch.getPath(), siblingBranch, nodeBranch)) - ) - .collect(Collectors.toList()) + FinalizedNodeBranch node = (FinalizedNodeBranch) parent; + CommonPath commonPath = CommonPath.create(remainingPath, parent.getPath()); + remainingPath = remainingPath.shiftRight(commonPath.getLength()); + + if (commonPath.getPath().compareTo(parent.getPath()) != 0 + || remainingPath.compareTo(BigInteger.ONE) == 0) { + return List.of( + new SparseMerkleSumTreePathStep( + BigInteger.ZERO, + node.getLeft() == null + ? null + : node.getLeft().getHash().getData(), + node.getLeft() == null + ? BigInteger.ZERO + : node.getLeft().getCounter() + ), + new SparseMerkleSumTreePathStep( + node.getPath(), + node.getRight() == null + ? null + : node.getRight().getHash().getData(), + node.getRight() == null + ? BigInteger.ZERO + : node.getRight().getCounter() + ) ); } - if (branch instanceof FinalizedLeafBranch) { + boolean isRight = remainingPath.testBit(0); + FinalizedBranch branch = isRight ? node.getRight() : node.getLeft(); + FinalizedBranch siblingBranch = isRight ? node.getLeft() : node.getRight(); + + SparseMerkleSumTreePathStep step = new SparseMerkleSumTreePathStep( + node.getPath(), + siblingBranch == null ? null : siblingBranch.getHash().getData(), + siblingBranch == null ? BigInteger.ZERO : siblingBranch.getCounter() + ); + + if (branch == null) { return List.of( - new SparseMerkleSumTreePathStep(branch.getPath(), siblingBranch, - (FinalizedLeafBranch) branch)); + new SparseMerkleSumTreePathStep( + isRight ? BigInteger.ONE : BigInteger.ZERO, + null, + BigInteger.ZERO + ), + step + ); } - return List.of( - new SparseMerkleSumTreePathStep(branch.getPath(), siblingBranch, - (FinalizedNodeBranch) branch)); + List list = new ArrayList<>( + SparseMerkleSumTreeRootNode.generatePath(remainingPath, branch) + ); + + list.add(step); + + return List.copyOf(list); } } 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 4948a33..8d732e0 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/DefaultPredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/DefaultPredicate.java @@ -177,10 +177,7 @@ public boolean verify(Token token, TransferTransaction transaction, RootTrust return false; } - RequestId requestId = RequestId.create( - this.publicKey, - transaction.getData().getSourceState().calculateHash() - ); + RequestId requestId = RequestId.create(this.publicKey, transaction.getData().getSourceState()); return transaction.getInclusionProof().verify( requestId, trustBase diff --git a/src/main/java/org/unicitylabs/sdk/signing/MintSigningService.java b/src/main/java/org/unicitylabs/sdk/signing/MintSigningService.java new file mode 100644 index 0000000..cc33d2a --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/signing/MintSigningService.java @@ -0,0 +1,24 @@ +package org.unicitylabs.sdk.signing; + +import org.unicitylabs.sdk.token.TokenId; +import org.unicitylabs.sdk.util.HexConverter; + +/** + * Signing service for minting operations. + */ +public class MintSigningService { + private static final byte[] MINTER_SECRET = HexConverter.decode( + "495f414d5f554e4956455253414c5f4d494e5445525f464f525f"); + + private MintSigningService() {} + + /** + * Create signing service for minting operations. + * + * @param tokenId token identifier + * @return signing service + */ + public static SigningService create(TokenId tokenId) { + return SigningService.createFromMaskedSecret(MINTER_SECRET, tokenId.getBytes()); + } +} diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintCommitment.java b/src/main/java/org/unicitylabs/sdk/transaction/MintCommitment.java index abf7b6f..03e9297 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintCommitment.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintCommitment.java @@ -6,9 +6,8 @@ import java.util.Objects; import org.unicitylabs.sdk.api.Authenticator; import org.unicitylabs.sdk.api.RequestId; -import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.signing.MintSigningService; import org.unicitylabs.sdk.signing.SigningService; -import org.unicitylabs.sdk.util.HexConverter; /** * Commitment representing a submitted transaction. @@ -17,9 +16,6 @@ */ public class MintCommitment extends Commitment> { - private static final byte[] MINTER_SECRET = HexConverter.decode( - "495f414d5f554e4956455253414c5f4d494e5445525f464f525f"); - @JsonCreator private MintCommitment( @JsonProperty("requestId") @@ -46,41 +42,24 @@ public MintTransaction toTransaction(InclusionProof inclusionProof) { /** * Create mint commitment from transaction data. * - * @param transactionData mint transaction data + * @param data mint transaction data * @param mint reason * @return mint commitment */ public static MintCommitment create( - MintTransaction.Data transactionData + MintTransaction.Data data ) { - Objects.requireNonNull(transactionData, "Transaction data cannot be null"); - - SigningService signingService = MintCommitment.createSigningService(transactionData); - - DataHash transactionHash = transactionData.calculateHash(); + Objects.requireNonNull(data, "Transaction data cannot be null"); - RequestId requestId = RequestId.create( - signingService.getPublicKey(), - transactionData.getSourceState() + SigningService signingService = MintSigningService.create(data.getTokenId()); + return new MintCommitment<>( + RequestId.create(signingService.getPublicKey(), data.getSourceState()), + data, + Authenticator.create( + signingService, + data.calculateHash(), + data.getSourceState() + ) ); - Authenticator authenticator = Authenticator.create( - signingService, - transactionHash, - transactionData.getSourceState() - ); - - return new MintCommitment<>(requestId, transactionData, authenticator); - } - - - /** - * 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/MintTransaction.java b/src/main/java/org/unicitylabs/sdk/transaction/MintTransaction.java index 10e199a..e80e9ed 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintTransaction.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintTransaction.java @@ -22,6 +22,7 @@ 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.MintSigningService; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenType; @@ -97,7 +98,7 @@ public VerificationResult verify(RootTrustBase trustBase) { return VerificationResult.fail("Invalid source state"); } - SigningService signingService = MintCommitment.createSigningService(this.getData()); + SigningService signingService = MintSigningService.create(this.getData().getTokenId()); if (!Arrays.equals(signingService.getPublicKey(), this.getInclusionProof().getAuthenticator().get().getPublicKey())) { return VerificationResult.fail("Authenticator public key mismatch"); @@ -117,12 +118,17 @@ public VerificationResult verify(RootTrustBase trustBase) { } InclusionProofVerificationStatus inclusionProofStatus = this.getInclusionProof().verify( - RequestId.create(signingService.getPublicKey(), this.getData().getSourceState()), + RequestId.create( + MintSigningService.create(this.getData().getTokenId()).getPublicKey(), + this.getData().getSourceState() + ), trustBase ); if (inclusionProofStatus != InclusionProofVerificationStatus.OK) { - return VerificationResult.fail("Inclusion proof verification failed"); + return VerificationResult.fail( + String.format("Inclusion proof verification failed with status %s", inclusionProofStatus) + ); } return VerificationResult.success(); diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionReasonJson.java b/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionReasonJson.java index dcb5c57..87eea9e 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionReasonJson.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionReasonJson.java @@ -1,17 +1,9 @@ package org.unicitylabs.sdk.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.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.sum.SparseMerkleSumTreePathStep; -import org.unicitylabs.sdk.mtree.sum.SparseMerkleSumTreePathStep.Branch; import org.unicitylabs.sdk.transaction.split.SplitMintReason; /** diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionState.java b/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionState.java index 9cb2c5c..cb11459 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionState.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionState.java @@ -24,6 +24,6 @@ private MintTransactionState(DataHash hash) { * @return mint state */ public static MintTransactionState create(TokenId tokenId) { - return new MintTransactionState(RequestId.createFromImprint(tokenId.getBytes(), MINT_SUFFIX)); + return new MintTransactionState(RequestId.create(tokenId.getBytes(), MINT_SUFFIX)); } } diff --git a/src/main/java/org/unicitylabs/sdk/transaction/Transaction.java b/src/main/java/org/unicitylabs/sdk/transaction/Transaction.java index 21c63c6..9336960 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/Transaction.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/Transaction.java @@ -40,7 +40,7 @@ public abstract class Transaction> { * @return transaction data */ public T getData() { - return data; + return this.data; } /** @@ -49,7 +49,7 @@ public T getData() { * @return inclusion proof */ public InclusionProof getInclusionProof() { - return inclusionProof; + return this.inclusionProof; } /** diff --git a/src/main/java/org/unicitylabs/sdk/transaction/TransferCommitment.java b/src/main/java/org/unicitylabs/sdk/transaction/TransferCommitment.java index 4784fc9..5e9396f 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/TransferCommitment.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/TransferCommitment.java @@ -63,16 +63,21 @@ public static TransferCommitment create( Objects.requireNonNull(salt, "Salt cannot be null"); Objects.requireNonNull(signingService, "SigningService cannot be null"); - TransferTransaction.Data transactionData = new TransferTransaction.Data( - token.getState(), recipient, salt, recipientDataHash, message, token.getNametags()); + TransferTransaction.Data data = new TransferTransaction.Data( + token.getState(), + recipient, + salt, + recipientDataHash, + message, + token.getNametags() + ); + RequestId requestId = RequestId.create(signingService.getPublicKey(), data.getSourceState()); + Authenticator authenticator = Authenticator.create( + signingService, + data.calculateHash(), + data.getSourceState().calculateHash() + ); - DataHash sourceStateHash = transactionData.getSourceState().calculateHash(); - DataHash transactionHash = transactionData.calculateHash(); - - RequestId requestId = RequestId.create(signingService.getPublicKey(), sourceStateHash); - Authenticator authenticator = Authenticator.create(signingService, transactionHash, - sourceStateHash); - - return new TransferCommitment(requestId, transactionData, authenticator); + return new TransferCommitment(requestId, data, authenticator); } } diff --git a/src/main/java/org/unicitylabs/sdk/transaction/TransferTransaction.java b/src/main/java/org/unicitylabs/sdk/transaction/TransferTransaction.java index 8992c73..d611143 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/TransferTransaction.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/TransferTransaction.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -26,7 +25,6 @@ import org.unicitylabs.sdk.token.TokenState; import org.unicitylabs.sdk.util.HexConverter; import org.unicitylabs.sdk.verification.VerificationResult; -import org.unicitylabs.sdk.verification.VerificationResultCode; /** * Token transfer transaction. 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 f3ba808..89b5f39 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReason.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReason.java @@ -13,7 +13,6 @@ 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; @@ -115,14 +114,13 @@ public VerificationResult verify(MintTransaction transaction) { List aggregationPathSteps = proof.getAggregationPath() .getSteps(); if (aggregationPathSteps.size() == 0 - || !Arrays.equals(proof.getCoinTreePath().getRoot().getHash().getImprint(), + || !Arrays.equals(proof.getCoinTreePath().getRootHash().getImprint(), aggregationPathSteps.get(0).getData().orElse(null)) ) { return VerificationResult.fail("Coin tree root does not match aggregation path leaf."); } - if (!proof.getCoinTreePath().getSteps().get(0).getBranch() - .map(Branch::getCounter).equals(Optional.ofNullable(coins.get(proof.getCoinId())))) { + if (!proof.getCoinTreePath().getSteps().get(0).getValue().equals(coins.get(proof.getCoinId()))) { return VerificationResult.fail("Coin amount in token does not match coin tree leaf."); } 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 1cdeea8..b4c97a2 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/split/TokenSplitBuilder.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/split/TokenSplitBuilder.java @@ -109,15 +109,15 @@ public TokenSplit build(Token token) throws LeafOutOfBoundsException, BranchE BigInteger coinsInToken = Optional.ofNullable(tokenCoins.get(tree.getKey())) .orElse(BigInteger.ZERO); SparseMerkleSumTreeRootNode root = tree.getValue().calculateRoot(); - if (root.getRoot().getCounter().compareTo(coinsInToken) != 0) { + if (root.getValue().compareTo(coinsInToken) != 0) { throw new IllegalArgumentException( String.format("Token contained %s %s coins, but tree has %s", - coinsInToken, tree.getKey(), root.getRoot().getCounter())); + coinsInToken, tree.getKey(), root.getValue())); } coinRoots.put(tree.getKey(), root); aggregationTree.addLeaf(tree.getKey().toBitString().toBigInteger(), - root.getRoot().getHash().getImprint()); + root.getRootHash().getImprint()); } return new TokenSplit( diff --git a/src/main/java/org/unicitylabs/sdk/util/InclusionProofUtils.java b/src/main/java/org/unicitylabs/sdk/util/InclusionProofUtils.java index a1e758f..fb40735 100644 --- a/src/main/java/org/unicitylabs/sdk/util/InclusionProofUtils.java +++ b/src/main/java/org/unicitylabs/sdk/util/InclusionProofUtils.java @@ -83,21 +83,22 @@ private static void checkInclusionProof( future.completeExceptionally(new TimeoutException("Timeout waiting for inclusion proof")); } - client.getInclusionProof(commitment).thenAccept(response -> { + client.getInclusionProof(commitment.getRequestId()).thenAccept(response -> { InclusionProofVerificationStatus status = response.getInclusionProof() .verify(commitment.getRequestId(), trustBase); - if (status == InclusionProofVerificationStatus.OK) { - future.complete(response.getInclusionProof()); - } - - if (status == InclusionProofVerificationStatus.PATH_NOT_INCLUDED) { - CompletableFuture.delayedExecutor(intervalMillis, TimeUnit.MILLISECONDS) - .execute(() -> checkInclusionProof(client, trustBase, commitment, future, startTime, - timeoutMillis, - intervalMillis)); - } else { - future.completeExceptionally( - new RuntimeException(String.format("Inclusion proof verification failed: %s", status))); + switch (status) { + case OK: + future.complete(response.getInclusionProof()); + break; + case PATH_NOT_INCLUDED: + CompletableFuture.delayedExecutor(intervalMillis, TimeUnit.MILLISECONDS) + .execute(() -> checkInclusionProof(client, trustBase, commitment, future, startTime, + timeoutMillis, + intervalMillis)); + break; + default: + future.completeExceptionally( + new RuntimeException(String.format("Inclusion proof verification failed: %s", status))); } }).exceptionally(e -> { future.completeExceptionally(e); diff --git a/src/test/java/org/unicitylabs/sdk/TestAggregatorClient.java b/src/test/java/org/unicitylabs/sdk/TestAggregatorClient.java index 6dfd192..3fca876 100644 --- a/src/test/java/org/unicitylabs/sdk/TestAggregatorClient.java +++ b/src/test/java/org/unicitylabs/sdk/TestAggregatorClient.java @@ -34,9 +34,11 @@ public TestAggregatorClient(SigningService signingService) { @Override - public CompletableFuture submitCommitment(RequestId requestId, - DataHash transactionHash, Authenticator authenticator) { - + public CompletableFuture submitCommitment( + RequestId requestId, + DataHash transactionHash, + Authenticator authenticator + ) { try { tree.addLeaf( requestId.toBitString().toBigInteger(), @@ -61,8 +63,8 @@ public CompletableFuture getInclusionProof(RequestId req new InclusionProofResponse( InclusionProofFixture.create( root.getPath(requestId.toBitString().toBigInteger()), - entry.getKey(), - entry.getValue(), + entry != null ? entry.getKey() : null, + entry != null ? entry.getValue() : null, UnicityCertificateUtils.generateCertificate(signingService, root.getRootHash()) ) ) diff --git a/src/test/java/org/unicitylabs/sdk/common/CommonTestFlow.java b/src/test/java/org/unicitylabs/sdk/common/CommonTestFlow.java index ba760a0..56c2b2b 100644 --- a/src/test/java/org/unicitylabs/sdk/common/CommonTestFlow.java +++ b/src/test/java/org/unicitylabs/sdk/common/CommonTestFlow.java @@ -15,6 +15,7 @@ import org.unicitylabs.sdk.StateTransitionClient; import org.unicitylabs.sdk.address.DirectAddress; import org.unicitylabs.sdk.address.ProxyAddress; +import org.unicitylabs.sdk.api.RequestId; import org.unicitylabs.sdk.api.SubmitCommitmentResponse; import org.unicitylabs.sdk.api.SubmitCommitmentStatus; import org.unicitylabs.sdk.bft.RootTrustBase; @@ -26,6 +27,7 @@ import org.unicitylabs.sdk.predicate.embedded.MaskedPredicateReference; import org.unicitylabs.sdk.predicate.embedded.UnmaskedPredicate; import org.unicitylabs.sdk.predicate.embedded.UnmaskedPredicateReference; +import org.unicitylabs.sdk.signing.MintSigningService; import org.unicitylabs.sdk.signing.SigningService; import org.unicitylabs.sdk.token.Token; import org.unicitylabs.sdk.token.TokenId; @@ -34,6 +36,7 @@ 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.InclusionProofVerificationStatus; import org.unicitylabs.sdk.transaction.MintCommitment; import org.unicitylabs.sdk.transaction.MintTransaction; import org.unicitylabs.sdk.transaction.TransferCommitment; @@ -67,6 +70,7 @@ public void testTransferFlow() throws Exception { ALICE_SECRET ); + assertTrue(this.client.isMinted(aliceToken.getId(), this.trustBase).get()); assertTrue(aliceToken.verify(this.trustBase).isSuccessful()); String bobNameTag = UUID.randomUUID().toString(); @@ -77,16 +81,17 @@ public void testTransferFlow() throws Exception { DataHash bobDataHash = new DataHasher(HashAlgorithm.SHA256).update(bobStateData).digest(); // Submit transfer transaction + SigningService aliceSigningService = SigningService.createFromMaskedSecret( + ALICE_SECRET, + ((MaskedPredicate) aliceToken.getState().getPredicate()).getNonce() + ); TransferCommitment aliceToBobTransferCommitment = TransferCommitment.create( aliceToken, ProxyAddress.create(bobNameTag), randomBytes(32), bobDataHash, null, - SigningService.createFromMaskedSecret( - ALICE_SECRET, - ((MaskedPredicate) aliceToken.getState().getPredicate()).getNonce() - ) + aliceSigningService ); SubmitCommitmentResponse aliceToBobTransferSubmitResponse = this.client.submitCommitment( aliceToBobTransferCommitment @@ -109,6 +114,9 @@ public void testTransferFlow() throws Exception { aliceToBobTransferInclusionProof ); + // Check if alice token is spent + assertTrue(this.client.isStateSpent(aliceToken, aliceSigningService.getPublicKey(), this.trustBase).get()); + // Bob prepares to receive the token DirectAddress bobAddress = UnmaskedPredicateReference.create( aliceToken.getType(), @@ -200,7 +208,7 @@ public void testTransferFlow() throws Exception { bobToken, new TokenState(carolPredicate, null), bobToCarolTransaction - ); + ); assertTrue(carolToken.verify(this.trustBase).isSuccessful()); assertEquals(2, carolToken.getTransactions().size()); diff --git a/src/test/java/org/unicitylabs/sdk/e2e/BasicE2ETest.java b/src/test/java/org/unicitylabs/sdk/e2e/BasicE2ETest.java index 99b000d..2469706 100644 --- a/src/test/java/org/unicitylabs/sdk/e2e/BasicE2ETest.java +++ b/src/test/java/org/unicitylabs/sdk/e2e/BasicE2ETest.java @@ -1,5 +1,6 @@ package org.unicitylabs.sdk.e2e; +import org.junit.jupiter.api.Disabled; import org.unicitylabs.sdk.api.JsonRpcAggregatorClient; import org.unicitylabs.sdk.api.Authenticator; import org.unicitylabs.sdk.api.RequestId; @@ -23,21 +24,9 @@ */ @Tag("integration") @EnabledIfEnvironmentVariable(named = "AGGREGATOR_URL", matches = ".+") +@Disabled("Skip performance tests") public class BasicE2ETest { - @Test - void testVerifyBlockHeight() throws Exception { - String aggregatorUrl = System.getenv("AGGREGATOR_URL"); - assertNotNull(aggregatorUrl, "AGGREGATOR_URL environment variable must be set"); - - JsonRpcAggregatorClient aggregatorClient = new JsonRpcAggregatorClient(aggregatorUrl); - Long blockHeight = aggregatorClient.getBlockHeight().get(); - - System.out.println("block height: " + blockHeight); - assertNotNull(blockHeight); - assertTrue(blockHeight > 0); - } - @Test void testCommitmentPerformance() throws Exception { String aggregatorUrl = System.getenv("AGGREGATOR_URL"); @@ -54,7 +43,7 @@ void testCommitmentPerformance() throws Exception { DataHash stateHash = new DataHasher(HashAlgorithm.SHA256).update(stateBytes).digest(); DataHash txDataHash = new DataHasher(HashAlgorithm.SHA256).update("test commitment performance".getBytes()).digest(); SigningService signingService = SigningService.createFromSecret(randomSecret); - RequestId requestId = RequestId.createFromImprint(signingService.getPublicKey(), stateHash.getImprint()); + RequestId requestId = RequestId.create(signingService.getPublicKey(), stateHash.getImprint()); Authenticator auth = Authenticator.create(signingService, txDataHash, stateHash); SubmitCommitmentResponse response = aggregatorClient.submitCommitment(requestId, txDataHash, auth).get(); @@ -99,7 +88,7 @@ void testCommitmentPerformanceMultiThreaded() throws Exception { DataHash stateHash = new DataHasher(HashAlgorithm.SHA256).update(stateBytes).digest(); DataHash txDataHash = new DataHasher(HashAlgorithm.SHA256).update(txData).digest(); SigningService signingService = SigningService.createFromSecret(randomSecret); - RequestId requestId = RequestId.createFromImprint(signingService.getPublicKey(), stateHash.getImprint()); + RequestId requestId = RequestId.create(signingService.getPublicKey(), stateHash.getImprint()); Authenticator auth = Authenticator.create(signingService, txDataHash, stateHash); SubmitCommitmentResponse response = aggregatorClient.submitCommitment(requestId, txDataHash, auth).get(); return response.getStatus() == SubmitCommitmentStatus.SUCCESS; diff --git a/src/test/java/org/unicitylabs/sdk/mtree/plain/MerkleTreePathTest.java b/src/test/java/org/unicitylabs/sdk/mtree/plain/MerkleTreePathTest.java index 8ced22a..de9210c 100644 --- a/src/test/java/org/unicitylabs/sdk/mtree/plain/MerkleTreePathTest.java +++ b/src/test/java/org/unicitylabs/sdk/mtree/plain/MerkleTreePathTest.java @@ -94,4 +94,15 @@ public void testShouldVerifyInclusionProof() { Assertions.assertEquals(new MerkleTreePathVerificationResult(true, false), path.verify(BigInteger.valueOf(0b111))); } + + @Test + public void testEmptyPathVerification() throws JsonProcessingException { + SparseMerkleTreePath path = UnicityObjectMapper.JSON.readValue( + "{\"root\":\"00001e54402898172f2948615fb17627733abbd120a85381c624ad060d28321be672\",\"steps\":[{\"path\":\"1\",\"data\":null},{\"path\":\"1\",\"data\":null}]}", + SparseMerkleTreePath.class); + + MerkleTreePathVerificationResult result = path.verify(BigInteger.valueOf(101)); + Assertions.assertTrue(result.isPathValid()); + Assertions.assertFalse(result.isPathIncluded()); + } } 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 647d093..c54a3c1 100644 --- a/src/test/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreeTest.java +++ b/src/test/java/org/unicitylabs/sdk/mtree/sum/SparseMerkleSumTreeTest.java @@ -26,7 +26,7 @@ void shouldBuildTreeWithNumericValues() throws Exception { } var root = tree.calculateRoot(); - Assertions.assertEquals(BigInteger.valueOf(100), root.getRoot().getCounter()); + Assertions.assertEquals(BigInteger.valueOf(100), root.getValue()); for (var entry : leaves.entrySet()) { var path = root.getPath(entry.getKey()); @@ -35,17 +35,20 @@ void shouldBuildTreeWithNumericValues() throws Exception { Assertions.assertTrue(verificationResult.isPathValid()); Assertions.assertTrue(verificationResult.isSuccessful()); - Assertions.assertEquals(root.getRoot().getCounter(), path.getRoot().getCounter()); - Assertions.assertEquals(root.getRoot(), path.getRoot()); - Assertions.assertArrayEquals(entry.getValue().getValue(), - path.getSteps().get(0).getBranch().get().getValue()); - Assertions.assertEquals(entry.getValue().getCounter(), - path.getSteps().get(0).getBranch().get().getCounter()); + Assertions.assertEquals(root.getRootHash(), path.getRootHash()); + Assertions.assertArrayEquals( + entry.getValue().getValue(), + path.getSteps().get(0).getData().orElse(null) + ); + Assertions.assertEquals( + entry.getValue().getCounter(), + path.getSteps().get(0).getValue() + ); } tree.addLeaf(new BigInteger("1110", 2), new LeafValue(new byte[32], BigInteger.valueOf(100))); root = tree.calculateRoot(); - Assertions.assertEquals(BigInteger.valueOf(200), root.getRoot().getCounter()); + Assertions.assertEquals(BigInteger.valueOf(200), root.getValue()); } @Test diff --git a/src/test/java/org/unicitylabs/sdk/utils/TestUtils.java b/src/test/java/org/unicitylabs/sdk/utils/TestUtils.java index d21a4af..bc35e1f 100644 --- a/src/test/java/org/unicitylabs/sdk/utils/TestUtils.java +++ b/src/test/java/org/unicitylabs/sdk/utils/TestUtils.java @@ -13,12 +13,9 @@ import org.unicitylabs.sdk.api.SubmitCommitmentResponse; import org.unicitylabs.sdk.api.SubmitCommitmentStatus; import org.unicitylabs.sdk.bft.RootTrustBase; -import org.unicitylabs.sdk.e2e.config.CucumberConfiguration; -import org.unicitylabs.sdk.e2e.context.TestContext; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.DataHasher; import org.unicitylabs.sdk.hash.HashAlgorithm; -import org.unicitylabs.sdk.predicate.PredicateEngine; import org.unicitylabs.sdk.predicate.PredicateEngineService; import org.unicitylabs.sdk.predicate.embedded.MaskedPredicate; import org.unicitylabs.sdk.signing.SigningService; @@ -246,7 +243,7 @@ public static boolean validateTokenOwnership(Token token, SigningService signing } public static RequestId createRequestId(SigningService signingService, DataHash stateHash) { - return RequestId.createFromImprint(signingService.getPublicKey(), stateHash.getImprint()); + return RequestId.create(signingService.getPublicKey(), stateHash); } public static Authenticator createAuthenticator(SigningService signingService, DataHash txDataHash, DataHash stateHash) {