Skip to content
This repository was archived by the owner on Apr 18, 2025. It is now read-only.

[FEAT] support mainnet block in mainnet.rs (#814) #1004

Merged
merged 43 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
7089eff
patch for L2 node rpc
noel2004 Oct 13, 2023
e035254
Merge remote-tracking branch 'origin/develop' into feat/mainnet_run
noel2004 Oct 16, 2023
ac2ad8b
constrain max_txs to pass pi circuit
noel2004 Oct 16, 2023
e9af451
Merge remote-tracking branch 'origin/develop' into feat/mainnet_run
noel2004 Oct 17, 2023
c94419d
revert get access set to the same implement as upstream
noel2004 Oct 17, 2023
e2b0733
update for using prestateTracer for state
noel2004 Oct 18, 2023
280c8f8
fix coinbase issue
noel2004 Oct 18, 2023
1d4e606
apply mock fill root
noel2004 Oct 18, 2023
645225c
better form for mock filling mpt
noel2004 Oct 19, 2023
91c0fd3
dep issue
noel2004 Oct 19, 2023
e884108
some trivial optimizations
noel2004 Oct 19, 2023
ff9e820
grep mpt standlone circuit from test
noel2004 Oct 20, 2023
f081696
update poseidon row params
noel2004 Oct 20, 2023
0adb0e7
fix issues in old_root setting
noel2004 Oct 20, 2023
dd0e65d
update ethblock for some fields
noel2004 Oct 23, 2023
fb44494
update more fields
noel2004 Oct 23, 2023
1c489fd
update keccak inputs
noel2004 Oct 23, 2023
aae2f3a
update prev state root
noel2004 Oct 23, 2023
7f1deca
update more fields for state root
noel2004 Oct 23, 2023
e2f8125
update code size
noel2004 Oct 23, 2023
ce02099
fix storage issue on missing field
noel2004 Oct 23, 2023
ff40bdd
optimize the pre state handling
noel2004 Oct 24, 2023
ac6e0f7
induce relax mode to raise some restrict
noel2004 Oct 24, 2023
4410789
picking coinbase/difficulity from block instead of default constants
noel2004 Oct 24, 2023
8a33907
custom diff/coinbase in padding
noel2004 Oct 25, 2023
3d71e90
fix the max_txs issue in pi_circuit
noel2004 Oct 25, 2023
db0d2fe
clippy and fmt
noel2004 Oct 25, 2023
ffd97e6
support single tx test
noel2004 Oct 25, 2023
65dd875
adjust params
noel2004 Oct 25, 2023
398544b
rebuild mpt state in mock tx proven
noel2004 Oct 25, 2023
04f7736
fmt
noel2004 Oct 25, 2023
80af57e
Merge remote-tracking branch 'origin/develop' into feat/mainnet_run
noel2004 Oct 25, 2023
bfde14f
resume some fields in prestate trace
noel2004 Oct 25, 2023
3733579
test creating supercircuit from dummy witness block
noel2004 Oct 25, 2023
f18d640
revert most params
noel2004 Oct 26, 2023
88f1b2f
Merge branch 'develop' into feat/mainnet_run
silathdiir Oct 26, 2023
00c2d46
mitigate an issue in prestate tracer
noel2004 Oct 31, 2023
44aea9c
add timeout spec for robust API call
noel2004 Oct 31, 2023
7f8ebfa
fmt
noel2004 Oct 31, 2023
28fe47e
add readme for test
noel2004 Nov 3, 2023
ba1bfe9
add more comment according to review
noel2004 Nov 3, 2023
3adb5e2
Merge remote-tracking branch 'origin/develop' into feat/mainnet_run
noel2004 Nov 9, 2023
360b0a6
Merge branch 'develop' into feat/mainnet_run
silathdiir Nov 16, 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
205 changes: 150 additions & 55 deletions bus-mapping/src/circuit_input_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::{
precompile::is_precompiled,
rpc::GethClient,
state_db::{self, CodeDB, StateDB},
util::{hash_code_keccak, KECCAK_CODE_HASH_EMPTY},
};
pub use access::{Access, AccessSet, AccessValue, CodeSource};
pub use block::{Block, BlockContext};
Expand Down Expand Up @@ -298,14 +299,25 @@ impl<'a> CircuitInputBuilder {
for (tx_index, tx) in eth_block.transactions.iter().enumerate() {
let chunk_tx_idx = self.block.txs.len();
if self.block.txs.len() >= self.block.circuits_params.max_txs {
log::error!(
"tx num overflow, MAX_TX limit {}, {}th tx(inner idx: {}) {:?}",
self.block.circuits_params.max_txs,
chunk_tx_idx,
tx.transaction_index.unwrap_or_default(),
tx.hash
);
return Err(Error::InternalError("tx num overflow"));
if self.block.is_relaxed() {
log::warn!(
"tx num overflow, MAX_TX limit {}, {}th tx(inner idx: {}) {:?}, would process for partial block",
self.block.circuits_params.max_txs,
chunk_tx_idx,
tx.transaction_index.unwrap_or_default(),
tx.hash
);
break;
} else {
log::error!(
"tx num overflow, MAX_TX limit {}, {}th tx(inner idx: {}) {:?}",
self.block.circuits_params.max_txs,
chunk_tx_idx,
tx.transaction_index.unwrap_or_default(),
tx.hash
);
return Err(Error::InternalError("tx num overflow"));
}
}
let geth_trace = &geth_traces[tx_index];
log::info!(
Expand Down Expand Up @@ -709,6 +721,12 @@ impl CircuitInputBuilder {
.iter()
.any(|tx| tx.has_l2_different_evm_behaviour_step())
}

/// enable relax mode for testing
pub fn enable_relax_mode(mut self) -> Self {
self.block = self.block.relax();
self
}
}

/// Return all the keccak inputs used during the processing of the current
Expand Down Expand Up @@ -950,7 +968,7 @@ pub struct BuilderClient<P: JsonRpcClient> {
pub fn get_state_accesses(
eth_block: &EthBlock,
geth_traces: &[eth_types::GethExecTrace],
) -> Result<Vec<Access>, Error> {
) -> Result<AccessSet, Error> {
let mut block_access_trace = vec![Access::new(
None,
RW::WRITE,
Expand All @@ -966,7 +984,7 @@ pub fn get_state_accesses(
block_access_trace.extend(tx_access_trace);
}

Ok(block_access_trace)
Ok(AccessSet::from(block_access_trace))
}

/// Build a partial StateDB from step 3
Expand Down Expand Up @@ -1060,26 +1078,11 @@ impl<P: JsonRpcClient> BuilderClient<P> {
}

/// Step 2. Get State Accesses from TxExecTraces
pub async fn get_state_accesses(&self, eth_block: &EthBlock) -> Result<AccessSet, Error> {
let mut access_set = AccessSet::default();
access_set.add_account(
eth_block
.author
.ok_or(Error::EthTypeError(eth_types::Error::IncompleteBlock))?,
);
let traces = self
.cli
.trace_block_prestate_by_hash(
eth_block
.hash
.ok_or(Error::EthTypeError(eth_types::Error::IncompleteBlock))?,
)
.await?;
for trace in traces.into_iter() {
access_set.extend_from_traces(&trace);
}

Ok(access_set)
pub fn get_state_accesses(
eth_block: &EthBlock,
geth_traces: &[eth_types::GethExecTrace],
) -> Result<AccessSet, Error> {
get_state_accesses(eth_block, geth_traces)
}

/// Step 3. Query geth for all accounts, storage keys, and codes from
Expand Down Expand Up @@ -1118,6 +1121,117 @@ impl<P: JsonRpcClient> BuilderClient<P> {
Ok((proofs, codes))
}

/// Yet-another Step 3. Get the account state and codes from pre-state tracing
/// the account state is limited since proof is not included,
/// but it is enough to build the sdb/cdb
/// if a hash for tx is provided, would return the prestate for this tx
pub async fn get_pre_state(
&self,
eth_block: &EthBlock,
tx_hash: Option<H256>,
) -> Result<
(
Vec<eth_types::EIP1186ProofResponse>,
HashMap<Address, Vec<u8>>,
),
Error,
> {
let traces = if let Some(tx_hash) = tx_hash {
vec![self.cli.trace_tx_prestate_by_hash(tx_hash).await?]
} else {
self.cli
.trace_block_prestate_by_hash(
eth_block
.hash
.ok_or(Error::EthTypeError(eth_types::Error::IncompleteBlock))?,
)
.await?
};

let mut account_set =
HashMap::<Address, (eth_types::EIP1186ProofResponse, HashMap<Word, Word>)>::new();
let mut code_set = HashMap::new();

for trace in traces.into_iter() {
for (addr, prestate) in trace.into_iter() {
let (_, storages) = account_set.entry(addr).or_insert_with(|| {
let code_size =
Word::from(prestate.code.as_ref().map(|bt| bt.len()).unwrap_or(0));
let (code_hash, keccak_code_hash) = if let Some(bt) = prestate.code {
let h = CodeDB::hash(&bt);
// only require for L2
let keccak_h = if cfg!(feature = "scroll") {
hash_code_keccak(&bt)
} else {
h
};
code_set.insert(addr, Vec::from(bt.as_ref()));
(h, keccak_h)
} else {
(CodeDB::empty_code_hash(), *KECCAK_CODE_HASH_EMPTY)
};

(
eth_types::EIP1186ProofResponse {
address: addr,
balance: prestate.balance.unwrap_or_default(),
nonce: prestate.nonce.unwrap_or_default().into(),
code_hash,
keccak_code_hash,
code_size,
..Default::default()
},
HashMap::new(),
)
});

if let Some(stg) = prestate.storage {
for (k, v) in stg {
storages.entry(k).or_insert(v);
}
}
}
}

// a hacking? since the coinbase address is not touch in prestate
let coinbase_addr = eth_block
.author
.ok_or(Error::EthTypeError(eth_types::Error::IncompleteBlock))?;
let block_num = eth_block
.number
.ok_or(Error::EthTypeError(eth_types::Error::IncompleteBlock))?;
assert_ne!(
block_num.as_u64(),
0,
"is not expected to access genesis block"
);
if let std::collections::hash_map::Entry::Vacant(e) = account_set.entry(coinbase_addr) {
let coinbase_proof = self
.cli
.get_proof(coinbase_addr, Vec::new(), (block_num - 1).into())
.await?;
e.insert((coinbase_proof, HashMap::new()));
}

Ok((
account_set
.into_iter()
.map(|(_, (mut acc_resp, storage_proofs))| {
acc_resp.storage_proof = storage_proofs
.into_iter()
.map(|(key, value)| eth_types::StorageProof {
key,
value,
..Default::default()
})
.collect();
acc_resp
})
.collect::<Vec<_>>(),
code_set,
))
}

/// Step 4. Build a partial StateDB from step 3
pub fn build_state_code_db(
proofs: Vec<eth_types::EIP1186ProofResponse>,
Expand Down Expand Up @@ -1184,8 +1298,8 @@ impl<P: JsonRpcClient> BuilderClient<P> {
> {
let (mut eth_block, mut geth_traces, history_hashes, prev_state_root) =
self.get_block(block_num).await?;
let access_set = self.get_state_accesses(&eth_block).await?;
let (proofs, codes) = self.get_state(block_num, access_set).await?;
//let access_set = Self::get_state_accesses(&eth_block, &geth_traces)?;
let (proofs, codes) = self.get_pre_state(&eth_block, None).await?;
let (state_db, code_db) = Self::build_state_code_db(proofs, codes);
if eth_block.transactions.len() > self.circuits_params.max_txs {
log::error!(
Expand Down Expand Up @@ -1220,7 +1334,7 @@ impl<P: JsonRpcClient> BuilderClient<P> {
let mut access_set = AccessSet::default();
for block_num in block_num_begin..block_num_end {
let (eth_block, geth_traces, _, _) = self.get_block(block_num).await?;
let mut access_list = self.get_state_accesses(&eth_block).await?;
let mut access_list = Self::get_state_accesses(&eth_block, &geth_traces)?;
access_set.extend(&mut access_list);
blocks_and_traces.push((eth_block, geth_traces));
}
Expand All @@ -1243,40 +1357,21 @@ impl<P: JsonRpcClient> BuilderClient<P> {

let mut tx: eth_types::Transaction = self.cli.get_tx_by_hash(tx_hash).await?;
tx.transaction_index = Some(0.into());
let geth_traces = self.cli.trace_tx_by_hash(tx_hash).await?;
let geth_trace = self.cli.trace_tx_by_hash(tx_hash).await?;
let mut eth_block = self
.cli
.get_block_by_number(tx.block_number.unwrap().into())
.await?;

eth_block.transactions = vec![tx.clone()];

let mut block_access_trace = vec![Access::new(
None,
RW::WRITE,
AccessValue::Account {
address: eth_block.author.unwrap(),
},
)];
let geth_trace = &geth_traces[0];
let tx_access_trace = gen_state_access_trace(
&eth_types::Block::<eth_types::Transaction>::default(),
&tx,
geth_trace,
)?;
block_access_trace.extend(tx_access_trace);

let access_set = AccessSet::from(block_access_trace);

let (proofs, codes) = self
.get_state(tx.block_number.unwrap().as_u64(), access_set)
.await?;
let (proofs, codes) = self.get_pre_state(&eth_block, Some(tx_hash)).await?;
let (state_db, code_db) = Self::build_state_code_db(proofs, codes);
let builder = self.gen_inputs_from_state(
state_db,
code_db,
&eth_block,
&geth_traces,
&[geth_trace],
Default::default(),
Default::default(),
)?;
Expand Down
16 changes: 16 additions & 0 deletions bus-mapping/src/circuit_input_builder/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ pub struct Block {
pub precompile_events: PrecompileEvents,
/// circuit capacity counter
copy_counter: usize,
/// relax mode indicate builder and circuit would skip
/// some sanity check, used by testing and debugging
relax_mode: bool,
}

impl Block {
Expand Down Expand Up @@ -311,6 +314,11 @@ impl Block {
self.chain_id
}

/// Return if the relax mode
pub fn is_relaxed(&self) -> bool {
self.relax_mode
}

/// ..
pub fn end_state_root(&self) -> Word {
self.headers
Expand All @@ -323,6 +331,14 @@ impl Block {
pub fn txs_mut(&mut self) -> &mut Vec<Transaction> {
&mut self.txs
}

/// switch to relax mode (used by testing and debugging,
/// see the note in defination of `relax_mode`)
#[cfg(feature = "test")]
pub fn relax(mut self) -> Self {
self.relax_mode = true;
self
}
}

impl Block {
Expand Down
24 changes: 19 additions & 5 deletions bus-mapping/src/evm/opcodes/begin_end_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,25 @@ pub fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep,
if state.tx.is_create()
&& ((!account_code_hash_is_empty_or_zero) || !callee_account.nonce.is_zero())
{
unimplemented!(
"deployment collision at {:?}, account {:?}",
call.address,
callee_account
);
// since there is a bug in the prestate
// tracer: https://github.com/ethereum/go-ethereum/issues/28439
// which may also act as the data source for our statedb,
// we have to relax the constarint a bit and fix it silently
if account_code_hash_is_empty_or_zero && callee_account.nonce == 1.into() {
log::warn!(
"fix deployment nonce for {:?} silently for the prestate tracer",
call.address,
);
let mut fixed_account = callee_account.clone();
fixed_account.nonce = Word::zero();
state.sdb.set_account(&call.address, fixed_account);
} else {
unimplemented!(
"deployment collision at {:?}, account {:?}",
call.address,
callee_account
);
}
}

// Transfer with fee
Expand Down
3 changes: 1 addition & 2 deletions bus-mapping/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ impl BlockData {

let access_set: AccessSet =
get_state_accesses(&geth_data.eth_block, &geth_data.geth_traces)
.expect("state accesses")
.into();
.expect("state accesses");
// Initialize all accesses accounts to zero
for addr in access_set.state.keys() {
sdb.set_account(addr, state_db::Account::zero());
Expand Down
Loading