From f46388e31dc40b44a7e4d8151583cfc97afb01fe Mon Sep 17 00:00:00 2001 From: Eshaan Bansal Date: Fri, 22 Nov 2024 19:06:28 +0530 Subject: [PATCH] perf: use get_block_receipts in verify_logs (#439) * perf: use get_block_receipts in verify_logs * remove debug println --- core/src/execution/errors.rs | 2 ++ core/src/execution/mod.rs | 58 ++++++++++++++++++------------------ 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/core/src/execution/errors.rs b/core/src/execution/errors.rs index 7f9f8422..80f4ddb7 100644 --- a/core/src/execution/errors.rs +++ b/core/src/execution/errors.rs @@ -17,6 +17,8 @@ pub enum ExecutionError { ReceiptRootMismatch(B256), #[error("could not prove receipt for tx: {0}")] NoReceiptForTransaction(B256), + #[error("could not prove receipts for block: {0}")] + NoReceiptsForBlock(BlockTag), #[error("missing log for transaction: {0}, index: {1}")] MissingLog(B256, U256), #[error("too many logs to prove: {0}, current limit is: {1}")] diff --git a/core/src/execution/mod.rs b/core/src/execution/mod.rs index db3ba140..7e021fe0 100644 --- a/core/src/execution/mod.rs +++ b/core/src/execution/mod.rs @@ -5,7 +5,7 @@ use alloy::primitives::{keccak256, Address, B256, U256}; use alloy::rlp::encode; use alloy::rpc::types::{Filter, Log}; use eyre::Result; -use futures::future::join_all; +use futures::future::try_join_all; use revm::primitives::KECCAK_EMPTY; use triehash_ethereum::ordered_trie_root; @@ -201,9 +201,9 @@ impl> ExecutionClient { .rpc .get_block_receipts(tag) .await? - .ok_or(eyre::eyre!("missing block receipt"))?; + .ok_or(eyre::eyre!(ExecutionError::NoReceiptsForBlock(tag)))?; - let receipts_encoded: Vec> = receipts.iter().map(N::encode_receipt).collect(); + let receipts_encoded = receipts.iter().map(N::encode_receipt).collect::>(); let expected_receipt_root = ordered_trie_root(receipts_encoded.clone()); let expected_receipt_root = B256::from_slice(&expected_receipt_root.to_fixed_bytes()); @@ -240,10 +240,9 @@ impl> ExecutionClient { .rpc .get_block_receipts(tag) .await? - .ok_or(eyre::eyre!("block receipts not found"))?; - - let receipts_encoded: Vec> = receipts.iter().map(N::encode_receipt).collect(); + .ok_or(eyre::eyre!(ExecutionError::NoReceiptsForBlock(tag)))?; + let receipts_encoded = receipts.iter().map(N::encode_receipt).collect::>(); let expected_receipt_root = ordered_trie_root(receipts_encoded); let expected_receipt_root = B256::from_slice(&expected_receipt_root.to_fixed_bytes()); @@ -327,39 +326,40 @@ impl> ExecutionClient { } async fn verify_logs(&self, logs: &[Log]) -> Result<()> { - // Collect all (unique) tx hashes - let txs_hash = logs + // Collect all (unique) block numbers + let block_nums = logs .iter() .map(|log| { - log.transaction_hash - .ok_or(eyre::eyre!("tx hash not found in log")) + log.block_number + .ok_or_else(|| eyre::eyre!("block num not found in log")) }) .collect::, _>>()?; - // Collect all (proven) tx receipts as a map of tx hash to receipt - // TODO: use get_block_receipts instead to reduce the number of RPC calls? - let receipts_fut = txs_hash.iter().map(|&tx_hash| async move { - let receipt = self.get_transaction_receipt(tx_hash).await; - receipt?.map(|r| (tx_hash, r)).ok_or(eyre::eyre!( - ExecutionError::NoReceiptForTransaction(tx_hash) - )) + // Collect all (proven) tx receipts for all block numbers + let blocks_receipts_fut = block_nums.into_iter().map(|block_num| async move { + let tag = BlockTag::Number(block_num); + let receipts = self.get_block_receipts(tag).await; + receipts?.ok_or_else(|| eyre::eyre!(ExecutionError::NoReceiptsForBlock(tag))) }); - let receipts = join_all(receipts_fut).await; - let receipts: HashMap<_, _> = receipts.into_iter().collect::>()?; + let blocks_receipts = try_join_all(blocks_receipts_fut).await?; + let receipts = blocks_receipts.into_iter().flatten().collect::>(); // Map tx hashes to encoded logs - let receipts_logs_encoded: HashMap<_, _> = receipts - .iter() - .map(|(tx_hash, receipt)| { - let encoded_logs = N::receipt_logs(&receipt) - .iter() - .map(|l| encode(&l.inner)) - .collect::>(); - (tx_hash, encoded_logs) + let receipts_logs_encoded = receipts + .into_iter() + .filter_map(|receipt| { + let logs = N::receipt_logs(&receipt); + if logs.is_empty() { + None + } else { + let tx_hash = logs[0].transaction_hash.unwrap(); + let encoded_logs = logs.iter().map(|l| encode(&l.inner)).collect::>(); + Some((tx_hash, encoded_logs)) + } }) - .collect(); + .collect::>(); - for log in logs.iter() { + for log in logs { // Check if the receipt contains the desired log // Encoding logs for comparison let tx_hash = log.transaction_hash.unwrap();