Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/java/org/unicitylabs/sdk/address/ProxyAddress.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ public static Address resolve(Address inputAddress, List<Token<?>> nametags) {
Address targetAddress = inputAddress;
while (targetAddress.getScheme() != AddressScheme.DIRECT) {
Token<?> nametag = nametagMap.get(targetAddress);
if (nametag == null || nametag.getState().getData().isEmpty()) {
if (nametag == null || nametag.getData().isEmpty()) {
return null;
}

targetAddress = AddressFactory.createAddress(
new String(nametag.getState().getData().get(), StandardCharsets.UTF_8));
new String(nametag.getData().get(), StandardCharsets.UTF_8));
}

return targetAddress;
Expand Down
12 changes: 8 additions & 4 deletions src/main/java/org/unicitylabs/sdk/predicate/BurnPredicate.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.unicitylabs.sdk.hash.DataHash;
import org.unicitylabs.sdk.hash.DataHasher;
import org.unicitylabs.sdk.hash.HashAlgorithm;
import org.unicitylabs.sdk.serializer.UnicityObjectMapper;
import org.unicitylabs.sdk.serializer.cbor.CborSerializationException;
import org.unicitylabs.sdk.token.Token;
import org.unicitylabs.sdk.token.TokenId;
import org.unicitylabs.sdk.token.TokenType;
import org.unicitylabs.sdk.transaction.Transaction;
import org.unicitylabs.sdk.transaction.TransferTransactionData;
import java.util.Arrays;
import java.util.Objects;

public class BurnPredicate implements Predicate {
private final DataHash burnReason;
Expand Down Expand Up @@ -47,8 +49,10 @@ public boolean isOwner(byte[] publicKey) {
}

@Override
public boolean verify(Transaction<TransferTransactionData> transaction, TokenId tokenId,
TokenType tokenType) {
public boolean verify(
List<Transaction<TransferTransactionData>> transactions,
Token<?> token
) {
return false;
}

Expand Down
18 changes: 12 additions & 6 deletions src/main/java/org/unicitylabs/sdk/predicate/DefaultPredicate.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.unicitylabs.sdk.api.Authenticator;
import org.unicitylabs.sdk.api.RequestId;
import org.unicitylabs.sdk.hash.DataHash;
import org.unicitylabs.sdk.hash.DataHasher;
import org.unicitylabs.sdk.hash.HashAlgorithm;
import org.unicitylabs.sdk.serializer.UnicityObjectMapper;
import org.unicitylabs.sdk.token.Token;
import org.unicitylabs.sdk.token.TokenId;
import org.unicitylabs.sdk.token.TokenType;
import org.unicitylabs.sdk.transaction.InclusionProofVerificationStatus;
import org.unicitylabs.sdk.transaction.Transaction;
import org.unicitylabs.sdk.transaction.TransferTransactionData;
import org.unicitylabs.sdk.util.HexConverter;
import java.util.Arrays;
import java.util.Objects;

/**
* Base class for unmasked and masked predicates
Expand Down Expand Up @@ -91,8 +93,12 @@ public boolean isOwner(byte[] publicKey) {
}

@Override
public boolean verify(Transaction<TransferTransactionData> transaction, TokenId tokenId,
TokenType tokenType) {
public boolean verify(
List<Transaction<TransferTransactionData>> transactions,
Token<?> token
) {
Transaction<TransferTransactionData> transaction = transactions.get(transactions.size() - 1);

Authenticator authenticator = transaction.getInclusionProof().getAuthenticator().orElse(null);
DataHash transactionHash = transaction.getInclusionProof().getTransactionHash().orElse(null);

Expand All @@ -104,12 +110,12 @@ public boolean verify(Transaction<TransferTransactionData> transaction, TokenId
return false;
}

if (!authenticator.verify(transaction.getData().calculateHash(tokenId, tokenType))) {
if (!authenticator.verify(transaction.getData().calculateHash(token.getId(), token.getType()))) {
return false;
}

RequestId requestId = RequestId.create(this.publicKey,
transaction.getData().getSourceState().calculateHash(tokenId, tokenType));
transaction.getData().getSourceState().calculateHash(token.getId(), token.getType()));
return transaction.getInclusionProof().verify(requestId) == InclusionProofVerificationStatus.OK;
}

Expand Down
8 changes: 6 additions & 2 deletions src/main/java/org/unicitylabs/sdk/predicate/Predicate.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.unicitylabs.sdk.predicate;

import java.util.List;
import org.unicitylabs.sdk.hash.DataHash;
import org.unicitylabs.sdk.token.Token;
import org.unicitylabs.sdk.token.TokenId;
import org.unicitylabs.sdk.token.TokenType;
import org.unicitylabs.sdk.transaction.Transaction;
Expand All @@ -12,6 +14,8 @@ public interface Predicate {
IPredicateReference getReference(TokenType tokenType);
byte[] getNonce();
boolean isOwner(byte[] publicKey);
boolean verify(Transaction<TransferTransactionData> transaction, TokenId tokenId,
TokenType tokenType);
boolean verify(
List<Transaction<TransferTransactionData>> transactions,
Token<?> token
);
}
40 changes: 19 additions & 21 deletions src/main/java/org/unicitylabs/sdk/predicate/UnmaskedPredicate.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@

package org.unicitylabs.sdk.predicate;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.unicitylabs.sdk.hash.DataHash;
import java.util.List;
import org.unicitylabs.sdk.hash.DataHasher;
import org.unicitylabs.sdk.hash.HashAlgorithm;
import org.unicitylabs.sdk.serializer.UnicityObjectMapper;
import org.unicitylabs.sdk.serializer.cbor.CborSerializationException;
import org.unicitylabs.sdk.signing.Signature;
import org.unicitylabs.sdk.signing.SigningService;
import org.unicitylabs.sdk.token.TokenId;
import org.unicitylabs.sdk.token.Token;
import org.unicitylabs.sdk.token.TokenType;
import org.unicitylabs.sdk.transaction.Transaction;
import org.unicitylabs.sdk.transaction.TransferTransactionData;

public class UnmaskedPredicate extends DefaultPredicate {

Expand Down Expand Up @@ -39,21 +37,21 @@ public static UnmaskedPredicate create(
}

@Override
public DataHash calculateHash(TokenId tokenId, TokenType tokenType) {
IPredicateReference reference = this.getReference(tokenType);

ArrayNode node = UnicityObjectMapper.CBOR.createArrayNode();
node.addPOJO(reference.getHash());
node.addPOJO(tokenId);
node.addPOJO(this.getNonce());

try {
return new DataHasher(HashAlgorithm.SHA256)
.update(UnicityObjectMapper.CBOR.writeValueAsBytes(reference.getHash()))
.digest();
} catch (JsonProcessingException e) {
throw new CborSerializationException(e);
}
public boolean verify(
List<Transaction<TransferTransactionData>> transactions,
Token<?> token
) {
return super.verify(transactions, token) && SigningService.verifyWithPublicKey(
new DataHasher(HashAlgorithm.SHA256)
.update(
transactions.size() > 1
? transactions.get(transactions.size() - 2).getData().getSalt()
: token.getGenesis().getData().getSalt()
)
.digest(),
this.getNonce(),
this.getPublicKey()
);
Comment on lines +44 to +54

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When transactions.size() == 1, this accesses transactions.get(-1) which will throw an IndexOutOfBoundsException. The logic should check transactions.size() > 1 for the previous transaction access.

Copilot uses AI. Check for mistakes.
}

public UnmaskedPredicateReference getReference(TokenType tokenType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static class Deserializer extends
@Override
public MaskedPredicate deserialize(JsonParser p, DeserializationContext ctx)
throws IOException {
PredicateType type = null;
PredicateType type;

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable type is now uninitialized. This will cause a compilation error when used before assignment. Initialize to null or ensure it's assigned before use.

Suggested change
PredicateType type;

Copilot uses AI. Check for mistakes.
byte[] publicKey = null;
String algorithm = null;
HashAlgorithm hashAlgorithm = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public static class Deserializer extends
@Override
public UnmaskedPredicate deserialize(JsonParser p, DeserializationContext ctx)
throws IOException {
PredicateType type = null;
PredicateType type;

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable type is now uninitialized. This will cause a compilation error when used before assignment. Initialize to null or ensure it's assigned before use.

Suggested change
PredicateType type;
PredicateType type = null;

Copilot uses AI. Check for mistakes.
byte[] publicKey = null;
String algorithm = null;
HashAlgorithm hashAlgorithm = null;
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/org/unicitylabs/sdk/signing/SigningService.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,16 @@ public static byte[] generatePrivateKey() {
}

/**
* Create signing service from secret and optional nonce.
* Create signing service from secret.
*/
public static SigningService createFromSecret(byte[] secret, byte[] nonce) {
public static SigningService createFromSecret(byte[] secret) {
return SigningService.createFromMaskedSecret(secret, null);
}

/**
* Create signing service from secret and nonce.
*/
public static SigningService createFromMaskedSecret(byte[] secret, byte[] nonce) {
DataHasher hasher = new DataHasher(HashAlgorithm.SHA256);
hasher.update(secret);
if (nonce != null) {
Expand Down
19 changes: 0 additions & 19 deletions src/main/java/org/unicitylabs/sdk/token/NameTagTokenState.java

This file was deleted.

41 changes: 25 additions & 16 deletions src/main/java/org/unicitylabs/sdk/token/Token.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package org.unicitylabs.sdk.token;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.unicitylabs.sdk.address.Address;
import org.unicitylabs.sdk.address.ProxyAddress;
import org.unicitylabs.sdk.api.RequestId;
Expand All @@ -10,17 +16,12 @@
import org.unicitylabs.sdk.token.fungible.TokenCoinData;
import org.unicitylabs.sdk.transaction.InclusionProofVerificationStatus;
import org.unicitylabs.sdk.transaction.MintCommitment;
import org.unicitylabs.sdk.transaction.MintTransactionState;
import org.unicitylabs.sdk.transaction.MintTransactionData;
import org.unicitylabs.sdk.transaction.MintTransactionState;
import org.unicitylabs.sdk.transaction.Transaction;
import org.unicitylabs.sdk.transaction.TransactionData;
import org.unicitylabs.sdk.transaction.TransferTransactionData;
import org.unicitylabs.sdk.util.VerificationResult;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public class Token<T extends MintTransactionData<?>> {

Expand All @@ -31,8 +32,12 @@ public class Token<T extends MintTransactionData<?>> {
private final List<Transaction<TransferTransactionData>> transactions;
private final List<Token<?>> nametags;

public Token(TokenState state, Transaction<T> genesis, List<Transaction<TransferTransactionData>> transactions,
List<Token<?>> nametags) {
public Token(
TokenState state,
Transaction<T> genesis,
List<Transaction<TransferTransactionData>> transactions,
List<Token<?>> nametags
) {
Objects.requireNonNull(state, "State cannot be null");
Objects.requireNonNull(genesis, "Genesis cannot be null");
Objects.requireNonNull(transactions, "Transactions list cannot be null");
Expand Down Expand Up @@ -97,8 +102,9 @@ public Token<T> update(
Objects.requireNonNull(transaction, "Transaction is null");
Objects.requireNonNull(transactionNametags, "Nametag tokens are null");

if (!transaction.getData().getSourceState().getUnlockPredicate()
.verify(transaction, this.getId(), this.getType())) {
LinkedList<Transaction<TransferTransactionData>> verificationTransactions = new LinkedList<>(this.transactions);
verificationTransactions.add(transaction);
if (!transaction.getData().getSourceState().getUnlockPredicate().verify(verificationTransactions, this)) {
throw new RuntimeException("Predicate verification failed");
}

Expand Down Expand Up @@ -130,15 +136,16 @@ public VerificationResult verify() {
);

Transaction<? extends TransactionData<?>> previousTransaction = this.genesis;
for (Transaction<TransferTransactionData> transaction : this.transactions) {
for (int i = 0; i < this.transactions.size(); i++) {
Transaction<TransferTransactionData> transaction = this.transactions.get(i);
Address recipient = previousTransaction.getData().getRecipient();

results.add(
VerificationResult.fromChildren(
"Transaction verification",
List.of(
this.verifyTransaction(
transaction,
this.transactions.subList(0, i + 1),
previousTransaction.getData().getDataHash().orElse(null),
recipient
)
Expand Down Expand Up @@ -189,8 +196,11 @@ public VerificationResult verify() {
}

private VerificationResult verifyTransaction(
Transaction<TransferTransactionData> transaction, DataHash dataHash, Address recipient) {

List<Transaction<TransferTransactionData>> transactions,
DataHash dataHash,
Address recipient
) {
Transaction<TransferTransactionData> transaction = transactions.get(transactions.size() - 1);
for (Token<?> nametag : transaction.getData().getNametags()) {
if (!nametag.verify().isSuccessful()) {
return VerificationResult.fail(
Expand All @@ -212,8 +222,7 @@ private VerificationResult verifyTransaction(
return VerificationResult.fail("data mismatch");
}

if (!transaction.getData().getSourceState().getUnlockPredicate()
.verify(transaction, this.getId(), this.getType())) {
if (!transaction.getData().getSourceState().getUnlockPredicate().verify(transactions, this)) {
return VerificationResult.fail("predicate verification failed");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static <T extends MintTransactionData<?>> MintCommitment<T> create(
}

public static SigningService createSigningService(MintTransactionData<?> transactionData) {
return SigningService.createFromSecret(MINTER_SECRET, transactionData.getTokenId().getBytes());
return SigningService.createFromMaskedSecret(MINTER_SECRET, transactionData.getTokenId().getBytes());
}

public Transaction<T> toTransaction(InclusionProof inclusionProof) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,28 @@
package org.unicitylabs.sdk.transaction;

import java.nio.charset.StandardCharsets;
import org.unicitylabs.sdk.address.Address;
import org.unicitylabs.sdk.hash.DataHasher;
import org.unicitylabs.sdk.hash.HashAlgorithm;
import org.unicitylabs.sdk.token.TokenId;
import org.unicitylabs.sdk.token.TokenType;
import org.unicitylabs.sdk.token.fungible.TokenCoinData;
import java.nio.charset.StandardCharsets;

public class NametagMintTransactionData<R extends MintTransactionReason> extends
MintTransactionData<R> {

public NametagMintTransactionData(
String name,
TokenType tokenType,
byte[] tokenData,
TokenCoinData coinData,
Address recipient,
byte[] salt,
Address targetAddress
) {
super(
TokenId.fromNameTag(name),
tokenType,
tokenData,
coinData,
targetAddress.getAddress().getBytes(StandardCharsets.UTF_8),
null,
recipient,
salt,
targetAddress == null
? null
: new DataHasher(HashAlgorithm.SHA256)
.update(targetAddress.getAddress().getBytes(StandardCharsets.UTF_8))
.digest(),
null,
null
);
}
Expand Down
Loading
Loading