Skip to content
Open
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
6 changes: 3 additions & 3 deletions src/main/java/org/unicitylabs/sdk/api/AggregatorClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ public interface AggregatorClient {
CompletableFuture<InclusionProofResponse> getInclusionProof(StateId stateId);

/**
* Get block height.
* Get the latest block number.
*
* @return block height
* @return latest block number
*/
CompletableFuture<Long> getBlockHeight();
CompletableFuture<Long> getLatestBlockNumber();
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ public enum CertificationStatus {
*/
SUCCESS("SUCCESS"),

/**
* The certification request failed because the state ID already exists.
*/
STATE_ID_EXISTS("STATE_ID_EXISTS"),
/**
* The certification request failed because the state ID does not match the expected format.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ public CompletableFuture<InclusionProofResponse> getInclusionProof(StateId state
}

/**
* Get block height.
* Get the latest block number.
*
* @return block height
* @return latest block number
*/
@Override
public CompletableFuture<Long> getBlockHeight() {
public CompletableFuture<Long> getLatestBlockNumber() {
return this.transport.request("get_block_height", Map.of(), BlockHeightResponse.class)
.thenApply(BlockHeightResponse::getBlockNumber);
}
Expand Down
79 changes: 79 additions & 0 deletions src/main/java/org/unicitylabs/sdk/api/NetworkId.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.unicitylabs.sdk.api;

/**
* Unicity network identifier ({@code α}). Used to scope token ids and other
* network-bound values so they cannot be replayed across networks.
*/
public final class NetworkId {

public static final NetworkId MAINNET = new NetworkId((short) 1, "MAINNET");
public static final NetworkId TESTNET = new NetworkId((short) 2, "TESTNET");
public static final NetworkId LOCAL = new NetworkId((short) 3, "LOCAL");

private final short id;
private final String name;

private NetworkId(short id, String name) {
this.id = id;
this.name = name;
}

private NetworkId(short id) {
this(id, null);
}

/**
* Resolve a NetworkId from its numeric identifier. Returns the registered
* singleton for known ids; constructs a new (unnamed) instance for any
* other 16-bit value.
*
* @param id numeric network identifier
* @return NetworkId for the given identifier
*/
public static NetworkId fromId(short id) {
if (id <= 0) {
throw new IllegalArgumentException(
"Network identifier out of allowed 16-bit unsigned range: " + id + ".");
}
if (id == MAINNET.id) {
return MAINNET;
}
if (id == TESTNET.id) {
return TESTNET;
}
if (id == LOCAL.id) {
return LOCAL;
}
return new NetworkId(id);
}

/**
* Get the numeric network identifier.
*
* @return numeric identifier
*/
public short getId() {
return this.id;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof NetworkId)) {
return false;
}
return this.id == ((NetworkId) o).id;
}

@Override
public int hashCode() {
return Short.hashCode(this.id);
}

@Override
public String toString() {
return "NetworkId[" + (this.name != null ? this.name : this.id) + "]";
}
}
7 changes: 4 additions & 3 deletions src/main/java/org/unicitylabs/sdk/api/bft/RootTrustBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.unicitylabs.sdk.api.NetworkId;
import org.unicitylabs.sdk.serializer.UnicityObjectMapper;
import org.unicitylabs.sdk.serializer.json.JsonSerializationException;
import org.unicitylabs.sdk.serializer.json.LongAsStringSerializer;
Expand All @@ -20,7 +21,7 @@
public class RootTrustBase {

private final long version;
private final int networkId;
private final NetworkId networkId;
private final long epoch;
private final long epochStartRound;
private final Set<NodeInfo> rootNodes;
Expand All @@ -33,7 +34,7 @@ public class RootTrustBase {
@JsonCreator
RootTrustBase(
@JsonProperty("version") long version,
@JsonProperty("networkId") int networkId,
@JsonProperty("networkId") NetworkId networkId,
@JsonProperty("epoch") long epoch,
@JsonProperty("epochStartRound") long epochStartRound,
@JsonProperty("rootNodes") Set<NodeInfo> rootNodes,
Expand Down Expand Up @@ -78,7 +79,7 @@ public long getVersion() {
*
* @return network id
*/
public int getNetworkId() {
public NetworkId getNetworkId() {
return this.networkId;
}

Expand Down
11 changes: 6 additions & 5 deletions src/main/java/org/unicitylabs/sdk/api/bft/UnicitySeal.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.unicitylabs.sdk.api.bft;

import org.unicitylabs.sdk.api.NetworkId;
import org.unicitylabs.sdk.serializer.cbor.CborDeserializer;
import org.unicitylabs.sdk.serializer.cbor.CborDeserializer.CborTag;
import org.unicitylabs.sdk.serializer.cbor.CborSerializationException;
Expand All @@ -17,7 +18,7 @@ public class UnicitySeal {
public static final long CBOR_TAG = 39005;
private static final int VERSION = 1;

private final short networkId;
private final NetworkId networkId;
private final long rootChainRoundNumber;
private final long epoch;
private final long timestamp;
Expand All @@ -26,7 +27,7 @@ public class UnicitySeal {
private final Set<SignatureEntry> signatures;

UnicitySeal(
short networkId,
NetworkId networkId,
long rootChainRoundNumber,
long epoch,
long timestamp,
Expand Down Expand Up @@ -74,7 +75,7 @@ public int getVersion() {
*
* @return network ID
*/
public short getNetworkId() {
public NetworkId getNetworkId() {
return this.networkId;
}

Expand Down Expand Up @@ -152,7 +153,7 @@ public static UnicitySeal fromCbor(byte[] bytes) {
}

return new UnicitySeal(
CborDeserializer.decodeUnsignedInteger(data.get(1)).asShort(),
NetworkId.fromId(CborDeserializer.decodeUnsignedInteger(data.get(1)).asShort()),
CborDeserializer.decodeUnsignedInteger(data.get(2)).asLong(),
CborDeserializer.decodeUnsignedInteger(data.get(3)).asLong(),
CborDeserializer.decodeUnsignedInteger(data.get(4)).asLong(),
Expand All @@ -177,7 +178,7 @@ public byte[] toCbor() {
UnicitySeal.CBOR_TAG,
CborSerializer.encodeArray(
CborSerializer.encodeUnsignedInteger(UnicitySeal.VERSION),
CborSerializer.encodeUnsignedInteger(this.networkId),
CborSerializer.encodeUnsignedInteger(this.networkId.getId()),
CborSerializer.encodeUnsignedInteger(this.rootChainRoundNumber),
CborSerializer.encodeUnsignedInteger(this.epoch),
CborSerializer.encodeUnsignedInteger(this.timestamp),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ private UnicityCertificateVerification() {
public static UnicityCertificateVerificationResult verify(RootTrustBase trustBase,
InclusionProof inclusionProof) {
ArrayList<VerificationResult<?>> results = new ArrayList<>();

if (!inclusionProof.getUnicityCertificate().getUnicitySeal().getNetworkId().equals(trustBase.getNetworkId())) {
results.add(new VerificationResult<>("UnicitySealNetworkMatchesTrustBaseRule", VerificationStatus.FAIL));
return UnicityCertificateVerificationResult.fail(results);
}
results.add(new VerificationResult<>("UnicitySealNetworkMatchesTrustBaseRule", VerificationStatus.OK));

VerificationResult<VerificationStatus> result = UnicitySealHashMatchesWithRootHashRule.verify(inclusionProof.getUnicityCertificate());
results.add(result);
if (result.getStatus() != VerificationStatus.OK) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.unicitylabs.sdk.payment;

/**
* Thrown when two split requests derive the same token id, which would collide in the sum trees.
*/
public class DuplicateSplitTokenIdException extends RuntimeException {

/**
* Create exception indicating that the given token id is shared by multiple split requests.
*
* @param tokenId duplicated token id description
*/
public DuplicateSplitTokenIdException(String tokenId) {
super("Duplicate token id across split requests: " + tokenId + ".");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import org.unicitylabs.sdk.serializer.cbor.CborSerializer;
import org.unicitylabs.sdk.transaction.Token;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -54,14 +54,18 @@ public List<SplitAssetProof> getProofs() {
*
* @return split mint justification
*/
public static SplitMintJustification create(Token token, Set<SplitAssetProof> proofs) {
public static SplitMintJustification create(Token token, List<SplitAssetProof> proofs) {
Objects.requireNonNull(token, "token cannot be null");
Objects.requireNonNull(proofs, "proofs cannot be null");

if (proofs.isEmpty()) {
throw new IllegalArgumentException("proofs cannot be empty");
}

if (new HashSet<>(proofs).size() != proofs.size()) {
throw new IllegalArgumentException("proofs contain duplicate asset ids");
}

return new SplitMintJustification(token, List.copyOf(proofs));
}

Expand All @@ -80,7 +84,9 @@ public static SplitMintJustification fromCbor(byte[] bytes) {
List<byte[]> data = CborDeserializer.decodeArray(tag.getData(), 2);
return SplitMintJustification.create(
Token.fromCbor(data.get(0)),
CborDeserializer.decodeArray(data.get(1)).stream().map(SplitAssetProof::fromCbor).collect(Collectors.toSet())
CborDeserializer.decodeArray(data.get(1)).stream()
.map(SplitAssetProof::fromCbor)
.collect(Collectors.toList())
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.unicitylabs.sdk.payment;

import org.unicitylabs.sdk.api.NetworkId;
import org.unicitylabs.sdk.api.bft.RootTrustBase;
import org.unicitylabs.sdk.crypto.hash.DataHash;
import org.unicitylabs.sdk.payment.asset.Asset;
Expand Down Expand Up @@ -69,6 +70,19 @@ public VerificationResult<VerificationStatus> verify(CertifiedMintTransaction tr
);
}

NetworkId sourceNetworkId = justification.getToken().getGenesis().getNetworkId();
if (!transaction.getNetworkId().equals(sourceNetworkId)) {
return new VerificationResult<>(
"SplitMintJustificationVerificationRule",
VerificationStatus.FAIL,
String.format(
"Network identifier mismatch: mint is on %s, source token is on %s.",
transaction.getNetworkId(),
sourceNetworkId
)
);
}

VerificationResult<VerificationStatus> verificationResult = justification.getToken()
.verify(trustBase, predicateVerifier, mintJustificationVerifier);
if (verificationResult.getStatus() != VerificationStatus.OK) {
Expand Down
27 changes: 9 additions & 18 deletions src/main/java/org/unicitylabs/sdk/payment/SplitResult.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
package org.unicitylabs.sdk.payment;

import org.unicitylabs.sdk.transaction.TokenId;
import org.unicitylabs.sdk.transaction.TransferTransaction;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

/**
* Result of token split generation containing burn transaction and per-token proofs.
* Result of token split generation containing burn transaction and per-output split tokens.
*/
public class SplitResult {

private final TransferTransaction burnTransaction;
private final Map<TokenId, List<SplitAssetProof>> proofs;
private final List<SplitToken> tokens;

SplitResult(TransferTransaction burnTransaction, Map<TokenId, List<SplitAssetProof>> proofs) {
SplitResult(TransferTransaction burnTransaction, List<SplitToken> tokens) {
this.burnTransaction = burnTransaction;
this.proofs = Map.copyOf(
proofs.entrySet().stream()
.collect(
Collectors.toMap(Entry::getKey, value -> List.copyOf(value.getValue()))
)
);
this.tokens = List.copyOf(tokens);
}

/**
Expand All @@ -36,11 +27,11 @@ public TransferTransaction getBurnTransaction() {
}

/**
* Get proofs grouped by resulting token id.
* Get the split tokens ready to be minted.
*
* @return split proofs map
* @return immutable list of split tokens
*/
public Map<TokenId, List<SplitAssetProof>> getProofs() {
return this.proofs;
public List<SplitToken> getTokens() {
return this.tokens;
}
}
}
Loading
Loading