Skip to content

Commit

Permalink
upgrade l2geth and remove old bytecode collecting codes (#1376)
Browse files Browse the repository at this point in the history
* upgrade l2geth and remove old bytecode collecting codes

* update copy circuit test
  • Loading branch information
lispc authored Jul 20, 2024
1 parent dfa0235 commit 238ef02
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 224 deletions.
8 changes: 5 additions & 3 deletions bus-mapping/src/circuit_input_builder/l2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
);

Expand All @@ -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));
}

Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions bus-mapping/src/circuit_input_builder/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,13 +282,15 @@ impl Transaction {
let block_num = eth_tx.block_number.unwrap().as_u64();
let (found, _) = sdb.get_account(&eth_tx.from);
if !found {
log::error!("tx.from not found {}", eth_tx.from);
return Err(Error::AccountNotFound(eth_tx.from));
}

let call = if let Some(address) = eth_tx.to {
// 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;
Expand Down
18 changes: 1 addition & 17 deletions eth-types/src/l2_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pub struct BlockHeader {

impl From<BlockTrace> 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 {
Expand Down Expand Up @@ -531,8 +531,6 @@ pub struct ExecStep {
pub memory: Option<Vec<crate::Word>>,
#[cfg(feature = "enable-storage")]
pub storage: Option<HashMap<crate::Word, crate::Word>>,
#[serde(rename = "extraData")]
pub extra_data: Option<ExtraData>,
}

impl From<ExecStep> for GethExecStep {
Expand All @@ -556,20 +554,6 @@ impl From<ExecStep> for GethExecStep {
}
}

/// extra data for some steps
#[derive(Serialize, Deserialize, Debug, Clone)]
#[doc(hidden)]
pub struct ExtraData {
#[serde(rename = "codeList")]
pub code_list: Option<Vec<Bytes>>,
}

impl ExtraData {
pub fn get_code_at(&self, i: usize) -> Option<Bytes> {
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)]
Expand Down
196 changes: 13 additions & 183 deletions eth-types/src/l2_types/trace.rs
Original file line number Diff line number Diff line change
@@ -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<Vec<(H256, Vec<u8>)>, 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::<Vec<_>>();
//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<Vec<(H256, Vec<u8>)>, 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<u8>)>,
code: Bytes,
step: &ExecStep,
addr: Option<Address>,
// 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<Vec<u8>, 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())
}
2 changes: 1 addition & 1 deletion geth-utils/l2geth/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
2 changes: 2 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
16 changes: 6 additions & 10 deletions zkevm-circuits/src/copy_circuit/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::{
copy_circuit::*,
evm_circuit::{test::rand_bytes, witness::block_convert},
test_util::CircuitTestBuilder,
util::unusable_rows,
witness::Block,
};
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(()));
}

Expand Down
Loading

0 comments on commit 238ef02

Please sign in to comment.