Skip to content

Commit

Permalink
refactor for stateless block verifier (#1236)
Browse files Browse the repository at this point in the history
* move CodeDB and StateDB

* update execute_precompiled

* fix precompile

* fix doc

* add revm primitives conversion

* update zktrie

* clean deps

---------

Co-authored-by: Zhang Zhuo <mycinbrin@gmail.com>
  • Loading branch information
lightsing and lispc authored Apr 29, 2024
1 parent a53859c commit 5dd697b
Show file tree
Hide file tree
Showing 48 changed files with 660 additions and 1,184 deletions.
821 changes: 106 additions & 715 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ members = [
"zkevm-circuits",
"bus-mapping",
"geth-utils",
"keccak256",
"zktrie",
"gadgets",
"integration-tests",
Expand Down Expand Up @@ -62,7 +61,8 @@ strum_macros = "0.25"
subtle = "2.4"
tokio = { version = "1.13", features = ["macros", "rt-multi-thread"] }
url = "2.2"
revm-precompile = { git = "https://github.com/scroll-tech/revm", branch = "scroll-fix" }
revm-precompile = { git = "https://github.com/scroll-tech/revm", rev = "9ecb479" } # v35
revm-primitives = { git = "https://github.com/scroll-tech/revm", rev = "9ecb479" } # v35
c-kzg = "1.0.0"

[patch.crates-io]
Expand Down
6 changes: 3 additions & 3 deletions bus-mapping/src/circuit_input_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ use crate::{
evm::opcodes::{gen_associated_ops, gen_associated_steps},
operation::{self, CallContextField, Operation, RWCounter, StartOp, StorageOp, RW},
rpc::GethClient,
state_db::{self, CodeDB, StateDB},
util::{hash_code_keccak, KECCAK_CODE_HASH_EMPTY},
util::KECCAK_CODE_HASH_EMPTY,
};
pub use access::{Access, AccessSet, AccessValue, CodeSource};
pub use block::{Block, BlockContext};
Expand All @@ -30,6 +29,7 @@ use eth_types::{
evm_types::{GasCost, OpcodeId},
geth_types::{self, TxType},
sign_types::{pk_bytes_le, pk_bytes_swap_endianness, SignData},
state_db::{self, CodeDB, StateDB},
Address, GethExecTrace, ToBigEndian, ToWord, Word, H256,
};
use ethers_providers::JsonRpcClient;
Expand All @@ -40,7 +40,7 @@ pub use execution::{
};
use hex::decode_to_slice;

use eth_types::sign_types::get_dummy_tx;
use eth_types::{sign_types::get_dummy_tx, utils::hash_code_keccak};
use ethers_core::utils::keccak256;
pub use input_state_ref::CircuitInputStateRef;
use itertools::Itertools;
Expand Down
5 changes: 3 additions & 2 deletions bus-mapping/src/circuit_input_builder/input_state_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ use crate::{
StackOp, Target, TxAccessListAccountOp, TxAccessListAccountStorageOp, TxLogField, TxLogOp,
TxReceiptField, TxReceiptOp, RW,
},
precompile::{is_precompiled, PrecompileCalls},
state_db::{CodeDB, StateDB},
precompile::PrecompileCalls,
Error,
};
use eth_types::{
Expand All @@ -30,6 +29,8 @@ use eth_types::{
memory::{MemoryRange, MemoryWordRange},
Gas, GasCost, Memory, MemoryAddress, MemoryRef, OpcodeId, StackAddress, MAX_CODE_SIZE,
},
state_db::{CodeDB, StateDB},
utils::is_precompiled,
Address, Bytecode, GethExecStep, ToAddress, ToBigEndian, ToWord, Word, H256, U256,
};
use ethers_core::utils::{get_contract_address, get_create2_address};
Expand Down
245 changes: 7 additions & 238 deletions bus-mapping/src/circuit_input_builder/l2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,247 +2,16 @@ pub use super::block::{Block, BlockContext};
use crate::{
circuit_input_builder::{self, BlockHead, CircuitInputBuilder, CircuitsParams},
error::Error,
precompile::is_precompiled,
state_db::{self, CodeDB, StateDB},
};
use eth_types::{
self,
evm_types::OpcodeId,
l2_types::{BlockTrace, EthBlock, ExecStep, StorageTrace},
Address, ToWord, Word, H256,
l2_types::{BlockTrace, EthBlock, StorageTrace},
state_db::{self, CodeDB, StateDB},
Address, ToWord, Word,
};
use ethers_core::types::Bytes;
use mpt_zktrie::state::{AccountData, ZktrieState};
use std::collections::hash_map::{Entry, HashMap};

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(),
}
}
}
}

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)
}

fn trace_code(
cdb: &mut CodeDB,
code_hash: Option<H256>,
code: Bytes,
step: &ExecStep,
addr: Option<Address>,
sdb: &StateDB,
) {
// first, try to read from sdb
// let stack = match step.stack.as_ref() {
// Some(stack) => stack,
// None => {
// log::error!("stack underflow, step {step:?}");
// return;
// }
// };
// if stack_pos >= stack.len() {
// log::error!("stack underflow, step {step:?}");
// return;
// }
// let addr = stack[stack.len() - stack_pos - 1].to_address(); //stack N-stack_pos
//
let code_hash = code_hash.or_else(|| {
addr.and_then(|addr| {
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);
}
}

// 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_or_create() {
// 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);
// 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,
};
let addr = call.to.unwrap();
trace_code(
cdb,
code_hash,
callee_code.unwrap_or_default(),
step,
Some(addr),
sdb,
);
}
OpcodeId::CREATE | OpcodeId::CREATE2 => {
// notice we do not need to insert code for CREATE,
// bustmapping do this job
unreachable!()
}
OpcodeId::EXTCODESIZE | OpcodeId::EXTCODECOPY => {
let code = data.get_code_at(0);
if code.is_none() {
log::warn!("unable to fetch code from step. {step:?}");
continue;
}
trace_code(cdb, None, code.unwrap(), step, None, sdb);
}

_ => {}
}
}
}
}

log::debug!("updating codedb done");
Ok(())
}
use mpt_zktrie::state::ZktrieState;
use std::collections::hash_map::HashMap;

fn dump_code_db(cdb: &CodeDB) {
for (k, v) in &cdb.0 {
Expand Down Expand Up @@ -404,7 +173,7 @@ impl CircuitInputBuilder {

let mut code_db = CodeDB::new();
code_db.insert(Vec::new());
update_codedb(&mut code_db, &sdb, &l2_trace)?;
code_db.update_codedb(&sdb, &l2_trace)?;

let mut builder_block = circuit_input_builder::Block::from_headers(&[], circuits_params);
builder_block.chain_id = chain_id;
Expand Down Expand Up @@ -475,7 +244,7 @@ impl CircuitInputBuilder {
*self.sdb.get_storage_mut(&addr, &key).1 = val;
}

update_codedb(&mut self.code_db, &self.sdb, &l2_trace)?;
self.code_db.update_codedb(&self.sdb, &l2_trace)?;

self.apply_l2_trace(l2_trace, !more)?;
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion bus-mapping/src/circuit_input_builder/tracer_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ use crate::{
ContractAddressCollisionError, DepthError, ExecError, InsufficientBalanceError, OogError,
},
operation::RWCounter,
state_db::Account,
};
use eth_types::{
address, bytecode,
evm_types::{stack::Stack, Gas, Memory, OpcodeId},
geth_types::GethData,
state_db::Account,
word, Bytecode, GethExecError, GethExecStep, Hash, ToAddress, ToWord, Word,
};
use mock::test_ctx::{helpers::*, LoggerConfig, TestContext};
Expand Down
7 changes: 2 additions & 5 deletions bus-mapping/src/circuit_input_builder/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
//! Transaction & TransactionContext utility module.
use super::{call::ReversionGroup, Call, CallContext, CallKind, CodeSource, ExecStep};
use crate::{
l2_predeployed::l1_gas_price_oracle,
state_db::{CodeDB, StateDB},
Error,
};
use crate::{l2_predeployed::l1_gas_price_oracle, Error};
use eth_types::{
evm_types::{gas_utils::tx_data_gas_cost, OpcodeId},
geth_types,
geth_types::{get_rlp_unsigned, TxType},
state_db::{CodeDB, StateDB},
AccessList, Address, GethExecTrace, Signature, Word, H256,
};
use ethers_core::utils::get_contract_address;
Expand Down
2 changes: 1 addition & 1 deletion bus-mapping/src/evm/opcodes/balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ mod balance_tests {
circuit_input_builder::ExecState,
mock::BlockData,
operation::{AccountOp, CallContextOp, StackOp, RW},
state_db::CodeDB,
};
use eth_types::{
address, bytecode,
evm_types::{OpcodeId, StackAddress},
geth_types::GethData,
state_db::CodeDB,
Bytecode, ToWord, Word, U256,
};
use mock::TestContext;
Expand Down
5 changes: 3 additions & 2 deletions bus-mapping/src/evm/opcodes/begin_end_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ use crate::{
operation::{
AccountField, AccountOp, CallContextField, StorageOp, TxReceiptField, TxRefundOp, RW,
},
precompile::{execute_precompiled, is_precompiled, PrecompileCalls},
state_db::CodeDB,
precompile::{execute_precompiled, PrecompileCalls},
Error,
};
use eth_types::{
evm_types::{
gas_utils::{tx_access_list_gas_cost, tx_data_gas_cost},
GasCost, MAX_REFUND_QUOTIENT_OF_GAS_USED,
},
state_db::CodeDB,
utils::is_precompiled,
Bytecode, ToWord, Word,
};
use ethers_core::utils::get_contract_address;
Expand Down
Loading

0 comments on commit 5dd697b

Please sign in to comment.