Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

Commit

Permalink
Update MiMC and SparseMerkleProof (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
thedarkjester authored Jul 17, 2024
1 parent 4b607be commit 0a7ebaf
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 23 deletions.
45 changes: 31 additions & 14 deletions contracts/lib/Mimc.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,46 @@
// limitations under the License.

// Code generated by gnark DO NOT EDIT
pragma solidity 0.8.24;
pragma solidity 0.8.25;

/**
* @title Library to perform MiMC hashing
* @author ConsenSys Software Inc.
* @custom:security-contact security-report@linea.build
*/
library Mimc {
uint256 constant FR_FIELD = 8444461749428370424248824938781546531375899335154063827935233455917409239041;
/**
* Thrown when the data is not provided
*/
error DataMissing();

/**
* Thrown when the data is not purely in 32 byte chunks
*/
error DataIsNotMod32();

uint256 constant FR_FIELD = 8444461749428370424248824938781546531375899335154063827935233455917409239041;
/**
* @notice Performs a MiMC hash on the data provided
* @param _msg The data to be hashed
* @dev Only data that has length modulus 32 is hashed, reverts otherwise
* @return mimcHash The computed MiMC hash
*/
function hash(bytes calldata _msg) external pure returns (bytes32 mimcHash) {
if (_msg.length == 0) {
revert DataMissing();
}

if (_msg.length % 0x20 != 0) {
revert DataIsNotMod32();
}

assembly {
let chunks := div(add(_msg.length, 0x1f), 0x20)

for {
let i := 0
} lt(i, sub(chunks, 1)) {
} lt(i, chunks) {
i := add(i, 1)
} {
let offset := add(_msg.offset, mul(i, 0x20))
Expand All @@ -36,17 +64,6 @@ library Mimc {
mimcHash := addmod(addmod(mimcHash, r, FR_FIELD), chunk, FR_FIELD)
}

let offset := add(_msg.offset, mul(sub(chunks, 1), 0x20))
let lastChunk := calldataload(offset)

if iszero(eq(mod(_msg.length, 0x20), 0)) {
let remaining := mod(_msg.length, 0x20)
lastChunk := shr(mul(sub(0x20, remaining), 0x8), lastChunk)
}

let r := encrypt(mimcHash, lastChunk)
mimcHash := addmod(addmod(mimcHash, r, FR_FIELD), lastChunk, FR_FIELD)

function encrypt(h, chunk) -> output {
let frField := FR_FIELD
let tmpSum := 0
Expand Down
62 changes: 57 additions & 5 deletions contracts/lib/SparseMerkleProof.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.24;
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,25 +24,54 @@ 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, bytes32[] memory proof) = _formatProof(_rawProof);
return _verify(proof, leafHash, _leafIndex, _root, nextFreeNode);
}
Expand Down Expand Up @@ -103,7 +141,7 @@ library SparseMerkleProof {
* @return {Leaf} Formatted leaf struct
*/
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 @@ -115,7 +153,7 @@ library SparseMerkleProof {
* @return {Account} Formatted account struct
*/
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 @@ -144,6 +182,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 All @@ -170,6 +213,10 @@ library SparseMerkleProof {
* @return isZeroBytes true if bytes contain only zero byte elements, false otherwise
*/
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 @@ -190,12 +237,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 @@ -207,6 +255,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(abi.encodePacked(_proof[height], computedHash));
else computedHash = Mimc.hash(abi.encodePacked(computedHash, _proof[height]));
Expand Down
8 changes: 4 additions & 4 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const config: HardhatUserConfig = {
solidity: {
compilers: [
{
version: "0.8.24",
version: "0.8.25",
settings: {
optimizer: {
enabled: true,
Expand All @@ -32,22 +32,22 @@ const config: HardhatUserConfig = {
},
},
{
version: "0.8.19",
version: "0.8.24",
settings: {
optimizer: {
enabled: true,
runs: 100000,
},
evmVersion: "cancun",
},
},
{
version: "0.8.15",
version: "0.8.19",
settings: {
optimizer: {
enabled: true,
runs: 100000,
},
evmVersion: "london",
},
},
],
Expand Down
Loading

0 comments on commit 0a7ebaf

Please sign in to comment.