Skip to content

Commit

Permalink
feat(l2): verify proof on chain (#1115)
Browse files Browse the repository at this point in the history
**Motivation**

After sending the commitment, we should generate the zkProof and then
verify it on chain.

**Description**

Using the Risc0Groth16Verifier in Sepolia.

Steps:
Use the `.env.example` but change the following variables:
```
RISC0_DEV_MODE=0
ETH_RPC_URL=<Any sepolia endpoint>
DEPLOYER_CONTRACT_VERIFIER=0xd9b0d07CeCd808a8172F21fA7C97992168f045CA (`Risc0Groth16Verifier`)
```
1. cd `~/lambda_ethereum_rust/crates/l2`
2. run `make deploy-l1` -- if it fails change the `SALT` in
`crates/l2/contracts/deployer.rs`
1. Copy the `OnChainProposer` address given in the .env file
(`PROPOSER_ON_CHAIN_PROPOSER_ADDRESS`)
2. Copy the `Bridge address` given in the .env file
(`L1_WATCHER_BRIDGE_ADDRESS`)
3. rm the libmdbx files `rm -rf ~/.local/share/ethereum_rust`
4. run the proposer/sequencer: `make init-l2`
5. In a new tab/window run the prover: `make init-l2-prover-gpu`

---------

Co-authored-by: Manuel Iñaki Bilbao <[email protected]>
Co-authored-by: Ivan Litteri <[email protected]>
  • Loading branch information
3 people authored Nov 14, 2024
1 parent ee714e4 commit b804aa2
Show file tree
Hide file tree
Showing 22 changed files with 1,220 additions and 586 deletions.
17 changes: 8 additions & 9 deletions cmd/ethereum_rust/ethereum_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ use tracing_subscriber::{EnvFilter, FmtSubscriber};
mod cli;
mod decode;

const DEFAULT_DATADIR: &str = "ethereum_rust";
#[tokio::main]
async fn main() {
let matches = cli::cli().get_matches();

if let Some(matches) = matches.subcommand_matches("removedb") {
let default_datadir = get_default_datadir();
let data_dir = matches
.get_one::<String>("datadir")
.unwrap_or(&default_datadir);
.map_or(set_datadir(DEFAULT_DATADIR), |datadir| set_datadir(datadir));
let path = Path::new(&data_dir);
if path.exists() {
std::fs::remove_dir_all(path).expect("Failed to remove data directory");
Expand Down Expand Up @@ -110,11 +110,11 @@ async fn main() {
let tcp_socket_addr =
parse_socket_addr(tcp_addr, tcp_port).expect("Failed to parse addr and port");

let default_datadir = get_default_datadir();
let data_dir = matches
.get_one::<String>("datadir")
.unwrap_or(&default_datadir);
let store = Store::new(data_dir, EngineType::Libmdbx).expect("Failed to create Store");
.map_or(set_datadir(DEFAULT_DATADIR), |datadir| set_datadir(datadir));

let store = Store::new(&data_dir, EngineType::Libmdbx).expect("Failed to create Store");

let genesis = read_genesis_file(genesis_file_path);
store
Expand Down Expand Up @@ -204,7 +204,7 @@ async fn main() {
// We do not want to start the networking module if the l2 feature is enabled.
cfg_if::cfg_if! {
if #[cfg(feature = "l2")] {
let l2_proposer = ethereum_rust_l2::start_proposer(store.clone()).into_future();
let l2_proposer = ethereum_rust_l2::start_proposer(store).into_future();
tracker.spawn(l2_proposer);
} else if #[cfg(feature = "dev")] {
use ethereum_rust_dev;
Expand Down Expand Up @@ -283,9 +283,8 @@ fn parse_socket_addr(addr: &str, port: &str) -> io::Result<SocketAddr> {
))
}

fn get_default_datadir() -> String {
let project_dir =
ProjectDirs::from("", "", "ethereum_rust").expect("Couldn't find home directory");
fn set_datadir(datadir: &str) -> String {
let project_dir = ProjectDirs::from("", "", datadir).expect("Couldn't find home directory");
project_dir
.data_local_dir()
.to_str()
Expand Down
21 changes: 16 additions & 5 deletions crates/l2/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
ETH_RPC_URL=http://localhost:8545
# If set to 0xAA skip proof verification.
# Only use in dev mode.
DEPLOYER_CONTRACT_VERIFIER=0x00000000000000000000000000000000000000AA
# Risc0Groth16Verifier Sepolia Address
# DEPLOYER_CONTRACT_VERIFIER=0xd9b0d07CeCd808a8172F21fA7C97992168f045CA
DEPLOYER_ADDRESS=0x3d1e15a1a55578f7c920884a9943b3b35d0d885b
DEPLOYER_PRIVATE_KEY=0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924
# If set to false, the salt will be randomized.
Expand All @@ -9,16 +14,22 @@ L1_WATCHER_CHECK_INTERVAL_MS=5000
L1_WATCHER_MAX_BLOCK_STEP=5000
L1_WATCHER_L2_PROPOSER_PRIVATE_KEY=0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924
L1_WATCHER_L2_PROPOSER_ADDRESS=0x3d1e15a1a55578f7c920884a9943b3b35d0d885b
ENGINE_API_RPC_URL=http://localhost:8551
ENGINE_API_RPC_URL=http://localhost:8552
ENGINE_API_JWT_PATH=./jwt.hex
PROVER_SERVER_LISTEN_IP=127.0.0.1
PROVER_SERVER_LISTEN_PORT=3000
# Not the same account as the COMMITTER_L1 Account
# The proposer is in charge of blob commitments.
# The prover_server is in charge of verifying the zkProofs.
PROVER_SERVER_VERIFIER_ADDRESS=0xE25583099BA105D9ec0A67f5Ae86D90e50036425
PROVER_SERVER_VERIFIER_PRIVATE_KEY=0x39725efee3fb28614de3bacaffe4cc4bd8c436257e2c8bb887c4b5c4be45e76d
PROVER_CLIENT_PROVER_SERVER_ENDPOINT=localhost:3000
PROPOSER_ON_CHAIN_PROPOSER_ADDRESS=0xe9927d77c931f8648da4cc6751ef4e5e2ce74608
PROPOSER_L1_ADDRESS=0x3d1e15a1a55578f7c920884a9943b3b35d0d885b
PROPOSER_L1_PRIVATE_KEY=0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924
COMMITTER_ON_CHAIN_PROPOSER_ADDRESS=0xe9927d77c931f8648da4cc6751ef4e5e2ce74608
COMMITTER_L1_ADDRESS=0x3d1e15a1a55578f7c920884a9943b3b35d0d885b
COMMITTER_L1_PRIVATE_KEY=0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924
COMMITTER_INTERVAL_MS=1000
PROPOSER_INTERVAL_MS=5000
# https://dev.risczero.com/api/generating-proofs/dev-mode
# 1/true means fake proofs
# The RISC0_DEV_MODE=1 should only be used with DEPLOYER_CONTRACT_VERIFIER=0xAA
RISC0_DEV_MODE=1
RUST_LOG="[executor]=info"
35 changes: 32 additions & 3 deletions crates/l2/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
.PHONY: help init down clean init-local-l1 down-local-l1 clean-local-l1 init-l2 down-l2 deploy-l1 deploy-block-executor deploy-inbox setup-prover test

L2_GENESIS_FILE_PATH=../../test_data/genesis-l2.json
L1_GENESIS_FILE_PATH=../../test_data/genesis-l1.json

help: ## 📚 Show help for each of the Makefile recipes
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
Expand All @@ -29,17 +30,36 @@ ETHEREUM_RUST_L2_CONTRACTS_PATH=./contracts
L1_RPC_URL=http://localhost:8545
L1_PRIVATE_KEY=0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924

ETHEREUM_RUST_L2_DEV_LIBMDBX=dev_ethereum_rust_l2
ETHEREUM_RUST_L1_DEV_LIBMDBX=dev_ethereum_rust_l1
L1_PORT=8545
L2_PORT=1729
L1_AUTH_PORT=8551
# Used in the .env file. Ensure the same port is used for `ENGINE_API_RPC_URL`.
L2_AUTH_PORT=8552

# Local L1

init-local-l1: ## 🚀 Initializes an L1 Lambda Ethereum Rust Client
init-local-l1: ## 🚀 Initializes an L1 Lambda Ethereum Rust Client with Docker (Used with make init)
docker compose -f ${ETHEREUM_RUST_DEV_DOCKER_COMPOSE_PATH} up -d

init-l1: ## 🚀 Initializes an L1 Lambda Ethereum Rust Client
cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust --features dev -- \
--network ${L1_GENESIS_FILE_PATH} \
--http.port ${L1_PORT} \
--http.addr 0.0.0.0 \
--authrpc.port ${L1_AUTH_PORT} \
--datadir ${ETHEREUM_RUST_L1_DEV_LIBMDBX}

down-local-l1: ## 🛑 Shuts down the L1 Lambda Ethereum Rust Client
docker compose -f ${ETHEREUM_RUST_DEV_DOCKER_COMPOSE_PATH} down
docker compose -f docker-compose-l2.yaml down

restart-local-l1: down-local-l1 init-local-l1 ## 🔄 Restarts the L1 Lambda Ethereum Rust Client

rm_dev_libmdbx_l1: ## 🛑 Removes the Libmdbx DB used by the L1
cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust -- removedb --datadir ${ETHEREUM_RUST_L1_DEV_LIBMDBX}

# Contracts

clean-contract-deps: ## 🧹 Cleans the dependencies for the L1 contracts.
Expand All @@ -54,7 +74,12 @@ deploy-l1: ## 📜 Deploys the L1 contracts
# L2

init-l2: ## 🚀 Initializes an L2 Lambda Ethereum Rust Client
cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust --features l2 -- --network ${L2_GENESIS_FILE_PATH} --http.port 1729
cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust --features l2 -- \
--network ${L2_GENESIS_FILE_PATH} \
--http.port ${L2_PORT} \
--http.addr 0.0.0.0 \
--authrpc.port ${L2_AUTH_PORT} \
--datadir ${ETHEREUM_RUST_L2_DEV_LIBMDBX}

down-l2: ## 🛑 Shuts down the L2 Lambda Ethereum Rust Client
pkill -f ethereum_rust || exit 0
Expand All @@ -65,7 +90,11 @@ init-l2-prover: ## 🚀 Initializes the Prover
cargo run --release --features build_zkvm --manifest-path ../../Cargo.toml --bin ethereum_rust_prover

init-l2-prover-gpu: ## 🚀 Initializes the Prover with GPU support
cargo run --release --features "build_zkvm,cuda" --manifest-path ../../Cargo.toml --bin ethereum_rust_prover
cargo run --release --features "build_zkvm,gpu" --manifest-path ../../Cargo.toml --bin ethereum_rust_prover

rm_dev_libmdbx_l2: ## 🛑 Removes the Libmdbx DB used by the L2
cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust -- removedb --datadir ${ETHEREUM_RUST_L2_DEV_LIBMDBX}


# CI Testing

Expand Down
44 changes: 36 additions & 8 deletions crates/l2/contracts/deployer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ lazy_static::lazy_static! {

#[tokio::main]
async fn main() {
let (deployer, deployer_private_key, eth_client, contracts_path) = setup();
let (deployer, deployer_private_key, contract_verifier_address, eth_client, contracts_path) =
setup();
download_contract_deps(&contracts_path);
compile_contracts(&contracts_path);
let (on_chain_proposer, bridge_address) =
Expand All @@ -38,6 +39,7 @@ async fn main() {
deployer_private_key,
on_chain_proposer,
bridge_address,
contract_verifier_address,
&eth_client,
)
.await;
Expand All @@ -50,7 +52,7 @@ async fn main() {
if let Some(eq) = line.find('=') {
let (envar, _) = line.split_at(eq);
line = match envar {
"PROPOSER_ON_CHAIN_PROPOSER_ADDRESS" => {
"COMMITTER_ON_CHAIN_PROPOSER_ADDRESS" => {
format!("{envar}={on_chain_proposer:#x}")
}
"L1_WATCHER_BRIDGE_ADDRESS" => {
Expand All @@ -64,7 +66,7 @@ async fn main() {
write_env(wr_lines).expect("Failed to write changes to the .env file.");
}

fn setup() -> (Address, SecretKey, EthClient, PathBuf) {
fn setup() -> (Address, SecretKey, Address, EthClient, PathBuf) {
if let Err(e) = read_env_file() {
warn!("Failed to read .env file: {e}");
}
Expand Down Expand Up @@ -99,12 +101,20 @@ fn setup() -> (Address, SecretKey, EthClient, PathBuf) {
"false" | "0" => {
let mut salt = SALT.lock().unwrap();
*salt = H256::random();
println!("SALT: {salt:?}");
}
_ => panic!("Invalid boolean string: {input}"),
};

(deployer, deployer_private_key, eth_client, contracts_path)
let contract_verifier_address = std::env::var("DEPLOYER_CONTRACT_VERIFIER")
.expect("DEPLOYER_CONTRACT_VERIFIER not set")
.parse()
.expect("Malformed DEPLOYER_CONTRACT_VERIFIER");
(
deployer,
deployer_private_key,
contract_verifier_address,
eth_client,
contracts_path,
)
}

fn download_contract_deps(contracts_path: &Path) {
Expand Down Expand Up @@ -177,9 +187,15 @@ async fn deploy_contracts(
eth_client: &EthClient,
contracts_path: &Path,
) -> (Address, Address) {
let gas_price = if eth_client.url.contains("localhost:8545") {
Some(1_000_000_000)
} else {
Some(eth_client.get_gas_price().await.unwrap().as_u64() * 2)
};

let overrides = Overrides {
gas_limit: Some(GAS_LIMIT_MINIMUM * GAS_LIMIT_ADJUSTMENT_FACTOR),
gas_price: Some(1_000_000_000),
gas_price,
..Default::default()
};

Expand Down Expand Up @@ -307,6 +323,7 @@ async fn create2_deploy(
)
.await
.expect("Failed to build create2 deploy tx");

let deploy_tx_hash = eth_client
.send_eip1559_transaction(deploy_tx, &deployer_private_key)
.await
Expand Down Expand Up @@ -341,6 +358,7 @@ async fn initialize_contracts(
deployer_private_key: SecretKey,
on_chain_proposer: Address,
bridge: Address,
contract_verifier_address: Address,
eth_client: &EthClient,
) {
let initialize_frames = spinner!(["🪄❱❱", "❱🪄❱", "❱❱🪄"], 200);
Expand All @@ -354,6 +372,7 @@ async fn initialize_contracts(
let initialize_tx_hash = initialize_on_chain_proposer(
on_chain_proposer,
bridge,
contract_verifier_address,
deployer,
deployer_private_key,
eth_client,
Expand Down Expand Up @@ -388,11 +407,12 @@ async fn initialize_contracts(
async fn initialize_on_chain_proposer(
on_chain_proposer: Address,
bridge: Address,
contract_verifier_address: Address,
deployer: Address,
deployer_private_key: SecretKey,
eth_client: &EthClient,
) -> H256 {
let on_chain_proposer_initialize_selector = keccak(b"initialize(address)")
let on_chain_proposer_initialize_selector = keccak(b"initialize(address,address)")
.as_bytes()
.get(..4)
.expect("Failed to get initialize selector")
Expand All @@ -404,10 +424,18 @@ async fn initialize_on_chain_proposer(
encoded_bridge
};

let encoded_contract_verifier = {
let offset = 32 - contract_verifier_address.as_bytes().len() % 32;
let mut encoded_contract_verifier = vec![0; offset];
encoded_contract_verifier.extend_from_slice(contract_verifier_address.as_bytes());
encoded_contract_verifier
};

let mut on_chain_proposer_initialization_calldata = Vec::new();
on_chain_proposer_initialization_calldata
.extend_from_slice(&on_chain_proposer_initialize_selector);
on_chain_proposer_initialization_calldata.extend_from_slice(&encoded_bridge);
on_chain_proposer_initialization_calldata.extend_from_slice(&encoded_contract_verifier);

let initialize_tx = eth_client
.build_eip1559_transaction(
Expand Down
Loading

0 comments on commit b804aa2

Please sign in to comment.