blobstream-contracts 0.0.1-security → 3.3.3

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 +94 -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,66 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.22;
3
+
4
+ import "ds-test/test.sol";
5
+
6
+ import "../../Constants.sol";
7
+ import "../../Types.sol";
8
+ import "../NamespaceNode.sol";
9
+ import "../TreeHasher.sol";
10
+
11
+ contract TreeHasherTest is DSTest {
12
+ function setUp() external {}
13
+
14
+ function assertEqNamespaceNode(NamespaceNode memory first, NamespaceNode memory second) internal {
15
+ assertTrue(first.min.equalTo(second.min));
16
+ assertTrue(first.max.equalTo(second.max));
17
+ assertEq(first.digest, second.digest);
18
+ }
19
+
20
+ function testLeafDigestEmpty() external {
21
+ Namespace memory nid = Namespace(0x00, 0x00000000000000000000000000000000000000000000000000000000);
22
+ NamespaceNode memory expected =
23
+ NamespaceNode(nid, nid, 0x0679246d6c4216de0daa08e5523fb2674db2b6599c3b72ff946b488a15290b62);
24
+ bytes memory data;
25
+ NamespaceNode memory node = leafDigest(nid, data);
26
+ assertEqNamespaceNode(node, expected);
27
+ }
28
+
29
+ function testLeafDigestSome() external {
30
+ Namespace memory nid = Namespace(0xde, 0xadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefde);
31
+ NamespaceNode memory expected =
32
+ NamespaceNode(nid, nid, 0x3624c7f7169cb5bbd0d010b851ebd0edca10b2a1b126f5fb1a6d5e0d98356e63);
33
+ bytes memory data = hex"69";
34
+ NamespaceNode memory node = leafDigest(nid, data);
35
+ assertEqNamespaceNode(node, expected);
36
+ }
37
+
38
+ function testNodeDigest() external {
39
+ Namespace memory nidLeft = Namespace(0x00, 0x00000000000000000000000000000000000000000000000000000000);
40
+ Namespace memory nidRight = Namespace(0xde, 0xadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefde);
41
+ NamespaceNode memory expected =
42
+ NamespaceNode(nidLeft, nidRight, 0x95cad48bc181484c851004cf772abe767391e19549d3b8192b55b1d654a71bcd);
43
+ NamespaceNode memory left =
44
+ NamespaceNode(nidLeft, nidLeft, 0xdb55da3fc3098e9c42311c6013304ff36b19ef73d12ea932054b5ad51df4f49d);
45
+ NamespaceNode memory right =
46
+ NamespaceNode(nidRight, nidRight, 0xc75cb66ae28d8ebc6eded002c28a8ba0d06d3a78c6b5cbf9b2ade051f0775ac4);
47
+ NamespaceNode memory node = nodeDigest(left, right);
48
+ assertEqNamespaceNode(node, expected);
49
+ }
50
+
51
+ function testNodeParity() external {
52
+ Namespace memory nidMin = Namespace(0x00, 0x00000000000000000000000000000000000000000000000000000000);
53
+ Namespace memory nidMax = Namespace(0xde, 0xadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefde);
54
+ NamespaceNode memory expected =
55
+ NamespaceNode(nidMin, nidMax, 0xc6960f535d4ab0aed075aed34a116725e8035012ceffe5405ae72abe3bcaa28f);
56
+ NamespaceNode memory left =
57
+ NamespaceNode(nidMin, nidMax, 0xdb55da3fc3098e9c42311c6013304ff36b19ef73d12ea932054b5ad51df4f49d);
58
+ NamespaceNode memory right = NamespaceNode(
59
+ PARITY_SHARE_NAMESPACE(),
60
+ PARITY_SHARE_NAMESPACE(),
61
+ 0xc75cb66ae28d8ebc6eded002c28a8ba0d06d3a78c6b5cbf9b2ade051f0775ac4
62
+ );
63
+ NamespaceNode memory node = nodeDigest(left, right);
64
+ assertEqNamespaceNode(node, expected);
65
+ }
66
+ }
@@ -0,0 +1,48 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.22;
3
+
4
+ import "ds-test/test.sol";
5
+
6
+ import "../Utils.sol";
7
+
8
+ contract UtilsTest is DSTest {
9
+ function testPathLengthFromKey0_2() external {
10
+ assertEq(pathLengthFromKey(0, 2), 1);
11
+ }
12
+
13
+ function testPathLengthFromKey1_2() external {
14
+ assertEq(pathLengthFromKey(1, 2), 1);
15
+ }
16
+
17
+ function testPathLengthFromKey0_8() external {
18
+ assertEq(pathLengthFromKey(0, 8), 3);
19
+ }
20
+
21
+ function testPathLengthFromKey1_8() external {
22
+ assertEq(pathLengthFromKey(1, 8), 3);
23
+ }
24
+
25
+ function testPathLengthFromKey2_8() external {
26
+ assertEq(pathLengthFromKey(2, 8), 3);
27
+ }
28
+
29
+ function testPathLengthFromKey3_8() external {
30
+ assertEq(pathLengthFromKey(3, 8), 3);
31
+ }
32
+
33
+ function testPathLengthFromKey4_8() external {
34
+ assertEq(pathLengthFromKey(4, 8), 3);
35
+ }
36
+
37
+ function testPathLengthFromKey5_8() external {
38
+ assertEq(pathLengthFromKey(5, 8), 3);
39
+ }
40
+
41
+ function testPathLengthFromKey6_8() external {
42
+ assertEq(pathLengthFromKey(6, 8), 3);
43
+ }
44
+
45
+ function testPathLengthFromKey7_8() external {
46
+ assertEq(pathLengthFromKey(7, 8), 3);
47
+ }
48
+ }
Binary file
Binary file
@@ -0,0 +1 @@
1
+ [{"start":8,"end":32,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOLCUcGcDNOGgcYmOnu7snv+cn+3G+vkto91wnXa3kVQ","/////////////////////////////////////////////////////////////////////////////zmvU+iSdf6GDmfvDMVa0YqTan9iPIiX5UHyC8zhZkkf"],"leaf_hash":"","is_max_namespace_ignored":true},{"start":0,"end":32,"nodes":["/////////////////////////////////////////////////////////////////////////////xZ6gFJq4RO/FIE75WZbKQOZmS3FCVTEVM/dKR/kzDZz"],"leaf_hash":"","is_max_namespace_ignored":true},{"start":0,"end":32,"nodes":["/////////////////////////////////////////////////////////////////////////////5MEmpTXPlH6UVRm8X2csA+EaccobRIwYWyOF5ls5abx"],"leaf_hash":"","is_max_namespace_ignored":true},{"start":0,"end":32,"nodes":["/////////////////////////////////////////////////////////////////////////////3wn2BGIhHap4sg/oUMt6THYs/c8kj+mFXPoFL3NxC9I"],"leaf_hash":"","is_max_namespace_ignored":true},{"start":0,"end":32,"nodes":["//////////////////////////////////////////////////////////////////////////////vTJmXZEYdLpfIuqDC7XrkhCLaw6GE1Iz1EzwDecR57"],"leaf_hash":"","is_max_namespace_ignored":true},{"start":0,"end":32,"nodes":["/////////////////////////////////////////////////////////////////////////////+HJEAbaxVdhII24yEvjUz8rAJmA8T9ratZdZHUASsLe"],"leaf_hash":"","is_max_namespace_ignored":true},{"start":0,"end":32,"nodes":["/////////////////////////////////////////////////////////////////////////////4K3KpNnwd8qcf40yM88DYQ087APK4Kjc98+WAchu59l"],"leaf_hash":"","is_max_namespace_ignored":true},{"start":0,"end":32,"nodes":["/////////////////////////////////////////////////////////////////////////////5wXTQR9kfP9JmlezblQvri0MM7UccRF1qUZ/2ELgyMf"],"leaf_hash":"","is_max_namespace_ignored":true},{"start":0,"end":24,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAABpbeXXkKu9gAAAAAAAAAAAAAAAAAAAAAAAAAAABnSAxKiMTRKTLbUSuhrp5YtSI7UOw4sUkTDR1mrRutu+xxGAq64vXA","/////////////////////////////////////////////////////////////////////////////9+uMDUChYyrcrRy2fQ9h15MuGF69AmtPLH3FGuTFCUX"],"leaf_hash":"","is_max_namespace_ignored":true}]
@@ -0,0 +1,328 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.22;
3
+
4
+ import "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
5
+
6
+ import "../../Constants.sol";
7
+ import "../../DataRootTuple.sol";
8
+ import "../../IDAOracle.sol";
9
+ import "../tree/binary/BinaryMerkleProof.sol";
10
+ import "../tree/binary/BinaryMerkleTree.sol";
11
+ import "../tree/namespace/NamespaceMerkleTree.sol";
12
+ import "../tree/Types.sol";
13
+
14
+ /// @notice Contains the necessary parameters to prove that some shares, which were posted to
15
+ /// the Celestia network, were committed to by the Blobstream smart contract.
16
+ struct SharesProof {
17
+ // The shares that were committed to.
18
+ bytes[] data;
19
+ // The shares proof to the row roots. If the shares span multiple rows, we will have multiple nmt proofs.
20
+ NamespaceMerkleMultiproof[] shareProofs;
21
+ // The namespace of the shares.
22
+ Namespace namespace;
23
+ // The rows where the shares belong. If the shares span multiple rows, we will have multiple rows.
24
+ NamespaceNode[] rowRoots;
25
+ // The proofs of the rowRoots to the data root.
26
+ BinaryMerkleProof[] rowProofs;
27
+ // The proof of the data root tuple to the data root tuple root that was posted to the Blobstream contract.
28
+ AttestationProof attestationProof;
29
+ }
30
+
31
+ /// @notice Contains the necessary parameters needed to verify that a data root tuple
32
+ /// was committed to, by the Blobstream smart contract, at some specif nonce.
33
+ struct AttestationProof {
34
+ // the attestation nonce that commits to the data root tuple.
35
+ uint256 tupleRootNonce;
36
+ // the data root tuple that was committed to.
37
+ DataRootTuple tuple;
38
+ // the binary merkle proof of the tuple to the commitment.
39
+ BinaryMerkleProof proof;
40
+ }
41
+
42
+ /// @title DAVerifier: Celestia -> EVM, Data Availability verifier.
43
+ /// @dev The DAVerifier verifies that some shares, which were posted on Celestia, were committed to
44
+ /// by the Blobstream smart contract.
45
+ library DAVerifier {
46
+ /////////////////
47
+ // Error codes //
48
+ /////////////////
49
+
50
+ enum ErrorCodes {
51
+ NoError,
52
+ /// @notice The shares to the rows proof is invalid.
53
+ InvalidSharesToRowsProof,
54
+ /// @notice The rows to the data root proof is invalid.
55
+ InvalidRowsToDataRootProof,
56
+ /// @notice The row to the data root proof is invalid.
57
+ InvalidRowToDataRootProof,
58
+ /// @notice The data root tuple to the data root tuple roof proof is invalid.
59
+ InvalidDataRootTupleToDataRootTupleRootProof,
60
+ /// @notice The number of share proofs isn't equal to the number of rows roots.
61
+ UnequalShareProofsAndRowRootsNumber,
62
+ /// @notice The number of rows proofs isn't equal to the number of rows roots.
63
+ UnequalRowProofsAndRowRootsNumber,
64
+ /// @notice The verifier data length isn't equal to the number of shares in the shares proofs.
65
+ UnequalDataLengthAndNumberOfSharesProofs,
66
+ /// @notice The number of leaves in the binary merkle proof is not divisible by 4.
67
+ InvalidNumberOfLeavesInProof,
68
+ /// @notice The provided range is invalid.
69
+ InvalidRange,
70
+ /// @notice The provided range is out of bounds.
71
+ OutOfBoundsRange
72
+ }
73
+
74
+ ///////////////
75
+ // Functions //
76
+ ///////////////
77
+
78
+ /// @notice Verifies that the shares, which were posted to Celestia, were committed to by the Blobstream smart contract.
79
+ /// @param _bridge The Blobstream smart contract instance.
80
+ /// @param _sharesProof The proof of the shares to the data root tuple root.
81
+ /// @return `true` if the proof is valid, `false` otherwise.
82
+ /// @return an error code if the proof is invalid, ErrorCodes.NoError otherwise.
83
+ function verifySharesToDataRootTupleRoot(IDAOracle _bridge, SharesProof memory _sharesProof)
84
+ internal
85
+ view
86
+ returns (bool, ErrorCodes)
87
+ {
88
+ // checking that the data root was committed to by the Blobstream smart contract.
89
+ (bool success, ErrorCodes errorCode) = verifyMultiRowRootsToDataRootTupleRoot(
90
+ _bridge, _sharesProof.rowRoots, _sharesProof.rowProofs, _sharesProof.attestationProof
91
+ );
92
+ if (!success) {
93
+ return (false, errorCode);
94
+ }
95
+
96
+ (bool valid, ErrorCodes error) = verifySharesToDataRootTupleRootProof(
97
+ _sharesProof.data,
98
+ _sharesProof.shareProofs,
99
+ _sharesProof.namespace,
100
+ _sharesProof.rowRoots,
101
+ _sharesProof.rowProofs,
102
+ _sharesProof.attestationProof.tuple.dataRoot
103
+ );
104
+
105
+ return (valid, error);
106
+ }
107
+
108
+ /// @notice Verifies the shares to data root tuple root proof.
109
+ /// NOTE: This doesn't authenticate the proof to Blobstream. It only verifies if the proof is valid.
110
+ /// @param _data The data that needs to proven.
111
+ /// @param _shareProofs The share to the row roots proof.
112
+ /// @param _namespace The namespace of the shares.
113
+ /// @param _rowRoots The row roots where the shares belong.
114
+ /// @param _rowProofs The proofs of the rowRoots to the data root.
115
+ /// @param _root The data root of the block that contains the shares.
116
+ /// @return `true` if the proof is valid, `false` otherwise.
117
+ /// @return an error code if the proof is invalid, ErrorCodes.NoError otherwise.
118
+ function verifySharesToDataRootTupleRootProof(
119
+ bytes[] memory _data,
120
+ NamespaceMerkleMultiproof[] memory _shareProofs,
121
+ Namespace memory _namespace,
122
+ NamespaceNode[] memory _rowRoots,
123
+ BinaryMerkleProof[] memory _rowProofs,
124
+ bytes32 _root
125
+ ) internal pure returns (bool, ErrorCodes) {
126
+ // verifying the row root to data root tuple root proof.
127
+ (bool success, ErrorCodes errorCode) = verifyMultiRowRootsToDataRootTupleRootProof(_rowRoots, _rowProofs, _root);
128
+ if (!success) {
129
+ return (false, errorCode);
130
+ }
131
+
132
+ // checking that the shares were committed to by the rows roots.
133
+ if (_shareProofs.length != _rowRoots.length) {
134
+ return (false, ErrorCodes.UnequalShareProofsAndRowRootsNumber);
135
+ }
136
+
137
+ uint256 numberOfSharesInProofs = 0;
138
+ for (uint256 i = 0; i < _shareProofs.length; i++) {
139
+ numberOfSharesInProofs += _shareProofs[i].endKey - _shareProofs[i].beginKey;
140
+ }
141
+
142
+ if (_data.length != numberOfSharesInProofs) {
143
+ return (false, ErrorCodes.UnequalDataLengthAndNumberOfSharesProofs);
144
+ }
145
+
146
+ uint256 cursor = 0;
147
+ for (uint256 i = 0; i < _shareProofs.length; i++) {
148
+ uint256 sharesUsed = _shareProofs[i].endKey - _shareProofs[i].beginKey;
149
+ (bytes[] memory s, ErrorCodes err) = slice(_data, cursor, cursor + sharesUsed);
150
+ if (err != ErrorCodes.NoError) {
151
+ return (false, err);
152
+ }
153
+ if (!NamespaceMerkleTree.verifyMulti(_rowRoots[i], _shareProofs[i], _namespace, s)) {
154
+ return (false, ErrorCodes.InvalidSharesToRowsProof);
155
+ }
156
+ cursor += sharesUsed;
157
+ }
158
+
159
+ return (true, ErrorCodes.NoError);
160
+ }
161
+
162
+ /// @notice Verifies that a row/column root, from a Celestia block, was committed to by the Blobstream smart contract.
163
+ /// @param _bridge The Blobstream smart contract instance.
164
+ /// @param _rowRoot The row/column root to be proven.
165
+ /// @param _rowProof The proof of the row/column root to the data root.
166
+ /// @return `true` if the proof is valid, `false` otherwise.
167
+ /// @return an error code if the proof is invalid, ErrorCodes.NoError otherwise.
168
+ function verifyRowRootToDataRootTupleRoot(
169
+ IDAOracle _bridge,
170
+ NamespaceNode memory _rowRoot,
171
+ BinaryMerkleProof memory _rowProof,
172
+ AttestationProof memory _attestationProof
173
+ ) internal view returns (bool, ErrorCodes) {
174
+ // checking that the data root was committed to by the Blobstream smart contract
175
+ if (
176
+ !_bridge.verifyAttestation(
177
+ _attestationProof.tupleRootNonce, _attestationProof.tuple, _attestationProof.proof
178
+ )
179
+ ) {
180
+ return (false, ErrorCodes.InvalidDataRootTupleToDataRootTupleRootProof);
181
+ }
182
+
183
+ (bool valid, ErrorCodes error) =
184
+ verifyRowRootToDataRootTupleRootProof(_rowRoot, _rowProof, _attestationProof.tuple.dataRoot);
185
+
186
+ return (valid, error);
187
+ }
188
+
189
+ /// @notice Verifies that a row/column root proof, from a Celestia block, to the data root tuple root.
190
+ /// NOTE: This doesn't authenticate the proof to Blobstream. It only verifies if the proof is valid.
191
+ /// @param _rowRoot The row/column root to be proven.
192
+ /// @param _rowProof The proof of the row/column root to the data root.
193
+ /// @param _root The data root of the block that contains the row.
194
+ /// @return `true` if the proof is valid, `false` otherwise.
195
+ /// @return an error code if the proof is invalid, ErrorCodes.NoError otherwise.
196
+ function verifyRowRootToDataRootTupleRootProof(
197
+ NamespaceNode memory _rowRoot,
198
+ BinaryMerkleProof memory _rowProof,
199
+ bytes32 _root
200
+ ) internal pure returns (bool, ErrorCodes) {
201
+ bytes memory rowRoot = abi.encodePacked(_rowRoot.min.toBytes(), _rowRoot.max.toBytes(), _rowRoot.digest);
202
+ (bool valid,) = BinaryMerkleTree.verify(_root, _rowProof, rowRoot);
203
+ if (!valid) {
204
+ return (false, ErrorCodes.InvalidRowToDataRootProof);
205
+ }
206
+
207
+ return (true, ErrorCodes.NoError);
208
+ }
209
+
210
+ /// @notice Verifies that a set of rows/columns, from a Celestia block, were committed to by the Blobstream smart contract.
211
+ /// @param _bridge The Blobstream smart contract instance.
212
+ /// @param _rowRoots The set of row/column roots to be proved.
213
+ /// @param _rowProofs The set of proofs of the _rowRoots in the same order.
214
+ /// @return `true` if the proof is valid, `false` otherwise.
215
+ /// @return an error code if the proof is invalid, ErrorCodes.NoError otherwise.
216
+ function verifyMultiRowRootsToDataRootTupleRoot(
217
+ IDAOracle _bridge,
218
+ NamespaceNode[] memory _rowRoots,
219
+ BinaryMerkleProof[] memory _rowProofs,
220
+ AttestationProof memory _attestationProof
221
+ ) internal view returns (bool, ErrorCodes) {
222
+ // checking that the data root was committed to by the Blobstream smart contract
223
+ if (
224
+ !_bridge.verifyAttestation(
225
+ _attestationProof.tupleRootNonce, _attestationProof.tuple, _attestationProof.proof
226
+ )
227
+ ) {
228
+ return (false, ErrorCodes.InvalidDataRootTupleToDataRootTupleRootProof);
229
+ }
230
+
231
+ // checking that the rows roots commit to the data root.
232
+ (bool valid, ErrorCodes error) =
233
+ verifyMultiRowRootsToDataRootTupleRootProof(_rowRoots, _rowProofs, _attestationProof.tuple.dataRoot);
234
+
235
+ return (valid, error);
236
+ }
237
+
238
+ /// @notice Verifies the proof a set of rows/columns, from a Celestia block, to their corresponding data root.
239
+ /// NOTE: This doesn't authenticate the proof to Blobstream. It only verifies if the proof is valid.
240
+ /// @param _rowRoots The set of row/column roots to be proved.
241
+ /// @param _rowProofs The set of proofs of the _rowRoots in the same order.
242
+ /// @param _root The data root of the block that contains the rows.
243
+ /// @return `true` if the proof is valid, `false` otherwise.
244
+ /// @return an error code if the proof is invalid, ErrorCodes.NoError otherwise.
245
+ function verifyMultiRowRootsToDataRootTupleRootProof(
246
+ NamespaceNode[] memory _rowRoots,
247
+ BinaryMerkleProof[] memory _rowProofs,
248
+ bytes32 _root
249
+ ) internal pure returns (bool, ErrorCodes) {
250
+ // checking that the rows roots commit to the data root.
251
+ if (_rowProofs.length != _rowRoots.length) {
252
+ return (false, ErrorCodes.UnequalRowProofsAndRowRootsNumber);
253
+ }
254
+
255
+ for (uint256 i = 0; i < _rowProofs.length; i++) {
256
+ bytes memory rowRoot =
257
+ abi.encodePacked(_rowRoots[i].min.toBytes(), _rowRoots[i].max.toBytes(), _rowRoots[i].digest);
258
+ (bool valid,) = BinaryMerkleTree.verify(_root, _rowProofs[i], rowRoot);
259
+ if (!valid) {
260
+ return (false, ErrorCodes.InvalidRowsToDataRootProof);
261
+ }
262
+ }
263
+
264
+ return (true, ErrorCodes.NoError);
265
+ }
266
+
267
+ /// @notice computes the Celestia block square size from a row/column root to data root binary merkle proof.
268
+ /// The square size is the number of rows of the original square.
269
+ /// Note: the provided proof is not authenticated to the Blobstream smart contract. It is the user's responsibility
270
+ /// to verify that the proof is valid and was successfully committed to using
271
+ // the `DAVerifier.verifyRowRootToDataRootTupleRoot()` method
272
+ /// Note: the minimum square size is 1. Thus, we don't expect the proof to have number of leaves equal to 0.
273
+ /// @param _proof The proof of the row/column root to the data root.
274
+ /// @return The square size of the corresponding block.
275
+ /// @return an error code if the _proof is invalid, Errors.NoError otherwise.
276
+ function computeSquareSizeFromRowProof(BinaryMerkleProof memory _proof)
277
+ internal
278
+ pure
279
+ returns (uint256, ErrorCodes)
280
+ {
281
+ if (_proof.numLeaves % 4 != 0) {
282
+ return (0, ErrorCodes.InvalidNumberOfLeavesInProof);
283
+ }
284
+ // we divide the number of leaves of the proof by 4 because the rows/columns tree is constructed
285
+ // from the extended block row roots and column roots.
286
+ return (_proof.numLeaves / 4, ErrorCodes.NoError);
287
+ }
288
+
289
+ /// @notice computes the Celestia block square size from a shares to row/column root proof.
290
+ /// The square size is the number of rows of the original square.
291
+ /// Note: the provided proof is not authenticated to the Blobstream smart contract. It is the user's responsibility
292
+ /// to verify that the proof is valid and that the shares were successfully committed to using
293
+ /// the `DAVerifier.verifySharesToDataRootTupleRoot()` method.
294
+ /// Note: the minimum square size is 1. Thus, we don't expect the proof not to contain any side node.
295
+ /// @param _proof The proof of the shares to the row/column root.
296
+ /// @return The square size of the corresponding block.
297
+ function computeSquareSizeFromShareProof(NamespaceMerkleMultiproof memory _proof) internal pure returns (uint256) {
298
+ uint256 extendedSquareRowSize = 2 ** _proof.sideNodes.length;
299
+ // we divide the extended square row size by 2 because the square size is the
300
+ // the size of the row of the original square size.
301
+ return extendedSquareRowSize / 2;
302
+ }
303
+
304
+ /// @notice creates a slice of bytes from the data slice of bytes containing the elements
305
+ /// that correspond to the provided range.
306
+ /// It selects a half-open range which includes the begin element, but excludes the end one.
307
+ /// @param _data The slice that we want to select data from.
308
+ /// @param _begin The beginning of the range (inclusive).
309
+ /// @param _end The ending of the range (exclusive).
310
+ /// @return _ the sliced data.
311
+ function slice(bytes[] memory _data, uint256 _begin, uint256 _end)
312
+ internal
313
+ pure
314
+ returns (bytes[] memory, ErrorCodes)
315
+ {
316
+ if (_begin > _end) {
317
+ return (_data, ErrorCodes.InvalidRange);
318
+ }
319
+ if (_begin > _data.length || _end > _data.length) {
320
+ return (_data, ErrorCodes.OutOfBoundsRange);
321
+ }
322
+ bytes[] memory out = new bytes[](_end - _begin);
323
+ for (uint256 i = _begin; i < _end; i++) {
324
+ out[i - _begin] = _data[i];
325
+ }
326
+ return (out, ErrorCodes.NoError);
327
+ }
328
+ }