Skip to content

Commit 1176eb8

Browse files
committed
Add feature for non inclusion proof
1 parent c7913e0 commit 1176eb8

File tree

3 files changed

+127
-3
lines changed

3 files changed

+127
-3
lines changed

contracts/implementation.sol

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ contract PatriciaTreeImplementation {
1010
constructor () public {
1111
}
1212

13+
function insert(bytes key, bytes value) public {
14+
tree.insert(key, value);
15+
}
16+
1317
function get(bytes key) public view returns (bytes) {
1418
return tree.get(key);
1519
}
@@ -42,12 +46,21 @@ contract PatriciaTreeImplementation {
4246
return tree.getProof(key);
4347
}
4448

49+
function getNonInclusionProof(bytes key) public view returns (
50+
bytes32 leafLabel,
51+
bytes32 leafNode,
52+
uint branchMask,
53+
bytes32[] _siblings
54+
) {
55+
return tree.getNonInclusionProof(key);
56+
}
57+
4558
function verifyProof(bytes32 rootHash, bytes key, bytes value, uint branchMask, bytes32[] siblings) public pure {
4659
PatriciaTree.verifyProof(rootHash, key, value, branchMask, siblings);
4760
}
4861

49-
function insert(bytes key, bytes value) public {
50-
tree.insert(key, value);
62+
function verifyNonInclusionProof(bytes32 rootHash, bytes key, bytes32 leafLabel, bytes32 leafNode, uint branchMask, bytes32[] siblings) public pure {
63+
PatriciaTree.verifyNonInclusionProof(rootHash, key, leafLabel, leafNode, branchMask, siblings);
5164
}
5265
}
5366

@@ -134,7 +147,7 @@ contract PatriciaTreeMerkleProof {
134147
}
135148

136149
function seal() public onlyFor(Status.OPENED) {
137-
// require(_verifyEdge(originalRootEdge));
150+
// require(_verifyEdge(originalRootEdge));
138151
tree.rootEdge = originalRootEdge;
139152
tree.root = PatriciaTree.edgeHash(tree.rootEdge);
140153
_changeStatus(Status.ONGOING);

contracts/tree.sol

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,54 @@ library PatriciaTree {
109109
}
110110
}
111111

112+
function getNonInclusionProof(Tree storage tree, bytes key) internal view returns (
113+
bytes32 potentialSiblingLabel,
114+
bytes32 potentialSiblingValue,
115+
uint branchMask,
116+
bytes32[] _siblings
117+
){
118+
uint length;
119+
uint numSiblings;
120+
121+
// Start from root edge
122+
D.Label memory label = D.Label(keccak256(key), 256);
123+
D.Edge memory e = tree.rootEdge;
124+
bytes32[256] memory siblings;
125+
126+
while (true) {
127+
// Find at edge
128+
require(label.length >= e.label.length);
129+
D.Label memory prefix;
130+
D.Label memory suffix;
131+
(prefix, suffix) = Utils.splitCommonPrefix(label, e.label);
132+
133+
// suffix.length == 0 means that the key exists. Thus the length of the suffix should be not zero
134+
require(suffix.length != 0);
135+
136+
if (prefix.length >= e.label.length) {
137+
// Partial matched, keep finding
138+
length += prefix.length;
139+
branchMask |= uint(1) << (255 - length);
140+
length += 1;
141+
uint head;
142+
(head, label) = Utils.chopFirstBit(suffix);
143+
siblings[numSiblings++] = edgeHash(tree.nodes[e.node].children[1 - head]);
144+
e = tree.nodes[e.node].children[head];
145+
} else {
146+
// Found the potential sibling. Set data to return
147+
potentialSiblingLabel = e.label.data;
148+
potentialSiblingValue = e.node;
149+
break;
150+
}
151+
}
152+
if (numSiblings > 0)
153+
{
154+
_siblings = new bytes32[](numSiblings);
155+
for (uint i = 0; i < numSiblings; i++)
156+
_siblings[i] = siblings[i];
157+
}
158+
}
159+
112160
function verifyProof(bytes32 rootHash, bytes key, bytes value, uint branchMask, bytes32[] siblings) public pure {
113161
D.Label memory k = D.Label(keccak256(key), 256);
114162
D.Edge memory e;
@@ -128,6 +176,29 @@ library PatriciaTree {
128176
require(rootHash == edgeHash(e));
129177
}
130178

179+
function verifyNonInclusionProof(bytes32 rootHash, bytes key, bytes32 potentialSiblingLabel, bytes32 potentialSiblingValue, uint branchMask, bytes32[] siblings) public pure {
180+
D.Label memory k = D.Label(keccak256(key), 256);
181+
D.Edge memory e;
182+
for (uint i = 0; branchMask != 0; i++) {
183+
uint bitSet = Utils.lowestBitSet(branchMask);
184+
branchMask &= ~(uint(1) << bitSet);
185+
(k, e.label) = Utils.splitAt(k, 255 - bitSet);
186+
uint bit;
187+
(bit, e.label) = Utils.chopFirstBit(e.label);
188+
bytes32[2] memory edgeHashes;
189+
if (i == 0) {
190+
e.label.length = bitSet;
191+
e.label.data = potentialSiblingLabel;
192+
e.node = potentialSiblingValue;
193+
}
194+
edgeHashes[bit] = edgeHash(e);
195+
edgeHashes[1 - bit] = siblings[siblings.length - i - 1];
196+
e.node = keccak256(abi.encode(edgeHashes[0], edgeHashes[1]));
197+
}
198+
e.label = k;
199+
require(rootHash == edgeHash(e));
200+
}
201+
131202
// TODO also return the proof
132203
function insert(Tree storage tree, bytes key, bytes value) internal {
133204
D.Label memory k = D.Label(keccak256(key), 256);

test/PatriciaTree.test.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,5 +236,45 @@ contract('PatriciaTree', async ([_, primary, nonPrimary]) => {
236236
})
237237
})
238238

239+
describe('getNonInclusionProof()', async () => {
240+
let items = { key1: 'value1', key2: 'value2', key3: 'value3' }
241+
it('should return proof data when the key does not exist', async () => {
242+
for (const key of Object.keys(items)) {
243+
await tree.insert(key, items[key], { from: primary })
244+
}
245+
await tree.getNonInclusionProof('key4')
246+
})
247+
it('should not return data when the key does exist', async () => {
248+
for (const key of Object.keys(items)) {
249+
await tree.insert(key, items[key], { from: primary })
250+
}
251+
try {
252+
await tree.getNonInclusionProof('key1')
253+
assert.fail('Did not reverted')
254+
} catch (e) {
255+
assert.ok('Reverted successfully')
256+
}
257+
})
258+
})
259+
260+
describe('verifyNonInclusionProof()', async () => {
261+
it('should be passed when we use correct proof data', async () => {
262+
let items = { key1: 'value1', key2: 'value2', key3: 'value3' }
263+
for (const key of Object.keys(items)) {
264+
await tree.insert(key, items[key], { from: primary })
265+
}
266+
let rootHash = await tree.getRootHash()
267+
let [potentialSiblingLabel, potentialSiblingValue, branchMask, siblings] = await tree.getNonInclusionProof('key4')
268+
await tree.verifyNonInclusionProof(rootHash, 'key4', potentialSiblingLabel, potentialSiblingValue, branchMask, siblings)
269+
for (const key of Object.keys(items)) {
270+
try {
271+
await tree.verifyNonInclusionProof(rootHash, key, potentialSiblingLabel, potentialSiblingValue, branchMask, siblings)
272+
assert.fail('Did not reverted')
273+
} catch (e) {
274+
assert.ok('Reverted successfully')
275+
}
276+
}
277+
})
278+
})
239279
})
240280
})

0 commit comments

Comments
 (0)