blobstream-contracts 0.0.1-security → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of blobstream-contracts might be problematic. Click here for more details.

Files changed (64) hide show
  1. package/.codecov.yml +51 -0
  2. package/.github/CODEOWNERS +7 -0
  3. package/.github/dependabot.yml +18 -0
  4. package/.github/workflows/code-analysis.yml +41 -0
  5. package/.github/workflows/contract-inheritance-check.yml +28 -0
  6. package/.github/workflows/go-check.yml +25 -0
  7. package/.github/workflows/labels.yml +19 -0
  8. package/.github/workflows/lint.yml +37 -0
  9. package/.github/workflows/tests.yml +72 -0
  10. package/.gitmodules +12 -0
  11. package/.golangci.yml +64 -0
  12. package/.markdownlint.yaml +5 -0
  13. package/.markdownlint.yml +4 -0
  14. package/.markdownlintignore +1 -0
  15. package/.prettierrc.json +11 -0
  16. package/LICENSE +201 -0
  17. package/Makefile +18 -0
  18. package/README.md +102 -5
  19. package/docs/inclusion-proofs.md +69 -0
  20. package/foundry.toml +4 -0
  21. package/go.mod +34 -0
  22. package/go.sum +212 -0
  23. package/hardhat.config.ts +46 -0
  24. package/index.js +16 -0
  25. package/package.json +31 -3
  26. package/remappings.txt +6 -0
  27. package/scripts/Dockerfile_Environment +39 -0
  28. package/scripts/deploy.ts +12 -0
  29. package/scripts/gen.sh +34 -0
  30. package/scripts/upgradability_check.sh +22 -0
  31. package/slither.config.json +3 -0
  32. package/src/Blobstream.sol +366 -0
  33. package/src/Constants.sol +10 -0
  34. package/src/DataRootTuple.sol +15 -0
  35. package/src/IDAOracle.sol +18 -0
  36. package/src/lib/tree/Constants.sol +23 -0
  37. package/src/lib/tree/Types.sol +37 -0
  38. package/src/lib/tree/Utils.sol +106 -0
  39. package/src/lib/tree/binary/BinaryMerkleMultiproof.sol +12 -0
  40. package/src/lib/tree/binary/BinaryMerkleProof.sol +12 -0
  41. package/src/lib/tree/binary/BinaryMerkleTree.sol +256 -0
  42. package/src/lib/tree/binary/TreeHasher.sol +23 -0
  43. package/src/lib/tree/binary/test/BinaryMerkleTree.t.sol +365 -0
  44. package/src/lib/tree/binary/test/TreeHasher.t.sol +40 -0
  45. package/src/lib/tree/namespace/NamespaceMerkleMultiproof.sol +14 -0
  46. package/src/lib/tree/namespace/NamespaceMerkleProof.sol +14 -0
  47. package/src/lib/tree/namespace/NamespaceMerkleTree.sol +306 -0
  48. package/src/lib/tree/namespace/NamespaceNode.sol +23 -0
  49. package/src/lib/tree/namespace/TreeHasher.sol +69 -0
  50. package/src/lib/tree/namespace/test/NamespaceMerkleMultiproof.t.sol +108 -0
  51. package/src/lib/tree/namespace/test/NamespaceMerkleTree.t.sol +644 -0
  52. package/src/lib/tree/namespace/test/TreeHasher.t.sol +66 -0
  53. package/src/lib/tree/test/Utils.t.sol +48 -0
  54. package/src/lib/tree/test/blob.dat +0 -0
  55. package/src/lib/tree/test/header.dat +0 -0
  56. package/src/lib/tree/test/proofs.json +1 -0
  57. package/src/lib/verifier/DAVerifier.sol +328 -0
  58. package/src/lib/verifier/test/DAVerifier.t.sol +396 -0
  59. package/src/lib/verifier/test/RollupInclusionProofs.t.sol +589 -0
  60. package/src/test/Blobstream.t.sol +200 -0
  61. package/src/test/BlobstreamBenchmark.t.sol +137 -0
  62. package/tsconfig.json +11 -0
  63. package/wrappers/Blobstream.sol/wrapper.go +1325 -0
  64. package/wrappers/ERC1967Proxy.sol/wrapper.go +668 -0
@@ -0,0 +1,256 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.22;
3
+
4
+ import "../Constants.sol";
5
+ import "../Utils.sol";
6
+ import "./TreeHasher.sol";
7
+ import "./BinaryMerkleProof.sol";
8
+ import "./BinaryMerkleMultiproof.sol";
9
+
10
+ /// @title Binary Merkle Tree.
11
+ library BinaryMerkleTree {
12
+ /////////////////
13
+ // Error codes //
14
+ /////////////////
15
+
16
+ enum ErrorCodes {
17
+ NoError,
18
+ /// @notice The provided side nodes count is invalid for the proof.
19
+ InvalidNumberOfSideNodes,
20
+ /// @notice The provided proof key is not part of the tree.
21
+ KeyNotInTree,
22
+ /// @notice Invalid number of leaves in proof.
23
+ InvalidNumberOfLeavesInProof,
24
+ /// @notice The proof contains unexpected side nodes.
25
+ UnexpectedInnerHashes,
26
+ /// @notice The proof verification expected at least one inner hash.
27
+ ExpectedAtLeastOneInnerHash
28
+ }
29
+
30
+ ///////////////
31
+ // Functions //
32
+ ///////////////
33
+
34
+ /// @notice Verify if element exists in Merkle tree, given data, proof, and root.
35
+ /// @param root The root of the tree in which verify the given leaf.
36
+ /// @param proof Binary Merkle proof for the leaf.
37
+ /// @param data The data of the leaf to verify.
38
+ /// @return `true` is proof is valid, `false` otherwise.
39
+ /// @dev proof.numLeaves is necessary to determine height of subtree containing the data to prove.
40
+ function verify(bytes32 root, BinaryMerkleProof memory proof, bytes memory data)
41
+ internal
42
+ pure
43
+ returns (bool, ErrorCodes)
44
+ {
45
+ // Check proof is correct length for the key it is proving
46
+ if (proof.numLeaves <= 1) {
47
+ if (proof.sideNodes.length != 0) {
48
+ return (false, ErrorCodes.InvalidNumberOfSideNodes);
49
+ }
50
+ } else if (proof.sideNodes.length != pathLengthFromKey(proof.key, proof.numLeaves)) {
51
+ return (false, ErrorCodes.InvalidNumberOfSideNodes);
52
+ }
53
+
54
+ // Check key is in tree
55
+ if (proof.key >= proof.numLeaves) {
56
+ return (false, ErrorCodes.KeyNotInTree);
57
+ }
58
+
59
+ // A sibling at height 1 is created by getting the hash of the data to prove.
60
+ bytes32 digest = leafDigest(data);
61
+
62
+ // Null proof is only valid if numLeaves = 1
63
+ // If so, just verify hash(data) is root
64
+ if (proof.sideNodes.length == 0) {
65
+ if (proof.numLeaves == 1) {
66
+ return (root == digest, ErrorCodes.NoError);
67
+ } else {
68
+ return (false, ErrorCodes.NoError);
69
+ }
70
+ }
71
+
72
+ (bytes32 computedHash, ErrorCodes error) = computeRootHash(proof.key, proof.numLeaves, digest, proof.sideNodes);
73
+
74
+ if (error != ErrorCodes.NoError) {
75
+ return (false, error);
76
+ }
77
+
78
+ return (computedHash == root, ErrorCodes.NoError);
79
+ }
80
+
81
+ function verifyMulti(bytes32 root, BinaryMerkleMultiproof memory proof, bytes[] memory data)
82
+ internal
83
+ pure
84
+ returns (bool)
85
+ {
86
+ bytes32[] memory nodes = new bytes32[](data.length);
87
+ for (uint256 i = 0; i < data.length; i++) {
88
+ nodes[i] = leafDigest(data[i]);
89
+ }
90
+
91
+ return verifyMultiHashes(root, proof, nodes);
92
+ }
93
+
94
+ function verifyMultiHashes(bytes32 root, BinaryMerkleMultiproof memory proof, bytes32[] memory leafNodes)
95
+ internal
96
+ pure
97
+ returns (bool)
98
+ {
99
+ uint256 leafIndex = 0;
100
+ bytes32[] memory leftSubtrees = new bytes32[](proof.sideNodes.length);
101
+
102
+ for (uint256 i = 0; leafIndex != proof.beginKey && i < proof.sideNodes.length; ++i) {
103
+ uint256 subtreeSize = _nextSubtreeSize(leafIndex, proof.beginKey);
104
+ leftSubtrees[i] = proof.sideNodes[i];
105
+ leafIndex += subtreeSize;
106
+ }
107
+
108
+ uint256 proofRangeSubtreeEstimate = _getSplitPoint(proof.endKey) * 2;
109
+ if (proofRangeSubtreeEstimate < 1) {
110
+ proofRangeSubtreeEstimate = 1;
111
+ }
112
+
113
+ (bytes32 rootHash, uint256 proofHead,,) =
114
+ _computeRootMulti(proof, leafNodes, 0, proofRangeSubtreeEstimate, 0, 0);
115
+ for (uint256 i = proofHead; i < proof.sideNodes.length; ++i) {
116
+ rootHash = nodeDigest(rootHash, proof.sideNodes[i]);
117
+ }
118
+
119
+ return (rootHash == root);
120
+ }
121
+
122
+ function _computeRootMulti(
123
+ BinaryMerkleMultiproof memory proof,
124
+ bytes32[] memory leafNodes,
125
+ uint256 begin,
126
+ uint256 end,
127
+ uint256 headProof,
128
+ uint256 headLeaves
129
+ ) private pure returns (bytes32, uint256, uint256, bool) {
130
+ // reached a leaf
131
+ if (end - begin == 1) {
132
+ // if current range overlaps with proof range, pop and return a leaf
133
+ if (proof.beginKey <= begin && begin < proof.endKey) {
134
+ // Note: second return value is guaranteed to be `false` by
135
+ // construction.
136
+ return _popLeavesIfNonEmpty(leafNodes, headLeaves, leafNodes.length, headProof);
137
+ }
138
+
139
+ // if current range does not overlap with proof range,
140
+ // pop and return a proof node (leaf) if present,
141
+ // else return nil because leaf doesn't exist
142
+ return _popProofIfNonEmpty(proof.sideNodes, headProof, end, headLeaves);
143
+ }
144
+
145
+ // if current range does not overlap with proof range,
146
+ // pop and return a proof node if present,
147
+ // else return nil because subtree doesn't exist
148
+ if (end <= proof.beginKey || begin >= proof.endKey) {
149
+ return _popProofIfNonEmpty(proof.sideNodes, headProof, end, headLeaves);
150
+ }
151
+
152
+ // Recursively get left and right subtree
153
+ uint256 k = _getSplitPoint(end - begin);
154
+ (bytes32 left, uint256 newHeadProofLeft, uint256 newHeadLeavesLeft,) =
155
+ _computeRootMulti(proof, leafNodes, begin, begin + k, headProof, headLeaves);
156
+ (bytes32 right, uint256 newHeadProof, uint256 newHeadLeaves, bool rightIsNil) =
157
+ _computeRootMulti(proof, leafNodes, begin + k, end, newHeadProofLeft, newHeadLeavesLeft);
158
+
159
+ // only right leaf/subtree can be non-existent
160
+ if (rightIsNil == true) {
161
+ return (left, newHeadProof, newHeadLeaves, false);
162
+ }
163
+ bytes32 hash = nodeDigest(left, right);
164
+ return (hash, newHeadProof, newHeadLeaves, false);
165
+ }
166
+
167
+ function _popProofIfNonEmpty(bytes32[] memory nodes, uint256 headProof, uint256 end, uint256 headLeaves)
168
+ private
169
+ pure
170
+ returns (bytes32, uint256, uint256, bool)
171
+ {
172
+ (bytes32 node, uint256 newHead, bool isNil) = _popIfNonEmpty(nodes, headProof, end);
173
+ return (node, newHead, headLeaves, isNil);
174
+ }
175
+
176
+ function _popLeavesIfNonEmpty(bytes32[] memory nodes, uint256 headLeaves, uint256 end, uint256 headProof)
177
+ private
178
+ pure
179
+ returns (bytes32, uint256, uint256, bool)
180
+ {
181
+ (bytes32 node, uint256 newHead, bool isNil) = _popIfNonEmpty(nodes, headLeaves, end);
182
+ return (node, headProof, newHead, isNil);
183
+ }
184
+
185
+ function _popIfNonEmpty(bytes32[] memory nodes, uint256 head, uint256 end)
186
+ private
187
+ pure
188
+ returns (bytes32, uint256, bool)
189
+ {
190
+ if (nodes.length == 0 || head >= nodes.length || head >= end) {
191
+ bytes32 node;
192
+ return (node, head, true);
193
+ }
194
+ return (nodes[head], head + 1, false);
195
+ }
196
+
197
+ /// @notice Use the leafHash and innerHashes to get the root merkle hash.
198
+ /// If the length of the innerHashes slice isn't exactly correct, the result is nil.
199
+ /// Recursive impl.
200
+ function computeRootHash(uint256 key, uint256 numLeaves, bytes32 leafHash, bytes32[] memory sideNodes)
201
+ private
202
+ pure
203
+ returns (bytes32, ErrorCodes)
204
+ {
205
+ if (numLeaves == 0) {
206
+ return (leafHash, ErrorCodes.InvalidNumberOfLeavesInProof);
207
+ }
208
+ if (numLeaves == 1) {
209
+ if (sideNodes.length != 0) {
210
+ return (leafHash, ErrorCodes.UnexpectedInnerHashes);
211
+ }
212
+ return (leafHash, ErrorCodes.NoError);
213
+ }
214
+ if (sideNodes.length == 0) {
215
+ return (leafHash, ErrorCodes.ExpectedAtLeastOneInnerHash);
216
+ }
217
+ uint256 numLeft = _getSplitPoint(numLeaves);
218
+ bytes32[] memory sideNodesLeft = slice(sideNodes, 0, sideNodes.length - 1);
219
+ ErrorCodes error;
220
+ if (key < numLeft) {
221
+ bytes32 leftHash;
222
+ (leftHash, error) = computeRootHash(key, numLeft, leafHash, sideNodesLeft);
223
+ if (error != ErrorCodes.NoError) {
224
+ return (leafHash, error);
225
+ }
226
+ return (nodeDigest(leftHash, sideNodes[sideNodes.length - 1]), ErrorCodes.NoError);
227
+ }
228
+ bytes32 rightHash;
229
+ (rightHash, error) = computeRootHash(key - numLeft, numLeaves - numLeft, leafHash, sideNodesLeft);
230
+ if (error != ErrorCodes.NoError) {
231
+ return (leafHash, error);
232
+ }
233
+ return (nodeDigest(sideNodes[sideNodes.length - 1], rightHash), ErrorCodes.NoError);
234
+ }
235
+
236
+ /// @notice creates a slice of bytes32 from the data slice of bytes32 containing the elements
237
+ /// that correspond to the provided range.
238
+ /// It selects a half-open range which includes the begin element, but excludes the end one.
239
+ /// @param _data The slice that we want to select data from.
240
+ /// @param _begin The beginning of the range (inclusive).
241
+ /// @param _end The ending of the range (exclusive).
242
+ /// @return _ the sliced data.
243
+ function slice(bytes32[] memory _data, uint256 _begin, uint256 _end) internal pure returns (bytes32[] memory) {
244
+ if (_begin > _end) {
245
+ revert("Invalid range: _begin is greater than _end");
246
+ }
247
+ if (_begin > _data.length || _end > _data.length) {
248
+ revert("Invalid range: _begin or _end are out of bounds");
249
+ }
250
+ bytes32[] memory out = new bytes32[](_end - _begin);
251
+ for (uint256 i = _begin; i < _end; i++) {
252
+ out[i - _begin] = _data[i];
253
+ }
254
+ return out;
255
+ }
256
+ }
@@ -0,0 +1,23 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.22;
3
+
4
+ import "../Constants.sol";
5
+
6
+ /// @notice Calculate the digest of a node.
7
+ /// @param left The left child.
8
+ /// @param right The right child.
9
+ /// @return digest The node digest.
10
+ /// @dev More details in https://github.com/celestiaorg/celestia-specs/blob/master/src/specs/data_structures.md#binary-merkle-tree
11
+ // solhint-disable-next-line func-visibility
12
+ function nodeDigest(bytes32 left, bytes32 right) pure returns (bytes32 digest) {
13
+ digest = sha256(abi.encodePacked(Constants.NODE_PREFIX, left, right));
14
+ }
15
+
16
+ /// @notice Calculate the digest of a leaf.
17
+ /// @param data The data of the leaf.
18
+ /// @return digest The leaf digest.
19
+ /// @dev More details in https://github.com/celestiaorg/celestia-specs/blob/master/src/specs/data_structures.md#binary-merkle-tree
20
+ // solhint-disable-next-line func-visibility
21
+ function leafDigest(bytes memory data) pure returns (bytes32 digest) {
22
+ digest = sha256(abi.encodePacked(Constants.LEAF_PREFIX, data));
23
+ }
@@ -0,0 +1,365 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.22;
3
+
4
+ import "ds-test/test.sol";
5
+ import "forge-std/Vm.sol";
6
+
7
+ import "../BinaryMerkleProof.sol";
8
+ import "../BinaryMerkleTree.sol";
9
+ import "../BinaryMerkleMultiproof.sol";
10
+
11
+ /**
12
+ * TEST VECTORS
13
+ *
14
+ * 0x01
15
+ * 0x02
16
+ * 0x03
17
+ * 0x04
18
+ * 0x05
19
+ * 0x06
20
+ * 0x07
21
+ * 0x08
22
+ *
23
+ * 0xb413f47d13ee2fe6c845b2ee141af81de858df4ec549a58b7970bb96645bc8d2
24
+ * 0xfcf0a6c700dd13e274b6fba8deea8dd9b26e4eedde3495717cac8408c9c5177f
25
+ * 0x583c7dfb7b3055d99465544032a571e10a134b1b6f769422bbb71fd7fa167a5d
26
+ * 0x4f35212d12f9ad2036492c95f1fe79baf4ec7bd9bef3dffa7579f2293ff546a4
27
+ * 0x9f1afa4dc124cba73134e82ff50f17c8f7164257c79fed9a13f5943a6acb8e3d
28
+ * 0x40d88127d4d31a3891f41598eeed41174e5bc89b1eb9bbd66a8cbfc09956a3fd
29
+ * 0x2ecd8a6b7d2845546659ad4cf443533cf921b19dc81fa83934e83821b4dfdcb7
30
+ * 0xb4c43b50bf245bd727623e3c775a8fcfb8d823d00b57dd65f7f79dd33f126315
31
+ *
32
+ * 0x6bcf0e2e93e0a18e22789aee965e6553f4fbe93f0acfc4a705d691c8311c4965
33
+ * 0x78850a5ab36238b076dd99fd258c70d523168704247988a94caa8c9ccd056b8d
34
+ * 0x90eeb2c4a04ec33ee4dd2677593331910e4203db4fcc120a6cdb95b13cfe83f0
35
+ * 0x28c01722dd8dd05b63bcdeb6878bc2c083118cc2b170646d6b842d0bdbdc9d29
36
+ *
37
+ * 0xfa02d31a63cc11cc624881e52af14af7a1c6ab745efa71021cb24086b9b1793f
38
+ * 0x4301a067262bbb18b4919742326f6f6d706099f9c0e8b0f2db7b88f204b2cf09
39
+ *
40
+ * 0xc1ad6548cb4c7663110df219ec8b36ca63b01158956f4be31a38a88d0c7f7071
41
+ *
42
+ */
43
+
44
+ // Intermediate mock contract that calls the library function
45
+ // and returns the result.
46
+ // This is done because of the expectRevert v1 behaviour change:
47
+ // https://github.com/foundry-rs/book/pull/922
48
+ contract BinaryMerkleTreeLibMock {
49
+ function slice(bytes32[] memory _data, uint256 _begin, uint256 _end) external returns (bytes32[] memory) {
50
+ return BinaryMerkleTree.slice(_data, _begin, _end);
51
+ }
52
+ }
53
+
54
+ contract BinaryMerkleProofTest is DSTest {
55
+ Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
56
+ BinaryMerkleTreeLibMock public binaryMerkleTreeMock;
57
+
58
+ function setUp() external {
59
+ binaryMerkleTreeMock = new BinaryMerkleTreeLibMock();
60
+ }
61
+
62
+ function testVerifyNone() external {
63
+ bytes32 root = sha256("");
64
+ bytes32[] memory sideNodes;
65
+ uint256 key = 0;
66
+ uint256 numLeaves = 0;
67
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
68
+ bytes memory data;
69
+ (bool isValid,) = BinaryMerkleTree.verify(root, proof, data);
70
+ assertTrue(!isValid);
71
+ }
72
+
73
+ function testVerifyOneLeafEmpty() external {
74
+ bytes32 root = 0x6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d;
75
+ bytes32[] memory sideNodes;
76
+ uint256 key = 0;
77
+ uint256 numLeaves = 1;
78
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
79
+ bytes memory data;
80
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
81
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
82
+ assertTrue(isValid);
83
+ }
84
+
85
+ function testVerifyOneLeafSome() external {
86
+ bytes32 root = 0x48c90c8ae24688d6bef5d48a30c2cc8b6754335a8db21793cc0a8e3bed321729;
87
+ bytes32[] memory sideNodes;
88
+ uint256 key = 0;
89
+ uint256 numLeaves = 1;
90
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
91
+ bytes memory data = hex"deadbeef";
92
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
93
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
94
+ assertTrue(isValid);
95
+ }
96
+
97
+ function testVerifyOneLeaf01() external {
98
+ bytes32 root = 0xb413f47d13ee2fe6c845b2ee141af81de858df4ec549a58b7970bb96645bc8d2;
99
+ bytes32[] memory sideNodes;
100
+ uint256 key = 0;
101
+ uint256 numLeaves = 1;
102
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
103
+ bytes memory data = hex"01";
104
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
105
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
106
+ assertTrue(isValid);
107
+ }
108
+
109
+ function testVerifyLeafOneOfEight() external {
110
+ bytes32 root = 0xc1ad6548cb4c7663110df219ec8b36ca63b01158956f4be31a38a88d0c7f7071;
111
+ bytes32[] memory sideNodes = new bytes32[](3);
112
+ sideNodes[0] = 0xfcf0a6c700dd13e274b6fba8deea8dd9b26e4eedde3495717cac8408c9c5177f;
113
+ sideNodes[1] = 0x78850a5ab36238b076dd99fd258c70d523168704247988a94caa8c9ccd056b8d;
114
+ sideNodes[2] = 0x4301a067262bbb18b4919742326f6f6d706099f9c0e8b0f2db7b88f204b2cf09;
115
+
116
+ uint256 key = 0;
117
+ uint256 numLeaves = 8;
118
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
119
+ bytes memory data = hex"01";
120
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
121
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
122
+ assertTrue(isValid);
123
+ }
124
+
125
+ function testVerifyLeafTwoOfEight() external {
126
+ bytes32 root = 0xc1ad6548cb4c7663110df219ec8b36ca63b01158956f4be31a38a88d0c7f7071;
127
+ bytes32[] memory sideNodes = new bytes32[](3);
128
+ sideNodes[0] = 0xb413f47d13ee2fe6c845b2ee141af81de858df4ec549a58b7970bb96645bc8d2;
129
+ sideNodes[1] = 0x78850a5ab36238b076dd99fd258c70d523168704247988a94caa8c9ccd056b8d;
130
+ sideNodes[2] = 0x4301a067262bbb18b4919742326f6f6d706099f9c0e8b0f2db7b88f204b2cf09;
131
+
132
+ uint256 key = 1;
133
+ uint256 numLeaves = 8;
134
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
135
+ bytes memory data = hex"02";
136
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
137
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
138
+ assertTrue(isValid);
139
+ }
140
+
141
+ function testVerifyLeafThreeOfEight() external {
142
+ bytes32 root = 0xc1ad6548cb4c7663110df219ec8b36ca63b01158956f4be31a38a88d0c7f7071;
143
+ bytes32[] memory sideNodes = new bytes32[](3);
144
+ sideNodes[0] = 0x4f35212d12f9ad2036492c95f1fe79baf4ec7bd9bef3dffa7579f2293ff546a4;
145
+ sideNodes[1] = 0x6bcf0e2e93e0a18e22789aee965e6553f4fbe93f0acfc4a705d691c8311c4965;
146
+ sideNodes[2] = 0x4301a067262bbb18b4919742326f6f6d706099f9c0e8b0f2db7b88f204b2cf09;
147
+
148
+ uint256 key = 2;
149
+ uint256 numLeaves = 8;
150
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
151
+ bytes memory data = hex"03";
152
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
153
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
154
+ assertTrue(isValid);
155
+ }
156
+
157
+ function testVerifyLeafSevenOfEight() external {
158
+ bytes32 root = 0xc1ad6548cb4c7663110df219ec8b36ca63b01158956f4be31a38a88d0c7f7071;
159
+ bytes32[] memory sideNodes = new bytes32[](3);
160
+ sideNodes[0] = 0xb4c43b50bf245bd727623e3c775a8fcfb8d823d00b57dd65f7f79dd33f126315;
161
+ sideNodes[1] = 0x90eeb2c4a04ec33ee4dd2677593331910e4203db4fcc120a6cdb95b13cfe83f0;
162
+ sideNodes[2] = 0xfa02d31a63cc11cc624881e52af14af7a1c6ab745efa71021cb24086b9b1793f;
163
+
164
+ uint256 key = 6;
165
+ uint256 numLeaves = 8;
166
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
167
+ bytes memory data = hex"07";
168
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
169
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
170
+ assertTrue(isValid);
171
+ }
172
+
173
+ function testVerifyLeafEightOfEight() external {
174
+ bytes32 root = 0xc1ad6548cb4c7663110df219ec8b36ca63b01158956f4be31a38a88d0c7f7071;
175
+ bytes32[] memory sideNodes = new bytes32[](3);
176
+ sideNodes[0] = 0x2ecd8a6b7d2845546659ad4cf443533cf921b19dc81fa83934e83821b4dfdcb7;
177
+ sideNodes[1] = 0x90eeb2c4a04ec33ee4dd2677593331910e4203db4fcc120a6cdb95b13cfe83f0;
178
+ sideNodes[2] = 0xfa02d31a63cc11cc624881e52af14af7a1c6ab745efa71021cb24086b9b1793f;
179
+
180
+ uint256 key = 7;
181
+ uint256 numLeaves = 8;
182
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
183
+ bytes memory data = hex"08";
184
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
185
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
186
+ assertTrue(isValid);
187
+ }
188
+
189
+ // Test vectors:
190
+ // 0x00
191
+ // 0x01
192
+ // 0x02
193
+ // 0x03
194
+ // 0x04
195
+ function testVerifyProofOfFiveLeaves() external {
196
+ bytes32 root = 0xb855b42d6c30f5b087e05266783fbd6e394f7b926013ccaa67700a8b0c5a596f;
197
+ bytes32[] memory sideNodes = new bytes32[](3);
198
+ sideNodes[0] = 0x96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7;
199
+ sideNodes[1] = 0x52c56b473e5246933e7852989cd9feba3b38f078742b93afff1e65ed46797825;
200
+ sideNodes[2] = 0x4f35212d12f9ad2036492c95f1fe79baf4ec7bd9bef3dffa7579f2293ff546a4;
201
+
202
+ uint256 key = 1;
203
+ uint256 numLeaves = 5;
204
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
205
+ bytes memory data = bytes(hex"01");
206
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
207
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
208
+ assertTrue(isValid);
209
+ }
210
+
211
+ function testVerifyInvalidProofRoot() external {
212
+ // correct root: 0xb855b42d6c30f5b087e05266783fbd6e394f7b926013ccaa67700a8b0c5a596f;
213
+ bytes32 root = 0xc855b42d6c30f5b087e05266783fbd6e394f7b926013ccaa67700a8b0c5a596f;
214
+ bytes32[] memory sideNodes = new bytes32[](3);
215
+ sideNodes[0] = 0x96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7;
216
+ sideNodes[1] = 0x52c56b473e5246933e7852989cd9feba3b38f078742b93afff1e65ed46797825;
217
+ sideNodes[2] = 0x4f35212d12f9ad2036492c95f1fe79baf4ec7bd9bef3dffa7579f2293ff546a4;
218
+
219
+ uint256 key = 1;
220
+ uint256 numLeaves = 5;
221
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
222
+ bytes memory data = bytes(hex"01");
223
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
224
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
225
+ assertTrue(!isValid);
226
+ }
227
+
228
+ function testVerifyInvalidProofKey() external {
229
+ bytes32 root = 0xb855b42d6c30f5b087e05266783fbd6e394f7b926013ccaa67700a8b0c5a596f;
230
+ bytes32[] memory sideNodes = new bytes32[](3);
231
+ sideNodes[0] = 0x96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7;
232
+ sideNodes[1] = 0x52c56b473e5246933e7852989cd9feba3b38f078742b93afff1e65ed46797825;
233
+ sideNodes[2] = 0x4f35212d12f9ad2036492c95f1fe79baf4ec7bd9bef3dffa7579f2293ff546a4;
234
+
235
+ // correct key: 1
236
+ uint256 key = 2;
237
+ uint256 numLeaves = 5;
238
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
239
+ bytes memory data = bytes(hex"01");
240
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
241
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
242
+ assertTrue(!isValid);
243
+ }
244
+
245
+ function testVerifyInvalidProofNumberOfLeaves() external {
246
+ bytes32 root = 0xb855b42d6c30f5b087e05266783fbd6e394f7b926013ccaa67700a8b0c5a596f;
247
+ bytes32[] memory sideNodes = new bytes32[](3);
248
+ sideNodes[0] = 0x96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7;
249
+ sideNodes[1] = 0x52c56b473e5246933e7852989cd9feba3b38f078742b93afff1e65ed46797825;
250
+ sideNodes[2] = 0x4f35212d12f9ad2036492c95f1fe79baf4ec7bd9bef3dffa7579f2293ff546a4;
251
+
252
+ uint256 key = 1;
253
+ // correct numLeaves: 5
254
+ uint256 numLeaves = 200;
255
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
256
+ bytes memory data = bytes(hex"01");
257
+ (bool isValid,) = BinaryMerkleTree.verify(root, proof, data);
258
+ assertTrue(!isValid);
259
+ }
260
+
261
+ function testVerifyInvalidProofSideNodes() external {
262
+ bytes32 root = 0xb855b42d6c30f5b087e05266783fbd6e394f7b926013ccaa67700a8b0c5a596f;
263
+ bytes32[] memory sideNodes = new bytes32[](3);
264
+ sideNodes[0] = 0x96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7;
265
+ sideNodes[1] = 0x52c56b473e5246933e7852989cd9feba3b38f078742b93afff1e65ed46797825;
266
+ // correct side node: 0x4f35212d12f9ad2036492c95f1fe79baf4ec7bd9bef3dffa7579f2293ff546a4;
267
+ sideNodes[2] = 0x5f35212d12f9ad2036492c95f1fe79baf4ec7bd9bef3dffa7579f2293ff546a4;
268
+
269
+ uint256 key = 1;
270
+ uint256 numLeaves = 5;
271
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
272
+ bytes memory data = bytes(hex"01");
273
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
274
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
275
+ assertTrue(!isValid);
276
+ }
277
+
278
+ function testVerifyInvalidProofData() external {
279
+ bytes32 root = 0xb855b42d6c30f5b087e05266783fbd6e394f7b926013ccaa67700a8b0c5a596f;
280
+ bytes32[] memory sideNodes = new bytes32[](3);
281
+ sideNodes[0] = 0x96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7;
282
+ sideNodes[1] = 0x52c56b473e5246933e7852989cd9feba3b38f078742b93afff1e65ed46797825;
283
+ sideNodes[2] = 0x4f35212d12f9ad2036492c95f1fe79baf4ec7bd9bef3dffa7579f2293ff546a4;
284
+
285
+ uint256 key = 1;
286
+ uint256 numLeaves = 5;
287
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
288
+ // correct data: 01
289
+ bytes memory data = bytes(hex"012345");
290
+ (bool isValid, BinaryMerkleTree.ErrorCodes error) = BinaryMerkleTree.verify(root, proof, data);
291
+ assertEq(uint256(BinaryMerkleTree.ErrorCodes.NoError), uint256(error));
292
+ assertTrue(!isValid);
293
+ }
294
+
295
+ function testValidSlice() public {
296
+ bytes32[] memory data = new bytes32[](4);
297
+ data[0] = "a";
298
+ data[1] = "b";
299
+ data[2] = "c";
300
+ data[3] = "d";
301
+
302
+ bytes32[] memory result = BinaryMerkleTree.slice(data, 1, 3);
303
+
304
+ assertEq(result[0], data[1]);
305
+ assertEq(result[1], data[2]);
306
+ }
307
+
308
+ function testSameKeyAndLeavesNumber() external {
309
+ bytes32 root = 0xb855b42d6c30f5b087e05266783fbd6e394f7b926013ccaa67700a8b0c5a596f;
310
+ bytes32[] memory sideNodes = new bytes32[](0);
311
+ uint256 key = 3;
312
+ uint256 numLeaves = 3;
313
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
314
+ bytes memory data = bytes(hex"01");
315
+ (bool isValid,) = BinaryMerkleTree.verify(root, proof, data);
316
+ assertTrue(!isValid);
317
+ }
318
+
319
+ function testConsecutiveKeyAndNumberOfLeaves() external {
320
+ bytes32 root = 0xb855b42d6c30f5b087e05266783fbd6e394f7b926013ccaa67700a8b0c5a596f;
321
+ bytes32[] memory sideNodes = new bytes32[](0);
322
+ uint256 key = 6;
323
+ uint256 numLeaves = 7;
324
+ BinaryMerkleProof memory proof = BinaryMerkleProof(sideNodes, key, numLeaves);
325
+ bytes memory data = bytes(hex"01");
326
+ (bool isValid,) = BinaryMerkleTree.verify(root, proof, data);
327
+ assertTrue(!isValid);
328
+ }
329
+
330
+ function testInvalidSliceBeginEnd() public {
331
+ bytes32[] memory data = new bytes32[](4);
332
+ data[0] = "a";
333
+ data[1] = "b";
334
+ data[2] = "c";
335
+ data[3] = "d";
336
+
337
+ vm.expectRevert("Invalid range: _begin is greater than _end");
338
+ binaryMerkleTreeMock.slice(data, 2, 1);
339
+ }
340
+
341
+ function testOutOfBoundsSlice() public {
342
+ bytes32[] memory data = new bytes32[](4);
343
+ data[0] = "a";
344
+ data[1] = "b";
345
+ data[2] = "c";
346
+ data[3] = "d";
347
+
348
+ vm.expectRevert("Invalid range: _begin or _end are out of bounds");
349
+ binaryMerkleTreeMock.slice(data, 2, 5);
350
+ }
351
+
352
+ // header.dat, blob.dat, and proofs.json test vectors included in ../../test/ and serialized to hex bytes using Rust
353
+ // The hard-coded serialized proofs and data were generated in Rust, with this code
354
+ // https://github.com/S1nus/hyperchain-da/blob/main/src/clients/celestia/evm_types.rs#L132
355
+ function testMultiproof() public {
356
+ bytes memory proofData =
357
+ hex"00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000006ce29bcde696f84e35c5626904542a549b080e92603243b34794242473940706917519bf954f5b30495af5c8cdb9983e6319104badc1ea811ed2c421018a3ad7821ea268d3540deab8f9b2024464618610c9a7083620badcf505bda647cc8e9f82bfc87d990d8344f6efd44fcb09b46b87f9a92230d41329452efee8656c6760a9ad9f3a95af971e89e2a80b255bb56d5aae15de69803b52aa5079b33374b16e16178fc62a2f2ce6bf21909c0a0edea9525486e0ece65bff23499342cca38dd62";
358
+ BinaryMerkleMultiproof memory multiproof = abi.decode(proofData, (BinaryMerkleMultiproof));
359
+ bytes32 dataroot = hex"ef8920d86519bd5f8ce3c802b84fc9b9512483e4d4a5c9608b44af4d6639f7d1";
360
+ bytes memory leafData =
361
+ hex"00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000102030405746e218305fe3dbbef65feceed939fe8dd93c88b06c95473fbe344fb864060f3000000000000000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000102030405000000000000000000000000000000000000000000000000010203040555cd7fb524ae792c9d4bc8946d07209728c533a3e14d4e7c0c95c0b150d0c284000000000000000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000001020304050000000000000000000000000000000000000000000000000102030405505c1e7c897461a152e152f1ff3ecc358fefdf1f69448ab1165b6ca76836933b000000000000000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000001020304050000000000000000000000000000000000000000000000000102030405100a0548893d8eab0322f34f45ac84785cdf50dfab5102a12d958e6031bacebe000000000000000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000102030405000000000000000000000000000000000000000000000000010203040566e5eb1da67430f204a3c5615591f71316695c7ec1f1f713cde7e936d4a43ec1000000000000000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000001020304050000000000000000000000000000000000000000000000000102030405d2a5de6299e28c2fec359a2718599f5ac22c2948a71d26a438295e531b6f4cb5000000000000000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000001020304050000000000000000000000000000000000000000000000000102030405688c5238e50c0a8a556bfabff31bef1fa9cdd812c9fd4dcee5c2a0836f687fbf000000000000000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000001020304050000000000000000000000000000000000000000000000000102030405b55a5b1efc2a22cdbfa21d050bd67147ff2b936c68354eb1a83bcdf14eb57e38000000000000000000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000010203040500000000000000000000000000000000000000000067480c4a88c4d129947e11c33fa811daa791771e591dd933498d1212d46b8cde9c34c28831b0b532000000000000";
362
+ bytes[] memory leaves = abi.decode(leafData, (bytes[]));
363
+ assertTrue(BinaryMerkleTree.verifyMulti(dataroot, multiproof, leaves));
364
+ }
365
+ }