-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from merklefruit/nico/feat/sol-example
feat: solidity verifier usage example
- Loading branch information
Showing
13 changed files
with
1,630 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "examples/eth/lib/forge-std"] | ||
path = examples/eth/lib/forge-std | ||
url = https://github.com/foundry-rs/forge-std |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Trie Proof examples | ||
|
||
Available examples: | ||
|
||
- [Ethereum transaction inclusion proof (Solidity)](./eth/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Compiler files | ||
cache/ | ||
out/ | ||
|
||
# Ignores development broadcast logs | ||
!/broadcast | ||
/broadcast/*/31337/ | ||
/broadcast/**/dry-run/ | ||
|
||
# Docs | ||
docs/ | ||
|
||
# Dotenv file | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Ethereum Trie Proof: Solidity example | ||
|
||
This example demonstrates how to verify an Ethereum transaction inclusion proof in Solidity. | ||
The proof will be generated with the [`cli`](../../cli/) binary directly from the Foundry test. | ||
|
||
## Overview of the example | ||
|
||
The example consists of a single end2end test in [Prover.t.sol](./test/Prover.t.sol) that generates a Merkle | ||
inclusion proof and verifies it in Solidity against a given transaction hash. | ||
|
||
## Usage | ||
|
||
Run the Foundry tests: | ||
|
||
```shell | ||
# make sure you are in the right directory | ||
cd examples/eth | ||
|
||
forge test --ffi | ||
``` | ||
|
||
## Troubleshooting | ||
|
||
If the test fails with an RPC error, you can customize the RPC endpoint by setting the `RPC_URL` | ||
environment variable to the desired endpoing in your shell. For example: | ||
|
||
```shell | ||
export RPC_URL=https://mainnet.infura.io/v3/your_project_id | ||
forge test --ffi | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[profile.default] | ||
src = "src" | ||
out = "out" | ||
libs = ["lib"] | ||
|
||
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import {MerkleTrie} from "./lib/MerkleTrie.sol"; | ||
|
||
contract Prover { | ||
constructor() {} | ||
|
||
function get(bytes memory _key, bytes memory _proof, bytes32 _root) public pure returns (bool, bytes memory) { | ||
return MerkleTrie.get(_key, _proof, _root); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
/** | ||
* @title BytesUtils | ||
*/ | ||
library BytesUtils { | ||
/** | ||
* | ||
* Internal Functions * | ||
* | ||
*/ | ||
function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { | ||
unchecked { | ||
require(_length + 31 >= _length, "slice_overflow"); | ||
require(_start + _length >= _start, "slice_overflow"); | ||
require(_bytes.length >= _start + _length, "slice_outOfBounds"); | ||
|
||
bytes memory tempBytes; | ||
|
||
assembly { | ||
switch iszero(_length) | ||
case 0 { | ||
// Get a location of some free memory and store it in tempBytes as | ||
// Solidity does for memory variables. | ||
tempBytes := mload(0x40) | ||
|
||
// The first word of the slice result is potentially a partial | ||
// word read from the original array. To read it, we calculate | ||
// the length of that partial word and start copying that many | ||
// bytes into the array. The first word we copy will start with | ||
// data we don't care about, but the last `lengthmod` bytes will | ||
// land at the beginning of the contents of the new array. When | ||
// we're done copying, we overwrite the full first word with | ||
// the actual length of the slice. | ||
let lengthmod := and(_length, 31) | ||
|
||
// The multiplication in the next line is necessary | ||
// because when slicing multiples of 32 bytes (lengthmod == 0) | ||
// the following copy loop was copying the origin's length | ||
// and then ending prematurely not copying everything it should. | ||
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) | ||
let end := add(mc, _length) | ||
|
||
for { | ||
// The multiplication in the next line has the same exact purpose | ||
// as the one above. | ||
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) | ||
} lt(mc, end) { | ||
mc := add(mc, 0x20) | ||
cc := add(cc, 0x20) | ||
} { mstore(mc, mload(cc)) } | ||
|
||
mstore(tempBytes, _length) | ||
|
||
//update free-memory pointer | ||
//allocating the array padded to 32 bytes like the compiler does now | ||
mstore(0x40, and(add(mc, 31), not(31))) | ||
} | ||
//if we want a zero-length slice let's just return a zero-length array | ||
default { | ||
tempBytes := mload(0x40) | ||
|
||
//zero out the 32 bytes slice we are about to return | ||
//we need to do it because Solidity does not garbage collect | ||
mstore(tempBytes, 0) | ||
|
||
mstore(0x40, add(tempBytes, 0x20)) | ||
} | ||
} | ||
|
||
return tempBytes; | ||
} | ||
} | ||
|
||
function slice(bytes memory _bytes, uint256 _start) internal pure returns (bytes memory) { | ||
unchecked { | ||
if (_bytes.length - _start == 0) { | ||
return bytes(""); | ||
} | ||
|
||
return slice(_bytes, _start, _bytes.length - _start); | ||
} | ||
} | ||
|
||
function toBytes32PadLeft(bytes memory _bytes) internal pure returns (bytes32) { | ||
unchecked { | ||
bytes32 ret; | ||
uint256 len = _bytes.length <= 32 ? _bytes.length : 32; | ||
assembly { | ||
ret := shr(mul(sub(32, len), 8), mload(add(_bytes, 32))) | ||
} | ||
return ret; | ||
} | ||
} | ||
|
||
function toBytes32(bytes memory _bytes) internal pure returns (bytes32) { | ||
unchecked { | ||
if (_bytes.length < 32) { | ||
bytes32 ret; | ||
assembly { | ||
ret := mload(add(_bytes, 32)) | ||
} | ||
return ret; | ||
} | ||
|
||
return abi.decode(_bytes, (bytes32)); // will truncate if input length > 32 bytes | ||
} | ||
} | ||
|
||
function toUint256(bytes memory _bytes) internal pure returns (uint256) { | ||
return uint256(toBytes32(_bytes)); | ||
} | ||
|
||
function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) { | ||
require(_start + 3 >= _start, "toUint24_overflow"); | ||
require(_bytes.length >= _start + 3, "toUint24_outOfBounds"); | ||
uint24 tempUint; | ||
|
||
assembly { | ||
tempUint := mload(add(add(_bytes, 0x3), _start)) | ||
} | ||
|
||
return tempUint; | ||
} | ||
|
||
function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { | ||
require(_start + 1 >= _start, "toUint8_overflow"); | ||
require(_bytes.length >= _start + 1, "toUint8_outOfBounds"); | ||
uint8 tempUint; | ||
|
||
assembly { | ||
tempUint := mload(add(add(_bytes, 0x1), _start)) | ||
} | ||
|
||
return tempUint; | ||
} | ||
|
||
function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { | ||
require(_start + 20 >= _start, "toAddress_overflow"); | ||
require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); | ||
address tempAddress; | ||
|
||
assembly { | ||
tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) | ||
} | ||
|
||
return tempAddress; | ||
} | ||
|
||
function toNibbles(bytes memory _bytes) internal pure returns (bytes memory) { | ||
unchecked { | ||
bytes memory nibbles = new bytes(_bytes.length * 2); | ||
|
||
for (uint256 i = 0; i < _bytes.length; i++) { | ||
nibbles[i * 2] = _bytes[i] >> 4; | ||
nibbles[i * 2 + 1] = bytes1(uint8(_bytes[i]) % 16); | ||
} | ||
|
||
return nibbles; | ||
} | ||
} | ||
|
||
function fromNibbles(bytes memory _bytes) internal pure returns (bytes memory) { | ||
unchecked { | ||
bytes memory ret = new bytes(_bytes.length / 2); | ||
|
||
for (uint256 i = 0; i < ret.length; i++) { | ||
ret[i] = (_bytes[i * 2] << 4) | (_bytes[i * 2 + 1]); | ||
} | ||
|
||
return ret; | ||
} | ||
} | ||
|
||
function equal(bytes memory _bytes, bytes memory _other) internal pure returns (bool) { | ||
return keccak256(_bytes) == keccak256(_other); | ||
} | ||
} |
Oops, something went wrong.