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.
- package/.codecov.yml +51 -0
- package/.github/CODEOWNERS +7 -0
- package/.github/dependabot.yml +18 -0
- package/.github/workflows/code-analysis.yml +41 -0
- package/.github/workflows/contract-inheritance-check.yml +28 -0
- package/.github/workflows/go-check.yml +25 -0
- package/.github/workflows/labels.yml +19 -0
- package/.github/workflows/lint.yml +37 -0
- package/.github/workflows/tests.yml +72 -0
- package/.gitmodules +12 -0
- package/.golangci.yml +64 -0
- package/.markdownlint.yaml +5 -0
- package/.markdownlint.yml +4 -0
- package/.markdownlintignore +1 -0
- package/.prettierrc.json +11 -0
- package/LICENSE +201 -0
- package/Makefile +18 -0
- package/README.md +102 -5
- package/docs/inclusion-proofs.md +69 -0
- package/foundry.toml +4 -0
- package/go.mod +34 -0
- package/go.sum +212 -0
- package/hardhat.config.ts +46 -0
- package/index.js +94 -0
- package/package.json +31 -3
- package/remappings.txt +6 -0
- package/scripts/Dockerfile_Environment +39 -0
- package/scripts/deploy.ts +12 -0
- package/scripts/gen.sh +34 -0
- package/scripts/upgradability_check.sh +22 -0
- package/slither.config.json +3 -0
- package/src/Blobstream.sol +366 -0
- package/src/Constants.sol +10 -0
- package/src/DataRootTuple.sol +15 -0
- package/src/IDAOracle.sol +18 -0
- package/src/lib/tree/Constants.sol +23 -0
- package/src/lib/tree/Types.sol +37 -0
- package/src/lib/tree/Utils.sol +106 -0
- package/src/lib/tree/binary/BinaryMerkleMultiproof.sol +12 -0
- package/src/lib/tree/binary/BinaryMerkleProof.sol +12 -0
- package/src/lib/tree/binary/BinaryMerkleTree.sol +256 -0
- package/src/lib/tree/binary/TreeHasher.sol +23 -0
- package/src/lib/tree/binary/test/BinaryMerkleTree.t.sol +365 -0
- package/src/lib/tree/binary/test/TreeHasher.t.sol +40 -0
- package/src/lib/tree/namespace/NamespaceMerkleMultiproof.sol +14 -0
- package/src/lib/tree/namespace/NamespaceMerkleProof.sol +14 -0
- package/src/lib/tree/namespace/NamespaceMerkleTree.sol +306 -0
- package/src/lib/tree/namespace/NamespaceNode.sol +23 -0
- package/src/lib/tree/namespace/TreeHasher.sol +69 -0
- package/src/lib/tree/namespace/test/NamespaceMerkleMultiproof.t.sol +108 -0
- package/src/lib/tree/namespace/test/NamespaceMerkleTree.t.sol +644 -0
- package/src/lib/tree/namespace/test/TreeHasher.t.sol +66 -0
- package/src/lib/tree/test/Utils.t.sol +48 -0
- package/src/lib/tree/test/blob.dat +0 -0
- package/src/lib/tree/test/header.dat +0 -0
- package/src/lib/tree/test/proofs.json +1 -0
- package/src/lib/verifier/DAVerifier.sol +328 -0
- package/src/lib/verifier/test/DAVerifier.t.sol +396 -0
- package/src/lib/verifier/test/RollupInclusionProofs.t.sol +589 -0
- package/src/test/Blobstream.t.sol +200 -0
- package/src/test/BlobstreamBenchmark.t.sol +137 -0
- package/tsconfig.json +11 -0
- package/wrappers/Blobstream.sol/wrapper.go +1325 -0
- package/wrappers/ERC1967Proxy.sol/wrapper.go +668 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.22;
|
3
|
+
|
4
|
+
import "./Constants.sol";
|
5
|
+
|
6
|
+
/// @notice Calculate the starting bit of the path to a leaf
|
7
|
+
/// @param numLeaves : The total number of leaves in the tree
|
8
|
+
/// @return startingBit : The starting bit of the path
|
9
|
+
// solhint-disable-next-line func-visibility
|
10
|
+
function getStartingBit(uint256 numLeaves) pure returns (uint256 startingBit) {
|
11
|
+
// Determine height of the left subtree. This is the maximum path length, so all paths start at this offset from the right-most bit
|
12
|
+
startingBit = 0;
|
13
|
+
while ((1 << startingBit) < numLeaves) {
|
14
|
+
startingBit += 1;
|
15
|
+
}
|
16
|
+
return Constants.MAX_HEIGHT - startingBit;
|
17
|
+
}
|
18
|
+
|
19
|
+
/// @notice Calculate the length of the path to a leaf
|
20
|
+
/// @param key: The key of the leaf
|
21
|
+
/// @param numLeaves: The total number of leaves in the tree
|
22
|
+
/// @return pathLength : The length of the path to the leaf
|
23
|
+
// solhint-disable-next-line func-visibility
|
24
|
+
function pathLengthFromKey(uint256 key, uint256 numLeaves) pure returns (uint256 pathLength) {
|
25
|
+
if (numLeaves <= 1) {
|
26
|
+
// if the number of leaves of the tree is 1 or 0, the path always is 0.
|
27
|
+
return 0;
|
28
|
+
}
|
29
|
+
// Get the height of the left subtree. This is equal to the offset of the starting bit of the path
|
30
|
+
pathLength = Constants.MAX_HEIGHT - getStartingBit(numLeaves);
|
31
|
+
|
32
|
+
// Determine the number of leaves in the left subtree
|
33
|
+
uint256 numLeavesLeftSubTree = (1 << (pathLength - 1));
|
34
|
+
|
35
|
+
// If leaf is in left subtree, path length is full height of left subtree
|
36
|
+
if (key <= numLeavesLeftSubTree - 1) {
|
37
|
+
return pathLength;
|
38
|
+
}
|
39
|
+
// If left sub tree has only one leaf but key is not there, path has one additional step
|
40
|
+
else if (numLeavesLeftSubTree == 1) {
|
41
|
+
return 1;
|
42
|
+
}
|
43
|
+
// Otherwise, add 1 to height and recurse into right subtree
|
44
|
+
else {
|
45
|
+
return 1 + pathLengthFromKey(key - numLeavesLeftSubTree, numLeaves - numLeavesLeftSubTree);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
/// @notice Returns the minimum number of bits required to represent `x`; the
|
50
|
+
/// result is 0 for `x` == 0.
|
51
|
+
/// @param x Number.
|
52
|
+
function _bitsLen(uint256 x) pure returns (uint256) {
|
53
|
+
uint256 count = 0;
|
54
|
+
|
55
|
+
while (x != 0) {
|
56
|
+
count++;
|
57
|
+
x >>= 1;
|
58
|
+
}
|
59
|
+
|
60
|
+
return count;
|
61
|
+
}
|
62
|
+
|
63
|
+
/// @notice Returns the largest power of 2 less than `x`.
|
64
|
+
/// @param x Number.
|
65
|
+
function _getSplitPoint(uint256 x) pure returns (uint256) {
|
66
|
+
// Note: since `x` is always an unsigned int * 2, the only way for this
|
67
|
+
// to be violated is if the input == 0. Since the input is the end
|
68
|
+
// index exclusive, an input of 0 is guaranteed to be invalid (it would
|
69
|
+
// be a proof of inclusion of nothing, which is vacuous).
|
70
|
+
require(x >= 1);
|
71
|
+
|
72
|
+
uint256 bitLen = _bitsLen(x);
|
73
|
+
uint256 k = 1 << (bitLen - 1);
|
74
|
+
if (k == x) {
|
75
|
+
k >>= 1;
|
76
|
+
}
|
77
|
+
return k;
|
78
|
+
}
|
79
|
+
|
80
|
+
/// @notice Returns the size of the subtree adjacent to `begin` that does
|
81
|
+
/// not overlap `end`.
|
82
|
+
/// @param begin Begin index, inclusive.
|
83
|
+
/// @param end End index, exclusive.
|
84
|
+
function _nextSubtreeSize(uint256 begin, uint256 end) pure returns (uint256) {
|
85
|
+
uint256 ideal = _bitsTrailingZeroes(begin);
|
86
|
+
uint256 max = _bitsLen(end - begin) - 1;
|
87
|
+
if (ideal > max) {
|
88
|
+
return 1 << max;
|
89
|
+
}
|
90
|
+
return 1 << ideal;
|
91
|
+
}
|
92
|
+
|
93
|
+
/// @notice Returns the number of trailing zero bits in `x`; the result is
|
94
|
+
/// 256 for `x` == 0.
|
95
|
+
/// @param x Number.
|
96
|
+
function _bitsTrailingZeroes(uint256 x) pure returns (uint256) {
|
97
|
+
uint256 mask = 1;
|
98
|
+
uint256 count = 0;
|
99
|
+
|
100
|
+
while (x != 0 && mask & x == 0) {
|
101
|
+
count++;
|
102
|
+
x >>= 1;
|
103
|
+
}
|
104
|
+
|
105
|
+
return count;
|
106
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.22;
|
3
|
+
|
4
|
+
/// @notice Merkle Tree Proof structure.
|
5
|
+
struct BinaryMerkleMultiproof {
|
6
|
+
// List of side nodes to verify and calculate tree.
|
7
|
+
bytes32[] sideNodes;
|
8
|
+
// The (included) beginning key of the leaves to verify.
|
9
|
+
uint256 beginKey;
|
10
|
+
// The (excluded) ending key of the leaves to verify.
|
11
|
+
uint256 endKey;
|
12
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.22;
|
3
|
+
|
4
|
+
/// @notice Merkle Tree Proof structure.
|
5
|
+
struct BinaryMerkleProof {
|
6
|
+
// List of side nodes to verify and calculate tree.
|
7
|
+
bytes32[] sideNodes;
|
8
|
+
// The key of the leaf to verify.
|
9
|
+
uint256 key;
|
10
|
+
// The number of leaves in the tree
|
11
|
+
uint256 numLeaves;
|
12
|
+
}
|
@@ -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
|
+
}
|