Skip to content

Commit

Permalink
WEB3-68: feat: Add EIP4788 commitments (#180)
Browse files Browse the repository at this point in the history
Adds the option to commit to a Beacon (Consensus) block instead of an
Execution block.

---------

Co-authored-by: capossele <[email protected]>
Co-authored-by: Victor Graf <[email protected]>
  • Loading branch information
3 people authored Aug 22, 2024
1 parent fb48ef7 commit 270e57e
Show file tree
Hide file tree
Showing 30 changed files with 916 additions and 350 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,19 @@ jobs:
- name: build erc20-Counter
run: cargo build
working-directory: examples/erc20-counter
- name: forge test erc20-Counter
run: forge test
working-directory: examples/erc20-counter
env:
ETH_RPC_URL: https://ethereum-sepolia-rpc.publicnode.com
- name: build token-stats
run: cargo build
working-directory: examples/token-stats
- name: test erc20-Counter
run: ./test-local-deployment.sh
working-directory: examples/erc20-counter
env:
RISC0_DEV_MODE: true
- run: sccache --show-stats

doc:
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ alloy-sol-types = { version = "0.7" }
alloy = { version = "0.2.1", features = ["full"] }
alloy-trie = { version = "0.4.0" }

# Beacon chain support
beacon-api-client = { git = "https://github.com/ralexstokes/ethereum-consensus.git", rev = "cf3c404043230559660810bc0c9d6d5a8498d819" }
ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus.git", rev = "cf3c404043230559660810bc0c9d6d5a8498d819" }

anyhow = { version = "1.0" }
bincode = { version = "1.3" }
clap = { version = "4.5", features = ["derive", "env"] }
Expand All @@ -41,6 +45,7 @@ once_cell = "1.19"
revm = { version = "13.0", default-features = false, features = ["std"] }
serde = "1.0"
serde_json = "1.0"
sha2 = { version = "0.10" }
test-log = "0.2.15"
tokio = { version = "1.35" }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
Expand Down
103 changes: 97 additions & 6 deletions contracts/src/steel/Steel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,107 @@ pragma solidity ^0.8.9;
/// @title Steel Library
/// @notice This library provides a collection of utilities to work with Steel commitments in Solidity.
library Steel {
/// @notice A Commitment struct representing a block number and its block hash.
/// @notice Represents a commitment to a specific block in the blockchain.
/// @dev The `blockID` encodes both the block identifier (block number or timestamp) and the version.
/// @dev The `blockDigest` is the block hash or beacon block root, used for validation.
struct Commitment {
uint256 blockNumber; // Block number at which the commitment was made.
bytes32 blockHash; // Hash of the block at the specified block number.
uint256 blockID;
bytes32 blockDigest;
}

/// @notice The version of the Commitment is incorrect.
error InvalidCommitmentVersion();

/// @notice The Commitment is too old and can no longer be validated.
error CommitmentTooOld();

/// @notice Validates if the provided Commitment matches the block hash of the given block number.
/// @param commitment The Commitment struct to validate.
/// @return isValid True if the commitment's block hash matches the block hash of the block number, false otherwise.
function validateCommitment(Commitment memory commitment) internal view returns (bool isValid) {
return commitment.blockHash == blockhash(commitment.blockNumber);
/// @return True if the commitment's block hash matches the block hash of the block number, false otherwise.
function validateCommitment(Commitment memory commitment) internal view returns (bool) {
(uint240 blockID, uint16 version) = Encoding.decodeVersionedID(commitment.blockID);
if (version == 0) {
return validateBlockCommitment(blockID, commitment.blockDigest);
} else if (version == 1) {
return validateBeaconCommitment(blockID, commitment.blockDigest);
} else {
revert InvalidCommitmentVersion();
}
}

/// @notice Validates if the provided block commitment matches the block hash of the given block number.
/// @param blockNumber The block number to compare against.
/// @param blockHash The block hash to validate.
/// @return True if the block's block hash matches the block hash, false otherwise.
function validateBlockCommitment(uint256 blockNumber, bytes32 blockHash) internal view returns (bool) {
if (block.number - blockNumber > 256) {
revert CommitmentTooOld();
}
return blockHash == blockhash(blockNumber);
}

/// @notice Validates if the provided beacon commitment matches the block root of the given timestamp.
/// @param blockTimestamp The timestamp to compare against.
/// @param blockRoot The block root to validate.
/// @return True if the block's block root matches the block root, false otherwise.
function validateBeaconCommitment(uint256 blockTimestamp, bytes32 blockRoot) internal view returns (bool) {
if (block.timestamp - blockTimestamp > 12 * 8191) {
revert CommitmentTooOld();
}
return blockRoot == Beacon.blockRoot(blockTimestamp);
}
}

/// @title Beacon Library
library Beacon {
/// @notice The address of the Beacon roots contract.
/// @dev https://eips.ethereum.org/EIPS/eip-4788
address internal constant BEACON_ROOTS_ADDRESS = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;

/// @notice The Beacon block root could not be found as the next block has not been issued yet.
error NoParentBeaconBlock();

/// @notice Attempts to find the root of the Beacon block with the given timestamp.
/// @dev Since the Beacon roots contract only returns the parent Beacon block’s root, we need to find the next
/// Beacon block instead. This is done by adding the block time of 12s until a value is returned.
function blockRoot(uint256 timestamp) internal view returns (bytes32 root) {
uint256 blockTimestamp = block.timestamp;
while (true) {
timestamp += 12; // Beacon block time is 12 seconds
if (timestamp > blockTimestamp) revert NoParentBeaconBlock();

(bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(abi.encode(timestamp));
if (success) {
return abi.decode(result, (bytes32));
}
}
}
}

/// @title Encoding Library
library Encoding {
/// @notice Encodes a version and ID into a single uint256 value.
/// @param id The base ID to be encoded, limited by 240 bits (or the maximum value of a uint240).
/// @param version The version number to be encoded, limited by 16 bits (or the maximum value of a uint16).
/// @return Returns a single uint256 value that contains both the `id` and the `version` encoded into it.
function encodeVersionedID(uint240 id, uint16 version) internal pure returns (uint256) {
uint256 encoded;
assembly {
encoded := or(shl(240, version), id)
}
return encoded;
}

/// @notice Decodes a version and ID from a single uint256 value.
/// @param id The single uint256 value to be decoded.
/// @return Returns two values: a uint240 for the original base ID and a uint16 for the version number encoded into it.
function decodeVersionedID(uint256 id) internal pure returns (uint240, uint16) {
uint240 decoded;
uint16 version;
assembly {
decoded := and(id, 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
version := shr(240, id)
}
return (decoded, version);
}
}
1 change: 0 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ Explore a more advanced interaction between [Steel] and a custom Ethereum smart
This example shows how the [Steel] library can be used to call multiple view functions of a contract.
This example generates a proof of a [Compound] cToken's APR (Annual Percentage Rate), showcasing the potential for on-chain verification of complex financial metrics.

[Counter]: ./erc20-counter/contracts/Counter.sol
[coprocessor]: https://www.risczero.com/news/a-guide-to-zk-coprocessors-for-scalability
[Steel]: ../steel
[Compound]: https://compound.finance/
1 change: 1 addition & 0 deletions examples/erc20-counter/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ETH_RPC_URL="https://ethereum-rpc.publicnode.com"
7 changes: 2 additions & 5 deletions examples/erc20-counter/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@ out/
anvil_logs.txt

# Autogenerated contracts
contracts/ImageID.sol
contracts/Elf.sol

# Dotenv file
.env
contracts/src/ImageID.sol
contracts/src/Elf.sol

# Cargo
target/
Expand Down
1 change: 1 addition & 0 deletions examples/erc20-counter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ bytemuck = { version = "1.14" }
clap = { version = "4.5" }
hex = { version = "0.4" }
erc20-counter-methods = { path = "./methods" }
log = { version = "0.4" }
serde = { version = "1.0", features = ["derive", "std"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tokio = { version = "1.39", features = ["full"] }
Expand Down
4 changes: 2 additions & 2 deletions examples/erc20-counter/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# RISC Zero View Call Proofs ERC20-Counter Example
# ERC20-Counter Example

This example implements a counter that increments based on off-chain RISC Zero [Steel] proofs submitted to the [Counter] contract.
The contract interacts with ERC-20 tokens, using [Steel] proofs to verify that an account holds at least 1 token before incrementing the counter.
Expand Down Expand Up @@ -35,6 +35,7 @@ The contract includes functionality to query the current value of the counter at
## Dependencies

To get started, you need to have the following installed:

- [Rust]
- [Foundry]
- [RISC Zero]
Expand All @@ -59,7 +60,6 @@ When you're ready, follow the [deployment guide] to get your application running
[Groth16 SNARK proof]: https://www.risczero.com/news/on-chain-verification
[RISC Zero]: https://dev.risczero.com/api/zkvm/install
[Sepolia]: https://www.alchemy.com/overviews/sepolia-testnet
[cargo-binstall]: https://github.com/cargo-bins/cargo-binstall#cargo-binaryinstall
[deployment guide]: ./deployment-guide.md
[Rust]: https://doc.rust-lang.org/cargo/getting-started/installation.html
[Counter]: ./contracts/Counter.sol
Expand Down
1 change: 1 addition & 0 deletions examples/erc20-counter/apps/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ alloy-primitives = { workspace = true }
anyhow = { workspace = true }
clap = { workspace = true, features = ["derive", "env"] }
erc20-counter-methods = { workspace = true }
log = { workspace = true }
risc0-ethereum-contracts = { workspace = true }
risc0-steel = { workspace = true, features = ["host"] }
risc0-zkvm = { workspace = true, features = ["client"] }
Expand Down
25 changes: 12 additions & 13 deletions examples/erc20-counter/apps/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,23 @@ cargo run --bin publisher
```text
$ cargo run --bin publisher -- --help
Usage: publisher --eth-wallet-private-key <ETH_WALLET_PRIVATE_KEY> --rpc-url <RPC_URL> --contract <CONTRACT> --token <TOKEN> --account <ACCOUNT>
Usage: publisher [OPTIONS] --eth-wallet-private-key <ETH_WALLET_PRIVATE_KEY> --eth-rpc-url <ETH_RPC_URL> --counter <COUNTER> --token-contract <TOKEN_CONTRACT> --account <ACCOUNT>
Options:
--eth-wallet-private-key <ETH_WALLET_PRIVATE_KEY>
Ethereum Node endpoint [env: ETH_WALLET_PRIVATE_KEY=]
--rpc-url <RPC_URL>
Ethereum Node endpoint [env: RPC_URL=]
--contract <CONTRACT>
Counter's contract address on Ethereum
--token <TOKEN>
ERC20 contract address on Ethereum
Private key [env: ETH_WALLET_PRIVATE_KEY=]
--eth-rpc-url <ETH_RPC_URL>
Ethereum RPC endpoint URL [env: ETH_RPC_URL=]
--beacon-api-url <BEACON_API_URL>
Beacon API endpoint URL [env: BEACON_API_URL=]
--counter <COUNTER>
Address of the Counter verifier
--token-contract <TOKEN_CONTRACT>
Address of the ERC20 token contract [env: TOKEN_CONTRACT=]
--account <ACCOUNT>
Account address to read the balance_of on Ethereum
Address to query the token balance of
-h, --help
Print help
-V, --version
Print version
```
Print help```
[publisher]: ./src/bin/publisher.rs
[Counter]: ../contracts/Counter.sol
Loading

0 comments on commit 270e57e

Please sign in to comment.