Skip to content
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

WEB3-68: feat: Add EIP4788 commitments #180

Merged
merged 32 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
07f0c8c
create slot commitment
Wollac Jul 19, 2024
f13ce98
minor improvements
Wollac Jul 21, 2024
261178f
minor renaming
Wollac Jul 22, 2024
47b4829
improve messages
Wollac Aug 1, 2024
c6ebea6
serde-json only used in tests
Wollac Aug 7, 2024
03c20e9
improve error messages
Wollac Aug 7, 2024
2c0b126
lock ethereum-consensus to specific commit
Wollac Aug 7, 2024
abeafd9
add dedicated example for Beacon root commitments
Wollac Aug 7, 2024
8b09132
use consistent endpoint naming
Wollac Aug 7, 2024
03bfbab
Add missing license header
Wollac Aug 7, 2024
271940b
rename example to eip4788-counter
Wollac Aug 8, 2024
4937a72
add building eip4788-counter to the CI
Wollac Aug 8, 2024
fd08b6e
Merge branch 'main' into feat/beacon-input
Wollac Aug 8, 2024
a43d22e
improve error messages
Wollac Aug 8, 2024
6c971f9
feat: Add version to Steel commitments (#189)
Wollac Aug 9, 2024
d4833b3
make input enum
Wollac Aug 9, 2024
482cc54
test blockID
Wollac Aug 9, 2024
3cefcd7
Merge branch 'main' into feat/beacon-input
Wollac Aug 9, 2024
397965b
fix package name
Wollac Aug 9, 2024
2614552
fix fmt
Wollac Aug 9, 2024
80ed989
move solidity code
Wollac Aug 13, 2024
74e32a8
add forge tests
Wollac Aug 14, 2024
82b0962
remove dedicated example
Wollac Aug 14, 2024
a596d9f
fix local deploy
Wollac Aug 14, 2024
ec59462
Merge branch 'main' into feat/beacon-input
Wollac Aug 14, 2024
6890cf8
Merge branch 'main' into feat/beacon-input
Wollac Aug 16, 2024
c0416be
improve Beacon API description
Wollac Aug 18, 2024
f30176e
fix README
Wollac Aug 18, 2024
16ac31f
pin toolchain
capossele Aug 19, 2024
9ae21fd
add forge test for erc20-counter
capossele Aug 19, 2024
5b957a7
update changelog
Wollac Aug 22, 2024
9fe9316
rename SolCommitment to Commitment and some other small changes
nategraf Aug 22, 2024
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 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
Wollac marked this conversation as resolved.
Show resolved Hide resolved
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 = "10.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
26 changes: 26 additions & 0 deletions examples/beacon-root/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Compiler files
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/*/11155111/
/broadcast/**/dry-run/

# Ignores anvil logs
anvil_logs.txt

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

# Dotenv file
.env

# Cargo
target/

# Misc
.DS_Store
.idea
41 changes: 41 additions & 0 deletions examples/beacon-root/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[workspace]
resolver = "2"
members = ["host", "methods"]
exclude = ["lib"]

[workspace.package]
version = "0.1.0"
edition = "2021"

[workspace.dependencies]
# Intra-workspace dependencies
beacon-root-methods = { path = "./methods" }
risc0-build-ethereum = { path = "../../build" }
risc0-ethereum-contracts = { path = "../../contracts" }
risc0-steel = { path = "../../steel" }

# risc0 monorepo dependencies.
risc0-build = { git = "https://github.com/risc0/risc0", branch = "main", features = [
"docker",
] }
risc0-zkvm = { git = "https://github.com/risc0/risc0", branch = "main", default-features = false }
risc0-zkp = { git = "https://github.com/risc0/risc0", branch = "main", default-features = false }

alloy = { version = "0.2.1", features = ["full"] }
alloy-primitives = { version = "0.7", features = ["rlp", "serde", "std"] }
alloy-sol-types = { version = "0.7" }
anyhow = { version = "1.0.75" }
bincode = { version = "1.3" }
bytemuck = { version = "1.14" }
clap = { version = "4.5" }
dotenvy = { version = "0.15" }
hex = { version = "0.4" }
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"] }
url = { version = "2.5" }

[profile.release]
debug = 1
lto = true
38 changes: 38 additions & 0 deletions examples/beacon-root/contracts/script/DeployCounter.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.20;

import {Script} from "forge-std/Script.sol";
import {console2} from "forge-std/console2.sol";
import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol";
import {Counter} from "../src/Counter.sol";

/// @notice Deployment script for the Counter contract.
contract DeployCounter is Script {
function run() external {
uint256 deployerKey = vm.envUint("ETH_WALLET_PRIVATE_KEY");
address verifier = vm.envAddress("RISC_ZERO_VERIFIER_ADDRESS");
address token = vm.envAddress("TOKEN_CONTRACT");

vm.startBroadcast(deployerKey);

Counter counter = new Counter(IRiscZeroVerifier(verifier), token);
console2.log("Deployed Counter to", address(counter));

vm.stopBroadcast();
}
}
54 changes: 54 additions & 0 deletions examples/beacon-root/contracts/src/Beacon.sol
Wollac marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.20;

import {Steel} from "risc0/steel/Steel.sol";

/// @title Steel Beacon Chain 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) {
nategraf marked this conversation as resolved.
Show resolved Hide resolved
uint256 blockTimestamp = block.timestamp;
while (true) {
timestamp += 12;
if (timestamp > blockTimestamp) revert NoParentBeaconBlock();

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

/// @notice Validates if the provided Commitment matches the Beacon block root of the given timestamp.
/// @param commitment The Commitment struct to validate.
/// @return isValid True if the commitment's block hash matches the Beacon block root, false otherwise.
function validateCommitment(Steel.Commitment memory commitment) internal view returns (bool isValid) {
// for Beacon Chain commitments the blockNumber corresponds to the timestamp and blockHash to the root
bytes32 blockHash = Beacon.blockRoot(commitment.blockNumber);
return commitment.blockHash == blockHash;
}
}
68 changes: 68 additions & 0 deletions examples/beacon-root/contracts/src/Counter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.20;

import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol";
import {Steel} from "risc0/steel/Steel.sol";
import {Beacon} from "./Beacon.sol";
import {ICounter} from "./ICounter.sol";
import {ImageID} from "./ImageID.sol"; // auto-generated contract after running `cargo build`.

/// @title Counter
/// @notice Implements a counter that increments based on off-chain Steel proofs submitted to this contract.
/// @dev The contract interacts with ERC-20 tokens, using Steel proofs to verify that an account holds at least 1 token
/// before incrementing the counter. This contract leverages RISC0-zkVM for generating and verifying these proofs.
contract Counter is ICounter {
/// @notice Image ID of the only zkVM binary to accept verification from.
bytes32 public constant imageId = ImageID.BALANCE_OF_ID;

/// @notice RISC Zero verifier contract address.
IRiscZeroVerifier public immutable verifier;

/// @notice Address of the ERC-20 token contract.
address public immutable tokenContract;

/// @notice Counter to track the number of successful verifications.
uint256 public counter;

/// @notice Journal that is committed to by the guest.
struct Journal {
Steel.Commitment commitment;
address tokenContract;
}

/// @notice Initialize the contract, binding it to a specified RISC Zero verifier and ERC-20 token address.
constructor(IRiscZeroVerifier _verifier, address _tokenContract) {
verifier = _verifier;
tokenContract = _tokenContract;
counter = 0;
}

/// @inheritdoc ICounter
function increment(bytes calldata journalData, bytes calldata seal) external {
// Decode and validate the journal data
Journal memory journal = abi.decode(journalData, (Journal));
require(journal.tokenContract == tokenContract, "Invalid token address");
require(Beacon.validateCommitment(journal.commitment), "Invalid commitment");

// Verify the proof
bytes32 journalHash = sha256(journalData);
verifier.verify(seal, imageId, journalHash);

counter += 1;
}
}
28 changes: 28 additions & 0 deletions examples/beacon-root/contracts/src/ICounter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.20;

interface ICounter {
/// @notice Increments the counter, if the Steel proof verifies that the specified account holds at least 1 token.
/// @dev The Steel proof must be generated off-chain using RISC0-zkVM and submitted here.
function increment(bytes calldata journalData, bytes calldata seal) external;

/// @notice Returns the value of the counter.
function counter() external view returns (uint256);

function imageId() external view returns (bytes32);
}
71 changes: 71 additions & 0 deletions examples/beacon-root/contracts/test/Counter.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.20;

import "forge-std/Test.sol";
import "forge-std/console.sol";
import {Receipt as RiscZeroReceipt} from "risc0/IRiscZeroVerifier.sol";
import {RiscZeroMockVerifier} from "risc0/test/RiscZeroMockVerifier.sol";
import {Counter} from "../src/Counter.sol";
import {Beacon} from "../src/Beacon.sol";
import {Steel} from "risc0/steel/Steel.sol";
import {ERC20} from "openzeppelin-contracts/token/ERC20/ERC20.sol";

contract ERC20FixedSupply is ERC20 {
constructor(string memory name, string memory symbol, address owner) ERC20(name, symbol) {
_mint(owner, 1000);
}
}

contract CounterTest is Test {
bytes4 constant MOCK_SELECTOR = bytes4(0);

RiscZeroMockVerifier private verifier;
ERC20 private token;
Counter private counter;
bytes32 private imageId;

function setUp() public {
// fork from the actual chain to get realestic Beacon block roots
string memory RPC_URL = vm.envString("ETH_RPC_URL");
vm.createSelectFork(RPC_URL);

verifier = new RiscZeroMockVerifier(MOCK_SELECTOR);
token = new ERC20FixedSupply("TOYKEN", "TOY", address(0x01));
counter = new Counter(verifier, address(token));
imageId = counter.imageId();
}

function testCounter() public {
// get the root of the previous Beacon block
uint256 beaconTimestamp = block.timestamp - 12;
bytes32 beaconRoot = Beacon.blockRoot(beaconTimestamp);

// mock the Journal
Counter.Journal memory journal =
Counter.Journal({commitment: Steel.Commitment(beaconTimestamp, beaconRoot), tokenContract: address(token)});
// create a mock proof
RiscZeroReceipt memory receipt = verifier.mockProve(imageId, sha256(abi.encode(journal)));

uint256 previous_count = counter.counter();

counter.increment(abi.encode(journal), receipt.seal);

// check that the counter was incremented
assert(counter.counter() == previous_count + 1);
}
}
11 changes: 11 additions & 0 deletions examples/beacon-root/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[profile.default]
src = "contracts/src"
out = "out"
libs = ["../../lib", "../../contracts/src"]
test = "contracts/test"
script = "contracts/script"
evm_version = 'cancun'

[fmt]
line_length=120
wrap_comments=true
19 changes: 19 additions & 0 deletions examples/beacon-root/host/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "host"
version = { workspace = true }
edition = { workspace = true }

[dependencies]
alloy = { workspace = true }
alloy-primitives = { workspace = true }
anyhow = { workspace = true }
beacon-root-methods = { workspace = true }
clap = { workspace = true, features = ["derive", "env"] }
dotenvy = { workspace = true }
log = { workspace = true }
risc0-ethereum-contracts = { workspace = true }
risc0-steel = { workspace = true, features = ["host"] }
risc0-zkvm = { workspace = true, features = ["client"] }
tokio = { workspace = true }
tracing-subscriber = { workspace = true }
url = { workspace = true }
Loading
Loading