mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
feat(trie): decode proofs in multiproof task (#16098)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -10344,6 +10344,7 @@ version = "1.4.3"
|
||||
dependencies = [
|
||||
"alloy-primitives",
|
||||
"alloy-rlp",
|
||||
"alloy-trie",
|
||||
"arbitrary",
|
||||
"assert_matches",
|
||||
"auto_impl",
|
||||
|
||||
@@ -17,8 +17,8 @@ use reth_provider::{
|
||||
};
|
||||
use reth_revm::state::EvmState;
|
||||
use reth_trie::{
|
||||
prefix_set::TriePrefixSetsMut, updates::TrieUpdatesSorted, HashedPostState,
|
||||
HashedPostStateSorted, HashedStorage, MultiProof, MultiProofTargets, TrieInput,
|
||||
prefix_set::TriePrefixSetsMut, updates::TrieUpdatesSorted, DecodedMultiProof, HashedPostState,
|
||||
HashedPostStateSorted, HashedStorage, MultiProofTargets, TrieInput,
|
||||
};
|
||||
use reth_trie_parallel::{proof::ParallelProof, proof_task::ProofTaskManagerHandle};
|
||||
use std::{
|
||||
@@ -42,7 +42,7 @@ pub struct SparseTrieUpdate {
|
||||
/// The state update that was used to calculate the proof
|
||||
pub(crate) state: HashedPostState,
|
||||
/// The calculated multiproof
|
||||
pub(crate) multiproof: MultiProof,
|
||||
pub(crate) multiproof: DecodedMultiProof,
|
||||
}
|
||||
|
||||
impl SparseTrieUpdate {
|
||||
@@ -53,8 +53,8 @@ impl SparseTrieUpdate {
|
||||
|
||||
/// Construct update from multiproof.
|
||||
#[cfg(test)]
|
||||
pub(super) fn from_multiproof(multiproof: MultiProof) -> Self {
|
||||
Self { multiproof, ..Default::default() }
|
||||
pub(super) fn from_multiproof(multiproof: reth_trie::MultiProof) -> alloy_rlp::Result<Self> {
|
||||
Ok(Self { multiproof: multiproof.try_into()?, ..Default::default() })
|
||||
}
|
||||
|
||||
/// Extend update with contents of the other.
|
||||
@@ -455,7 +455,7 @@ where
|
||||
storage_proof_task_handle.clone(),
|
||||
)
|
||||
.with_branch_node_masks(true)
|
||||
.storage_proof(hashed_address, proof_targets);
|
||||
.decoded_storage_proof(hashed_address, proof_targets);
|
||||
let elapsed = start.elapsed();
|
||||
trace!(
|
||||
target: "engine::root",
|
||||
@@ -473,7 +473,10 @@ where
|
||||
sequence_number: proof_sequence_number,
|
||||
update: SparseTrieUpdate {
|
||||
state: hashed_state_update,
|
||||
multiproof: MultiProof::from_storage_proof(hashed_address, proof),
|
||||
multiproof: DecodedMultiProof::from_storage_proof(
|
||||
hashed_address,
|
||||
proof,
|
||||
),
|
||||
},
|
||||
elapsed,
|
||||
}),
|
||||
@@ -523,7 +526,7 @@ where
|
||||
storage_proof_task_handle.clone(),
|
||||
)
|
||||
.with_branch_node_masks(true)
|
||||
.multiproof(proof_targets);
|
||||
.decoded_multiproof(proof_targets);
|
||||
let elapsed = start.elapsed();
|
||||
trace!(
|
||||
target: "engine::root",
|
||||
@@ -973,7 +976,7 @@ where
|
||||
|
||||
if let Some(combined_update) = self.on_proof(
|
||||
sequence_number,
|
||||
SparseTrieUpdate { state, multiproof: MultiProof::default() },
|
||||
SparseTrieUpdate { state, multiproof: Default::default() },
|
||||
) {
|
||||
let _ = self.to_sparse_trie.send(combined_update);
|
||||
}
|
||||
@@ -1117,7 +1120,7 @@ mod tests {
|
||||
use super::*;
|
||||
use alloy_primitives::map::B256Set;
|
||||
use reth_provider::{providers::ConsistentDbView, test_utils::create_test_provider_factory};
|
||||
use reth_trie::TrieInput;
|
||||
use reth_trie::{MultiProof, TrieInput};
|
||||
use reth_trie_parallel::proof_task::{ProofTaskCtx, ProofTaskManager};
|
||||
use revm_primitives::{B256, U256};
|
||||
use std::sync::Arc;
|
||||
@@ -1169,11 +1172,11 @@ mod tests {
|
||||
let proof2 = MultiProof::default();
|
||||
sequencer.next_sequence = 2;
|
||||
|
||||
let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof1));
|
||||
let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof1).unwrap());
|
||||
assert_eq!(ready.len(), 1);
|
||||
assert!(!sequencer.has_pending());
|
||||
|
||||
let ready = sequencer.add_proof(1, SparseTrieUpdate::from_multiproof(proof2));
|
||||
let ready = sequencer.add_proof(1, SparseTrieUpdate::from_multiproof(proof2).unwrap());
|
||||
assert_eq!(ready.len(), 1);
|
||||
assert!(!sequencer.has_pending());
|
||||
}
|
||||
@@ -1186,15 +1189,15 @@ mod tests {
|
||||
let proof3 = MultiProof::default();
|
||||
sequencer.next_sequence = 3;
|
||||
|
||||
let ready = sequencer.add_proof(2, SparseTrieUpdate::from_multiproof(proof3));
|
||||
let ready = sequencer.add_proof(2, SparseTrieUpdate::from_multiproof(proof3).unwrap());
|
||||
assert_eq!(ready.len(), 0);
|
||||
assert!(sequencer.has_pending());
|
||||
|
||||
let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof1));
|
||||
let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof1).unwrap());
|
||||
assert_eq!(ready.len(), 1);
|
||||
assert!(sequencer.has_pending());
|
||||
|
||||
let ready = sequencer.add_proof(1, SparseTrieUpdate::from_multiproof(proof2));
|
||||
let ready = sequencer.add_proof(1, SparseTrieUpdate::from_multiproof(proof2).unwrap());
|
||||
assert_eq!(ready.len(), 2);
|
||||
assert!(!sequencer.has_pending());
|
||||
}
|
||||
@@ -1206,10 +1209,10 @@ mod tests {
|
||||
let proof3 = MultiProof::default();
|
||||
sequencer.next_sequence = 3;
|
||||
|
||||
let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof1));
|
||||
let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof1).unwrap());
|
||||
assert_eq!(ready.len(), 1);
|
||||
|
||||
let ready = sequencer.add_proof(2, SparseTrieUpdate::from_multiproof(proof3));
|
||||
let ready = sequencer.add_proof(2, SparseTrieUpdate::from_multiproof(proof3).unwrap());
|
||||
assert_eq!(ready.len(), 0);
|
||||
assert!(sequencer.has_pending());
|
||||
}
|
||||
@@ -1220,10 +1223,10 @@ mod tests {
|
||||
let proof1 = MultiProof::default();
|
||||
let proof2 = MultiProof::default();
|
||||
|
||||
let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof1));
|
||||
let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof1).unwrap());
|
||||
assert_eq!(ready.len(), 1);
|
||||
|
||||
let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof2));
|
||||
let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof2).unwrap());
|
||||
assert_eq!(ready.len(), 0);
|
||||
assert!(!sequencer.has_pending());
|
||||
}
|
||||
@@ -1234,12 +1237,13 @@ mod tests {
|
||||
let proofs: Vec<_> = (0..5).map(|_| MultiProof::default()).collect();
|
||||
sequencer.next_sequence = 5;
|
||||
|
||||
sequencer.add_proof(4, SparseTrieUpdate::from_multiproof(proofs[4].clone()));
|
||||
sequencer.add_proof(2, SparseTrieUpdate::from_multiproof(proofs[2].clone()));
|
||||
sequencer.add_proof(1, SparseTrieUpdate::from_multiproof(proofs[1].clone()));
|
||||
sequencer.add_proof(3, SparseTrieUpdate::from_multiproof(proofs[3].clone()));
|
||||
sequencer.add_proof(4, SparseTrieUpdate::from_multiproof(proofs[4].clone()).unwrap());
|
||||
sequencer.add_proof(2, SparseTrieUpdate::from_multiproof(proofs[2].clone()).unwrap());
|
||||
sequencer.add_proof(1, SparseTrieUpdate::from_multiproof(proofs[1].clone()).unwrap());
|
||||
sequencer.add_proof(3, SparseTrieUpdate::from_multiproof(proofs[3].clone()).unwrap());
|
||||
|
||||
let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proofs[0].clone()));
|
||||
let ready =
|
||||
sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proofs[0].clone()).unwrap());
|
||||
assert_eq!(ready.len(), 5);
|
||||
assert!(!sequencer.has_pending());
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ where
|
||||
let started_at = Instant::now();
|
||||
|
||||
// Reveal new accounts and storage slots.
|
||||
trie.reveal_multiproof(multiproof)?;
|
||||
trie.reveal_decoded_multiproof(multiproof)?;
|
||||
let reveal_multiproof_elapsed = started_at.elapsed();
|
||||
trace!(
|
||||
target: "engine::root::sparse",
|
||||
|
||||
@@ -308,6 +308,14 @@ pub struct DecodedMultiProof {
|
||||
}
|
||||
|
||||
impl DecodedMultiProof {
|
||||
/// Returns true if the multiproof is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.account_subtree.is_empty() &&
|
||||
self.branch_node_hash_masks.is_empty() &&
|
||||
self.branch_node_tree_masks.is_empty() &&
|
||||
self.storages.is_empty()
|
||||
}
|
||||
|
||||
/// Return the account proof nodes for the given account path.
|
||||
pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, TrieNode)> {
|
||||
self.account_subtree.matching_nodes_sorted(path)
|
||||
@@ -404,6 +412,36 @@ impl DecodedMultiProof {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`DecodedMultiProof`] from a [`DecodedStorageMultiProof`].
|
||||
pub fn from_storage_proof(
|
||||
hashed_address: B256,
|
||||
storage_proof: DecodedStorageMultiProof,
|
||||
) -> Self {
|
||||
Self {
|
||||
storages: B256Map::from_iter([(hashed_address, storage_proof)]),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<MultiProof> for DecodedMultiProof {
|
||||
type Error = alloy_rlp::Error;
|
||||
|
||||
fn try_from(multi_proof: MultiProof) -> Result<Self, Self::Error> {
|
||||
let account_subtree = DecodedProofNodes::try_from(multi_proof.account_subtree)?;
|
||||
let storages = multi_proof
|
||||
.storages
|
||||
.into_iter()
|
||||
.map(|(address, storage)| Ok((address, storage.try_into()?)))
|
||||
.collect::<Result<B256Map<_>, alloy_rlp::Error>>()?;
|
||||
Ok(Self {
|
||||
account_subtree,
|
||||
branch_node_hash_masks: multi_proof.branch_node_hash_masks,
|
||||
branch_node_tree_masks: multi_proof.branch_node_tree_masks,
|
||||
storages,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The merkle multiproof of storage trie.
|
||||
@@ -513,6 +551,20 @@ impl DecodedStorageMultiProof {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<StorageMultiProof> for DecodedStorageMultiProof {
|
||||
type Error = alloy_rlp::Error;
|
||||
|
||||
fn try_from(multi_proof: StorageMultiProof) -> Result<Self, Self::Error> {
|
||||
let subtree = DecodedProofNodes::try_from(multi_proof.subtree)?;
|
||||
Ok(Self {
|
||||
root: multi_proof.root,
|
||||
subtree,
|
||||
branch_node_hash_masks: multi_proof.branch_node_hash_masks,
|
||||
branch_node_tree_masks: multi_proof.branch_node_tree_masks,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The merkle proof with the relevant account info.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
|
||||
|
||||
@@ -25,8 +25,8 @@ use reth_trie::{
|
||||
trie_cursor::{InMemoryTrieCursorFactory, TrieCursorFactory},
|
||||
updates::TrieUpdatesSorted,
|
||||
walker::TrieWalker,
|
||||
HashBuilder, HashedPostStateSorted, MultiProof, MultiProofTargets, Nibbles, StorageMultiProof,
|
||||
TRIE_ACCOUNT_RLP_MAX_SIZE,
|
||||
DecodedMultiProof, DecodedStorageMultiProof, HashBuilder, HashedPostStateSorted, MultiProof,
|
||||
MultiProofTargets, Nibbles, StorageMultiProof, TRIE_ACCOUNT_RLP_MAX_SIZE,
|
||||
};
|
||||
use reth_trie_common::proof::ProofRetainer;
|
||||
use reth_trie_db::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
|
||||
@@ -145,6 +145,21 @@ where
|
||||
proof_result
|
||||
}
|
||||
|
||||
/// Generate a [`DecodedStorageMultiProof`] for the given proof by first calling
|
||||
/// `storage_proof`, then decoding the proof nodes.
|
||||
pub fn decoded_storage_proof(
|
||||
self,
|
||||
hashed_address: B256,
|
||||
target_slots: B256Set,
|
||||
) -> Result<DecodedStorageMultiProof, ParallelStateRootError> {
|
||||
let proof = self.storage_proof(hashed_address, target_slots)?;
|
||||
|
||||
// Now decode the nodes of the proof
|
||||
let proof = proof.try_into()?;
|
||||
|
||||
Ok(proof)
|
||||
}
|
||||
|
||||
/// Generate a state multiproof according to specified targets.
|
||||
pub fn multiproof(
|
||||
self,
|
||||
@@ -317,6 +332,21 @@ where
|
||||
|
||||
Ok(MultiProof { account_subtree, branch_node_hash_masks, branch_node_tree_masks, storages })
|
||||
}
|
||||
|
||||
/// Returns a [`DecodedMultiProof`] for the given proof.
|
||||
///
|
||||
/// Uses `multiproof` first to get the proof, and then decodes the nodes of the multiproof.
|
||||
pub fn decoded_multiproof(
|
||||
self,
|
||||
targets: MultiProofTargets,
|
||||
) -> Result<DecodedMultiProof, ParallelStateRootError> {
|
||||
let multiproof = self.multiproof(targets)?;
|
||||
|
||||
// Now decode the nodes of the multiproof
|
||||
let multiproof = multiproof.try_into()?;
|
||||
|
||||
Ok(multiproof)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -250,6 +250,12 @@ impl From<ParallelStateRootError> for ProviderError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<alloy_rlp::Error> for ParallelStateRootError {
|
||||
fn from(error: alloy_rlp::Error) -> Self {
|
||||
Self::Provider(ProviderError::Rlp(error))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -17,6 +17,7 @@ reth-primitives-traits.workspace = true
|
||||
reth-execution-errors.workspace = true
|
||||
reth-trie-common.workspace = true
|
||||
tracing.workspace = true
|
||||
alloy-trie.workspace = true
|
||||
|
||||
# alloy
|
||||
alloy-primitives.workspace = true
|
||||
@@ -53,12 +54,13 @@ rand_08.workspace = true
|
||||
[features]
|
||||
default = ["std", "metrics"]
|
||||
std = [
|
||||
"reth-storage-api/std",
|
||||
"reth-primitives-traits/std",
|
||||
"reth-execution-errors/std",
|
||||
"reth-trie-common/std",
|
||||
"alloy-primitives/std",
|
||||
"alloy-rlp/std",
|
||||
"alloy-trie/std",
|
||||
"reth-execution-errors/std",
|
||||
"reth-primitives-traits/std",
|
||||
"reth-storage-api/std",
|
||||
"reth-trie-common/std",
|
||||
"tracing/std",
|
||||
]
|
||||
metrics = ["dep:reth-metrics", "dep:metrics", "std"]
|
||||
@@ -72,9 +74,10 @@ test-utils = [
|
||||
]
|
||||
arbitrary = [
|
||||
"std",
|
||||
"alloy-primitives/arbitrary",
|
||||
"alloy-trie/arbitrary",
|
||||
"reth-primitives-traits/arbitrary",
|
||||
"reth-trie-common/arbitrary",
|
||||
"alloy-primitives/arbitrary",
|
||||
"smallvec/arbitrary",
|
||||
]
|
||||
|
||||
|
||||
@@ -9,14 +9,15 @@ use alloy_primitives::{
|
||||
Bytes, B256,
|
||||
};
|
||||
use alloy_rlp::{Decodable, Encodable};
|
||||
use alloy_trie::proof::DecodedProofNodes;
|
||||
use core::{fmt, iter::Peekable};
|
||||
use reth_execution_errors::{SparseStateTrieErrorKind, SparseStateTrieResult, SparseTrieErrorKind};
|
||||
use reth_primitives_traits::Account;
|
||||
use reth_trie_common::{
|
||||
proof::ProofNodes,
|
||||
updates::{StorageTrieUpdates, TrieUpdates},
|
||||
MultiProof, Nibbles, RlpNode, StorageMultiProof, TrieAccount, TrieMask, TrieNode,
|
||||
EMPTY_ROOT_HASH, TRIE_ACCOUNT_RLP_MAX_SIZE,
|
||||
DecodedMultiProof, DecodedStorageMultiProof, MultiProof, Nibbles, RlpNode, StorageMultiProof,
|
||||
TrieAccount, TrieMask, TrieNode, EMPTY_ROOT_HASH, TRIE_ACCOUNT_RLP_MAX_SIZE,
|
||||
};
|
||||
use tracing::trace;
|
||||
|
||||
@@ -282,7 +283,20 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
|
||||
/// Reveal unknown trie paths from multiproof.
|
||||
/// NOTE: This method does not extensively validate the proof.
|
||||
pub fn reveal_multiproof(&mut self, multiproof: MultiProof) -> SparseStateTrieResult<()> {
|
||||
let MultiProof {
|
||||
// first decode the multiproof
|
||||
let decoded_multiproof = multiproof.try_into()?;
|
||||
|
||||
// then reveal the decoded multiproof
|
||||
self.reveal_decoded_multiproof(decoded_multiproof)
|
||||
}
|
||||
|
||||
/// Reveal unknown trie paths from decoded multiproof.
|
||||
/// NOTE: This method does not extensively validate the proof.
|
||||
pub fn reveal_decoded_multiproof(
|
||||
&mut self,
|
||||
multiproof: DecodedMultiProof,
|
||||
) -> SparseStateTrieResult<()> {
|
||||
let DecodedMultiProof {
|
||||
account_subtree,
|
||||
storages,
|
||||
branch_node_hash_masks,
|
||||
@@ -290,7 +304,7 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
|
||||
} = multiproof;
|
||||
|
||||
// first reveal the account proof nodes
|
||||
self.reveal_account_multiproof(
|
||||
self.reveal_decoded_account_multiproof(
|
||||
account_subtree,
|
||||
branch_node_hash_masks,
|
||||
branch_node_tree_masks,
|
||||
@@ -298,7 +312,7 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
|
||||
|
||||
// then reveal storage proof nodes for each storage trie
|
||||
for (account, storage_subtree) in storages {
|
||||
self.reveal_storage_multiproof(account, storage_subtree)?;
|
||||
self.reveal_decoded_storage_multiproof(account, storage_subtree)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -311,12 +325,28 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
|
||||
branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
|
||||
branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
|
||||
) -> SparseStateTrieResult<()> {
|
||||
let DecodedProofNodes {
|
||||
// decode the multiproof first
|
||||
let decoded_multiproof = account_subtree.try_into()?;
|
||||
self.reveal_decoded_account_multiproof(
|
||||
decoded_multiproof,
|
||||
branch_node_hash_masks,
|
||||
branch_node_tree_masks,
|
||||
)
|
||||
}
|
||||
|
||||
/// Reveals a decoded account multiproof.
|
||||
pub fn reveal_decoded_account_multiproof(
|
||||
&mut self,
|
||||
account_subtree: DecodedProofNodes,
|
||||
branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
|
||||
branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
|
||||
) -> SparseStateTrieResult<()> {
|
||||
let FilteredProofNodes {
|
||||
nodes,
|
||||
new_nodes,
|
||||
total_nodes: _total_nodes,
|
||||
skipped_nodes: _skipped_nodes,
|
||||
} = decode_proof_nodes(account_subtree, &self.revealed_account_paths)?;
|
||||
} = filter_revealed_nodes(account_subtree, &self.revealed_account_paths)?;
|
||||
#[cfg(feature = "metrics")]
|
||||
{
|
||||
self.metrics.increment_total_account_nodes(_total_nodes as u64);
|
||||
@@ -366,15 +396,26 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
|
||||
&mut self,
|
||||
account: B256,
|
||||
storage_subtree: StorageMultiProof,
|
||||
) -> SparseStateTrieResult<()> {
|
||||
// decode the multiproof first
|
||||
let decoded_multiproof = storage_subtree.try_into()?;
|
||||
self.reveal_decoded_storage_multiproof(account, decoded_multiproof)
|
||||
}
|
||||
|
||||
/// Reveals a decoded storage multiproof for the given address.
|
||||
pub fn reveal_decoded_storage_multiproof(
|
||||
&mut self,
|
||||
account: B256,
|
||||
storage_subtree: DecodedStorageMultiProof,
|
||||
) -> SparseStateTrieResult<()> {
|
||||
let revealed_nodes = self.revealed_storage_paths.entry(account).or_default();
|
||||
|
||||
let DecodedProofNodes {
|
||||
let FilteredProofNodes {
|
||||
nodes,
|
||||
new_nodes,
|
||||
total_nodes: _total_nodes,
|
||||
skipped_nodes: _skipped_nodes,
|
||||
} = decode_proof_nodes(storage_subtree.subtree, revealed_nodes)?;
|
||||
} = filter_revealed_nodes(storage_subtree.subtree, revealed_nodes)?;
|
||||
#[cfg(feature = "metrics")]
|
||||
{
|
||||
self.metrics.increment_total_storage_nodes(_total_nodes as u64);
|
||||
@@ -829,9 +870,9 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of [`decode_proof_nodes`].
|
||||
/// Result of [`filter_revealed_nodes`].
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct DecodedProofNodes {
|
||||
struct FilteredProofNodes {
|
||||
/// Filtered, decoded and sorted proof nodes.
|
||||
nodes: Vec<(Nibbles, TrieNode)>,
|
||||
/// Number of nodes in the proof.
|
||||
@@ -843,20 +884,20 @@ struct DecodedProofNodes {
|
||||
new_nodes: usize,
|
||||
}
|
||||
|
||||
/// Decodes the proof nodes returning additional information about the number of total, skipped, and
|
||||
/// new nodes.
|
||||
fn decode_proof_nodes(
|
||||
proof_nodes: ProofNodes,
|
||||
/// Filters the decoded nodes that are already revealed and returns additional information about the
|
||||
/// number of total, skipped, and new nodes.
|
||||
fn filter_revealed_nodes(
|
||||
proof_nodes: DecodedProofNodes,
|
||||
revealed_nodes: &HashSet<Nibbles>,
|
||||
) -> alloy_rlp::Result<DecodedProofNodes> {
|
||||
let mut result = DecodedProofNodes {
|
||||
) -> alloy_rlp::Result<FilteredProofNodes> {
|
||||
let mut result = FilteredProofNodes {
|
||||
nodes: Vec::with_capacity(proof_nodes.len()),
|
||||
total_nodes: 0,
|
||||
skipped_nodes: 0,
|
||||
new_nodes: 0,
|
||||
};
|
||||
|
||||
for (path, bytes) in proof_nodes.into_inner() {
|
||||
for (path, node) in proof_nodes.into_inner() {
|
||||
result.total_nodes += 1;
|
||||
// If the node is already revealed, skip it.
|
||||
if revealed_nodes.contains(&path) {
|
||||
@@ -864,7 +905,6 @@ fn decode_proof_nodes(
|
||||
continue
|
||||
}
|
||||
|
||||
let node = TrieNode::decode(&mut &bytes[..])?;
|
||||
result.new_nodes += 1;
|
||||
// If it's a branch node, increase the number of new nodes by the number of children
|
||||
// according to the state mask.
|
||||
@@ -892,7 +932,7 @@ mod tests {
|
||||
use assert_matches::assert_matches;
|
||||
use rand::{rngs::StdRng, Rng, SeedableRng};
|
||||
use reth_primitives_traits::Account;
|
||||
use reth_trie::{updates::StorageTrieUpdates, HashBuilder, EMPTY_ROOT_HASH};
|
||||
use reth_trie::{updates::StorageTrieUpdates, HashBuilder, MultiProof, EMPTY_ROOT_HASH};
|
||||
use reth_trie_common::{
|
||||
proof::{ProofNodes, ProofRetainer},
|
||||
BranchNode, LeafNode, StorageMultiProof, TrieMask,
|
||||
@@ -987,7 +1027,7 @@ mod tests {
|
||||
};
|
||||
|
||||
// Reveal multiproof and check that the state trie contains the leaf node and value
|
||||
sparse.reveal_multiproof(multiproof.clone()).unwrap();
|
||||
sparse.reveal_decoded_multiproof(multiproof.clone().try_into().unwrap()).unwrap();
|
||||
assert!(sparse
|
||||
.state_trie_ref()
|
||||
.unwrap()
|
||||
@@ -1014,7 +1054,7 @@ mod tests {
|
||||
|
||||
// Reveal multiproof again and check that the state trie still does not contain the leaf
|
||||
// node and value, because they were already revealed before
|
||||
sparse.reveal_multiproof(multiproof).unwrap();
|
||||
sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
|
||||
assert!(!sparse
|
||||
.state_trie_ref()
|
||||
.unwrap()
|
||||
@@ -1066,7 +1106,7 @@ mod tests {
|
||||
};
|
||||
|
||||
// Reveal multiproof and check that the storage trie contains the leaf node and value
|
||||
sparse.reveal_multiproof(multiproof.clone()).unwrap();
|
||||
sparse.reveal_decoded_multiproof(multiproof.clone().try_into().unwrap()).unwrap();
|
||||
assert!(sparse
|
||||
.storage_trie_ref(&B256::ZERO)
|
||||
.unwrap()
|
||||
@@ -1096,7 +1136,7 @@ mod tests {
|
||||
|
||||
// Reveal multiproof again and check that the storage trie still does not contain the leaf
|
||||
// node and value, because they were already revealed before
|
||||
sparse.reveal_multiproof(multiproof).unwrap();
|
||||
sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
|
||||
assert!(!sparse
|
||||
.storage_trie_ref(&B256::ZERO)
|
||||
.unwrap()
|
||||
@@ -1166,34 +1206,38 @@ mod tests {
|
||||
|
||||
let mut sparse = SparseStateTrie::default().with_updates(true);
|
||||
sparse
|
||||
.reveal_multiproof(MultiProof {
|
||||
account_subtree: proof_nodes,
|
||||
branch_node_hash_masks: HashMap::from_iter([(
|
||||
Nibbles::from_nibbles([0x1]),
|
||||
TrieMask::new(0b00),
|
||||
)]),
|
||||
branch_node_tree_masks: HashMap::default(),
|
||||
storages: HashMap::from_iter([
|
||||
(
|
||||
address_1,
|
||||
StorageMultiProof {
|
||||
root,
|
||||
subtree: storage_proof_nodes.clone(),
|
||||
branch_node_hash_masks: storage_branch_node_hash_masks.clone(),
|
||||
branch_node_tree_masks: HashMap::default(),
|
||||
},
|
||||
),
|
||||
(
|
||||
address_2,
|
||||
StorageMultiProof {
|
||||
root,
|
||||
subtree: storage_proof_nodes,
|
||||
branch_node_hash_masks: storage_branch_node_hash_masks,
|
||||
branch_node_tree_masks: HashMap::default(),
|
||||
},
|
||||
),
|
||||
]),
|
||||
})
|
||||
.reveal_decoded_multiproof(
|
||||
MultiProof {
|
||||
account_subtree: proof_nodes,
|
||||
branch_node_hash_masks: HashMap::from_iter([(
|
||||
Nibbles::from_nibbles([0x1]),
|
||||
TrieMask::new(0b00),
|
||||
)]),
|
||||
branch_node_tree_masks: HashMap::default(),
|
||||
storages: HashMap::from_iter([
|
||||
(
|
||||
address_1,
|
||||
StorageMultiProof {
|
||||
root,
|
||||
subtree: storage_proof_nodes.clone(),
|
||||
branch_node_hash_masks: storage_branch_node_hash_masks.clone(),
|
||||
branch_node_tree_masks: HashMap::default(),
|
||||
},
|
||||
),
|
||||
(
|
||||
address_2,
|
||||
StorageMultiProof {
|
||||
root,
|
||||
subtree: storage_proof_nodes,
|
||||
branch_node_hash_masks: storage_branch_node_hash_masks,
|
||||
branch_node_tree_masks: HashMap::default(),
|
||||
},
|
||||
),
|
||||
]),
|
||||
}
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(sparse.root().unwrap(), root);
|
||||
@@ -1235,7 +1279,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_proof_nodes() {
|
||||
fn test_filter_revealed_nodes() {
|
||||
let revealed_nodes = HashSet::from_iter([Nibbles::from_nibbles([0x0])]);
|
||||
let leaf = TrieNode::Leaf(LeafNode::new(Nibbles::default(), alloy_rlp::encode([])));
|
||||
let leaf_encoded = alloy_rlp::encode(&leaf);
|
||||
@@ -1243,17 +1287,17 @@ mod tests {
|
||||
vec![RlpNode::from_rlp(&leaf_encoded), RlpNode::from_rlp(&leaf_encoded)],
|
||||
TrieMask::new(0b11),
|
||||
));
|
||||
let proof_nodes = ProofNodes::from_iter([
|
||||
(Nibbles::default(), alloy_rlp::encode(&branch).into()),
|
||||
(Nibbles::from_nibbles([0x0]), leaf_encoded.clone().into()),
|
||||
(Nibbles::from_nibbles([0x1]), leaf_encoded.into()),
|
||||
let proof_nodes = alloy_trie::proof::DecodedProofNodes::from_iter([
|
||||
(Nibbles::default(), branch.clone()),
|
||||
(Nibbles::from_nibbles([0x0]), leaf.clone()),
|
||||
(Nibbles::from_nibbles([0x1]), leaf.clone()),
|
||||
]);
|
||||
|
||||
let decoded = decode_proof_nodes(proof_nodes, &revealed_nodes).unwrap();
|
||||
let decoded = filter_revealed_nodes(proof_nodes, &revealed_nodes).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
decoded,
|
||||
DecodedProofNodes {
|
||||
FilteredProofNodes {
|
||||
nodes: vec![(Nibbles::default(), branch), (Nibbles::from_nibbles([0x1]), leaf)],
|
||||
// Branch, leaf, leaf
|
||||
total_nodes: 3,
|
||||
|
||||
Reference in New Issue
Block a user