diff --git a/Cargo.lock b/Cargo.lock index 4add27ae..c12c73c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1687,8 +1687,8 @@ dependencies = [ "alloy-transport-http 0.1.0 (git+https://github.com/alloy-rs/alloy)", "alloy-trie", "anyhow", - "futures", "reqwest", + "smallvec", "tokio", "tracing", "tracing-subscriber", @@ -2373,9 +2373,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" dependencies = [ "bitflags 2.5.0", "errno", @@ -2569,9 +2569,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] diff --git a/crates/mpt/src/list_walker.rs b/crates/mpt/src/list_walker.rs index 129ed70e..3aa0f8cb 100644 --- a/crates/mpt/src/list_walker.rs +++ b/crates/mpt/src/list_walker.rs @@ -132,18 +132,19 @@ impl Iterator for OrderedListWalker { #[cfg(test)] mod test { - extern crate std; - use super::*; - use crate::test_util::{get_live_derivable_receipts_list, ordered_trie_with_encoder}; + use crate::test_util::{ + get_live_derivable_receipts_list, get_live_derivable_transactions_list, + ordered_trie_with_encoder, + }; use alloc::{collections::BTreeMap, string::String, vec::Vec}; - use alloy_consensus::ReceiptEnvelope; + use alloy_consensus::{ReceiptEnvelope, TxEnvelope}; use alloy_primitives::keccak256; use alloy_provider::network::eip2718::Decodable2718; use alloy_rlp::Encodable; #[tokio::test] - async fn test_list_walker_online() { + async fn test_list_walker_online_receipts() { let (root, preimages, envelopes) = get_live_derivable_receipts_list().await.unwrap(); let list = OrderedListWalker::try_new_hydrated(root, |f| Ok(preimages.get(&f).unwrap().clone())) @@ -157,6 +158,21 @@ mod test { ); } + #[tokio::test] + async fn test_list_walker_online_transactions() { + let (root, preimages, envelopes) = get_live_derivable_transactions_list().await.unwrap(); + let list = + OrderedListWalker::try_new_hydrated(root, |f| Ok(preimages.get(&f).unwrap().clone())) + .unwrap(); + + assert_eq!( + list.into_iter() + .map(|rlp| TxEnvelope::decode(&mut rlp.as_ref()).unwrap()) + .collect::>(), + envelopes + ); + } + #[test] fn test_list_walker() { const VALUES: [&str; 3] = ["test one", "test two", "test three"]; diff --git a/crates/mpt/src/node.rs b/crates/mpt/src/node.rs index 31030227..105ae1fc 100644 --- a/crates/mpt/src/node.rs +++ b/crates/mpt/src/node.rs @@ -48,6 +48,26 @@ pub enum TrieNode { }, } +impl TrieNode { + /// Attempts to convert a `path` and `value` into a [TrieNode], if they correspond to a + /// [TrieNode::Leaf] or [TrieNode::Extension]. + pub fn try_from_path_and_value(path: Bytes, value: Bytes) -> Result { + match path[0] >> 4 { + PREFIX_EXTENSION_EVEN | PREFIX_EXTENSION_ODD => { + // extension node + Ok(TrieNode::Extension { prefix: path, node: value }) + } + PREFIX_LEAF_EVEN | PREFIX_LEAF_ODD => { + // leaf node + Ok(TrieNode::Leaf { key: path, value }) + } + _ => { + anyhow::bail!("Unexpected path identifier in high-order nibble") + } + } + } +} + impl Decodable for TrieNode { /// Attempts to decode the [TrieNode]. fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { @@ -59,28 +79,12 @@ impl Decodable for TrieNode { let Some(NodeElement::String(path)) = list.pop_front() else { return Err(alloy_rlp::Error::UnexpectedList); }; + let Some(NodeElement::String(value)) = list.pop_front() else { + return Err(alloy_rlp::Error::UnexpectedList); + }; - match path[0] >> 4 { - PREFIX_EXTENSION_EVEN | PREFIX_EXTENSION_ODD => { - // extension node - let Some(NodeElement::String(node)) = list.pop_front() else { - return Err(alloy_rlp::Error::UnexpectedList); - }; - - Ok(Self::Extension { prefix: path, node }) - } - PREFIX_LEAF_EVEN | PREFIX_LEAF_ODD => { - // leaf node - let Some(NodeElement::String(value)) = list.pop_front() else { - return Err(alloy_rlp::Error::UnexpectedList); - }; - - Ok(Self::Leaf { key: path, value }) - } - _ => Err(alloy_rlp::Error::Custom( - "Unexpected path identifier in high-order nibble", - )), - } + Self::try_from_path_and_value(path, value) + .map_err(|_| alloy_rlp::Error::UnexpectedList) } _ => Err(alloy_rlp::Error::UnexpectedLength), } @@ -110,22 +114,8 @@ impl NodeElement { } let path = list.pop_front().ok_or(anyhow!("List is empty; Impossible case"))?; - match path[0] >> 4 { - 0 | 1 => { - // extension node - let node = list.pop_front().ok_or(anyhow!("List is empty; Impossible case"))?; - Ok(TrieNode::Extension { prefix: path, node }) - } - 2 | 3 => { - // leaf node - let value = - list.pop_front().ok_or(anyhow!("List is empty; Impossible case"))?; - Ok(TrieNode::Leaf { key: path, value }) - } - _ => { - anyhow::bail!("Unexpected path identifier in high-order nibble") - } - } + let value = list.pop_front().ok_or(anyhow!("List is empty; Impossible case"))?; + TrieNode::try_from_path_and_value(path, value) } else { anyhow::bail!("Self is not a list") } diff --git a/crates/mpt/src/test_util.rs b/crates/mpt/src/test_util.rs index c3c4584d..49194d0e 100644 --- a/crates/mpt/src/test_util.rs +++ b/crates/mpt/src/test_util.rs @@ -3,24 +3,26 @@ extern crate std; use alloc::{collections::BTreeMap, vec::Vec}; -use alloy_consensus::{Receipt, ReceiptEnvelope, ReceiptWithBloom, TxType}; +use alloy_consensus::{Receipt, ReceiptEnvelope, ReceiptWithBloom, TxEnvelope, TxType}; use alloy_primitives::{keccak256, Bytes, Log, B256}; use alloy_provider::{network::eip2718::Encodable2718, Provider, ProviderBuilder}; use alloy_rlp::{BufMut, Encodable}; +use alloy_rpc_types::BlockTransactions; use alloy_trie::{HashBuilder, Nibbles}; use anyhow::{anyhow, Result}; use reqwest::Url; +const RPC_URL: &str = "https://docs-demo.quiknode.pro/"; + /// Grabs a live merkleized receipts list within a block header. pub(crate) async fn get_live_derivable_receipts_list( ) -> Result<(B256, BTreeMap, Vec)> { // Initialize the provider. - let rpc_url = "http://anton.clab.by:8545"; let provider = ProviderBuilder::new() - .on_http(Url::parse(rpc_url).expect("invalid rpc url")) + .on_http(Url::parse(RPC_URL).expect("invalid rpc url")) .map_err(|e| anyhow!(e))?; - let block_number = provider.get_block_number().await.map_err(|e| anyhow!(e))?; + let block_number = 19005266; let block = provider .get_block(block_number.into(), true) .await @@ -77,6 +79,47 @@ pub(crate) async fn get_live_derivable_receipts_list( Ok((root, preimages, consensus_receipts)) } +/// Grabs a live merkleized transactions list within a block header. +pub(crate) async fn get_live_derivable_transactions_list( +) -> Result<(B256, BTreeMap, Vec)> { + // Initialize the provider. + let provider = ProviderBuilder::new() + .on_http(Url::parse(RPC_URL).expect("invalid rpc url")) + .map_err(|e| anyhow!(e))?; + + let block_number = 19005266; + let block = provider + .get_block(block_number.into(), true) + .await + .map_err(|e| anyhow!(e))? + .ok_or(anyhow!("Missing block"))?; + + let BlockTransactions::Full(txs) = block.transactions else { + anyhow::bail!("Did not fetch full block"); + }; + let consensus_txs = txs + .into_iter() + .map(|tx| TxEnvelope::try_from(tx).map_err(|e| anyhow!(e))) + .collect::>>()?; + + // Compute the derivable list + let mut list = + ordered_trie_with_encoder(consensus_txs.as_ref(), |rlp, buf| rlp.encode_2718(buf)); + let root = list.root(); + + // Sanity check transaction root is correct + assert_eq!(block.header.transactions_root, root); + + // Construct the mapping of hashed intermediates -> raw intermediates + let preimages = + list.take_proofs().into_iter().fold(BTreeMap::default(), |mut acc, (_, value)| { + acc.insert(keccak256(value.as_ref()), value); + acc + }); + + Ok((root, preimages, consensus_txs)) +} + /// Compute a trie root of the collection of items with a custom encoder. pub(crate) fn ordered_trie_with_encoder(items: &[T], mut encode: F) -> HashBuilder where