From 7f84c9ea1171220db5a14a49917afd65ea24afc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Est=C3=A9fano=20Bargas?= Date: Fri, 25 Oct 2024 17:50:51 +0000 Subject: [PATCH] feat(l2): prove block validation (#973) **Motivation** the block execution program is missing many validation steps, this PR adds them. **Description** - adds validation steps to zkVM program --- crates/blockchain/blockchain.rs | 5 ++- crates/l2/proposer/prover_server.rs | 15 +++++++- crates/l2/prover/src/prover.rs | 2 + .../l2/prover/zkvm/interface/guest/Cargo.toml | 1 + .../prover/zkvm/interface/guest/src/main.rs | 37 ++++++++++++++----- 5 files changed, 47 insertions(+), 13 deletions(-) diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index 5e1df5a3b5..960ba645c6 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -158,7 +158,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/proposer/prover_server.rs b/crates/l2/proposer/prover_server.rs index 13606cecd0..0dbe7a036c 100644 --- a/crates/l2/proposer/prover_server.rs +++ b/crates/l2/proposer/prover_server.rs @@ -11,12 +11,13 @@ use std::{ use tokio::signal::unix::{signal, SignalKind}; use tracing::{debug, info, warn}; -use ethereum_rust_core::types::Block; +use ethereum_rust_core::types::{Block, BlockHeader}; #[derive(Debug, Serialize, Deserialize, Default)] pub struct ProverInputData { pub db: ExecutionDB, pub block: Block, + pub parent_header: BlockHeader, } use crate::utils::config::prover_server::ProverServerConfig; @@ -223,8 +224,18 @@ impl ProverServer { }; 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 }) + Ok(ProverInputData { + db, + block, + parent_header, + }) } } diff --git a/crates/l2/prover/src/prover.rs b/crates/l2/prover/src/prover.rs index 936cd609f4..4826755a67 100644 --- a/crates/l2/prover/src/prover.rs +++ b/crates/l2/prover/src/prover.rs @@ -35,10 +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_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(&input.db).unwrap(); + self.env_builder.write(&parent_header_rlp).unwrap(); self } diff --git a/crates/l2/prover/zkvm/interface/guest/Cargo.toml b/crates/l2/prover/zkvm/interface/guest/Cargo.toml index 4ea269dd26..ff0d146085 100644 --- a/crates/l2/prover/zkvm/interface/guest/Cargo.toml +++ b/crates/l2/prover/zkvm/interface/guest/Cargo.toml @@ -11,6 +11,7 @@ risc0-zkvm = { version = "1.1.2", default-features = false, features = ['std'] } ethereum_rust-core = { path = "../../../../../common", default-features = false } ethereum_rust-rlp = { path = "../../../../../common/rlp" } ethereum_rust-vm = { path = "../../../../../vm", default-features = false } +ethereum_rust-blockchain = { path = "../../../../../blockchain", default-features = false } [patch.crates-io] crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" } diff --git a/crates/l2/prover/zkvm/interface/guest/src/main.rs b/crates/l2/prover/zkvm/interface/guest/src/main.rs index 08e86f465b..95eefaf92c 100644 --- a/crates/l2/prover/zkvm/interface/guest/src/main.rs +++ b/crates/l2/prover/zkvm/interface/guest/src/main.rs @@ -1,22 +1,39 @@ -use ethereum_rust_rlp::decode::RLPDecode; +use ethereum_rust_rlp::{decode::RLPDecode, error::RLPDecodeError}; use risc0_zkvm::guest::env; -use ethereum_rust_core::types::Block; -use ethereum_rust_vm::{execute_block, execution_db::ExecutionDB, EvmState}; +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 inputs + let (block, execution_db, parent_header) = read_inputs().expect("failed to read inputs"); + let mut state = EvmState::from_exec_db(execution_db.clone()); + + // Validate the block pre-execution + validate_block(&block, &parent_header, &state).expect("invalid block"); + + let receipts = execute_block(&block, &mut state).unwrap(); + + validate_gas_used(&receipts, &block.header).expect("invalid gas used"); + + let _account_updates = get_state_transitions(&mut state); + + // TODO: compute new state root from account updates and check it matches with the block's + // header one. +} + +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::>(); - let block = Block::decode(&head_block_bytes).unwrap(); + let block = Block::decode(&head_block_bytes)?; + let parent_header = BlockHeader::decode(&parent_header_bytes)?; - // Make inputs public + // make inputs public env::commit(&block); env::commit(&execution_db); + env::commit(&parent_header); - // Execute block - let mut state = EvmState::from_exec_db(execution_db); - let block_receipts = execute_block(&block, &mut state).unwrap(); - env::commit(&block_receipts); + Ok((block, execution_db, parent_header)) }