From 5be64611821f388ae76626a141c96d68dc75106a Mon Sep 17 00:00:00 2001 From: BGluth Date: Thu, 9 May 2024 14:14:34 -0600 Subject: [PATCH] SQUASH!! --- trace_decoder/src/decoding.rs | 609 ++++++++++++++- trace_decoder/src/decoding_mpt.rs | 705 +++--------------- trace_decoder/src/decoding_smt.rs | 2 +- trace_decoder/src/processed_block_trace.rs | 33 +- .../src/processed_block_trace_mpt.rs | 38 +- .../src/processed_block_trace_smt.rs | 3 +- 6 files changed, 728 insertions(+), 662 deletions(-) diff --git a/trace_decoder/src/decoding.rs b/trace_decoder/src/decoding.rs index f9b6b7bdc..d2d134343 100644 --- a/trace_decoder/src/decoding.rs +++ b/trace_decoder/src/decoding.rs @@ -1,29 +1,48 @@ -use std::fmt::{self, Display, Formatter}; +use std::{collections::HashMap, fmt::{self, Display, Formatter}, iter::once}; use ethereum_types::{Address, U256, U512}; use keccak_hash::H256; -use mpt_trie::{nibbles::Nibbles, trie_ops::TrieOpError}; +use mpt_trie::{nibbles::Nibbles, partial_trie::HashedPartialTrie, trie_ops::TrieOpError}; use thiserror::Error; use crate::{ - compact::compact_processing_common::CompactParsingError, - types::{HashedAccountAddr, StorageVal, TrieRootHash}, - utils::{hash, optional_field, optional_field_hex}, + aliased_crate_types::{MptExtraBlockData, MptTrieInputs, MptTrieRoots}, compact::compact_processing_common::CompactParsingError, decoding_mpt::TxnMetaState, processed_block_trace::{NodesUsedByTxn, ProcessedBlockTrace, ProcessedSectionInfo, ProcessedSectionTxnInfo}, types::{HashedAccountAddr, HashedStorageAddr, OtherBlockData, StorageVal, TrieRootHash, TxnIdx, EMPTY_ACCOUNT_BYTES_RLPED, ZERO_STORAGE_SLOT_VAL_RLPED}, utils::{hash, optional_field, optional_field_hex} }; +pub type TraceParsingResult = Result>; + pub(crate) trait ProcessedBlockTraceDecode { + type Spec; type CurrBlockTries; type TrieInputs; type AccountRlp; + type Ir; + type TState: Clone + TrieState; + + fn get_trie_pre_image(spec: &Self::Spec) -> Self::TState; fn delete_node(h_addr: &Nibbles); - fn write_account_data(h_addr: HashedAccountAddr, data: Self::AccountRlp); - fn delete_account(h_addr: HashedAccountAddr); + fn create_trie_subsets(tries: &Self::CurrBlockTries) -> Self::TrieInputs; +} + +pub(crate) trait TrieState { + type AccountRlp; - fn set_storage_slot(h_addr: HashedAccountAddr, h_slot: HashedAccountAddr, val: StorageVal); + fn account_has_storage(&self, h_addr: &HashedAccountAddr) -> bool; + fn write_account_data(&mut self, h_addr: HashedAccountAddr, data: Self::AccountRlp); + fn delete_account(&mut self, h_addr: &HashedAccountAddr); - fn create_trie_subsets(tries: &Self::CurrBlockTries) -> Self::TrieInputs; + fn set_storage_slot(&mut self, h_addr: HashedAccountAddr, h_slot: HashedAccountAddr, val: NodeInsertType); + + fn insert_receipt_node(&mut self, txn_idx: Nibbles, node_bytes: &[u8]); + fn insert_txn_node(&mut self, txn_idx: Nibbles, node_bytes: &[u8]); +} + +#[derive(Debug)] +pub(crate) enum NodeInsertType { + Val(Vec), + Hash(H256), } // TODO: Make this also work with SMT decoding... @@ -229,6 +248,15 @@ pub struct TraceDecodingError { reason: TraceParsingErrorReason, // The original error type } +/// Additional information discovered during delta application. +#[derive(Debug, Default)] +struct TrieDeltaApplicationOutput { + // During delta application, if a delete occurs, we may have to make sure additional nodes + // that are not accessed by the txn remain unhashed. + additional_state_trie_paths_to_not_hash: Vec, + additional_storage_trie_paths_to_not_hash: HashMap>, +} + impl Display for TraceDecodingError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let h_slot = self.slot.map(|slot| { @@ -312,3 +340,566 @@ impl TraceDecodingError { self } } + +impl ProcessedBlockTrace +where + D: ProcessedBlockTraceDecode +{ + + pub(crate) fn into_proof_gen_ir( + self, + other_data: OtherBlockData, + ) -> TraceParsingResult> { + match self.sect_info { + ProcessedSectionInfo::Continuations(_) => { + todo!("MPT continuations are not implemented yet!") + } + ProcessedSectionInfo::Txns(txns) => { + Self::process_txns(txns, D::get_trie_pre_image(&self.spec), self.withdrawals, &other_data) + } + } + } + + fn process_txns( + txns: Vec, + tries: D::TState, + withdrawals: Vec<(Address, U256)>, + other_data: &OtherBlockData, + ) -> TraceParsingResult> { + let mut curr_block_tries = tries; + + // This is just a copy of `curr_block_tries`. + // TODO: Check if we can remove these clones before PR merge... + let initial_tries_for_dummies = curr_block_tries.clone(); + + let mut extra_data = MptExtraBlockData { + checkpoint_state_trie_root: other_data.checkpoint_state_trie_root, + txn_number_before: U256::zero(), + txn_number_after: U256::zero(), + gas_used_before: U256::zero(), + gas_used_after: U256::zero(), + }; + + // A copy of the initial extra_data possibly needed during padding. + let extra_data_for_dummies = extra_data.clone(); + + let mut ir = txns + .into_iter() + .enumerate() + .map(|(txn_idx, sect_info)| { + Self::process_txn_info( + txn_idx, + sect_info, + &mut curr_block_tries, + &mut extra_data, + other_data, + ) + .map_err(|mut e| { + e.txn_idx(txn_idx); + e + }) + }) + .collect::>>() + .map_err(|mut e| { + e.block_num(other_data.b_data.b_meta.block_number); + e.block_chain_id(other_data.b_data.b_meta.block_chain_id); + e + })?; + + Self::pad_gen_inputs_with_dummy_inputs_if_needed( + &mut ir, + other_data, + &extra_data, + &extra_data_for_dummies, + &initial_tries_for_dummies, + &curr_block_tries, + ); + + if !withdrawals.is_empty() { + Self::add_withdrawals_to_txns(&mut ir, &mut curr_block_tries, withdrawals.clone())?; + } + + Ok(ir) + } + + fn update_txn_and_receipt_tries( + trie_state: &mut D::TState, + meta: &TxnMetaState, + txn_idx: TxnIdx, + ) { + let txn_k = Nibbles::from_bytes_be(&rlp::encode(&txn_idx)).unwrap(); + + trie_state.insert_txn_node(txn_k, &meta.txn_bytes()); + trie_state.insert_receipt_node(txn_k, meta.receipt_node_bytes.as_ref()); + } + + /// If the account does not have a storage trie or does but is not + /// accessed by any txns, then we still need to manually create an entry for + /// them. + fn init_any_needed_empty_storage_tries<'a>( + trie_state: &mut D::TState, + accounts_with_storage: impl Iterator, + state_accounts_with_no_accesses_but_storage_tries: &'a HashMap< + HashedAccountAddr, + TrieRootHash, + >, + ) { + for h_addr in accounts_with_storage { + + if !trie_state.account_has_storage(h_addr) { + trie_state.set_storage_slot(h_addr, h_slot, val) + + let trie = state_accounts_with_no_accesses_but_storage_tries + .get(h_addr) + .map(|s_root| HashedPartialTrie::new(Node::Hash(*s_root))) + .unwrap_or_default(); + + storage_tries.insert(*h_addr, trie); + }; + } + } + + fn apply_deltas_to_trie_state( + trie_state: &mut D::TState, + deltas: &NodesUsedByTxn, + meta: &TxnMetaState, + ) -> TraceDecodingResult { + let mut out = TrieDeltaApplicationOutput::default(); + + for (hashed_acc_addr, storage_writes) in deltas.storage_writes.iter() { + let mut storage_trie = + trie_state.storage.get_mut(hashed_acc_addr).ok_or_else(|| { + let hashed_acc_addr = *hashed_acc_addr; + let mut e = TraceParsingError::new( + TraceParsingErrorReason::MissingAccountStorageTrie(hashed_acc_addr), + ); + e.h_addr(hashed_acc_addr); + e + })?; + + for (slot, val) in storage_writes + .iter() + .map(|(k, v)| (Nibbles::from_h256_be(hash(&k.bytes_be())), v)) + { + // If we are writing a zero, then we actually need to perform a delete. + match val == &ZERO_STORAGE_SLOT_VAL_RLPED { + false => storage_trie.insert(slot, val.clone()).map_err(|err| { + let mut e = + TraceParsingError::new(TraceParsingErrorReason::TrieOpError(err)); + e.slot(U512::from_big_endian(slot.bytes_be().as_slice())); + e.slot_value(U512::from_big_endian(val.as_slice())); + e + })?, + true => { + if let Some(remaining_slot_key) = + Self::delete_node_and_report_remaining_key_if_branch_collapsed( + storage_trie, + &slot, + ) + { + out.additional_storage_trie_paths_to_not_hash + .entry(*hashed_acc_addr) + .or_default() + .push(remaining_slot_key); + } + } + }; + } + } + + for (hashed_acc_addr, s_trie_writes) in deltas.state_writes.iter() { + let val_k = Nibbles::from_h256_be(*hashed_acc_addr); + + // If the account was created, then it will not exist in the trie. + let val_bytes = trie_state + .state + .get(val_k) + .unwrap_or(&EMPTY_ACCOUNT_BYTES_RLPED); + + let mut account = account_from_rlped_bytes(val_bytes)?; + + s_trie_writes.apply_writes_to_state_node( + &mut account, + hashed_acc_addr, + &trie_state.storage, + )?; + + let updated_account_bytes = rlp::encode(&account); + trie_state + .state + .insert(val_k, updated_account_bytes.to_vec()); + } + + // Remove any accounts that self-destructed. + for hashed_addr in deltas.self_destructed_accounts.iter() { + let k = Nibbles::from_h256_be(*hashed_addr); + + trie_state.storage.remove(hashed_addr).ok_or_else(|| { + let hashed_addr = *hashed_addr; + let mut e = TraceParsingError::new( + TraceParsingErrorReason::MissingAccountStorageTrie(hashed_addr), + ); + e.h_addr(hashed_addr); + e + })?; + + // TODO: Once the mechanism for resolving code hashes settles, we probably want + // to also delete the code hash mapping here as well... + + if let Some(remaining_account_key) = + Self::delete_node_and_report_remaining_key_if_branch_collapsed( + &mut trie_state.state, + &k, + ) + { + out.additional_state_trie_paths_to_not_hash + .push(remaining_account_key); + } + } + + Ok(out) + } + + /// Pads a generated IR vec with additional "dummy" entries if needed. + /// We need to ensure that generated IR always has at least `2` elements, + /// and if there are only `0` or `1` elements, then we need to pad so + /// that we have two entries in total. These dummy entries serve only to + /// allow the proof generation process to finish. Specifically, we need + /// at least two entries to generate an agg proof, and we need an agg + /// proof to generate a block proof. These entries do not mutate state. + fn pad_gen_inputs_with_dummy_inputs_if_needed( + gen_inputs: &mut Vec, + other_data: &OtherBlockData, + final_extra_data: &MptExtraBlockData, + initial_extra_data: &MptExtraBlockData, + initial_tries: &PartialTrieState, + final_tries: &PartialTrieState, + ) { + match gen_inputs.len() { + 0 => { + debug_assert!(initial_tries.state == final_tries.state); + debug_assert!(initial_extra_data == final_extra_data); + // We need to pad with two dummy entries. + gen_inputs.extend(create_dummy_txn_pair_for_empty_block( + other_data, + final_extra_data, + initial_tries, + )); + } + 1 => { + // We just need one dummy entry. + // The dummy proof will be prepended to the actual txn. + let dummy_txn = + create_dummy_gen_input(other_data, initial_extra_data, initial_tries); + gen_inputs.insert(0, dummy_txn) + } + _ => (), + } + } + + /// The withdrawals are always in the final ir payload. + fn add_withdrawals_to_txns( + txn_ir: &mut [GenerationInputs], + final_trie_state: &mut PartialTrieState, + withdrawals: Vec<(Address, U256)>, + ) -> MptTraceParsingResult<()> { + let withdrawals_with_hashed_addrs_iter = || { + withdrawals + .iter() + .map(|(addr, v)| (*addr, hash(addr.as_bytes()), *v)) + }; + + let last_inputs = txn_ir + .last_mut() + .expect("We cannot have an empty list of payloads."); + + if last_inputs.signed_txn.is_none() { + // This is a dummy payload, hence it does not contain yet + // state accesses to the withdrawal addresses. + let withdrawal_addrs = + withdrawals_with_hashed_addrs_iter().map(|(_, h_addr, _)| h_addr); + last_inputs.tries.state_trie = create_minimal_state_partial_trie( + &last_inputs.tries.state_trie, + withdrawal_addrs, + iter::empty(), + )?; + } + + Self::update_trie_state_from_withdrawals( + withdrawals_with_hashed_addrs_iter(), + &mut final_trie_state.state, + )?; + + last_inputs.withdrawals = withdrawals; + last_inputs.trie_roots_after.state_root = final_trie_state.state.hash(); + + Ok(()) + } + + /// Withdrawals update balances in the account trie, so we need to update + /// our local trie state. + fn update_trie_state_from_withdrawals<'a>( + withdrawals: impl IntoIterator + 'a, + state: &mut HashedPartialTrie, + ) -> MptTraceParsingResult<()> { + for (addr, h_addr, amt) in withdrawals { + let h_addr_nibs = Nibbles::from_h256_be(h_addr); + + let acc_bytes = state.get(h_addr_nibs).ok_or_else(|| { + let mut e = TraceParsingError::new( + TraceParsingErrorReason::MissingWithdrawalAccount(addr, h_addr, amt), + ); + e.addr(addr); + e.h_addr(h_addr); + e + })?; + let mut acc_data = account_from_rlped_bytes(acc_bytes)?; + + acc_data.balance += amt; + + state.insert(h_addr_nibs, rlp::encode(&acc_data).to_vec()); + } + + Ok(()) + } + + /// Processes a single transaction in the trace. + fn process_txn_info( + txn_idx: usize, + txn_info: ProcessedSectionTxnInfo, + curr_block_tries: &mut PartialTrieState, + extra_data: &mut MptExtraBlockData, + other_data: &OtherBlockData, + ) -> MptTraceParsingResult { + trace!("Generating proof IR for txn {}...", txn_idx); + + Self::init_any_needed_empty_storage_tries( + &mut curr_block_tries.storage, + txn_info + .nodes_used_by_txn + .storage_accesses + .iter() + .map(|(k, _)| k), + &txn_info + .nodes_used_by_txn + .state_accounts_with_no_accesses_but_storage_tries, + ); + // For each non-dummy txn, we increment `txn_number_after` by 1, and + // update `gas_used_after` accordingly. + extra_data.txn_number_after += U256::one(); + extra_data.gas_used_after += txn_info.meta.gas_used.into(); + + // Because we need to run delta application before creating the minimal + // sub-tries (we need to detect if deletes collapsed any branches), we need to + // do this clone every iteration. + let tries_at_start_of_txn = curr_block_tries.clone(); + + Self::update_txn_and_receipt_tries(curr_block_tries, &txn_info.meta, txn_idx); + + let delta_out = Self::apply_deltas_to_trie_state( + curr_block_tries, + &txn_info.nodes_used_by_txn, + &txn_info.meta, + )?; + + let tries = Self::create_minimal_partial_tries_needed_by_txn( + &tries_at_start_of_txn, + &txn_info.nodes_used_by_txn, + txn_idx, + delta_out, + &other_data.b_data.b_meta.block_beneficiary, + )?; + + let trie_roots_after = calculate_trie_input_hashes(curr_block_tries); + let gen_inputs = GenerationInputs { + txn_number_before: extra_data.txn_number_before, + gas_used_before: extra_data.gas_used_before, + gas_used_after: extra_data.gas_used_after, + signed_txn: txn_info.meta.txn_bytes, + withdrawals: Vec::default(), /* Only ever set in a dummy txn at the end of + * the block (see `[add_withdrawals_to_txns]` + * for more info). */ + tries, + trie_roots_after, + checkpoint_state_trie_root: extra_data.checkpoint_state_trie_root, + contract_code: txn_info.contract_code_accessed, + block_metadata: other_data.b_data.b_meta.clone(), + block_hashes: other_data.b_data.b_hashes.clone(), + }; + + // After processing a transaction, we update the remaining accumulators + // for the next transaction. + extra_data.txn_number_before += U256::one(); + extra_data.gas_used_before = extra_data.gas_used_after; + + Ok(gen_inputs) + } +} + +impl StateTrieWrites { + fn apply_writes_to_state_node( + &self, + state_node: &mut MptAccountRlp, + h_addr: &HashedAccountAddr, + acc_storage_tries: &HashMap, + ) -> MptTraceParsingResult<()> { + let storage_root_hash_change = match self.storage_trie_change { + false => None, + true => { + let storage_trie = acc_storage_tries.get(h_addr).ok_or_else(|| { + let h_addr = *h_addr; + let mut e = TraceParsingError::new( + TraceParsingErrorReason::MissingAccountStorageTrie(h_addr), + ); + e.h_addr(h_addr); + e + })?; + + Some(storage_trie.hash()) + } + }; + + update_val_if_some(&mut state_node.balance, self.balance); + update_val_if_some(&mut state_node.nonce, self.nonce); + update_val_if_some(&mut state_node.storage_root, storage_root_hash_change); + update_val_if_some(&mut state_node.code_hash, self.code_hash); + + Ok(()) + } +} + +fn calculate_trie_input_hashes(t_inputs: &PartialTrieState) -> MptTrieRoots { + MptTrieRoots { + state_root: t_inputs.state.hash(), + transactions_root: t_inputs.txn.hash(), + receipts_root: t_inputs.receipt.hash(), + } +} + +// We really want to get a trie with just a hash node here, and this is an easy +// way to do it. +fn create_fully_hashed_out_sub_partial_trie(trie: &HashedPartialTrie) -> HashedPartialTrie { + // Impossible to actually fail with an empty iter. + create_trie_subset(trie, empty::()).unwrap() +} + +fn create_dummy_txn_pair_for_empty_block( + other_data: &OtherBlockData, + extra_data: &MptExtraBlockData, + final_tries: &PartialTrieState, +) -> [GenerationInputs; 2] { + [ + create_dummy_gen_input(other_data, extra_data, final_tries), + create_dummy_gen_input(other_data, extra_data, final_tries), + ] +} + +fn create_dummy_gen_input( + other_data: &OtherBlockData, + extra_data: &MptExtraBlockData, + final_tries: &PartialTrieState, +) -> GenerationInputs { + let sub_tries = create_dummy_proof_trie_inputs( + final_tries, + create_fully_hashed_out_sub_partial_trie(&final_tries.state), + ); + create_dummy_gen_input_common(other_data, extra_data, sub_tries) +} + +fn create_dummy_gen_input_with_state_addrs_accessed( + other_data: &OtherBlockData, + extra_data: &MptExtraBlockData, + final_tries: &PartialTrieState, + account_addrs_accessed: impl Iterator, +) -> MptTraceParsingResult { + let sub_tries = create_dummy_proof_trie_inputs( + final_tries, + create_minimal_state_partial_trie( + &final_tries.state, + account_addrs_accessed, + iter::empty(), + )?, + ); + Ok(create_dummy_gen_input_common( + other_data, extra_data, sub_tries, + )) +} + +fn create_dummy_gen_input_common( + other_data: &OtherBlockData, + extra_data: &MptExtraBlockData, + sub_tries: MptTrieInputs, +) -> GenerationInputs { + let trie_roots_after = MptTrieRoots { + state_root: sub_tries.state_trie.hash(), + transactions_root: sub_tries.transactions_trie.hash(), + receipts_root: sub_tries.receipts_trie.hash(), + }; + + // Sanity checks + assert_eq!( + extra_data.txn_number_before, extra_data.txn_number_after, + "Txn numbers before/after differ in a dummy payload with no txn!" + ); + assert_eq!( + extra_data.gas_used_before, extra_data.gas_used_after, + "Gas used before/after differ in a dummy payload with no txn!" + ); + + GenerationInputs { + signed_txn: None, + tries: sub_tries, + trie_roots_after, + checkpoint_state_trie_root: extra_data.checkpoint_state_trie_root, + block_metadata: other_data.b_data.b_meta.clone(), + block_hashes: other_data.b_data.b_hashes.clone(), + txn_number_before: extra_data.txn_number_before, + gas_used_before: extra_data.gas_used_before, + gas_used_after: extra_data.gas_used_after, + contract_code: HashMap::default(), + withdrawals: vec![], // this is set after creating dummy payloads + } +} + +fn create_dummy_proof_trie_inputs( + final_tries_at_end_of_block: &PartialTrieState, + state_trie: HashedPartialTrie, +) -> MptTrieInputs { + let partial_sub_storage_tries: Vec<_> = final_tries_at_end_of_block + .storage + .iter() + .map(|(hashed_acc_addr, s_trie)| { + ( + *hashed_acc_addr, + create_fully_hashed_out_sub_partial_trie(s_trie), + ) + }) + .collect(); + + MptTrieInputs { + state_trie, + transactions_trie: create_fully_hashed_out_sub_partial_trie( + &final_tries_at_end_of_block.txn, + ), + receipts_trie: create_fully_hashed_out_sub_partial_trie( + &final_tries_at_end_of_block.receipt, + ), + storage_tries: partial_sub_storage_tries, + } +} + +#[derive(Debug, Default)] +pub(crate) struct TxnMetaState { + pub(crate) txn_bytes: Option>, + pub(crate) receipt_node_bytes: Vec, + pub(crate) gas_used: u64, +} + +impl TxnMetaState { + fn txn_bytes(&self) -> Vec { + match self.txn_bytes.as_ref() { + Some(v) => v.clone(), + None => Vec::default(), + } + } +} diff --git a/trace_decoder/src/decoding_mpt.rs b/trace_decoder/src/decoding_mpt.rs index 592d1b25e..bca11a441 100644 --- a/trace_decoder/src/decoding_mpt.rs +++ b/trace_decoder/src/decoding_mpt.rs @@ -19,289 +19,61 @@ use mpt_trie::{ use crate::{ aliased_crate_types::{ MptAccountRlp, MptExtraBlockData, MptGenerationInputs, MptTrieInputs, MptTrieRoots, - }, - compact::compact_mpt_processing::MptPartialTriePreImages, - decoding::{ - TraceDecodingError, TraceDecodingResult, TraceParsingError, TraceParsingErrorReason, - TrieType, - }, - processed_block_trace::{ - NodesUsedByTxn, ProcessedSectionInfo, ProcessedSectionTxnInfo, StateTrieWrites, - }, - processed_block_trace_mpt::MptProcessedBlockTrace, - types::{ - HashedAccountAddr, HashedNodeAddr, HashedStorageAddr, HashedStorageAddrNibbles, - OtherBlockData, TrieRootHash, TxnIdx, EMPTY_ACCOUNT_BYTES_RLPED, - ZERO_STORAGE_SLOT_VAL_RLPED, - }, - utils::{hash, update_val_if_some}, + }, compact::compact_mpt_processing::MptPartialTriePreImages, decoding::{ + ProcessedBlockTraceDecode, TraceDecodingError, TraceDecodingResult, TraceParsingError, TraceParsingErrorReason, TrieType + }, processed_block_trace::{ + NodesUsedByTxn, ProcessedSectionInfo, ProcessedSectionTxnInfo, ProcessingMeta, StateTrieWrites + }, processed_block_trace_mpt::{MptBlockTraceProcessing, MptProcessedBlockTrace, ProcedBlockTraceMptSpec}, protocol_processing::TraceProtocolDecodingResult, trace_protocol::BlockTrace, types::{ + CodeHashResolveFunc, HashedAccountAddr, HashedNodeAddr, HashedStorageAddr, HashedStorageAddrNibbles, OtherBlockData, TrieRootHash, TxnIdx, EMPTY_ACCOUNT_BYTES_RLPED, ZERO_STORAGE_SLOT_VAL_RLPED + }, utils::{hash, update_val_if_some} }; // TODO: Make a final decision if we need a separate error for MPT... pub(crate) type MptTraceParsingError = TraceParsingError; -impl MptProcessedBlockTrace { - pub(crate) fn into_proof_gen_ir( - self, - other_data: OtherBlockData, - ) -> TraceDecodingResult> { - match self.spec.sect_info { - ProcessedSectionInfo::Continuations(_) => { - todo!("MPT continuations are not implemented yet!") - } - ProcessedSectionInfo::Txns(txns) => { - Self::process_txns(txns, self.spec.tries, self.withdrawals, &other_data) - } - } - } - - fn process_txns( - txns: Vec, - tries: MptPartialTriePreImages, - withdrawals: Vec<(Address, U256)>, - other_data: &OtherBlockData, - ) -> TraceDecodingResult> { - let mut curr_block_tries = PartialTrieState { - state: tries.state.clone(), - storage: tries.storage.clone(), +impl ProcessedBlockTraceDecode for MptProcessedBlockTrace { + type Spec = ProcedBlockTraceMptSpec; + type CurrBlockTries = PartialTrieState; + type TrieInputs; + type AccountRlp; + type Ir; + type TState; + + fn get_trie_pre_image(spec: &Self::Spec) -> Self::TState { + Self::TState { + state: spec.tries.state.clone(), + storage: spec.tries.storage.clone(), ..Default::default() - }; - - // This is just a copy of `curr_block_tries`. - let initial_tries_for_dummies = PartialTrieState { - state: tries.state.clone(), - storage: tries.storage.clone(), - ..Default::default() - }; - - let mut extra_data = MptExtraBlockData { - checkpoint_state_trie_root: other_data.checkpoint_state_trie_root, - txn_number_before: U256::zero(), - txn_number_after: U256::zero(), - gas_used_before: U256::zero(), - gas_used_after: U256::zero(), - }; - - // A copy of the initial extra_data possibly needed during padding. - let extra_data_for_dummies = extra_data.clone(); - - let mut ir = txns - .into_iter() - .enumerate() - .map(|(txn_idx, sect_info)| { - Self::process_txn_info( - txn_idx, - sect_info, - &mut curr_block_tries, - &mut extra_data, - other_data, - ) - .map_err(|mut e| { - e.txn_idx(txn_idx); - e - }) - }) - .collect::>() - .map_err(|mut e| { - e.block_num(other_data.b_data.b_meta.block_number); - e.block_chain_id(other_data.b_data.b_meta.block_chain_id); - e - })?; - - Self::pad_gen_inputs_with_dummy_inputs_if_needed( - &mut ir, - other_data, - &extra_data, - &extra_data_for_dummies, - &initial_tries_for_dummies, - &curr_block_tries, - ); - - if !withdrawals.is_empty() { - Self::add_withdrawals_to_txns(&mut ir, &mut curr_block_tries, withdrawals.clone())?; } - - Ok(ir) } - fn update_txn_and_receipt_tries( - trie_state: &mut PartialTrieState, - meta: &TxnMetaState, - txn_idx: TxnIdx, - ) -> TrieOpResult<()> { - let txn_k = Nibbles::from_bytes_be(&rlp::encode(&txn_idx)).unwrap(); - trie_state.txn.insert(txn_k, meta.txn_bytes())?; - - trie_state - .receipt - .insert(txn_k, meta.receipt_node_bytes.as_ref()) - .map_err(TrieOpError::from) + fn delete_node(h_addr: &Nibbles) { + todo!() } - /// If the account does not have a storage trie or does but is not - /// accessed by any txns, then we still need to manually create an entry for - /// them. - fn init_any_needed_empty_storage_tries<'a>( - storage_tries: &mut HashMap, - accounts_with_storage: impl Iterator, - state_accounts_with_no_accesses_but_storage_tries: &'a HashMap< - HashedAccountAddr, - TrieRootHash, - >, - ) { - for h_addr in accounts_with_storage { - if !storage_tries.contains_key(h_addr) { - let trie = state_accounts_with_no_accesses_but_storage_tries - .get(h_addr) - .map(|s_root| HashedPartialTrie::new(Node::Hash(*s_root))) - .unwrap_or_default(); - - storage_tries.insert(*h_addr, trie); - }; - } + fn write_account_data(h_addr: HashedAccountAddr, data: evm_arithmetization_mpt::generation::mpt::AccountRlp) { + todo!() } - fn create_minimal_partial_tries_needed_by_txn( - curr_block_tries: &PartialTrieState, - nodes_used_by_txn: &NodesUsedByTxn, - txn_idx: TxnIdx, - delta_application_out: TrieDeltaApplicationOutput, - _coin_base_addr: &Address, - ) -> TraceDecodingResult { - let state_trie = create_minimal_state_partial_trie( - &curr_block_tries.state, - nodes_used_by_txn.state_accesses.iter().cloned(), - delta_application_out - .additional_state_trie_paths_to_not_hash - .into_iter(), - )?; - - let txn_k = Nibbles::from_bytes_be(&rlp::encode(&txn_idx)).unwrap(); - // TODO: Replace cast once `mpt_trie` supports `into` for `usize... - let transactions_trie = - create_trie_subset_wrapped(&curr_block_tries.txn, once(txn_k), TrieType::Txn)?; - - let receipts_trie = - create_trie_subset_wrapped(&curr_block_tries.receipt, once(txn_k), TrieType::Receipt)?; - - let storage_tries = create_minimal_storage_partial_tries( - &curr_block_tries.storage, - nodes_used_by_txn.storage_accesses.iter(), - &delta_application_out.additional_storage_trie_paths_to_not_hash, - )?; - - Ok(MptTrieInputs { - state_trie, - transactions_trie, - receipts_trie, - storage_tries, - }) + fn delete_account(h_addr: HashedAccountAddr) { + todo!() } - fn apply_deltas_to_trie_state( - trie_state: &mut PartialTrieState, - deltas: &NodesUsedByTxn, - ) -> TraceDecodingResult { - let mut out: TrieDeltaApplicationOutput = TrieDeltaApplicationOutput::default(); - - for (hashed_acc_addr, storage_writes) in deltas.storage_writes.iter() { - let storage_trie = trie_state.storage.get_mut(hashed_acc_addr).ok_or_else(|| { - let hashed_acc_addr = *hashed_acc_addr; - let mut e = TraceDecodingError::new( - TraceParsingErrorReason::MissingAccountStorageTrie(hashed_acc_addr), - ); - e.h_addr(hashed_acc_addr); - e - })?; - - for (slot, val) in storage_writes - .iter() - .map(|(k, v)| (Nibbles::from_h256_be(hash(&k.bytes_be())), v)) - { - // If we are writing a zero, then we actually need to perform a delete. - match val == &ZERO_STORAGE_SLOT_VAL_RLPED { - false => storage_trie.insert(slot, val.clone()).map_err(|err| { - let mut e = - TraceDecodingError::new(TraceParsingErrorReason::TrieOpError(err)); - e.slot(U512::from_big_endian(slot.bytes_be().as_slice())); - e.slot_value(U512::from_big_endian(val.as_slice())); - e - })?, - true => { - if let Some(remaining_slot_key) = - Self::delete_node_and_report_remaining_key_if_branch_collapsed( - storage_trie, - &slot, - ) - .map_err(TraceDecodingError::from)? - { - out.additional_storage_trie_paths_to_not_hash - .entry(*hashed_acc_addr) - .or_default() - .push(remaining_slot_key); - } - } - }; - } - } - - for (hashed_acc_addr, s_trie_writes) in deltas.state_writes.iter() { - let val_k = Nibbles::from_h256_be(*hashed_acc_addr); - - // If the account was created, then it will not exist in the trie. - let val_bytes = trie_state - .state - .get(val_k) - .unwrap_or(&EMPTY_ACCOUNT_BYTES_RLPED); - - let mut account = account_from_rlped_bytes(val_bytes)?; - - s_trie_writes.apply_writes_to_state_node( - &mut account, - hashed_acc_addr, - &trie_state.storage, - )?; - - let updated_account_bytes = rlp::encode(&account); - trie_state - .state - .insert(val_k, updated_account_bytes.to_vec()) - .map_err(TraceDecodingError::from)?; - } - - // Remove any accounts that self-destructed. - for hashed_addr in deltas.self_destructed_accounts.iter() { - let k = Nibbles::from_h256_be(*hashed_addr); - - trie_state.storage.remove(hashed_addr).ok_or_else(|| { - let hashed_addr = *hashed_addr; - let mut e = TraceDecodingError::new( - TraceParsingErrorReason::MissingAccountStorageTrie(hashed_addr), - ); - e.h_addr(hashed_addr); - e - })?; - - // TODO: Once the mechanism for resolving code hashes settles, we probably want - // to also delete the code hash mapping here as well... - - if let Some(remaining_account_key) = - Self::delete_node_and_report_remaining_key_if_branch_collapsed( - &mut trie_state.state, - &k, - ) - .map_err(TraceDecodingError::from)? - { - out.additional_state_trie_paths_to_not_hash - .push(remaining_account_key); - } - } + fn set_storage_slot(h_addr: HashedAccountAddr, h_slot: HashedAccountAddr, val: crate::types::StorageVal) { + todo!() + } - Ok(out) + fn create_trie_subsets(tries: &Self::CurrBlockTries) -> Self::TrieInputs { + todo!() } +} - fn get_trie_trace(trie: &HashedPartialTrie, k: &Nibbles) -> TriePath { - path_for_query(trie, *k, true).collect() +impl MptProcessedBlockTrace { + pub(crate) fn into_proof_gen_mpt_ir( + self, + other_data: OtherBlockData, + ) -> MptTraceParsingResult> { + todo!() } /// If a branch collapse occurred after a delete, then we must ensure that @@ -320,6 +92,10 @@ impl MptProcessedBlockTrace { )) } + fn get_trie_trace(trie: &HashedPartialTrie, k: &Nibbles) -> TriePath { + path_for_query(trie, *k, true).collect() + } + /// Comparing the path of the deleted key before and after the deletion, /// determine if the deletion resulted in a branch collapsing into a leaf or /// extension node, and return the path to the remaining child if this @@ -344,314 +120,6 @@ impl MptProcessedBlockTrace { // collapse. branch_collapse_occurred.then(|| new_path.iter().into_key()) } - - /// Pads a generated IR vec with additional "dummy" entries if needed. - /// We need to ensure that generated IR always has at least `2` elements, - /// and if there are only `0` or `1` elements, then we need to pad so - /// that we have two entries in total. These dummy entries serve only to - /// allow the proof generation process to finish. Specifically, we need - /// at least two entries to generate an agg proof, and we need an agg - /// proof to generate a block proof. These entries do not mutate state. - fn pad_gen_inputs_with_dummy_inputs_if_needed( - gen_inputs: &mut Vec, - other_data: &OtherBlockData, - final_extra_data: &MptExtraBlockData, - initial_extra_data: &MptExtraBlockData, - initial_tries: &PartialTrieState, - final_tries: &PartialTrieState, - ) { - match gen_inputs.len() { - 0 => { - debug_assert!(initial_tries.state == final_tries.state); - debug_assert!(initial_extra_data == final_extra_data); - // We need to pad with two dummy entries. - gen_inputs.extend(create_dummy_txn_pair_for_empty_block( - other_data, - final_extra_data, - initial_tries, - )); - } - 1 => { - // We just need one dummy entry. - // The dummy proof will be prepended to the actual txn. - let dummy_txn = - create_dummy_gen_input(other_data, initial_extra_data, initial_tries); - gen_inputs.insert(0, dummy_txn) - } - _ => (), - } - } - - /// The withdrawals are always in the final ir payload. - fn add_withdrawals_to_txns( - txn_ir: &mut [GenerationInputs], - final_trie_state: &mut PartialTrieState, - withdrawals: Vec<(Address, U256)>, - ) -> TraceDecodingResult<()> { - let withdrawals_with_hashed_addrs_iter = || { - withdrawals - .iter() - .map(|(addr, v)| (*addr, hash(addr.as_bytes()), *v)) - }; - - let last_inputs = txn_ir - .last_mut() - .expect("We cannot have an empty list of payloads."); - - if last_inputs.signed_txn.is_none() { - // This is a dummy payload, hence it does not contain yet - // state accesses to the withdrawal addresses. - let withdrawal_addrs = - withdrawals_with_hashed_addrs_iter().map(|(_, h_addr, _)| h_addr); - last_inputs.tries.state_trie = create_minimal_state_partial_trie( - &last_inputs.tries.state_trie, - withdrawal_addrs, - iter::empty(), - )?; - } - - Self::update_trie_state_from_withdrawals( - withdrawals_with_hashed_addrs_iter(), - &mut final_trie_state.state, - )?; - - last_inputs.withdrawals = withdrawals; - last_inputs.trie_roots_after.state_root = final_trie_state.state.hash(); - - Ok(()) - } - - /// Withdrawals update balances in the account trie, so we need to update - /// our local trie state. - fn update_trie_state_from_withdrawals<'a>( - withdrawals: impl IntoIterator + 'a, - state: &mut HashedPartialTrie, - ) -> TraceDecodingResult<()> { - for (addr, h_addr, amt) in withdrawals { - let h_addr_nibs = Nibbles::from_h256_be(h_addr); - - let acc_bytes = state.get(h_addr_nibs).ok_or_else(|| { - let mut e = TraceDecodingError::new( - TraceParsingErrorReason::MissingWithdrawalAccount(addr, h_addr, amt), - ); - e.addr(addr); - e.h_addr(h_addr); - e - })?; - let mut acc_data = account_from_rlped_bytes(acc_bytes)?; - - acc_data.balance += amt; - - state - .insert(h_addr_nibs, rlp::encode(&acc_data).to_vec()) - .map_err(TraceDecodingError::from)?; - } - - Ok(()) - } - - /// Processes a single transaction in the trace. - fn process_txn_info( - txn_idx: usize, - txn_info: ProcessedSectionTxnInfo, - curr_block_tries: &mut PartialTrieState, - extra_data: &mut MptExtraBlockData, - other_data: &OtherBlockData, - ) -> TraceDecodingResult { - trace!("Generating proof IR for txn {}...", txn_idx); - - Self::init_any_needed_empty_storage_tries( - &mut curr_block_tries.storage, - txn_info - .nodes_used_by_txn - .storage_accesses - .iter() - .map(|(k, _)| k), - &txn_info - .nodes_used_by_txn - .state_accounts_with_no_accesses_but_storage_tries, - ); - // For each non-dummy txn, we increment `txn_number_after` by 1, and - // update `gas_used_after` accordingly. - extra_data.txn_number_after += U256::one(); - extra_data.gas_used_after += txn_info.meta.gas_used.into(); - - // Because we need to run delta application before creating the minimal - // sub-tries (we need to detect if deletes collapsed any branches), we need to - // do this clone every iteration. - let tries_at_start_of_txn = curr_block_tries.clone(); - - Self::update_txn_and_receipt_tries(curr_block_tries, &txn_info.meta, txn_idx) - .map_err(TraceDecodingError::from)?; - - let delta_out = - Self::apply_deltas_to_trie_state(curr_block_tries, &txn_info.nodes_used_by_txn)?; - - let tries = Self::create_minimal_partial_tries_needed_by_txn( - &tries_at_start_of_txn, - &txn_info.nodes_used_by_txn, - txn_idx, - delta_out, - &other_data.b_data.b_meta.block_beneficiary, - )?; - - let trie_roots_after = calculate_trie_input_hashes(curr_block_tries); - let gen_inputs = GenerationInputs { - txn_number_before: extra_data.txn_number_before, - gas_used_before: extra_data.gas_used_before, - gas_used_after: extra_data.gas_used_after, - signed_txn: txn_info.meta.txn_bytes, - withdrawals: Vec::default(), /* Only ever set in a dummy txn at the end of - * the block (see `[add_withdrawals_to_txns]` - * for more info). */ - tries, - trie_roots_after, - checkpoint_state_trie_root: extra_data.checkpoint_state_trie_root, - contract_code: txn_info.contract_code_accessed, - block_metadata: other_data.b_data.b_meta.clone(), - block_hashes: other_data.b_data.b_hashes.clone(), - }; - - // After processing a transaction, we update the remaining accumulators - // for the next transaction. - extra_data.txn_number_before += U256::one(); - extra_data.gas_used_before = extra_data.gas_used_after; - - Ok(gen_inputs) - } -} - -impl StateTrieWrites { - fn apply_writes_to_state_node( - &self, - state_node: &mut MptAccountRlp, - h_addr: &HashedAccountAddr, - acc_storage_tries: &HashMap, - ) -> TraceDecodingResult<()> { - let storage_root_hash_change = match self.storage_trie_change { - false => None, - true => { - let storage_trie = acc_storage_tries.get(h_addr).ok_or_else(|| { - let h_addr = *h_addr; - let mut e = TraceDecodingError::new( - TraceParsingErrorReason::MissingAccountStorageTrie(h_addr), - ); - e.h_addr(h_addr); - e - })?; - - Some(storage_trie.hash()) - } - }; - - update_val_if_some(&mut state_node.balance, self.balance); - update_val_if_some(&mut state_node.nonce, self.nonce); - update_val_if_some(&mut state_node.storage_root, storage_root_hash_change); - update_val_if_some(&mut state_node.code_hash, self.code_hash); - - Ok(()) - } -} - -fn calculate_trie_input_hashes(t_inputs: &PartialTrieState) -> MptTrieRoots { - MptTrieRoots { - state_root: t_inputs.state.hash(), - transactions_root: t_inputs.txn.hash(), - receipts_root: t_inputs.receipt.hash(), - } -} - -// We really want to get a trie with just a hash node here, and this is an easy -// way to do it. -fn create_fully_hashed_out_sub_partial_trie(trie: &HashedPartialTrie) -> HashedPartialTrie { - // Impossible to actually fail with an empty iter. - create_trie_subset(trie, empty::()).unwrap() -} - -fn create_dummy_txn_pair_for_empty_block( - other_data: &OtherBlockData, - extra_data: &MptExtraBlockData, - final_tries: &PartialTrieState, -) -> [GenerationInputs; 2] { - [ - create_dummy_gen_input(other_data, extra_data, final_tries), - create_dummy_gen_input(other_data, extra_data, final_tries), - ] -} - -fn create_dummy_gen_input( - other_data: &OtherBlockData, - extra_data: &MptExtraBlockData, - final_tries: &PartialTrieState, -) -> GenerationInputs { - let sub_tries = create_dummy_proof_trie_inputs( - final_tries, - create_fully_hashed_out_sub_partial_trie(&final_tries.state), - ); - create_dummy_gen_input_common(other_data, extra_data, sub_tries) -} - -fn create_dummy_gen_input_common( - other_data: &OtherBlockData, - extra_data: &MptExtraBlockData, - sub_tries: MptTrieInputs, -) -> GenerationInputs { - let trie_roots_after = MptTrieRoots { - state_root: sub_tries.state_trie.hash(), - transactions_root: sub_tries.transactions_trie.hash(), - receipts_root: sub_tries.receipts_trie.hash(), - }; - - // Sanity checks - assert_eq!( - extra_data.txn_number_before, extra_data.txn_number_after, - "Txn numbers before/after differ in a dummy payload with no txn!" - ); - assert_eq!( - extra_data.gas_used_before, extra_data.gas_used_after, - "Gas used before/after differ in a dummy payload with no txn!" - ); - - GenerationInputs { - signed_txn: None, - tries: sub_tries, - trie_roots_after, - checkpoint_state_trie_root: extra_data.checkpoint_state_trie_root, - block_metadata: other_data.b_data.b_meta.clone(), - block_hashes: other_data.b_data.b_hashes.clone(), - txn_number_before: extra_data.txn_number_before, - gas_used_before: extra_data.gas_used_before, - gas_used_after: extra_data.gas_used_after, - contract_code: HashMap::default(), - withdrawals: vec![], // this is set after creating dummy payloads - } -} - -fn create_dummy_proof_trie_inputs( - final_tries_at_end_of_block: &PartialTrieState, - state_trie: HashedPartialTrie, -) -> MptTrieInputs { - let partial_sub_storage_tries: Vec<_> = final_tries_at_end_of_block - .storage - .iter() - .map(|(hashed_acc_addr, s_trie)| { - ( - *hashed_acc_addr, - create_fully_hashed_out_sub_partial_trie(s_trie), - ) - }) - .collect(); - - MptTrieInputs { - state_trie, - transactions_trie: create_fully_hashed_out_sub_partial_trie( - &final_tries_at_end_of_block.txn, - ), - receipts_trie: create_fully_hashed_out_sub_partial_trie( - &final_tries_at_end_of_block.receipt, - ), - storage_tries: partial_sub_storage_tries, - } } fn create_minimal_state_partial_trie( @@ -724,22 +192,6 @@ fn account_from_rlped_bytes(bytes: &[u8]) -> TraceDecodingResult }) } -#[derive(Debug, Default)] -pub(crate) struct TxnMetaState { - pub(crate) txn_bytes: Option>, - pub(crate) receipt_node_bytes: Vec, - pub(crate) gas_used: u64, -} - -impl TxnMetaState { - fn txn_bytes(&self) -> Vec { - match self.txn_bytes.as_ref() { - Some(v) => v.clone(), - None => Vec::default(), - } - } -} - /// The current state of all tries as we process txn deltas. These are mutated /// after every txn we process in the trace. #[derive(Clone, Debug, Default)] @@ -750,11 +202,72 @@ struct PartialTrieState { receipt: HashedPartialTrie, } -/// Additional information discovered during delta application. -#[derive(Debug, Default)] -struct TrieDeltaApplicationOutput { - // During delta application, if a delete occurs, we may have to make sure additional nodes - // that are not accessed by the txn remain unhashed. - additional_state_trie_paths_to_not_hash: Vec, - additional_storage_trie_paths_to_not_hash: HashMap>, + +impl BlockTrace { + /// Processes and returns the [GenerationInputs] for all transactions in the + /// block. + pub fn into_proof_gen_mpt_ir( + self, + p_meta: &ProcessingMeta, + other_data: OtherBlockData, + ) -> TraceProtocolDecodingResult> + where + F: CodeHashResolveFunc, + { + let processed_block_trace = + self.into_mpt_processed_block_trace(p_meta, other_data.b_data.withdrawals.clone())?; + + let res = processed_block_trace.into_proof_gen_ir(other_data)?; + + Ok(res) + } + + fn into_mpt_processed_block_trace( + self, + p_meta: &ProcessingMeta, + withdrawals: Vec<(Address, U256)>, + ) -> TraceProtocolDecodingResult + where + F: CodeHashResolveFunc, + { + self.into_processed_block_trace::<_, MptBlockTraceProcessing, MptProcessedBlockTrace>(p_meta, withdrawals) + } +} + +fn create_minimal_partial_tries_needed_by_txn( + curr_block_tries: &D::TState, + nodes_used_by_txn: &NodesUsedByTxn, + txn_idx: TxnIdx, + delta_application_out: TrieDeltaApplicationOutput, + _coin_base_addr: &Address, +) -> TraceDecodingResult { + let state_trie = create_minimal_state_partial_trie( + &curr_block_tries.state, + nodes_used_by_txn.state_accesses.iter().cloned(), + delta_application_out + .additional_state_trie_paths_to_not_hash + .into_iter(), + )?; + + let txn_k = Nibbles::from_bytes_be(&rlp::encode(&txn_idx)).unwrap(); + // TODO: Replace cast once `mpt_trie` supports `into` for `usize... + let transactions_trie = + create_trie_subset_wrapped(&curr_block_tries.txn, once(txn_k), TrieType::Txn)?; + + let receipts_trie = + create_trie_subset_wrapped(&curr_block_tries.receipt, once(txn_k), TrieType::Receipt)?; + + let storage_tries = create_minimal_storage_partial_tries( + &curr_block_tries.storage, + &nodes_used_by_txn.state_accounts_with_no_accesses_but_storage_tries, + nodes_used_by_txn.storage_accesses.iter(), + &delta_application_out.additional_storage_trie_paths_to_not_hash, + )?; + + Ok(MptTrieInputs { + state_trie, + transactions_trie, + receipts_trie, + storage_tries, + }) } diff --git a/trace_decoder/src/decoding_smt.rs b/trace_decoder/src/decoding_smt.rs index 980aa1f69..12f1d4406 100644 --- a/trace_decoder/src/decoding_smt.rs +++ b/trace_decoder/src/decoding_smt.rs @@ -4,7 +4,7 @@ use crate::{ }; impl SmtProcessedBlockTrace { - pub(crate) fn into_proof_gen_ir( + pub(crate) fn into_proof_gen_smt_ir( self, _other_data: OtherBlockData, ) -> TraceDecodingResult> { diff --git a/trace_decoder/src/processed_block_trace.rs b/trace_decoder/src/processed_block_trace.rs index 4b087bb5c..67f3857a2 100644 --- a/trace_decoder/src/processed_block_trace.rs +++ b/trace_decoder/src/processed_block_trace.rs @@ -1,6 +1,6 @@ use std::{ collections::{HashMap, HashSet}, - iter::once, + iter::once, marker::PhantomData, }; use ethereum_types::{Address, U256}; @@ -8,16 +8,7 @@ use evm_arithmetization_mpt::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use mpt_trie::nibbles::Nibbles; use crate::{ - aliased_crate_types::MptAccountRlp, - decoding_mpt::TxnMetaState, - processed_block_trace_mpt::{StorageAccess, StorageWrite}, - protocol_processing::TraceProtocolDecodingResult, - trace_protocol::{AtomicUnitInfo, BlockTrace, ContractCodeUsage, TriePreImage, TxnInfo}, - types::{ - CodeHash, CodeHashResolveFunc, HashedAccountAddr, HashedNodeAddr, TrieRootHash, - EMPTY_CODE_HASH, EMPTY_TRIE_HASH, - }, - utils::hash, + aliased_crate_types::MptAccountRlp, decoding::ProcessedBlockTraceDecode, decoding_mpt::TxnMetaState, processed_block_trace_mpt::{ProcedBlockTraceMptSpec, StorageAccess, StorageWrite}, protocol_processing::TraceProtocolDecodingResult, trace_protocol::{AtomicUnitInfo, BlockTrace, ContractCodeUsage, TriePreImage, TxnInfo}, types::{CodeHash, CodeHashResolveFunc, HashedAccountAddr, HashedNodeAddr, TrieRootHash, EMPTY_CODE_HASH, EMPTY_TRIE_HASH}, utils::hash }; pub(crate) trait BlockTraceProcessing { @@ -35,23 +26,29 @@ pub(crate) trait BlockTraceProcessing { ) -> Option<&HashMap>>; fn create_spec_output( image: Self::ProcessedPreImage, - sect_info: ProcessedSectionInfo, ) -> Self::Output; } #[derive(Debug)] -pub(crate) struct ProcessedBlockTrace { +pub(crate) struct ProcessedBlockTrace +where + D: ProcessedBlockTraceDecode, +{ pub(crate) spec: T, + pub(crate) sect_info: ProcessedSectionInfo, pub(crate) withdrawals: Vec<(Address, U256)>, + decode_spec: PhantomData, } impl BlockTrace { - pub(crate) fn into_processed_block_trace( + pub(crate) fn into_processed_block_trace( self, p_meta: &ProcessingMeta, withdrawals: Vec<(Address, U256)>, - ) -> TraceProtocolDecodingResult> + ) -> TraceProtocolDecodingResult> where F: CodeHashResolveFunc, + P: BlockTraceProcessing, + D: ProcessedBlockTraceDecode, { // The compact format is able to provide actual code, so if it does, we should // take advantage of it. @@ -73,9 +70,9 @@ impl BlockTrace { &withdrawals, ); - let spec = P::create_spec_output(pre_image_data, sect_info); + let spec = P::create_spec_output(pre_image_data); - Ok(ProcessedBlockTrace { spec, withdrawals }) + Ok(ProcessedBlockTrace { spec, sect_info, withdrawals, decode_spec: PhantomData:: }) } fn process_atomic_units( @@ -116,7 +113,7 @@ impl BlockTrace { ProcessedSectionInfo::Txns(proced_txn_info) } - AtomicUnitInfo::Continuations(cont_info) => { + AtomicUnitInfo::Continuations(_) => { todo!("Continuation support with MPT not yet implemented!") } } diff --git a/trace_decoder/src/processed_block_trace_mpt.rs b/trace_decoder/src/processed_block_trace_mpt.rs index 86163bdd8..fa1f4a54d 100644 --- a/trace_decoder/src/processed_block_trace_mpt.rs +++ b/trace_decoder/src/processed_block_trace_mpt.rs @@ -23,7 +23,7 @@ use crate::{ processed_block_trace::{BlockTraceProcessing, ProcessedSectionInfo, ProcessingMeta}, }; -pub(crate) type MptProcessedBlockTrace = ProcessedBlockTrace; +pub(crate) type MptProcessedBlockTrace = ProcessedBlockTrace; pub(crate) type StorageAccess = Vec; pub(crate) type StorageWrite = Vec<(HashedStorageAddrNibbles, Vec)>; @@ -31,10 +31,9 @@ pub(crate) type StorageWrite = Vec<(HashedStorageAddrNibbles, Vec)>; #[derive(Debug)] pub(crate) struct ProcedBlockTraceMptSpec { pub(crate) tries: MptPartialTriePreImages, - pub(crate) sect_info: ProcessedSectionInfo, } -struct MptBlockTraceProcessing; +pub(crate) struct MptBlockTraceProcessing; impl BlockTraceProcessing for MptBlockTraceProcessing { type ProcessedPreImage = MptProcessedBlockTracePreImages; @@ -68,46 +67,13 @@ impl BlockTraceProcessing for MptBlockTraceProcessing { fn create_spec_output( image: Self::ProcessedPreImage, - sect_info: ProcessedSectionInfo, ) -> Self::Output { ProcedBlockTraceMptSpec { tries: image.tries, - sect_info, } } } -impl BlockTrace { - /// Processes and returns the [GenerationInputs] for all transactions in the - /// block. - pub fn into_proof_gen_mpt_ir( - self, - p_meta: &ProcessingMeta, - other_data: OtherBlockData, - ) -> TraceProtocolDecodingResult> - where - F: CodeHashResolveFunc, - { - let processed_block_trace = - self.into_mpt_processed_block_trace(p_meta, other_data.b_data.withdrawals.clone())?; - - let res = processed_block_trace.into_proof_gen_ir(other_data)?; - - Ok(res) - } - - fn into_mpt_processed_block_trace( - self, - p_meta: &ProcessingMeta, - withdrawals: Vec<(Address, U256)>, - ) -> TraceProtocolDecodingResult - where - F: CodeHashResolveFunc, - { - self.into_processed_block_trace::<_, MptBlockTraceProcessing>(p_meta, withdrawals) - } -} - /// Mpt processed pre-image. #[derive(Clone, Debug)] pub struct MptProcessedBlockTracePreImages { diff --git a/trace_decoder/src/processed_block_trace_smt.rs b/trace_decoder/src/processed_block_trace_smt.rs index f001d33ef..53e30f663 100644 --- a/trace_decoder/src/processed_block_trace_smt.rs +++ b/trace_decoder/src/processed_block_trace_smt.rs @@ -14,7 +14,7 @@ use crate::{ types::{CodeHash, CodeHashResolveFunc, HashedAccountAddr, OtherBlockData}, }; -pub(crate) type SmtProcessedBlockTrace = ProcessedBlockTrace; +pub(crate) type SmtProcessedBlockTrace = ProcessedBlockTrace; /// Smt processed pre-image. #[derive(Clone, Debug)] @@ -51,7 +51,6 @@ impl BlockTraceProcessing for SmtBlockTraceProcessing { fn create_spec_output( image: Self::ProcessedPreImage, - sect_info: ProcessedSectionInfo, ) -> Self::Output { todo!() }