Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
146 commits
Select commit Hold shift + click to select a range
9eb5f1c
Add memory utils
ernestognw Sep 4, 2024
2d397f4
Fix tests upgradeable
ernestognw Sep 4, 2024
2a0fb7e
Add docs
ernestognw Sep 5, 2024
a7e61c3
Make use of the library
ernestognw Sep 5, 2024
1aae8bb
Update docs/modules/ROOT/pages/utilities.adoc
ernestognw Oct 9, 2024
1b2679a
Merge branch 'master' into utils/memory
Amxx Mar 6, 2025
d514606
fix tests
Amxx Mar 6, 2025
14fa04e
Update contracts/utils/Memory.sol
ernestognw May 7, 2025
d0d55fc
Update contracts/utils/Memory.sol
arr00 May 7, 2025
7b3cb66
Add RLP library
ernestognw May 10, 2025
95149f8
Add TrieProof library
ernestognw May 11, 2025
ad5d4ac
up
ernestognw May 11, 2025
18540ef
Add docs
ernestognw May 11, 2025
163f27c
Workaround stack too deep
ernestognw May 24, 2025
c484289
Add Changesets
ernestognw May 29, 2025
e0d4790
Add more changesets
ernestognw Jun 7, 2025
a6f9053
Add FV and fuzz tests
ernestognw Jun 7, 2025
e2b5e4c
Merge branch 'master' into feature/rlp
ernestognw Jun 7, 2025
203d1a2
up
ernestognw Jun 7, 2025
48eabc1
docs
ernestognw Jun 7, 2025
63ced95
up pragma
ernestognw Jun 7, 2025
f342756
Add missing Bytes test
ernestognw Jun 7, 2025
23dba37
Add unit tests
ernestognw Jun 7, 2025
0cacca2
up pragma
ernestognw Jun 7, 2025
831e8ab
Move TrieProof
ernestognw Jun 7, 2025
5da111f
Fix countLeadingZeroes
ernestognw Jun 8, 2025
ba2293e
nits
ernestognw Jun 8, 2025
9409bc6
Improve
ernestognw Jun 8, 2025
e740dac
Fix
ernestognw Jun 8, 2025
0332ffe
Add Memory.sol library
ernestognw Jun 8, 2025
608e3cd
Merge branch 'master' into utils/memory
ernestognw Jun 8, 2025
ac92bb4
up
ernestognw Jun 8, 2025
6094bb7
Merge branch 'master' into utils/memory
ernestognw Jun 8, 2025
6bb96d5
WIP: Add more Memory functions
ernestognw Jun 8, 2025
860e5a8
up
ernestognw Jun 8, 2025
ecdb768
revert
ernestognw Jun 8, 2025
95907aa
Update docs
ernestognw Jun 8, 2025
124ccee
Nit
ernestognw Jun 8, 2025
c3237df
Finish fuzz tests and FV
ernestognw Jun 9, 2025
27f0a9b
up
ernestognw Jun 9, 2025
282ce39
up
ernestognw Jun 9, 2025
bdd2cf1
Add operations to Math.sol
ernestognw Jun 9, 2025
42c79f1
Add new equal, nibbles and countLeadingZeroes functions
ernestognw Jun 9, 2025
5754ab8
Rename countLeadingZeroes to clz
ernestognw Jun 9, 2025
44f0e14
up
ernestognw Jun 9, 2025
05c73bd
Pragma changes
ernestognw Jun 9, 2025
3a6fbf6
up
ernestognw Jun 9, 2025
e67e8b4
up
ernestognw Jul 4, 2025
3385718
Rename to in Math library and update corresponding tests for consis…
ernestognw Jul 9, 2025
40d7922
Update return types of reverseBits functions to match their respectiv…
ernestognw Jul 9, 2025
89860bc
Refactor reverseBits functions in to use fixed-size byte types
ernestognw Jul 9, 2025
9b58730
Test nits
ernestognw Jul 9, 2025
77ffa8c
Simplify
ernestognw Jul 9, 2025
ce91c80
up
ernestognw Jul 9, 2025
b3e3add
Move reverse functions to Bytes.sol
ernestognw Jul 9, 2025
2f3107c
Move Bytes.t.sol
ernestognw Jul 9, 2025
4383e01
Merge branch 'master' into feat/bytes-rlp
ernestognw Jul 9, 2025
5a44b11
up
ernestognw Jul 9, 2025
d6db2d7
Document
ernestognw Jul 9, 2025
3847050
Remove extra functions
ernestognw Jul 9, 2025
4fd1947
Update docs
ernestognw Jul 9, 2025
c4e0375
up
ernestognw Jul 9, 2025
acb14cb
Merge branch 'utils/memory' into feature/rlp
ernestognw Jul 9, 2025
2208006
Merge branch 'feat/math-reverse-bits' into feature/rlp
ernestognw Jul 9, 2025
13f4d8f
Merge branch 'feat/bytes-rlp' into feature/rlp
ernestognw Jul 9, 2025
502a520
Merge branch 'master' into feature/rlp
ernestognw Jul 12, 2025
aab9274
Merge branch 'master' into feature/rlp
Amxx Jul 15, 2025
aa26e48
up
Amxx Jul 15, 2025
948f0a1
Merge branch 'master' into feature/rlp
ernestognw Jul 31, 2025
d4bfb8b
Fix compilation
ernestognw Jul 31, 2025
138de7f
Remove dangling clz
ernestognw Jul 31, 2025
5efeb37
Make nibbles function private
ernestognw Jul 31, 2025
00ff228
Remove nibbles test
ernestognw Jul 31, 2025
fd7d2b5
up
ernestognw Jul 31, 2025
940ede8
Merge remote-tracking branch 'upstream/master' into feat/trie-proof
james-toussaint Nov 14, 2025
5b93acb
Verify proof
james-toussaint Nov 18, 2025
48c7dcb
Mirror storage on both networks
james-toussaint Nov 19, 2025
eaf3527
Gracefully kill anvil
james-toussaint Nov 20, 2025
f587b88
Verify proof with hashed key
james-toussaint Nov 20, 2025
03f42b9
Traverse after extension node
james-toussaint Nov 21, 2025
67a74b0
Delete .changeset/lovely-cooks-add.md
Amxx Nov 27, 2025
85d638e
tests update
Amxx Nov 27, 2025
62f255d
remove unecessary string casting
Amxx Nov 27, 2025
4d4edb8
refactor TrieProof library
Amxx Nov 27, 2025
1286c96
add to stateless
Amxx Nov 27, 2025
9853d3d
fuzz tests for Memory.getHash and RLP.readBytesHash
Amxx Nov 27, 2025
2ef93d2
remove radix reference
Amxx Nov 27, 2025
6211aed
update
Amxx Nov 27, 2025
466ecdf
Return error if leaf value is too large
james-toussaint Nov 28, 2025
0b6d207
fix issue identified though optimism-bedrock unit tests
Amxx Nov 28, 2025
d47d52e
update
Amxx Nov 28, 2025
fc69547
Test invalid internal short node
james-toussaint Nov 28, 2025
702bf9c
update
Amxx Nov 28, 2025
7020ab4
Merge branch 'feat/trie-proof' of https://github.com/ernestognw/openz…
Amxx Nov 28, 2025
a37e305
fix issue with small nodes and add unit test from optimism
Amxx Nov 28, 2025
4366235
more unit tests
Amxx Nov 28, 2025
2a9a3ec
Make unit tests validProof 8, 9 and 10 pass
Amxx Nov 28, 2025
60f5d7d
more optimism unit testing
Amxx Nov 28, 2025
c3cacbd
testing
Amxx Nov 28, 2025
1ed7f87
typo
Amxx Nov 28, 2025
b3c7f16
return bytes memory
Amxx Nov 29, 2025
658c9ca
check node length
Amxx Nov 29, 2025
8f60bec
refactor tests
Amxx Nov 29, 2025
278ce24
expand testing to proving accounts against the block state root
Amxx Dec 1, 2025
ea53d08
typo
Amxx Dec 1, 2025
8fbcb34
Apply suggestions from code review
Amxx Dec 1, 2025
ff31d24
revert unused new features in RLP
Amxx Dec 1, 2025
0706595
Merge branch 'feat/trie-proof' of https://github.com/ernestognw/openz…
Amxx Dec 1, 2025
0a3e229
Merge branch 'master' into feat/trie-proof
Amxx Dec 1, 2025
6e38f3e
Remove _commonPrefixLength function
Amxx Dec 1, 2025
b5bc785
Update test/utils/cryptography/TrieProof.test.js
Amxx Dec 1, 2025
1725d92
use some assembly to optimize memory operations
Amxx Dec 1, 2025
05d05c1
fix test flackiness
Amxx Dec 2, 2025
926d704
move toNibbles to Bytes.sol and update doc
Amxx Dec 2, 2025
47a7d2f
cache pathRemainder.length()
Amxx Dec 2, 2025
3e6b05f
Add toNibbles tests
james-toussaint Dec 2, 2025
77192fe
Update Bytes.t.sol
Amxx Dec 2, 2025
18e1c8e
reorder arguments
Amxx Dec 5, 2025
70d76d9
update
Amxx Dec 5, 2025
13619d5
Update contracts/utils/cryptography/TrieProof.sol
Amxx Dec 5, 2025
8d80329
Handle empty leaf/ext path and empty ext path remainder
james-toussaint Dec 9, 2025
66ddaa1
Rename and reorder errors
james-toussaint Dec 9, 2025
217fdf1
Return extra proof error without reading value
james-toussaint Dec 9, 2025
7a7bb97
Merge branch 'master' into feat/trie-proof
james-toussaint Dec 9, 2025
536dc09
Update TrieProof.sol
Amxx Dec 9, 2025
9603aee
lint
Amxx Dec 9, 2025
39e56bb
add travese / tryTraverse options
Amxx Dec 9, 2025
8f7ed6a
remove superflous check that can be proven as a loop invariant
Amxx Dec 9, 2025
4004388
remove the Node structure
Amxx Dec 9, 2025
ad5476b
Update contracts/utils/cryptography/TrieProof.sol
Amxx Dec 9, 2025
19336bf
Remove skipped tests related to removed scenario
james-toussaint Dec 9, 2025
14fe8e4
Update doc with traverse naming
james-toussaint Dec 9, 2025
8707438
fix test flakyness
Amxx Dec 10, 2025
486a562
sanity check the proof value and key
Amxx Dec 10, 2025
bdff2ad
spelling
Amxx Dec 10, 2025
80149e4
Merge branch 'master' into feat/trie-proof
ernestognw Dec 10, 2025
0600dd4
Update contracts/utils/cryptography/TrieProof.sol
Amxx Dec 11, 2025
8a04d05
Add comment on path expansion
ernestognw Dec 12, 2025
b683357
Add note about 32 length in _getNodeId
ernestognw Dec 12, 2025
86ed474
Remove `Memory.getHash` in favor of `Memory.equal`
ernestognw Dec 12, 2025
6a6a4b2
Test verification of transaction inclusion
Amxx Dec 12, 2025
30cefc6
up
Amxx Dec 12, 2025
784ff8a
Update contracts/utils/cryptography/TrieProof.sol
Amxx Dec 12, 2025
d19f5f9
Update contracts/utils/cryptography/TrieProof.sol
ernestognw Dec 12, 2025
2ca2a3d
Lint
ernestognw Dec 12, 2025
bb02a8e
verify event inclusion
Amxx Dec 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/shaky-phones-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`TrieProof`: Add library for verifying Ethereum Merkle-Patricia trie inclusion proofs.
5 changes: 5 additions & 0 deletions .changeset/tender-pans-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`Bytes`: Add the `toNibbles` function that expands the nibbles (4 bits chunk) of a `bytes` buffer. Used for manipulating Patricia Merkle Trees keys and paths.
1 change: 1 addition & 0 deletions contracts/mocks/Stateless.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,6 @@ import {SignedMath} from "../utils/math/SignedMath.sol";
import {StorageSlot} from "../utils/StorageSlot.sol";
import {Strings} from "../utils/Strings.sol";
import {Time} from "../utils/types/Time.sol";
import {TrieProof} from "../utils/cryptography/TrieProof.sol";

contract Dummy1234 {}
42 changes: 42 additions & 0 deletions contracts/utils/Bytes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,48 @@ library Bytes {
return result;
}

/**
* @dev Split each byte in `input` into two nibbles (4 bits each)
*
* Example: hex"01234567" → hex"0001020304050607"
*/
function toNibbles(bytes memory input) internal pure returns (bytes memory output) {
assembly ("memory-safe") {
let length := mload(input)
output := mload(0x40)
mstore(0x40, add(add(output, 0x20), mul(length, 2)))
mstore(output, mul(length, 2))
for {
let i := 0
} lt(i, length) {
i := add(i, 0x10)
} {
let chunk := shr(128, mload(add(add(input, 0x20), i)))
chunk := and(
0x0000000000000000ffffffffffffffff0000000000000000ffffffffffffffff,
or(shl(64, chunk), chunk)
)
chunk := and(
0x00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff,
or(shl(32, chunk), chunk)
)
chunk := and(
0x0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff,
or(shl(16, chunk), chunk)
)
chunk := and(
0x00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff,
or(shl(8, chunk), chunk)
)
chunk := and(
0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f,
or(shl(4, chunk), chunk)
)
mstore(add(add(output, 0x20), mul(i, 2)), chunk)
}
}
}

/**
* @dev Returns true if the two byte buffers are equal.
*/
Expand Down
11 changes: 11 additions & 0 deletions contracts/utils/Memory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,17 @@ library Memory {
}
}

/// @dev Returns true if the two slices contain the same data.
function equal(Slice a, Slice b) internal pure returns (bool result) {
Memory.Pointer ptrA = _pointer(a);
Memory.Pointer ptrB = _pointer(b);
uint256 lenA = length(a);
uint256 lenB = length(b);
assembly ("memory-safe") {
result := eq(keccak256(ptrA, lenA), keccak256(ptrB, lenB))
}
}

/**
* @dev Private helper: create a slice from raw values (length and pointer)
*
Expand Down
3 changes: 3 additions & 0 deletions contracts/utils/cryptography/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ A collection of contracts and libraries that implement various signature validat
* {SignatureChecker}: A library helper to support regular ECDSA from EOAs as well as ERC-1271 signatures for smart contracts.
* {Hashes}: Commonly used hash functions.
* {MerkleProof}: Functions for verifying https://en.wikipedia.org/wiki/Merkle_tree[Merkle Tree] proofs.
* {TrieProof}: Library for verifying Ethereum Merkle-Patricia trie inclusion proofs.
* {EIP712}: Contract with functions to allow processing signed typed structure data according to https://eips.ethereum.org/EIPS/eip-712[EIP-712].
* {ERC7739Utils}: Utilities library that implements a defensive rehashing mechanism to prevent replayability of smart contract signatures based on ERC-7739.
* {WebAuthn}: Library for verifying WebAuthn Authentication Assertions.
Expand Down Expand Up @@ -38,6 +39,8 @@ A collection of contracts and libraries that implement various signature validat

{{MerkleProof}}

{{TrieProof}}

{{EIP712}}

{{ERC7739Utils}}
Expand Down
231 changes: 231 additions & 0 deletions contracts/utils/cryptography/TrieProof.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {Math} from "../math/Math.sol";
import {Bytes} from "../Bytes.sol";
import {Memory} from "../Memory.sol";
import {RLP} from "../RLP.sol";

/**
* @dev Library for verifying Ethereum Merkle-Patricia trie inclusion proofs.
*
* The {traverse} and {verify} functions can be used to prove the following value:
*
* * Transaction against the transactionsRoot of a block.
* * Event against receiptsRoot of a block.
* * Account details (RLP encoding of [nonce, balance, storageRoot, codeHash]) against the stateRoot of a block.
* * Storage slot (RLP encoding of the value) against the storageRoot of a account.
*
* Proving a storage slot is usually done in 3 steps:
*
* * From the stateRoot of a block, process the account proof (see `eth_getProof`) to get the account details.
* * RLP decode the account details to extract the storageRoot.
* * Use storageRoot of that account to process the storageProof (again, see `eth_getProof`).
*
* See https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie[Merkle-Patricia trie]
*
* Based on https://github.com/ethereum-optimism/optimism/blob/ef970556e668b271a152124023a8d6bb5159bacf/packages/contracts-bedrock/src/libraries/trie/MerkleTrie.sol[this implementation from optimism].
*/
library TrieProof {
using Bytes for *;
using RLP for *;
using Memory for *;

enum Prefix {
EXTENSION_EVEN, // 0 - Extension node with even length path
EXTENSION_ODD, // 1 - Extension node with odd length path
LEAF_EVEN, // 2 - Leaf node with even length path
LEAF_ODD // 3 - Leaf node with odd length path
}

enum ProofError {
NO_ERROR, // No error occurred during proof traversal
EMPTY_KEY, // The provided key is empty
INVALID_ROOT, // The validation of the root node failed
INVALID_LARGE_NODE, // The validation of a large node failed
INVALID_SHORT_NODE, // The validation of a short node failed
EMPTY_PATH, // The path in a leaf or extension node is empty
INVALID_PATH_REMAINDER, // The path remainder in a leaf or extension node is invalid
EMPTY_EXTENSION_PATH_REMAINDER, // The path remainder in an extension node is empty
INVALID_EXTRA_PROOF_ELEMENT, // A leaf value should be the last proof element
EMPTY_VALUE, // The leaf value is empty
MISMATCH_LEAF_PATH_KEY_REMAINDER, // The path remainder in a leaf node doesn't match the key remainder
UNKNOWN_NODE_PREFIX, // The node prefix is unknown
UNPARSEABLE_NODE, // The node cannot be parsed from RLP encoding
INVALID_PROOF // General failure during proof traversal
}

error TrieProofTraversalError(ProofError err);

/// @dev The radix of the Ethereum trie
uint256 internal constant EVM_TREE_RADIX = 16;

/// @dev Number of items in a branch node (16 children + 1 value)
uint256 internal constant BRANCH_NODE_LENGTH = EVM_TREE_RADIX + 1;

/// @dev Number of items in leaf or extension nodes (always 2)
uint256 internal constant LEAF_OR_EXTENSION_NODE_LENGTH = 2;

/// @dev Verifies a `proof` against a given `key`, `value`, `and root` hash.
function verify(
bytes memory value,
bytes32 root,
bytes memory key,
bytes[] memory proof
) internal pure returns (bool) {
(bytes memory processedValue, ProofError err) = tryTraverse(root, key, proof);
return processedValue.equal(value) && err == ProofError.NO_ERROR;
}

/**
* @dev Traverses a proof with a given key and returns the value.
*
* Reverts with {TrieProofTraversalError} if proof is invalid.
*/
function traverse(bytes32 root, bytes memory key, bytes[] memory proof) internal pure returns (bytes memory) {
(bytes memory value, ProofError err) = tryTraverse(root, key, proof);
require(err == ProofError.NO_ERROR, TrieProofTraversalError(err));
return value;
}

/**
* @dev Traverses a proof with a given key and returns the value and an error flag
* instead of reverting if the proof is invalid. This function may still revert if
* malformed input leads to RLP decoding errors.
*/
function tryTraverse(
bytes32 root,
bytes memory key,
bytes[] memory proof
) internal pure returns (bytes memory value, ProofError err) {
if (key.length == 0) return (_emptyBytesMemory(), ProofError.EMPTY_KEY);

// Expand the key
bytes memory keyExpanded = key.toNibbles();

bytes32 currentNodeId;
uint256 currentNodeIdLength;

// Free memory pointer cache
Memory.Pointer fmp = Memory.getFreeMemoryPointer();

// Traverse proof
uint256 keyIndex = 0;
uint256 proofLength = proof.length;
for (uint256 i = 0; i < proofLength; ++i) {
// validates the encoded node matches the expected node id
bytes memory encoded = proof[i];
if (keyIndex == 0) {
// Root node must match root hash
if (keccak256(encoded) != root) return (_emptyBytesMemory(), ProofError.INVALID_ROOT);
} else if (encoded.length >= 32) {
// Large nodes are stored as hashes
if (currentNodeIdLength != 32 || keccak256(encoded) != currentNodeId)
return (_emptyBytesMemory(), ProofError.INVALID_LARGE_NODE);
} else {
// Small nodes must match directly
if (currentNodeIdLength != encoded.length || bytes32(encoded) != currentNodeId)
return (_emptyBytesMemory(), ProofError.INVALID_SHORT_NODE);
}

// decode the current node as an RLP list, and process it
Memory.Slice[] memory decoded = encoded.decodeList();
if (decoded.length == BRANCH_NODE_LENGTH) {
// If we've consumed the entire key, the value must be in the last slot
// Otherwise, continue down the branch specified by the next nibble in the key
if (keyIndex == keyExpanded.length) {
return _validateLastItem(decoded[EVM_TREE_RADIX], proofLength, i);
} else {
bytes1 branchKey = keyExpanded[keyIndex];
(currentNodeId, currentNodeIdLength) = _getNodeId(decoded[uint8(branchKey)]);
keyIndex += 1;
}
} else if (decoded.length == LEAF_OR_EXTENSION_NODE_LENGTH) {
bytes memory path = decoded[0].readBytes().toNibbles(); // expanded path
// The following is equivalent to path.length < 2 because toNibbles can't return odd-length buffers
if (path.length == 0) {
return (_emptyBytesMemory(), ProofError.EMPTY_PATH);
}
uint8 prefix = uint8(path[0]);
Memory.Slice keyRemainder = keyExpanded.asSlice().slice(keyIndex); // Remaining key to match
Memory.Slice pathRemainder = path.asSlice().slice(2 - (prefix % 2)); // Path after the prefix
uint256 pathRemainderLength = pathRemainder.length();

// pathRemainder must not be longer than keyRemainder, and it must be a prefix of it
if (
pathRemainderLength > keyRemainder.length() ||
!pathRemainder.equal(keyRemainder.slice(0, pathRemainderLength))
) {
return (_emptyBytesMemory(), ProofError.INVALID_PATH_REMAINDER);
}

if (prefix <= uint8(Prefix.EXTENSION_ODD)) {
// Eq to: prefix == EXTENSION_EVEN || prefix == EXTENSION_ODD
if (pathRemainderLength == 0) {
return (_emptyBytesMemory(), ProofError.EMPTY_EXTENSION_PATH_REMAINDER);
}
// Increment keyIndex by the number of nibbles consumed and continue traversal
(currentNodeId, currentNodeIdLength) = _getNodeId(decoded[1]);
keyIndex += pathRemainderLength;
} else if (prefix <= uint8(Prefix.LEAF_ODD)) {
// Eq to: prefix == LEAF_EVEN || prefix == LEAF_ODD
//
// Leaf node (terminal) - return its value if key matches completely
// we already know that pathRemainder is a prefix of keyRemainder, so checking the length sufficient
return
pathRemainderLength == keyRemainder.length()
? _validateLastItem(decoded[1], proofLength, i)
: (_emptyBytesMemory(), ProofError.MISMATCH_LEAF_PATH_KEY_REMAINDER);
} else {
return (_emptyBytesMemory(), ProofError.UNKNOWN_NODE_PREFIX);
}
} else {
return (_emptyBytesMemory(), ProofError.UNPARSEABLE_NODE);
}

// Reset memory before next iteration. Deallocates `decoded` and `path`.
Memory.setFreeMemoryPointer(fmp);
}

// If we've gone through all proof elements without finding a value, the proof is invalid
return (_emptyBytesMemory(), ProofError.INVALID_PROOF);
}

/**
* @dev Validates that we've reached a valid leaf value and this is the last proof element.
* Ensures the value is not empty and no extra proof elements exist.
*/
function _validateLastItem(
Memory.Slice item,
uint256 trieProofLength,
uint256 i
) private pure returns (bytes memory, ProofError) {
if (i != trieProofLength - 1) {
return (_emptyBytesMemory(), ProofError.INVALID_EXTRA_PROOF_ELEMENT);
}
bytes memory value = item.readBytes();
return (value, value.length == 0 ? ProofError.EMPTY_VALUE : ProofError.NO_ERROR);
}

/**
* @dev Extracts the node ID (hash or raw data based on size)
*
* For small nodes (encoded length < 32 bytes) the node ID is the node content itself,
* For larger nodes, the node ID is the hash of the encoded node data.
*
* NOTE: Under normal operation, the input should never be exactly 32-byte inputs. If such an input is provided,
* it will be used directly, similarly to how small nodes are processed. The following traversal check whether
* the next node is a large one, and whether its hash matches the raw 32 bytes we have here. If that is the case,
* the value will be accepted. Otherwise, the next step will return an {INVALID_LARGE_NODE} error.
*/
function _getNodeId(Memory.Slice node) private pure returns (bytes32 nodeId, uint256 nodeIdLength) {
uint256 nodeLength = node.length();
return nodeLength < 33 ? (node.load(0), nodeLength) : (node.readBytes32(), 32);
}

function _emptyBytesMemory() private pure returns (bytes memory result) {
assembly ("memory-safe") {
result := 0x60 // mload(0x60) is always 0
}
}
}
Loading