diff --git a/bus-mapping/src/circuit_input_builder/l2.rs b/bus-mapping/src/circuit_input_builder/l2.rs index 97a0ac805f..81b9763b50 100644 --- a/bus-mapping/src/circuit_input_builder/l2.rs +++ b/bus-mapping/src/circuit_input_builder/l2.rs @@ -138,7 +138,7 @@ impl CircuitInputBuilder { .map_err(Error::IoError)?; log::debug!( - "building partial statedb done, root {}", + "building partial ZktrieState done, root {}", hex::encode(mpt_init_state.root()) ); @@ -152,6 +152,7 @@ impl CircuitInputBuilder { &l2_trace.storage_trace, )) { let (addr, acc) = parsed.map_err(Error::IoError)?; + log::trace!("sdb trace {:?} {:?}", addr, acc); sdb.set_account(&addr, state_db::Account::from(&acc)); } @@ -160,13 +161,14 @@ impl CircuitInputBuilder { )) { let ((addr, key), val) = parsed.map_err(Error::IoError)?; let key = key.to_word(); + log::trace!("sdb trace storage {:?} {:?} {:?}", addr, key, val); *sdb.get_storage_mut(&addr, &key).1 = val.into(); } let mut code_db = CodeDB::new(); code_db.insert(Vec::new()); - let codes = collect_codes(&l2_trace, Some(&sdb))?; + let codes = collect_codes(&l2_trace)?; for (hash, code) in codes { code_db.insert_with_hash(hash, code); } @@ -240,7 +242,7 @@ impl CircuitInputBuilder { *self.sdb.get_storage_mut(&addr, &key).1 = val; } - let codes = collect_codes(&l2_trace, Some(&self.sdb))?; + let codes = collect_codes(&l2_trace)?; for (hash, code) in codes { self.code_db.insert_with_hash(hash, code); } diff --git a/bus-mapping/src/circuit_input_builder/transaction.rs b/bus-mapping/src/circuit_input_builder/transaction.rs index 390dfdc21c..fa32dc87f8 100644 --- a/bus-mapping/src/circuit_input_builder/transaction.rs +++ b/bus-mapping/src/circuit_input_builder/transaction.rs @@ -282,6 +282,7 @@ impl Transaction { let block_num = eth_tx.block_number.unwrap().as_u64(); let (found, _) = sdb.get_account(ð_tx.from); if !found { + log::error!("tx.from not found {}", eth_tx.from); return Err(Error::AccountNotFound(eth_tx.from)); } @@ -289,6 +290,7 @@ impl Transaction { // Contract Call / Transfer let (found, account) = sdb.get_account(&address); if !found { + log::error!("tx.to not found {}", address); return Err(Error::AccountNotFound(address)); } let code_hash = account.code_hash; diff --git a/eth-types/src/l2_types.rs b/eth-types/src/l2_types.rs index 988b6d8345..f912d2652b 100644 --- a/eth-types/src/l2_types.rs +++ b/eth-types/src/l2_types.rs @@ -82,7 +82,7 @@ pub struct BlockHeader { impl From for BlockTraceV2 { fn from(b: BlockTrace) -> Self { - let codes = collect_codes(&b, None) + let codes = collect_codes(&b) .expect("collect codes should not fail") .into_iter() .map(|(hash, code)| BytecodeTrace { @@ -531,8 +531,6 @@ pub struct ExecStep { pub memory: Option>, #[cfg(feature = "enable-storage")] pub storage: Option>, - #[serde(rename = "extraData")] - pub extra_data: Option, } impl From for GethExecStep { @@ -556,20 +554,6 @@ impl From for GethExecStep { } } -/// extra data for some steps -#[derive(Serialize, Deserialize, Debug, Clone)] -#[doc(hidden)] -pub struct ExtraData { - #[serde(rename = "codeList")] - pub code_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() - } -} - /// account wrapper for account status #[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq)] #[doc(hidden)] diff --git a/eth-types/src/l2_types/trace.rs b/eth-types/src/l2_types/trace.rs index 28bcf14be9..46f9a4aedf 100644 --- a/eth-types/src/l2_types/trace.rs +++ b/eth-types/src/l2_types/trace.rs @@ -1,187 +1,17 @@ -use crate::{ - evm_types::OpcodeId, - l2_types::BlockTrace, - state_db::{CodeDB, StateDB}, - utils::is_precompiled, - Address, Error, H256, -}; -use ethers_core::types::Bytes; +use crate::{l2_types::BlockTrace, Error, H256}; use itertools::Itertools; -use super::ExecStep; - -/// Update codedb from statedb and trace -pub fn collect_codes( - block: &BlockTrace, - sdb: Option<&StateDB>, -) -> Result)>, Error> { - if !block.codes.is_empty() { - log::debug!("codes available in trace, skip collecting"); - return Ok(block - .codes - .iter() - .map(|b| (b.hash, b.code.to_vec())) - .collect_vec()); - } - - log::debug!("collect_codes for block {:?}", block.header.number); - if sdb.is_none() { - log::warn!("collect_codes without sdb can be slow"); - } - let mut codes = Vec::new(); - for (er_idx, execution_result) in block.execution_results.iter().enumerate() { - if let Some(bytecode) = &execution_result.byte_code { - if let Some(to) = &execution_result.to { - // Not contract deployment - let bytecode = decode_bytecode(bytecode)?.to_vec(); - let code_hash = to.poseidon_code_hash; - // code hash 0 means non-existed account - if !code_hash.is_zero() { - codes.push((code_hash, bytecode)); - } - //log::debug!("inserted tx bytecode {:?} {:?}", code_hash, hash); - } - } - - // filter all precompile calls, empty calls and create - let mut call_trace = execution_result - .call_trace - .flatten_trace(&execution_result.prestate) - .into_iter() - .filter(|call| { - let is_call_to_precompile = call.to.as_ref().map(is_precompiled).unwrap_or(false); - let is_call_to_empty = call.gas_used.is_zero() - && !call.call_type.is_create() - && call.is_callee_code_empty; - !(is_call_to_precompile || is_call_to_empty || call.call_type.is_create()) - }) - .collect::>(); - //log::trace!("call_trace: {call_trace:?}"); - - for (idx, step) in execution_result.exec_steps.iter().enumerate().rev() { - if step.op.is_create() { - continue; - } - let call = if step.op.is_call() { - // filter call to empty/precompile/!precheck_ok - if let Some(next_step) = execution_result.exec_steps.get(idx + 1) { - // the call doesn't have inner steps, it could be: - // - a call to a precompiled contract - // - a call to an empty account - // - a call that !is_precheck_ok - if next_step.depth != step.depth + 1 { - log::trace!("skip call step due to no inner step, curr: {step:?}, next: {next_step:?}"); - continue; - } - } else { - // this is the final step, no inner steps - log::trace!("skip call step due this is the final step: {step:?}"); - continue; - } - let call = call_trace.pop(); - //log::trace!("call_trace pop: {call:?}, current step: {step:?}"); - call - } else { - None - }; - - if let Some(data) = &step.extra_data { - match step.op { - OpcodeId::CALL - | OpcodeId::CALLCODE - | OpcodeId::DELEGATECALL - | OpcodeId::STATICCALL => { - let call = call.unwrap(); - assert_eq!(call.call_type, step.op, "{call:?}"); - let code_idx = if block.transactions[er_idx].to.is_none() { - 0 - } else { - 1 - }; - let callee_code = data.get_code_at(code_idx); - let addr = call.to.unwrap(); - trace_code( - &mut codes, - callee_code.unwrap_or_default(), - step, - Some(addr), - sdb, - block, - ); - } - OpcodeId::EXTCODECOPY => { - let code = data.get_code_at(0); - if code.is_none() { - log::warn!("unable to fetch code from step. {step:?}"); - continue; - } - log::info!("trace extcodecopy! block {:?}", block.header.number); - trace_code(&mut codes, code.unwrap(), step, None, sdb, block); - } - - _ => {} - } - } - } +/// Collect bytecodes from trace +pub fn collect_codes(block: &BlockTrace) -> Result)>, Error> { + if block.codes.is_empty() { + return Err(Error::TracingError(format!( + "codes not available for block {:?}", + block.header.number + ))); } - - log::debug!("collect codes done"); - Ok(codes) -} - -fn trace_code( - codes: &mut Vec<(H256, Vec)>, - code: Bytes, - step: &ExecStep, - addr: Option
, - // sdb is used to read codehash if available without recomputing - sdb: Option<&StateDB>, - block: &BlockTrace, -) { - let code_hash = addr.and_then(|addr| { - sdb.and_then(|sdb| { - let (_existed, acc_data) = sdb.get_account(&addr); - if acc_data.code_hash != CodeDB::empty_code_hash() && !code.is_empty() { - Some(acc_data.code_hash) - } else { - None - } - }) - }); - let code_hash = match code_hash { - Some(code_hash) if !code_hash.is_zero() => code_hash, - _ => { - let hash = CodeDB::hash(&code); - log::debug!( - "hash_code done: addr {addr:?}, size {}, hash {hash:?}, step {:?}, gas.left {:?}, block {:?}", - &code.len(), - step.op, - step.gas, - block.header.number, - ); - hash - } - }; - codes.push((code_hash, code.to_vec())); - log::trace!( - "trace code addr {:?}, size {} hash {:?}", - addr, - &code.len(), - code_hash - ); -} - -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) + Ok(block + .codes + .iter() + .map(|b| (b.hash, b.code.to_vec())) + .collect_vec()) } diff --git a/geth-utils/l2geth/go.mod b/geth-utils/l2geth/go.mod index 421cd196eb..c7051095cb 100644 --- a/geth-utils/l2geth/go.mod +++ b/geth-utils/l2geth/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/imdario/mergo v0.3.16 - github.com/scroll-tech/go-ethereum v1.10.14-0.20240621133406-517e5b4b0764 + github.com/scroll-tech/go-ethereum v1.10.14-0.20240717120140-0360eba83660 ) require ( diff --git a/go.work.sum b/go.work.sum index 3c4f5090af..33db0477e4 100644 --- a/go.work.sum +++ b/go.work.sum @@ -331,6 +331,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/scroll-tech/go-ethereum v1.10.14-0.20231108100028-cb76ecd42bf7 h1:xDtuJk3CjD46kHw87Xe9o/1PcvTVgNZYoT2vGgRmO5s= github.com/scroll-tech/go-ethereum v1.10.14-0.20231108100028-cb76ecd42bf7/go.mod h1:4HrFcoStbViFVy/9l/rvKl1XmizVAaPdgqI8v0U8hOc= +github.com/scroll-tech/go-ethereum v1.10.14-0.20240717120140-0360eba83660 h1:pSFXa3YwuzNiMJAzYDQ4XDml2b5ioDVZ5FJsDPTu/4c= +github.com/scroll-tech/go-ethereum v1.10.14-0.20240717120140-0360eba83660/go.mod h1:e3uA1ySy33Pl+B0PBW5/TbHoLhUtojzjPCYT2a7YdJc= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= diff --git a/zkevm-circuits/src/copy_circuit/test.rs b/zkevm-circuits/src/copy_circuit/test.rs index ee146012ad..1b9f8ef8a8 100644 --- a/zkevm-circuits/src/copy_circuit/test.rs +++ b/zkevm-circuits/src/copy_circuit/test.rs @@ -3,6 +3,7 @@ use crate::{ copy_circuit::*, evm_circuit::{test::rand_bytes, witness::block_convert}, + test_util::CircuitTestBuilder, util::unusable_rows, witness::Block, }; @@ -285,7 +286,7 @@ fn gen_tx_log_data() -> CircuitInputBuilder { builder } -fn gen_access_list_data() -> CircuitInputBuilder { +fn gen_access_list_data() -> Block { let test_access_list = AccessList(vec![ AccessListItem { address: address!("0x0000000000000000000000000000000000001111"), @@ -318,13 +319,9 @@ fn gen_access_list_data() -> CircuitInputBuilder { |block, _tx| block.number(0xcafeu64), ) .unwrap(); - let block: GethData = test_ctx.into(); - let mut builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); - builder - .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - - builder + CircuitTestBuilder::new_from_test_ctx(test_ctx) + .build_witness_block() + .0 } fn gen_create_data() -> CircuitInputBuilder { @@ -421,8 +418,7 @@ fn copy_circuit_valid_tx_log() { #[test] fn copy_circuit_valid_access_list() { - let builder = gen_access_list_data(); - let block = block_convert(&builder.block, &builder.code_db).unwrap(); + let block = gen_access_list_data(); assert_eq!(test_copy_circuit_from_block(block), Ok(())); } diff --git a/zkevm-circuits/src/test_util.rs b/zkevm-circuits/src/test_util.rs index 009d2839cb..cccf01976d 100644 --- a/zkevm-circuits/src/test_util.rs +++ b/zkevm-circuits/src/test_util.rs @@ -27,6 +27,8 @@ fn init_env_logger() { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("error")).init(); } +pub(crate) type FnBlockChecker = Option, &Vec, &Vec)>>; + #[allow(clippy::type_complexity)] /// Struct used to easily generate tests for EVM &| State circuits being able to /// customize all of the steps involved in the testing itself. @@ -82,9 +84,9 @@ pub struct CircuitTestBuilder { test_ctx: Option>, circuits_params: Option, block: Option, - evm_checks: Option, &Vec, &Vec)>>, - state_checks: Option, &Vec, &Vec)>>, - copy_checks: Option, &Vec, &Vec)>>, + evm_checks: FnBlockChecker, + state_checks: FnBlockChecker, + copy_checks: FnBlockChecker, block_modifiers: Vec>, } @@ -199,10 +201,8 @@ impl CircuitTestBuilder { } impl CircuitTestBuilder { - /// Triggers the `CircuitTestBuilder` to convert the [`TestContext`] if any, - /// into a [`Block`] and apply the default or provided block_modifiers or - /// circuit checks to the provers generated for the State and EVM circuits. - pub fn run(self) { + /// Return the witness block + pub fn build_witness_block(self) -> (Block, FnBlockChecker, FnBlockChecker, FnBlockChecker) { let mut params = if let Some(block) = self.block.as_ref() { block.circuits_params } else { @@ -254,10 +254,17 @@ impl CircuitTestBuilder { } else { panic!("No attribute to build a block was passed to the CircuitTestBuilder") }; + (block, self.evm_checks, self.state_checks, self.copy_checks) + } + /// Triggers the `CircuitTestBuilder` to convert the [`TestContext`] if any, + /// into a [`Block`] and apply the default or provided block_modifiers or + /// circuit checks to the provers generated for the State and EVM circuits. + pub fn run(self) { + let (block, evm_checks, state_checks, copy_checks) = self.build_witness_block(); const NUM_BLINDING_ROWS: usize = 64; // Run evm circuit test - if let Some(evm_checks) = &self.evm_checks { + if let Some(evm_checks) = &evm_checks { let k = block.get_evm_test_circuit_degree(); assert!(k <= 20); let (active_gate_rows, active_lookup_rows) = EvmCircuit::::get_active_rows(&block); @@ -269,7 +276,7 @@ impl CircuitTestBuilder { } // Run state circuit test - if let Some(state_checks) = &self.state_checks { + if let Some(state_checks) = &state_checks { let (_, rows_needed) = StateCircuit::::min_num_rows_block(&block); let k: u32 = log2_ceil(rows_needed + NUM_BLINDING_ROWS); assert!(k <= 20); @@ -288,7 +295,7 @@ impl CircuitTestBuilder { } // Run copy circuit test - if let Some(copy_checks) = &self.copy_checks { + if let Some(copy_checks) = ©_checks { let (active_rows, max_rows) = CopyCircuit::::min_num_rows_block(&block); let k1 = block.get_evm_test_circuit_degree(); let k2 = log2_ceil(max_rows + NUM_BLINDING_ROWS);