Skip to content

Fixes 4.7,4.8,4.9,4.10,4.13,4.22 #179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@ pragma solidity 0.8.25;

import {Mimc} from "./Mimc.sol";

/**
* @title Library to perform SparseMerkleProof actions using the MiMC hashing algorithm
* @author ConsenSys Software Inc.
* @custom:security-contact security-report@linea.build
*/
library SparseMerkleProof {
using Mimc for *;

/**
* The Account struct represents the state of the account including the storage root, nonce, balance and codesize
* @dev This is mapped directly to the output of the storage proof
*/
struct Account {
uint64 nonce;
uint256 balance;
Expand All @@ -15,29 +24,58 @@ library SparseMerkleProof {
uint64 codeSize;
}

/**
* Represents the leaf structure in both account and storage tries
* @dev This is mapped directly to the output of the storage proof
*/
struct Leaf {
uint256 prev;
uint256 next;
bytes32 hKey;
bytes32 hValue;
}

/**
* Thrown when expected bytes length is incorrect
*/
error WrongBytesLength(uint256 expectedLength, uint256 bytesLength);

/**
* Thrown when the length of bytes is not in exactly 32 byte chunks
*/
error LengthNotMod32();

/**
* Thrown when the leaf index is higher than the tree depth
*/
error MaxTreeLeafIndexExceed();

/**
* Thrown when the length of the unformatted proof is not provided exactly as expected (UNFORMATTED_PROOF_LENGTH)
*/
error WrongProofLength(uint256 expectedLength, uint256 actualLength);

uint256 internal constant TREE_DEPTH = 40;
uint256 internal constant UNFORMATTED_PROOF_LENGTH = 42;
bytes32 internal constant ZERO_HASH = 0x0;
uint256 internal constant MAX_TREE_LEAF_INDEX = 2 ** TREE_DEPTH - 1;

/**
* @notice Format input and verify sparse merkle proof
* @notice Formats input, computes root and returns true if it matches the provided merkle root
* @param _rawProof Raw sparse merkle tree proof
* @param _leafIndex Index of the leaf
* @param _root Sparse merkle root
* @return If the computed merkle root matches the provided one
*/
function verifyProof(
bytes[] calldata _rawProof,
uint256 _leafIndex,
bytes32 _root
) external pure returns (bool) {
if (_rawProof.length != UNFORMATTED_PROOF_LENGTH) {
revert WrongProofLength(UNFORMATTED_PROOF_LENGTH, _rawProof.length);
}

(
bytes32 nextFreeNode,
bytes32 leafHash,
Expand Down Expand Up @@ -119,7 +157,7 @@ library SparseMerkleProof {
function _parseLeaf(
bytes calldata _encodedLeaf
) private pure returns (Leaf memory) {
if (_encodedLeaf.length < 128) {
if (_encodedLeaf.length != 128) {
revert WrongBytesLength(128, _encodedLeaf.length);
}
return abi.decode(_encodedLeaf, (Leaf));
Expand All @@ -133,7 +171,7 @@ library SparseMerkleProof {
function _parseAccount(
bytes calldata _value
) private pure returns (Account memory) {
if (_value.length < 192) {
if (_value.length != 192) {
revert WrongBytesLength(192, _value.length);
}
return abi.decode(_value, (Account));
Expand Down Expand Up @@ -166,6 +204,11 @@ library SparseMerkleProof {
uint256 formattedProofLength = rawProofLength - 2;

bytes32[] memory proof = new bytes32[](formattedProofLength);

if (_rawProof[0].length != 0x40) {
revert WrongBytesLength(0x40, _rawProof[0].length);
}

bytes32 nextFreeNode = bytes32(_rawProof[0][:32]);
bytes32 leafHash = Mimc.hash(_rawProof[rawProofLength - 1]);

Expand Down Expand Up @@ -194,6 +237,10 @@ library SparseMerkleProof {
function _isZeroBytes(
bytes calldata _data
) private pure returns (bool isZeroBytes) {
if (_data.length % 0x20 != 0) {
revert LengthNotMod32();
}

isZeroBytes = true;
assembly {
let dataStart := _data.offset
Expand All @@ -214,12 +261,13 @@ library SparseMerkleProof {
}

/**
* @notice Verify sparse merkle proof
* @notice Computes merkle root from proof and compares it to the provided root
* @param _proof Sparse merkle tree proof
* @param _leafHash Leaf hash
* @param _leafIndex Index of the leaf
* @param _root Sparse merkle root
* @param _nextFreeNode Next free node
* @return If the computed merkle root matches the provided one
*/
function _verify(
bytes32[] memory _proof,
Expand All @@ -231,6 +279,10 @@ library SparseMerkleProof {
bytes32 computedHash = _leafHash;
uint256 currentIndex = _leafIndex;

if (_leafIndex > MAX_TREE_LEAF_INDEX) {
revert MaxTreeLeafIndexExceed();
}

for (uint256 height; height < TREE_DEPTH; ++height) {
if ((currentIndex >> height) & 1 == 1)
computedHash = Mimc.hash(
Expand Down
60 changes: 56 additions & 4 deletions packages/linea-verifier/contracts/lib/SparseMerkleProof.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@ pragma solidity 0.8.25;

import {Mimc} from "./Mimc.sol";

/**
* @title Library to perform SparseMerkleProof actions using the MiMC hashing algorithm
* @author ConsenSys Software Inc.
* @custom:security-contact security-report@linea.build
*/
library SparseMerkleProof {
using Mimc for *;

/**
* The Account struct represents the state of the account including the storage root, nonce, balance and codesize
* @dev This is mapped directly to the output of the storage proof
*/
struct Account {
uint64 nonce;
uint256 balance;
Expand All @@ -15,29 +24,58 @@ library SparseMerkleProof {
uint64 codeSize;
}

/**
* Represents the leaf structure in both account and storage tries
* @dev This is mapped directly to the output of the storage proof
*/
struct Leaf {
uint256 prev;
uint256 next;
bytes32 hKey;
bytes32 hValue;
}

/**
* Thrown when expected bytes length is incorrect
*/
error WrongBytesLength(uint256 expectedLength, uint256 bytesLength);

/**
* Thrown when the length of bytes is not in exactly 32 byte chunks
*/
error LengthNotMod32();

/**
* Thrown when the leaf index is higher than the tree depth
*/
error MaxTreeLeafIndexExceed();

/**
* Thrown when the length of the unformatted proof is not provided exactly as expected (UNFORMATTED_PROOF_LENGTH)
*/
error WrongProofLength(uint256 expectedLength, uint256 actualLength);

uint256 internal constant TREE_DEPTH = 40;
uint256 internal constant UNFORMATTED_PROOF_LENGTH = 42;
bytes32 internal constant ZERO_HASH = 0x0;
uint256 internal constant MAX_TREE_LEAF_INDEX = 2 ** TREE_DEPTH - 1;

/**
* @notice Format input and verify sparse merkle proof
* @notice Formats input, computes root and returns true if it matches the provided merkle root
* @param _rawProof Raw sparse merkle tree proof
* @param _leafIndex Index of the leaf
* @param _root Sparse merkle root
* @return If the computed merkle root matches the provided one
*/
function verifyProof(
bytes[] calldata _rawProof,
uint256 _leafIndex,
bytes32 _root
) external pure returns (bool) {
if (_rawProof.length != UNFORMATTED_PROOF_LENGTH) {
revert WrongProofLength(UNFORMATTED_PROOF_LENGTH, _rawProof.length);
}

(
bytes32 nextFreeNode,
bytes32 leafHash,
Expand Down Expand Up @@ -119,7 +157,7 @@ library SparseMerkleProof {
function _parseLeaf(
bytes calldata _encodedLeaf
) private pure returns (Leaf memory) {
if (_encodedLeaf.length < 128) {
if (_encodedLeaf.length != 128) {
revert WrongBytesLength(128, _encodedLeaf.length);
}
return abi.decode(_encodedLeaf, (Leaf));
Expand All @@ -133,7 +171,7 @@ library SparseMerkleProof {
function _parseAccount(
bytes calldata _value
) private pure returns (Account memory) {
if (_value.length < 192) {
if (_value.length != 192) {
revert WrongBytesLength(192, _value.length);
}
return abi.decode(_value, (Account));
Expand Down Expand Up @@ -166,6 +204,11 @@ library SparseMerkleProof {
uint256 formattedProofLength = rawProofLength - 2;

bytes32[] memory proof = new bytes32[](formattedProofLength);

if (_rawProof[0].length != 0x40) {
revert WrongBytesLength(0x40, _rawProof[0].length);
}

bytes32 nextFreeNode = bytes32(_rawProof[0][:32]);
bytes32 leafHash = Mimc.hash(_rawProof[rawProofLength - 1]);

Expand Down Expand Up @@ -194,6 +237,10 @@ library SparseMerkleProof {
function _isZeroBytes(
bytes calldata _data
) private pure returns (bool isZeroBytes) {
if (_data.length % 0x20 != 0) {
revert LengthNotMod32();
}

isZeroBytes = true;
assembly {
let dataStart := _data.offset
Expand All @@ -214,12 +261,13 @@ library SparseMerkleProof {
}

/**
* @notice Verify sparse merkle proof
* @notice Computes merkle root from proof and compares it to the provided root
* @param _proof Sparse merkle tree proof
* @param _leafHash Leaf hash
* @param _leafIndex Index of the leaf
* @param _root Sparse merkle root
* @param _nextFreeNode Next free node
* @return If the computed merkle root matches the provided one
*/
function _verify(
bytes32[] memory _proof,
Expand All @@ -231,6 +279,10 @@ library SparseMerkleProof {
bytes32 computedHash = _leafHash;
uint256 currentIndex = _leafIndex;

if (_leafIndex > MAX_TREE_LEAF_INDEX) {
revert MaxTreeLeafIndexExceed();
}

for (uint256 height; height < TREE_DEPTH; ++height) {
if ((currentIndex >> height) & 1 == 1)
computedHash = Mimc.hash(
Expand Down