Skip to content

Commit 4c3ad04

Browse files
committed
#75 Update smt region calculation
1 parent 543ddf1 commit 4c3ad04

2 files changed

Lines changed: 50 additions & 7 deletions

File tree

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

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,46 @@
11
package org.unicitylabs.sdk.smt;
22

33
import java.math.BigInteger;
4+
import java.util.Objects;
45

56
/**
67
* Path utilities for the radix sparse Merkle trees.
78
*/
89
public final class SparseMerkleTreePathUtils {
910

11+
/**
12+
* Region size in bytes: the SMT key length, since the region holds a full 256-bit key prefix. A
13+
* fixed protocol constant that must match the JS SDK; it is not a tunable parameter.
14+
*/
15+
private static final int REGION_LENGTH = 32;
16+
1017
private SparseMerkleTreePathUtils() {
1118
}
1219

1320
/**
14-
* Region committed by an interior node: the node's {@code depth}-bit key prefix packed into 32
15-
* bytes least-significant-byte-first. {@code path} is the absolute node/key path (leading
16-
* sentinel bit at {@code depth}); its low {@code depth} bits are the prefix.
21+
* Region committed by an interior node: the `depth`-bit common prefix of all leaves in the node's
22+
* sub-tree. The `i`th lowest bit of path will be the `i mod 8`th lowest bit in the `i div 8`th
23+
* byte of the returned 32-byte array (so the packing is little-endian); the remaining bits of the array
24+
* are set to zero.
1725
*
1826
* @param path absolute node or key path
1927
* @param depth bifurcation depth of the node
2028
* @return 32-byte region
29+
* @throws NullPointerException if {@code path} is {@code null}
2130
*/
2231
public static byte[] pathToRegion(BigInteger path, int depth) {
23-
BigInteger bits = path.and(BigInteger.ONE.shiftLeft(depth).subtract(BigInteger.ONE));
24-
byte[] region = new byte[32];
25-
for (int j = 0; j * 8 < depth; j++) {
26-
region[j] = (byte) bits.shiftRight(8 * j).and(BigInteger.valueOf(0xff)).intValue();
32+
Objects.requireNonNull(path, "path cannot be null");
33+
34+
byte[] region = new byte[REGION_LENGTH];
35+
int fullBytes = depth / 8;
36+
int remainderBits = depth % 8;
37+
38+
BigInteger bits = path;
39+
for (int j = 0; j < fullBytes && j < REGION_LENGTH; j++, bits = bits.shiftRight(8)) {
40+
region[j] = bits.byteValue();
41+
}
42+
if (remainderBits > 0 && fullBytes < REGION_LENGTH) {
43+
region[fullBytes] = (byte) (bits.byteValue() & ((1 << remainderBits) - 1));
2744
}
2845
return region;
2946
}

src/test/java/org/unicitylabs/sdk/smt/radix/SparseMerkleTreeTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,30 @@ void deepSplitAtDepth255VerifiesWithRegion() throws Exception {
4444
Assertions.assertTrue(certificate.verify(key, value, root.getHash()));
4545
}
4646
}
47+
48+
@Test
49+
void everyLeafVerifiesThroughNonZeroRegions() throws Exception {
50+
int[] firstBytes = {0b10010000, 0b00000000, 0b00010000, 0b10000000, 0b01100000, 0b00010100};
51+
byte[][] keys = new byte[firstBytes.length][];
52+
byte[][] values = new byte[firstBytes.length][];
53+
SparseMerkleTree tree = new SparseMerkleTree(HashAlgorithm.SHA256);
54+
for (int i = 0; i < firstBytes.length; i++) {
55+
byte[] key = new byte[32];
56+
key[0] = (byte) firstBytes[i];
57+
byte[] value = new byte[32];
58+
value[0] = (byte) (i + 1);
59+
keys[i] = key;
60+
values[i] = value;
61+
tree.addLeaf(key, value);
62+
}
63+
64+
FinalizedNodeBranch root = tree.calculateRoot();
65+
66+
for (int i = 0; i < keys.length; i++) {
67+
InclusionCertificate certificate = InclusionCertificate.create(root, keys[i]);
68+
StateId key = StateId.fromCbor(CborSerializer.encodeByteString(keys[i]));
69+
DataHash value = new DataHash(HashAlgorithm.SHA256, values[i]);
70+
Assertions.assertTrue(certificate.verify(key, value, root.getHash()));
71+
}
72+
}
4773
}

0 commit comments

Comments
 (0)