Skip to content

Commit 5a2bee2

Browse files
committed
#79 Change SMT bit ordering
1 parent 36c6010 commit 5a2bee2

35 files changed

Lines changed: 667 additions & 453 deletions

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

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,11 @@
33
import org.unicitylabs.sdk.crypto.hash.DataHash;
44
import org.unicitylabs.sdk.crypto.hash.DataHasher;
55
import org.unicitylabs.sdk.crypto.hash.HashAlgorithm;
6-
import org.unicitylabs.sdk.smt.radix.FinalizedBranch;
7-
import org.unicitylabs.sdk.smt.radix.FinalizedLeafBranch;
86
import org.unicitylabs.sdk.smt.SparseMerkleTreePathUtils;
9-
import org.unicitylabs.sdk.smt.radix.FinalizedNodeBranch;
10-
import org.unicitylabs.sdk.util.BitString;
7+
import org.unicitylabs.sdk.smt.radix.SparseMerkleTreeRootNode;
118
import org.unicitylabs.sdk.util.HexConverter;
129
import org.unicitylabs.sdk.util.LongConverter;
1310

14-
import java.math.BigInteger;
1511
import java.util.ArrayList;
1612
import java.util.Arrays;
1713
import java.util.List;
@@ -30,35 +26,19 @@ private InclusionCertificate(byte[] bitmap, List<DataHash> siblings) {
3026
this.siblings = siblings;
3127
}
3228

33-
public static InclusionCertificate create(FinalizedNodeBranch root, byte[] key) {
34-
FinalizedBranch node = root;
29+
public static InclusionCertificate create(SparseMerkleTreeRootNode root, byte[] key) {
30+
Objects.requireNonNull(root, "root cannot be null");
3531

3632
ArrayList<DataHash> siblings = new ArrayList<>();
3733
byte[] bitmap = new byte[InclusionCertificate.BITMAP_SIZE];
38-
BigInteger keyPath = BitString.fromBytesReversedLSB(key).toBigInteger();
3934

40-
while (node != null) {
41-
if (node instanceof FinalizedLeafBranch) {
42-
FinalizedLeafBranch leaf = (FinalizedLeafBranch) node;
43-
if (!Arrays.equals(leaf.getKey(), key)) {
44-
throw new RuntimeException(String.format("Leaf not found for key: %s", HexConverter.encode(key)));
45-
}
46-
47-
return new InclusionCertificate(bitmap, siblings);
48-
}
49-
50-
FinalizedNodeBranch nodeBranch = (FinalizedNodeBranch) node;
51-
boolean isRight = keyPath.testBit(nodeBranch.getDepth());
52-
FinalizedBranch sibling = isRight ? nodeBranch.getLeft() : nodeBranch.getRight();
53-
if (sibling != null) {
54-
bitmap[nodeBranch.getDepth() / 8] |= (byte) (1 << nodeBranch.getDepth() % 8);
55-
siblings.add(sibling.getHash());
56-
}
57-
58-
node = isRight ? nodeBranch.getRight() : nodeBranch.getLeft();
35+
for (SparseMerkleTreeRootNode.Sibling sibling : root.getPath(key)) {
36+
int depth = sibling.getDepth();
37+
bitmap[depth >> 3] |= (byte) (0x80 >> (depth & 7));
38+
siblings.add(sibling.getHash());
5939
}
6040

61-
throw new RuntimeException("Could not construct inclusion certificate: Invalid path");
41+
return new InclusionCertificate(bitmap, siblings);
6242
}
6343

6444
public static InclusionCertificate decode(byte[] bytes) {
@@ -114,20 +94,17 @@ public boolean verify(StateId leafKey, DataHash leafValue, DataHash expectedRoot
11494
.update(value)
11595
.digest();
11696

117-
BigInteger keyPath = BitString.fromBytesReversedLSB(key).toBigInteger();
118-
BigInteger bitmapPath = BitString.fromBytesReversedLSB(this.bitmap).toBigInteger();
119-
12097
int position = this.siblings.size();
12198
for (int depth = InclusionCertificate.MAX_DEPTH; depth >= 0; depth--) {
122-
if (!bitmapPath.testBit(depth)) continue;
99+
if (SparseMerkleTreePathUtils.getBitAtDepth(this.bitmap, depth) == 0) continue;
123100

124101
position -= 1;
125102
if (position < 0) return false;
126103

127104
DataHash sibling = this.siblings.get(position);
128105

129106
byte[] left, right;
130-
if (keyPath.testBit(depth)) {
107+
if (SparseMerkleTreePathUtils.getBitAtDepth(key, depth) == 1) {
131108
left = sibling.getData();
132109
right = hash.getData();
133110
} else {
@@ -138,7 +115,7 @@ public boolean verify(StateId leafKey, DataHash leafValue, DataHash expectedRoot
138115
hash = new DataHasher(HashAlgorithm.SHA256)
139116
.update(new byte[]{0x01})
140117
.update(LongConverter.encode(depth))
141-
.update(SparseMerkleTreePathUtils.pathToRegion(keyPath, depth))
118+
.update(SparseMerkleTreePathUtils.regionFromKey(key, depth))
142119
.update(left)
143120
.update(right)
144121
.digest();

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

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,12 @@
66
import org.unicitylabs.sdk.serializer.cbor.CborDeserializer;
77
import org.unicitylabs.sdk.serializer.cbor.CborSerializationException;
88
import org.unicitylabs.sdk.serializer.cbor.CborSerializer;
9-
import org.unicitylabs.sdk.smt.radixsum.FinalizedBranch;
10-
import org.unicitylabs.sdk.smt.radixsum.FinalizedLeafBranch;
11-
import org.unicitylabs.sdk.smt.radixsum.FinalizedNodeBranch;
9+
import org.unicitylabs.sdk.smt.SparseMerkleTreePathUtils;
1210
import org.unicitylabs.sdk.smt.radixsum.SparseMerkleSumTreeRootNode;
1311
import org.unicitylabs.sdk.util.BigIntegerConverter;
14-
import org.unicitylabs.sdk.util.BitString;
15-
import org.unicitylabs.sdk.util.HexConverter;
1612

1713
import java.math.BigInteger;
1814
import java.util.ArrayList;
19-
import java.util.Arrays;
20-
import java.util.Collections;
2115
import java.util.List;
2216
import java.util.Objects;
2317

@@ -65,37 +59,11 @@ public static SplitAllocationProof create(SparseMerkleSumTreeRootNode root, byte
6559
throw new IllegalArgumentException("Key must be 32 bytes long.");
6660
}
6761

68-
BigInteger keyPath = BitString.fromBytesReversedLSB(key).toBigInteger();
6962
List<Sibling> siblings = new ArrayList<>();
70-
71-
boolean isRight = keyPath.testBit(0);
72-
FinalizedBranch sibling = isRight ? root.getLeft() : root.getRight();
73-
FinalizedBranch node = isRight ? root.getRight() : root.getLeft();
74-
if (sibling != null) {
75-
siblings.add(new Sibling(0, sibling.getHash(), sibling.getValue()));
63+
for (SparseMerkleSumTreeRootNode.Sibling sibling : root.getPath(key)) {
64+
siblings.add(new Sibling(sibling.getDepth(), sibling.getHash(), sibling.getValue()));
7665
}
7766

78-
while (node instanceof FinalizedNodeBranch) {
79-
FinalizedNodeBranch branch = (FinalizedNodeBranch) node;
80-
isRight = keyPath.testBit(branch.getDepth());
81-
sibling = isRight ? branch.getLeft() : branch.getRight();
82-
node = isRight ? branch.getRight() : branch.getLeft();
83-
if (sibling != null) {
84-
siblings.add(new Sibling(branch.getDepth(), sibling.getHash(), sibling.getValue()));
85-
}
86-
}
87-
88-
if (!(node instanceof FinalizedLeafBranch)) {
89-
throw new IllegalArgumentException(
90-
"Could not construct split allocation proof: invalid path.");
91-
}
92-
93-
if (!Arrays.equals(((FinalizedLeafBranch) node).getKey(), key)) {
94-
throw new IllegalArgumentException(
95-
String.format("Leaf not found for key: %s", HexConverter.encode(key)));
96-
}
97-
98-
Collections.reverse(siblings);
9967
return new SplitAllocationProof(siblings);
10068
}
10169

@@ -170,8 +138,6 @@ public Root calculateRoot(byte[] key, byte[] data, BigInteger value) {
170138
throw new IllegalArgumentException("Data must be 32 bytes long.");
171139
}
172140

173-
BigInteger keyPath = BitString.fromBytesReversedLSB(key).toBigInteger();
174-
175141
DataHash hash = new DataHasher(HashAlgorithm.SHA256)
176142
.update(new byte[]{0x10})
177143
.update(key)
@@ -186,7 +152,7 @@ public Root calculateRoot(byte[] key, byte[] data, BigInteger value) {
186152
throw new ArithmeticException("Reconstructed sum overflows 256 bits.");
187153
}
188154

189-
boolean isRight = keyPath.testBit(sibling.depth);
155+
boolean isRight = SparseMerkleTreePathUtils.getBitAtDepth(key, sibling.depth) == 1;
190156
DataHash leftHash = isRight ? sibling.hash : hash;
191157
BigInteger leftValue = isRight ? sibling.sum : sum;
192158
DataHash rightHash = isRight ? hash : sibling.hash;

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

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
import org.unicitylabs.sdk.payment.asset.PaymentAssetCollection;
1010
import org.unicitylabs.sdk.predicate.EncodedPredicate;
1111
import org.unicitylabs.sdk.predicate.builtin.BurnPredicate;
12-
import org.unicitylabs.sdk.smt.BranchExistsException;
13-
import org.unicitylabs.sdk.smt.LeafOutOfBoundsException;
12+
import org.unicitylabs.sdk.smt.LeafExistsException;
1413
import org.unicitylabs.sdk.smt.radixsum.SparseMerkleSumTree;
1514
import org.unicitylabs.sdk.smt.radixsum.SparseMerkleSumTreeRootNode;
1615
import org.unicitylabs.sdk.transaction.StateMask;
@@ -43,14 +42,13 @@ private TokenSplit() {
4342
* @param paymentDataDeserializer decoder for the source token's payment data
4443
* @param requests per-output mint requests; each carries its own payment data
4544
* @return burn predicate, burn transaction and split tokens ready to mint
46-
* @throws LeafOutOfBoundsException if a leaf path is invalid for merkle tree insertion
47-
* @throws BranchExistsException if duplicate branches are inserted into a merkle tree
45+
* @throws LeafExistsException if duplicate leaves are inserted into a merkle tree
4846
*/
4947
public static SplitResult split(
5048
Token token,
5149
PaymentDataDeserializer paymentDataDeserializer,
5250
List<SplitTokenRequest> requests
53-
) throws LeafOutOfBoundsException, BranchExistsException {
51+
) throws LeafExistsException {
5452
return TokenSplit.split(token, paymentDataDeserializer, requests, StateMask.generate());
5553
}
5654

@@ -64,15 +62,14 @@ public static SplitResult split(
6462
* (re-buildable) split supply a deterministically derived mask so the identical burn
6563
* transaction can be reconstructed after a failure
6664
* @return burn predicate, burn transaction and split tokens ready to mint
67-
* @throws LeafOutOfBoundsException if a leaf path is invalid for merkle tree insertion
68-
* @throws BranchExistsException if duplicate branches are inserted into a merkle tree
65+
* @throws LeafExistsException if duplicate leaves are inserted into a merkle tree
6966
*/
7067
public static SplitResult split(
7168
Token token,
7269
PaymentDataDeserializer paymentDataDeserializer,
7370
List<SplitTokenRequest> requests,
7471
StateMask burnStateMask
75-
) throws LeafOutOfBoundsException, BranchExistsException {
72+
) throws LeafExistsException {
7673
Objects.requireNonNull(token, "Token cannot be null");
7774
Objects.requireNonNull(paymentDataDeserializer, "Payment data deserializer cannot be null");
7875
Objects.requireNonNull(requests, "Requests cannot be null");

src/main/java/org/unicitylabs/sdk/smt/BranchExistsException.java

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/main/java/org/unicitylabs/sdk/smt/CommonPath.java

Lines changed: 0 additions & 72 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.unicitylabs.sdk.smt;
2+
3+
/**
4+
* Exception thrown when a sparse Merkle tree insertion targets a key that is already present in the
5+
* tree.
6+
*/
7+
public class LeafExistsException extends Exception {
8+
9+
/**
10+
* Create exception indicating that a leaf already exists for the inserted key.
11+
*/
12+
public LeafExistsException() {
13+
super("Leaf already exists.");
14+
}
15+
}

src/main/java/org/unicitylabs/sdk/smt/LeafOutOfBoundsException.java

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)