Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Increased the public interface for trie_tools #123

Merged
merged 1 commit into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 63 additions & 30 deletions trace_decoder/src/compact/compact_prestate_processing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ use crate::{
types::{CodeHash, HashedAccountAddr, TrieRootHash},
};

/// Result alias for any error that can occur when processing encoded compact
/// prestate.
pub type CompactParsingResult<T> = Result<T, CompactParsingError>;

type BranchMask = u32;
Expand All @@ -46,20 +48,29 @@ const MAX_WITNESS_ENTRIES_NEEDED_TO_MATCH_A_RULE: usize = 3;
const BRANCH_MAX_CHILDREN: usize = 16;
const CURSOR_ERROR_BYTES_MAX_LEN: usize = 10;

/// An error from processing Erigon's compact witness format.
#[derive(Debug, Error)]
pub enum CompactParsingError {
/// The header in the compact payload was missing. This is just a single
/// byte that is used for versioning.
#[error("Missing header")]
MissingHeader,

/// Encountered a byte representing an opcode that does not represent any
/// known opcode
#[error("Invalid opcode operator (\"{0:x}\")")]
InvalidOperator(u8),
InvalidOpcode(u8),

/// Encountered the end of the byte stream when we were still expecting more
/// data.
#[error("Reached the end of the byte stream when we still expected more data")]
UnexpectedEndOfStream,

/// Failed to decode a byte vector from CBOR.
#[error("Unable to parse an expected byte vector (field name: {0}) (error: {1}). Cursor error info: {2}")]
InvalidByteVector(&'static str, String, CursorBytesErrorInfo),

/// Failed to decode a given type from CBOR.
#[error(
"Unable to parse the type \"{0}\" (field name: {1}) from bytes {2}. Cursor error info: {3} (err: {4})"
)]
Expand All @@ -71,32 +82,48 @@ pub enum CompactParsingError {
String,
),

/// Encountered a sequence of instructions of nodes that should not be able
/// to occur.
#[error("Invalid block witness entries: {0:?}")]
InvalidWitnessFormat(Vec<WitnessEntry>),

/// Multiple entries were remaining after we were unable to apply any more
/// rules. There should always only be one remaining entry after we can not
/// apply any more rules.
#[error("There were multiple entries remaining after the compact block witness was processed (Remaining entries: {0:#?})")]
NonSingleEntryAfterProcessing(WitnessEntries),

/// A branch was found that had an unexpected number of child nodes trailing
/// it than expected.
#[error("Branch mask {0:#b} stated there should be {1} preceding nodes but instead found {2} (nodes: {3:?})")]
IncorrectNumberOfNodesPrecedingBranch(BranchMask, usize, usize, Vec<WitnessEntry>),

/// Found a branch that had claimed to have `n` children but instead had a
/// different amount.
#[error(
"Expected a branch to have {0} preceding nodes but only had {1} (mask: {2}, nodes: {3:?})"
)]
MissingExpectedNodesPrecedingBranch(usize, usize, BranchMask, Vec<WitnessEntry>),

/// Expected a preceding node to be of a given type but instead found one of
/// a different type.
#[error("Expected the entry preceding {0} positions behind a {1} entry to be a node of type but instead found a {2} node. (nodes: {3:?})")]
UnexpectedPrecedingNodeFoundWhenProcessingRule(usize, &'static str, String, Vec<WitnessEntry>),

/// Expected a compact node type that should not be present in the given
/// type of trie.
#[error("Found an unexpected compact node type ({0:?}) during processing compact into a `mpt_trie` {1} partial trie.")]
UnexpectedNodeForTrieType(UnexpectedCompactNodeType, TrieType),

#[error("Expected the entry preceding {0} positions behind a {1} entry to be a node but instead found a {2}. (nodes: {3:?})")]
PrecedingNonNodeEntryFoundWhenProcessingRule(usize, &'static str, String, Vec<WitnessEntry>),

// TODO: No constructors for this, but I think there should be one in
// [`key_bytes_to_nibbles`]...
/// Error when constructing a key from bytes.
#[error("Unable to create key nibbles from bytes {0}")]
KeyError(#[from] FromHexPrefixError),
}

#[derive(Debug)]
pub struct CursorBytesErrorInfo {
pub(crate) struct CursorBytesErrorInfo {
error_start_pos: usize,
bad_bytes_hex: String,
}
Expand Down Expand Up @@ -141,7 +168,7 @@ enum Opcode {
}

#[derive(Clone, Debug, EnumAsInner)]
pub enum WitnessEntry {
pub(crate) enum WitnessEntry {
Instruction(Instruction),
Node(NodeEntry),
}
Expand Down Expand Up @@ -290,11 +317,11 @@ impl Header {
}
}

#[derive(Debug)]
pub(crate) struct WitnessOutput {
pub(crate) tries: PartialTriePreImages,
pub(crate) code: Option<HashMap<CodeHash, Vec<u8>>>,
}
// #[derive(Debug)]
BGluth marked this conversation as resolved.
Show resolved Hide resolved
// pub struct CompactWitnessDecodingOutput {
// pub tries: PartialTriePreImages,
// pub code: Option<HashMap<CodeHash, Vec<u8>>>,
// }

#[derive(Debug)]
struct ParserState {
Expand Down Expand Up @@ -325,7 +352,7 @@ impl ParserState {
Ok((header, p_state))
}

fn parse(mut self) -> CompactParsingResult<WitnessOutput> {
fn parse(mut self) -> CompactParsingResult<StateTrieExtractionOutput> {
let mut entry_buf = Vec::new();

loop {
Expand All @@ -346,15 +373,7 @@ impl ParserState {
)),
}?;

let tries = PartialTriePreImages {
state: res.trie,
storage: res.storage_tries,
};

// Replace with a none if there are no entries.
let code = (!res.code.is_empty()).then_some(res.code);

Ok(WitnessOutput { tries, code })
Ok(res)
}

fn apply_rules_to_witness_entries(
Expand Down Expand Up @@ -484,7 +503,7 @@ impl ParserState {
let n_entries_behind_cursor =
number_available_preceding_elems - curr_traverser_node_idx;

CompactParsingError::PrecedingNonNodeEntryFoundWhenProcessingRule(
CompactParsingError::UnexpectedPrecedingNodeFoundWhenProcessingRule(
n_entries_behind_cursor,
"Branch",
entry_to_check.to_string(),
Expand Down Expand Up @@ -722,7 +741,7 @@ impl<C: CompactCursor> WitnessBytes<C> {
let opcode_byte = self.byte_cursor.read_byte()?;

let opcode =
Opcode::n(opcode_byte).ok_or(CompactParsingError::InvalidOperator(opcode_byte))?;
Opcode::n(opcode_byte).ok_or(CompactParsingError::InvalidOpcode(opcode_byte))?;

trace!("Processed \"{:?}\" opcode", opcode);

Expand Down Expand Up @@ -1219,24 +1238,38 @@ enum TraverserDirection {

#[derive(Debug, Default)]
pub(crate) struct PartialTriePreImages {
pub(crate) state: HashedPartialTrie,
pub(crate) storage: HashMap<HashedAccountAddr, HashedPartialTrie>,
pub state: HashedPartialTrie,
pub storage: HashMap<HashedAccountAddr, HashedPartialTrie>,
}

/// The output we get from processing prestate compact into the trie format of
/// `mpt_trie`.
///
/// Note that this format contains storage tries embedded within the state trie,
/// so there may be multiple tries inside this output. Also note that the
/// bytecode (instead of just the code hash) may be embedded directly in this
/// format.
#[derive(Debug)]
pub(crate) struct ProcessedCompactOutput {
pub(crate) header: Header,
pub(crate) witness_out: WitnessOutput,
pub struct ProcessedCompactOutput {
/// The header of the compact.
pub header: Header,

/// The actual processed `mpt_trie` tries and additional code hash mappings
/// from the compact.
pub witness_out: StateTrieExtractionOutput,
}

pub(crate) fn process_compact_prestate(
/// Processes the compact prestate into the trie format of `mpt_trie`.
pub fn process_compact_prestate(
state: TrieCompact,
) -> CompactParsingResult<ProcessedCompactOutput> {
process_compact_prestate_common(state, ParserState::create_and_extract_header)
}

/// Processes the compact prestate into the trie format of `mpt_trie`. Also
/// enables heavy debug traces during processing.
// TODO: Move behind a feature flag...
pub(crate) fn process_compact_prestate_debug(
pub fn process_compact_prestate_debug(
state: TrieCompact,
) -> CompactParsingResult<ProcessedCompactOutput> {
process_compact_prestate_common(state, ParserState::create_and_extract_header_debug)
Expand Down
18 changes: 12 additions & 6 deletions trace_decoder/src/compact/compact_to_partial_trie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,16 @@ pub(super) enum UnexpectedCompactNodeType {

/// Output from constructing a state trie from compact.
#[derive(Debug, Default)]
pub(super) struct StateTrieExtractionOutput {
pub(super) trie: HashedPartialTrie,
pub(super) code: HashMap<CodeHash, Vec<u8>>,
pub(super) storage_tries: HashMap<HashedAccountAddr, HashedPartialTrie>,
pub struct StateTrieExtractionOutput {
/// The state trie of the compact.
pub state_trie: HashedPartialTrie,

/// Any embedded contract bytecode that appears in the compact will be
/// present here.
pub code: HashMap<CodeHash, Vec<u8>>,

/// All storage tries present in the compact.
pub storage_tries: HashMap<HashedAccountAddr, HashedPartialTrie>,
}

impl CompactToPartialTrieExtractionOutput for StateTrieExtractionOutput {
Expand All @@ -125,7 +131,7 @@ impl CompactToPartialTrieExtractionOutput for StateTrieExtractionOutput {
leaf_node_data: &LeafNodeData,
) -> CompactParsingResult<()> {
process_leaf_common(
&mut self.trie,
&mut self.state_trie,
curr_key,
leaf_key,
leaf_node_data,
Expand All @@ -140,7 +146,7 @@ impl CompactToPartialTrieExtractionOutput for StateTrieExtractionOutput {
)
}
fn trie(&mut self) -> &mut HashedPartialTrie {
&mut self.trie
&mut self.state_trie
}
}

Expand Down
25 changes: 14 additions & 11 deletions trace_decoder/src/compact/complex_test_payloads.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use evm_arithmetization::generation::mpt::AccountRlp;
use mpt_trie::partial_trie::PartialTrie;

use super::compact_prestate_processing::{
process_compact_prestate, process_compact_prestate_debug, CompactParsingResult,
PartialTriePreImages, ProcessedCompactOutput,
use super::{
compact_prestate_processing::{
process_compact_prestate, process_compact_prestate_debug, CompactParsingResult,
PartialTriePreImages, ProcessedCompactOutput,
},
compact_to_partial_trie::StateTrieExtractionOutput,
};
use crate::{
trace_protocol::TrieCompact,
Expand Down Expand Up @@ -56,23 +59,23 @@ impl TestProtocolInputAndRoot {
Ok(x) => x,
Err(err) => panic!("{}", err.to_string()),
};
let trie_hash = out.witness_out.tries.state.hash();
let trie_hash = out.witness_out.state_trie.hash();

print_value_and_hash_nodes_of_trie(&out.witness_out.tries.state);
print_value_and_hash_nodes_of_trie(&out.witness_out.state_trie);

for (hashed_addr, s_trie) in out.witness_out.tries.storage.iter() {
for (hashed_addr, s_trie) in out.witness_out.storage_tries.iter() {
print_value_and_hash_nodes_of_storage_trie(hashed_addr, s_trie);
}

assert!(out.header.version_is_compatible(1));
assert_eq!(trie_hash, expected_hash);

Self::assert_non_all_storage_roots_exist_in_storage_trie_map(&out.witness_out.tries);
Self::assert_non_all_storage_roots_exist_in_storage_trie_map(&out.witness_out);
}

fn assert_non_all_storage_roots_exist_in_storage_trie_map(images: &PartialTriePreImages) {
let non_empty_account_s_roots = images
.state
fn assert_non_all_storage_roots_exist_in_storage_trie_map(out: &StateTrieExtractionOutput) {
let non_empty_account_s_roots = out
.state_trie
.items()
.filter_map(|(addr, data)| {
data.as_val().map(|data| {
Expand All @@ -86,7 +89,7 @@ impl TestProtocolInputAndRoot {
.map(|(addr, _)| addr);

for account_with_non_empty_root in non_empty_account_s_roots {
assert!(images.storage.contains_key(&account_with_non_empty_root));
assert!(out.storage_tries.contains_key(&account_with_non_empty_root));
}
}
}
4 changes: 2 additions & 2 deletions trace_decoder/src/compact/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub(crate) mod compact_prestate_processing;
mod compact_to_partial_trie;
pub mod compact_prestate_processing;
pub mod compact_to_partial_trie;

#[cfg(test)]
pub(crate) mod complex_test_payloads;
22 changes: 17 additions & 5 deletions trace_decoder/src/processed_block_trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use mpt_trie::nibbles::Nibbles;
use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie};

use crate::compact::compact_prestate_processing::{
process_compact_prestate_debug, PartialTriePreImages,
process_compact_prestate_debug, PartialTriePreImages, ProcessedCompactOutput,
};
use crate::decoding::TraceParsingResult;
use crate::trace_protocol::{
Expand Down Expand Up @@ -109,6 +109,21 @@ struct ProcessedBlockTracePreImages {
extra_code_hash_mappings: Option<HashMap<CodeHash, Vec<u8>>>,
}

impl From<ProcessedCompactOutput> for ProcessedBlockTracePreImages {
fn from(v: ProcessedCompactOutput) -> Self {
let tries = PartialTriePreImages {
state: v.witness_out.state_trie,
storage: v.witness_out.storage_tries,
};

Self {
tries,
extra_code_hash_mappings: (!v.witness_out.code.is_empty())
.then_some(v.witness_out.code),
}
}
}

fn process_block_trace_trie_pre_images(
block_trace_pre_images: BlockTraceTriePreImages,
) -> ProcessedBlockTracePreImages {
Expand Down Expand Up @@ -169,10 +184,7 @@ fn process_compact_trie(trie: TrieCompact) -> ProcessedBlockTracePreImages {
// TODO: Make this into a result...
assert!(out.header.version_is_compatible(COMPATIBLE_HEADER_VERSION));

ProcessedBlockTracePreImages {
tries: out.witness_out.tries,
extra_code_hash_mappings: out.witness_out.code,
}
out.into()
}

/// Structure storing a function turning a `CodeHash` into bytes.
Expand Down