From fb636ad707358e6ddcd2f22593551294ff7f41a1 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:32:41 -0400 Subject: [PATCH 1/6] Fix 2-to-1 test for Cancun (#464) --- evm_arithmetization/tests/two_to_one_block.rs | 197 +++++------------- 1 file changed, 57 insertions(+), 140 deletions(-) diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index fe479bf65..5d4922fb4 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -1,18 +1,17 @@ -use std::collections::HashMap; -use std::str::FromStr; - use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use ethereum_types::{Address, BigEndianHash, H256, U256}; +use ethereum_types::{Address, BigEndianHash, H256}; use evm_arithmetization::fixed_recursive_verifier::{ extract_block_public_values, extract_two_to_one_block_hash, }; -use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockMetadata, PublicValues, TrieRoots}; +use evm_arithmetization::testing_utils::{ + beacon_roots_account_nibbles, beacon_roots_contract_from_storage, ger_account_nibbles, + preinitialized_state_and_storage_tries, update_beacon_roots_account_storage, + GLOBAL_EXIT_ROOT_ACCOUNT, +}; use evm_arithmetization::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; use hex_literal::hex; -use keccak_hash::keccak; -use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::hash::poseidon::PoseidonHash; @@ -24,59 +23,14 @@ type F = GoldilocksField; const D: usize = 2; type C = PoseidonGoldilocksConfig; -fn eth_to_wei(eth: U256) -> U256 { - // 1 ether = 10^18 wei. - eth * U256::from(10).pow(18.into()) -} - fn init_logger() { let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); } -/// Get `GenerationInputs` for a simple token transfer txn, where the block has -/// the given timestamp. -fn empty_transfer(timestamp: u64) -> anyhow::Result { - init_logger(); - +/// Get `GenerationInputs` for a dummy payload, where the block has the given +/// timestamp. +fn dummy_payload(timestamp: u64, is_first_payload: bool) -> anyhow::Result { let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); - let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); - - let sender_state_key = keccak(sender); - let to_state_key = keccak(to); - - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); - - let sender_account_before = AccountRlp { - nonce: 5.into(), - balance: eth_to_wei(100_000.into()), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), - }; - let to_account_before = AccountRlp::default(); - - let state_trie_before: HashedPartialTrie = Node::Leaf { - nibbles: sender_nibbles, - value: rlp::encode(&sender_account_before).to_vec(), - } - .into(); - let checkpoint_state_trie_root = state_trie_before.hash(); - assert_eq!( - checkpoint_state_trie_root, - hex!("ef46022eafbc33d70e6ea9c6aef1074c1ff7ad36417ffbc64307ad3a8c274b75").into() - ); - - let tries_before = TrieInputs { - state_trie: HashedPartialTrie::from(Node::Empty), - transactions_trie: HashedPartialTrie::from(Node::Empty), - receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries: vec![], - }; - - // Generated using a little py-evm script. - let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd"); - let value = U256::from(100u32); let block_metadata = BlockMetadata { block_beneficiary: Address::from(beneficiary), @@ -90,73 +44,58 @@ fn empty_transfer(timestamp: u64) -> anyhow::Result { ..Default::default() }; - let contract_code = HashMap::new(); - - let expected_state_trie_after: HashedPartialTrie = { - let txdata_gas = 2 * 16; - let gas_used = 21_000 + txdata_gas; - - let sender_account_after = AccountRlp { - balance: sender_account_before.balance - value - gas_used * 10, - nonce: sender_account_before.nonce + 1, - ..sender_account_before - }; - let to_account_after = AccountRlp { - balance: value, - ..to_account_before - }; - - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: sender_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&sender_account_after).to_vec(), - } - .into(); - children[to_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: to_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&to_account_after).to_vec(), - } - .into(); - Node::Branch { - children, - value: vec![], - } - .into() - }; + let (mut state_trie_before, mut storage_tries) = preinitialized_state_and_storage_tries()?; + let checkpoint_state_trie_root = state_trie_before.hash(); + let mut beacon_roots_account_storage = storage_tries[0].1.clone(); - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: 21032.into(), - bloom: vec![0; 256].into(), - logs: vec![], - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert( - Nibbles::from_str("0x80").unwrap(), - rlp::encode(&receipt_0).to_vec(), + update_beacon_roots_account_storage( + &mut beacon_roots_account_storage, + block_metadata.block_timestamp, + block_metadata.parent_beacon_block_root, )?; - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), + let updated_beacon_roots_account = + beacon_roots_contract_from_storage(&beacon_roots_account_storage); + + if !is_first_payload { + // This isn't the first dummy payload being processed. We need to update the + // initial state trie to account for the update on the beacon roots contract. + state_trie_before.insert( + beacon_roots_account_nibbles(), + rlp::encode(&updated_beacon_roots_account).to_vec(), + )?; + storage_tries[0].1 = beacon_roots_account_storage; } - .into(); - let _trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), + let tries_before = TrieInputs { + state_trie: state_trie_before, + storage_tries, + ..Default::default() + }; + + let expected_state_trie_after: HashedPartialTrie = { + let mut state_trie_after = HashedPartialTrie::from(Node::Empty); + state_trie_after.insert( + beacon_roots_account_nibbles(), + rlp::encode(&updated_beacon_roots_account).to_vec(), + )?; + state_trie_after.insert( + ger_account_nibbles(), + rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), + )?; + + state_trie_after }; let trie_roots_after = TrieRoots { - state_root: tries_before.state_trie.hash(), + state_root: expected_state_trie_after.hash(), transactions_root: tries_before.transactions_trie.hash(), receipts_root: tries_before.receipts_trie.hash(), }; + let inputs = GenerationInputs { tries: tries_before.clone(), trie_roots_after, - contract_code, - checkpoint_state_trie_root: tries_before.state_trie.hash(), + checkpoint_state_trie_root, block_metadata, ..Default::default() }; @@ -170,46 +109,24 @@ fn get_test_block_proof( all_stark: &AllStark, config: &StarkConfig, ) -> anyhow::Result> { - let inputs0 = empty_transfer(timestamp)?; - let inputs = inputs0.clone(); - let dummy0 = GenerationInputs { - txn_number_before: inputs.txn_number_before, - gas_used_before: inputs.gas_used_after, - gas_used_after: inputs.gas_used_after, - signed_txn: None, - global_exit_roots: vec![], - withdrawals: vec![], - tries: TrieInputs { - state_trie: HashedPartialTrie::from(Node::Hash(inputs.trie_roots_after.state_root)), - transactions_trie: HashedPartialTrie::from(Node::Hash( - inputs.trie_roots_after.transactions_root, - )), - receipts_trie: HashedPartialTrie::from(Node::Hash( - inputs.trie_roots_after.receipts_root, - )), - storage_tries: vec![], - }, - trie_roots_after: inputs.trie_roots_after, - checkpoint_state_trie_root: inputs.checkpoint_state_trie_root, - contract_code: Default::default(), - block_metadata: inputs.block_metadata.clone(), - block_hashes: inputs.block_hashes.clone(), - }; + let dummy0 = dummy_payload(timestamp, true)?; + let dummy1 = dummy_payload(timestamp, false)?; let timing = &mut TimingTree::new(&format!("Blockproof {timestamp}"), log::Level::Info); - let (root_proof0, pv0) = all_circuits.prove_root(all_stark, config, inputs0, timing, None)?; - all_circuits.verify_root(root_proof0.clone())?; let (dummy_proof0, dummy_pv0) = all_circuits.prove_root(all_stark, config, dummy0, timing, None)?; all_circuits.verify_root(dummy_proof0.clone())?; + let (dummy_proof1, dummy_pv1) = + all_circuits.prove_root(all_stark, config, dummy1, timing, None)?; + all_circuits.verify_root(dummy_proof1.clone())?; let (agg_proof0, pv0) = all_circuits.prove_aggregation( - false, - &root_proof0, - pv0, false, &dummy_proof0, dummy_pv0, + false, + &dummy_proof1, + dummy_pv1, )?; all_circuits.verify_aggregation(&agg_proof0)?; @@ -244,7 +161,7 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { let config = StarkConfig::standard_fast_config(); let all_circuits = AllRecursiveCircuits::::new( &all_stark, - &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], + &[16..17, 8..10, 14..15, 14..15, 9..10, 12..13, 17..18], &config, ); From 3f20a79d365098c97d1446c1c86d8578e513e9a9 Mon Sep 17 00:00:00 2001 From: Arpit Temani Date: Thu, 8 Aug 2024 18:53:40 +0530 Subject: [PATCH 2/6] bug(zero-bin): verifier binary not picking up pre-processed circuits (#474) * set env var at runtime * env var already set at compiletime --- zero_bin/common/src/prover_state/persistence.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zero_bin/common/src/prover_state/persistence.rs b/zero_bin/common/src/prover_state/persistence.rs index 87eb7d1ae..750726696 100644 --- a/zero_bin/common/src/prover_state/persistence.rs +++ b/zero_bin/common/src/prover_state/persistence.rs @@ -109,7 +109,7 @@ impl DiskResource for BaseProverResource { "{}/{}_base_{}_{}", &relative_circuit_dir_path(), PROVER_STATE_FILE_PREFIX, - env::var("EVM_ARITHMETIZATION_PKG_VER").unwrap_or("NA".to_string()), + env!("EVM_ARITHMETIZATION_PKG_VER"), p.get_configuration_digest() ) } @@ -145,7 +145,7 @@ impl DiskResource for MonolithicProverResource { "{}/{}_monolithic_{}_{}", &relative_circuit_dir_path(), PROVER_STATE_FILE_PREFIX, - env::var("EVM_ARITHMETIZATION_PKG_VER").unwrap_or("NA".to_string()), + env!("EVM_ARITHMETIZATION_PKG_VER"), p.get_configuration_digest() ) } @@ -180,7 +180,7 @@ impl DiskResource for RecursiveCircuitResource { "{}/{}_{}_{}_{}", &relative_circuit_dir_path(), PROVER_STATE_FILE_PREFIX, - env::var("EVM_ARITHMETIZATION_PKG_VER").unwrap_or("NA".to_string()), + env!("EVM_ARITHMETIZATION_PKG_VER"), circuit_type.as_short_str(), size ) @@ -224,7 +224,7 @@ impl DiskResource for VerifierResource { "{}/{}_{}_{}", &relative_circuit_dir_path(), VERIFIER_STATE_FILE_PREFIX, - env::var("EVM_ARITHMETIZATION_PKG_VER").unwrap_or("NA".to_string()), + env!("EVM_ARITHMETIZATION_PKG_VER"), p.get_configuration_digest() ) } From dc83b22da9e2215e21686ee66dc79f58d64f02b3 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Thu, 8 Aug 2024 19:03:17 -0400 Subject: [PATCH 3/6] feat: Implement Columns view for BytePackingStark (#422) --- .../src/byte_packing/byte_packing_stark.rs | 132 +++++++++++------- .../src/byte_packing/columns.rs | 79 ++++++----- 2 files changed, 123 insertions(+), 88 deletions(-) diff --git a/evm_arithmetization/src/byte_packing/byte_packing_stark.rs b/evm_arithmetization/src/byte_packing/byte_packing_stark.rs index e37195497..018c23e14 100644 --- a/evm_arithmetization/src/byte_packing/byte_packing_stark.rs +++ b/evm_arithmetization/src/byte_packing/byte_packing_stark.rs @@ -28,6 +28,7 @@ //! 256^length, and as a result a different value will be stored in memory. use core::marker::PhantomData; +use std::borrow::Borrow; use itertools::Itertools; use plonky2::field::extension::{Extendable, FieldExtension}; @@ -46,10 +47,7 @@ use starky::stark::Stark; use super::NUM_BYTES; use crate::all_stark::EvmStarkFrame; -use crate::byte_packing::columns::{ - index_len, value_bytes, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, IS_READ, LEN_INDICES_COLS, - NUM_COLUMNS, RANGE_COUNTER, RC_FREQUENCIES, TIMESTAMP, -}; +use crate::byte_packing::columns::*; use crate::witness::memory::MemoryAddress; /// Strict upper bound for the individual bytes range-check. @@ -65,7 +63,8 @@ pub(crate) fn ctl_looked_data() -> Vec> { // obtain the corresponding limb. let outputs: Vec> = (0..8) .map(|i| { - let range = value_bytes(i * 4)..value_bytes(i * 4) + 4; + let range = BYTE_PACKING_COL_MAP.value_bytes[i * 4] + ..BYTE_PACKING_COL_MAP.value_bytes[i * 4] + 4; Column::linear_combination( range .enumerate() @@ -74,34 +73,54 @@ pub(crate) fn ctl_looked_data() -> Vec> { }) .collect(); - let sequence_len: Column = Column::linear_combination( - (0..NUM_BYTES).map(|i| (index_len(i), F::from_canonical_usize(i + 1))), - ); - - Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]) - .chain([sequence_len]) - .chain(Column::singles(&[TIMESTAMP])) - .chain(outputs) - .collect() + let sequence_len: Column = Column::linear_combination((0..NUM_BYTES).map(|i| { + ( + BYTE_PACKING_COL_MAP.index_len[i], + F::from_canonical_usize(i + 1), + ) + })); + + Column::singles([ + BYTE_PACKING_COL_MAP.is_read, + BYTE_PACKING_COL_MAP.addr_context, + BYTE_PACKING_COL_MAP.addr_segment, + BYTE_PACKING_COL_MAP.addr_virtual, + ]) + .chain([sequence_len]) + .chain(Column::singles(&[BYTE_PACKING_COL_MAP.timestamp])) + .chain(outputs) + .collect() } /// CTL filter for the `BytePackingStark` looked table. pub(crate) fn ctl_looked_filter() -> Filter { // The CPU table is only interested in our sequence end rows, // since those contain the final limbs of our packed int. - Filter::new_simple(Column::sum((0..NUM_BYTES).map(index_len))) + Filter::new_simple(Column::sum( + (0..NUM_BYTES).map(|i| BYTE_PACKING_COL_MAP.index_len[i]), + )) } /// Column linear combination for the `BytePackingStark` table reading/writing /// the `i`th byte sequence from `MemoryStark`. pub(crate) fn ctl_looking_memory(i: usize) -> Vec> { - let mut res = Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT]).collect_vec(); + let mut res = Column::singles([ + BYTE_PACKING_COL_MAP.is_read, + BYTE_PACKING_COL_MAP.addr_context, + BYTE_PACKING_COL_MAP.addr_segment, + ]) + .collect_vec(); // Compute the virtual address: `ADDR_VIRTUAL` + `sequence_len` - 1 - i. let sequence_len_minus_one = (0..NUM_BYTES) - .map(|j| (index_len(j), F::from_canonical_usize(j))) + .map(|j| { + ( + BYTE_PACKING_COL_MAP.index_len[j], + F::from_canonical_usize(j), + ) + }) .collect::>(); - let mut addr_virt_cols = vec![(ADDR_VIRTUAL, F::ONE)]; + let mut addr_virt_cols = vec![(BYTE_PACKING_COL_MAP.addr_virtual, F::ONE)]; addr_virt_cols.extend(sequence_len_minus_one); let addr_virt = Column::linear_combination_with_constant( addr_virt_cols, @@ -111,12 +130,12 @@ pub(crate) fn ctl_looking_memory(i: usize) -> Vec> { res.push(addr_virt); // The i'th input byte being read/written. - res.push(Column::single(value_bytes(i))); + res.push(Column::single(BYTE_PACKING_COL_MAP.value_bytes[i])); // Since we're reading a single byte, the higher limbs must be zero. res.extend((1..8).map(|_| Column::zero())); - res.push(Column::single(TIMESTAMP)); + res.push(Column::single(BYTE_PACKING_COL_MAP.timestamp)); res } @@ -124,7 +143,9 @@ pub(crate) fn ctl_looking_memory(i: usize) -> Vec> { /// CTL filter for reading/writing the `i`th byte of the byte sequence from/to /// memory. pub(crate) fn ctl_looking_memory_filter(i: usize) -> Filter { - Filter::new_simple(Column::sum((i..NUM_BYTES).map(index_len))) + Filter::new_simple(Column::sum( + (i..NUM_BYTES).map(|i| BYTE_PACKING_COL_MAP.index_len[i]), + )) } /// Information about a byte packing operation needed for witness generation. @@ -205,24 +226,24 @@ impl, const D: usize> BytePackingStark { virt, } = base_address; - let mut row = [F::ZERO; NUM_COLUMNS]; - row[IS_READ] = F::from_bool(is_read); + let mut row = BytePackingColumnsView::default(); + row.is_read = F::from_bool(is_read); - row[ADDR_CONTEXT] = F::from_canonical_usize(context); - row[ADDR_SEGMENT] = F::from_canonical_usize(segment); + row.addr_context = F::from_canonical_usize(context); + row.addr_segment = F::from_canonical_usize(segment); // We store the initial virtual segment. But the CTLs, // we start with virt + sequence_len - 1. - row[ADDR_VIRTUAL] = F::from_canonical_usize(virt); + row.addr_virtual = F::from_canonical_usize(virt); - row[TIMESTAMP] = F::from_canonical_usize(timestamp); + row.timestamp = F::from_canonical_usize(timestamp); - row[index_len(bytes.len() - 1)] = F::ONE; + row.index_len[bytes.len() - 1] = F::ONE; for (i, &byte) in bytes.iter().rev().enumerate() { - row[value_bytes(i)] = F::from_canonical_u8(byte); + row.value_bytes[i] = F::from_canonical_u8(byte); } - row + row.into() } const fn generate_padding_row(&self) -> [F; NUM_COLUMNS] { @@ -237,10 +258,11 @@ impl, const D: usize> BytePackingStark { debug_assert!(cols.iter().all(|col| col.len() == n_rows)); for i in 0..BYTE_RANGE_MAX { - cols[RANGE_COUNTER][i] = F::from_canonical_usize(i); + cols[BYTE_PACKING_COL_MAP.range_counter][i] = F::from_canonical_usize(i); } for i in BYTE_RANGE_MAX..n_rows { - cols[RANGE_COUNTER][i] = F::from_canonical_usize(BYTE_RANGE_MAX - 1); + cols[BYTE_PACKING_COL_MAP.range_counter][i] = + F::from_canonical_usize(BYTE_RANGE_MAX - 1); } // For each column c in cols, generate the range-check @@ -248,7 +270,7 @@ impl, const D: usize> BytePackingStark { // columns rc_c and rc_c+1. for col in 0..NUM_BYTES { for i in 0..n_rows { - let c = value_bytes(col); + let c = BYTE_PACKING_COL_MAP.value_bytes[col]; let x = cols[c][i].to_canonical_u64() as usize; assert!( x < BYTE_RANGE_MAX, @@ -256,7 +278,7 @@ impl, const D: usize> BytePackingStark { x, BYTE_RANGE_MAX ); - cols[RC_FREQUENCIES][x] += F::ONE; + cols[BYTE_PACKING_COL_MAP.rc_frequencies][x] += F::ONE; } } } @@ -279,13 +301,15 @@ impl, const D: usize> Stark for BytePackingSt P: PackedField, { let local_values: &[P; NUM_COLUMNS] = vars.get_local_values().try_into().unwrap(); + let local_values: &BytePackingColumnsView

= local_values.borrow(); let next_values: &[P; NUM_COLUMNS] = vars.get_next_values().try_into().unwrap(); + let next_values: &BytePackingColumnsView

= next_values.borrow(); // Check the range column: First value must be 0, last row // must be 255, and intermediate rows must increment by 0 // or 1. - let rc1 = local_values[RANGE_COUNTER]; - let rc2 = next_values[RANGE_COUNTER]; + let rc1 = local_values.range_counter; + let rc2 = next_values.range_counter; yield_constr.constraint_first_row(rc1); let incr = rc2 - rc1; yield_constr.constraint_transition(incr * incr - incr); @@ -296,24 +320,24 @@ impl, const D: usize> Stark for BytePackingSt // We filter active columns by summing all the byte indices. // Constraining each of them to be boolean is done later on below. - let current_filter = local_values[LEN_INDICES_COLS].iter().copied().sum::

(); + let current_filter = local_values.index_len.iter().copied().sum::

(); yield_constr.constraint(current_filter * (current_filter - one)); // The filter column must start by one. yield_constr.constraint_first_row(current_filter - one); // The is_read flag must be boolean. - let current_is_read = local_values[IS_READ]; + let current_is_read = local_values.is_read; yield_constr.constraint(current_is_read * (current_is_read - one)); // Each byte index must be boolean. for i in 0..NUM_BYTES { - let idx_i = local_values[index_len(i)]; + let idx_i = local_values.index_len[i]; yield_constr.constraint(idx_i * (idx_i - one)); } // Only padding rows have their filter turned off. - let next_filter = next_values[LEN_INDICES_COLS].iter().copied().sum::

(); + let next_filter = next_values.index_len.iter().copied().sum::

(); yield_constr.constraint_transition(next_filter * (next_filter - current_filter)); // Check that all limbs after final length are 0. @@ -321,7 +345,7 @@ impl, const D: usize> Stark for BytePackingSt // If the length is i+1, then value_bytes(i+1),...,value_bytes(NUM_BYTES-1) must // be 0. for j in i + 1..NUM_BYTES { - yield_constr.constraint(local_values[index_len(i)] * local_values[value_bytes(j)]); + yield_constr.constraint(local_values.index_len[i] * local_values.value_bytes[j]); } } } @@ -334,14 +358,16 @@ impl, const D: usize> Stark for BytePackingSt ) { let local_values: &[ExtensionTarget; NUM_COLUMNS] = vars.get_local_values().try_into().unwrap(); + let local_values: &BytePackingColumnsView> = local_values.borrow(); let next_values: &[ExtensionTarget; NUM_COLUMNS] = vars.get_next_values().try_into().unwrap(); + let next_values: &BytePackingColumnsView> = next_values.borrow(); // Check the range column: First value must be 0, last row // must be 255, and intermediate rows must increment by 0 // or 1. - let rc1 = local_values[RANGE_COUNTER]; - let rc2 = next_values[RANGE_COUNTER]; + let rc1 = local_values.range_counter; + let rc2 = next_values.range_counter; yield_constr.constraint_first_row(builder, rc1); let incr = builder.sub_extension(rc2, rc1); let t = builder.mul_sub_extension(incr, incr, incr); @@ -353,7 +379,7 @@ impl, const D: usize> Stark for BytePackingSt // We filter active columns by summing all the byte indices. // Constraining each of them to be boolean is done later on below. - let current_filter = builder.add_many_extension(&local_values[LEN_INDICES_COLS]); + let current_filter = builder.add_many_extension(local_values.index_len); let constraint = builder.mul_sub_extension(current_filter, current_filter, current_filter); yield_constr.constraint(builder, constraint); @@ -362,20 +388,20 @@ impl, const D: usize> Stark for BytePackingSt yield_constr.constraint_first_row(builder, constraint); // The is_read flag must be boolean. - let current_is_read = local_values[IS_READ]; + let current_is_read = local_values.is_read; let constraint = builder.mul_sub_extension(current_is_read, current_is_read, current_is_read); yield_constr.constraint(builder, constraint); // Each byte index must be boolean. for i in 0..NUM_BYTES { - let idx_i = local_values[index_len(i)]; + let idx_i = local_values.index_len[i]; let constraint = builder.mul_sub_extension(idx_i, idx_i, idx_i); yield_constr.constraint(builder, constraint); } // Only padding rows have their filter turned off. - let next_filter = builder.add_many_extension(&next_values[LEN_INDICES_COLS]); + let next_filter = builder.add_many_extension(next_values.index_len); let constraint = builder.sub_extension(next_filter, current_filter); let constraint = builder.mul_extension(next_filter, constraint); yield_constr.constraint_transition(builder, constraint); @@ -386,7 +412,7 @@ impl, const D: usize> Stark for BytePackingSt // be 0. for j in i + 1..NUM_BYTES { let constr = - builder.mul_extension(local_values[index_len(i)], local_values[value_bytes(j)]); + builder.mul_extension(local_values.index_len[i], local_values.value_bytes[j]); yield_constr.constraint(builder, constr); } } @@ -398,9 +424,13 @@ impl, const D: usize> Stark for BytePackingSt fn lookups(&self) -> Vec> { vec![Lookup { - columns: Column::singles(value_bytes(0)..value_bytes(0) + NUM_BYTES).collect(), - table_column: Column::single(RANGE_COUNTER), - frequencies_column: Column::single(RC_FREQUENCIES), + columns: Column::singles( + BYTE_PACKING_COL_MAP.value_bytes[0] + ..BYTE_PACKING_COL_MAP.value_bytes[0] + NUM_BYTES, + ) + .collect(), + table_column: Column::single(BYTE_PACKING_COL_MAP.range_counter), + frequencies_column: Column::single(BYTE_PACKING_COL_MAP.rc_frequencies), filter_columns: vec![Default::default(); NUM_BYTES], }] } diff --git a/evm_arithmetization/src/byte_packing/columns.rs b/evm_arithmetization/src/byte_packing/columns.rs index 45e3211de..5e394ae71 100644 --- a/evm_arithmetization/src/byte_packing/columns.rs +++ b/evm_arithmetization/src/byte_packing/columns.rs @@ -1,43 +1,48 @@ //! Byte packing registers. -use core::ops::Range; - -use crate::byte_packing::NUM_BYTES; - -/// 1 if this is a READ operation, and 0 if this is a WRITE operation. -pub(crate) const IS_READ: usize = 0; - -pub(super) const LEN_INDICES_START: usize = IS_READ + 1; -// There are `NUM_BYTES` columns used to represent the length of -// the input byte sequence for a (un)packing operation. -// index_len(i) is 1 iff the length is i+1. -pub(crate) const fn index_len(i: usize) -> usize { - debug_assert!(i < NUM_BYTES); - LEN_INDICES_START + i +use std::mem::transmute; + +use zk_evm_proc_macro::Columns; + +use crate::{byte_packing::NUM_BYTES, util::indices_arr}; + +/// A view of `BytePackingStark`'s columns. +#[repr(C)] +#[derive(Columns, Eq, PartialEq, Debug)] +pub(crate) struct BytePackingColumnsView { + /// 1 if this is a READ operation, and 0 if this is a WRITE operation. + pub is_read: T, + + // There are `NUM_BYTES` columns used to represent the length of + // the input byte sequence for a (un)packing operation. + // index_len[i] is 1 iff the length is i+1. + pub index_len: [T; NUM_BYTES], + + pub addr_context: T, + pub addr_segment: T, + pub addr_virtual: T, + pub timestamp: T, + + // 32 byte limbs hold a total of 256 bits. + // There are `NUM_BYTES` columns used to store the values of the bytes + // that are being read/written for an (un)packing operation. + pub value_bytes: [T; NUM_BYTES], + + /// The counter column (used for the logUp range check) starts from 0 and + /// increments. + pub range_counter: T, + /// The frequencies column used in logUp. + pub rc_frequencies: T, } -// Note: Those are used to obtain the length of a sequence of bytes being -// processed. -pub(crate) const LEN_INDICES_COLS: Range = LEN_INDICES_START..LEN_INDICES_START + NUM_BYTES; - -pub(crate) const ADDR_CONTEXT: usize = LEN_INDICES_START + NUM_BYTES; -pub(crate) const ADDR_SEGMENT: usize = ADDR_CONTEXT + 1; -pub(crate) const ADDR_VIRTUAL: usize = ADDR_SEGMENT + 1; -pub(crate) const TIMESTAMP: usize = ADDR_VIRTUAL + 1; - -// 32 byte limbs hold a total of 256 bits. -const BYTES_VALUES_START: usize = TIMESTAMP + 1; -// There are `NUM_BYTES` columns used to store the values of the bytes -// that are being read/written for an (un)packing operation. -pub(crate) const fn value_bytes(i: usize) -> usize { - debug_assert!(i < NUM_BYTES); - BYTES_VALUES_START + i -} +// `u8` is guaranteed to have a `size_of` of 1. +/// Number of columns in `BytePackingStark`. +pub(crate) const NUM_COLUMNS: usize = size_of::>(); -/// The counter column (used for the range check) starts from 0 and increments. -pub(crate) const RANGE_COUNTER: usize = BYTES_VALUES_START + NUM_BYTES; -/// The frequencies column used in logUp. -pub(crate) const RC_FREQUENCIES: usize = RANGE_COUNTER + 1; +const fn make_col_map() -> BytePackingColumnsView { + let indices_arr = indices_arr::(); + unsafe { transmute::<[usize; NUM_COLUMNS], BytePackingColumnsView>(indices_arr) } +} -/// Number of columns in `BytePackingStark`. -pub(crate) const NUM_COLUMNS: usize = RANGE_COUNTER + 2; +/// Map between the `BytePacking` columns and (0..`NUM_COLUMNS`) +pub(crate) const BYTE_PACKING_COL_MAP: BytePackingColumnsView = make_col_map(); From 16baa989527af1015fef1b4395c89c6ac5c86bdc Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Fri, 9 Aug 2024 06:06:25 -0400 Subject: [PATCH 4/6] Flush logs upon success (#481) --- zero_bin/tools/prove_stdio.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zero_bin/tools/prove_stdio.sh b/zero_bin/tools/prove_stdio.sh index 35a9d9990..d284a0fa1 100755 --- a/zero_bin/tools/prove_stdio.sh +++ b/zero_bin/tools/prove_stdio.sh @@ -93,9 +93,10 @@ fi # proof. This is useful for quickly testing decoding and all of the # other non-proving code. if [[ $TEST_ONLY == "test_only" ]]; then - cargo run --release --features test_only --bin leader -- --runtime in-memory --load-strategy on-demand stdio < $INPUT_FILE | tee $TEST_OUT_PATH + cargo run --release --features test_only --bin leader -- --runtime in-memory --load-strategy on-demand stdio < $INPUT_FILE &> $TEST_OUT_PATH if grep -q 'All proof witnesses have been generated successfully.' $TEST_OUT_PATH; then echo -e "\n\nSuccess - Note this was just a test, not a proof" + rm $TEST_OUT_PATH exit else echo "Failed to create proof witnesses. See \"zk_evm/tools/test.out\" for more details." @@ -106,7 +107,7 @@ fi cargo build --release --jobs "$num_procs" start_time=$(date +%s%N) -"${TOOLS_DIR}/../../target/release/leader" --runtime in-memory --load-strategy on-demand stdio < $INPUT_FILE | tee $LEADER_OUT_PATH +"${TOOLS_DIR}/../../target/release/leader" --runtime in-memory --load-strategy on-demand stdio < $INPUT_FILE &> $LEADER_OUT_PATH end_time=$(date +%s%N) tail -n 1 $LEADER_OUT_PATH > $PROOFS_JSON_PATH @@ -119,6 +120,7 @@ if grep -q 'All proofs verified successfully!' $VERIFY_OUT_PATH; then echo "Success!" echo "Duration:" $duration_sec " seconds" echo "Note, this duration is inclusive of circuit handling and overall process initialization"; + rm $LEADER_OUT_PATH $VERIFY_OUT_PATH # we keep the generated proof for potential reuse else echo "there was an issue with proof verification"; exit 1 From cf222f0a25822847c21ba2a746a5ca7e652b9591 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Fri, 9 Aug 2024 14:13:18 -0400 Subject: [PATCH 5/6] Fix account creation reversion in decoder processing (#480) * Fix account creation reversion in decoder processing * Also prune storage if account is removed * Handle all reverts cases together * Pacify mighty clippy --- evm_arithmetization/src/generation/mpt.rs | 12 +- trace_decoder/src/decoding.rs | 54 ++- .../data/witnesses/zero_jerigon/b28_dev.json | 325 ++++++++++++++++++ .../zero_jerigon/b28_dev_header.json | 32 ++ 4 files changed, 414 insertions(+), 9 deletions(-) create mode 100644 trace_decoder/tests/data/witnesses/zero_jerigon/b28_dev.json create mode 100644 trace_decoder/tests/data/witnesses/zero_jerigon/b28_dev_header.json diff --git a/evm_arithmetization/src/generation/mpt.rs b/evm_arithmetization/src/generation/mpt.rs index a38835d7f..dd96ff08f 100644 --- a/evm_arithmetization/src/generation/mpt.rs +++ b/evm_arithmetization/src/generation/mpt.rs @@ -67,7 +67,12 @@ impl LegacyReceiptRlp { } } -pub(crate) fn parse_receipts(rlp: &[u8]) -> Result, ProgramError> { +/// Decodes a transaction receipt from an RLP string, outputting a tuple +/// consisting of: +/// - the receipt's [`PayloadInfo`], +/// - the transaction type, +/// - the decoded [`LegacyReceiptRlp`]. +pub fn decode_receipt(rlp: &[u8]) -> Result<(PayloadInfo, usize, LegacyReceiptRlp), ProgramError> { let txn_type = match rlp.first().ok_or(ProgramError::InvalidRlp)? { 1 => 1, 2 => 2, @@ -82,6 +87,11 @@ pub(crate) fn parse_receipts(rlp: &[u8]) -> Result, ProgramError> { let decoded_receipt: LegacyReceiptRlp = rlp::decode(rlp).map_err(|_| ProgramError::InvalidRlp)?; + Ok((payload_info, txn_type, decoded_receipt)) +} + +pub(crate) fn parse_receipts(rlp: &[u8]) -> Result, ProgramError> { + let (payload_info, txn_type, decoded_receipt) = decode_receipt(rlp)?; let mut parsed_receipt = if txn_type == 0 { Vec::new() } else { diff --git a/trace_decoder/src/decoding.rs b/trace_decoder/src/decoding.rs index 8621c8d51..80ac4e385 100644 --- a/trace_decoder/src/decoding.rs +++ b/trace_decoder/src/decoding.rs @@ -6,7 +6,10 @@ use std::{ use ethereum_types::{Address, BigEndianHash, H256, U256, U512}; use evm_arithmetization::{ - generation::{mpt::AccountRlp, GenerationInputs, TrieInputs}, + generation::{ + mpt::{decode_receipt, AccountRlp}, + GenerationInputs, TrieInputs, + }, proof::{BlockMetadata, ExtraBlockData, TrieRoots}, testing_utils::{BEACON_ROOTS_CONTRACT_ADDRESS_HASHED, HISTORY_BUFFER_LENGTH}, }; @@ -152,6 +155,10 @@ pub enum TraceParsingErrorReason { #[error("Failed to decode RLP bytes ({0}) as an Ethereum account due to the error: {1}")] AccountDecode(String, String), + /// Failure to decode a transaction receipt. + #[error("Failed to decode RLP bytes ({0}) as a transaction receipt due to the error: {1}")] + ReceiptDecode(String, String), + /// Failure due to trying to access or delete a storage trie missing /// from the base trie. #[error("Missing account storage trie in base trie when constructing subset partial trie for txn (account: {0:x})")] @@ -470,6 +477,7 @@ impl ProcessedBlockTrace { fn apply_deltas_to_trie_state( trie_state: &mut PartialTrieState, deltas: &NodesUsedByTxn, + meta: &TxnMetaState, ) -> TraceParsingResult { let mut out = TrieDeltaApplicationOutput::default(); @@ -517,11 +525,11 @@ impl ProcessedBlockTrace { 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); + // If the account was created, then it will not exist yet in the trie. + let (is_created, val_bytes) = match trie_state.state.get(val_k) { + Some(bytes) => (false, bytes), + None => (true, &EMPTY_ACCOUNT_BYTES_RLPED[..]), + }; let mut account = account_from_rlped_bytes(val_bytes)?; @@ -532,6 +540,33 @@ impl ProcessedBlockTrace { )?; let updated_account_bytes = rlp::encode(&account); + if is_created { + // If the account did not exist prior this transaction, we + // need to make sure the transaction didn't revert. + + // Check status in the receipt. + let (_, _, receipt) = decode_receipt(&meta.receipt_node_bytes).map_err(|err| { + Box::new(TraceParsingError::new( + TraceParsingErrorReason::ReceiptDecode( + hex::encode(&meta.receipt_node_bytes), + format!("{:?}", err), + ), + )) + })?; + + if !receipt.status { + // The transaction failed, hence any created account should be removed. + trie_state + .state + .delete(val_k) + .map_err(TraceParsingError::from)?; + + trie_state.storage.remove(hashed_acc_addr); + + continue; + } + } + trie_state .state .insert(val_k, updated_account_bytes.to_vec()) @@ -705,8 +740,11 @@ impl ProcessedBlockTrace { Self::update_txn_and_receipt_tries(curr_block_tries, &txn_info.meta, txn_idx) .map_err(TraceParsingError::from)?; - let mut delta_out = - Self::apply_deltas_to_trie_state(curr_block_tries, &txn_info.nodes_used_by_txn)?; + let mut delta_out = Self::apply_deltas_to_trie_state( + curr_block_tries, + &txn_info.nodes_used_by_txn, + &txn_info.meta, + )?; let nodes_used_by_txn = if is_initial_payload { let mut nodes_used = txn_info.nodes_used_by_txn; diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b28_dev.json b/trace_decoder/tests/data/witnesses/zero_jerigon/b28_dev.json new file mode 100644 index 000000000..e94bfd458 --- /dev/null +++ b/trace_decoder/tests/data/witnesses/zero_jerigon/b28_dev.json @@ -0,0 +1,325 @@ +[ + { + "block_trace": { + "trie_pre_images": { + "combined": { + "compact": "0x000397e4ae58e8d997dd2b19ed09ad9a66d1c0fd304d074e4a7bfb3f1fcc512a3e6303e3f965eb951555f4f5b98ab8ff7a6e33813bf9ac6830c1f8e5806e5563d580580338e34f9e0e4830343ba24f5fcf0eba28d79cb86397adfb16a0169ee7f018003605582003d2dbe83a6ba7fd75737c8d7453d984e7938ba7ae113d3da2ad7433061157b0084101055820039c0a91ba30d346a55890b1b07287d8aae35baa8c4068ef8f1de66084aca750084101055820032dee2834b372effea65d7c74ab9f3ac60ef271e846721aa8759a346a3b55400c182c4c033b2e3c9e69be1f3b8f45fc0219020303d2e57f615a47508c6e60935353428b9fc1cc75677a3eb8f5f73d61dd0aaff5f5055820021d99c6bec3aecbec962be74b25448511983b013c0876f6f4e6029773dfad61084101034d78166b48044fdc28ed22d2fd39c8df6f8aaa04cb71d3a17286856f6893ff830361d0d822132d4f46ea985e243ddfec94d3d12badf05efc100c2acaab3232979a05582002b3096c912adc7674ca92b68ddb3b0494e9b988fbef1bed7938e8fac4c2df7a0841010458613373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff01550003b42b3ff34cf94e9bbe576c7d3407125a4a78bc7ed3612b0859c3d54732184c04005820027b7c396774442700f076020d6320b11205bdfa1ed29bca4e6403a4ed13003e5820ae9e0c29f970fa4b19e4dd1e122f44e36e28b0366016bede9cad8ee2b9abc0ec00582002b38c731c4c7dcfa7fe43e88c29fc820b7a639725c025ad4d57a4ba4bfc7021582056138500dd56d246b423922d012878336234e86eb5163ddfe56ded875b6bb90b0219010803ae2be4c29fc3af59f45ec87171244d90f717dff0b4053c3f9e515f255ad21016037be6082e5131fd0ec3d0443048028ff5165919d8135b83eb9e871689af6151b800582002a35688244862fc757c3be1f20703dd54ed23a0500e2ba3a8c715f441dab6695820ee297c12fffb82a628f254b4e8b04d1068789f928e5ebbd53a9c6960f6a09ff500582002ac6e5f6b2ae78fed03998a21fffd6d0abe57ee36fa18fd8f390bf96881e1864466b12ee40219801003805f6a0a46b011fe00f83634ae7b5ee301e296ae987730717c0438d498fdb74303d08a05d0052c73e1436082038fba2562fcf36d5f1f6366027bf2c4c78218324703202eba034d3583cbc1fa600657c8bb355910c171c2e7174a016ee7f84a9982bb0352392a3a7f95abdd8c48bbaadf77e7be701e1a9c259a379f7837c6fdd77d569703327d2799bdf55eb20ae98ff3acac7a7e6d1bd5d5a763f529efb0872988c15e1f03241da08797ca89e9a4ae4d4d6fcbb1b22001c4aac25a1ef3e8b2edef94e3d62503f9b28e4f179934edd4393a00171269821a9a44893002137f760faa68fe8f19fb03bb4cbca0dff5a0525413795bd818ec527f9e385e8df4c101f7050f0cf93e61fa0363f140c8c24a1024f88d5c51292e514db03f1c7eec2a58e7c06d1ebeeea4ad7f03650d71b12779260726c804623311ab746d2bfa6162f236b8fd87234c0a63eb5e0332ee5d5e5496bb9209cd8bc651126d19b79b38b9fc12122ce469cfefd53099180219ffff05582002d65eaa92c6bc4c13a5ec45527f0c18ea8932588728769ec7aecfe6d9f32e420701186105582002401b4e97e67c4a6d8095979b4c1ecb9eaf5726fc8a12798975a7653e98087208410105582002c8bb92227ba91a2657f29c5498f5c5b07ae36369c8d9283444f477930c79be0841010391d9c76bfbc066e84f0b415c737ab8c477498701d920526db41690050cfade9905582002a5ac70a18399817ccbe8bd4d9c78115136cc61699d51af87608af520226c32084101055820029bd268aedc8dbb36db0a4664e17b83abcfd87e404901eee7bd5704a95064ee08410105582002a06a67e7fee9c938155c50e6cec9da70b66679e777da6f02149c6674cd910508410103d9240a9d2d5851d05a97ff3305334dfdb0101e1e321fc279d2bb3cad6afa8fc802197ffd03bb47debc67bc4630b38be2bcb4da207e5eeb900be8ebef5391e33f311259fe8f03f5973ddf4b6e95090d9f952ebba2f1efb05ecf7a8ca1108a51b83035168aedbb03724f631a141f6a338cc83620dba6cbc53e49035312204f3847a68e2d852fd67a0318385194f4abe12385937758d7599c7a11760dcfe3e3e2ba06ebfad44f10484e035750dfde66a7a051d429386138dc80cc40f94d0212ebbd6f3dd7950b994b7e6903be88e4724326382a8b56e2328eeef0ad51f18d5bae0e84296afe14c4028c4af903bcfafd70d9348d083c7d2e0e4a50b5012fc315c98366d3549628ce4ef1ee27d30558200216c0a47b12c20d5ac5c381464a89521d8b0c1a5cb328aeabb29b70bc3347d408410105582002b41c13f6e16fd2242a43b041cfd1f02cbb4cf06943d14365bb8f0a30ff507a0841010558200225fd832d00c025204251bf6c3f044d57347429973f8c48fd3d325f0e758f1b084c033b2e3c9fd0803ce8000000055820022e9dc15a35c1ab9d0ac4345b1f02fd8e8070883a33651c0c143b25827082680841010558200276da518a393dbd067dc72abfa08d475ed6447fca96d92ec3f9e7eba503ca61084102055820023db8611e8d0c159eb724d2aea72763dfb5a6909a8516467c79d0386d61a51108410103fd04efc67927fbaef3b8defd259a7f78b261bd8a0f93eedf78c0cb27c491083a0390c5d06de0e326fe782f9469df01c9028941571dffbbc643aed052feb1cff2f603ce35ec89408b73b495e2b3e0ef46d1ec796c7c2e6b05b8ad21547ffa391875c40558200203175444dc43cc33391c4c4010eac15d401b2eb5ffc7bc778abe372f28ce370841010219e75d031796617427e67ed10cdf8a72b02689a700ba71eb93186a1b120c9ad0b0e56eae03ebc052143e57028286ef115b03a8a7736efa26c165fcfffc807960b2bf91e1c003bffc43dfea9c9a304f9ae5154c60adff35f4c9a6e722a59d60cca4d97c4038bb030029f4fb3c18471948807d3163670fc396e8459d738d87a9aeb32444a6b9322d035740575999fde2ddd421a9f5907dfc4be5fc3f5f96d65240ae3b3b596d38465f055820028fd226a1d43fd9da3e823f52b7dbdfcb4e694ea884df54d86a14f708f43bd30848016345785d8a000005582002b693085beb21b0878c7e5fab7519ce0d6105354cf6744dbfb73e3745e929e4084101055820027f470df15c88b9beaf3fad72890a64f8851d01ece546b0c9fb3bdf92041a35084101055820029cb4b751333c144710961e20611eb00c783b77e8cb532c64307202fb109699084c033b2e3c9fd0803ce8000000035f4ca08c32e4a3997d0e94d8946e3d361ec5d0225d9cd85c6de8d572bb0a99c903faecf459527318d12fee2db8d85c54fc244d0207ba336a01e397593b801ae61f055820039bbcf08818ec3329e5769dff3fbdad66e6312a961acb32c1b203edf70aeba0084101031c13664b9096979e0a6a58919940b248687992af952308581eff9d0e8c14119a02194008033c4172d2125ea08dbe10db0a4a54290a622f10f24ba3a2460969ac13fcab7db3055820026a58207750197f48cb90864096850259845c2c8e90c74433325c0b144bf8bb08410105582002fa0eae268038cfa984647a1d0635beb86eda9fb7b500688f3189520cfa9ee50841010219fb710219ffff" + } + }, + "code_db": null, + "txn_info": [ + { + "traces": { + "0x6383f81bdc7c347888a54a8c6dd72f808221de21": { + "balance": "0x0" + }, + "0x3c0273033f6ad2ed3d768bb83b269d4dcf381fd1": { + "balance": "0x162e7fb2886c260", + "nonce": "0x1" + }, + "0x8943545177806ed17b9f23f0a21ee5948ecaa776": { + "balance": "0x33b2e3c9e6a196373797c7c" + } + }, + "meta": { + "byte_code": "0xf86a80843d44ddd9830186a08087d529ae9e8600009060005b600101804050630000000256008360306ba0b4615acfbef3163918e6907cfe40f0225ef4ce9ce3663ac905e01e56b307064ca03b2189980a28734c355d1b4da64cc8c4027806c5de077d7a84763a3db08a3db2", + "new_txn_trie_node_byte": "0xf86a80843d44ddd9830186a08087d529ae9e8600009060005b600101804050630000000256008360306ba0b4615acfbef3163918e6907cfe40f0225ef4ce9ce3663ac905e01e56b307064ca03b2189980a28734c355d1b4da64cc8c4027806c5de077d7a84763a3db08a3db2", + "new_receipt_trie_node_byte": "0xf9010980830186a0b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0", + "gas_used": 100000 + } + } + ] + }, + "other_data": { + "b_data": { + "b_meta": { + "block_beneficiary": "0x8943545177806ed17b9f23f0a21ee5948ecaa776", + "block_timestamp": "0x66b12fe0", + "block_number": "0x1c", + "block_difficulty": "0x0", + "block_random": "0x1c84d61f862e3937c259d3f998dac1381f170c18fcd63652db04f4eada8defc8", + "block_gaslimit": "0x1c9c380", + "block_chain_id": "0x301824", + "block_base_fee": "0x174e475", + "block_gas_used": "0x186a0", + "block_blob_gas_used": "0x0", + "block_excess_blob_gas": "0x0", + "parent_beacon_block_root": "0x287ecf202259331c4b437237cf469539abc2ab02d6ab3072f14a8523cd855a36", + "block_bloom": [ + "0x0", + "0x0", + "0x0", + "0x0", + "0x0", + "0x0", + "0x0", + "0x0" + ] + }, + "b_hashes": { + "prev_hashes": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x5c09356fd54924bd63d5bef3f0f334114d8cb34e545b98f6289115d996e40c82", + "0xc7c2166896d1e9a890bcdeb2ca8470ca7d020de136588d8a05edb8fdf6ad9ac3", + "0x5329cbde8e0b834329c353a5f95b49beed7fb692395838fd005c26a2146fcc49", + "0xe4c9847766b928037efe232abe211de3c2cc75254d8a2cde999c2c00682c82eb", + "0xb3f21feee05522028e2618ea8377f6a2a84ecec35fe3292ced387537e66f33e2", + "0xd209ac9bd40d44278a40bbebcda14234d00c17519242847224256ce3d054f236", + "0x89003258f6a8487f5b66efa320cfde14fabb3b904bbf6cd71a3869045d7b961e", + "0x369bd1e5568cc7270031694949fbce7a44b7fe8b41d23e48a43c8d2d2c76dc45", + "0x289a8ad22308f255c8831085473986eb119cb1fe931efddffbbc38a45ca21978", + "0x30302bbf676bd710268dd16aaee094293722f3ffbbaaa895c04e44cd6e617586", + "0xd527fd267764e4edd30e6b00997ba8dc8372ffb0011a550bd1cd90ae08b71c2d", + "0x25dee803de79dce71c8c69ebbdb785df3e9a702fea51e44dfbc5772602d579c6", + "0xaa1e00aa825a379627e2ad25494ef4df8746117516777c059a56189b439f160f", + "0x05998aee0a94a1641718cfeabef8be9c1ff2317d40ee6f6d556984e47806645f", + "0xba33e8ebb6967582ac6655ff3df8c853b7b9c8f1049fe428ba983ae0777acb01", + "0x12c849a7cd349cad36c8af763dcde0aad8599e379cd4eb10ba1db4f71b849485", + "0xe47d533ba58626f1cac70e4c3c0232dbeb82fad0a6a40ca72dda8109ef3f0ea4", + "0xc4328e04fc8b7ec9391e934f283b2b1c7aea53fb6d20c057ffc87bffeceb79bb", + "0x53ea70df099bd2917e3c92b227dddcd08ec9304e09fc890ba10bbec233fcb951", + "0x10efed26e262a50f58b785edcfc20f0a12a4dfc792e6d49ec41dfa59023e3cbd", + "0x26acd6026abf999eb2be60bcb83ddbb63321c2527a5b3a9ec00a57d28535dddf", + "0x4cc4863be89f71110bfae4adcedd6baffdbfc6bab79647f20efd70883d220e63", + "0xe9a0d90fcd20b37ce2a01b75801f3aab6a5a4916e5c6d2586df64d2c0e5ccf6d", + "0xdb18b431446c147511678da8c3974ed68917e76238c0058ea281684ee45aac4e", + "0x4c82a772a2ef771fc7e0b2fdc95a74c1c37629c434658f21ffcf7f05cd055450", + "0xf0b132b1881455ebeff23c2823ceeaf4cd919803d1f0d28a208a32c6ba9e2e75", + "0xf52f4745f69063ba2705aec5c7478eb1cbaf3d4ef82e15ffe221d2a2dc798e30", + "0x46df26e4fe1d2df293cf06fe02296c0c80aa33cb00b6a3ca68baff077aefd17e" + ], + "cur_hash": "0x92162ed5e309ee46bf55cb3a811d423e6a34c5003e9d73cfacc6014789f48917" + }, + "withdrawals": [] + }, + "checkpoint_state_trie_root": "0x106d584f6804109c493182d0bb8ef06380aea582090f4c2927276869a8d1e436" + } + } +] \ No newline at end of file diff --git a/trace_decoder/tests/data/witnesses/zero_jerigon/b28_dev_header.json b/trace_decoder/tests/data/witnesses/zero_jerigon/b28_dev_header.json new file mode 100644 index 000000000..abf62be05 --- /dev/null +++ b/trace_decoder/tests/data/witnesses/zero_jerigon/b28_dev_header.json @@ -0,0 +1,32 @@ +[ + { + "baseFeePerGas": "0x174e475", + "blobGasUsed": "0x0", + "difficulty": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x", + "gasLimit": "0x1c9c380", + "gasUsed": "0x186a0", + "hash": "0x92162ed5e309ee46bf55cb3a811d423e6a34c5003e9d73cfacc6014789f48917", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x8943545177806ed17b9f23f0a21ee5948ecaa776", + "mixHash": "0x1c84d61f862e3937c259d3f998dac1381f170c18fcd63652db04f4eada8defc8", + "nonce": "0x0000000000000000", + "number": "0x1c", + "parentBeaconBlockRoot": "0x287ecf202259331c4b437237cf469539abc2ab02d6ab3072f14a8523cd855a36", + "parentHash": "0x46df26e4fe1d2df293cf06fe02296c0c80aa33cb00b6a3ca68baff077aefd17e", + "receiptsRoot": "0x777f1c1c378807634128348e4f0eeca6a0e7f516ea411690ca04266323f671a4", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x2b7", + "stateRoot": "0x1cc97e7468f7cfb76f239d373d9f5124d1055f72e400b6ba865ae4f0aa1abbbf", + "timestamp": "0x66b12fe0", + "totalDifficulty": "0x1", + "transactions": [ + "0xf7be24d19c8398bbbfc2238de4b3da9a9a2a645099de4601bfa2fadf70aac9da" + ], + "transactionsRoot": "0x75cd82907b1e556b0cb52d18449487629b2b82c769a37a4b60b1760bac90a734", + "uncles": [], + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" + } +] \ No newline at end of file From c0e03518a0512c37b2d67ca7fd9244fa131287f5 Mon Sep 17 00:00:00 2001 From: BGluth Date: Fri, 9 Aug 2024 15:16:57 -0400 Subject: [PATCH 6/6] Feat/237 mpt trie ext to branch collapse error (#455) * Added `Nibbles::empty()` - While `Nibbles::default()` already does this, I think having an explicit method that makes an empty `Nibbles` is a bit cleaner, even if it's a bit redundant. * Now returns an error if we collapse an `E --> H` - Unsafe, as if the extension was pointing to a (hashed) leaf, then we must collapse the extension into the leaf. - However, because the leaf is hashed, we can not tell that the hash node is from a leaf and also can not extract the leaf's key. - It's the user's responsibility to ensure that this scenario never occurs, so we need to return an error when it does. * Requested PR changes for #455 --- mpt_trie/src/nibbles.rs | 21 ++++++++++- mpt_trie/src/trie_ops.rs | 75 ++++++++++++++++++++++++++++++++++------ 2 files changed, 85 insertions(+), 11 deletions(-) diff --git a/mpt_trie/src/nibbles.rs b/mpt_trie/src/nibbles.rs index d72e027cf..982692c27 100644 --- a/mpt_trie/src/nibbles.rs +++ b/mpt_trie/src/nibbles.rs @@ -254,7 +254,7 @@ impl From for NibblesIntern { } } -#[derive(Copy, Clone, Deserialize, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[derive(Copy, Clone, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] /// A sequence of nibbles which is used as the key type into /// [`PartialTrie`][`crate::partial_trie::PartialTrie`]. /// @@ -323,6 +323,14 @@ impl Debug for Nibbles { } } +/// While we could just derive `Default` and it would be correct, it's a bit +/// cleaner to instead call [`Nibbles::new`] explicitly. +impl Default for Nibbles { + fn default() -> Self { + Self::new() + } +} + impl FromStr for Nibbles { type Err = StrToNibblesError; @@ -355,6 +363,17 @@ impl UpperHex for Nibbles { } impl Nibbles { + /// Create `Nibbles` that is empty. + /// + /// Note that mean that the key size is `0` and does not mean that the key + /// contains the `0` [`Nibble`]. + pub fn new() -> Self { + Self { + count: 0, + packed: NibblesIntern::default(), + } + } + /// Creates `Nibbles` from big endian bytes. /// /// Returns an error if the byte slice is empty or is longer than `32` diff --git a/mpt_trie/src/trie_ops.rs b/mpt_trie/src/trie_ops.rs index d142ea49a..1d6c0ab4d 100644 --- a/mpt_trie/src/trie_ops.rs +++ b/mpt_trie/src/trie_ops.rs @@ -31,11 +31,24 @@ pub enum TrieOpError { #[error("Attempted to delete a value that ended up inside a hash node! (hash: {0})")] HashNodeDeleteError(H256), - /// An error that occurs when encontered an unexisting type of node during - /// an extension node collapse. - #[error("Extension managed to get an unexisting child node type! (child: {0})")] + /// An error that occurs when we encounter an non-existing type of node + /// during an extension node collapse. + #[error("Extension managed to get an non-existing child node type! (child: {0})")] HashNodeExtError(TrieNodeType), + /// An error that occurs when we attempted to collapse an extension node + /// into a hash node. + /// + /// If this occurs, then there is a chance that we can not collapse + /// correctly and will produce the incorrect trie (and also the incorrect + /// trie hash). If the hash node is a hash of a leaf, then we need to + /// collapse the extension key into the leaf. However, this information + /// is lost if the node is hashed, and we can not tell if the hash node + /// was made from a leaf. As such, it's the responsibility of whoever is + /// constructing & mutating the trie that this will never occur. + #[error("Attempted to collapse an extension node into a hash node! This is unsafe! (See https://github.com/0xPolygonZero/zk_evm/issues/237 for more info) (Extension key: {0:x}, child hash node: {1:x})")] + ExtensionCollapsedIntoHashError(Nibbles, H256), + /// Failed to insert a hash node into the trie. #[error("Attempted to place a hash node on an existing node! (hash: {0})")] ExistingHashNodeError(H256), @@ -360,6 +373,11 @@ impl Node { } } + /// Deletes a key if it exists in the trie. + /// + /// If the key exists, then the existing node value that was deleted is + /// returned. Otherwise, if the key is not present, then `None` is returned + /// instead. pub(crate) fn trie_delete(&mut self, k: K) -> TrieOpResult>> where K: Into, @@ -368,8 +386,10 @@ impl Node { trace!("Deleting a leaf node with key {} if it exists", k); delete_intern(&self.clone(), k)?.map_or(Ok(None), |(updated_root, deleted_val)| { - // Final check at the root if we have an extension node - let wrapped_node = try_collapse_if_extension(updated_root)?; + // Final check at the root if we have an extension node. While this check also + // exists as we recursively traverse down the trie, it can not perform this + // check on the root node. + let wrapped_node = try_collapse_if_extension(updated_root, &Nibbles::default())?; let node_ref: &Node = &wrapped_node; *self = node_ref.clone(); @@ -521,12 +541,15 @@ fn delete_intern( { false => { // Branch stays. + let mut updated_children = children.clone(); updated_children[nibble as usize] = - try_collapse_if_extension(updated_child)?; + try_collapse_if_extension(updated_child, &curr_k)?; branch(updated_children, value.clone()) } true => { + // We need to collapse the branch into an extension/leaf node. + let (child_nibble, non_empty_node) = get_other_non_empty_child_and_nibble_in_two_elem_branch( children, nibble, @@ -559,7 +582,7 @@ fn delete_intern( delete_intern(child, curr_k).and_then(|res| { res.map_or(Ok(None), |(updated_child, value_deleted)| { let updated_node = - collapse_ext_node_if_needed(ext_nibbles, &updated_child)?; + collapse_ext_node_if_needed(ext_nibbles, &updated_child, &curr_k)?; Ok(Some((updated_node, value_deleted))) }) }) @@ -576,16 +599,44 @@ fn delete_intern( } } -fn try_collapse_if_extension(node: WrappedNode) -> TrieOpResult> { +fn try_collapse_if_extension( + node: WrappedNode, + curr_key: &Nibbles, +) -> TrieOpResult> { match node.as_ref() { - Node::Extension { nibbles, child } => collapse_ext_node_if_needed(nibbles, child), + Node::Extension { nibbles, child } => collapse_ext_node_if_needed(nibbles, child, curr_key), _ => Ok(node), } } +/// Attempt to collapse an extension node if we are required. +/// +/// The scenarios where we are required to do so are where the extension node is +/// pointing to a: +/// - Extension (the parent extension absorbs the child's key). +/// - Leaf (the leaf absorbs the extension's key). +/// +/// While an extension does not collapse when its child is a branch, we need to +/// still check that a specific edge case does not exist for the branch node. +/// Specifically, if all of the following holds true for the branch where: +/// - It has exactly two children. +/// - One child that is a hash node. +/// - One child that is a leaf node. +/// - The leaf child ends up getting deleted. +/// +/// Then we need to return an error, because if the hash node was created from a +/// leaf node, we are required to collapse the extension key into the leaf node. +/// However, since it's a hash node, we: +/// - Have no idea what the original node was. +/// - Can not access the underlying key if we needed to collapse it. +/// +/// Because of this, we need to rely on the user to not allow `mpt_trie` to +/// arrive at this state, as we can not ensure that we will be able to produce +/// the correct trie. fn collapse_ext_node_if_needed( ext_nibbles: &Nibbles, child: &WrappedNode, + curr_key: &Nibbles, ) -> TrieOpResult> { trace!( "Collapsing extension node ({:x}) with child {}...", @@ -606,7 +657,11 @@ fn collapse_ext_node_if_needed( nibbles: leaf_nibbles, value, } => Ok(leaf(ext_nibbles.merge_nibbles(leaf_nibbles), value.clone())), - Node::Hash(_) => Ok(extension(*ext_nibbles, child.clone())), + Node::Hash(h) => Err(TrieOpError::ExtensionCollapsedIntoHashError( + curr_key.merge_nibbles(ext_nibbles), + *h, + )), + // Can never do this safely, so return an error. _ => Err(TrieOpError::HashNodeExtError(TrieNodeType::from(child))), } }