From 725d995f8b7bf9c67e9e9307087c6d5a97808b13 Mon Sep 17 00:00:00 2001 From: Martti Marran Date: Tue, 2 Dec 2025 01:56:05 +0200 Subject: [PATCH 1/2] Add user defined mint reasons --- .../sdk/StateTransitionClient.java | 50 +++--- .../unicitylabs/sdk/address/ProxyAddress.java | 8 +- .../sdk/api/JsonRpcAggregatorClient.java | 9 +- .../unicitylabs/sdk/predicate/Predicate.java | 2 +- .../sdk/predicate/embedded/BurnPredicate.java | 2 +- .../predicate/embedded/DefaultPredicate.java | 2 +- .../predicate/embedded/MaskedPredicate.java | 9 +- .../predicate/embedded/UnmaskedPredicate.java | 28 +-- .../java/org/unicitylabs/sdk/token/Token.java | 122 ++++++++------ .../transaction/DefaultMintReasonFactory.java | 45 +++++ .../sdk/transaction/MintCommitment.java | 21 +-- .../sdk/transaction/MintReasonFactory.java | 15 ++ .../sdk/transaction/MintReasonType.java | 11 -- .../sdk/transaction/MintTransaction.java | 159 ++++++++++++++---- .../transaction/MintTransactionReason.java | 10 +- .../sdk/transaction/TransactionData.java | 7 + .../sdk/transaction/TransferCommitment.java | 2 +- .../sdk/transaction/TransferTransaction.java | 11 +- .../transaction/split/SplitMintReason.java | 36 ++-- .../transaction/split/TokenSplitBuilder.java | 16 +- .../sdk/common/BaseEscrowSwapTest.java | 44 +++-- .../sdk/common/CommonTestFlow.java | 56 +++--- .../sdk/common/split/BaseTokenSplitTest.java | 34 ++-- .../sdk/e2e/context/TestContext.java | 2 +- .../e2e/steps/AdvancedStepDefinitions.java | 24 ++- .../sdk/e2e/steps/StepDefinitions.java | 50 ++++-- .../steps/shared/SharedStepDefinitions.java | 25 ++- .../sdk/e2e/steps/shared/StepHelper.java | 21 ++- ...nedPredicateDoubleSpendPreventionTest.java | 24 ++- .../sdk/token/TokenSplitBuilderTest.java | 70 ++++---- .../org/unicitylabs/sdk/token/TokenTest.java | 10 +- .../sdk/transaction/CommitmentTest.java | 7 +- .../transaction/MintTransactionFixture.java | 6 +- .../org/unicitylabs/sdk/utils/TestUtils.java | 45 ++--- .../org/unicitylabs/sdk/utils/TokenUtils.java | 30 ++-- 35 files changed, 629 insertions(+), 384 deletions(-) create mode 100644 src/main/java/org/unicitylabs/sdk/transaction/DefaultMintReasonFactory.java create mode 100644 src/main/java/org/unicitylabs/sdk/transaction/MintReasonFactory.java delete mode 100644 src/main/java/org/unicitylabs/sdk/transaction/MintReasonType.java diff --git a/src/main/java/org/unicitylabs/sdk/StateTransitionClient.java b/src/main/java/org/unicitylabs/sdk/StateTransitionClient.java index 6949a26..d4f892b 100644 --- a/src/main/java/org/unicitylabs/sdk/StateTransitionClient.java +++ b/src/main/java/org/unicitylabs/sdk/StateTransitionClient.java @@ -5,9 +5,9 @@ import java.util.Objects; import java.util.concurrent.CompletableFuture; import org.unicitylabs.sdk.api.AggregatorClient; +import org.unicitylabs.sdk.api.CertificationResponse; import org.unicitylabs.sdk.api.InclusionProofResponse; import org.unicitylabs.sdk.api.StateId; -import org.unicitylabs.sdk.api.CertificationResponse; import org.unicitylabs.sdk.bft.RootTrustBase; import org.unicitylabs.sdk.predicate.Predicate; import org.unicitylabs.sdk.predicate.PredicateEngineService; @@ -17,7 +17,7 @@ import org.unicitylabs.sdk.token.TokenState; import org.unicitylabs.sdk.transaction.InclusionProofVerificationStatus; import org.unicitylabs.sdk.transaction.MintCommitment; -import org.unicitylabs.sdk.transaction.MintTransactionReason; +import org.unicitylabs.sdk.transaction.MintReasonFactory; import org.unicitylabs.sdk.transaction.MintTransactionState; import org.unicitylabs.sdk.transaction.TransferCommitment; import org.unicitylabs.sdk.transaction.TransferTransaction; @@ -46,11 +46,9 @@ public StateTransitionClient(AggregatorClient client) { * 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) { + public CompletableFuture submitCommitment(MintCommitment commitment) { return this.client.submitCertificationRequest(commitment.getCertificationData(), false); } @@ -77,45 +75,47 @@ public CompletableFuture submitCommitment(TransferCommitm /** * 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. + * @param trustBase The root trust base for inclusion proof verification. + * @param mintReasonFactory factory to create mint transaction reasons + * @param token The token to be updated. + * @param state The current state of the token. + * @param transaction The transaction containing transfer data. * @return The updated token after applying the transaction. * @throws VerificationException if verification fails during the update process. */ - public Token finalizeTransaction( + public Token finalizeTransaction( RootTrustBase trustBase, - Token token, + MintReasonFactory mintReasonFactory, + Token token, TokenState state, TransferTransaction transaction ) throws VerificationException { - return this.finalizeTransaction(trustBase, token, state, transaction, List.of()); + return this.finalizeTransaction(trustBase, mintReasonFactory, token, state, transaction, List.of()); } /** * 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. + * @param trustBase The root trust base for inclusion proof verification. + * @param mintReasonFactory factory to create mint transaction reasons + * @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. * @return The updated token after applying the transaction. * @throws VerificationException if verification fails during the update process. */ - public Token finalizeTransaction( + public Token finalizeTransaction( RootTrustBase trustBase, - Token token, + MintReasonFactory mintReasonFactory, + Token token, TokenState state, TransferTransaction transaction, - List> nametags + List nametags ) throws VerificationException { Objects.requireNonNull(token, "Token is null"); - return token.update(trustBase, state, transaction, nametags); + return token.update(trustBase, mintReasonFactory, state, transaction, nametags); } /** @@ -131,7 +131,7 @@ public CompletableFuture getInclusionProof(StateId state /** * Check if state is already spent for given state id. * - * @param stateId state id + * @param stateId state id * @param trustBase root trust base * @return A CompletableFuture that resolves to true if state is spent, false otherwise. */ @@ -162,7 +162,7 @@ public CompletableFuture isStateSpent(StateId stateId, RootTrustBase tr * @return A CompletableFuture that resolves to the inclusion proof response from the aggregator. */ public CompletableFuture isStateSpent( - Token token, + Token token, byte[] publicKey, RootTrustBase trustBase ) { diff --git a/src/main/java/org/unicitylabs/sdk/address/ProxyAddress.java b/src/main/java/org/unicitylabs/sdk/address/ProxyAddress.java index e10a8ff..bde37fc 100644 --- a/src/main/java/org/unicitylabs/sdk/address/ProxyAddress.java +++ b/src/main/java/org/unicitylabs/sdk/address/ProxyAddress.java @@ -67,9 +67,9 @@ public String getAddress() { * @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) { + public static Address resolve(Address inputAddress, List nametags) { + Map nametagMap = new HashMap<>(); + for (Token token : nametags) { if (token == null) { throw new IllegalArgumentException("Nametag tokens list cannot contain null elements"); } @@ -84,7 +84,7 @@ public static Address resolve(Address inputAddress, List> nametags) { Address targetAddress = inputAddress; while (targetAddress.getScheme() != AddressScheme.DIRECT) { - Token nametag = nametagMap.get(targetAddress); + Token nametag = nametagMap.get(targetAddress); if (nametag == null || !nametag.getData().isPresent()) { return null; } diff --git a/src/main/java/org/unicitylabs/sdk/api/JsonRpcAggregatorClient.java b/src/main/java/org/unicitylabs/sdk/api/JsonRpcAggregatorClient.java index 82f7f3f..c7187f8 100644 --- a/src/main/java/org/unicitylabs/sdk/api/JsonRpcAggregatorClient.java +++ b/src/main/java/org/unicitylabs/sdk/api/JsonRpcAggregatorClient.java @@ -37,11 +37,18 @@ public JsonRpcAggregatorClient(String url, String apiKey) { this.apiKey = apiKey; } + /** + * Submit certification request. + * + * @param certificationData certification data + * @param receipt whether to request a receipt + * @return certification response + */ public CompletableFuture submitCertificationRequest( CertificationData certificationData, boolean receipt ) { - CertificationRequest request = CertificationRequest.create(certificationData,receipt); + CertificationRequest request = CertificationRequest.create(certificationData, receipt); Map> headers = this.apiKey == null ? Map.of() diff --git a/src/main/java/org/unicitylabs/sdk/predicate/Predicate.java b/src/main/java/org/unicitylabs/sdk/predicate/Predicate.java index 2a9b95d..569e601 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/Predicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/Predicate.java @@ -40,6 +40,6 @@ public interface Predicate extends SerializablePredicate { * @param trustBase trust base to verify against. * @return true if successful */ - boolean verify(Token token, TransferTransaction transaction, RootTrustBase trustBase); + boolean verify(Token token, TransferTransaction transaction, RootTrustBase trustBase); } 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 ad3a1f9..d51a1d6 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/BurnPredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/BurnPredicate.java @@ -74,7 +74,7 @@ public boolean isOwner(byte[] publicKey) { } @Override - public boolean verify(Token token, TransferTransaction transaction, RootTrustBase trustBase) { + public boolean verify(Token token, TransferTransaction transaction, RootTrustBase trustBase) { return false; } 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 b9e7c02..a4dab1a 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/DefaultPredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/DefaultPredicate.java @@ -157,7 +157,7 @@ public boolean isOwner(byte[] publicKey) { } @Override - public boolean verify(Token token, TransferTransaction 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; } 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 3e62247..6987ab8 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/MaskedPredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/MaskedPredicate.java @@ -4,8 +4,10 @@ import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; 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.MintTransaction; /** * Masked predicate. @@ -22,7 +24,7 @@ public class MaskedPredicate extends DefaultPredicate { * @param hashAlgorithm hash algorithm * @param nonce predicate nonce */ - public MaskedPredicate( + MaskedPredicate( TokenId tokenId, TokenType tokenType, byte[] publicKey, @@ -41,7 +43,7 @@ public MaskedPredicate( } /** - * Create masked predicate from signing service. + * Create masked predicate from mint transaction and signing service. * * @param tokenId token id * @param tokenType token type @@ -55,7 +57,8 @@ public static MaskedPredicate create( TokenType tokenType, SigningService signingService, HashAlgorithm hashAlgorithm, - byte[] nonce) { + byte[] nonce + ) { return new MaskedPredicate(tokenId, tokenType, signingService.getPublicKey(), signingService.getAlgorithm(), hashAlgorithm, nonce); } 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 c019cf0..bd395f3 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/UnmaskedPredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/UnmaskedPredicate.java @@ -11,6 +11,8 @@ import org.unicitylabs.sdk.token.Token; import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenType; +import org.unicitylabs.sdk.transaction.MintTransaction; +import org.unicitylabs.sdk.transaction.Transaction; import org.unicitylabs.sdk.transaction.TransferTransaction; /** @@ -31,24 +33,25 @@ public class UnmaskedPredicate extends DefaultPredicate { } /** - * Create unmasked predicate. + * Create masked predicate from transaction and signing service. * * @param tokenId token id * @param tokenType token type + * @param transaction received transaction * @param signingService signing service * @param hashAlgorithm hash algorithm - * @param salt received transaction salt - * @return unmasked predicate + * @return predicate */ public static UnmaskedPredicate create( TokenId tokenId, TokenType tokenType, + Transaction transaction, SigningService signingService, - HashAlgorithm hashAlgorithm, - byte[] salt + HashAlgorithm hashAlgorithm ) { - Signature nonce = signingService.sign( - new DataHasher(HashAlgorithm.SHA256).update(salt).digest()); + Signature signature = signingService.sign( + new DataHasher(HashAlgorithm.SHA256).update(transaction.getData().getSalt()).digest() + ); return new UnmaskedPredicate( tokenId, @@ -56,7 +59,8 @@ public static UnmaskedPredicate create( signingService.getPublicKey(), signingService.getAlgorithm(), hashAlgorithm, - nonce.getBytes()); + signature.getBytes() + ); } /** @@ -69,7 +73,7 @@ public static UnmaskedPredicate create( */ @Override public boolean verify( - Token token, + Token token, TransferTransaction transaction, RootTrustBase trustBase ) { @@ -77,11 +81,7 @@ public boolean verify( return super.verify(token, transaction, trustBase) && SigningService.verifyWithPublicKey( new DataHasher(HashAlgorithm.SHA256) - .update( - transactions.isEmpty() - ? token.getGenesis().getData().getSalt() - : transactions.get(transactions.size() - 1).getData().getSalt() - ) + .update(token.getLatestTransaction().getData().getSalt()) .digest(), this.getNonce(), this.getPublicKey() diff --git a/src/main/java/org/unicitylabs/sdk/token/Token.java b/src/main/java/org/unicitylabs/sdk/token/Token.java index 83e2943..0f2fcaf 100644 --- a/src/main/java/org/unicitylabs/sdk/token/Token.java +++ b/src/main/java/org/unicitylabs/sdk/token/Token.java @@ -21,8 +21,8 @@ import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.serializer.json.JsonSerializationException; import org.unicitylabs.sdk.token.fungible.TokenCoinData; +import org.unicitylabs.sdk.transaction.MintReasonFactory; import org.unicitylabs.sdk.transaction.MintTransaction; -import org.unicitylabs.sdk.transaction.MintTransactionReason; import org.unicitylabs.sdk.transaction.Transaction; import org.unicitylabs.sdk.transaction.TransferTransaction; import org.unicitylabs.sdk.verification.VerificationException; @@ -30,10 +30,8 @@ /** * Token representation. - * - * @param mint transaction reason for current token. */ -public class Token { +public class Token { /** * Current token representation version. @@ -41,20 +39,20 @@ public class Token { public static final String TOKEN_VERSION = "2.0"; private final TokenState state; - private final MintTransaction genesis; + private final MintTransaction genesis; private final List transactions; - private final List> nametags; + private final List nametags; @JsonCreator Token( @JsonProperty("state") TokenState state, @JsonProperty("genesis") - MintTransaction genesis, + MintTransaction genesis, @JsonProperty("transactions") List transactions, @JsonProperty("nametags") - List> nametags + List nametags ) { Objects.requireNonNull(state, "State cannot be null"); Objects.requireNonNull(genesis, "Genesis cannot be null"); @@ -131,7 +129,7 @@ public TokenState getState() { * * @return token genesis */ - public MintTransaction getGenesis() { + public MintTransaction getGenesis() { return this.genesis; } @@ -144,58 +142,70 @@ public List getTransactions() { return this.transactions; } + /** + * Get token's latest transaction. + * + * @return latest transaction + */ + @JsonIgnore + public Transaction getLatestTransaction() { + return this.transactions.size() > 0 ? this.transactions.get(this.transactions.size() - 1) : this.genesis; + } + /** * Get token current state nametags. * * @return nametags */ - public List> getNametags() { + public List getNametags() { return this.nametags; } /** * 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 + * @param trustBase trust base for mint transaction verification + * @param mintReasonFactory factory to create mint transaction reasons + * @param state initial state + * @param transaction mint transaction * @return token * @throws VerificationException if token state is invalid */ - public static Token create( + public static Token mint( RootTrustBase trustBase, + MintReasonFactory mintReasonFactory, TokenState state, - MintTransaction transaction + MintTransaction transaction ) throws VerificationException { - return Token.create(trustBase, state, transaction, List.of()); + return Token.mint(trustBase, mintReasonFactory, state, transaction, List.of()); } /** - * Create token state from mint transaction, initial state and nametags. Also verify if state is - * correct. + * 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 + * @param trustBase trust base for mint transaction verification + * @param mintReasonFactory factory to create mint transaction reasons + * @param state initial state + * @param transaction mint transaction + * @param nametags nametags associated with transaction * @return token * @throws VerificationException if token state is invalid */ - public static Token create( + public static Token mint( RootTrustBase trustBase, + MintReasonFactory mintReasonFactory, TokenState state, - MintTransaction transaction, - List> nametags + MintTransaction transaction, + List nametags ) throws VerificationException { Objects.requireNonNull(state, "State cannot be null"); Objects.requireNonNull(transaction, "Genesis cannot be null"); Objects.requireNonNull(trustBase, "Trust base cannot be null"); + Objects.requireNonNull(mintReasonFactory, "Mint reason factory cannot be null"); Objects.requireNonNull(nametags, "Nametag tokens cannot be null"); - Token token = new Token<>(state, transaction, List.of(), nametags); - VerificationResult result = token.verify(trustBase); + Token token = new Token(state, transaction, List.of(), nametags); + VerificationResult result = token.verify(trustBase, mintReasonFactory); if (!result.isSuccessful()) { throw new VerificationException("Token verification failed", result); } @@ -206,25 +216,28 @@ public static Token create( /** * 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 + * @param trustBase trust base to verify latest state + * @param mintReasonFactory factory to create mint transaction reasons + * @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( + public Token update( RootTrustBase trustBase, + MintReasonFactory mintReasonFactory, TokenState state, TransferTransaction transaction, - List> nametags + List nametags ) throws VerificationException { Objects.requireNonNull(state, "State cannot be null"); Objects.requireNonNull(transaction, "Transaction cannot be null"); Objects.requireNonNull(nametags, "Nametag tokens cannot be null"); Objects.requireNonNull(trustBase, "Trust base cannot be null"); + Objects.requireNonNull(mintReasonFactory, "MintReasonFactory cannot be null"); - VerificationResult result = transaction.verify(trustBase, this); + VerificationResult result = transaction.verify(trustBase, mintReasonFactory, this); if (!result.isSuccessful()) { throw new VerificationException("Transaction verification failed", result); @@ -232,9 +245,9 @@ public Token update( LinkedList transactions = new LinkedList<>(this.transactions); transactions.add(transaction); - Token token = new Token<>(state, this.genesis, transactions, nametags); + Token token = new Token(state, this.genesis, transactions, nametags); - result = token.verifyNametagTokens(trustBase); + result = token.verifyNametagTokens(trustBase, mintReasonFactory); if (!result.isSuccessful()) { throw new VerificationException("Nametag tokens verification failed", result); } @@ -255,15 +268,19 @@ public Token update( /** * Verify current token state against trustbase. * - * @param trustBase trust base to verify state against + * @param trustBase trust base to verify state against + * @param mintReasonFactory factory to create mint transaction reasons * @return verification result */ - public VerificationResult verify(RootTrustBase trustBase) { + public VerificationResult verify(RootTrustBase trustBase, MintReasonFactory mintReasonFactory) { + Objects.requireNonNull(trustBase, "Trust base cannot be null"); + Objects.requireNonNull(mintReasonFactory, "Mint reason factory cannot be null"); + List results = new ArrayList<>(); results.add( VerificationResult.fromChildren( "Genesis verification", - List.of(this.genesis.verify(trustBase)) + List.of(this.genesis.verify(trustBase, mintReasonFactory)) ) ); @@ -272,7 +289,8 @@ public VerificationResult verify(RootTrustBase trustBase) { results.add( transaction.verify( trustBase, - new Token<>( + mintReasonFactory, + new Token( transaction.getData().getSourceState(), this.genesis, this.transactions.subList(0, i), @@ -286,7 +304,7 @@ public VerificationResult verify(RootTrustBase trustBase) { VerificationResult.fromChildren( "Current state verification", List.of( - this.verifyNametagTokens(trustBase), + this.verifyNametagTokens(trustBase, mintReasonFactory), this.verifyRecipient(), this.verifyRecipientData() ) @@ -299,14 +317,18 @@ public VerificationResult verify(RootTrustBase trustBase) { /** * Verify token nametag tokens against trust base. * - * @param trustBase trust base to verify against + * @param trustBase trust base to verify against + * @param mintReasonFactory factory to create mint transaction reasons * @return verification result */ - public VerificationResult verifyNametagTokens(RootTrustBase trustBase) { + public VerificationResult verifyNametagTokens(RootTrustBase trustBase, MintReasonFactory mintReasonFactory) { + Objects.requireNonNull(trustBase, "Trust base cannot be null"); + Objects.requireNonNull(mintReasonFactory, "Mint reason factory cannot be null"); + return VerificationResult.fromChildren( "Nametag verification", this.nametags.stream() - .map(token -> token.verify(trustBase)) + .map(token -> token.verify(trustBase, mintReasonFactory)) .collect(Collectors.toList())); } @@ -356,14 +378,14 @@ public VerificationResult verifyRecipientData() { * @param bytes CBOR bytes * @return token */ - public static Token fromCbor(byte[] bytes) { + 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); } - return new Token<>( + return new Token( TokenState.fromCbor(data.get(1)), MintTransaction.fromCbor(data.get(2)), CborDeserializer.readArray(data.get(3)).stream() @@ -404,7 +426,7 @@ public byte[] toCbor() { * @param input JSON string * @return token */ - public static Token fromJson(String input) { + public static Token fromJson(String input) { try { return UnicityObjectMapper.JSON.readValue(input, Token.class); } catch (JsonProcessingException e) { @@ -430,7 +452,7 @@ public boolean equals(Object o) { if (!(o instanceof Token)) { return false; } - Token token = (Token) o; + Token token = (Token) o; return Objects.equals(this.state, token.state) && Objects.equals(this.genesis, token.genesis) && Objects.equals(this.transactions, token.transactions) && Objects.equals(this.nametags, token.nametags); diff --git a/src/main/java/org/unicitylabs/sdk/transaction/DefaultMintReasonFactory.java b/src/main/java/org/unicitylabs/sdk/transaction/DefaultMintReasonFactory.java new file mode 100644 index 0000000..82f70d2 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/transaction/DefaultMintReasonFactory.java @@ -0,0 +1,45 @@ +package org.unicitylabs.sdk.transaction; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.transaction.split.SplitMintReason; + +/** + * Default implementation for mint reason factory. + */ +public class DefaultMintReasonFactory implements MintReasonFactory { + + private final Map> reasons; + + /** + * Create default mint reason factory. + * + * @param reasons mint transaction reason parser map + */ + public DefaultMintReasonFactory(Map> reasons) { + this.reasons = Map.copyOf(reasons); + } + + /** + * Create default mint reason factory. + */ + public DefaultMintReasonFactory() { + this(Map.of(SplitMintReason.TYPE, SplitMintReason::fromCbor)); + } + + @Override + public MintTransactionReason create(byte[] bytes) { + List data = CborDeserializer.readArray(bytes); + + long type = CborDeserializer.readUnsignedInteger(data.get(0)).asLong(); + Function factory = this.reasons.get(type); + if (factory == null) { + throw new IllegalStateException(String.format("Unsupported user defined mint reason type '%s'", type)); + } + + return factory.apply(bytes); + } + +} diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintCommitment.java b/src/main/java/org/unicitylabs/sdk/transaction/MintCommitment.java index db2c66b..3cb84ab 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintCommitment.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintCommitment.java @@ -14,15 +14,13 @@ /** * Commitment representing a submitted transaction. - * - * @param the type of transaction data */ -public class MintCommitment extends - Commitment> { +public class MintCommitment extends + Commitment { @JsonCreator private MintCommitment( @JsonProperty("transactionData") - MintTransaction.Data transactionData, + MintTransaction.Data transactionData, @JsonProperty("authenticator") CertificationData certificationData ) { @@ -36,8 +34,8 @@ private MintCommitment( * @return mint transaction */ @Override - public MintTransaction toTransaction(InclusionProof inclusionProof) { - return new MintTransaction<>(this.getTransactionData(), inclusionProof); + public MintTransaction toTransaction(InclusionProof inclusionProof) { + return new MintTransaction(this.getTransactionData(), inclusionProof); } /** @@ -46,7 +44,7 @@ public MintTransaction toTransaction(InclusionProof inclusionProof) { * @param input JSON string * @return mint commitment data */ - public static MintCommitment fromJson(String input) { + public static MintCommitment fromJson(String input) { try { return UnicityObjectMapper.JSON.readValue(input, MintCommitment.class); } catch (JsonProcessingException e) { @@ -58,11 +56,10 @@ public static MintCommitment fromJson(String input) { * Create mint commitment from transaction data. * * @param transactionData mint transaction data - * @param mint reason * @return mint commitment */ - public static MintCommitment create( - MintTransaction.Data transactionData + public static MintCommitment create( + MintTransaction.Data transactionData ) { Objects.requireNonNull(transactionData, "Transaction data cannot be null"); @@ -73,6 +70,6 @@ public static MintCommitment create( signingService ); - return new MintCommitment<>(transactionData, certificationData); + return new MintCommitment(transactionData, certificationData); } } diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintReasonFactory.java b/src/main/java/org/unicitylabs/sdk/transaction/MintReasonFactory.java new file mode 100644 index 0000000..dd45ce2 --- /dev/null +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintReasonFactory.java @@ -0,0 +1,15 @@ +package org.unicitylabs.sdk.transaction; + +/** + * Mint reason factory. + */ +public interface MintReasonFactory { + + /** + * Create mint reason. + * + * @param bytes encoded mint reason + * @return mint reason + */ + MintTransactionReason create(byte[] bytes); +} diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintReasonType.java b/src/main/java/org/unicitylabs/sdk/transaction/MintReasonType.java deleted file mode 100644 index b88cdfc..0000000 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintReasonType.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.unicitylabs.sdk.transaction; - -/** - * Mint reason type. - */ -public enum MintReasonType { - /** - * Tokens mint reason based on the split of a token. - */ - TOKEN_SPLIT, -} diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintTransaction.java b/src/main/java/org/unicitylabs/sdk/transaction/MintTransaction.java index 95d7768..c98c128 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintTransaction.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintTransaction.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; @@ -28,23 +27,20 @@ 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; import org.unicitylabs.sdk.verification.VerificationResult; /** * Mint transaction. - * - * @param mint reason */ -public class MintTransaction extends - Transaction> { +public class MintTransaction extends + Transaction { @JsonCreator MintTransaction( @JsonProperty("data") - Data data, + Data data, @JsonProperty("inclusionProof") InclusionProof inclusionProof) { super(data, inclusionProof); @@ -56,10 +52,10 @@ public class MintTransaction extends * @param bytes CBOR bytes * @return mint transaction */ - public static MintTransaction fromCbor(byte[] bytes) { + public static MintTransaction fromCbor(byte[] bytes) { List data = CborDeserializer.readArray(bytes); - return new MintTransaction<>( + return new MintTransaction( Data.fromCbor(data.get(0)), InclusionProof.fromCbor(data.get(1)) ); @@ -71,7 +67,7 @@ public static MintTransaction fromCbor(byte[] bytes) { * @param input JSON string * @return mint transaction */ - public static MintTransaction fromJson(String input) { + public static MintTransaction fromJson(String input) { try { return UnicityObjectMapper.JSON.readValue(input, MintTransaction.class); } catch (JsonProcessingException e) { @@ -83,9 +79,10 @@ public static MintTransaction fromJson(String input) { * Verify mint transaction. * * @param trustBase root trust base + * @param mintReasonFactory mint reason factory * @return verification result */ - public VerificationResult verify(RootTrustBase trustBase) { + public VerificationResult verify(RootTrustBase trustBase, MintReasonFactory mintReasonFactory) { CertificationData certificationData = this.getInclusionProof().getCertificationData().orElse(null); if (certificationData == null) { return VerificationResult.fail("Missing certification data"); @@ -106,7 +103,7 @@ public VerificationResult verify(RootTrustBase trustBase) { } VerificationResult reasonResult = this.getData().getReason() - .map(reason -> reason.verify(this)) + .map(reason -> mintReasonFactory.create(reason).verify(this)) .orElse(VerificationResult.success()); if (!reasonResult.isSuccessful()) { @@ -129,10 +126,8 @@ public VerificationResult verify(RootTrustBase trustBase) { /** * Mint transaction data. - * - * @param mint reason */ - public static class Data implements + public static class Data implements TransactionData { private final TokenId tokenId; @@ -143,7 +138,7 @@ public static class Data implements private final Address recipient; private final byte[] salt; private final DataHash recipientDataHash; - private final R reason; + private final byte[] reason; /** * Create mint transaction data. @@ -155,7 +150,7 @@ public static class Data implements * @param recipient token recipient address * @param salt mint transaction salt * @param recipientDataHash recipient data hash - * @param reason mint reason + * @param reason optional mint reason bytes */ @JsonCreator public Data( @@ -166,9 +161,7 @@ public Data( @JsonProperty("recipient") Address recipient, @JsonProperty("salt") byte[] salt, @JsonProperty("recipientDataHash") DataHash recipientDataHash, - @JsonProperty("reason") - @JsonDeserialize(using = MintTransactionReasonJson.Deserializer.class) - R reason + @JsonProperty("reason") byte[] reason ) { Objects.requireNonNull(tokenId, "Token ID cannot be null"); Objects.requireNonNull(tokenType, "Token type cannot be null"); @@ -183,7 +176,103 @@ public Data( this.recipient = recipient; this.salt = Arrays.copyOf(salt, salt.length); this.recipientDataHash = recipientDataHash; - this.reason = reason; + this.reason = reason != null ? Arrays.copyOf(reason, reason.length) : null; + } + + /** + * 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 object + */ + public Data( + TokenId tokenId, + TokenType tokenType, + byte[] tokenData, + TokenCoinData coinData, + Address recipient, + byte[] salt, + DataHash recipientDataHash, + MintTransactionReason reason + ) { + this( + tokenId, + tokenType, + tokenData, + coinData, + recipient, + salt, + recipientDataHash, + reason != null ? reason.toCbor() : null + ); + } + + /** + * 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 + */ + public Data( + TokenId tokenId, + TokenType tokenType, + byte[] tokenData, + TokenCoinData coinData, + Address recipient, + byte[] salt, + DataHash recipientDataHash + ) { + this( + tokenId, + tokenType, + tokenData, + coinData, + recipient, + salt, + recipientDataHash, + (byte[]) null + ); + } + + /** + * 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 + */ + public Data( + TokenId tokenId, + TokenType tokenType, + byte[] tokenData, + TokenCoinData coinData, + Address recipient, + byte[] salt + ) { + this( + tokenId, + tokenType, + tokenData, + coinData, + recipient, + salt, + null, + (byte[]) null + ); } /** @@ -254,8 +343,8 @@ public Address getRecipient() { * * @return mint reason */ - public Optional getReason() { - return Optional.ofNullable(this.reason); + public Optional getReason() { + return Optional.ofNullable(this.reason != null ? Arrays.copyOf(this.reason, this.reason.length) : null); } /** @@ -285,10 +374,10 @@ public DataHash calculateHash() { * @param bytes CBOR bytes * @return mint transaction data */ - public static Data fromCbor(byte[] bytes) { + public static Data fromCbor(byte[] bytes) { List data = CborDeserializer.readArray(bytes); - return new Data<>( + return new Data( TokenId.fromCbor(data.get(0)), TokenType.fromCbor(data.get(1)), CborDeserializer.readOptional(data.get(2), CborDeserializer::readByteString), @@ -296,7 +385,7 @@ public static Data fromCbor(byte[] bytes) { 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) + CborDeserializer.readOptional(data.get(7), CborDeserializer::readByteString) ); } @@ -314,7 +403,7 @@ public byte[] toCbor() { CborSerializer.encodeTextString(this.recipient.getAddress()), CborSerializer.encodeByteString(this.salt), CborSerializer.encodeOptional(this.recipientDataHash, DataHash::toCbor), - CborSerializer.encodeOptional(this.reason, MintTransactionReason::toCbor) + CborSerializer.encodeOptional(this.reason, CborSerializer::encodeByteString) ); } @@ -324,7 +413,7 @@ public byte[] toCbor() { * @param input JSON string * @return mint transaction data */ - public static Data fromJson(String input) { + public static Data fromJson(String input) { try { return UnicityObjectMapper.JSON.readValue(input, Data.class); } catch (JsonProcessingException e) { @@ -350,7 +439,7 @@ public boolean equals(Object o) { if (!(o instanceof Data)) { return false; } - Data that = (Data) o; + Data that = (Data) o; return Objects.equals(this.tokenId, that.tokenId) && Objects.equals(this.tokenType, that.tokenType) @@ -360,14 +449,14 @@ public boolean equals(Object o) { && Objects.equals(this.recipient, that.recipient) && Objects.deepEquals(this.salt, that.salt) && Objects.equals(this.recipientDataHash, that.recipientDataHash) - && Objects.equals(this.reason, that.reason); + && Arrays.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); + this.recipient, Arrays.hashCode(this.salt), this.recipientDataHash, Arrays.hashCode(this.reason)); } @Override @@ -387,14 +476,14 @@ public String toString() { this.tokenId, this.tokenType, this.tokenData != null ? HexConverter.encode(this.tokenData) : null, this.coinData, this.sourceState, this.recipient, HexConverter.encode(this.salt), this.recipientDataHash, - this.reason); + this.reason != null ? HexConverter.encode(this.reason) : null); } } /** * Nametag mint data. */ - public static class NametagData extends Data { + public static class NametagData extends Data { /** * Create nametag mint data. @@ -418,9 +507,7 @@ public NametagData( targetAddress.getAddress().getBytes(StandardCharsets.UTF_8), null, recipient, - salt, - null, - null + salt ); } } diff --git a/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionReason.java b/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionReason.java index e7718ae..f2ba729 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionReason.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/MintTransactionReason.java @@ -6,21 +6,13 @@ * Mint transaction reason. */ public interface MintTransactionReason { - - /** - * Get mint reason type. - * - * @return reason type - */ - String getType(); - /** * Verify mint reason for genesis. * * @param genesis Genesis to verify against * @return verification result */ - VerificationResult verify(MintTransaction genesis); + VerificationResult verify(MintTransaction genesis); /** * Convert mint transaction reason to CBOR bytes. diff --git a/src/main/java/org/unicitylabs/sdk/transaction/TransactionData.java b/src/main/java/org/unicitylabs/sdk/transaction/TransactionData.java index 74c0028..f277446 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/TransactionData.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/TransactionData.java @@ -25,6 +25,13 @@ public interface TransactionData { */ Address getRecipient(); + /** + * Get transaction salt. + * + * @return transaction salt + */ + byte[] getSalt(); + /** * Gets the optional recipient data hash. * diff --git a/src/main/java/org/unicitylabs/sdk/transaction/TransferCommitment.java b/src/main/java/org/unicitylabs/sdk/transaction/TransferCommitment.java index f42f01f..5262bc9 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/TransferCommitment.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/TransferCommitment.java @@ -66,7 +66,7 @@ public static TransferCommitment fromJson(String input) { * @return transfer commitment */ public static TransferCommitment create( - Token token, + Token token, Address recipient, byte[] salt, DataHash recipientDataHash, diff --git a/src/main/java/org/unicitylabs/sdk/transaction/TransferTransaction.java b/src/main/java/org/unicitylabs/sdk/transaction/TransferTransaction.java index d611143..9f86e70 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/TransferTransaction.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/TransferTransaction.java @@ -73,14 +73,15 @@ public static TransferTransaction fromJson(String input) { * Verify if transaction is based off of that token state. * * @param trustBase trust base to verify against + * @param mintReasonFactory factory to create mint transaction reasons * @param token token * @return verification result */ - public VerificationResult verify(RootTrustBase trustBase, Token token) { + public VerificationResult verify(RootTrustBase trustBase, MintReasonFactory mintReasonFactory, Token token) { Predicate predicate = PredicateEngineService.createPredicate(token.getState().getPredicate()); return VerificationResult.fromChildren("Transaction verification", List.of( - token.verifyNametagTokens(trustBase), + token.verifyNametagTokens(trustBase, mintReasonFactory), token.verifyRecipient(), token.verifyRecipientData(), predicate.verify(token, this, trustBase) @@ -99,7 +100,7 @@ public static class Data implements TransactionData { private final byte[] salt; private final DataHash recipientDataHash; private final byte[] message; - private final List> nametags; + private final List nametags; @JsonCreator Data( @@ -108,7 +109,7 @@ public static class Data implements TransactionData { @JsonProperty("salt") byte[] salt, @JsonProperty("recipientDataHash") DataHash recipientDataHash, @JsonProperty("message") byte[] message, - @JsonProperty("nametags") List> nametags + @JsonProperty("nametags") List nametags ) { Objects.requireNonNull(sourceState, "SourceState cannot be null"); Objects.requireNonNull(recipient, "Recipient cannot be null"); @@ -175,7 +176,7 @@ public Optional getMessage() { * * @return nametags */ - public List> getNametags() { + public List getNametags() { return 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 2a637a9..8692e44 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReason.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReason.java @@ -15,11 +15,11 @@ import org.unicitylabs.sdk.predicate.PredicateEngineService; import org.unicitylabs.sdk.predicate.embedded.BurnPredicate; import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; +import org.unicitylabs.sdk.serializer.cbor.CborDeserializer.CborNumber; 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.MintTransaction; import org.unicitylabs.sdk.transaction.MintTransactionReason; import org.unicitylabs.sdk.verification.VerificationResult; @@ -27,16 +27,19 @@ /** * Mint reason for splitting a token. */ -@JsonIgnoreProperties() public class SplitMintReason implements MintTransactionReason { - private final Token token; + /** + * Split reason type for encoding and decoding. + */ + public static final long TYPE = 1; + + private final Token token; private final List proofs; - @JsonCreator SplitMintReason( - @JsonProperty("token") Token token, - @JsonProperty("proofs") List proofs + Token token, + List proofs ) { Objects.requireNonNull(token, "Token cannot be null"); Objects.requireNonNull(proofs, "Proofs cannot be null"); @@ -45,22 +48,12 @@ public class SplitMintReason implements MintTransactionReason { this.proofs = List.copyOf(proofs); } - /** - * Get mint reason type. - * - * @return token split reason - */ - @JsonProperty(access = JsonProperty.Access.READ_ONLY) - public String getType() { - return MintReasonType.TOKEN_SPLIT.name(); - } - /** * Get token which was burnt for split. * * @return burnt token */ - public Token getToken() { + public Token getToken() { return this.token; } @@ -79,7 +72,7 @@ public List getProofs() { * @param transaction Genesis to verify against * @return verification result */ - public VerificationResult verify(MintTransaction transaction) { + public VerificationResult verify(MintTransaction transaction) { if (!transaction.getData().getCoinData().isPresent()) { return VerificationResult.fail("Coin data is missing."); } @@ -140,6 +133,11 @@ public VerificationResult verify(MintTransaction transaction) { public static SplitMintReason fromCbor(byte[] bytes) { List data = CborDeserializer.readArray(bytes); + long type = CborDeserializer.readUnsignedInteger(data.get(0)).asLong(); + if (type != SplitMintReason.TYPE) { + throw new IllegalArgumentException("Invalid type for SplitMintReason: " + type); + } + return new SplitMintReason( Token.fromCbor(data.get(1)), CborDeserializer.readArray(data.get(2)).stream() @@ -155,7 +153,7 @@ public static SplitMintReason fromCbor(byte[] bytes) { */ public byte[] toCbor() { return CborSerializer.encodeArray( - CborSerializer.encodeTextString(this.getType()), + CborSerializer.encodeUnsignedInteger(SplitMintReason.TYPE), this.token.toCbor(), CborSerializer.encodeArray( this.proofs.stream() 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 b4c97a2..f3b1cf9 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/split/TokenSplitBuilder.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/split/TokenSplitBuilder.java @@ -30,6 +30,7 @@ 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.MintReasonFactory; import org.unicitylabs.sdk.transaction.MintTransaction; import org.unicitylabs.sdk.transaction.TransferCommitment; import org.unicitylabs.sdk.transaction.TransferTransaction; @@ -83,7 +84,7 @@ public TokenSplitBuilder createToken( * @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 { + public TokenSplit build(Token token) throws LeafOutOfBoundsException, BranchExistsException { Objects.requireNonNull(token, "Token cannot be null"); Map trees = new HashMap<>(); @@ -133,13 +134,13 @@ public TokenSplit build(Token token) throws LeafOutOfBoundsException, BranchE */ public static class TokenSplit { - private final Token token; + private final Token token; private final SparseMerkleTreeRootNode aggregationRoot; private final Map coinRoots; private final Map tokens; private TokenSplit( - Token token, + Token token, SparseMerkleTreeRootNode aggregationRoot, Map coinRoots, Map tokens @@ -175,18 +176,21 @@ public TransferCommitment createBurnCommitment(byte[] salt, SigningService signi * Create split mint commitments after burn transaction is received. * * @param trustBase trust base for burn transaction verification + * @param mintReasonFactory factory to create mint transaction reasons * @param burnTransaction burn transaction * @return list of mint commitments for sending to unicity service * @throws VerificationException if token verification fails */ - public List> createSplitMintCommitments( + public List createSplitMintCommitments( RootTrustBase trustBase, + MintReasonFactory mintReasonFactory, TransferTransaction burnTransaction ) throws VerificationException { Objects.requireNonNull(burnTransaction, "Burn transaction cannot be null"); - Token burnedToken = this.token.update( + Token burnedToken = this.token.update( trustBase, + mintReasonFactory, new TokenState( new BurnPredicate( this.token.getId(), @@ -202,7 +206,7 @@ public List> createSplitMintCommitments( return List.copyOf( this.tokens.values().stream() .map(request -> MintCommitment.create( - new MintTransaction.Data<>( + new MintTransaction.Data( request.id, request.type, request.data, diff --git a/src/test/java/org/unicitylabs/sdk/common/BaseEscrowSwapTest.java b/src/test/java/org/unicitylabs/sdk/common/BaseEscrowSwapTest.java index 770f352..f521e75 100644 --- a/src/test/java/org/unicitylabs/sdk/common/BaseEscrowSwapTest.java +++ b/src/test/java/org/unicitylabs/sdk/common/BaseEscrowSwapTest.java @@ -20,6 +20,8 @@ import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenState; import org.unicitylabs.sdk.token.TokenType; +import org.unicitylabs.sdk.transaction.DefaultMintReasonFactory; +import org.unicitylabs.sdk.transaction.MintReasonFactory; import org.unicitylabs.sdk.transaction.TransferCommitment; import org.unicitylabs.sdk.transaction.TransferTransaction; import org.unicitylabs.sdk.util.HexConverter; @@ -38,6 +40,7 @@ public abstract class BaseEscrowSwapTest { protected StateTransitionClient client; protected RootTrustBase trustBase; + private final MintReasonFactory mintReasonFactory = new DefaultMintReasonFactory(); private final TokenType tokenType = new TokenType(HexConverter.decode( "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509")); @@ -50,7 +53,7 @@ public abstract class BaseEscrowSwapTest { private final String BOB_NAMETAG = String.format("BOB_%s", System.currentTimeMillis()); private final String CAROL_NAMETAG = String.format("CAROL_%s", System.currentTimeMillis()); - private String[] transferToken(Token token, SigningService signingService, String nametag) + private String[] transferToken(Token token, SigningService signingService, String nametag) throws Exception { TransferCommitment commitment = TransferCommitment.create( token, @@ -78,10 +81,11 @@ private String[] transferToken(Token token, SigningService signingService, St }; } - private Token mintToken(byte[] secret) throws Exception { + private Token mintToken(byte[] secret) throws Exception { return TokenUtils.mintToken( this.client, this.trustBase, + this.mintReasonFactory, secret, new TokenId(randomBytes(32)), this.tokenType, @@ -93,24 +97,25 @@ private Token mintToken(byte[] secret) throws Exception { ); } - private Token receiveToken(String[] tokenInfo, SigningService signingService, - Token nametagToken) throws Exception { - Token token = Token.fromJson(tokenInfo[0]); + private Token receiveToken(String[] tokenInfo, SigningService signingService, + Token nametagToken) throws Exception { + Token token = Token.fromJson(tokenInfo[0]); TransferTransaction transaction = TransferTransaction.fromJson(tokenInfo[1]); TokenState state = new TokenState( UnmaskedPredicate.create( token.getId(), token.getType(), + transaction, signingService, - HashAlgorithm.SHA256, - transaction.getData().getSalt() + HashAlgorithm.SHA256 ), null ); return this.client.finalizeTransaction( this.trustBase, + this.mintReasonFactory, token, state, transaction, @@ -121,7 +126,7 @@ private Token receiveToken(String[] tokenInfo, SigningService signingService, @Test void testEscrow() throws Exception { // Make nametags unique for each test run - Token bobToken = mintToken(BOB_SECRET); + Token bobToken = mintToken(BOB_SECRET); String[] bobSerializedData = this.transferToken( bobToken, SigningService.createFromMaskedSecret( @@ -131,7 +136,7 @@ void testEscrow() throws Exception { ALICE_NAMETAG ); - Token carolToken = mintToken(CAROL_SECRET); + Token carolToken = mintToken(CAROL_SECRET); String[] carolSerializedData = this.transferToken( carolToken, SigningService.createFromMaskedSecret( @@ -141,9 +146,10 @@ void testEscrow() throws Exception { ALICE_NAMETAG ); - Token aliceNametagToken = TokenUtils.mintNametagToken( + Token aliceNametagToken = TokenUtils.mintNametagToken( this.client, this.trustBase, + this.mintReasonFactory, ALICE_SECRET, this.tokenType, ALICE_NAMETAG, @@ -156,20 +162,20 @@ void testEscrow() throws Exception { randomBytes(32) ); - Token aliceBobToken = receiveToken( + Token aliceBobToken = receiveToken( bobSerializedData, SigningService.createFromSecret(ALICE_SECRET), aliceNametagToken ); - Assertions.assertTrue(aliceBobToken.verify(this.trustBase).isSuccessful()); - Token aliceCarolToken = receiveToken( + Assertions.assertTrue(aliceBobToken.verify(this.trustBase, this.mintReasonFactory).isSuccessful()); + Token aliceCarolToken = receiveToken( carolSerializedData, SigningService.createFromSecret(ALICE_SECRET), aliceNametagToken ); - Assertions.assertTrue(aliceCarolToken.verify(this.trustBase).isSuccessful()); + Assertions.assertTrue(aliceCarolToken.verify(this.trustBase, this.mintReasonFactory).isSuccessful()); - Token aliceToCarolToken = receiveToken( + Token aliceToCarolToken = receiveToken( transferToken( aliceBobToken, SigningService.createFromSecret(ALICE_SECRET), @@ -179,6 +185,7 @@ void testEscrow() throws Exception { TokenUtils.mintNametagToken( this.client, this.trustBase, + this.mintReasonFactory, CAROL_SECRET, this.tokenType, CAROL_NAMETAG, @@ -191,9 +198,9 @@ void testEscrow() throws Exception { randomBytes(32) ) ); - Assertions.assertTrue(aliceToCarolToken.verify(this.trustBase).isSuccessful()); + Assertions.assertTrue(aliceToCarolToken.verify(this.trustBase, this.mintReasonFactory).isSuccessful()); - Token aliceToBobToken = receiveToken( + Token aliceToBobToken = receiveToken( transferToken( aliceCarolToken, SigningService.createFromSecret(ALICE_SECRET), @@ -203,6 +210,7 @@ void testEscrow() throws Exception { TokenUtils.mintNametagToken( this.client, this.trustBase, + this.mintReasonFactory, BOB_SECRET, this.tokenType, BOB_NAMETAG, @@ -215,6 +223,6 @@ void testEscrow() throws Exception { randomBytes(32) ) ); - Assertions.assertTrue(aliceToBobToken.verify(this.trustBase).isSuccessful()); + Assertions.assertTrue(aliceToBobToken.verify(this.trustBase, this.mintReasonFactory).isSuccessful()); } } diff --git a/src/test/java/org/unicitylabs/sdk/common/CommonTestFlow.java b/src/test/java/org/unicitylabs/sdk/common/CommonTestFlow.java index a5c8c49..363203e 100644 --- a/src/test/java/org/unicitylabs/sdk/common/CommonTestFlow.java +++ b/src/test/java/org/unicitylabs/sdk/common/CommonTestFlow.java @@ -33,12 +33,13 @@ 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.DefaultMintReasonFactory; import org.unicitylabs.sdk.transaction.InclusionProof; import org.unicitylabs.sdk.transaction.MintCommitment; +import org.unicitylabs.sdk.transaction.MintReasonFactory; import org.unicitylabs.sdk.transaction.MintTransaction; import org.unicitylabs.sdk.transaction.TransferCommitment; 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; import org.unicitylabs.sdk.util.InclusionProofUtils; @@ -52,6 +53,8 @@ public abstract class CommonTestFlow { protected StateTransitionClient client; protected RootTrustBase trustBase; + private final MintReasonFactory mintReasonFactory = new DefaultMintReasonFactory(); + private static final byte[] ALICE_SECRET = "Alice".getBytes(StandardCharsets.UTF_8); private static final byte[] BOB_SECRET = "Bob".getBytes(StandardCharsets.UTF_8); private static final byte[] CAROL_SECRET = "Carol".getBytes(StandardCharsets.UTF_8); @@ -61,14 +64,15 @@ public abstract class CommonTestFlow { */ @Test public void testTransferFlow() throws Exception { - Token aliceToken = TokenUtils.mintToken( + Token aliceToken = TokenUtils.mintToken( this.client, this.trustBase, + this.mintReasonFactory, ALICE_SECRET ); assertTrue(this.client.isMinted(aliceToken.getId(), this.trustBase).get()); - assertTrue(aliceToken.verify(this.trustBase).isSuccessful()); + assertTrue(aliceToken.verify(this.trustBase, this.mintReasonFactory).isSuccessful()); String bobNameTag = UUID.randomUUID().toString(); @@ -122,25 +126,27 @@ public void testTransferFlow() throws Exception { ).toAddress(); // Bob mints a name tag tokens - Token bobNametagToken = TokenUtils.mintNametagToken( + Token bobNametagToken = TokenUtils.mintNametagToken( this.client, this.trustBase, + this.mintReasonFactory, BOB_SECRET, bobNameTag, bobAddress ); // Bob finalizes the token - Token bobToken = client.finalizeTransaction( + Token bobToken = client.finalizeTransaction( this.trustBase, + this.mintReasonFactory, aliceToken, new TokenState( UnmaskedPredicate.create( aliceToken.getId(), aliceToken.getType(), + aliceToBobTransferTransaction, SigningService.createFromSecret(BOB_SECRET), - HashAlgorithm.SHA256, - aliceToBobTransferTransaction.getData().getSalt() + HashAlgorithm.SHA256 ), bobStateData ), @@ -149,7 +155,7 @@ public void testTransferFlow() throws Exception { ); // Verify Bob is now the owner - assertTrue(bobToken.verify(this.trustBase).isSuccessful()); + assertTrue(bobToken.verify(this.trustBase, this.mintReasonFactory).isSuccessful()); assertTrue(PredicateEngineService .createPredicate(bobToken.getState().getPredicate()) .isOwner(SigningService.createFromSecret(BOB_SECRET).getPublicKey()) @@ -195,19 +201,20 @@ public void testTransferFlow() throws Exception { UnmaskedPredicate carolPredicate = UnmaskedPredicate.create( bobToken.getId(), bobToken.getType(), + bobToCarolTransaction, SigningService.createFromSecret(CAROL_SECRET), - HashAlgorithm.SHA256, - bobToCarolTransaction.getData().getSalt() + HashAlgorithm.SHA256 ); - Token carolToken = this.client.finalizeTransaction( + Token carolToken = this.client.finalizeTransaction( this.trustBase, + this.mintReasonFactory, bobToken, new TokenState(carolPredicate, null), bobToCarolTransaction ); - assertTrue(carolToken.verify(this.trustBase).isSuccessful()); + assertTrue(carolToken.verify(this.trustBase, this.mintReasonFactory).isSuccessful()); assertEquals(2, carolToken.getTransactions().size()); // Bob receives carol token with nametag @@ -238,16 +245,17 @@ public void testTransferFlow() throws Exception { carolToBobInclusionProof ); - Token carolToBobToken = client.finalizeTransaction( + Token carolToBobToken = client.finalizeTransaction( this.trustBase, + this.mintReasonFactory, carolToken, new TokenState( UnmaskedPredicate.create( carolToken.getId(), carolToken.getType(), + carolToBobTransaction, SigningService.createFromSecret(BOB_SECRET), - HashAlgorithm.SHA256, - carolToBobTransaction.getData().getSalt() + HashAlgorithm.SHA256 ), null ), @@ -255,7 +263,7 @@ public void testTransferFlow() throws Exception { List.of(bobNametagToken) ); - assertTrue(carolToBobToken.verify(this.trustBase).isSuccessful()); + assertTrue(carolToBobToken.verify(this.trustBase, this.mintReasonFactory).isSuccessful()); // SPLIT List> splitCoins = carolToken.getCoins() @@ -306,8 +314,9 @@ public void testTransferFlow() throws Exception { throw new Exception("Failed to submit burn commitment"); } - List> splitCommitments = split.createSplitMintCommitments( + List splitCommitments = split.createSplitMintCommitments( this.trustBase, + this.mintReasonFactory, burnCommitment.toTransaction( InclusionProofUtils.waitInclusionProof( this.client, @@ -317,8 +326,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() != CertificationStatus.SUCCESS) { throw new Exception("Failed to submit split mint commitment"); } @@ -329,11 +338,7 @@ public void testTransferFlow() throws Exception { Assertions.assertEquals( 2, splitTransactions.stream() - .map(transaction -> transaction.getData() - .getReason() - .map(reason -> reason.verify(transaction).isSuccessful()) - .orElse(false) - ) + .map(transaction -> transaction.verify(this.trustBase, this.mintReasonFactory).isSuccessful()) .filter(Boolean::booleanValue) .count() ); @@ -347,8 +352,9 @@ public void testTransferFlow() throws Exception { ); Assertions.assertDoesNotThrow(() -> - Token.create( + Token.mint( this.trustBase, + this.mintReasonFactory, new TokenState(splitTokenPredicate, null), splitTransactions.get(0) ) 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 b33ff2c..f3066c8 100644 --- a/src/test/java/org/unicitylabs/sdk/common/split/BaseTokenSplitTest.java +++ b/src/test/java/org/unicitylabs/sdk/common/split/BaseTokenSplitTest.java @@ -25,9 +25,11 @@ 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.DefaultMintReasonFactory; import org.unicitylabs.sdk.transaction.MintCommitment; +import org.unicitylabs.sdk.transaction.MintReasonFactory; +import org.unicitylabs.sdk.transaction.MintTransaction; import org.unicitylabs.sdk.transaction.TransferCommitment; -import org.unicitylabs.sdk.transaction.split.SplitMintReason; import org.unicitylabs.sdk.transaction.split.TokenSplitBuilder; import org.unicitylabs.sdk.transaction.split.TokenSplitBuilder.TokenSplit; import org.unicitylabs.sdk.util.HexConverter; @@ -39,15 +41,18 @@ public abstract class BaseTokenSplitTest { protected StateTransitionClient client; protected RootTrustBase trustBase; + private final MintReasonFactory mintReasonFactory = new DefaultMintReasonFactory(); + @Test void testTokenSplitFullAmounts() throws Exception { TokenType tokenType = new TokenType(HexConverter.decode( "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509")); byte[] secret = "SECRET".getBytes(StandardCharsets.UTF_8); - Token token = TokenUtils.mintToken( + Token token = TokenUtils.mintToken( this.client, this.trustBase, + this.mintReasonFactory, secret, new TokenId(randomBytes(32)), tokenType, @@ -67,9 +72,10 @@ void testTokenSplitFullAmounts() throws Exception { String nametag = UUID.randomUUID().toString(); - Token nametagToken = TokenUtils.mintNametagToken( + Token nametagToken = TokenUtils.mintNametagToken( this.client, this.trustBase, + this.mintReasonFactory, secret, nametag, UnmaskedPredicateReference.create( @@ -124,8 +130,9 @@ void testTokenSplitFullAmounts() throws Exception { burnCommitmentResponse.getStatus())); } - List> mintCommitments = split.createSplitMintCommitments( + List mintCommitments = split.createSplitMintCommitments( this.trustBase, + this.mintReasonFactory, burnCommitment.toTransaction( InclusionProofUtils.waitInclusionProof( this.client, @@ -135,7 +142,7 @@ void testTokenSplitFullAmounts() throws Exception { ) ); - for (MintCommitment commitment : mintCommitments) { + for (MintCommitment commitment : mintCommitments) { CertificationResponse response = this.client .submitCommitment(commitment) .get(); @@ -145,27 +152,30 @@ void testTokenSplitFullAmounts() throws Exception { response.getStatus())); } + MintTransaction transaction = commitment.toTransaction( + InclusionProofUtils.waitInclusionProof(this.client, this.trustBase, commitment).get() + ); + TokenState state = new TokenState( UnmaskedPredicate.create( commitment.getTransactionData().getTokenId(), commitment.getTransactionData().getTokenType(), + transaction, SigningService.createFromSecret(secret), - HashAlgorithm.SHA256, - commitment.getTransactionData().getSalt() + HashAlgorithm.SHA256 ), null ); - Token splitToken = Token.create( + Token splitToken = Token.mint( this.trustBase, + this.mintReasonFactory, state, - commitment.toTransaction( - InclusionProofUtils.waitInclusionProof(this.client, this.trustBase, commitment).get() - ), + transaction, List.of(nametagToken) ); - Assertions.assertTrue(splitToken.verify(this.trustBase).isSuccessful()); + Assertions.assertTrue(splitToken.verify(this.trustBase, this.mintReasonFactory).isSuccessful()); } diff --git a/src/test/java/org/unicitylabs/sdk/e2e/context/TestContext.java b/src/test/java/org/unicitylabs/sdk/e2e/context/TestContext.java index d57e105..0846b0b 100644 --- a/src/test/java/org/unicitylabs/sdk/e2e/context/TestContext.java +++ b/src/test/java/org/unicitylabs/sdk/e2e/context/TestContext.java @@ -190,7 +190,7 @@ public void setAggregatorClients(List aggregatorClients) { public int getConfiguredTokensPerUser() { return configuredTokensPerUser; } public void setConfiguredTokensPerUser(int configuredTokensPerUser) { this.configuredTokensPerUser = configuredTokensPerUser; } - public void savePendingTransfer(String user, Token token, TransferTransaction tx) { + public void savePendingTransfer(String user, Token token, TransferTransaction tx) { pendingTransfers.computeIfAbsent(user, k -> new ArrayList<>()) .add(new PendingTransfer(token, tx)); } diff --git a/src/test/java/org/unicitylabs/sdk/e2e/steps/AdvancedStepDefinitions.java b/src/test/java/org/unicitylabs/sdk/e2e/steps/AdvancedStepDefinitions.java index 2eec822..c0bb800 100644 --- a/src/test/java/org/unicitylabs/sdk/e2e/steps/AdvancedStepDefinitions.java +++ b/src/test/java/org/unicitylabs/sdk/e2e/steps/AdvancedStepDefinitions.java @@ -25,7 +25,7 @@ import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenType; import org.unicitylabs.sdk.token.fungible.TokenCoinData; -import org.unicitylabs.sdk.transaction.Transaction; +import org.unicitylabs.sdk.transaction.DefaultMintReasonFactory; import org.unicitylabs.sdk.transaction.TransferTransaction; import org.unicitylabs.sdk.utils.TestUtils; import org.unicitylabs.sdk.utils.helpers.PendingTransfer; @@ -101,7 +101,11 @@ public void theTokenIsTransferredThroughTheChain() throws Exception { @Then("the final token should maintain original properties") public void theFinalTokenShouldMaintainOriginalProperties() { assertNotNull(context.getChainToken(), "Final token should exist"); - assertTrue(context.getChainToken().verify(context.getTrustBase()).isSuccessful(), "Final token should be valid"); + assertTrue(context.getChainToken().verify( + context.getTrustBase(), + // TODO: Add this to global variable + new DefaultMintReasonFactory() + ).isSuccessful(), "Final token should be valid"); // Additional property validation can be added based on requirements } @@ -188,7 +192,11 @@ public void userShouldOwnTokens(String username, int expectedTokenCount) { SigningService signingService = SigningService.createFromSecret( context.getUserSecret().get(username) ); - assertTrue(token.verify(context.getTrustBase()).isSuccessful(), "Token should be valid"); + assertTrue(token.verify( + context.getTrustBase(), + // TODO: Add this to global variable + new DefaultMintReasonFactory() + ).isSuccessful(), "Token should be valid"); assertTrue(TestUtils .validateTokenOwnership( token, @@ -203,7 +211,11 @@ public void userShouldOwnTokens(String username, int expectedTokenCount) { public void allNameTagTokensShouldRemainValid(String username) { List nametags = context.getNameTagTokens().get(username); for (Token nametag : nametags) { - assertTrue(nametag.verify(context.getTrustBase()).isSuccessful(), "All name tag tokens should remain valid"); + assertTrue(nametag.verify( + context.getTrustBase(), + // TODO: Add this to global variable + new DefaultMintReasonFactory() + ).isSuccessful(), "All name tag tokens should remain valid"); } } @@ -256,7 +268,7 @@ public void finalizesAllReceivedTokens(String username) throws Exception { List pendingTransfers = context.getPendingTransfers(username); for (PendingTransfer pending : pendingTransfers) { - Token token = pending.getSourceToken(); + Token token = pending.getSourceToken(); TransferTransaction tx = pending.getTransaction(); helper.finalizeTransfer( username, @@ -274,7 +286,7 @@ public void createsNametagCountTokens(String username, int quantity) throws Exce TokenType tokenType = TestUtils.generateRandomTokenType(); TokenCoinData coinData = TestUtils.createRandomCoinData(1); - Token token = TestUtils.mintTokenForUser( + Token token = TestUtils.mintTokenForUser( context.getClient(), context.getUserSigningServices().get(username), context.getUserNonces().get(username), diff --git a/src/test/java/org/unicitylabs/sdk/e2e/steps/StepDefinitions.java b/src/test/java/org/unicitylabs/sdk/e2e/steps/StepDefinitions.java index e799d47..7a672ce 100644 --- a/src/test/java/org/unicitylabs/sdk/e2e/steps/StepDefinitions.java +++ b/src/test/java/org/unicitylabs/sdk/e2e/steps/StepDefinitions.java @@ -1,30 +1,36 @@ package org.unicitylabs.sdk.e2e.steps; -import org.unicitylabs.sdk.address.ProxyAddress; -import org.unicitylabs.sdk.e2e.config.CucumberConfiguration; -import org.unicitylabs.sdk.e2e.context.TestContext; -import org.unicitylabs.sdk.e2e.steps.shared.StepHelper; -import org.unicitylabs.sdk.token.fungible.CoinId; -import org.unicitylabs.sdk.utils.TestUtils; -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.token.fungible.TokenCoinData; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + import io.cucumber.java.en.And; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; - import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; - - -import static org.junit.jupiter.api.Assertions.*; +import org.unicitylabs.sdk.address.ProxyAddress; +import org.unicitylabs.sdk.e2e.config.CucumberConfiguration; +import org.unicitylabs.sdk.e2e.context.TestContext; +import org.unicitylabs.sdk.e2e.steps.shared.StepHelper; +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.token.fungible.CoinId; +import org.unicitylabs.sdk.token.fungible.TokenCoinData; +import org.unicitylabs.sdk.transaction.DefaultMintReasonFactory; +import org.unicitylabs.sdk.utils.TestUtils; /** * Refactored step definitions that use TestContext and SharedStepDefinitions. @@ -116,7 +122,11 @@ public void theTokenShouldBeMintedSuccessfully() { public void theTokenShouldBeVerifiedSuccessfully() { String user = context.getCurrentUser(); Token token = context.getUserToken(user); - assertTrue(token.verify(context.getTrustBase()).isSuccessful(), "Token should be verified successfully"); + assertTrue(token.verify( + context.getTrustBase(), + // TODO: Add this to global variable + new DefaultMintReasonFactory() + ).isSuccessful(), "Token should be verified successfully"); } @And("the token should belong to the user") @@ -139,7 +149,11 @@ public void theNameTagTokenShouldBeCreatedSuccessfully() { String user = context.getCurrentUser(); Token nametagToken = context.getNameTagToken(user); assertNotNull(nametagToken, "Name tag token should be created"); - assertTrue(nametagToken.verify(context.getTrustBase()).isSuccessful(), "Name tag token should be valid"); + assertTrue(nametagToken.verify( + context.getTrustBase(), + // TODO: Add this to global variable + new DefaultMintReasonFactory() + ).isSuccessful(), "Name tag token should be valid"); } @And("the name tag should be usable for proxy addressing") diff --git a/src/test/java/org/unicitylabs/sdk/e2e/steps/shared/SharedStepDefinitions.java b/src/test/java/org/unicitylabs/sdk/e2e/steps/shared/SharedStepDefinitions.java index 890ad2b..da75ad1 100644 --- a/src/test/java/org/unicitylabs/sdk/e2e/steps/shared/SharedStepDefinitions.java +++ b/src/test/java/org/unicitylabs/sdk/e2e/steps/shared/SharedStepDefinitions.java @@ -9,7 +9,9 @@ import org.unicitylabs.sdk.e2e.context.TestContext; import org.unicitylabs.sdk.predicate.PredicateEngineService; import org.unicitylabs.sdk.predicate.embedded.UnmaskedPredicate; +import org.unicitylabs.sdk.predicate.embedded.UnmaskedPredicateReference; import org.unicitylabs.sdk.serializer.UnicityObjectMapper; +import org.unicitylabs.sdk.transaction.DefaultMintReasonFactory; import org.unicitylabs.sdk.utils.TestUtils; import org.unicitylabs.sdk.hash.DataHash; import org.unicitylabs.sdk.hash.HashAlgorithm; @@ -228,7 +230,7 @@ public void userMintsATokenWithRandomCoinData(String username) throws Exception TokenType tokenType = TestUtils.generateRandomTokenType();; TokenCoinData coinData = TestUtils.createRandomCoinData(2); - Token token = TestUtils.mintTokenForUser( + Token token = TestUtils.mintTokenForUser( context.getClient(), context.getUserSigningServices().get(username), context.getUserNonces().get(username), @@ -261,16 +263,13 @@ public void userTransfersTheTokenToUserUsingAnUnmaskedPredicate(String fromUser, Token sourceToken = context.getUserToken(fromUser); SigningService toSigningService = context.getUserSigningServices().get(toUser); - UnmaskedPredicate userPredicate = UnmaskedPredicate.create( - sourceToken.getId(), + UnmaskedPredicateReference userPredicateReference = UnmaskedPredicateReference.create( sourceToken.getType(), toSigningService, - HashAlgorithm.SHA256, - context.getUserNonces().get(toUser) + HashAlgorithm.SHA256 ); - context.getUserPredicate().put(toUser, userPredicate); - DirectAddress toAddress = userPredicate.getReference().toAddress(); + DirectAddress toAddress = userPredicateReference.toAddress(); helper.transferToken(fromUser, toUser, sourceToken, toAddress, null); } @@ -280,7 +279,11 @@ public void userShouldOwnTheTokenSuccessfully(String username) { Token token = context.getUserToken(username); context.setCurrentUser(username); SigningService signingService = context.getUserSigningServices().get(username); - VerificationResult result = token.verify(context.getTrustBase()); + VerificationResult result = token.verify( + context.getTrustBase(), + // TODO: Use already defined DefaultMintReasonFactory + new DefaultMintReasonFactory() + ); assertTrue(result.isSuccessful(), "Token should be valid"); // assertTrue(token.getState().getPredicate().getEngine().equals(signingService.getPublicKey()), username + " should own the token"); assertTrue(PredicateEngineService.createPredicate(token.getState().getPredicate()).isOwner(signingService.getPublicKey()), username + " should own the token"); @@ -318,7 +321,11 @@ public void userCreateANametagTokenWithCustomData(String username, String custom customData ); assertNotNull(nametagToken, "Name tag token should be created"); - assertTrue(nametagToken.verify(context.getTrustBase()).isSuccessful(), "Name tag token should be valid"); + assertTrue(nametagToken.verify( + context.getTrustBase(), + // TODO: Use already defined DefaultMintReasonFactory + new DefaultMintReasonFactory() + ).isSuccessful(), "Name tag token should be valid"); context.addNameTagToken(username, nametagToken); } diff --git a/src/test/java/org/unicitylabs/sdk/e2e/steps/shared/StepHelper.java b/src/test/java/org/unicitylabs/sdk/e2e/steps/shared/StepHelper.java index 999ecbb..1e5296c 100644 --- a/src/test/java/org/unicitylabs/sdk/e2e/steps/shared/StepHelper.java +++ b/src/test/java/org/unicitylabs/sdk/e2e/steps/shared/StepHelper.java @@ -7,7 +7,6 @@ import org.unicitylabs.sdk.api.CertificationData; import org.unicitylabs.sdk.api.CertificationResponse; import org.unicitylabs.sdk.api.CertificationStatus; -import org.unicitylabs.sdk.api.StateId; import org.unicitylabs.sdk.e2e.config.CucumberConfiguration; import org.unicitylabs.sdk.e2e.context.TestContext; import org.unicitylabs.sdk.hash.DataHash; @@ -83,10 +82,12 @@ public Token createNameTagTokenForUser(String username, Token token, String name context.getTrustBase(), nametagMintCommitment ).get(); - MintTransaction nametagGenesis = nametagMintCommitment.toTransaction(inclusionProof); + MintTransaction nametagGenesis = nametagMintCommitment.toTransaction(inclusionProof); - return Token.create( + return Token.mint( context.getTrustBase(), + // TODO: Add this to global variable + new DefaultMintReasonFactory(), new TokenState(nametagPredicate, null), nametagGenesis ); @@ -131,14 +132,14 @@ public void transferToken(String fromUser, String toUser, Token token, Address t context.savePendingTransfer(toUser, token, transferTransaction); } - public void finalizeTransfer(String username, Token token, TransferTransaction tx) throws Exception { + public void finalizeTransfer(String username, Token token, TransferTransaction tx) throws Exception { byte[] secret = context.getUserSecret().get(username); - Token currentNameTagToken = context.getNameTagToken(username); + Token currentNameTagToken = context.getNameTagToken(username); List nametagTokens = context.getNameTagTokens().get(username); if (nametagTokens != null && !nametagTokens.isEmpty()) { - for (Token t : nametagTokens) { + for (Token t : nametagTokens) { String actualNametagAddress = tx.getData().getRecipient().getAddress(); String expectedProxyAddress = ProxyAddress.create(t.getId()).getAddress(); @@ -149,7 +150,7 @@ public void finalizeTransfer(String username, Token token, TransferTransactio } } - List> additionalTokens = new ArrayList<>(); + List additionalTokens = new ArrayList<>(); if (currentNameTagToken != null) { additionalTokens.add(currentNameTagToken); } @@ -160,9 +161,9 @@ public void finalizeTransfer(String username, Token token, TransferTransactio unlockPredicate = UnmaskedPredicate.create( token.getId(), token.getType(), + tx, context.getUserSigningServices().get(username), - HashAlgorithm.SHA256, - tx.getData().getSalt() + HashAlgorithm.SHA256 ); } @@ -173,6 +174,8 @@ public void finalizeTransfer(String username, Token token, TransferTransactio Token finalizedToken = context.getClient().finalizeTransaction( context.getTrustBase(), + // TODO: Add this to global variable + new DefaultMintReasonFactory(), token, recipientState, tx, diff --git a/src/test/java/org/unicitylabs/sdk/functional/FunctionalUnsignedPredicateDoubleSpendPreventionTest.java b/src/test/java/org/unicitylabs/sdk/functional/FunctionalUnsignedPredicateDoubleSpendPreventionTest.java index 78439c6..2d28d16 100644 --- a/src/test/java/org/unicitylabs/sdk/functional/FunctionalUnsignedPredicateDoubleSpendPreventionTest.java +++ b/src/test/java/org/unicitylabs/sdk/functional/FunctionalUnsignedPredicateDoubleSpendPreventionTest.java @@ -23,6 +23,8 @@ import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenState; import org.unicitylabs.sdk.token.TokenType; +import org.unicitylabs.sdk.transaction.DefaultMintReasonFactory; +import org.unicitylabs.sdk.transaction.MintReasonFactory; import org.unicitylabs.sdk.transaction.TransferCommitment; import org.unicitylabs.sdk.transaction.TransferTransaction; import org.unicitylabs.sdk.util.HexConverter; @@ -35,9 +37,11 @@ public class FunctionalUnsignedPredicateDoubleSpendPreventionTest { protected StateTransitionClient client; protected RootTrustBase trustBase; + private final MintReasonFactory mintReasonFactory = new DefaultMintReasonFactory(); + private final byte[] BOB_SECRET = "BOB_SECRET".getBytes(StandardCharsets.UTF_8); - private String[] transferToken(Token token, byte[] secret, Address address) throws Exception { + private String[] transferToken(Token token, byte[] secret, Address address) throws Exception { TransferCommitment commitment = TransferCommitment.create( token, address, @@ -64,10 +68,11 @@ private String[] transferToken(Token token, byte[] secret, Address address) t }; } - private Token mintToken(byte[] secret) throws Exception { + private Token mintToken(byte[] secret) throws Exception { return TokenUtils.mintToken( this.client, this.trustBase, + this.mintReasonFactory, secret, new TokenId(randomBytes(32)), new TokenType(HexConverter.decode( @@ -80,23 +85,24 @@ private Token mintToken(byte[] secret) throws Exception { ); } - private Token receiveToken(String[] tokenInfo, byte[] secret) throws Exception { - Token token = Token.fromJson(tokenInfo[0]); + private Token receiveToken(String[] tokenInfo, byte[] secret) throws Exception { + Token token = Token.fromJson(tokenInfo[0]); TransferTransaction transaction = TransferTransaction.fromJson(tokenInfo[1]); TokenState state = new TokenState( UnmaskedPredicate.create( token.getId(), token.getType(), + transaction, SigningService.createFromSecret(secret), - HashAlgorithm.SHA256, - transaction.getData().getSalt() + HashAlgorithm.SHA256 ), null ); return this.client.finalizeTransaction( this.trustBase, + this.mintReasonFactory, token, state, transaction, @@ -113,7 +119,7 @@ void setUp() { @Test void testDoubleSpend() throws Exception { - Token token = mintToken(BOB_SECRET); + Token token = mintToken(BOB_SECRET); UnmaskedPredicateReference reference = UnmaskedPredicateReference.create( token.getType(), @@ -125,13 +131,13 @@ void testDoubleSpend() throws Exception { receiveToken( transferToken(token, BOB_SECRET, reference.toAddress()), BOB_SECRET - ).verify(trustBase).isSuccessful()); + ).verify(this.trustBase, this.mintReasonFactory).isSuccessful()); RuntimeException ex = Assertions.assertThrows( RuntimeException.class, () -> receiveToken( transferToken(token, BOB_SECRET, reference.toAddress()), BOB_SECRET - ).verify(trustBase) + ).verify(this.trustBase, this.mintReasonFactory) ); Assertions.assertInstanceOf(BranchExistsException.class, ex.getCause()); diff --git a/src/test/java/org/unicitylabs/sdk/token/TokenSplitBuilderTest.java b/src/test/java/org/unicitylabs/sdk/token/TokenSplitBuilderTest.java index b698f35..eca5eb2 100644 --- a/src/test/java/org/unicitylabs/sdk/token/TokenSplitBuilderTest.java +++ b/src/test/java/org/unicitylabs/sdk/token/TokenSplitBuilderTest.java @@ -1,6 +1,6 @@ package org.unicitylabs.sdk.token; -import java.io.IOException; +import io.cucumber.java.ro.Si; import java.math.BigInteger; import java.util.List; import java.util.Map; @@ -16,53 +16,55 @@ import org.unicitylabs.sdk.mtree.plain.SparseMerkleTreePathFixture; import org.unicitylabs.sdk.predicate.Predicate; import org.unicitylabs.sdk.predicate.embedded.MaskedPredicate; +import org.unicitylabs.sdk.predicate.embedded.MaskedPredicateReference; 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.InclusionProofFixture; import org.unicitylabs.sdk.transaction.MintTransaction; +import org.unicitylabs.sdk.transaction.MintTransaction.Data; import org.unicitylabs.sdk.transaction.MintTransactionFixture; import org.unicitylabs.sdk.transaction.split.TokenSplitBuilder; -import org.unicitylabs.sdk.verification.VerificationException; public class TokenSplitBuilderTest { - private Token createToken(TokenCoinData coinData) { - SigningService signingService = new SigningService(SigningService.generatePrivateKey()); + private Token createToken(TokenCoinData coinData) { UnicityCertificate unicityCertificate = UnicityCertificateUtils.generateCertificate( - signingService, DataHash.fromImprint(new byte[34])); + new SigningService(SigningService.generatePrivateKey()), DataHash.fromImprint(new byte[34])); TokenId tokenId = new TokenId(new byte[10]); TokenType tokenType = new TokenType(new byte[10]); + byte[] nonce = new byte[32]; - Predicate predicate = new MaskedPredicate( + SigningService signingService = SigningService.createFromMaskedSecret("SECRET".getBytes(), nonce); + + MintTransaction transaction = MintTransactionFixture.create( + new Data( + tokenId, + tokenType, + null, + coinData, + MaskedPredicateReference.create(tokenType, signingService, HashAlgorithm.SHA256, nonce).toAddress(), + new byte[20] + ), + InclusionProofFixture.create( + SparseMerkleTreePathFixture.create(), + null, + unicityCertificate + ) + ); + + Predicate predicate = MaskedPredicate.create( tokenId, tokenType, - new byte[32], - "secp256k1", + signingService, HashAlgorithm.SHA256, - new byte[32] + nonce ); - return new Token<>( + return new Token( new TokenState(predicate, null), - MintTransactionFixture.create( - new MintTransaction.Data<>( - tokenId, - tokenType, - null, - coinData, - predicate.getReference().toAddress(), - new byte[20], - null, - null - ), - InclusionProofFixture.create( - SparseMerkleTreePathFixture.create(), - null, - unicityCertificate - ) - ), + transaction, List.of(), List.of() ); @@ -71,7 +73,7 @@ private Token createToken(TokenCoinData coinData) { @Test public void testTokenSplitIntoMultipleTokens() throws LeafOutOfBoundsException, BranchExistsException { - Token token = this.createToken( + Token token = this.createToken( new TokenCoinData( Map.of( new CoinId("coin1".getBytes()), @@ -79,11 +81,10 @@ public void testTokenSplitIntoMultipleTokens() ) )); - Predicate predicate = new MaskedPredicate( + Predicate predicate = MaskedPredicate.create( token.getId(), token.getType(), - new byte[32], - "secp256k1", + SigningService.createFromSecret(SigningService.generatePrivateKey()), HashAlgorithm.SHA256, new byte[32] ); @@ -145,13 +146,12 @@ public void testTokenSplitIntoMultipleTokens() @Test public void testTokenSplitUnknownSplitCoin() { - Token token = this.createToken(null); + Token token = this.createToken(null); - Predicate predicate = new MaskedPredicate( + Predicate predicate = MaskedPredicate.create( token.getId(), token.getType(), - new byte[32], - "secp256k1", + SigningService.createFromSecret(SigningService.generatePrivateKey()), HashAlgorithm.SHA256, new byte[32] ); diff --git a/src/test/java/org/unicitylabs/sdk/token/TokenTest.java b/src/test/java/org/unicitylabs/sdk/token/TokenTest.java index 4de056b..6cc2754 100644 --- a/src/test/java/org/unicitylabs/sdk/token/TokenTest.java +++ b/src/test/java/org/unicitylabs/sdk/token/TokenTest.java @@ -32,7 +32,7 @@ public void testJsonSerialization() throws IOException { UnicityCertificate unicityCertificate = UnicityCertificateUtils.generateCertificate( signingService, DataHash.fromImprint(new byte[34])); - MintTransaction.Data genesisData = new MintTransaction.Data<>( + MintTransaction.Data genesisData = new MintTransaction.Data( new TokenId(TestUtils.randomBytes(32)), new TokenType(TestUtils.randomBytes(32)), TestUtils.randomBytes(10), @@ -43,9 +43,7 @@ public void testJsonSerialization() throws IOException { ) ), DirectAddress.create(new DataHash(HashAlgorithm.SHA256, TestUtils.randomBytes(32))), - TestUtils.randomBytes(32), - null, - null + TestUtils.randomBytes(32) ); byte[] nametagNonce = TestUtils.randomBytes(32); @@ -57,7 +55,7 @@ public void testJsonSerialization() throws IOException { DirectAddress.create(new DataHash(HashAlgorithm.SHA256, TestUtils.randomBytes(32))) ); - Token nametagToken = new Token<>( + Token nametagToken = new Token( new TokenState( MaskedPredicate.create( nametagGenesisData.getTokenId(), @@ -78,7 +76,7 @@ public void testJsonSerialization() throws IOException { List.of() ); - Token token = new Token<>( + Token token = new Token( new TokenState( MaskedPredicate.create( genesisData.getTokenId(), diff --git a/src/test/java/org/unicitylabs/sdk/transaction/CommitmentTest.java b/src/test/java/org/unicitylabs/sdk/transaction/CommitmentTest.java index 102866b..d54b5b2 100644 --- a/src/test/java/org/unicitylabs/sdk/transaction/CommitmentTest.java +++ b/src/test/java/org/unicitylabs/sdk/transaction/CommitmentTest.java @@ -28,7 +28,7 @@ public void testJsonSerialization() throws IOException { MaskedPredicateReference predicateReference = MaskedPredicateReference.create(tokenType, signingService.getAlgorithm(), signingService.getPublicKey(), HashAlgorithm.SHA256, nonce); - MintTransaction.Data transactionData = new MintTransaction.Data<>( + MintTransaction.Data transactionData = new MintTransaction.Data( new TokenId(new byte[32]), tokenType, new byte[5], @@ -38,10 +38,9 @@ public void testJsonSerialization() throws IOException { )), predicateReference.toAddress(), new byte[10], - new DataHash(HashAlgorithm.SHA256, new byte[32]), - null + new DataHash(HashAlgorithm.SHA256, new byte[32]) ); - MintCommitment commitment = MintCommitment.create(transactionData); + MintCommitment commitment = MintCommitment.create(transactionData); ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new Jdk8Module()); diff --git a/src/test/java/org/unicitylabs/sdk/transaction/MintTransactionFixture.java b/src/test/java/org/unicitylabs/sdk/transaction/MintTransactionFixture.java index 5f85623..71f05ce 100644 --- a/src/test/java/org/unicitylabs/sdk/transaction/MintTransactionFixture.java +++ b/src/test/java/org/unicitylabs/sdk/transaction/MintTransactionFixture.java @@ -1,10 +1,10 @@ package org.unicitylabs.sdk.transaction; public class MintTransactionFixture { - public static MintTransaction create( - MintTransaction.Data data, + public static MintTransaction create( + MintTransaction.Data data, InclusionProof inclusionProof ) { - return new MintTransaction<>(data, inclusionProof); + return new MintTransaction(data, inclusionProof); } } diff --git a/src/test/java/org/unicitylabs/sdk/utils/TestUtils.java b/src/test/java/org/unicitylabs/sdk/utils/TestUtils.java index c6c7173..5363c2f 100644 --- a/src/test/java/org/unicitylabs/sdk/utils/TestUtils.java +++ b/src/test/java/org/unicitylabs/sdk/utils/TestUtils.java @@ -24,6 +24,7 @@ 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.DefaultMintReasonFactory; import org.unicitylabs.sdk.transaction.InclusionProof; import org.unicitylabs.sdk.transaction.MintCommitment; import org.unicitylabs.sdk.transaction.MintTransaction; @@ -38,7 +39,7 @@ public class TestUtils { private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; private static final SecureRandom RANDOM = new SecureRandom(); - + /** * Generate random bytes of specified length. */ @@ -47,14 +48,14 @@ public static byte[] randomBytes(int length) { RANDOM.nextBytes(bytes); return bytes; } - + /** * Generate a random coin amount between 10 and 99. */ public static BigInteger randomCoinAmount() { return BigInteger.valueOf(10 + RANDOM.nextInt(90)); } - + /** * Create random coin data with specified number of coins. */ @@ -71,7 +72,7 @@ public static TokenCoinData randomCoinData(int numCoins) { /** * Creates a token mint commitment and submits it to the client */ - public static Token mintTokenForUser( + public static Token mintTokenForUser( StateTransitionClient client, SigningService signingService, byte[] nonce, @@ -85,16 +86,14 @@ public static Token mintTokenForUser( Address address = predicate.getReference().toAddress(); TokenState tokenState = new TokenState(predicate, null); - MintCommitment mintCommitment = MintCommitment.create( - new MintTransaction.Data<>( + MintCommitment mintCommitment = MintCommitment.create( + new MintTransaction.Data( tokenId, tokenType, new TestTokenData(randomBytes(32)).getData(), coinData, address, - randomBytes(5), - null, - null + randomBytes(5) ) ); @@ -109,8 +108,10 @@ public static Token mintTokenForUser( trustBase, mintCommitment ).get(); - return Token.create( + return Token.mint( trustBase, + // TODO: Add this to global variable + new DefaultMintReasonFactory(), tokenState, mintCommitment.toTransaction(inclusionProof) ); @@ -119,15 +120,15 @@ public static Token mintTokenForUser( /** * Transfers a token from one user to another */ - public static Token transferToken( + public static Token transferToken( StateTransitionClient client, - Token sourceToken, + Token sourceToken, SigningService fromSigningService, SigningService toSigningService, byte[] toNonce, Address toAddress, byte[] customData, - List> additionalTokens, + List additionalTokens, RootTrustBase trustBase ) throws Exception { @@ -169,6 +170,8 @@ public static Token transferToken( // Finalize transaction return client.finalizeTransaction( trustBase, + // TODO: Add this to global variable + new DefaultMintReasonFactory(), sourceToken, new TokenState(toPredicate, customData), transferTransaction @@ -238,8 +241,12 @@ public static void setupUser(String userName, /** * Validates that a token is properly owned by a signing service */ - public static boolean validateTokenOwnership(Token token, SigningService signingService, RootTrustBase trustBase) { - if (!token.verify(trustBase).isSuccessful()) { + public static boolean validateTokenOwnership(Token token, SigningService signingService, RootTrustBase trustBase) { + if (!token.verify( + trustBase, + // TODO: Add this to global variable + new DefaultMintReasonFactory() + ).isSuccessful()) { return false; } return PredicateEngineService.createPredicate(token.getState().getPredicate()).isOwner(signingService.getPublicKey()); @@ -302,17 +309,17 @@ public static void validateSuccessRate(long successful, long total, double minSu public static class TokenOperationResult { private final boolean success; private final String message; - private final Token token; + private final Token token; private final Exception error; - public TokenOperationResult(boolean success, String message, Token token, Exception error) { + public TokenOperationResult(boolean success, String message, Token token, Exception error) { this.success = success; this.message = message; this.token = token; this.error = error; } - public static TokenOperationResult success(String message, Token token) { + public static TokenOperationResult success(String message, Token token) { return new TokenOperationResult(true, message, token, null); } @@ -322,7 +329,7 @@ public static TokenOperationResult failure(String message, Exception error) { public boolean isSuccess() { return success; } public String getMessage() { return message; } - public Token getToken() { return token; } + public Token getToken() { return token; } public Exception getError() { return error; } } diff --git a/src/test/java/org/unicitylabs/sdk/utils/TokenUtils.java b/src/test/java/org/unicitylabs/sdk/utils/TokenUtils.java index 38b17ab..1e20ff8 100644 --- a/src/test/java/org/unicitylabs/sdk/utils/TokenUtils.java +++ b/src/test/java/org/unicitylabs/sdk/utils/TokenUtils.java @@ -20,19 +20,22 @@ import org.unicitylabs.sdk.token.fungible.TokenCoinData; import org.unicitylabs.sdk.transaction.InclusionProof; import org.unicitylabs.sdk.transaction.MintCommitment; +import org.unicitylabs.sdk.transaction.MintReasonFactory; import org.unicitylabs.sdk.transaction.MintTransaction; import org.unicitylabs.sdk.util.InclusionProofUtils; public class TokenUtils { - public static Token mintToken( + public static Token mintToken( StateTransitionClient client, RootTrustBase trustBase, + MintReasonFactory mintReasonFactory, byte[] secret ) throws Exception { return TokenUtils.mintToken( client, trustBase, + mintReasonFactory, secret, new TokenId(randomBytes(32)), new TokenType(randomBytes(32)), @@ -44,9 +47,10 @@ public static Token mintToken( ); } - public static Token mintToken( + public static Token mintToken( StateTransitionClient client, RootTrustBase trustBase, + MintReasonFactory mintReasonFactory, byte[] secret, TokenId tokenId, TokenType tokenType, @@ -69,16 +73,15 @@ public static Token mintToken( Address address = predicate.getReference().toAddress(); TokenState tokenState = new TokenState(predicate, null); - MintCommitment commitment = MintCommitment.create( - new MintTransaction.Data<>( + MintCommitment commitment = MintCommitment.create( + new MintTransaction.Data( tokenId, tokenType, tokenData, coinData, address, salt, - dataHash, - null + dataHash ) ); @@ -99,16 +102,18 @@ public static Token mintToken( ).get(); // Create mint transaction - return Token.create( + return Token.mint( trustBase, + mintReasonFactory, tokenState, commitment.toTransaction(inclusionProof) ); } - public static Token mintNametagToken( + public static Token mintNametagToken( StateTransitionClient client, RootTrustBase trustBase, + MintReasonFactory mintReasonFactory, byte[] secret, String nametag, Address targetAddress @@ -116,6 +121,7 @@ public static Token mintNametagToken( return mintNametagToken( client, trustBase, + mintReasonFactory, secret, new TokenType(randomBytes(32)), nametag, @@ -125,9 +131,10 @@ public static Token mintNametagToken( ); } - public static Token mintNametagToken( + public static Token mintNametagToken( StateTransitionClient client, RootTrustBase trustBase, + MintReasonFactory mintReasonFactory, byte[] secret, TokenType tokenType, String nametag, @@ -143,7 +150,7 @@ public static Token mintNametagToken( HashAlgorithm.SHA256, nonce).toAddress(); - MintCommitment commitment = MintCommitment.create( + MintCommitment commitment = MintCommitment.create( new MintTransaction.NametagData( nametag, tokenType, @@ -170,8 +177,9 @@ public static Token mintNametagToken( ).get(); // Create mint transaction - return Token.create( + return Token.mint( trustBase, + mintReasonFactory, new TokenState( MaskedPredicate.create( commitment.getTransactionData().getTokenId(), From f52af11d81ae4709470f481a2d651ed36a170865 Mon Sep 17 00:00:00 2001 From: Martti Marran Date: Tue, 2 Dec 2025 01:59:39 +0200 Subject: [PATCH 2/2] Remove unused imports --- .../unicitylabs/sdk/predicate/embedded/MaskedPredicate.java | 2 -- .../sdk/predicate/embedded/UnmaskedPredicate.java | 1 - .../unicitylabs/sdk/transaction/split/SplitMintReason.java | 4 ---- src/test/java/org/unicitylabs/sdk/api/StateIdTest.java | 5 ++--- src/test/java/org/unicitylabs/sdk/bft/RootTrustBaseTest.java | 1 - .../java/org/unicitylabs/sdk/bft/UnicityCertificateTest.java | 1 - src/test/java/org/unicitylabs/sdk/hash/DataHashTest.java | 1 - .../org/unicitylabs/sdk/token/TokenSplitBuilderTest.java | 1 - src/test/java/org/unicitylabs/sdk/token/TokenTest.java | 1 - 9 files changed, 2 insertions(+), 15 deletions(-) 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 6987ab8..c8ce268 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/MaskedPredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/MaskedPredicate.java @@ -4,10 +4,8 @@ import org.unicitylabs.sdk.hash.HashAlgorithm; import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; 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.MintTransaction; /** * Masked predicate. 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 bd395f3..f785a4b 100644 --- a/src/main/java/org/unicitylabs/sdk/predicate/embedded/UnmaskedPredicate.java +++ b/src/main/java/org/unicitylabs/sdk/predicate/embedded/UnmaskedPredicate.java @@ -11,7 +11,6 @@ import org.unicitylabs.sdk.token.Token; import org.unicitylabs.sdk.token.TokenId; import org.unicitylabs.sdk.token.TokenType; -import org.unicitylabs.sdk.transaction.MintTransaction; import org.unicitylabs.sdk.transaction.Transaction; import org.unicitylabs.sdk.transaction.TransferTransaction; 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 8692e44..246a499 100644 --- a/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReason.java +++ b/src/main/java/org/unicitylabs/sdk/transaction/split/SplitMintReason.java @@ -1,9 +1,6 @@ package org.unicitylabs.sdk.transaction.split; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; import java.math.BigInteger; import java.util.Arrays; import java.util.List; @@ -15,7 +12,6 @@ import org.unicitylabs.sdk.predicate.PredicateEngineService; import org.unicitylabs.sdk.predicate.embedded.BurnPredicate; import org.unicitylabs.sdk.serializer.cbor.CborDeserializer; -import org.unicitylabs.sdk.serializer.cbor.CborDeserializer.CborNumber; import org.unicitylabs.sdk.serializer.cbor.CborSerializer; import org.unicitylabs.sdk.token.Token; import org.unicitylabs.sdk.token.fungible.CoinId; diff --git a/src/test/java/org/unicitylabs/sdk/api/StateIdTest.java b/src/test/java/org/unicitylabs/sdk/api/StateIdTest.java index b9fa620..0ceaaf6 100644 --- a/src/test/java/org/unicitylabs/sdk/api/StateIdTest.java +++ b/src/test/java/org/unicitylabs/sdk/api/StateIdTest.java @@ -1,11 +1,10 @@ package org.unicitylabs.sdk.api; -import com.fasterxml.jackson.core.JsonProcessingException; -import org.unicitylabs.sdk.hash.DataHash; -import org.unicitylabs.sdk.hash.HashAlgorithm; import java.math.BigInteger; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.unicitylabs.sdk.hash.DataHash; +import org.unicitylabs.sdk.hash.HashAlgorithm; public class StateIdTest { diff --git a/src/test/java/org/unicitylabs/sdk/bft/RootTrustBaseTest.java b/src/test/java/org/unicitylabs/sdk/bft/RootTrustBaseTest.java index 919cfb3..ec975b0 100644 --- a/src/test/java/org/unicitylabs/sdk/bft/RootTrustBaseTest.java +++ b/src/test/java/org/unicitylabs/sdk/bft/RootTrustBaseTest.java @@ -1,6 +1,5 @@ package org.unicitylabs.sdk.bft; -import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/unicitylabs/sdk/bft/UnicityCertificateTest.java b/src/test/java/org/unicitylabs/sdk/bft/UnicityCertificateTest.java index ad03b92..224bacb 100644 --- a/src/test/java/org/unicitylabs/sdk/bft/UnicityCertificateTest.java +++ b/src/test/java/org/unicitylabs/sdk/bft/UnicityCertificateTest.java @@ -1,6 +1,5 @@ package org.unicitylabs.sdk.bft; -import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.unicitylabs.sdk.util.HexConverter; diff --git a/src/test/java/org/unicitylabs/sdk/hash/DataHashTest.java b/src/test/java/org/unicitylabs/sdk/hash/DataHashTest.java index 31dd336..e3e502e 100644 --- a/src/test/java/org/unicitylabs/sdk/hash/DataHashTest.java +++ b/src/test/java/org/unicitylabs/sdk/hash/DataHashTest.java @@ -1,6 +1,5 @@ package org.unicitylabs.sdk.hash; -import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.unicitylabs.sdk.serializer.json.JsonSerializationException; diff --git a/src/test/java/org/unicitylabs/sdk/token/TokenSplitBuilderTest.java b/src/test/java/org/unicitylabs/sdk/token/TokenSplitBuilderTest.java index eca5eb2..4a07a9b 100644 --- a/src/test/java/org/unicitylabs/sdk/token/TokenSplitBuilderTest.java +++ b/src/test/java/org/unicitylabs/sdk/token/TokenSplitBuilderTest.java @@ -1,6 +1,5 @@ package org.unicitylabs.sdk.token; -import io.cucumber.java.ro.Si; import java.math.BigInteger; import java.util.List; import java.util.Map; diff --git a/src/test/java/org/unicitylabs/sdk/token/TokenTest.java b/src/test/java/org/unicitylabs/sdk/token/TokenTest.java index 6cc2754..8853840 100644 --- a/src/test/java/org/unicitylabs/sdk/token/TokenTest.java +++ b/src/test/java/org/unicitylabs/sdk/token/TokenTest.java @@ -22,7 +22,6 @@ import org.unicitylabs.sdk.transaction.MintTransaction; import org.unicitylabs.sdk.transaction.MintTransactionFixture; import org.unicitylabs.sdk.utils.TestUtils; -import org.unicitylabs.sdk.verification.VerificationException; public class TokenTest {