From 2a6cc041ac0817fb1909b8590d8eb9387ff0d884 Mon Sep 17 00:00:00 2001 From: Ho Date: Fri, 25 Aug 2023 11:26:43 +0800 Subject: [PATCH] [Feat] make supercircuit test applying l2trace (#767) * induce l2 types for l2trace * add l2trace entry * wip: some refactoring * refactor zktrie for decoupling from bus-mapping * more refactoring in zktrie * wip: l2 circuit builder * use l2 trace in super circuit test * prune unnecessary sign * fmt and lint * missed exportion * update l2geth util for l1 queue index * apply zktrie and storage proof * sdb skip empty value * update state root in tracing * more update for state root * fix tests * lints * lint for all-target * fix memory transfer * fmt * disable callee code assert for precompile * set all difficulity in l2 test to 0 * reroll the difficulity setting in test apply global difficulity to 0 for l2 * fix deploy tx issue and prune l2geth_util * revert incorrect fixing * disable doc test temporarily * add incremental update for l2 trace entry in circuitbuilder * zero coinbase patch for sdb * rename variables for better self-doc * re-enable tx storage fiield * add modexp unittest * turn ec test to serial * bump the version of l2geth_util * make l1 queue index default * fix an issue in sdb update add an entry for convenient * mpt_state: add light_mode for ZktrieState::from_trace_with_additional --------- Co-authored-by: Zhang Zhuo --- Cargo.lock | 2 +- bus-mapping/Cargo.toml | 1 + bus-mapping/src/circuit_input_builder.rs | 9 + bus-mapping/src/circuit_input_builder/l2.rs | 407 ++++++++++++++++++++ bus-mapping/src/error.rs | 4 + bus-mapping/src/lib.rs | 2 +- circuit-benchmarks/src/super_circuit.rs | 11 +- eth-types/src/geth_types.rs | 4 +- eth-types/src/l2_types.rs | 289 ++++++++++++++ eth-types/src/lib.rs | 1 + external-tracer/src/lib.rs | 33 ++ geth-utils/l2geth/go.mod | 2 +- geth-utils/l2geth/go.sum | 6 +- geth-utils/l2geth/trace.go | 41 +- geth-utils/src/lib.rs | 29 ++ mock/src/block.rs | 6 +- mock/src/test_ctx.rs | 50 ++- mock/src/transaction.rs | 7 +- testool/src/statetest/executor.rs | 7 +- zkevm-circuits/src/pi_circuit/test.rs | 6 +- zkevm-circuits/src/super_circuit/test.rs | 185 +++++---- zktrie/Cargo.toml | 1 - zktrie/src/state.rs | 203 ++++------ zktrie/src/state/builder.rs | 12 +- zktrie/src/state/test.rs | 13 +- zktrie/src/state/witness.rs | 69 ++-- 26 files changed, 1107 insertions(+), 293 deletions(-) create mode 100644 bus-mapping/src/circuit_input_builder/l2.rs create mode 100644 eth-types/src/l2_types.rs diff --git a/Cargo.lock b/Cargo.lock index b3df46cdfa..b2c55a0167 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -466,6 +466,7 @@ dependencies = [ "lazy_static", "log", "mock", + "mpt-zktrie", "once_cell", "poseidon-circuit", "pretty_assertions", @@ -2934,7 +2935,6 @@ dependencies = [ name = "mpt-zktrie" version = "0.1.0" dependencies = [ - "bus-mapping", "env_logger 0.9.3", "eth-types", "halo2-mpt-circuits", diff --git a/bus-mapping/Cargo.toml b/bus-mapping/Cargo.toml index dad25cc000..728e3f19ec 100644 --- a/bus-mapping/Cargo.toml +++ b/bus-mapping/Cargo.toml @@ -9,6 +9,7 @@ license = "MIT OR Apache-2.0" eth-types = { path = "../eth-types" } gadgets = { path = "../gadgets" } keccak256 = { path = "../keccak256" } +mpt-zktrie = {path = "../zktrie"} mock = { path = "../mock", optional = true } ethers-core = "0.17.0" diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 3163f33f0b..8e3562e71c 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -6,6 +6,8 @@ mod block; mod call; mod execution; mod input_state_ref; +#[cfg(feature = "scroll")] +mod l2; #[cfg(test)] mod tracer_tests; mod transaction; @@ -46,6 +48,8 @@ use ethers_core::utils::keccak256; pub use input_state_ref::CircuitInputStateRef; use itertools::Itertools; use log::warn; +#[cfg(feature = "scroll")] +use mpt_zktrie::state::ZktrieState; use std::{ collections::{BTreeMap, HashMap}, iter, @@ -175,6 +179,9 @@ pub struct CircuitInputBuilder { pub block: Block, /// Block Context pub block_ctx: BlockContext, + #[cfg(feature = "scroll")] + /// Initial Zktrie Status for a incremental updating + pub mpt_init_state: ZktrieState, } impl<'a> CircuitInputBuilder { @@ -186,6 +193,8 @@ impl<'a> CircuitInputBuilder { code_db, block: block.clone(), block_ctx: BlockContext::new(), + #[cfg(feature = "scroll")] + mpt_init_state: Default::default(), } } /// Create a new CircuitInputBuilder from the given `eth_block` and diff --git a/bus-mapping/src/circuit_input_builder/l2.rs b/bus-mapping/src/circuit_input_builder/l2.rs new file mode 100644 index 0000000000..c5b002b955 --- /dev/null +++ b/bus-mapping/src/circuit_input_builder/l2.rs @@ -0,0 +1,407 @@ +pub use super::block::{Block, BlockContext}; +use crate::{ + circuit_input_builder::{self, BlockHead, CircuitInputBuilder, CircuitsParams}, + error::Error, + state_db::{self, CodeDB, StateDB}, +}; +use eth_types::{ + self, + evm_types::OpcodeId, + l2_types::{BlockTrace, EthBlock, ExecStep, StorageTrace}, + Address, ToAddress, Word, H256, +}; +use ethers_core::types::{Bytes, U256}; +use mpt_zktrie::state::{AccountData, ZktrieState}; +use std::collections::hash_map::Entry; + +impl From<&AccountData> for state_db::Account { + fn from(acc_data: &AccountData) -> Self { + if acc_data.keccak_code_hash.is_zero() { + state_db::Account::zero() + } else { + Self { + nonce: acc_data.nonce.into(), + balance: acc_data.balance, + code_hash: acc_data.poseidon_code_hash, + keccak_code_hash: acc_data.keccak_code_hash, + code_size: acc_data.code_size.into(), + storage: Default::default(), + } + } + } +} + +impl From<&ZktrieState> for StateDB { + fn from(mpt_state: &ZktrieState) -> Self { + let mut sdb = StateDB::new(); + + for (addr, acc) in mpt_state.state() { + sdb.set_account(addr, acc.into()) + } + + for (storage_key, data) in mpt_state.storage() { + if !data.as_ref().is_zero() { + //TODO: add an warning on non-existed account? + let (_, acc) = sdb.get_account_mut(&storage_key.0); + acc.storage.insert(storage_key.1, *data.as_ref()); + } + } + sdb + } +} + +fn decode_bytecode(bytecode: &str) -> Result, Error> { + let mut stripped = if let Some(stripped) = bytecode.strip_prefix("0x") { + stripped.to_string() + } else { + bytecode.to_string() + }; + + let bytecode_len = stripped.len() as u64; + if (bytecode_len & 1) != 0 { + stripped = format!("0{stripped}"); + } + + hex::decode(stripped).map_err(Error::HexError) +} + +fn trace_code( + cdb: &mut CodeDB, + code_hash: Option, + code: Bytes, + step: &ExecStep, + sdb: &StateDB, + stack_pos: usize, +) { + // first, try to read from sdb + let stack = step + .stack + .as_ref() + .expect("should have stack in call context"); + let addr = stack[stack.len() - stack_pos - 1].to_address(); //stack N-stack_pos + + let code_hash = code_hash.or_else(|| { + let (_existed, acc_data) = sdb.get_account(&addr); + if acc_data.code_hash != CodeDB::empty_code_hash() && !code.is_empty() { + // they must be same + Some(acc_data.code_hash) + } else { + // let us re-calculate it + None + } + }); + let code_hash = match code_hash { + Some(code_hash) => { + if code_hash.is_zero() { + CodeDB::hash(&code) + } else { + if log::log_enabled!(log::Level::Trace) { + assert_eq!( + code_hash, + CodeDB::hash(&code), + "bytecode len {:?}, step {:?}", + code.len(), + step + ); + } + code_hash + } + } + None => { + let hash = CodeDB::hash(&code); + log::debug!( + "hash_code done: addr {addr:?}, size {}, hash {hash:?}", + &code.len() + ); + hash + } + }; + + cdb.0.entry(code_hash).or_insert_with(|| { + log::trace!( + "trace code addr {:?}, size {} hash {:?}", + addr, + &code.len(), + code_hash + ); + code.to_vec() + }); +} + +fn update_codedb(cdb: &mut CodeDB, sdb: &StateDB, block: &BlockTrace) -> Result<(), Error> { + log::debug!("build_codedb for block {:?}", block.header.number); + for (er_idx, execution_result) in block.execution_results.iter().enumerate() { + if let Some(bytecode) = &execution_result.byte_code { + let bytecode = decode_bytecode(bytecode)?.to_vec(); + + let code_hash = execution_result + .to + .as_ref() + .and_then(|t| t.poseidon_code_hash) + .unwrap_or_else(|| CodeDB::hash(&bytecode)); + let code_hash = if code_hash.is_zero() { + CodeDB::hash(&bytecode) + } else { + code_hash + }; + if let Entry::Vacant(e) = cdb.0.entry(code_hash) { + e.insert(bytecode); + //log::debug!("inserted tx bytecode {:?} {:?}", code_hash, hash); + } + if execution_result.account_created.is_none() { + //assert_eq!(Some(hash), execution_result.code_hash); + } + } + + for step in execution_result.exec_steps.iter().rev() { + if let Some(data) = &step.extra_data { + match step.op { + OpcodeId::CALL + | OpcodeId::CALLCODE + | OpcodeId::DELEGATECALL + | OpcodeId::STATICCALL => { + let code_idx = if block.transactions[er_idx].to.is_none() { + 0 + } else { + 1 + }; + let callee_code = data.get_code_at(code_idx); + // TODO: make nil code ("0x") is not None and assert None case + // assert!( + // callee_code.is_none(), + // "invalid trace: cannot get code of call: {step:?}" + // ); + let code_hash = match step.op { + OpcodeId::CALL | OpcodeId::CALLCODE => data.get_code_hash_at(1), + OpcodeId::STATICCALL => data.get_code_hash_at(0), + _ => None, + }; + trace_code( + cdb, + code_hash, + callee_code.unwrap_or_default(), + step, + sdb, + 1, + ); + } + OpcodeId::CREATE | OpcodeId::CREATE2 => { + // notice we do not need to insert code for CREATE, + // bustmapping do this job + } + OpcodeId::EXTCODESIZE | OpcodeId::EXTCODECOPY => { + let code = data.get_code_at(0); + if code.is_none() { + return Err(Error::InvalidGethExecStep( + "invalid trace: cannot get code of ext", + Box::new(eth_types::GethExecStep::from(step)), + )); + } + trace_code(cdb, None, code.unwrap(), step, sdb, 0); + } + + _ => {} + } + } + } + } + + log::debug!("updating codedb done"); + Ok(()) +} + +fn dump_code_db(cdb: &CodeDB) { + for (k, v) in &cdb.0 { + assert!(!k.is_zero()); + log::trace!("codedb codehash {:?}, len {}", k, v.len()); + } +} + +impl CircuitInputBuilder { + fn apply_l2_trace(&mut self, block_trace: &BlockTrace, is_last: bool) -> Result<(), Error> { + if is_last { + dump_code_db(&self.code_db); + } + + let geth_trace: Vec = block_trace + .execution_results + .iter() + .map(From::from) + .collect(); + let eth_block: EthBlock = block_trace.clone().into(); + assert_eq!( + self.block.chain_id, block_trace.chain_id, + "unexpected chain id in new block_trace" + ); + // TODO: Get the history_hashes. + let mut header = BlockHead::new_with_l1_queue_index( + self.block.chain_id, + block_trace.start_l1_queue_index, + Vec::new(), + ð_block, + )?; + // override zeroed minder field with additional "coinbase" field in blocktrace + if let Some(address) = block_trace.coinbase.address { + header.coinbase = address; + } + let block_num = header.number.as_u64(); + // TODO: should be check the block number is in sequence? + self.block.headers.insert(block_num, header); + // note the actions when `handle_rwc_reversion` argument (the 4th one) + // is true is executing outside this closure + self.handle_block_inner(ð_block, &geth_trace, false, is_last)?; + log::debug!("handle_block_inner done for block {:?}", block_num); + Ok(()) + } + + fn collect_account_proofs( + storage_trace: &StorageTrace, + ) -> impl Iterator)> + Clone { + storage_trace.proofs.iter().flat_map(|kv_map| { + kv_map + .iter() + .map(|(k, bts)| (k, bts.iter().map(Bytes::as_ref))) + }) + } + + fn collect_storage_proofs( + storage_trace: &StorageTrace, + ) -> impl Iterator)> + Clone { + storage_trace.storage_proofs.iter().flat_map(|(k, kv_map)| { + kv_map + .iter() + .map(move |(sk, bts)| (k, sk, bts.iter().map(Bytes::as_ref))) + }) + } + + /// Create a new CircuitInputBuilder from the given `eth_block` and + /// `StateDB`, `CodeDB`, `ZktrieState` + pub fn new_with_trie_state( + sdb: StateDB, + code_db: CodeDB, + mpt_init_state: ZktrieState, + block: &Block, + ) -> Self { + Self { + sdb, + code_db, + block: block.clone(), + block_ctx: BlockContext::new(), + mpt_init_state, + } + } + + /// Create a new CircuitInputBuilder from the given `l2_trace` and `circuits_params` + pub fn new_from_l2_trace( + circuits_params: CircuitsParams, + l2_trace: &BlockTrace, + more: bool, + light_mode: bool, + ) -> Result { + let chain_id = l2_trace.chain_id; + + let old_root = l2_trace.storage_trace.root_before; + log::debug!( + "building zktrie state for block {:?}, old root {}", + l2_trace.header.number, + hex::encode(old_root), + ); + + let mpt_init_state = ZktrieState::from_trace_with_additional( + old_root, + Self::collect_account_proofs(&l2_trace.storage_trace), + Self::collect_storage_proofs(&l2_trace.storage_trace), + l2_trace + .storage_trace + .deletion_proofs + .iter() + .map(Bytes::as_ref), + light_mode, + ) + .unwrap(); + + log::debug!( + "building partial statedb done, root {}", + hex::encode(mpt_init_state.root()) + ); + + let mut sdb = StateDB::from(&mpt_init_state); + + let (zero_coinbase_exist, _) = sdb.get_account(&Default::default()); + if !zero_coinbase_exist { + sdb.set_account(&Default::default(), state_db::Account::zero()); + } + + let mut code_db = CodeDB::new(); + code_db.insert(Vec::new()); + update_codedb(&mut code_db, &sdb, l2_trace)?; + + let mut builder_block = circuit_input_builder::Block::from_headers(&[], circuits_params); + builder_block.chain_id = chain_id; + builder_block.prev_state_root = U256::from(mpt_init_state.root()); + builder_block.start_l1_queue_index = l2_trace.start_l1_queue_index; + let mut builder = Self { + sdb, + code_db, + block: builder_block, + block_ctx: BlockContext::new(), + mpt_init_state, + }; + + builder.apply_l2_trace(l2_trace, !more)?; + Ok(builder) + } + + /// ... + pub fn add_more_l2_trace( + &mut self, + l2_trace: &BlockTrace, + more: bool, + light_mode: bool, + ) -> Result<(), Error> { + // update sdb for new data from storage + if !light_mode { + self.mpt_init_state.update_nodes_from_proofs( + Self::collect_account_proofs(&l2_trace.storage_trace), + Self::collect_storage_proofs(&l2_trace.storage_trace), + l2_trace + .storage_trace + .deletion_proofs + .iter() + .map(Bytes::as_ref), + ); + } + + self.mpt_init_state + .update_account_from_proofs( + Self::collect_account_proofs(&l2_trace.storage_trace), + |addr, acc_data| { + self.sdb.set_account(addr, acc_data.into()); + Ok(()) + }, + ) + .map_err(Error::IoError)?; + + self.mpt_init_state + .update_storage_from_proofs( + Self::collect_storage_proofs(&l2_trace.storage_trace), + |storage_key, value| { + *self.sdb.get_storage_mut(&storage_key.0, &storage_key.1).1 = *value.as_ref(); + Ok(()) + }, + ) + .map_err(Error::IoError)?; + + update_codedb(&mut self.code_db, &self.sdb, l2_trace)?; + + self.apply_l2_trace(l2_trace, !more)?; + Ok(()) + } + + /// make finalize actions on building, must called after + /// all block trace have been input + pub fn finalize_building(&mut self) -> Result<(), Error> { + self.set_value_ops_call_context_rwc_eor(); + self.set_end_block() + } +} diff --git a/bus-mapping/src/error.rs b/bus-mapping/src/error.rs index b3f18c02cc..9368f24d6a 100644 --- a/bus-mapping/src/error.rs +++ b/bus-mapping/src/error.rs @@ -15,6 +15,10 @@ use crate::geth_errors::{ pub enum Error { /// Serde de/serialization error. SerdeError(serde_json::error::Error), + /// Parsing error + IoError(std::io::Error), + /// hex parsing error + HexError(hex::FromHexError), /// JSON-RPC related error. JSONRpcError(ProviderError), /// OpcodeId is not a call type. diff --git a/bus-mapping/src/lib.rs b/bus-mapping/src/lib.rs index 5a149bf3c7..cfca225f0f 100644 --- a/bus-mapping/src/lib.rs +++ b/bus-mapping/src/lib.rs @@ -47,7 +47,7 @@ //! with all of the Memory, Stack and Storage ops performed //! by the provided trace. //! -//! ```rust +//! ```rust, no_run //! use bus_mapping::{Error, mock::BlockData}; //! use bus_mapping::state_db::{self, StateDB, CodeDB}; //! use eth_types::{ diff --git a/circuit-benchmarks/src/super_circuit.rs b/circuit-benchmarks/src/super_circuit.rs index cc61f30cd4..dce51c2340 100644 --- a/circuit-benchmarks/src/super_circuit.rs +++ b/circuit-benchmarks/src/super_circuit.rs @@ -24,7 +24,7 @@ mod tests { use mock::{TestContext, MOCK_CHAIN_ID}; use rand::SeedableRng; use rand_chacha::ChaChaRng; - use std::{collections::HashMap, env::var}; + use std::env::var; use zkevm_circuits::super_circuit::SuperCircuit; #[cfg_attr(not(feature = "benches"), ignore)] @@ -53,10 +53,7 @@ mod tests { let addr_a = wallet_a.address(); let addr_b = address!("0x000000000000000000000000000000000000BBBB"); - let mut wallets = HashMap::new(); - wallets.insert(wallet_a.address(), wallet_a); - - let mut block: GethData = TestContext::<2, 1>::new( + let block: GethData = TestContext::<2, 1>::new( None, |accs| { accs[0] @@ -67,7 +64,7 @@ mod tests { }, |mut txs, accs| { txs[0] - .from(accs[1].address) + .from(wallet_a) .to(accs[0].address) .gas(Word::from(1_000_000u64)); }, @@ -76,8 +73,6 @@ mod tests { .unwrap() .into(); - block.sign(&wallets); - const MAX_TXS: usize = 1; const MAX_CALLDATA: usize = 32; const MAX_INNER_BLOCKS: usize = 64; diff --git a/eth-types/src/geth_types.rs b/eth-types/src/geth_types.rs index bfc3c6e73b..3f3a3b82c5 100644 --- a/eth-types/src/geth_types.rs +++ b/eth-types/src/geth_types.rs @@ -8,7 +8,6 @@ use crate::{ use ethers_core::types::{ Eip1559TransactionRequest, Eip2930TransactionRequest, NameOrAddress, TransactionRequest, H256, }; -use ethers_signers::{LocalWallet, Signer}; use halo2_proofs::halo2curves::{group::ff::PrimeField, secp256k1}; use num::Integer; use num_bigint::BigUint; @@ -384,7 +383,7 @@ pub struct GethData { /// Accounts pub accounts: Vec, } - +/* impl GethData { /// Signs transactions with selected wallets pub fn sign(&mut self, wallets: &HashMap) { @@ -403,3 +402,4 @@ impl GethData { } } } +*/ diff --git a/eth-types/src/l2_types.rs b/eth-types/src/l2_types.rs new file mode 100644 index 0000000000..436ab34bda --- /dev/null +++ b/eth-types/src/l2_types.rs @@ -0,0 +1,289 @@ +//! L2 types used to deserialize traces for l2geth. + +use crate::{ + evm_types::{Gas, GasCost, Memory, OpcodeId, ProgramCounter, Stack, Storage}, + Block, GethExecStep, GethExecTrace, Hash, Transaction, Word, H256, +}; +use ethers_core::types::{Address, Bytes, U256, U64}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// l2 block full trace +#[derive(Deserialize, Serialize, Default, Debug, Clone)] +pub struct BlockTrace { + /// chain id + #[serde(rename = "chainID", default)] + pub chain_id: u64, + /// coinbase's status AFTER execution + pub coinbase: AccountProofWrapper, + /// block + pub header: EthBlock, + /// txs + pub transactions: Vec, + /// execution results + #[serde(rename = "executionResults")] + pub execution_results: Vec, + /// storage trace BEFORE execution + #[serde(rename = "storageTrace")] + pub storage_trace: StorageTrace, + /// per-tx storage used by ccc + #[serde(rename = "txStorageTraces", default)] + pub tx_storage_trace: Vec, + /// l1 tx queue + #[serde(rename = "startL1QueueIndex", default)] + pub start_l1_queue_index: u64, +} + +impl From for EthBlock { + fn from(mut b: BlockTrace) -> Self { + let mut txs = Vec::new(); + for (idx, tx_data) in b.transactions.iter_mut().enumerate() { + let tx_idx = Some(U64::from(idx)); + let tx = tx_data.to_eth_tx(b.header.hash, b.header.number, tx_idx); + txs.push(tx) + } + EthBlock { + transactions: txs, + difficulty: 0.into(), + ..b.header + } + } +} + +/// l2 tx trace +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct TransactionTrace { + // FIXME after traces upgraded + /// tx hash + #[serde(default, rename = "txHash")] + pub tx_hash: H256, + /// tx type (in raw from) + #[serde(rename = "type")] + pub type_: u8, + /// nonce + pub nonce: u64, + /// gas limit + pub gas: u64, + #[serde(rename = "gasPrice")] + /// gas price + pub gas_price: U256, + /// from + pub from: Address, + /// to, NONE for creation (0 addr) + pub to: Option
, + /// chain id + #[serde(rename = "chainId")] + pub chain_id: U256, + /// value amount + pub value: U256, + /// call data + pub data: Bytes, + /// is creation + #[serde(rename = "isCreate")] + pub is_create: bool, + /// signature v + pub v: U64, + /// signature r + pub r: U256, + /// signature s + pub s: U256, +} + +impl TransactionTrace { + /// transfer to eth type tx + pub fn to_eth_tx( + &self, + block_hash: Option, + block_number: Option, + transaction_index: Option, + ) -> Transaction { + Transaction { + hash: self.tx_hash, + nonce: U256::from(self.nonce), + block_hash, + block_number, + transaction_index, + from: self.from, + to: self.to, + value: self.value, + gas_price: Some(self.gas_price), + gas: U256::from(self.gas), + input: self.data.clone(), + v: self.v, + r: self.r, + s: self.s, + transaction_type: Some(U64::from(self.type_ as u64)), + access_list: None, + max_priority_fee_per_gas: None, + max_fee_per_gas: None, + chain_id: Some(self.chain_id), + other: Default::default(), + } + } +} + +/// account trie proof in storage proof +pub type AccountTrieProofs = HashMap>; +/// storage trie proof in storage proof +pub type StorageTrieProofs = HashMap>>; + +/// storage trace +#[derive(Deserialize, Serialize, Default, Debug, Clone)] +pub struct StorageTrace { + /// root before + #[serde(rename = "rootBefore")] + pub root_before: Hash, + /// root after + #[serde(rename = "rootAfter")] + pub root_after: Hash, + /// account proofs + pub proofs: Option, + #[serde(rename = "storageProofs", default)] + /// storage proofs for each account + pub storage_proofs: StorageTrieProofs, + #[serde(rename = "deletionProofs", default)] + /// additional deletion proofs + pub deletion_proofs: Vec, +} + +/// ... +pub type EthBlock = Block; + +/// extension of `GethExecTrace`, with compatible serialize form +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct ExecutionResult { + /// L1 fee + #[serde(rename = "l1DataFee", default)] + pub l1_fee: U256, + /// used gas + pub gas: u64, + /// True when the transaction has failed. + pub failed: bool, + /// Return value of execution which is a hex encoded byte array + #[serde(rename = "returnValue", default)] + pub return_value: String, + /// Status of from account AFTER execution + pub from: Option, + /// Status of to account AFTER execution + pub to: Option, + #[serde(rename = "accountAfter", default)] + /// List of accounts' (coinbase etc) status AFTER execution + pub account_after: Vec, + #[serde(rename = "accountCreated")] + /// Status of created account AFTER execution + pub account_created: Option, + #[serde(rename = "poseidonCodeHash")] + /// code hash of called + pub code_hash: Option, + #[serde(rename = "byteCode")] + /// called code + pub byte_code: Option, + #[serde(rename = "structLogs")] + /// Exec steps + pub exec_steps: Vec, +} + +impl From<&ExecutionResult> for GethExecTrace { + fn from(e: &ExecutionResult) -> Self { + let mut struct_logs = Vec::new(); + for exec_step in &e.exec_steps { + let step = exec_step.into(); + struct_logs.push(step) + } + GethExecTrace { + l1_fee: e.l1_fee.as_u64(), + gas: Gas(e.gas), + failed: e.failed, + return_value: e.return_value.clone(), + struct_logs, + } + } +} + +/// extension of `GethExecStep`, with compatible serialize form +#[derive(Deserialize, Serialize, Debug, Clone)] +#[doc(hidden)] +pub struct ExecStep { + pub pc: u64, + pub op: OpcodeId, + pub gas: u64, + #[serde(rename = "gasCost")] + pub gas_cost: u64, + #[serde(default)] + pub refund: u64, + pub depth: isize, + pub error: Option, + pub stack: Option>, + pub memory: Option>, + pub storage: Option>, + #[serde(rename = "extraData")] + pub extra_data: Option, +} + +impl From<&ExecStep> for GethExecStep { + fn from(e: &ExecStep) -> Self { + let stack = e.stack.clone().map_or_else(Stack::new, Stack::from); + let storage = e.storage.clone().map_or_else(Storage::empty, Storage::from); + let memory = e.memory.clone().map_or_else(Memory::default, Memory::from); + + GethExecStep { + pc: ProgramCounter(e.pc as usize), + // FIXME + op: e.op, + gas: Gas(e.gas), + gas_cost: GasCost(e.gas_cost), + refund: Gas(e.refund), + depth: e.depth as u16, + error: e.error.clone(), + stack, + memory, + storage, + } + } +} + +/// extra data for some steps +#[derive(Serialize, Deserialize, Debug, Clone)] +#[doc(hidden)] +pub struct ExtraData { + #[serde(rename = "codeList")] + pub code_list: Option>, + #[serde(rename = "proofList")] + pub proof_list: Option>, +} + +impl ExtraData { + pub fn get_code_at(&self, i: usize) -> Option { + self.code_list.as_ref().and_then(|c| c.get(i)).cloned() + } + + pub fn get_code_hash_at(&self, i: usize) -> Option { + self.get_proof_at(i).and_then(|a| a.poseidon_code_hash) + } + + pub fn get_proof_at(&self, i: usize) -> Option { + self.proof_list.as_ref().and_then(|p| p.get(i)).cloned() + } +} + +/// account wrapper for account status +#[derive(Serialize, Deserialize, Clone, Default, Debug)] +#[doc(hidden)] +pub struct AccountProofWrapper { + pub address: Option
, + pub nonce: Option, + pub balance: Option, + #[serde(rename = "keccakCodeHash")] + pub keccak_code_hash: Option, + #[serde(rename = "poseidonCodeHash")] + pub poseidon_code_hash: Option, + pub storage: Option, +} + +/// storage wrapper for storage status +#[derive(Serialize, Deserialize, Clone, Debug)] +#[doc(hidden)] +pub struct StorageProofWrapper { + pub key: Option, + pub value: Option, +} diff --git a/eth-types/src/lib.rs b/eth-types/src/lib.rs index a2dad03853..04588e6d92 100644 --- a/eth-types/src/lib.rs +++ b/eth-types/src/lib.rs @@ -20,6 +20,7 @@ pub mod error; pub mod bytecode; pub mod evm_types; pub mod geth_types; +pub mod l2_types; pub mod sign_types; pub use bytecode::Bytecode; diff --git a/external-tracer/src/lib.rs b/external-tracer/src/lib.rs index 97cc48b64a..a520de664c 100644 --- a/external-tracer/src/lib.rs +++ b/external-tracer/src/lib.rs @@ -1,5 +1,7 @@ //! This module generates traces by connecting to an external tracer +#[cfg(feature = "scroll")] +use eth_types::l2_types::BlockTrace; use eth_types::{ geth_types::{Account, BlockConstants, Transaction}, Address, Error, GethExecTrace, Word, @@ -25,6 +27,9 @@ pub struct TraceConfig { pub logger_config: LoggerConfig, /// chain config pub chain_config: Option, + /// beginning index of l1 queue + #[cfg(feature = "scroll")] + pub l1_queue_index: u64, } /// Configuration structure for `logger.Config` @@ -88,6 +93,7 @@ impl ChainConfig { } /// Creates a trace for the specified config +#[cfg(not(feature = "scroll"))] pub fn trace(config: &TraceConfig) -> Result, Error> { // Get the trace let trace_string = geth_utils::trace(&serde_json::to_string(&config).unwrap()).map_err( @@ -101,3 +107,30 @@ pub fn trace(config: &TraceConfig) -> Result, Error> { let trace = serde_json::from_str(&trace_string).map_err(Error::SerdeError)?; Ok(trace) } + +/// Creates a l2-trace for the specified config +#[cfg(feature = "scroll")] +pub fn l2trace(config: &TraceConfig) -> Result { + // Get the trace + let trace_string = geth_utils::trace(&serde_json::to_string(&config).unwrap()).map_err( + |error| match error { + geth_utils::Error::TracingError(error) => Error::TracingError(error), + }, + )?; + + log::trace!("trace: {}", trace_string); + + let trace = serde_json::from_str(&trace_string).map_err(Error::SerdeError)?; + Ok(trace) +} + +#[cfg(feature = "scroll")] +pub fn trace(config: &TraceConfig) -> Result, Error> { + let block_trace = l2trace(config)?; + + Ok(block_trace + .execution_results + .iter() + .map(From::from) + .collect::>()) +} diff --git a/geth-utils/l2geth/go.mod b/geth-utils/l2geth/go.mod index f944fa86b2..20ff5bd49b 100644 --- a/geth-utils/l2geth/go.mod +++ b/geth-utils/l2geth/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/holiman/uint256 v1.2.0 github.com/imdario/mergo v0.3.15 - github.com/scroll-tech/go-ethereum v1.10.14-0.20230809083042-eed2b93a7953 + github.com/scroll-tech/go-ethereum v1.10.14-0.20230820103920-be1600f07899 ) require ( diff --git a/geth-utils/l2geth/go.sum b/geth-utils/l2geth/go.sum index 5bce179585..17ddc222d4 100644 --- a/geth-utils/l2geth/go.sum +++ b/geth-utils/l2geth/go.sum @@ -103,8 +103,10 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/scroll-tech/go-ethereum v1.10.14-0.20230809083042-eed2b93a7953 h1:cNFBGyQuk5NVcOZJpYhClSUkvS/OiM6JThNm5eO5Qxw= -github.com/scroll-tech/go-ethereum v1.10.14-0.20230809083042-eed2b93a7953/go.mod h1:DiN3p2inoXOxGffxSswDKqWjQ7bU+Mp0c9v0XQXKmaA= +github.com/scroll-tech/go-ethereum v1.10.14-0.20230815053544-f828d88a8c6a h1:MAFYEgBGPwKkJv34Ep/tWxnDX9oOVaOJSSVjKFcjUYA= +github.com/scroll-tech/go-ethereum v1.10.14-0.20230815053544-f828d88a8c6a/go.mod h1:DiN3p2inoXOxGffxSswDKqWjQ7bU+Mp0c9v0XQXKmaA= +github.com/scroll-tech/go-ethereum v1.10.14-0.20230820103920-be1600f07899 h1:bExZNuOgv3hWXajRTDzhk9wBBK9HJbfp2AQ18IcsFok= +github.com/scroll-tech/go-ethereum v1.10.14-0.20230820103920-be1600f07899/go.mod h1:DiN3p2inoXOxGffxSswDKqWjQ7bU+Mp0c9v0XQXKmaA= github.com/scroll-tech/zktrie v0.6.0 h1:xLrMAO31Yo2BiPg1jtYKzcjpEFnXy8acbB7iIsyshPs= github.com/scroll-tech/zktrie v0.6.0/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= diff --git a/geth-utils/l2geth/trace.go b/geth-utils/l2geth/trace.go index 53de36cc0a..3fadbadaed 100644 --- a/geth-utils/l2geth/trace.go +++ b/geth-utils/l2geth/trace.go @@ -13,6 +13,7 @@ import ( "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/core/vm" "github.com/scroll-tech/go-ethereum/params" + "github.com/scroll-tech/go-ethereum/trie" ) type Block struct { @@ -52,7 +53,8 @@ type Transaction struct { } type TraceConfig struct { - ChainID uint64 `json:"chain_id"` + ChainID uint64 `json:"chain_id"` + StartL1QueueIndex uint64 `json:"l1_queue_index"` // HistoryHashes contains most recent 256 block hashes in history, // where the lastest one is at HistoryHashes[len(HistoryHashes)-1]. HistoryHashes []*hexutil.Big `json:"history_hashes"` @@ -120,7 +122,7 @@ func transferTxs(txs []Transaction) types.Transactions { return types.Transactions(t_txs) } -func L2Trace(config TraceConfig) (*types.BlockTrace, error) { +func Trace(config TraceConfig) (*types.BlockTrace, error) { chainConfig := params.ChainConfig{ ChainID: new(big.Int).SetUint64(config.ChainID), @@ -198,8 +200,12 @@ func L2Trace(config TraceConfig) (*types.BlockTrace, error) { } block := types.NewBlockWithHeader(header).WithBody(txs, nil) + trieCfg := &trie.Config{Zktrie: true} // Setup state db with accounts from argument - stateDB, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + stateDB, _ := state.New( + common.Hash{}, + state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), trieCfg), + nil) for address, account := range config.Accounts { stateDB.SetNonce(address, uint64(account.Nonce)) stateDB.SetCode(address, account.Code) @@ -210,30 +216,35 @@ func L2Trace(config TraceConfig) (*types.BlockTrace, error) { stateDB.SetState(address, key, value) } } - stateDB.Finalise(true) - traceEnv := core.CreateTraceEnvDirect( + rootBefore, err := stateDB.Commit(true) + if err != nil { + return nil, err + } + + traceEnv := core.CreateTraceEnvHelper( &chainConfig, - &vm.LogConfig{ - EnableMemory: true, - EnableReturnData: true, - }, + config.LoggerConfig, blockCtx, + config.StartL1QueueIndex, blockCtx.Coinbase, stateDB, - parent, + rootBefore, block, true, ) - return traceEnv.GetBlockTrace(block) -} + trace, err := traceEnv.GetBlockTrace(block) + if err != nil { + return nil, err + } -func Trace(config TraceConfig) ([]*types.ExecutionResult, error) { - l2trace, err := L2Trace(config) + rootAfter, err := stateDB.Commit(true) if err != nil { return nil, err } - return l2trace.ExecutionResults, nil + trace.StorageTrace.RootAfter = rootAfter + trace.Header.Root = rootAfter + return trace, nil } diff --git a/geth-utils/src/lib.rs b/geth-utils/src/lib.rs index 19e0749197..830d6e999a 100644 --- a/geth-utils/src/lib.rs +++ b/geth-utils/src/lib.rs @@ -9,6 +9,35 @@ use std::{ extern "C" { fn CreateTrace(str: *const c_char) -> *const c_char; fn FreeString(str: *const c_char); + #[cfg(feature = "scroll")] + fn CreateL2Trace(str: *const c_char) -> *const c_char; +} + +/// Creates the l2 trace +#[cfg(feature = "scroll")] +pub fn l2trace(config: &str) -> Result { + // Create a string we can pass into Go + let c_config = CString::new(config).expect("invalid config"); + + // Generate the trace externally + let result = unsafe { CreateL2Trace(c_config.as_ptr()) }; + + // Convert the returned string to something we can use in Rust again. + // Also make sure the returned data is copied to rust managed memory. + let c_result = unsafe { CStr::from_ptr(result) }; + let result = c_result + .to_str() + .expect("Error translating EVM trace from library") + .to_string(); + + // We can now free the returned string (memory managed by Go) + unsafe { FreeString(c_result.as_ptr()) }; + + // Return the trace + match result.is_empty() || result.starts_with("Failed") { + true => Err(Error::TracingError(result)), + false => Ok(result), + } } /// Creates the trace diff --git a/mock/src/block.rs b/mock/src/block.rs index 46e734afa1..a5db0df131 100644 --- a/mock/src/block.rs +++ b/mock/src/block.rs @@ -1,6 +1,10 @@ //! Mock Block definition and builder related methods. -use crate::{MockTransaction, MOCK_BASEFEE, MOCK_CHAIN_ID, MOCK_DIFFICULTY, MOCK_GASLIMIT}; +#[cfg(not(feature = "scroll"))] +use crate::MOCK_DIFFICULTY; +#[cfg(feature = "scroll")] +use crate::MOCK_DIFFICULTY_L2GETH as MOCK_DIFFICULTY; +use crate::{MockTransaction, MOCK_BASEFEE, MOCK_CHAIN_ID, MOCK_GASLIMIT}; use eth_types::{Address, Block, Bytes, Hash, Transaction, Word, H64, U64}; use ethers_core::types::{Bloom, OtherFields}; diff --git a/mock/src/test_ctx.rs b/mock/src/test_ctx.rs index fed2944737..3958b7c11f 100644 --- a/mock/src/test_ctx.rs +++ b/mock/src/test_ctx.rs @@ -1,11 +1,17 @@ //! Mock types and functions to generate Test enviroments for ZKEVM tests use crate::{eth, MockAccount, MockBlock, MockTransaction}; +#[cfg(feature = "scroll")] +use eth_types::l2_types::BlockTrace; use eth_types::{ geth_types::{Account, BlockConstants, GethData}, - BigEndianHash, Block, Bytecode, Error, GethExecTrace, Transaction, Word, H256, + BigEndianHash, Block, Bytecode, Error, Transaction, Word, H256, }; -use external_tracer::{trace, TraceConfig}; +#[cfg(feature = "scroll")] +use external_tracer::l2trace; +#[cfg(not(feature = "scroll"))] +use external_tracer::trace; +use external_tracer::TraceConfig; use helpers::*; use itertools::Itertools; @@ -89,6 +95,9 @@ pub struct TestContext { pub eth_block: eth_types::Block, /// Execution Trace from geth pub geth_traces: Vec, + + #[cfg(feature = "scroll")] + block_trace: BlockTrace, } impl From> for GethData { @@ -173,7 +182,7 @@ impl TestContext { .try_into() .expect("Mismatched acc len"); - let geth_traces = gen_geth_traces( + let trace_config = gen_trace_config( chain_id, block.clone(), accounts.to_vec(), @@ -181,12 +190,27 @@ impl TestContext { logger_config, )?; + #[cfg(feature = "scroll")] + let block_trace = l2trace(&trace_config)?; + + #[cfg(feature = "scroll")] + let geth_traces = block_trace + .execution_results + .iter() + .map(From::from) + .collect::>(); + + #[cfg(not(feature = "scroll"))] + let geth_traces = trace(&trace_config)?; + Ok(Self { chain_id, accounts, history_hashes: history_hashes.unwrap_or_default(), eth_block: block, geth_traces, + #[cfg(feature = "scroll")] + block_trace, }) } @@ -216,6 +240,12 @@ impl TestContext { ) } + /// obtain the full l2 block trace + #[cfg(feature = "scroll")] + pub fn l2_trace(&self) -> &BlockTrace { + &self.block_trace + } + /// Returns a simple TestContext setup with a single tx executing the /// bytecode passed as parameters. The balances of the 2 accounts and /// addresses are the ones used in [`TestContext:: @@ -231,16 +261,16 @@ impl TestContext { } } -/// Generates execution traces for the transactions included in the provided +/// Generates config to generating execution traces for the transactions included in the provided /// Block -pub fn gen_geth_traces( +pub fn gen_trace_config( chain_id: u64, block: Block, accounts: Vec, history_hashes: Option>, logger_config: LoggerConfig, -) -> Result, Error> { - let trace_config = TraceConfig { +) -> Result { + Ok(TraceConfig { chain_id, history_hashes: history_hashes.unwrap_or_default(), block_constants: BlockConstants::try_from(&block)?, @@ -258,9 +288,9 @@ pub fn gen_geth_traces( chain_config: Some(external_tracer::ChainConfig::shanghai()), #[cfg(not(feature = "shanghai"))] chain_config: None, - }; - let traces = trace(&trace_config)?; - Ok(traces) + #[cfg(feature = "scroll")] + l1_queue_index: 0, + }) } /// Collection of helper functions which contribute to specific rutines on the diff --git a/mock/src/transaction.rs b/mock/src/transaction.rs index fa9eb2e037..c42974c676 100644 --- a/mock/src/transaction.rs +++ b/mock/src/transaction.rs @@ -334,7 +334,6 @@ impl MockTransaction { pub fn build(&mut self) -> Self { let tx = TransactionRequest::new() .from(self.from.address()) - .to(self.to.clone().unwrap_or_default().address()) .nonce(self.nonce) .value(self.value) .data(self.input.clone()) @@ -342,6 +341,12 @@ impl MockTransaction { .gas_price(self.gas_price) .chain_id(self.chain_id); + let tx = if let Some(to_addr) = self.to.clone() { + tx.to(to_addr.address()) + } else { + tx + }; + match (self.v, self.r, self.s) { (None, None, None) => { // Compute sig params and set them in case we have a wallet as `from` attr. diff --git a/testool/src/statetest/executor.rs b/testool/src/statetest/executor.rs index e252ccea72..d7cc293234 100644 --- a/testool/src/statetest/executor.rs +++ b/testool/src/statetest/executor.rs @@ -192,6 +192,8 @@ fn into_traceconfig(st: StateTest) -> (String, TraceConfig, StateTestResult) { chain_config: Some(external_tracer::ChainConfig::shanghai()), #[cfg(not(feature = "shanghai"))] chain_config: None, + #[cfg(feature = "scroll")] + l1_queue_index: 0, }, st.result, ) @@ -296,7 +298,7 @@ pub fn run_test( ); // process the transaction - let mut geth_data = eth_types::geth_types::GethData { + let geth_data = eth_types::geth_types::GethData { chain_id: trace_config.chain_id, history_hashes: trace_config.history_hashes.clone(), geth_traces: geth_traces.clone(), @@ -342,7 +344,8 @@ pub fn run_test( .copy_checks(None) .run(); } else { - geth_data.sign(&wallets); + // we should have signed tx in into_traceconfig + // geth_data.sign(&wallets); let circuits_params = CircuitsParams { max_txs: MAX_TXS, diff --git a/zkevm-circuits/src/pi_circuit/test.rs b/zkevm-circuits/src/pi_circuit/test.rs index 7bcf49b66a..431bf4c8a0 100644 --- a/zkevm-circuits/src/pi_circuit/test.rs +++ b/zkevm-circuits/src/pi_circuit/test.rs @@ -5,7 +5,11 @@ use halo2_proofs::{ dev::{MockProver, VerifyFailure}, halo2curves::bn256::Fr, }; -use mock::{test_ctx::helpers::tx_from_1_to_0, CORRECT_MOCK_TXS, MOCK_CHAIN_ID, MOCK_DIFFICULTY}; +#[cfg(not(feature = "scroll"))] +use mock::MOCK_DIFFICULTY; +#[cfg(feature = "scroll")] +use mock::MOCK_DIFFICULTY_L2GETH as MOCK_DIFFICULTY; +use mock::{test_ctx::helpers::tx_from_1_to_0, CORRECT_MOCK_TXS, MOCK_CHAIN_ID}; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; use std::env::set_var; diff --git a/zkevm-circuits/src/super_circuit/test.rs b/zkevm-circuits/src/super_circuit/test.rs index 13995d52ef..279e58c7e6 100644 --- a/zkevm-circuits/src/super_circuit/test.rs +++ b/zkevm-circuits/src/super_circuit/test.rs @@ -1,19 +1,24 @@ +#![allow(unused_imports)] pub use super::*; use bus_mapping::{ - circuit_input_builder::keccak_inputs, + circuit_input_builder::CircuitInputBuilder, evm::{OpcodeId, PrecompileCallArgs}, precompile::PrecompileCalls, }; use ethers_signers::{LocalWallet, Signer}; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; use log::error; -use mock::{eth, TestContext, MOCK_CHAIN_ID, MOCK_DIFFICULTY}; -use mpt_zktrie::state::builder::HASH_SCHEME_DONE; +#[cfg(not(feature = "scroll"))] +use mock::MOCK_DIFFICULTY; +#[cfg(feature = "scroll")] +use mock::MOCK_DIFFICULTY_L2GETH as MOCK_DIFFICULTY; +use mock::{eth, TestContext, MOCK_CHAIN_ID}; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; -use std::{collections::HashMap, env::set_var}; +use std::env::set_var; -use eth_types::{address, bytecode, geth_types::GethData, word, Bytecode, ToWord, Word}; +use crate::witness::block_apply_mpt_state; +use eth_types::{address, bytecode, l2_types::BlockTrace, word, Bytecode, ToWord, Word}; #[test] fn super_circuit_degree() { @@ -24,13 +29,14 @@ fn super_circuit_degree() { assert!(cs.degree() <= 9); } +#[cfg(feature = "scroll")] fn test_super_circuit< const MAX_TXS: usize, const MAX_CALLDATA: usize, const MAX_INNER_BLOCKS: usize, const MOCK_RANDOMNESS: u64, >( - geth_data: GethData, + l2_trace: BlockTrace, circuits_params: CircuitsParams, ) { set_var("COINBASE", "0x0000000000000000000000000000000000000000"); @@ -39,22 +45,17 @@ fn test_super_circuit< MOCK_DIFFICULTY.to_big_endian(&mut difficulty_be_bytes); set_var("DIFFICULTY", hex::encode(difficulty_be_bytes)); - let block_data = BlockData::new_from_geth_data_with_params(geth_data, circuits_params); - let mut builder = block_data.new_circuit_input_builder(); + let mut builder = + CircuitInputBuilder::new_from_l2_trace(circuits_params, &l2_trace, false, false) + .expect("could not handle block tx"); + builder - .handle_block(&block_data.eth_block, &block_data.geth_traces) - .expect("could not handle block tx"); + .finalize_building() + .expect("could not finalize building block"); + let mut block = block_convert(&builder.block, &builder.code_db).unwrap(); block.randomness = Fr::from(MOCK_RANDOMNESS); - - // Mock fill state roots - assert!(*HASH_SCHEME_DONE); - block.mpt_updates.mock_fill_state_roots(); - block.prev_state_root = block.mpt_updates.old_root(); - - // Recompute keccak inputs for updated prev_state_root. - builder.block.prev_state_root = block.mpt_updates.old_root(); - block.keccak_inputs = keccak_inputs(&builder.block, &builder.code_db).unwrap(); + block_apply_mpt_state(&mut block, &builder.mpt_init_state); let active_row_num =SuperCircuit::< Fr, @@ -100,7 +101,8 @@ fn callee_bytecode(is_return: bool, offset: u64, length: u64) -> Bytecode { code } -fn block_1tx_deploy() -> GethData { +#[cfg(feature = "scroll")] +fn block_1tx_deploy() -> BlockTrace { let mut rng = ChaCha20Rng::seed_from_u64(2); let chain_id = *MOCK_CHAIN_ID; @@ -108,27 +110,23 @@ fn block_1tx_deploy() -> GethData { let wallet_a = LocalWallet::new(&mut rng).with_chain_id(chain_id); let addr_a = wallet_a.address(); - let mut wallets = HashMap::new(); - wallets.insert(wallet_a.address(), wallet_a); - let tx_input = callee_bytecode(true, 300, 20).code(); - let mut block: GethData = TestContext::<2, 1>::new( + TestContext::<2, 1>::new( Some(vec![Word::zero()]), |accs| { accs[0].address(addr_a).balance(eth(10)); }, - |mut txs, accs| { - txs[0].from(accs[0].address).input(tx_input.into()); + |mut txs, _accs| { + txs[0].from(wallet_a).input(tx_input.into()); }, |block, _tx| block.number(0xcafeu64), ) .unwrap() - .into(); - block.sign(&wallets); - block + .l2_trace() + .clone() } -pub(crate) fn block_1tx() -> GethData { +fn block_1tx_ctx() -> TestContext<2, 1> { let mut rng = ChaCha20Rng::seed_from_u64(2); let chain_id = *MOCK_CHAIN_ID; @@ -143,10 +141,7 @@ pub(crate) fn block_1tx() -> GethData { let addr_a = wallet_a.address(); let addr_b = address!("0x000000000000000000000000000000000000BBBB"); - let mut wallets = HashMap::new(); - wallets.insert(wallet_a.address(), wallet_a); - - let mut block: GethData = TestContext::<2, 1>::new( + TestContext::new( Some(vec![Word::zero()]), |accs| { accs[0] @@ -157,19 +152,25 @@ pub(crate) fn block_1tx() -> GethData { }, |mut txs, accs| { txs[0] - .from(accs[1].address) + .from(wallet_a) .to(accs[0].address) .gas(Word::from(1_000_000u64)); }, |block, _tx| block.number(0xcafeu64), ) .unwrap() - .into(); - block.sign(&wallets); - block } -pub(crate) fn block_2tx() -> GethData { +#[cfg(feature = "scroll")] +fn block_1tx_trace() -> BlockTrace { + block_1tx_ctx().l2_trace().clone() +} + +pub(crate) fn block_1tx() -> GethData { + block_1tx_ctx().into() +} + +fn block_2tx_ctx() -> TestContext<2, 2> { let mut rng = ChaCha20Rng::seed_from_u64(2); let chain_id = *MOCK_CHAIN_ID; @@ -184,10 +185,7 @@ pub(crate) fn block_2tx() -> GethData { let addr_a = wallet_a.address(); let addr_b = address!("0x000000000000000000000000000000000000BBBB"); - let mut wallets = HashMap::new(); - wallets.insert(wallet_a.address(), wallet_a); - - let mut block: GethData = TestContext::<2, 2>::new( + TestContext::new( Some(vec![Word::zero()]), |accs| { accs[0] @@ -198,23 +196,30 @@ pub(crate) fn block_2tx() -> GethData { }, |mut txs, accs| { txs[0] - .from(accs[1].address) + .from(wallet_a.clone()) .to(accs[0].address) .gas(Word::from(1_000_000u64)); txs[1] - .from(accs[1].address) + .from(wallet_a.clone()) .to(accs[0].address) .gas(Word::from(1_000_000u64)); }, |block, _tx| block.number(0xcafeu64), ) .unwrap() - .into(); - block.sign(&wallets); - block } -pub(crate) fn block_ec_ops() -> GethData { +#[cfg(feature = "scroll")] +fn block_2tx_trace() -> BlockTrace { + block_2tx_ctx().l2_trace().clone() +} + +pub(crate) fn block_2tx() -> GethData { + block_2tx_ctx().into() +} + +#[cfg(feature = "scroll")] +fn block_ec_ops() -> BlockTrace { let mut rng = ChaCha20Rng::seed_from_u64(2); let chain_id = *MOCK_CHAIN_ID; @@ -335,6 +340,40 @@ pub(crate) fn block_ec_ops() -> GethData { ..Default::default() } .with_call_op(OpcodeId::DELEGATECALL); + let bytecode_modexp_256 = PrecompileCallArgs { + name: "modexp length in u256", + setup_code: bytecode! { + // Base size + PUSH1(0x20) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x20) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x20) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000008")) + PUSH1(0x60) + MSTORE + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000009")) + PUSH1(0x80) + MSTORE + PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) + PUSH1(0xA0) + MSTORE + }, + call_data_offset: 0x0.into(), + call_data_length: 0xc0.into(), + ret_offset: 0xe0.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + } + .with_call_op(OpcodeId::STATICCALL); let wallet_a = LocalWallet::new(&mut rng).with_chain_id(chain_id); @@ -342,15 +381,13 @@ pub(crate) fn block_ec_ops() -> GethData { let addr_b = address!("0x000000000000000000000000000000000000BBBB"); let addr_c = address!("0x000000000000000000000000000000000000CCCC"); let addr_d = address!("0x000000000000000000000000000000000000DDDD"); + let addr_e = address!("0x000000000000000000000000000000000000EEEE"); - let mut wallets = HashMap::new(); - wallets.insert(wallet_a.address(), wallet_a); - - // 4 accounts and 3 txs. - let mut block: GethData = TestContext::<4, 3>::new( + // 5 accounts and 4 txs. + TestContext::<5, 4>::new( Some(vec![Word::zero()]), |accs| { - accs[0].address(addr_a).balance(Word::from(1u64 << 20)); + accs[0].address(addr_a).balance(Word::from(1u64 << 24)); accs[1] .address(addr_b) .balance(Word::from(1u64 << 20)) @@ -363,27 +400,34 @@ pub(crate) fn block_ec_ops() -> GethData { .address(addr_d) .balance(Word::from(1u64 << 20)) .code(bytecode_ec_pairing); + accs[4] + .address(addr_e) + .balance(Word::from(1u64 << 20)) + .code(bytecode_modexp_256); }, |mut txs, accs| { txs[0] - .from(accs[0].address) + .from(wallet_a.clone()) .to(accs[1].address) .gas(Word::from(1_000_000u64)); txs[1] - .from(accs[0].address) + .from(wallet_a.clone()) .to(accs[2].address) .gas(Word::from(1_000_000u64)); txs[2] - .from(accs[0].address) + .from(wallet_a.clone()) .to(accs[3].address) .gas(Word::from(1_000_000u64)); + txs[3] + .from(wallet_a.clone()) + .to(accs[4].address) + .gas(Word::from(1_000_000u64)); }, |block, _tx| block.number(0xcafeu64), ) .unwrap() - .into(); - block.sign(&wallets); - block + .l2_trace() + .clone() } const TEST_MOCK_RANDOMNESS: u64 = 0x100; @@ -394,7 +438,7 @@ const TEST_MOCK_RANDOMNESS: u64 = 0x100; #[cfg(feature = "scroll")] #[test] fn serial_test_super_circuit_1tx_1max_tx() { - let block = block_1tx(); + let block = block_1tx_trace(); const MAX_TXS: usize = 1; const MAX_CALLDATA: usize = 256; const MAX_INNER_BLOCKS: usize = 1; @@ -454,7 +498,7 @@ fn serial_test_super_circuit_1tx_deploy_2max_tx() { #[cfg(feature = "scroll")] #[test] fn serial_test_super_circuit_1tx_2max_tx() { - let block = block_1tx(); + let block = block_1tx_trace(); const MAX_TXS: usize = 2; const MAX_CALLDATA: usize = 256; const MAX_INNER_BLOCKS: usize = 1; @@ -483,7 +527,7 @@ fn serial_test_super_circuit_1tx_2max_tx() { #[cfg(feature = "scroll")] #[test] fn serial_test_super_circuit_2tx_4max_tx() { - let block = block_2tx(); + let block = block_2tx_trace(); const MAX_TXS: usize = 4; const MAX_CALLDATA: usize = 320; const MAX_INNER_BLOCKS: usize = 1; @@ -514,7 +558,7 @@ fn serial_test_super_circuit_2tx_4max_tx() { #[cfg(feature = "scroll")] #[test] fn serial_test_super_circuit_2tx_2max_tx() { - let block = block_2tx(); + let block = block_2tx_trace(); const MAX_TXS: usize = 2; const MAX_CALLDATA: usize = 256; const MAX_INNER_BLOCKS: usize = 1; @@ -539,15 +583,16 @@ fn serial_test_super_circuit_2tx_2max_tx() { ); } +#[ignore] #[cfg(feature = "scroll")] #[test] -fn test_super_circuit_ec_ops_txs() { +fn serial_test_super_circuit_ec_ops_txs() { let block = block_ec_ops(); - const MAX_TXS: usize = 3; + const MAX_TXS: usize = 4; const MAX_CALLDATA: usize = 320; const MAX_INNER_BLOCKS: usize = 1; const MAX_RWS: usize = 1024; - const MAX_COPY_ROWS: usize = 2048; + const MAX_COPY_ROWS: usize = 16384; // precompile require many copies let circuits_params = CircuitsParams { max_txs: MAX_TXS, max_calldata: MAX_CALLDATA, @@ -557,7 +602,9 @@ fn test_super_circuit_ec_ops_txs() { max_mpt_rows: 2048, max_poseidon_rows: 2048, max_evm_rows: 0, - max_keccak_rows: 0, + // modexp ref this to decide its ability, we + // need at least one (~25000 rows) + max_keccak_rows: 30000, max_inner_blocks: MAX_INNER_BLOCKS, max_exp_steps: 256, max_rlp_rows: 800, diff --git a/zktrie/Cargo.toml b/zktrie/Cargo.toml index e33562ca81..3114c13089 100644 --- a/zktrie/Cargo.toml +++ b/zktrie/Cargo.toml @@ -11,7 +11,6 @@ halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.gi mpt-circuits = { package = "halo2-mpt-circuits", git = "https://github.com/scroll-tech/mpt-circuit.git", tag = "v0.5.1" } zktrie = { git = "https://github.com/scroll-tech/zktrie.git", branch = "v0.6" } hash-circuit = { package = "poseidon-circuit", git = "https://github.com/scroll-tech/poseidon-circuit.git", branch = "scroll-dev-0723"} -bus-mapping = { path = "../bus-mapping" } eth-types = { path = "../eth-types" } lazy_static = "1.4" num-bigint = { version = "0.4" } diff --git a/zktrie/src/state.rs b/zktrie/src/state.rs index 2e801eb64a..8d1532773d 100644 --- a/zktrie/src/state.rs +++ b/zktrie/src/state.rs @@ -1,7 +1,5 @@ //! Represent the storage state under zktrie as implement - -use bus_mapping::state_db::{Account, StateDB}; -use eth_types::{Address, Hash, Word, H256, U256}; +use eth_types::{Address, Hash, Word}; use mpt_circuits::MPTProofType; use std::{collections::HashMap, io::Error}; @@ -9,16 +7,17 @@ pub use zktrie::{Hash as ZkTrieHash, ZkMemoryDb, ZkTrie, ZkTrieNode}; pub mod builder; pub mod witness; +pub use builder::{AccountData, StorageData}; use std::{cell::RefCell, fmt, rc::Rc}; /// represent a storage state being applied in specified block #[derive(Clone, Default)] pub struct ZktrieState { - sdb: StateDB, + accounts: HashMap, + account_storages: HashMap<(Address, Word), StorageData>, zk_db: Rc>, trie_root: ZkTrieHash, - accounts: HashMap, } unsafe impl Send for ZktrieState {} @@ -27,10 +26,8 @@ impl fmt::Debug for ZktrieState { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "ZktrieState: {{sdb: {:?}, trie: {:x?}, accounts: {:?}}}", - self.sdb, - self.trie_root, - self.accounts.keys() + "ZktrieState: {{accounts: {:?}, storage: {:?}, trie: {:x?}}}", + self.accounts, self.account_storages, self.trie_root, ) } } @@ -42,13 +39,13 @@ impl ZktrieState { } /// help to query account data - pub fn state(&self) -> &StateDB { - &self.sdb + pub fn state(&self) -> &HashMap { + &self.accounts } - /// ... - pub fn set_state(&mut self, statedb: StateDB) { - self.sdb = statedb + /// help to query storage data + pub fn storage(&self) -> &HashMap<(Address, Word), StorageData> { + &self.account_storages } /// construct from external data @@ -65,41 +62,23 @@ impl ZktrieState { let zk_db = ZkMemoryDb::default(); Self { - sdb: Default::default(), zk_db: Rc::new(RefCell::new(zk_db)), trie_root: state_root.0, - accounts: Default::default(), + ..Default::default() } } - /// construct from external data - pub fn from_trace<'d, BYTES>( - state_root: Hash, - account_proofs: impl Iterator + Clone, - storage_proofs: impl Iterator + Clone, - ) -> Result - where - BYTES: IntoIterator, - { - Self::from_trace_with_additional( - state_root, - account_proofs, - storage_proofs, - std::iter::empty(), - ) - } - - /// .. - pub fn update_statedb_from_proofs<'d, BYTES>( + /// incremental updating for account from external data, catch each written of new account in + /// tries + pub fn update_account_from_proofs<'d, BYTES>( &mut self, - account_proofs: impl Iterator + Clone, - storage_proofs: impl Iterator + Clone, - _additional_proofs: impl Iterator + Clone, + account_proofs: impl Iterator, + mut on_account: impl FnMut(&Address, &AccountData) -> Result<(), Error> + 'd, ) -> Result<(), Error> where BYTES: IntoIterator, { - use builder::{AccountProof, BytesArray, StorageProof}; + use builder::{AccountProof, BytesArray}; for (addr, bytes) in account_proofs { let acc_proof = builder::verify_proof_leaf( @@ -107,8 +86,8 @@ impl ZktrieState { &builder::extend_address_to_h256(addr), ); let acc_data = acc_proof.data; - let (exists, acc) = self.sdb.get_account(addr); - if exists { + let acc = self.accounts.get(addr); + if acc.is_some() { log::trace!( "skip trace account into sdb: addr {:?}, new {:?}, keep old: {:?}", addr, @@ -119,97 +98,67 @@ impl ZktrieState { } if acc_proof.key.is_some() { log::trace!("trace account into sdb: {:?} => {:?}", addr, acc_data); - self.sdb.set_account( - addr, - Account { - nonce: acc_data.nonce.into(), - balance: acc_data.balance, - code_hash: acc_data.poseidon_code_hash, - keccak_code_hash: acc_data.keccak_code_hash, - - code_size: acc_data.code_size.into(), - storage: Default::default(), - }, - ); - self.accounts.insert(*addr, acc_data.storage_root.0); + on_account(addr, &acc_data)?; + self.accounts.insert(*addr, acc_data); } else { - self.sdb.set_account(addr, Account::zero()); - self.accounts.insert(*addr, H256::zero().0); + self.accounts.insert(*addr, Default::default()); } } - for (addr, key, bytes) in storage_proofs { - let (exists, old_value) = self.sdb.get_storage(addr, key); - let old_value = *old_value; - if exists { + Ok(()) + } + + /// incremental updating for storage from external data, catch each written of new (non-zero) + /// value in tries + pub fn update_storage_from_proofs<'d, BYTES>( + &mut self, + storage_proofs: impl Iterator, + mut on_storage: impl FnMut(&(Address, Word), &StorageData) -> Result<(), Error> + 'd, + ) -> Result<(), Error> + where + BYTES: IntoIterator, + { + use builder::{BytesArray, StorageProof}; + + for (&addr, &key, bytes) in storage_proofs { + let storage_key: (Address, Word) = (addr, key); + let old_value = self.account_storages.get(&storage_key); + if old_value.is_some() { continue; } - let (_, acc) = self.sdb.get_account_mut(addr); let mut key_buf = [0u8; 32]; key.to_big_endian(key_buf.as_mut_slice()); let bytes_array = BytesArray(bytes.into_iter()); let store_proof = builder::verify_proof_leaf(StorageProof::try_from(bytes_array)?, &key_buf); if store_proof.key.is_some() { - if !store_proof.data.as_ref().is_zero() { - log::trace!( - "insert storage addr {:?} key {:?} value {:?}", - addr, - key, - *store_proof.data.as_ref() - ); - acc.storage.insert(*key, *store_proof.data.as_ref()); - } else { - log::trace!( - "set storage to 0, addr {:?} key {:?} old value {:?}", - addr, - key, - old_value - ); - //acc.storage.remove(key); - acc.storage.insert(*key, U256::zero()); - } - } else { log::trace!( - "clear storage addr {:?} key {:?} old value {:?}", - addr, - key, - old_value + "insert storage key {:?} value {:?}", + storage_key, + *store_proof.data.as_ref() ); - // acc.storage.remove(key); - acc.storage.insert(*key, U256::zero()); + + on_storage(&storage_key, &store_proof.data)?; + self.account_storages.insert(storage_key, store_proof.data); + } else { + log::trace!("insert storage key {:?} for zero", storage_key,); + self.account_storages + .insert(storage_key, Default::default()); } } Ok(()) } - /// .. - pub fn update_from_proofs<'d, BYTES>( - &mut self, - account_proofs: impl Iterator + Clone, - storage_proofs: impl Iterator + Clone, - additional_proofs: impl Iterator + Clone, - ) -> Result<(), Error> - where - BYTES: IntoIterator, - { - self.update_statedb_from_proofs( - account_proofs.clone(), - storage_proofs.clone(), - additional_proofs.clone(), - )?; - self.update_nodes_from_proofs(account_proofs, storage_proofs, additional_proofs)?; - Ok(()) - } - /// .. - pub fn update_nodes_from_proofs<'d, BYTES>( + + /// incremental updating nodes in db from external data + pub fn update_nodes_from_proofs<'d, BYTES1, BYTES2>( &mut self, - account_proofs: impl Iterator + Clone, - storage_proofs: impl Iterator + Clone, - additional_proofs: impl Iterator + Clone, - ) -> Result<(), Error> - where - BYTES: IntoIterator, + account_proofs: impl Iterator, + storage_proofs: impl Iterator, + additional_proofs: impl Iterator, + ) where + BYTES1: IntoIterator, + BYTES2: IntoIterator, { let proofs = account_proofs .flat_map(|(_, bytes)| bytes) @@ -219,27 +168,33 @@ impl ZktrieState { for bytes in proofs { zk_db.add_node_bytes(bytes).unwrap(); } - Ok(()) } /// construct from external data, with additional proofs (trie node) can be /// provided - pub fn from_trace_with_additional<'d, BYTES>( + pub fn from_trace_with_additional<'d, BYTES1, BYTES2>( state_root: Hash, - account_proofs: impl Iterator + Clone, - storage_proofs: impl Iterator + Clone, - additional_proofs: impl Iterator + Clone, + account_proofs: impl Iterator + Clone, + storage_proofs: impl Iterator + Clone, + additional_proofs: impl Iterator, + light_mode: bool, ) -> Result where - BYTES: IntoIterator, + BYTES1: IntoIterator, + BYTES2: IntoIterator, { let mut state = ZktrieState::construct(state_root); - state.update_statedb_from_proofs( - account_proofs.clone(), - storage_proofs.clone(), - additional_proofs.clone(), - )?; - state.update_nodes_from_proofs(account_proofs, storage_proofs, additional_proofs)?; + if !light_mode { + // a lot of poseidon computation + state.update_nodes_from_proofs( + account_proofs.clone(), + storage_proofs.clone(), + additional_proofs, + ); + } + state.update_account_from_proofs(account_proofs, |_, _| Ok(()))?; + state.update_storage_from_proofs(storage_proofs, |_, _| Ok(()))?; + Ok(state) } } diff --git a/zktrie/src/state/builder.rs b/zktrie/src/state/builder.rs index 70e550eeea..693d61263b 100644 --- a/zktrie/src/state/builder.rs +++ b/zktrie/src/state/builder.rs @@ -77,13 +77,20 @@ const NODE_TYPE_LEAF: u8 = 4; const NODE_TYPE_EMPTY: u8 = 5; const SECURE_HASH_DOMAIN: u64 = 512; +/// AccountData #[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] -pub(crate) struct AccountData { +pub struct AccountData { + /// nonce pub nonce: u64, + /// balance pub balance: U256, + /// keccak code hash pub keccak_code_hash: H256, + /// poseidon code hash pub poseidon_code_hash: H256, + /// code size pub code_size: u64, + /// storage root pub storage_root: H256, } @@ -140,8 +147,9 @@ impl CanRead for AccountData { } } +/// Storage data #[derive(Debug, Default, Clone)] -pub(crate) struct StorageData(Word); +pub struct StorageData(Word); impl AsRef for StorageData { fn as_ref(&self) -> &Word { diff --git a/zktrie/src/state/test.rs b/zktrie/src/state/test.rs index 0d200c1c1d..768d3e8a16 100644 --- a/zktrie/src/state/test.rs +++ b/zktrie/src/state/test.rs @@ -30,7 +30,7 @@ fn build_state_from_sample(sample_file: &str) -> (ZktrieState, Hash) { .storage_trace; ( - ZktrieState::from_trace( + ZktrieState::from_trace_with_additional( trace.root_before, trace .proofs @@ -42,6 +42,8 @@ fn build_state_from_sample(sample_file: &str) -> (ZktrieState, Hash) { .iter() .map(move |(sk, bts)| (k, sk, bts.iter().map(Bytes::as_ref))) }), + std::iter::empty(), + false, ) .unwrap(), trace.root_after, @@ -245,8 +247,9 @@ fn witgen_update_one() { .unwrap() .as_slice(), ); - let (existed, start_state) = state.state().get_account(&target_addr); - assert!(existed, "we picked an existed account"); + let start_state = state.state().get(&target_addr); + assert!(start_state.is_some(), "we picked an existed account"); + let start_state = start_state.unwrap(); let trace = w.handle_new_state( MPTProofType::BalanceChanged, @@ -267,8 +270,8 @@ fn witgen_update_one() { MPTProofType::StorageChanged, target_addr, U256::from(1u32), - if let Some(v) = start_state.storage.get(&U256::zero()) { - *v + if let Some(v) = state.storage().get(&(target_addr, U256::zero())) { + *v.as_ref() } else { U256::default() }, diff --git a/zktrie/src/state/witness.rs b/zktrie/src/state/witness.rs index 7861b4c776..50e743d79a 100644 --- a/zktrie/src/state/witness.rs +++ b/zktrie/src/state/witness.rs @@ -3,7 +3,7 @@ use super::{ builder::{extend_address_to_h256, AccountData, BytesArray, CanRead, TrieProof}, MPTProofType, ZktrieState, }; -use bus_mapping::{state_db::CodeDB, util::KECCAK_CODE_HASH_ZERO}; +// use bus_mapping::{state_db::CodeDB, util::KECCAK_CODE_HASH_ZERO}; use eth_types::{Address, Hash, Word, H256, U256}; use halo2_proofs::halo2curves::group::ff::PrimeField; use mpt_circuits::serde::{ @@ -43,47 +43,29 @@ pub struct WitnessGenerator { impl From<&ZktrieState> for WitnessGenerator { fn from(state: &ZktrieState) -> Self { - let sdb = &state.sdb; - let trie = state.zk_db.borrow_mut().new_trie(&state.trie_root).unwrap(); - let accounts: HashMap<_, _> = state .accounts .iter() - .map(|(addr, storage_root)| { - let (existed, acc_data) = sdb.get_account(addr); - assert!( - existed, - "expected to be consistented between records in sdb and account root" - ); - (*addr, acc_data, storage_root) - }) // filter out the account data which is empty can provide update applying some // convenient - .filter(|(_, acc_data, _)| !acc_data.is_empty()) - .map(|(addr, acc_data, storage_root)| { - ( - addr, - AccountData { - nonce: acc_data.nonce.as_u64(), - balance: acc_data.balance, - poseidon_code_hash: acc_data.code_hash, - keccak_code_hash: acc_data.keccak_code_hash, - code_size: acc_data.code_size.as_u64(), - storage_root: H256::from(storage_root), - }, - ) - }) + .filter(|(_, acc)| !acc.keccak_code_hash.is_zero()) + .map(|(key, acc)| (*key, *acc)) .collect(); let storages: HashMap<_, _> = state .accounts .iter() - .map(|(addr, storage_root)| (*addr, state.zk_db.borrow_mut().new_trie(storage_root))) + .map(|(addr, acc)| { + ( + *addr, + state.zk_db.borrow_mut().new_trie(&acc.storage_root.0), + ) + }) // if an account has no storage slot being touched in execution, they do not need // storage trie and would be filter out here - .filter(|(_, storage_root)| storage_root.is_some()) - .map(|(addr, storage_root)| (addr, storage_root.expect("None has been filtered"))) + .filter(|(_, storage_trie)| storage_trie.is_some()) + .map(|(addr, storage_trie)| (addr, storage_trie.expect("None has been filtered"))) .collect(); Self { @@ -344,14 +326,12 @@ impl WitnessGenerator { MPTProofType::CodeHashExists => { let mut code_hash = [0u8; 32]; old_val.to_big_endian(code_hash.as_mut_slice()); - if H256::from(code_hash) != acc_data.poseidon_code_hash { - if H256::from(code_hash).is_zero() - && acc_data.keccak_code_hash == *KECCAK_CODE_HASH_ZERO - { - log::trace!("codehash 0->keccak(nil)"); - } else { - debug_assert_eq!(H256::from(code_hash), acc_data.keccak_code_hash); - } + if H256::from(code_hash) != acc_data.keccak_code_hash { + log::trace!( + "codehash {} ->keccak {}(nil)", + H256::from(code_hash), + acc_data.keccak_code_hash + ); } new_val.to_big_endian(code_hash.as_mut_slice()); acc_data.keccak_code_hash = H256::from(code_hash); @@ -360,16 +340,11 @@ impl WitnessGenerator { let mut code_hash = [0u8; 32]; old_val.to_big_endian(code_hash.as_mut_slice()); if H256::from(code_hash) != acc_data.poseidon_code_hash { - if H256::from(code_hash).is_zero() - && acc_data.poseidon_code_hash == CodeDB::empty_code_hash() - { - log::trace!("codehash 0->poseidon(nil)"); - } else { - debug_assert_eq!( - H256::from(code_hash), - acc_data.poseidon_code_hash - ); - } + log::trace!( + "codehash {} ->poseidon {}(nil)", + H256::from(code_hash), + acc_data.poseidon_code_hash + ); } new_val.to_big_endian(code_hash.as_mut_slice()); acc_data.poseidon_code_hash = H256::from(code_hash);