Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] make supercircuit test applying l2trace #767

Merged
merged 50 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
f4a36cc
induce l2 types for l2trace
noel2004 Aug 13, 2023
5a2a942
add l2trace entry
noel2004 Aug 13, 2023
d4c0a11
wip: some refactoring
noel2004 Aug 14, 2023
e8edbaa
refactor zktrie for decoupling from bus-mapping
noel2004 Aug 14, 2023
33919fe
more refactoring in zktrie
noel2004 Aug 15, 2023
98a920c
wip: l2 circuit builder
noel2004 Aug 15, 2023
cdb62f6
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 15, 2023
d949847
use l2 trace in super circuit test
noel2004 Aug 15, 2023
f628e0c
prune unnecessary sign
noel2004 Aug 15, 2023
a265998
fmt and lint
noel2004 Aug 15, 2023
1157cd3
missed exportion
noel2004 Aug 15, 2023
9a5b56f
update l2geth util for l1 queue index
noel2004 Aug 15, 2023
b7cf183
apply zktrie and storage proof
noel2004 Aug 15, 2023
bc57a83
sdb skip empty value
noel2004 Aug 15, 2023
f5f1f45
update state root in tracing
noel2004 Aug 15, 2023
a6e3bed
more update for state root
noel2004 Aug 15, 2023
2f47db8
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 15, 2023
2b938eb
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 15, 2023
2fad3b6
fix tests
noel2004 Aug 15, 2023
07bd7f4
lints
noel2004 Aug 15, 2023
65be42e
lint for all-target
noel2004 Aug 15, 2023
0c7b6b8
fix memory transfer
noel2004 Aug 16, 2023
2d6b1ee
fmt
noel2004 Aug 16, 2023
2d5d512
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 16, 2023
3c8b9a5
disable callee code assert for precompile
noel2004 Aug 16, 2023
822bc80
set all difficulity in l2 test to 0
noel2004 Aug 16, 2023
0d0ef51
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 16, 2023
2c5b62f
reroll the difficulity setting in test
noel2004 Aug 16, 2023
8d02f4a
fix deploy tx issue and prune l2geth_util
noel2004 Aug 16, 2023
b93b1a3
revert incorrect fixing
noel2004 Aug 16, 2023
e67f78f
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 16, 2023
b59af93
disable doc test temporarily
noel2004 Aug 16, 2023
1a64b1f
add incremental update for l2 trace
noel2004 Aug 17, 2023
61cb695
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 17, 2023
18a3e8e
zero coinbase patch for sdb
noel2004 Aug 17, 2023
eebacea
rename variables for better self-doc
noel2004 Aug 17, 2023
c491484
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 17, 2023
d3bf203
re-enable tx storage fiield
noel2004 Aug 18, 2023
d02d80f
add modexp unittest
noel2004 Aug 18, 2023
5e9cb63
turn ec test to serial
noel2004 Aug 18, 2023
56d4ced
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 18, 2023
dd82c8f
bump the version of l2geth_util
noel2004 Aug 20, 2023
4896caa
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 21, 2023
fee35d6
make l1 queue index default
noel2004 Aug 18, 2023
fb7871b
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 22, 2023
0ae06dd
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 22, 2023
e5d465b
fix an issue in sdb update
noel2004 Aug 23, 2023
9c273c5
Merge remote-tracking branch 'origin/develop' into feat/supercircuit_…
noel2004 Aug 24, 2023
6334b80
mpt_state: add light_mode for ZktrieState::from_trace_with_additional
lispc Aug 25, 2023
c29a7f1
Merge remote-tracking branch 'scroll/develop' into feat/supercircuit_…
lispc Aug 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions bus-mapping/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
9 changes: 9 additions & 0 deletions bus-mapping/src/circuit_input_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -166,6 +170,9 @@ pub struct CircuitInputBuilder {
pub block: Block,
/// Block Context
pub block_ctx: BlockContext,
#[cfg(feature = "scroll")]
/// Zktrie Status
pub mpt_state: ZktrieState,
}

impl<'a> CircuitInputBuilder {
Expand All @@ -177,6 +184,8 @@ impl<'a> CircuitInputBuilder {
code_db,
block: block.clone(),
block_ctx: BlockContext::new(),
#[cfg(feature = "scroll")]
mpt_state: Default::default(),
}
}
/// Create a new CircuitInputBuilder from the given `eth_block` and
Expand Down
329 changes: 329 additions & 0 deletions bus-mapping/src/circuit_input_builder/l2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,329 @@
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},
ToAddress, 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<Vec<u8>, hex::FromHexError> {
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)
}

fn trace_code(
cdb: &mut CodeDB,
code_hash: Option<H256>,
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) {
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).unwrap().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);
// 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);
assert!(
code.is_none(),
"invalid trace: cannot get code of ext: {step:?}"
);
trace_code(cdb, None, code.unwrap(), step, sdb, 0);
}

_ => {}
}
}
}
}

log::debug!("updating codedb done");
}

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> {
update_codedb(&mut self.code_db, &self.sdb, block_trace);
if is_last {
dump_code_db(&self.code_db);
}

let geth_trace: Vec<eth_types::GethExecTrace> = 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(),
&eth_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();
self.block.start_l1_queue_index = block_trace.start_l1_queue_index;
lispc marked this conversation as resolved.
Show resolved Hide resolved
// 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(&eth_block, &geth_trace, false, is_last)?;
log::debug!("handle_block_inner done for block {:?}", block_num);
Ok(())
}

/// 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,
) -> Result<Self, Error> {
let chain_id = l2_trace.chain_id;

let mut code_db = CodeDB::new();
code_db.insert(Vec::new());

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 account_proofs = l2_trace.storage_trace.proofs.iter().flat_map(|kv_map| {
kv_map
.iter()
.map(|(k, bts)| (k, bts.iter().map(Bytes::as_ref)))
});
let storage_proofs =
l2_trace
.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)))
});
let additional_proofs = l2_trace
.storage_trace
.deletion_proofs
.iter()
.map(Bytes::as_ref);

let mpt_state = ZktrieState::from_trace_with_additional(
old_root,
account_proofs,
storage_proofs,
additional_proofs,
)
.unwrap();

log::debug!(
"building partial statedb done, root {}",
hex::encode(mpt_state.root())
);

let sdb = StateDB::from(&mpt_state);

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_state.root());
let mut builder = Self {
sdb,
code_db,
block: builder_block,
block_ctx: BlockContext::new(),
mpt_state,
};

builder.apply_l2_trace(l2_trace, !more)?;
Ok(builder)
}

/// 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()
}
}
2 changes: 1 addition & 1 deletion bus-mapping/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down
Loading
Loading