diff --git a/crates/consensus/auto-seal/src/task.rs b/crates/consensus/auto-seal/src/task.rs index 0173503b9..ea1ee693a 100644 --- a/crates/consensus/auto-seal/src/task.rs +++ b/crates/consensus/auto-seal/src/task.rs @@ -153,7 +153,7 @@ where header.transactions_root = if transactions.is_empty() { EMPTY_TRANSACTIONS } else { - proofs::calculate_transaction_root(transactions.iter()) + proofs::calculate_transaction_root(&transactions) }; let block = @@ -186,7 +186,7 @@ where .iter() .map(|r| r.clone().into()) .collect::>(); - proofs::calculate_receipt_root(receipts_with_bloom.iter()) + proofs::calculate_receipt_root(&receipts_with_bloom) }; let transactions = body.clone(); let body = diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index c48be5daf..bea26aa5d 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -192,7 +192,7 @@ pub fn validate_block_standalone( // Check transaction root // TODO(onbjerg): This should probably be accessible directly on [Block] - let transaction_root = reth_primitives::proofs::calculate_transaction_root(block.body.iter()); + let transaction_root = reth_primitives::proofs::calculate_transaction_root(&block.body); if block.header.transactions_root != transaction_root { return Err(ConsensusError::BodyTransactionRootDiff { got: transaction_root, @@ -204,8 +204,7 @@ pub fn validate_block_standalone( if chain_spec.fork(Hardfork::Shanghai).active_at_timestamp(block.timestamp) { let withdrawals = block.withdrawals.as_ref().ok_or(ConsensusError::BodyWithdrawalsMissing)?; - let withdrawals_root = - reth_primitives::proofs::calculate_withdrawals_root(withdrawals.iter()); + let withdrawals_root = reth_primitives::proofs::calculate_withdrawals_root(withdrawals); let header_withdrawals_root = block.withdrawals_root.as_ref().ok_or(ConsensusError::WithdrawalsRootMissing)?; if withdrawals_root != *header_withdrawals_root { @@ -626,7 +625,7 @@ mod tests { .collect::>(); SealedBlock { header: Header { - withdrawals_root: Some(proofs::calculate_withdrawals_root(withdrawals.iter())), + withdrawals_root: Some(proofs::calculate_withdrawals_root(&withdrawals)), ..Default::default() } .seal_slow(), diff --git a/crates/interfaces/src/test_utils/generators.rs b/crates/interfaces/src/test_utils/generators.rs index 4316fa476..ebb2a18e7 100644 --- a/crates/interfaces/src/test_utils/generators.rs +++ b/crates/interfaces/src/test_utils/generators.rs @@ -110,7 +110,7 @@ pub fn random_block( (0..ommers_count).map(|_| random_header(number, parent).unseal()).collect::>(); // Calculate roots - let transactions_root = proofs::calculate_transaction_root(transactions.iter()); + let transactions_root = proofs::calculate_transaction_root(&transactions); let ommers_hash = proofs::calculate_ommers_root(&ommers); SealedBlock { diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index bc06d5cb4..407520ae1 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -661,7 +661,7 @@ fn build_payload( let state_root = db.db.0.state_root(post_state)?; // create the block header - let transactions_root = proofs::calculate_transaction_root(executed_txs.iter()); + let transactions_root = proofs::calculate_transaction_root(&executed_txs); let header = Header { parent_hash: parent_block.hash, @@ -807,7 +807,7 @@ where increment_account_balance(db, post_state, block_number, address, increment)?; } - let withdrawals_root = proofs::calculate_withdrawals_root(withdrawals.iter()); + let withdrawals_root = proofs::calculate_withdrawals_root(&withdrawals); // calculate withdrawals root Ok(WithdrawalsOutcome { diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index 48196cab8..95fd279d1 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -1,14 +1,16 @@ use crate::{ - keccak256, Address, Bytes, GenesisAccount, Header, Log, ReceiptWithBloom, ReceiptWithBloomRef, + keccak256, + trie::{HashBuilder, Nibbles}, + Address, Bytes, GenesisAccount, Header, Log, ReceiptWithBloom, ReceiptWithBloomRef, TransactionSigned, Withdrawal, H256, }; -use bytes::BytesMut; +use bytes::{BufMut, BytesMut}; use hash_db::Hasher; use hex_literal::hex; use plain_hasher::PlainHasher; use reth_rlp::Encodable; use std::collections::HashMap; -use triehash::{ordered_trie_root, sec_trie_root}; +use triehash::sec_trie_root; /// Keccak-256 hash of the RLP of an empty list, KEC("\xc0"). pub const EMPTY_LIST_HASH: H256 = @@ -44,50 +46,66 @@ pub const fn adjust_index_for_rlp(i: usize, len: usize) -> usize { } } +/// Compute a trie root of the collection of rlp encodable items. +pub fn ordered_trie_root(items: &[T]) -> H256 { + ordered_trie_root_with_encoder(items, |item, buf| item.encode(buf)) +} + +/// Compute a trie root of the collection of items with a custom encoder. +pub fn ordered_trie_root_with_encoder(items: &[T], mut encode: F) -> H256 +where + F: FnMut(&T, &mut dyn BufMut), +{ + let mut index_buffer = BytesMut::new(); + let mut value_buffer = BytesMut::new(); + + let mut hb = HashBuilder::default(); + let items_len = items.len(); + for i in 0..items_len { + let index = adjust_index_for_rlp(i, items_len); + + index_buffer.clear(); + index.encode(&mut index_buffer); + + value_buffer.clear(); + encode(&items[index], &mut value_buffer); + + hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); + } + + hb.root() +} + /// Calculate a transaction root. /// /// `(rlp(index), encoded(tx))` pairs. -pub fn calculate_transaction_root(transactions: I) -> H256 +pub fn calculate_transaction_root(transactions: &[T]) -> H256 where - I: IntoIterator, T: AsRef, { - ordered_trie_root::(transactions.into_iter().map(|tx| { - let mut tx_rlp = Vec::new(); - tx.as_ref().encode_inner(&mut tx_rlp, false); - tx_rlp - })) + ordered_trie_root_with_encoder(transactions, |tx: &T, buf| tx.as_ref().encode_inner(buf, false)) } /// Calculates the root hash of the withdrawals. -pub fn calculate_withdrawals_root<'a>( - withdrawals: impl IntoIterator, -) -> H256 { - ordered_trie_root::(withdrawals.into_iter().map(|withdrawal| { - let mut withdrawal_rlp = Vec::new(); - withdrawal.encode(&mut withdrawal_rlp); - withdrawal_rlp - })) +pub fn calculate_withdrawals_root(withdrawals: &[Withdrawal]) -> H256 { + ordered_trie_root(withdrawals) } /// Calculates the receipt root for a header. -pub fn calculate_receipt_root<'a>(receipts: impl Iterator) -> H256 { - ordered_trie_root::(receipts.into_iter().map(|receipt| { - let mut receipt_rlp = Vec::new(); - receipt.encode_inner(&mut receipt_rlp, false); - receipt_rlp - })) +pub fn calculate_receipt_root(receipts: &[ReceiptWithBloom]) -> H256 { + ordered_trie_root_with_encoder(receipts, |r, buf| r.encode_inner(buf, false)) } /// Calculates the receipt root for a header for the reference type of [ReceiptWithBloom]. -pub fn calculate_receipt_root_ref<'a>( - receipts: impl Iterator>, -) -> H256 { - ordered_trie_root::(receipts.into_iter().map(|receipt| { - let mut receipt_rlp = Vec::new(); - receipt.encode_inner(&mut receipt_rlp, false); - receipt_rlp - })) +/// +/// NOTE: Prefer [calculate_receipt_root] if you have log blooms memoized. +pub fn calculate_receipt_root_ref(receipts: &[T]) -> H256 +where + for<'a> ReceiptWithBloomRef<'a>: From<&'a T>, +{ + ordered_trie_root_with_encoder(receipts, |r, buf| { + ReceiptWithBloomRef::from(r).encode_inner(buf, false) + }) } /// Calculates the log root for headers. @@ -139,7 +157,7 @@ mod tests { let block_rlp = &mut data.as_slice(); let block: Block = Block::decode(block_rlp).unwrap(); - let tx_root = calculate_transaction_root(block.body.iter()); + let tx_root = calculate_transaction_root(&block.body); assert_eq!(block.transactions_root, tx_root, "Must be the same"); } @@ -157,7 +175,7 @@ mod tests { bloom, }; let receipt = vec![receipt]; - let root = calculate_receipt_root(receipt.iter()); + let root = calculate_receipt_root(&receipt); assert_eq!( root, H256(hex!("fe70ae4a136d98944951b2123859698d59ad251a381abc9960fa81cae3d0d4a0")) @@ -173,7 +191,7 @@ mod tests { assert!(block.withdrawals.is_some()); let withdrawals = block.withdrawals.as_ref().unwrap(); assert_eq!(withdrawals.len(), 1); - let withdrawals_root = calculate_withdrawals_root(withdrawals.iter()); + let withdrawals_root = calculate_withdrawals_root(withdrawals); assert_eq!(block.withdrawals_root, Some(withdrawals_root)); // 4 withdrawals, identical indices @@ -183,7 +201,7 @@ mod tests { assert!(block.withdrawals.is_some()); let withdrawals = block.withdrawals.as_ref().unwrap(); assert_eq!(withdrawals.len(), 4); - let withdrawals_root = calculate_withdrawals_root(withdrawals.iter()); + let withdrawals_root = calculate_withdrawals_root(withdrawals); assert_eq!(block.withdrawals_root, Some(withdrawals_root)); } diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 73ad299a4..3486c6312 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -492,7 +492,7 @@ pub fn verify_receipt<'a>( ) -> Result<(), Error> { // Check receipts root. let receipts_with_bloom = receipts.map(|r| r.clone().into()).collect::>(); - let receipts_root = reth_primitives::proofs::calculate_receipt_root(receipts_with_bloom.iter()); + let receipts_root = reth_primitives::proofs::calculate_receipt_root(&receipts_with_bloom); if receipts_root != expected_receipts_root { return Err(Error::ReceiptRootDiff { got: receipts_root, expected: expected_receipts_root }) } diff --git a/crates/rpc/rpc-types/src/eth/engine/payload.rs b/crates/rpc/rpc-types/src/eth/engine/payload.rs index 76ac7c4b6..9fdc948a4 100644 --- a/crates/rpc/rpc-types/src/eth/engine/payload.rs +++ b/crates/rpc/rpc-types/src/eth/engine/payload.rs @@ -139,10 +139,10 @@ impl TryFrom for SealedBlock { .iter() .map(|tx| TransactionSigned::decode(&mut tx.as_ref())) .collect::, _>>()?; - let transactions_root = proofs::calculate_transaction_root(transactions.iter()); + let transactions_root = proofs::calculate_transaction_root(&transactions); let withdrawals_root = - payload.withdrawals.as_ref().map(|w| proofs::calculate_withdrawals_root(w.iter())); + payload.withdrawals.as_ref().map(|w| proofs::calculate_withdrawals_root(w)); let header = Header { parent_hash: payload.parent_hash, @@ -411,7 +411,7 @@ mod tests { let mut transformed: Block = f(unsealed); // Recalculate roots transformed.header.transactions_root = - proofs::calculate_transaction_root(transformed.body.iter()); + proofs::calculate_transaction_root(&transformed.body); transformed.header.ommers_hash = proofs::calculate_ommers_root(&transformed.ommers); SealedBlock { header: transformed.header.seal_slow(), diff --git a/crates/storage/provider/src/post_state.rs b/crates/storage/provider/src/post_state.rs index b6b8c560d..53c879727 100644 --- a/crates/storage/provider/src/post_state.rs +++ b/crates/storage/provider/src/post_state.rs @@ -197,8 +197,10 @@ impl PostState { } /// Returns the receipt root for all recorded receipts. + /// TODO: This function hides an expensive operation (bloom). We should probably make it more + /// explicit. pub fn receipts_root(&self) -> H256 { - calculate_receipt_root_ref(self.receipts().iter().map(Into::into)) + calculate_receipt_root_ref(self.receipts()) } /// Hash all changed accounts and storage entries that are currently stored in the post state.