perf(proofs): root calculation using hash builder (#2517)

This commit is contained in:
Roman Krasiuk
2023-05-02 18:41:22 +03:00
committed by GitHub
parent 949b3639c3
commit 3cf6015149
8 changed files with 69 additions and 50 deletions

View File

@ -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::<Vec<ReceiptWithBloom>>();
proofs::calculate_receipt_root(receipts_with_bloom.iter())
proofs::calculate_receipt_root(&receipts_with_bloom)
};
let transactions = body.clone();
let body =

View File

@ -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::<Vec<_>>();
SealedBlock {
header: Header {
withdrawals_root: Some(proofs::calculate_withdrawals_root(withdrawals.iter())),
withdrawals_root: Some(proofs::calculate_withdrawals_root(&withdrawals)),
..Default::default()
}
.seal_slow(),

View File

@ -110,7 +110,7 @@ pub fn random_block(
(0..ommers_count).map(|_| random_header(number, parent).unseal()).collect::<Vec<_>>();
// 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 {

View File

@ -661,7 +661,7 @@ fn build_payload<Pool, Client>(
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 {

View File

@ -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<T: Encodable>(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<T, F>(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<I, T>(transactions: I) -> H256
pub fn calculate_transaction_root<T>(transactions: &[T]) -> H256
where
I: IntoIterator<Item = T>,
T: AsRef<TransactionSigned>,
{
ordered_trie_root::<KeccakHasher, _>(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<Item = &'a Withdrawal>,
) -> H256 {
ordered_trie_root::<KeccakHasher, _>(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<Item = &'a ReceiptWithBloom>) -> H256 {
ordered_trie_root::<KeccakHasher, _>(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<Item = ReceiptWithBloomRef<'a>>,
) -> H256 {
ordered_trie_root::<KeccakHasher, _>(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<T>(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));
}

View File

@ -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::<Vec<ReceiptWithBloom>>();
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 })
}

View File

@ -139,10 +139,10 @@ impl TryFrom<ExecutionPayload> for SealedBlock {
.iter()
.map(|tx| TransactionSigned::decode(&mut tx.as_ref()))
.collect::<Result<Vec<_>, _>>()?;
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(),

View File

@ -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.