diff --git a/contracts/src/deploy-config/holesky.ts b/contracts/src/deploy-config/holesky.ts index f5a5a090f..b5407b027 100644 --- a/contracts/src/deploy-config/holesky.ts +++ b/contracts/src/deploy-config/holesky.ts @@ -14,7 +14,7 @@ const config = { l2BaseFee: 0.1, // Gwei // verify contract config - programVkey: '0x001fe18a35489f0e7ae203bc2b1f26b7696f00bafa8000518c94a479d29c4076', + programVkey: '0x00b450ec2a1b8dfba81ade90afbcc96842055548b814c991bb13bdca34980c63', // rollup contract config // initialize config finalizationPeriodSeconds: 600, diff --git a/contracts/src/deploy-config/hoodi.ts b/contracts/src/deploy-config/hoodi.ts index 15f50dda4..b95fa1434 100644 --- a/contracts/src/deploy-config/hoodi.ts +++ b/contracts/src/deploy-config/hoodi.ts @@ -17,7 +17,7 @@ const config = { l2BaseFee: 0.1, // Gwei // verify contract config - programVkey: '0x001fe18a35489f0e7ae203bc2b1f26b7696f00bafa8000518c94a479d29c4076', + programVkey: '0x00b450ec2a1b8dfba81ade90afbcc96842055548b814c991bb13bdca34980c63', // rollup contract config // initialize config finalizationPeriodSeconds: 600, diff --git a/contracts/src/deploy-config/l1.ts b/contracts/src/deploy-config/l1.ts index 680b89db7..6f09ba7eb 100644 --- a/contracts/src/deploy-config/l1.ts +++ b/contracts/src/deploy-config/l1.ts @@ -17,7 +17,7 @@ const config = { l2BaseFee: 0.1, // Gwei // verify contract config - programVkey: '0x001fe18a35489f0e7ae203bc2b1f26b7696f00bafa8000518c94a479d29c4076', + programVkey: '0x00b450ec2a1b8dfba81ade90afbcc96842055548b814c991bb13bdca34980c63', // rollup contract config // initialize config finalizationPeriodSeconds: 10, diff --git a/contracts/src/deploy-config/qanetl1.ts b/contracts/src/deploy-config/qanetl1.ts index c21ea93b6..b869fa25c 100644 --- a/contracts/src/deploy-config/qanetl1.ts +++ b/contracts/src/deploy-config/qanetl1.ts @@ -14,7 +14,7 @@ const config = { l2BaseFee: 0.1, // Gwei // verify contract config - programVkey: '0x001fe18a35489f0e7ae203bc2b1f26b7696f00bafa8000518c94a479d29c4076', + programVkey: '0x00b450ec2a1b8dfba81ade90afbcc96842055548b814c991bb13bdca34980c63', // rollup contract config // initialize config finalizationPeriodSeconds: 600, diff --git a/contracts/src/deploy-config/sepolia.ts b/contracts/src/deploy-config/sepolia.ts index 0facf9f99..d2db493cf 100644 --- a/contracts/src/deploy-config/sepolia.ts +++ b/contracts/src/deploy-config/sepolia.ts @@ -18,7 +18,7 @@ const config = { /** * ---to---legacy property */ - programVkey: '0x001fe18a35489f0e7ae203bc2b1f26b7696f00bafa8000518c94a479d29c4076', + programVkey: '0x00b450ec2a1b8dfba81ade90afbcc96842055548b814c991bb13bdca34980c63', rollupMinDeposit: 0.0001, rollupProofWindow: 86400, rollupGenesisBlockNumber: 0, diff --git a/contracts/src/deploy-config/testnetl1.ts b/contracts/src/deploy-config/testnetl1.ts index 501bb7b7f..a8d567ad9 100644 --- a/contracts/src/deploy-config/testnetl1.ts +++ b/contracts/src/deploy-config/testnetl1.ts @@ -13,7 +13,7 @@ const config = { sequencerWindowSize: 200, channelTimeout: 120, - programVkey: '0x001fe18a35489f0e7ae203bc2b1f26b7696f00bafa8000518c94a479d29c4076', + programVkey: '0x00b450ec2a1b8dfba81ade90afbcc96842055548b814c991bb13bdca34980c63', rollupMinDeposit: 1, rollupProofWindow: 100, rollupGenesisBlockNumber: 0, diff --git a/prover/bin/client/elf/verifier-client b/prover/bin/client/elf/verifier-client index 754175072..d37f736a8 100755 Binary files a/prover/bin/client/elf/verifier-client and b/prover/bin/client/elf/verifier-client differ diff --git a/prover/bin/server/src/queue.rs b/prover/bin/server/src/queue.rs index 37306e489..eb055c8b8 100644 --- a/prover/bin/server/src/queue.rs +++ b/prover/bin/server/src/queue.rs @@ -114,12 +114,17 @@ impl Prover { let prove_rt = self.batch_prover.prove(&mut input, !shadow).await; match prove_rt { - Ok(Some(proof)) => { - save_proof(batch_index, proof); - PROVE_RESULT.set(1); - let duration_mins = start.elapsed().as_secs() / 60; - PROVE_TIME.set(duration_mins.try_into().unwrap_or_default()); - } + Ok(Some(proof)) => match save_proof(batch_index, proof) { + Ok(()) => { + PROVE_RESULT.set(1); + let duration_mins = start.elapsed().as_secs() / 60; + PROVE_TIME.set(duration_mins.try_into().unwrap_or_default()); + } + Err(e) => { + PROVE_RESULT.set(2); + log::error!("Save evm proof of batch-{:?} error: {:?}", batch_index, e); + } + }, Ok(None) => { PROVE_RESULT.set(2); log::error!("Gen proof of batch-{:?} is none", batch_index) @@ -196,16 +201,13 @@ async fn gen_client_input( } /// Save evm proof to file. -fn save_proof(batch_index: u64, proof: EvmProofFixture) { +fn save_proof(batch_index: u64, proof: EvmProofFixture) -> Result<(), anyhow::Error> { let batch_dir = PathBuf::from(PROVER_PROOF_DIR.to_string()).join(format!("batch_{batch_index}")); - std::fs::create_dir_all(&batch_dir).expect("failed to create proof path"); - std::fs::write( - batch_dir.join("plonk_proof.json"), - serde_json::to_string_pretty(&proof).unwrap(), - ) - .expect("failed to write proof"); + std::fs::create_dir_all(&batch_dir)?; + std::fs::write(batch_dir.join("plonk_proof.json"), serde_json::to_string_pretty(&proof)?)?; log::info!("Successfully save evm proof of batch-{:?}", batch_index); + Ok(()) } #[allow(dead_code)] diff --git a/prover/bin/shadow-prove/contracts/README.md b/prover/bin/shadow-prove/contracts/README.md index 2c03d9401..fea0c204e 100644 --- a/prover/bin/shadow-prove/contracts/README.md +++ b/prover/bin/shadow-prove/contracts/README.md @@ -16,7 +16,7 @@ forge build EvmVerifier: ``` -forge create --broadcast --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --rpc-url http://localhost:8545 src/libs/EvmVerifier.sol:EvmVerifier --constructor-args 0x001fe18a35489f0e7ae203bc2b1f26b7696f00bafa8000518c94a479d29c4076 +forge create --broadcast --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --rpc-url http://localhost:8545 src/libs/EvmVerifier.sol:EvmVerifier --constructor-args 0x00b450ec2a1b8dfba81ade90afbcc96842055548b814c991bb13bdca34980c63 ``` ShadowRollup diff --git a/prover/contracts/README.md b/prover/contracts/README.md index f745f97e3..689aaa988 100644 --- a/prover/contracts/README.md +++ b/prover/contracts/README.md @@ -17,5 +17,5 @@ $ forge test ### Deploy ``` -forge create --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --rpc-url http://localhost:8545 prover/contracts/src/EvmVerifier.sol:EvmVerifier --constructor-args 0x001fe18a35489f0e7ae203bc2b1f26b7696f00bafa8000518c94a479d29c4076 +forge create --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --rpc-url http://localhost:8545 prover/contracts/src/EvmVerifier.sol:EvmVerifier --constructor-args 0x00b450ec2a1b8dfba81ade90afbcc96842055548b814c991bb13bdca34980c63 ``` \ No newline at end of file diff --git a/prover/crates/executor/client/src/types/error.rs b/prover/crates/executor/client/src/types/error.rs index 915e88cce..2105ad4b8 100644 --- a/prover/crates/executor/client/src/types/error.rs +++ b/prover/crates/executor/client/src/types/error.rs @@ -27,6 +27,8 @@ pub enum ClientError { MissingTrie(Address), #[error("Invalid block number found in headers \n expected: {} found: {}", .0, .1)] InvalidHeaderBlockNumber(u64, u64), + #[error("Invalid base fee found in header \n expected: {} found: {}", .0, .1)] + InvalidHeaderBaseFee(u64, u64), #[error("Invalid parent header found for block \n expected: {}, found: {}", .0, .1)] InvalidHeaderParentHash(FixedBytes<32>, FixedBytes<32>), #[error("Failed to validate post exectution state {}", 0)] diff --git a/prover/crates/executor/client/src/types/input.rs b/prover/crates/executor/client/src/types/input.rs index 4ac2fd13b..91487ad58 100644 --- a/prover/crates/executor/client/src/types/input.rs +++ b/prover/crates/executor/client/src/types/input.rs @@ -2,6 +2,7 @@ use alloy_primitives::{map::HashMap, U256}; use prover_mpt::EthereumState; use prover_primitives::{types::block::L2Block, Address}; use prover_storage_witness::TrieDB; +use reth_trie::{TrieAccount, EMPTY_ROOT_HASH}; use revm::{primitives::keccak256, state::Bytecode}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; @@ -30,12 +31,26 @@ pub struct BlockInput { } impl BlockInput { - pub fn witness_db(&self) -> Result, ClientError> { - // verify the state root + fn validate_parent_state(&self) -> Result<(), ClientError> { if self.current_block.prev_state_root != self.parent_state.state_root() { return Err(ClientError::InvalidHeaderStateRoot); } + for (hashed_address, storage_trie) in &self.parent_state.storage_tries { + let account = + self.parent_state.state_trie.get_rlp::(hashed_address.as_slice())?; + let storage_root = account.map_or(EMPTY_ROOT_HASH, |account| account.storage_root); + if storage_trie.hash() != storage_root { + return Err(ClientError::MismatchedStorageRoot); + } + } + + Ok(()) + } + + pub fn witness_db(&self) -> Result, ClientError> { + self.validate_parent_state()?; + let bytecodes_by_hash = self.bytecodes.iter().map(|code| (code.hash_slow(), code)).collect::>(); @@ -50,17 +65,21 @@ impl BlockInput { /// Get storage value of address at index. pub fn get_storage_value(&self, address: Address, index: U256) -> Result { let hashed_address = keccak256(address); - let hashed_address = hashed_address.as_slice(); let storage_trie = self .parent_state .storage_tries - .get(hashed_address) - .expect("A storage trie must be provided for each account"); + .get(&hashed_address) + .ok_or(ClientError::MissingTrie(address))?; + let account = + self.parent_state.state_trie.get_rlp::(hashed_address.as_slice())?; + let storage_root = account.map_or(EMPTY_ROOT_HASH, |account| account.storage_root); + if storage_trie.hash() != storage_root { + return Err(ClientError::MismatchedStorageRoot); + } Ok(storage_trie - .get_rlp::(keccak256(index.to_be_bytes::<32>()).as_slice()) - .expect("Can get from MPT") + .get_rlp::(keccak256(index.to_be_bytes::<32>()).as_slice())? .unwrap_or_default()) } } diff --git a/prover/crates/executor/client/src/verifier/evm_verifier.rs b/prover/crates/executor/client/src/verifier/evm_verifier.rs index 05a200098..83e5a9f93 100644 --- a/prover/crates/executor/client/src/verifier/evm_verifier.rs +++ b/prover/crates/executor/client/src/verifier/evm_verifier.rs @@ -10,6 +10,8 @@ use revm::database::State; // use Verifier; pub struct EVMVerifier; +const L2_BASE_FEE: u64 = 1_000_000; + impl EVMVerifier { pub fn verify(blocks: Vec) -> Result { // Edge case: nothing to execute. @@ -18,13 +20,20 @@ impl EVMVerifier { "empty batch: no block inputs provided".to_owned(), )); } - // Verify that each block's `prev_state_root` matches the previous block's `post_state_root`. - // This ensures the batch is contiguous. - if blocks - .windows(2) - .any(|w| w[0].current_block.post_state_root != w[1].current_block.prev_state_root) - { - return Err(ClientError::DiscontinuousStateRoot); + // Verify that block numbers and state roots are consecutive within the batch. + for window in blocks.windows(2) { + let previous = window[0].current_block.header.number.to::(); + let current = window[1].current_block.header.number.to::(); + let expected = previous + .checked_add(1) + .ok_or(ClientError::InvalidHeaderBlockNumber(previous, current))?; + if current != expected { + return Err(ClientError::InvalidHeaderBlockNumber(expected, current)); + } + + if window[0].current_block.post_state_root != window[1].current_block.prev_state_root { + return Err(ClientError::DiscontinuousStateRoot); + } } execute(blocks) } @@ -70,7 +79,12 @@ fn execute_block(block_input: &mut BlockInput) -> Result<(), ClientError> { } return Ok(()); } + let header = &block.header; + let base_fee = header.base_fee_per_gas.unwrap_or_default().to::(); + if base_fee != L2_BASE_FEE { + return Err(ClientError::InvalidHeaderBaseFee(L2_BASE_FEE, base_fee)); + } let chain_id = block.chain_id; let _tx_count = block.transactions.len(); let _block_num = header.number.to::(); diff --git a/prover/crates/primitives/src/predeployed.rs b/prover/crates/primitives/src/predeployed.rs index dd5800071..f712f632c 100644 --- a/prover/crates/primitives/src/predeployed.rs +++ b/prover/crates/primitives/src/predeployed.rs @@ -1,33 +1,3 @@ -/// Predeployed Gas Price Oracle -pub mod l1_gas_price_oracle { - use alloy_primitives::{address, Address, U256}; - - /// L1GasPriceOracle predeployed address - pub const ADDRESS: Address = address!("5300000000000000000000000000000000000002"); - /// L1 base fee slot in L1GasPriceOracle - pub const BASE_FEE_SLOT: U256 = U256::from_limbs([1, 0, 0, 0]); - - /// The following 2 slots will be depreciated after curie fork - /// L1 overhead slot in L1GasPriceOracle - pub const OVERHEAD_SLOT: U256 = U256::from_limbs([2, 0, 0, 0]); - /// L1 scalar slot in L1GasPriceOracle - pub const SCALAR_SLOT: U256 = U256::from_limbs([3, 0, 0, 0]); - - /// THe following 3 slots plus `BASE_FEE_SLOT` will be used for l1 fee after curie fork - /// L1 BlobBaseFee slot in L1GasPriceOracle after Curie fork - pub const L1_BLOB_BASEFEE_SLOT: U256 = U256::from_limbs([5, 0, 0, 0]); - /// L1 commitScalar slot in L1GasPriceOracle after Curie fork - pub const COMMIT_SCALAR_SLOT: U256 = U256::from_limbs([6, 0, 0, 0]); - /// L1 blob_scalar slot in L1GasPriceOracle after Curie fork - pub const BLOB_SCALAR_SLOT: U256 = U256::from_limbs([7, 0, 0, 0]); - /// L1 isCurie slot in L1GasPriceOracle after Curie fork - pub const IS_CURIE_SLOT: U256 = U256::from_limbs([8, 0, 0, 0]); - /// Initial commit scalar after curie fork - pub const INITIAL_COMMIT_SCALAR: U256 = U256::from_limbs([230759955285, 0, 0, 0]); - /// Initial blob scalar after curie fork - pub const INITIAL_BLOB_SCALAR: U256 = U256::from_limbs([417565260, 0, 0, 0]); -} - /// Predeployed L2ToL1Message pub mod l2_to_l1_message { use alloy_primitives::{address, uint, Address, U256}; diff --git a/prover/crates/storage/witness-db/src/lib.rs b/prover/crates/storage/witness-db/src/lib.rs index e466b1b66..6488eef57 100644 --- a/prover/crates/storage/witness-db/src/lib.rs +++ b/prover/crates/storage/witness-db/src/lib.rs @@ -104,11 +104,15 @@ impl DatabaseRef for TrieDB<'_> { .inner .storage_tries .get(hashed_address) - .expect("A storage trie must be provided for each account"); + .ok_or(TrieDBError(format!("storage trie not found for address: {:?}", address)))?; Ok(storage_trie .get_rlp::(keccak256(index.to_be_bytes::<32>()).as_slice()) - .expect("Can get from MPT") + .map_err(|_| { + TrieDBError(format!( + "failed to read storage slot from trie: address={address:?}, slot={index:?}" + )) + })? .unwrap_or_default()) }