diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index 63732b45da..3b8acef935 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -161,7 +161,10 @@ pub fn is_canonical( } } -fn validate_gas_used(receipts: &[Receipt], block_header: &BlockHeader) -> Result<(), ChainError> { +pub fn validate_gas_used( + receipts: &[Receipt], + block_header: &BlockHeader, +) -> Result<(), ChainError> { if let Some(last) = receipts.last() { if last.cumulative_gas_used != block_header.gas_used { return Err(ChainError::InvalidBlock(InvalidBlockError::GasUsedMismatch)); diff --git a/crates/l2/Cargo.toml b/crates/l2/Cargo.toml index 6b96d8a033..2957b4ab47 100644 --- a/crates/l2/Cargo.toml +++ b/crates/l2/Cargo.toml @@ -18,6 +18,7 @@ ethereum_rust-rlp.workspace = true ethereum_rust-rpc.workspace = true ethereum_rust-blockchain.workspace = true ethereum_rust-storage.workspace = true +ethereum_rust-vm.workspace = true ethereum_rust-dev = { path = "../../crates/blockchain/dev" } hex.workspace = true bytes.workspace = true diff --git a/crates/l2/proposer/prover_server.rs b/crates/l2/proposer/prover_server.rs index 08b79f37bd..1c752337bc 100644 --- a/crates/l2/proposer/prover_server.rs +++ b/crates/l2/proposer/prover_server.rs @@ -1,5 +1,6 @@ use crate::utils::eth_client::RpcResponse; use ethereum_rust_storage::Store; +use ethereum_rust_vm::execution_db::ExecutionDB; use reqwest::Client; use serde::{Deserialize, Serialize}; use std::{ @@ -14,15 +15,11 @@ use ethereum_rust_core::types::{Block, BlockHeader}; #[derive(Debug, Serialize, Deserialize, Default)] pub struct ProverInputData { - pub db: MemoryDB, - pub parent_block_header: BlockHeader, + pub db: ExecutionDB, pub block: Block, + pub parent_header: BlockHeader, } -// Placeholder structure until we have ExecutionDB on L1 -#[derive(Debug, Serialize, Deserialize, Default)] -pub struct MemoryDB; - use crate::utils::config::prover_server::ProverServerConfig; use super::errors::ProverServerError; @@ -177,22 +174,22 @@ impl ProverServer { ) -> Result<(), String> { debug!("Request received"); - //let last_block_number = Self::get_last_block_number().await?; let last_block_number = self .store .get_latest_block_number() .map_err(|e| e.to_string())? .ok_or("missing latest block number".to_string())?; + let input = self.create_prover_input(last_block_number)?; let response = if last_block_number > last_proved_block { ProofData::Response { block_number: Some(last_block_number), - input: ProverInputData::default(), + input, } } else { ProofData::Response { block_number: None, - input: ProverInputData::default(), + input, } }; let writer = BufWriter::new(stream); @@ -211,4 +208,35 @@ impl ProverServer { let writer = BufWriter::new(stream); serde_json::to_writer(writer, &response).map_err(|e| e.to_string()) } + + fn create_prover_input(&self, block_number: u64) -> Result { + let header = self + .store + .get_block_header(block_number) + .map_err(|err| err.to_string())? + .ok_or("block header not found")?; + let body = self + .store + .get_block_body(block_number) + .map_err(|err| err.to_string())? + .ok_or("block body not found")?; + + let block = Block::new(header, body); + + let db = ExecutionDB::from_exec(&block, &self.store).map_err(|err| err.to_string())?; + + let parent_header = self + .store + .get_block_header_by_hash(block.header.parent_hash) + .map_err(|err| err.to_string())? + .ok_or("missing parent header".to_string())?; + + debug!("Created prover input for block {block_number}"); + + Ok(ProverInputData { + db, + block, + parent_header, + }) + } } diff --git a/crates/l2/prover/src/prover.rs b/crates/l2/prover/src/prover.rs index e4f8a2f67b..4826755a67 100644 --- a/crates/l2/prover/src/prover.rs +++ b/crates/l2/prover/src/prover.rs @@ -1,9 +1,10 @@ +use ethereum_rust_core::types::Block; use tracing::info; // risc0 use zkvm_interface::methods::{ZKVM_PROGRAM_ELF, ZKVM_PROGRAM_ID}; -use risc0_zkvm::{default_prover, ExecutorEnv, ExecutorEnvBuilder}; +use risc0_zkvm::{default_prover, ExecutorEnv, ExecutorEnvBuilder, ProverOpts}; use ethereum_rust_rlp::encode::RLPEncode; @@ -34,12 +35,12 @@ impl<'a> Prover<'a> { pub fn set_input(&mut self, input: ProverInputData) -> &mut Self { let head_block_rlp = input.block.encode_to_vec(); - let parent_block_header_rlp = input.parent_block_header.encode_to_vec(); + let parent_header_rlp = input.parent_header.encode_to_vec(); // We should pass the inputs as a whole struct self.env_builder.write(&head_block_rlp).unwrap(); - self.env_builder.write(&parent_block_header_rlp).unwrap(); self.env_builder.write(&input.db).unwrap(); + self.env_builder.write(&parent_header_rlp).unwrap(); self } @@ -59,13 +60,18 @@ impl<'a> Prover<'a> { // Proof information by proving the specified ELF binary. // This struct contains the receipt along with statistics about execution of the guest let prove_info = prover - .prove(env, self.elf) + .prove_with_opts(env, self.elf, &ProverOpts::groth16()) .map_err(|_| "Failed to prove".to_string())?; // extract the receipt. let receipt = prove_info.receipt; - info!("Successfully generated Receipt!"); + let executed_block: Block = receipt.journal.decode().map_err(|err| err.to_string())?; + + info!( + "Successfully generated execution proof receipt for block {}", + executed_block.header.compute_block_hash() + ); Ok(receipt) } diff --git a/crates/l2/prover/zkvm/interface/guest/Cargo.toml b/crates/l2/prover/zkvm/interface/guest/Cargo.toml index a79a9a1185..ff0d146085 100644 --- a/crates/l2/prover/zkvm/interface/guest/Cargo.toml +++ b/crates/l2/prover/zkvm/interface/guest/Cargo.toml @@ -13,9 +13,8 @@ ethereum_rust-rlp = { path = "../../../../../common/rlp" } ethereum_rust-vm = { path = "../../../../../vm", default-features = false } ethereum_rust-blockchain = { path = "../../../../../blockchain", default-features = false } -# revm -revm = { version = "14.0.3", features = [ - "std", - "serde", - "kzg-rs", -], default-features = false } +[patch.crates-io] +crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" } +k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.3-risczero.0" } +sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.6-risczero.0" } +secp256k1 = { git = "https://github.com/sp1-patches/rust-secp256k1", branch = "patch-secp256k1-v0.29.1" } diff --git a/crates/l2/prover/zkvm/interface/guest/src/main.rs b/crates/l2/prover/zkvm/interface/guest/src/main.rs index c3a2a57bd4..95eefaf92c 100644 --- a/crates/l2/prover/zkvm/interface/guest/src/main.rs +++ b/crates/l2/prover/zkvm/interface/guest/src/main.rs @@ -1,111 +1,39 @@ +use ethereum_rust_rlp::{decode::RLPDecode, error::RLPDecodeError}; use risc0_zkvm::guest::env; -//use ethereum_rust_blockchain::validate_gas_used; -use ethereum_rust_core::types::{Receipt, Transaction}; -// We have to import the ExecutionDB. -use ethereum_rust_vm::{block_env, tx_env}; - -use revm::{ - db::CacheDB, inspectors::TracerEip3155, primitives::ResultAndState as RevmResultAndState, - Evm as Revm, -}; +use ethereum_rust_blockchain::{validate_block, validate_gas_used}; +use ethereum_rust_core::types::{Block, BlockHeader}; +use ethereum_rust_vm::{execute_block, execution_db::ExecutionDB, get_state_transitions, EvmState}; fn main() { - // Read the input - let head_block_bytes = env::read::>(); - let parent_header_bytes = env::read::>(); - //let execution_db = env::read::(); + let (block, execution_db, parent_header) = read_inputs().expect("failed to read inputs"); + let mut state = EvmState::from_exec_db(execution_db.clone()); - // SetUp data from inputs - let block = ::decode( - &head_block_bytes, - ) - .unwrap(); + // Validate the block pre-execution + validate_block(&block, &parent_header, &state).expect("invalid block"); - let parent_header = - ::decode( - &parent_header_bytes, - ) - .unwrap(); + let receipts = execute_block(&block, &mut state).unwrap(); - // Make DataInputs public. - env::commit(&block); - env::commit(&parent_header); - //env::commit(&execution_db); - - // SetUp CacheDB in order to use execute_block() - //let mut cache_db = CacheDB::new(execution_db); - println!("executing block"); + validate_gas_used(&receipts, &block.header).expect("invalid gas used"); - //let block_receipts = execute_block(&block, &mut cache_db).unwrap(); - // TODO - // Handle the case in which the gas used differs and throws an error. - // Should the zkVM panic? Should it generate a dummy proof? - // Private function - //let _ = validate_gas_used(&block_receipts, &block.header); + let _account_updates = get_state_transitions(&mut state); - //env::commit(&block_receipts); + // TODO: compute new state root from account updates and check it matches with the block's + // header one. } -// Modified from ethereum_rust-vm -/* -fn execute_block( - block: ðereum_rust_core::types::Block, - db: &mut CacheDB, -) -> Result, ethereum_rust_vm::EvmError> { - let spec_id = revm::primitives::SpecId::CANCUN; - let mut receipts = Vec::new(); - let mut cumulative_gas_used = 0; - - for transaction in block.body.transactions.iter() { - let result = execute_tx(transaction, &block.header, db, spec_id)?; - cumulative_gas_used += result.gas_used(); - let receipt = Receipt::new( - transaction.tx_type(), - result.is_success(), - cumulative_gas_used, - result.logs(), - ); - receipts.push(receipt); - } +fn read_inputs() -> Result<(Block, ExecutionDB, BlockHeader), RLPDecodeError> { + let head_block_bytes = env::read::>(); + let execution_db = env::read::(); + let parent_header_bytes = env::read::>(); - Ok(receipts) -} + let block = Block::decode(&head_block_bytes)?; + let parent_header = BlockHeader::decode(&parent_header_bytes)?; -// Modified from ethereum_rust-vm -fn execute_tx( - transaction: &Transaction, - block_header: ðereum_rust_core::types::BlockHeader, - db: &mut CacheDB, - spec_id: revm::primitives::SpecId, -) -> Result { - let block_env = block_env(block_header); - let tx_env = tx_env(transaction); - run_evm(tx_env, block_env, db, spec_id) - .map(Into::into) - .map_err(ethereum_rust_vm::EvmError::from) -} + // make inputs public + env::commit(&block); + env::commit(&execution_db); + env::commit(&parent_header); -// Modified from ethereum_rust-vm -fn run_evm( - tx_env: revm::primitives::TxEnv, - block_env: revm::primitives::BlockEnv, - db: &mut CacheDB, - spec_id: revm::primitives::SpecId, -) -> Result { - // let chain_spec = db.get_chain_config()?; - let mut evm = Revm::builder() - .with_db(db) - .with_block_env(block_env) - .with_tx_env(tx_env) - // If the chain_id is not correct, it throws: - // Transaction(InvalidChainId) - // TODO: do not hardcode the chain_id - .modify_cfg_env(|cfg| cfg.chain_id = 1729) - .with_spec_id(spec_id) - .with_external_context(TracerEip3155::new(Box::new(std::io::stderr())).without_summary()) - .build(); - let RevmResultAndState { result, state: _ } = evm.transact().unwrap(); - Ok(result.into()) + Ok((block, execution_db, parent_header)) } -*/ diff --git a/crates/vm/vm.rs b/crates/vm/vm.rs index 99374a5a13..d691d69208 100644 --- a/crates/vm/vm.rs +++ b/crates/vm/vm.rs @@ -53,6 +53,10 @@ pub enum EvmState { } impl EvmState { + pub fn from_exec_db(db: ExecutionDB) -> Self { + EvmState::Execution(revm::db::CacheDB::new(db)) + } + /// Get a reference to inner `Store` database pub fn database(&self) -> Option<&Store> { if let EvmState::Store(db) = self {