Skip to content

Commit 389b3f1

Browse files
committed
inclusion proof
1 parent 7c0ea30 commit 389b3f1

File tree

2 files changed

+83
-2
lines changed

2 files changed

+83
-2
lines changed

src/TreeCbor.sol

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,25 @@ library TreeCbor {
4343
function getCid(Tree memory tree, CidCbor.CidBytes32 indexCid, uint startIdx)
4444
internal
4545
pure
46-
returns (TreeNodeCbor.TreeNode memory, uint index)
46+
returns (TreeNodeCbor.TreeNode memory, uint)
4747
{
4848
bytes32 indexBytes = CidCbor.CidBytes32.unwrap(indexCid);
4949
for (uint i = startIdx; i < tree.cids.length; i++) {
5050
if (CidCbor.CidBytes32.unwrap(tree.cids[i]) == indexBytes) {
5151
return (tree.nodes[i], i);
5252
}
5353
}
54-
revert("node not found");
54+
revert("couldn't get cid, not found");
55+
}
56+
57+
function hasCid(Tree memory tree, CidCbor.CidBytes32 indexCid, uint startIdx) internal pure returns (bool, uint) {
58+
bytes32 indexBytes = CidCbor.CidBytes32.unwrap(indexCid);
59+
for (uint i = startIdx; i < tree.cids.length; i++) {
60+
if (CidCbor.CidBytes32.unwrap(tree.cids[i]) == indexBytes) {
61+
return (true, i);
62+
}
63+
}
64+
return (false, 0);
5565
}
5666

5767
function validTree(Tree memory tree) internal pure returns (Tree memory) {
@@ -64,4 +74,68 @@ library TreeCbor {
6474
}
6575
return tree;
6676
}
77+
78+
function verifyInclusion(
79+
TreeCbor.Tree memory tree,
80+
bytes[] memory treeCbor,
81+
CidCbor.CidBytes32 entryCid,
82+
bytes memory targetRecord,
83+
string memory targetKey
84+
) internal pure returns (bool) {
85+
CidCbor.CidBytes32 targetCid = CidCbor.CidBytes32.wrap(sha256(targetRecord));
86+
CidCbor.CidBytes32[] memory queue = new CidCbor.CidBytes32[](1);
87+
queue[0] = entryCid;
88+
89+
CidCbor.CidBytes32 leftWalk;
90+
CidCbor.CidBytes32[] memory rightWalk;
91+
CidCbor.CidBytes32 currentCid;
92+
TreeNodeCbor.TreeNode memory currentNode;
93+
uint currentIndex;
94+
95+
bool found = false;
96+
bool hasCurrent = false;
97+
98+
CidCbor.CidBytes32[] memory newQueue;
99+
100+
while (queue.length > 0) {
101+
currentCid = queue[0];
102+
(hasCurrent, currentIndex) = TreeCbor.hasCid(tree, currentCid, currentIndex);
103+
if (!hasCurrent) {
104+
newQueue = new CidCbor.CidBytes32[](queue.length - 1);
105+
for (uint i = 0; i < newQueue.length; i++) {
106+
newQueue[i] = queue[i + 1];
107+
}
108+
queue = newQueue;
109+
continue;
110+
}
111+
(currentNode, currentIndex) = TreeCbor.getCid(tree, currentCid, currentIndex);
112+
113+
leftWalk = CidCbor.readCidBytes32(treeCbor[currentIndex], currentNode.left);
114+
rightWalk = new CidCbor.CidBytes32[](currentNode.entries.length);
115+
116+
for (uint i = 0; i < currentNode.entries.length; i++) {
117+
rightWalk[i] = CidCbor.readCidBytes32(treeCbor[currentIndex], currentNode.entries[i].tree);
118+
if (keccak256(abi.encode(targetKey)) == keccak256(abi.encode(currentNode.entries[i].key))) {
119+
CidCbor.CidBytes32 valueCid =
120+
CidCbor.readCidBytes32(treeCbor[currentIndex], currentNode.entries[i].value);
121+
if (CidCbor.CidBytes32.unwrap(valueCid) == CidCbor.CidBytes32.unwrap(targetCid)) {
122+
require(!found, "duplicate entry");
123+
found = true;
124+
}
125+
}
126+
}
127+
128+
newQueue = new CidCbor.CidBytes32[](queue.length + rightWalk.length);
129+
newQueue[0] = leftWalk;
130+
for (uint i = 1; i < queue.length; i++) {
131+
newQueue[i] = queue[i];
132+
}
133+
for (uint i = 0; i < rightWalk.length; i++) {
134+
newQueue[queue.length + i] = rightWalk[i];
135+
}
136+
queue = newQueue;
137+
}
138+
139+
return found;
140+
}
67141
}

test/TreeCbor.t.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ contract TreeTest is Test {
1414
hex"A56364696478206469643A706C633A6D74713365346D67743777796A6868616E69657A656A3637637265766D336C61796B6C746F73703232716464617461D82A5825000171122066DA6655BF8DA79B69A87299CF170FED8497FA3059379DC4A8BFE1E28CAB5D936470726576F66776657273696F6E03";
1515
bytes32 private constant correctLeftCidBytes = hex"6E7335ED248EDAE3ED49D47B88A5FCAD2985E15F416F8AE23A49DFC1231AEB91";
1616

17+
bytes private constant targetRecord =
18+
hex"a4647465787478196361722066696c65732063616e6e6f74206875727420796f75652474797065726170702e62736b792e666565642e706f7374656c616e67738162656e696372656174656441747818323032342d31312d31355431323a31303a33322e3031345a";
19+
1720
//bytes32 private constant recordCidBytes = hex"6d51f125727763752e073d9301892fbddaa4cc6090d5fff9af7d49106b92d457";
1821
//CidCbor.CidBytes32 private constant recordIdx = CidCbor.CidBytes32.wrap(recordCidBytes);
1922

@@ -72,4 +75,8 @@ contract TreeTest is Test {
7275
console.log("node.left index", CidCbor.CidIndex.unwrap(rootNode.left));
7376
console.log("node.entries length", rootNode.entries.length);
7477
}
78+
79+
function test_verifyInclusion_only() public view {
80+
TreeCbor.verifyInclusion(tree, nodeCbors, rootCid, targetRecord, "app.bsky.feed.post/3laydu3mgac2");
81+
}
7582
}

0 commit comments

Comments
 (0)