From 4629fb0a8850569feab166d7d2aca9c8d3dda65a Mon Sep 17 00:00:00 2001 From: Marko Atanasievski Date: Fri, 20 Sep 2024 14:56:48 +0200 Subject: [PATCH 1/4] feat: full tries comparison --- mpt_trie/src/debug_tools/diff.rs | 208 +++++++++++++++++++++++-------- zero/src/trie_diff/mod.rs | 26 ++-- 2 files changed, 170 insertions(+), 64 deletions(-) diff --git a/mpt_trie/src/debug_tools/diff.rs b/mpt_trie/src/debug_tools/diff.rs index 880984290..c08cb80d1 100644 --- a/mpt_trie/src/debug_tools/diff.rs +++ b/mpt_trie/src/debug_tools/diff.rs @@ -20,10 +20,9 @@ //! - Top-down will find the highest point of a structural divergence and report //! it. If there are multiple divergences, then only the one that is the //! highest in the trie will be reported. -//! - Bottom-up (not implemented) is a lot more complex to implement, but will -//! attempt to find the smallest structural trie difference between the trie. -//! If there are multiple differences, then this will likely be what you want -//! to use. +//! - Bottom-up is a lot more complex to implement, but will attempt to find the +//! smallest structural trie difference between the trie. If there are +//! multiple differences, then this will likely be what you want to use. use std::fmt::{self, Debug}; use std::{fmt::Display, ops::Deref}; @@ -48,17 +47,16 @@ fn get_key_piece_from_node(n: &Node) -> Nibbles { #[derive(Clone, Debug, Eq, Hash, PartialEq)] /// The difference between two Tries, represented as the highest -/// point of a structural divergence. +/// array of `DiffPoint`s. pub struct TrieDiff { - /// The highest point of structural divergence. - pub latest_diff_res: Option, - // TODO: Later add a second pass for finding diffs from the bottom up (`earliest_diff_res`). + /// Diff points between the two tries. + pub diff_points: Vec, } impl Display for TrieDiff { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(diff) = &self.latest_diff_res { - write!(f, "{}", diff)?; + for (index, diff_point) in self.diff_points.iter().enumerate() { + writeln!(f, "{}: {}\n", index, diff_point)?; } Ok(()) @@ -176,32 +174,30 @@ impl NodeInfo { } /// Create a diff between two tries. Will perform both types of diff searches -/// (top-down & bottom-up). -pub fn create_diff_between_tries(a: &HashedPartialTrie, b: &HashedPartialTrie) -> TrieDiff { +/// (top-down & bottom-up). It will not stop on first difference. +pub fn create_full_diff_between_tries(a: &HashedPartialTrie, b: &HashedPartialTrie) -> TrieDiff { TrieDiff { - latest_diff_res: find_latest_diff_point_between_tries(a, b), + diff_points: find_all_diff_points_between_tries(a, b), } } -// Only support `HashedPartialTrie` due to it being significantly faster to -// detect differences because of caching hashes. -fn find_latest_diff_point_between_tries( +fn find_all_diff_points_between_tries( a: &HashedPartialTrie, b: &HashedPartialTrie, -) -> Option { +) -> Vec { let state = DepthDiffPerCallState::new(a, b, Nibbles::default(), 0); - let mut longest_state = DepthNodeDiffState::default(); - - find_latest_diff_point_between_tries_rec(&state, &mut longest_state); - - // If there was a node diff, we always want to prioritize displaying this over a - // hash diff. The reasoning behind this is hash diffs can become sort of - // meaningless or misleading if the trie diverges at some point (eg. saying - // there is a hash diff deep in two separate trie structures doesn't make much - // sense). - longest_state - .longest_key_node_diff - .or(longest_state.longest_key_hash_diff) + let mut longest_states = Vec::new(); + + find_all_diff_points_between_tries_rec(&state, &mut longest_states); + + longest_states + .into_iter() + .filter_map(|longest_state| { + longest_state + .longest_key_node_diff + .or(longest_state.longest_key_hash_diff) + }) + .collect() } #[derive(Debug, Default)] @@ -298,9 +294,11 @@ impl<'a> DepthDiffPerCallState<'a> { } } -fn find_latest_diff_point_between_tries_rec( +// Search for the differences between two tries. Do not stop on first +// difference. +fn find_all_diff_points_between_tries_rec( state: &DepthDiffPerCallState, - depth_state: &mut DepthNodeDiffState, + depth_states: &mut Vec, ) -> DiffDetectionState { let a_hash = state.a.hash(); let b_hash = state.b.hash(); @@ -319,19 +317,28 @@ fn find_latest_diff_point_between_tries_rec( // Note that differences in a node's `value` will be picked up by a hash // mismatch. + let mut current_depth_node_diff_state: DepthNodeDiffState = Default::default(); if (a_type, a_key_piece) != (b_type, b_key_piece) { - depth_state.try_update_longest_divergence_key_node(state); + current_depth_node_diff_state.try_update_longest_divergence_key_node(state); + depth_states.push(current_depth_node_diff_state); DiffDetectionState::NodeTypesDiffer } else { match (&state.a.node, &state.b.node) { (Node::Empty, Node::Empty) => DiffDetectionState::NoDiffDetected, (Node::Hash(a_hash), Node::Hash(b_hash)) => { - create_diff_detection_state_based_from_hashes( + match create_diff_detection_state_based_from_hashes( a_hash, b_hash, &state.new_from_parent(state.a, state.b, &Nibbles::default()), - depth_state, - ) + &mut current_depth_node_diff_state, + ) { + DiffDetectionState::NoDiffDetected => DiffDetectionState::NoDiffDetected, + result @ (DiffDetectionState::HashDiffDetected + | DiffDetectionState::NodeTypesDiffer) => { + depth_states.push(current_depth_node_diff_state); + result + } + } } ( Node::Branch { @@ -346,13 +353,13 @@ fn find_latest_diff_point_between_tries_rec( let mut most_significant_diff_found = DiffDetectionState::NoDiffDetected; for i in 0..16_usize { - let res = find_latest_diff_point_between_tries_rec( + let res = find_all_diff_points_between_tries_rec( &state.new_from_parent( &a_children[i], &b_children[i], &Nibbles::from_nibble(i as u8), ), - depth_state, + depth_states, ); most_significant_diff_found = most_significant_diff_found.pick_most_significant_state(&res); @@ -364,8 +371,18 @@ fn find_latest_diff_point_between_tries_rec( ) { most_significant_diff_found } else { - // Also run a hash check if we haven't picked anything up yet. - create_diff_detection_state_based_from_hash_and_gen_hashes(state, depth_state) + // Also run a hash check if we haven't picked anything up + match create_diff_detection_state_based_from_hash_and_gen_hashes( + state, + &mut current_depth_node_diff_state, + ) { + DiffDetectionState::NoDiffDetected => DiffDetectionState::NoDiffDetected, + result @ (DiffDetectionState::HashDiffDetected + | DiffDetectionState::NodeTypesDiffer) => { + depth_states.push(current_depth_node_diff_state); + result + } + } } } ( @@ -377,12 +394,22 @@ fn find_latest_diff_point_between_tries_rec( nibbles: _b_nibs, child: b_child, }, - ) => find_latest_diff_point_between_tries_rec( + ) => find_all_diff_points_between_tries_rec( &state.new_from_parent(a_child, b_child, a_nibs), - depth_state, + depth_states, ), (Node::Leaf { .. }, Node::Leaf { .. }) => { - create_diff_detection_state_based_from_hash_and_gen_hashes(state, depth_state) + match create_diff_detection_state_based_from_hash_and_gen_hashes( + state, + &mut current_depth_node_diff_state, + ) { + DiffDetectionState::NoDiffDetected => DiffDetectionState::NoDiffDetected, + result @ (DiffDetectionState::HashDiffDetected + | DiffDetectionState::NodeTypesDiffer) => { + depth_states.push(current_depth_node_diff_state); + result + } + } } _ => unreachable!(), } @@ -425,17 +452,22 @@ const fn get_value_from_node(n: &Node) -> Option<&Vec> { #[cfg(test)] mod tests { - use super::{create_diff_between_tries, DiffPoint, NodeInfo, TriePath}; + use std::str::FromStr; + + use ethereum_types::BigEndianHash; + use rlp_derive::{RlpDecodable, RlpEncodable}; + + use super::create_full_diff_between_tries; use crate::{ + debug_tools::diff::{DiffPoint, NodeInfo}, nibbles::Nibbles, partial_trie::{HashedPartialTrie, PartialTrie}, trie_ops::TrieOpResult, - utils::TrieNodeType, + utils::{TrieNodeType, TriePath}, }; #[test] fn depth_single_node_hash_diffs_work() -> TrieOpResult<()> { - // TODO: Reduce duplication once we identify common structures across tests... let mut a = HashedPartialTrie::default(); a.insert(0x1234, vec![0])?; let a_hash = a.hash(); @@ -444,7 +476,7 @@ mod tests { b.insert(0x1234, vec![1])?; let b_hash = b.hash(); - let diff = create_diff_between_tries(&a, &b); + let diff = create_full_diff_between_tries(&a, &b); let expected_a = NodeInfo { key: 0x1234.into(), @@ -468,18 +500,90 @@ mod tests { b_info: expected_b, }; - assert_eq!(diff.latest_diff_res, Some(expected)); - + assert_eq!(diff.diff_points[0], expected); Ok(()) } - // TODO: Will finish these tests later (low-priority). #[test] - #[ignore] - fn depth_single_node_node_diffs_work() { - todo!() + fn depth_multi_node_diff_works() -> std::result::Result<(), Box> { + use ethereum_types::{H256, U256}; + use keccak_hash::keccak; + #[derive( + RlpEncodable, RlpDecodable, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, + )] + pub struct TestAccountRlp { + pub nonce: U256, + pub balance: U256, + pub storage_root: H256, + pub code_hash: H256, + } + + let mut data = vec![ + ( + keccak(hex::decode("f0d4c12a5768d806021f80a262b4d39d26c58b8d").unwrap()), + TestAccountRlp { + nonce: U256::from(1), + balance: U256::from(2), + storage_root: H256::from_uint(&1312378.into()), + code_hash: H256::from_uint(&943221.into()), + }, + ), + ( + keccak(hex::decode("95222290dd7278aa3ddd389cc1e1d165cc4bafe5").unwrap()), + TestAccountRlp { + nonce: U256::from(2), + balance: U256::from(3), + storage_root: H256::from_uint(&1123178.into()), + code_hash: H256::from_uint(&8133221.into()), + }, + ), + ( + keccak(hex::decode("43682bcf1ce452a70b72c109551084076c6377e0").unwrap()), + TestAccountRlp { + nonce: U256::from(100), + balance: U256::from(101), + storage_root: H256::from_uint(&12345678.into()), + code_hash: H256::from_uint(&94321.into()), + }, + ), + ( + keccak(hex::decode("97a9a15168c22b3c137e6381037e1499c8ad0978").unwrap()), + TestAccountRlp { + nonce: U256::from(3000), + balance: U256::from(3002), + storage_root: H256::from_uint(&123456781.into()), + code_hash: H256::from_uint(&943214141.into()), + }, + ), + ]; + + let create_trie_with_data = |trie: &Vec<(H256, TestAccountRlp)>| -> Result> { + let mut tr = HashedPartialTrie::default(); + tr.insert::(Nibbles::from_str(&hex::encode(trie[0].0.as_bytes()))?, rlp::encode(&trie[0].1).as_ref())?; + tr.insert::(Nibbles::from_str(&hex::encode(trie[1].0.as_bytes()))?, rlp::encode(&trie[1].1).as_ref())?; + tr.insert::(Nibbles::from_str(&hex::encode(trie[2].0.as_bytes()))?, rlp::encode(&trie[2].1).as_ref())?; + tr.insert::(Nibbles::from_str(&hex::encode(trie[3].0.as_bytes()))?, rlp::encode(&trie[3].1).as_ref())?; + Ok(tr) + }; + + let a = create_trie_with_data(&data)?; + + // Change data on multiple accounts + data[1].1.balance += U256::from(1); + data[3].1.nonce += U256::from(2); + data[3].1.storage_root = H256::from_uint(&4445556.into()); + let b = create_trie_with_data(&data)?; + + let diff = create_full_diff_between_tries(&a, &b); + + assert_eq!(diff.diff_points.len(), 2); + assert_eq!(&diff.diff_points[0].key.to_string(), "0x3"); + assert_eq!(&diff.diff_points[1].key.to_string(), "0x55"); + + Ok(()) } + // TODO: Will finish these tests later (low-priority). #[test] #[ignore] fn depth_multi_node_single_node_hash_diffs_work() { diff --git a/zero/src/trie_diff/mod.rs b/zero/src/trie_diff/mod.rs index f659dc86e..088e2d835 100644 --- a/zero/src/trie_diff/mod.rs +++ b/zero/src/trie_diff/mod.rs @@ -1,6 +1,6 @@ use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::DebugOutputTries; -use mpt_trie::debug_tools::diff::{create_diff_between_tries, DiffPoint}; +use mpt_trie::debug_tools::diff::{create_full_diff_between_tries, DiffPoint}; use mpt_trie::utils::TrieNodeType; use tracing::info; @@ -24,13 +24,17 @@ pub fn compare_tries( V: rlp::Decodable + std::fmt::Debug, >( trie_name: &str, - diff_point: Option, + diff_point: Vec, block_number: u64, batch_index: usize, decode_key: bool, decode_data: bool, ) -> anyhow::Result<()> { - if let Some(ref trie_diff_point) = diff_point { + if diff_point.is_empty() { + info!("{trie_name} for block {block_number} batch {batch_index} matches."); + return Ok(()); + } + for (index, trie_diff_point) in diff_point.into_iter().enumerate() { if trie_diff_point.a_info.node_type == TrieNodeType::Leaf { if let Some(ref td_value) = trie_diff_point.a_info.value { let td_key_str: &str = if decode_key { @@ -73,19 +77,17 @@ pub fn compare_tries( } info!( - "{trie_name} block {block_number} batch {batch_index} diff: {:#?}", + "Diff {index} {trie_name} block {block_number} batch {batch_index} diff:\n{}\n", trie_diff_point ); - } else { - info!("{trie_name} for block {block_number} batch {batch_index} matches."); } Ok(()) } - let state_trie_diff = create_diff_between_tries(&left.state_trie, &right.state_trie); + let state_trie_diff = create_full_diff_between_tries(&left.state_trie, &right.state_trie); compare_tries_and_output_results::( "state trie", - state_trie_diff.latest_diff_res, + state_trie_diff.diff_points, block_number, batch_index, false, @@ -93,20 +95,20 @@ pub fn compare_tries( )?; let transaction_trie_diff = - create_diff_between_tries(&left.transaction_trie, &right.transaction_trie); + create_full_diff_between_tries(&left.transaction_trie, &right.transaction_trie); compare_tries_and_output_results::( "transaction trie", - transaction_trie_diff.latest_diff_res, + transaction_trie_diff.diff_points, block_number, batch_index, false, true, )?; - let receipt_trie_diff = create_diff_between_tries(&left.receipt_trie, &right.receipt_trie); + let receipt_trie_diff = create_full_diff_between_tries(&left.receipt_trie, &right.receipt_trie); compare_tries_and_output_results::( "receipt trie", - receipt_trie_diff.latest_diff_res, + receipt_trie_diff.diff_points, block_number, batch_index, true, From 4257345e2649d2817f280bcf71f1e23f8425ceab Mon Sep 17 00:00:00 2001 From: Marko Atanasievski Date: Wed, 25 Sep 2024 14:22:40 +0200 Subject: [PATCH 2/4] fix: improvements --- evm_arithmetization/src/generation/mod.rs | 6 +- mpt_trie/src/debug_tools/diff.rs | 260 ++++++++++++++++++---- zero/src/bin/trie_diff.rs | 8 + 3 files changed, 224 insertions(+), 50 deletions(-) diff --git a/evm_arithmetization/src/generation/mod.rs b/evm_arithmetization/src/generation/mod.rs index bcec4083d..19ea55039 100644 --- a/evm_arithmetization/src/generation/mod.rs +++ b/evm_arithmetization/src/generation/mod.rs @@ -520,13 +520,11 @@ pub fn generate_traces, const D: usize>( let registers_after: RegistersData = RegistersData::from(*registers_after); apply_metadata_and_tries_memops(&mut state, inputs, ®isters_before, ®isters_after); - let cpu_res = timed!( + timed!( timing, "simulate CPU", simulate_cpu(&mut state, *max_cpu_len_log) - ); - - cpu_res?; + )?; let trace_lengths = state.traces.get_lengths(); diff --git a/mpt_trie/src/debug_tools/diff.rs b/mpt_trie/src/debug_tools/diff.rs index c08cb80d1..ed06841b5 100644 --- a/mpt_trie/src/debug_tools/diff.rs +++ b/mpt_trie/src/debug_tools/diff.rs @@ -46,8 +46,7 @@ fn get_key_piece_from_node(n: &Node) -> Nibbles { } #[derive(Clone, Debug, Eq, Hash, PartialEq)] -/// The difference between two Tries, represented as the highest -/// array of `DiffPoint`s. +/// The difference between two Tries, represented as the array of `DiffPoint`s. pub struct TrieDiff { /// Diff points between the two tries. pub diff_points: Vec, @@ -147,7 +146,7 @@ pub struct NodeInfo { impl Display for NodeInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "NodeInfo {{ Key: 0x{:x}, ", self.key)?; + write!(f, "NodeInfo {{ Key: {:x}, ", self.key)?; match &self.value { Some(v) => write!(f, "Value: 0x{}, ", hex::encode(v))?, @@ -173,8 +172,7 @@ impl NodeInfo { } } -/// Create a diff between two tries. Will perform both types of diff searches -/// (top-down & bottom-up). It will not stop on first difference. +/// Create a diff between two tries. It will try to find all the differences. pub fn create_full_diff_between_tries(a: &HashedPartialTrie, b: &HashedPartialTrie) -> TrieDiff { TrieDiff { diff_points: find_all_diff_points_between_tries(a, b), @@ -458,6 +456,8 @@ mod tests { use rlp_derive::{RlpDecodable, RlpEncodable}; use super::create_full_diff_between_tries; + use crate::trie_ops::ValOrHash; + use crate::utils::TryFromIterator; use crate::{ debug_tools::diff::{DiffPoint, NodeInfo}, nibbles::Nibbles, @@ -466,10 +466,17 @@ mod tests { utils::{TrieNodeType, TriePath}, }; + fn create_trie(data: impl IntoIterator) -> TrieOpResult + where + K: Into, + V: Into, + { + HashedPartialTrie::try_from_iter(data) + } + #[test] - fn depth_single_node_hash_diffs_work() -> TrieOpResult<()> { - let mut a = HashedPartialTrie::default(); - a.insert(0x1234, vec![0])?; + fn single_node_diff_works() -> Result<(), Box> { + let a = create_trie(vec![(0x1234, vec![0])])?; let a_hash = a.hash(); let mut b = a.clone(); @@ -505,7 +512,205 @@ mod tests { } #[test] - fn depth_multi_node_diff_works() -> std::result::Result<(), Box> { + fn multi_node_single_diff_works() -> Result<(), Box> { + let a = create_trie(vec![ + (Nibbles::from_str("0x1111")?, 0x01u8), + (Nibbles::from_str("0x1112")?, 0x02u8), + (Nibbles::from_str("0x3333")?, 0x03u8), + (Nibbles::from_str("0x4444")?, 0x04u8), + ])?; + + let b = create_trie(vec![ + (Nibbles::from_str("0x1111")?, 0x01u8), + (Nibbles::from_str("0x1112")?, 0x03u8), + (Nibbles::from_str("0x3333")?, 0x03u8), + (Nibbles::from_str("0x4444")?, 0x04u8), + ])?; + + let diff = create_full_diff_between_tries(&a, &b); + + assert_eq!(diff.diff_points.len(), 1); + assert_eq!(diff.diff_points[0].a_info.node_type, TrieNodeType::Leaf); + assert_eq!(diff.diff_points[0].a_info.key, Nibbles::from_str("0x1112")?); + assert_eq!(diff.diff_points[0].a_info.value, Some(vec![0x02u8])); + assert_eq!(diff.diff_points[0].b_info.node_type, TrieNodeType::Leaf); + assert_eq!(diff.diff_points[0].b_info.key, Nibbles::from_str("0x1112")?); + assert_eq!(diff.diff_points[0].b_info.value, Some(vec![0x03u8])); + + Ok(()) + } + + #[test] + fn multi_node_single_diff_works_2() -> Result<(), Box> { + let a = create_trie(vec![ + (Nibbles::from_str("0x1111")?, 0x01u8), + (Nibbles::from_str("0x1122")?, 0x02u8), + (Nibbles::from_str("0x3333")?, 0x03u8), + (Nibbles::from_str("0x4444")?, 0x04u8), + ])?; + + let mut b = a.clone(); + b.insert(Nibbles::from_str("0x3334")?, 0x05u8)?; + + let diff = create_full_diff_between_tries(&a, &b); + + assert_eq!(diff.diff_points.len(), 1); + assert_eq!(diff.diff_points[0].a_info.node_type, TrieNodeType::Leaf); + assert_eq!(diff.diff_points[0].a_info.key, Nibbles::from_str("0x3333")?); + assert_eq!( + diff.diff_points[0].b_info.node_type, + TrieNodeType::Extension + ); + assert_eq!(diff.diff_points[0].b_info.key, Nibbles::from_str("0x333")?); + + Ok(()) + } + + #[test] + fn multi_node_single_diff_works_3() -> Result<(), Box> { + let a = create_trie(vec![ + (Nibbles::from_str("0x1111")?, 0x01u8), + (Nibbles::from_str("0x1122")?, 0x02u8), + ])?; + + let b = create_trie(vec![ + (Nibbles::from_str("0x3333")?, 0x03u8), + (Nibbles::from_str("0x4444")?, 0x04u8), + ])?; + + let diff = create_full_diff_between_tries(&a, &b); + + println!("Diff is: {}", diff); + + assert_eq!(diff.diff_points.len(), 1); + assert_eq!( + diff.diff_points[0].a_info.node_type, + TrieNodeType::Extension + ); + assert_eq!(diff.diff_points[0].a_info.key, Nibbles::from_str("0x11")?); + assert_eq!(diff.diff_points[0].b_info.node_type, TrieNodeType::Branch); + assert_eq!(diff.diff_points[0].b_info.key, Nibbles::from_str("")?); + + Ok(()) + } + + #[test] + fn multi_node_multi_diff_works() -> Result<(), Box> { + let a = create_trie(vec![ + (Nibbles::from_str("0x1111")?, 0x01u8), + (Nibbles::from_str("0x1122")?, 0x02u8), + (Nibbles::from_str("0x3333")?, 0x03u8), + (Nibbles::from_str("0x4444")?, 0x04u8), + ])?; + + let mut b = a.clone(); + b.insert(Nibbles::from_str("0x1113")?, 0x05u8)?; + b.insert(Nibbles::from_str("0x3334")?, 0x06u8)?; + + let diff = create_full_diff_between_tries(&a, &b); + println!("Diff is: {}", diff); + assert_eq!(diff.diff_points.len(), 2); + assert_eq!(diff.diff_points[0].a_info.node_type, TrieNodeType::Leaf); + assert_eq!(diff.diff_points[0].a_info.key, Nibbles::from_str("0x1111")?); + assert_eq!(diff.diff_points[0].b_info.node_type, TrieNodeType::Branch); + assert_eq!(diff.diff_points[0].b_info.key, Nibbles::from_str("0x111")?); + + assert_eq!(diff.diff_points[1].a_info.node_type, TrieNodeType::Leaf); + assert_eq!(diff.diff_points[1].a_info.key, Nibbles::from_str("0x3333")?); + assert_eq!( + diff.diff_points[1].b_info.node_type, + TrieNodeType::Extension + ); + assert_eq!(diff.diff_points[1].b_info.key, Nibbles::from_str("0x333")?); + + Ok(()) + } + + #[test] + fn multi_node_multi_diff_works_2() -> Result<(), Box> { + let a = create_trie(vec![ + (Nibbles::from_str("0x1111")?, 0x01u8), + (Nibbles::from_str("0x1122")?, 0x02u8), + (Nibbles::from_str("0x3333")?, 0x03u8), + (Nibbles::from_str("0x4444")?, 0x04u8), + ])?; + + let b = create_trie(vec![ + (Nibbles::from_str("0x1112")?, 0x01u8), + (Nibbles::from_str("0x1123")?, 0x02u8), + (Nibbles::from_str("0x3334")?, 0x03u8), + (Nibbles::from_str("0x4445")?, 0x04u8), + ])?; + + let diff = create_full_diff_between_tries(&a, &b); + println!("Diff is: {}", diff); + assert_eq!(diff.diff_points.len(), 4); + assert_eq!(diff.diff_points[0].a_info.key, Nibbles::from_str("0x1111")?); + assert_eq!(diff.diff_points[0].b_info.key, Nibbles::from_str("0x1112")?); + assert_eq!(diff.diff_points[1].a_info.key, Nibbles::from_str("0x1122")?); + assert_eq!(diff.diff_points[1].b_info.key, Nibbles::from_str("0x1123")?); + assert_eq!(diff.diff_points[2].a_info.key, Nibbles::from_str("0x3333")?); + assert_eq!(diff.diff_points[2].b_info.key, Nibbles::from_str("0x3334")?); + assert_eq!(diff.diff_points[3].a_info.key, Nibbles::from_str("0x4444")?); + assert_eq!(diff.diff_points[3].b_info.key, Nibbles::from_str("0x4445")?); + + Ok(()) + } + + #[test] + fn multi_node_multi_diff_works_3() -> Result<(), Box> { + let a = create_trie(vec![ + (Nibbles::from_str("0x1111")?, 0x01u8), + (Nibbles::from_str("0x1112")?, 0x02u8), + (Nibbles::from_str("0x1113")?, 0x03u8), + (Nibbles::from_str("0x1114")?, 0x04u8), + (Nibbles::from_str("0x2221")?, 0x06u8), + (Nibbles::from_str("0x2222")?, 0x07u8), + (Nibbles::from_str("0x2223")?, 0x08u8), + (Nibbles::from_str("0x2224")?, 0x09u8), + ])?; + + let b = create_trie(vec![ + (Nibbles::from_str("0x1114")?, 0x04u8), + (Nibbles::from_str("0x1115")?, 0x06u8), + (Nibbles::from_str("0x1116")?, 0x07u8), + (Nibbles::from_str("0x1117")?, 0x08u8), + (Nibbles::from_str("0x2224")?, 0x09u8), + (Nibbles::from_str("0x2225")?, 0x07u8), + (Nibbles::from_str("0x2226")?, 0x08u8), + (Nibbles::from_str("0x2227")?, 0x09u8), + ])?; + + let diff = create_full_diff_between_tries(&a, &b); + assert_eq!(diff.diff_points.len(), 12); + + assert_eq!(diff.diff_points[0].a_info.key, Nibbles::from_str("0x1111")?); + assert_eq!(diff.diff_points[0].a_info.node_type, TrieNodeType::Leaf); + assert_eq!(diff.diff_points[0].b_info.key, Nibbles::from_str("0x1111")?); + assert_eq!(diff.diff_points[0].b_info.node_type, TrieNodeType::Empty); + + assert_eq!(diff.diff_points[4].a_info.key, Nibbles::from_str("0x1116")?); + assert_eq!(diff.diff_points[4].a_info.node_type, TrieNodeType::Empty); + assert_eq!(diff.diff_points[4].b_info.key, Nibbles::from_str("0x1116")?); + assert_eq!(diff.diff_points[4].b_info.node_type, TrieNodeType::Leaf); + + assert_eq!( + diff.diff_points[11].a_info.key, + Nibbles::from_str("0x2227")? + ); + assert_eq!(diff.diff_points[11].a_info.node_type, TrieNodeType::Empty); + assert_eq!( + diff.diff_points[11].b_info.key, + Nibbles::from_str("0x2227")? + ); + assert_eq!(diff.diff_points[11].b_info.node_type, TrieNodeType::Leaf); + + Ok(()) + } + + #[test] + /// Do one real world test where we change the values of the accounts. + fn multi_node_multi_diff_works_accounts() -> Result<(), Box> { use ethereum_types::{H256, U256}; use keccak_hash::keccak; #[derive( @@ -582,41 +787,4 @@ mod tests { Ok(()) } - - // TODO: Will finish these tests later (low-priority). - #[test] - #[ignore] - fn depth_multi_node_single_node_hash_diffs_work() { - todo!() - } - - #[test] - #[ignore] - fn depth_multi_node_single_node_node_diffs_work() { - todo!() - } - - #[test] - #[ignore] - fn depth_massive_single_node_diff_tests() { - todo!() - } - - #[test] - #[ignore] - fn depth_multi_node_multi_node_hash_diffs_work() { - todo!() - } - - #[test] - #[ignore] - fn depth_multi_node_multi_node_node_diffs_work() { - todo!() - } - - #[test] - #[ignore] - fn depth_massive_multi_node_diff_tests() { - todo!() - } } diff --git a/zero/src/bin/trie_diff.rs b/zero/src/bin/trie_diff.rs index 312feceb1..4c00d2ca3 100644 --- a/zero/src/bin/trie_diff.rs +++ b/zero/src/bin/trie_diff.rs @@ -28,9 +28,17 @@ use tracing::{error, info}; use zero::ops::register; use zero::prover::{cli::CliProverConfig, BlockProverInput, ProverConfig}; +/// This binary is a debugging tool used to compare +/// the trace decoder output tries and the post kernel execution tries (state, +/// transaction and receipt). +/// +/// Usage: +/// +/// `trie_diff < ./witness_json_input.json` #[derive(Parser)] #[command(version = zero::version(), propagate_version = true)] pub(crate) struct Cli { + /// Prover configuration #[clap(flatten)] pub(crate) prover_config: CliProverConfig, From 40ebddda4777e43304e3e2e167a0db3893a92f28 Mon Sep 17 00:00:00 2001 From: Marko Atanasievski Date: Tue, 1 Oct 2024 13:38:26 +0200 Subject: [PATCH 3/4] fix: comment --- mpt_trie/src/debug_tools/diff.rs | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/mpt_trie/src/debug_tools/diff.rs b/mpt_trie/src/debug_tools/diff.rs index ed06841b5..6332e44d0 100644 --- a/mpt_trie/src/debug_tools/diff.rs +++ b/mpt_trie/src/debug_tools/diff.rs @@ -1,28 +1,10 @@ //! Diffing tools to compare two tries against each other. Useful when you want -//! to find where the tries diverge from one each other. +//! to find where the tries diverge from one other. //! -//! There are a few considerations when implementing the logic to create a trie -//! diff: -//! - What should be reported when the trie node structures diverge (eg. two -//! different node types proceeding a given common node)? -//! - If there are multiple structural differences, how do we discover the -//! smallest difference to report back? -//! -//! If the node types between the tries (structure) are identical but some -//! values are different, then these types of diffs are easy to detect and -//! report the lowest difference. Structural differences are more challenging -//! and a bit hard to report well. There are two approaches (only one currently -//! is implemented) in how to detect structural differences: -//! - Top-down search -//! - Bottom-up search -//! -//! These two searches are somewhat self-explanatory: -//! - Top-down will find the highest point of a structural divergence and report -//! it. If there are multiple divergences, then only the one that is the -//! highest in the trie will be reported. -//! - Bottom-up is a lot more complex to implement, but will attempt to find the -//! smallest structural trie difference between the trie. If there are -//! multiple differences, then this will likely be what you want to use. +//! Here a top-down approach is used, following the trie structure from the root to +//! the leaves. The diffing is done by comparing the nodes at each level. Diff functions +//! will not return on the first difference, but will try to find and collect all diff +//! points. use std::fmt::{self, Debug}; use std::{fmt::Display, ops::Deref}; From c5b0bd7ce4c7103b94cb332362f449c6df3b9f51 Mon Sep 17 00:00:00 2001 From: Marko Atanasievski Date: Tue, 1 Oct 2024 15:10:03 +0200 Subject: [PATCH 4/4] fix: lint --- mpt_trie/src/debug_tools/diff.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mpt_trie/src/debug_tools/diff.rs b/mpt_trie/src/debug_tools/diff.rs index 6332e44d0..eb7c923ab 100644 --- a/mpt_trie/src/debug_tools/diff.rs +++ b/mpt_trie/src/debug_tools/diff.rs @@ -1,10 +1,10 @@ //! Diffing tools to compare two tries against each other. Useful when you want //! to find where the tries diverge from one other. //! -//! Here a top-down approach is used, following the trie structure from the root to -//! the leaves. The diffing is done by comparing the nodes at each level. Diff functions -//! will not return on the first difference, but will try to find and collect all diff -//! points. +//! Here a top-down approach is used, following the trie structure from the root +//! to the leaves. The diffing is done by comparing the nodes at each level. +//! Diff functions will not return on the first difference, but will try to find +//! and collect all diff points. use std::fmt::{self, Debug}; use std::{fmt::Display, ops::Deref};