Skip to content

Commit 91f400e

Browse files
authored
Merge pull request #69 from unicitynetwork/issue-68
#68 Add networkId and its verification, reorder mint transaction params
2 parents 64ee533 + b9d08a1 commit 91f400e

33 files changed

Lines changed: 893 additions & 197 deletions

src/main/java/org/unicitylabs/sdk/api/AggregatorClient.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ public interface AggregatorClient {
2525
CompletableFuture<InclusionProofResponse> getInclusionProof(StateId stateId);
2626

2727
/**
28-
* Get block height.
28+
* Get the latest block number.
2929
*
30-
* @return block height
30+
* @return latest block number
3131
*/
32-
CompletableFuture<Long> getBlockHeight();
32+
CompletableFuture<Long> getLatestBlockNumber();
3333
}

src/main/java/org/unicitylabs/sdk/api/CertificationStatus.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ public enum CertificationStatus {
99
*/
1010
SUCCESS("SUCCESS"),
1111

12-
/**
13-
* The certification request failed because the state ID already exists.
14-
*/
15-
STATE_ID_EXISTS("STATE_ID_EXISTS"),
1612
/**
1713
* The certification request failed because the state ID does not match the expected format.
1814
*/

src/main/java/org/unicitylabs/sdk/api/JsonRpcAggregatorClient.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,12 @@ public CompletableFuture<InclusionProofResponse> getInclusionProof(StateId state
8787
}
8888

8989
/**
90-
* Get block height.
90+
* Get the latest block number.
9191
*
92-
* @return block height
92+
* @return latest block number
9393
*/
9494
@Override
95-
public CompletableFuture<Long> getBlockHeight() {
95+
public CompletableFuture<Long> getLatestBlockNumber() {
9696
return this.transport.request("get_block_height", Map.of(), BlockHeightResponse.class)
9797
.thenApply(BlockHeightResponse::getBlockNumber);
9898
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.unicitylabs.sdk.api;
2+
3+
/**
4+
* Unicity network identifier ({@code α}). Used to scope token ids and other
5+
* network-bound values so they cannot be replayed across networks.
6+
*/
7+
public final class NetworkId {
8+
9+
public static final NetworkId MAINNET = new NetworkId((short) 1, "MAINNET");
10+
public static final NetworkId TESTNET = new NetworkId((short) 2, "TESTNET");
11+
public static final NetworkId LOCAL = new NetworkId((short) 3, "LOCAL");
12+
13+
private final short id;
14+
private final String name;
15+
16+
private NetworkId(short id, String name) {
17+
this.id = id;
18+
this.name = name;
19+
}
20+
21+
private NetworkId(short id) {
22+
this(id, null);
23+
}
24+
25+
/**
26+
* Resolve a NetworkId from its numeric identifier. Returns the registered
27+
* singleton for known ids; constructs a new (unnamed) instance for any
28+
* other 16-bit value.
29+
*
30+
* @param id numeric network identifier
31+
* @return NetworkId for the given identifier
32+
*/
33+
public static NetworkId fromId(short id) {
34+
if (id <= 0) {
35+
throw new IllegalArgumentException(
36+
"Network identifier out of allowed 16-bit unsigned range: " + id + ".");
37+
}
38+
if (id == MAINNET.id) {
39+
return MAINNET;
40+
}
41+
if (id == TESTNET.id) {
42+
return TESTNET;
43+
}
44+
if (id == LOCAL.id) {
45+
return LOCAL;
46+
}
47+
return new NetworkId(id);
48+
}
49+
50+
/**
51+
* Get the numeric network identifier.
52+
*
53+
* @return numeric identifier
54+
*/
55+
public short getId() {
56+
return this.id;
57+
}
58+
59+
@Override
60+
public boolean equals(Object o) {
61+
if (this == o) {
62+
return true;
63+
}
64+
if (!(o instanceof NetworkId)) {
65+
return false;
66+
}
67+
return this.id == ((NetworkId) o).id;
68+
}
69+
70+
@Override
71+
public int hashCode() {
72+
return Short.hashCode(this.id);
73+
}
74+
75+
@Override
76+
public String toString() {
77+
return "NetworkId[" + (this.name != null ? this.name : this.id) + "]";
78+
}
79+
}

src/main/java/org/unicitylabs/sdk/api/bft/RootTrustBase.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.fasterxml.jackson.annotation.JsonProperty;
55
import com.fasterxml.jackson.core.JsonProcessingException;
66
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
7+
import org.unicitylabs.sdk.api.NetworkId;
78
import org.unicitylabs.sdk.serializer.UnicityObjectMapper;
89
import org.unicitylabs.sdk.serializer.json.JsonSerializationException;
910
import org.unicitylabs.sdk.serializer.json.LongAsStringSerializer;
@@ -20,7 +21,7 @@
2021
public class RootTrustBase {
2122

2223
private final long version;
23-
private final int networkId;
24+
private final NetworkId networkId;
2425
private final long epoch;
2526
private final long epochStartRound;
2627
private final Set<NodeInfo> rootNodes;
@@ -33,7 +34,7 @@ public class RootTrustBase {
3334
@JsonCreator
3435
RootTrustBase(
3536
@JsonProperty("version") long version,
36-
@JsonProperty("networkId") int networkId,
37+
@JsonProperty("networkId") NetworkId networkId,
3738
@JsonProperty("epoch") long epoch,
3839
@JsonProperty("epochStartRound") long epochStartRound,
3940
@JsonProperty("rootNodes") Set<NodeInfo> rootNodes,
@@ -78,7 +79,7 @@ public long getVersion() {
7879
*
7980
* @return network id
8081
*/
81-
public int getNetworkId() {
82+
public NetworkId getNetworkId() {
8283
return this.networkId;
8384
}
8485

src/main/java/org/unicitylabs/sdk/api/bft/UnicitySeal.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.unicitylabs.sdk.api.bft;
22

3+
import org.unicitylabs.sdk.api.NetworkId;
34
import org.unicitylabs.sdk.serializer.cbor.CborDeserializer;
45
import org.unicitylabs.sdk.serializer.cbor.CborDeserializer.CborTag;
56
import org.unicitylabs.sdk.serializer.cbor.CborSerializationException;
@@ -17,7 +18,7 @@ public class UnicitySeal {
1718
public static final long CBOR_TAG = 39005;
1819
private static final int VERSION = 1;
1920

20-
private final short networkId;
21+
private final NetworkId networkId;
2122
private final long rootChainRoundNumber;
2223
private final long epoch;
2324
private final long timestamp;
@@ -26,7 +27,7 @@ public class UnicitySeal {
2627
private final Set<SignatureEntry> signatures;
2728

2829
UnicitySeal(
29-
short networkId,
30+
NetworkId networkId,
3031
long rootChainRoundNumber,
3132
long epoch,
3233
long timestamp,
@@ -74,7 +75,7 @@ public int getVersion() {
7475
*
7576
* @return network ID
7677
*/
77-
public short getNetworkId() {
78+
public NetworkId getNetworkId() {
7879
return this.networkId;
7980
}
8081

@@ -152,7 +153,7 @@ public static UnicitySeal fromCbor(byte[] bytes) {
152153
}
153154

154155
return new UnicitySeal(
155-
CborDeserializer.decodeUnsignedInteger(data.get(1)).asShort(),
156+
NetworkId.fromId(CborDeserializer.decodeUnsignedInteger(data.get(1)).asShort()),
156157
CborDeserializer.decodeUnsignedInteger(data.get(2)).asLong(),
157158
CborDeserializer.decodeUnsignedInteger(data.get(3)).asLong(),
158159
CborDeserializer.decodeUnsignedInteger(data.get(4)).asLong(),
@@ -177,7 +178,7 @@ public byte[] toCbor() {
177178
UnicitySeal.CBOR_TAG,
178179
CborSerializer.encodeArray(
179180
CborSerializer.encodeUnsignedInteger(UnicitySeal.VERSION),
180-
CborSerializer.encodeUnsignedInteger(this.networkId),
181+
CborSerializer.encodeUnsignedInteger(this.networkId.getId()),
181182
CborSerializer.encodeUnsignedInteger(this.rootChainRoundNumber),
182183
CborSerializer.encodeUnsignedInteger(this.epoch),
183184
CborSerializer.encodeUnsignedInteger(this.timestamp),

src/main/java/org/unicitylabs/sdk/api/bft/verification/UnicityCertificateVerification.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ private UnicityCertificateVerification() {
2828
public static UnicityCertificateVerificationResult verify(RootTrustBase trustBase,
2929
InclusionProof inclusionProof) {
3030
ArrayList<VerificationResult<?>> results = new ArrayList<>();
31+
32+
if (!inclusionProof.getUnicityCertificate().getUnicitySeal().getNetworkId().equals(trustBase.getNetworkId())) {
33+
results.add(new VerificationResult<>("UnicitySealNetworkMatchesTrustBaseRule", VerificationStatus.FAIL));
34+
return UnicityCertificateVerificationResult.fail(results);
35+
}
36+
results.add(new VerificationResult<>("UnicitySealNetworkMatchesTrustBaseRule", VerificationStatus.OK));
37+
3138
VerificationResult<VerificationStatus> result = UnicitySealHashMatchesWithRootHashRule.verify(inclusionProof.getUnicityCertificate());
3239
results.add(result);
3340
if (result.getStatus() != VerificationStatus.OK) {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.unicitylabs.sdk.payment;
2+
3+
/**
4+
* Thrown when two split requests derive the same token id, which would collide in the sum trees.
5+
*/
6+
public class DuplicateSplitTokenIdException extends RuntimeException {
7+
8+
/**
9+
* Create exception indicating that the given token id is shared by multiple split requests.
10+
*
11+
* @param tokenId duplicated token id description
12+
*/
13+
public DuplicateSplitTokenIdException(String tokenId) {
14+
super("Duplicate token id across split requests: " + tokenId + ".");
15+
}
16+
}

src/main/java/org/unicitylabs/sdk/payment/SplitMintJustification.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
import org.unicitylabs.sdk.serializer.cbor.CborSerializer;
66
import org.unicitylabs.sdk.transaction.Token;
77

8+
import java.util.HashSet;
89
import java.util.List;
910
import java.util.Objects;
10-
import java.util.Set;
1111
import java.util.stream.Collectors;
1212

1313
/**
@@ -54,14 +54,18 @@ public List<SplitAssetProof> getProofs() {
5454
*
5555
* @return split mint justification
5656
*/
57-
public static SplitMintJustification create(Token token, Set<SplitAssetProof> proofs) {
57+
public static SplitMintJustification create(Token token, List<SplitAssetProof> proofs) {
5858
Objects.requireNonNull(token, "token cannot be null");
5959
Objects.requireNonNull(proofs, "proofs cannot be null");
6060

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

65+
if (new HashSet<>(proofs).size() != proofs.size()) {
66+
throw new IllegalArgumentException("proofs contain duplicate asset ids");
67+
}
68+
6569
return new SplitMintJustification(token, List.copyOf(proofs));
6670
}
6771

@@ -80,7 +84,9 @@ public static SplitMintJustification fromCbor(byte[] bytes) {
8084
List<byte[]> data = CborDeserializer.decodeArray(tag.getData(), 2);
8185
return SplitMintJustification.create(
8286
Token.fromCbor(data.get(0)),
83-
CborDeserializer.decodeArray(data.get(1)).stream().map(SplitAssetProof::fromCbor).collect(Collectors.toSet())
87+
CborDeserializer.decodeArray(data.get(1)).stream()
88+
.map(SplitAssetProof::fromCbor)
89+
.collect(Collectors.toList())
8490
);
8591
}
8692

src/main/java/org/unicitylabs/sdk/payment/SplitMintJustificationVerifier.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.unicitylabs.sdk.payment;
22

3+
import org.unicitylabs.sdk.api.NetworkId;
34
import org.unicitylabs.sdk.api.bft.RootTrustBase;
45
import org.unicitylabs.sdk.crypto.hash.DataHash;
56
import org.unicitylabs.sdk.payment.asset.Asset;
@@ -69,6 +70,19 @@ public VerificationResult<VerificationStatus> verify(CertifiedMintTransaction tr
6970
);
7071
}
7172

73+
NetworkId sourceNetworkId = justification.getToken().getGenesis().getNetworkId();
74+
if (!transaction.getNetworkId().equals(sourceNetworkId)) {
75+
return new VerificationResult<>(
76+
"SplitMintJustificationVerificationRule",
77+
VerificationStatus.FAIL,
78+
String.format(
79+
"Network identifier mismatch: mint is on %s, source token is on %s.",
80+
transaction.getNetworkId(),
81+
sourceNetworkId
82+
)
83+
);
84+
}
85+
7286
VerificationResult<VerificationStatus> verificationResult = justification.getToken()
7387
.verify(trustBase, predicateVerifier, mintJustificationVerifier);
7488
if (verificationResult.getStatus() != VerificationStatus.OK) {

0 commit comments

Comments
 (0)