Skip to content

Commit 07bca4f

Browse files
authored
perf(engine): only recover senders once (#20118)
1 parent 9e1b247 commit 07bca4f

File tree

10 files changed

+115
-143
lines changed

10 files changed

+115
-143
lines changed

crates/chain-state/src/test_utils.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
9292
&mut self,
9393
number: BlockNumber,
9494
parent_hash: B256,
95-
) -> RecoveredBlock<reth_ethereum_primitives::Block> {
95+
) -> SealedBlock<reth_ethereum_primitives::Block> {
9696
let mut rng = rand::rng();
9797

9898
let mock_tx = |nonce: u64| -> Recovered<_> {
@@ -167,17 +167,14 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
167167
..Default::default()
168168
};
169169

170-
let block = SealedBlock::from_sealed_parts(
170+
SealedBlock::from_sealed_parts(
171171
SealedHeader::seal_slow(header),
172172
BlockBody {
173173
transactions: transactions.into_iter().map(|tx| tx.into_inner()).collect(),
174174
ommers: Vec::new(),
175175
withdrawals: Some(vec![].into()),
176176
},
177-
);
178-
179-
RecoveredBlock::try_recover_sealed_with_senders(block, vec![self.signer; num_txs as usize])
180-
.unwrap()
177+
)
181178
}
182179

183180
/// Creates a fork chain with the given base block.
@@ -191,7 +188,9 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
191188

192189
for _ in 0..length {
193190
let block = self.generate_random_block(parent.number + 1, parent.hash());
194-
parent = block.clone_sealed_block();
191+
parent = block.clone();
192+
let senders = vec![self.signer; block.body().transactions.len()];
193+
let block = block.with_senders(senders);
195194
fork.push(block);
196195
}
197196

@@ -205,9 +204,8 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
205204
receipts: Vec<Vec<Receipt>>,
206205
parent_hash: B256,
207206
) -> ExecutedBlock {
208-
let block_with_senders = self.generate_random_block(block_number, parent_hash);
209-
210-
let (block, senders) = block_with_senders.split_sealed();
207+
let block = self.generate_random_block(block_number, parent_hash);
208+
let senders = vec![self.signer; block.body().transactions.len()];
211209
let trie_data = ComputedTrieData::default();
212210
ExecutedBlock::new(
213211
Arc::new(RecoveredBlock::new_sealed(block, senders)),

crates/engine/tree/src/download.rs

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use reth_network_p2p::{
99
full_block::{FetchFullBlockFuture, FetchFullBlockRangeFuture, FullBlockClient},
1010
BlockClient,
1111
};
12-
use reth_primitives_traits::{Block, RecoveredBlock, SealedBlock};
12+
use reth_primitives_traits::{Block, SealedBlock};
1313
use std::{
1414
cmp::{Ordering, Reverse},
1515
collections::{binary_heap::PeekMut, BinaryHeap, HashSet, VecDeque},
@@ -44,7 +44,7 @@ pub enum DownloadAction {
4444
#[derive(Debug)]
4545
pub enum DownloadOutcome<B: Block> {
4646
/// Downloaded blocks.
47-
Blocks(Vec<RecoveredBlock<B>>),
47+
Blocks(Vec<SealedBlock<B>>),
4848
/// New download started.
4949
NewDownloadStarted {
5050
/// How many blocks are pending in this download.
@@ -68,7 +68,7 @@ where
6868
inflight_block_range_requests: Vec<FetchFullBlockRangeFuture<Client>>,
6969
/// Buffered blocks from downloads - this is a min-heap of blocks, using the block number for
7070
/// ordering. This means the blocks will be popped from the heap with ascending block numbers.
71-
set_buffered_blocks: BinaryHeap<Reverse<OrderedRecoveredBlock<B>>>,
71+
set_buffered_blocks: BinaryHeap<Reverse<OrderedSealedBlock<B>>>,
7272
/// Engine download metrics.
7373
metrics: BlockDownloaderMetrics,
7474
/// Pending events to be emitted.
@@ -226,15 +226,8 @@ where
226226
let mut request = self.inflight_block_range_requests.swap_remove(idx);
227227
if let Poll::Ready(blocks) = request.poll_unpin(cx) {
228228
trace!(target: "engine::download", len=?blocks.len(), first=?blocks.first().map(|b| b.num_hash()), last=?blocks.last().map(|b| b.num_hash()), "Received full block range, buffering");
229-
self.set_buffered_blocks.extend(
230-
blocks
231-
.into_iter()
232-
.map(|b| {
233-
let senders = b.senders().unwrap_or_default();
234-
OrderedRecoveredBlock(RecoveredBlock::new_sealed(b, senders))
235-
})
236-
.map(Reverse),
237-
);
229+
self.set_buffered_blocks
230+
.extend(blocks.into_iter().map(OrderedSealedBlock).map(Reverse));
238231
} else {
239232
// still pending
240233
self.inflight_block_range_requests.push(request);
@@ -248,8 +241,7 @@ where
248241
}
249242

250243
// drain all unique element of the block buffer if there are any
251-
let mut downloaded_blocks: Vec<RecoveredBlock<B>> =
252-
Vec::with_capacity(self.set_buffered_blocks.len());
244+
let mut downloaded_blocks = Vec::with_capacity(self.set_buffered_blocks.len());
253245
while let Some(block) = self.set_buffered_blocks.pop() {
254246
// peek ahead and pop duplicates
255247
while let Some(peek) = self.set_buffered_blocks.peek_mut() {
@@ -265,32 +257,31 @@ where
265257
}
266258
}
267259

268-
/// A wrapper type around [`RecoveredBlock`] that implements the [Ord]
260+
/// A wrapper type around [`SealedBlock`] that implements the [Ord]
269261
/// trait by block number.
270262
#[derive(Debug, Clone, PartialEq, Eq)]
271-
struct OrderedRecoveredBlock<B: Block>(RecoveredBlock<B>);
263+
struct OrderedSealedBlock<B: Block>(SealedBlock<B>);
272264

273-
impl<B: Block> PartialOrd for OrderedRecoveredBlock<B> {
265+
impl<B: Block> PartialOrd for OrderedSealedBlock<B> {
274266
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
275267
Some(self.cmp(other))
276268
}
277269
}
278270

279-
impl<B: Block> Ord for OrderedRecoveredBlock<B> {
271+
impl<B: Block> Ord for OrderedSealedBlock<B> {
280272
fn cmp(&self, other: &Self) -> Ordering {
281273
self.0.number().cmp(&other.0.number())
282274
}
283275
}
284276

285-
impl<B: Block> From<SealedBlock<B>> for OrderedRecoveredBlock<B> {
277+
impl<B: Block> From<SealedBlock<B>> for OrderedSealedBlock<B> {
286278
fn from(block: SealedBlock<B>) -> Self {
287-
let senders = block.senders().unwrap_or_default();
288-
Self(RecoveredBlock::new_sealed(block, senders))
279+
Self(block)
289280
}
290281
}
291282

292-
impl<B: Block> From<OrderedRecoveredBlock<B>> for RecoveredBlock<B> {
293-
fn from(value: OrderedRecoveredBlock<B>) -> Self {
283+
impl<B: Block> From<OrderedSealedBlock<B>> for SealedBlock<B> {
284+
fn from(value: OrderedSealedBlock<B>) -> Self {
294285
value.0
295286
}
296287
}

crates/engine/tree/src/engine.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use reth_chain_state::ExecutedBlock;
1111
use reth_engine_primitives::{BeaconEngineMessage, ConsensusEngineEvent};
1212
use reth_ethereum_primitives::EthPrimitives;
1313
use reth_payload_primitives::PayloadTypes;
14-
use reth_primitives_traits::{Block, NodePrimitives, RecoveredBlock};
14+
use reth_primitives_traits::{Block, NodePrimitives, SealedBlock};
1515
use std::{
1616
collections::HashSet,
1717
fmt::Display,
@@ -307,7 +307,7 @@ pub enum FromEngine<Req, B: Block> {
307307
/// Request from the engine.
308308
Request(Req),
309309
/// Downloaded blocks from the network.
310-
DownloadedBlocks(Vec<RecoveredBlock<B>>),
310+
DownloadedBlocks(Vec<SealedBlock<B>>),
311311
}
312312

313313
impl<Req: Display, B: Block> Display for FromEngine<Req, B> {

crates/engine/tree/src/tree/block_buffer.rs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::tree::metrics::BlockBufferMetrics;
22
use alloy_consensus::BlockHeader;
33
use alloy_primitives::{BlockHash, BlockNumber};
4-
use reth_primitives_traits::{Block, RecoveredBlock};
4+
use reth_primitives_traits::{Block, SealedBlock};
55
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
66

77
/// Contains the tree of pending blocks that cannot be executed due to missing parent.
@@ -18,7 +18,7 @@ use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
1818
#[derive(Debug)]
1919
pub struct BlockBuffer<B: Block> {
2020
/// All blocks in the buffer stored by their block hash.
21-
pub(crate) blocks: HashMap<BlockHash, RecoveredBlock<B>>,
21+
pub(crate) blocks: HashMap<BlockHash, SealedBlock<B>>,
2222
/// Map of any parent block hash (even the ones not currently in the buffer)
2323
/// to the buffered children.
2424
/// Allows connecting buffered blocks by parent.
@@ -49,12 +49,12 @@ impl<B: Block> BlockBuffer<B> {
4949
}
5050

5151
/// Return reference to the requested block.
52-
pub fn block(&self, hash: &BlockHash) -> Option<&RecoveredBlock<B>> {
52+
pub fn block(&self, hash: &BlockHash) -> Option<&SealedBlock<B>> {
5353
self.blocks.get(hash)
5454
}
5555

5656
/// Return a reference to the lowest ancestor of the given block in the buffer.
57-
pub fn lowest_ancestor(&self, hash: &BlockHash) -> Option<&RecoveredBlock<B>> {
57+
pub fn lowest_ancestor(&self, hash: &BlockHash) -> Option<&SealedBlock<B>> {
5858
let mut current_block = self.blocks.get(hash)?;
5959
while let Some(parent) = self.blocks.get(&current_block.parent_hash()) {
6060
current_block = parent;
@@ -63,7 +63,7 @@ impl<B: Block> BlockBuffer<B> {
6363
}
6464

6565
/// Insert a correct block inside the buffer.
66-
pub fn insert_block(&mut self, block: RecoveredBlock<B>) {
66+
pub fn insert_block(&mut self, block: SealedBlock<B>) {
6767
let hash = block.hash();
6868

6969
self.parent_to_child.entry(block.parent_hash()).or_default().insert(hash);
@@ -87,10 +87,7 @@ impl<B: Block> BlockBuffer<B> {
8787
///
8888
/// Note: that order of returned blocks is important and the blocks with lower block number
8989
/// in the chain will come first so that they can be executed in the correct order.
90-
pub fn remove_block_with_children(
91-
&mut self,
92-
parent_hash: &BlockHash,
93-
) -> Vec<RecoveredBlock<B>> {
90+
pub fn remove_block_with_children(&mut self, parent_hash: &BlockHash) -> Vec<SealedBlock<B>> {
9491
let removed = self
9592
.remove_block(parent_hash)
9693
.into_iter()
@@ -149,7 +146,7 @@ impl<B: Block> BlockBuffer<B> {
149146
/// This method will only remove the block if it's present inside `self.blocks`.
150147
/// The block might be missing from other collections, the method will only ensure that it has
151148
/// been removed.
152-
fn remove_block(&mut self, hash: &BlockHash) -> Option<RecoveredBlock<B>> {
149+
fn remove_block(&mut self, hash: &BlockHash) -> Option<SealedBlock<B>> {
153150
let block = self.blocks.remove(hash)?;
154151
self.remove_from_earliest_blocks(block.number(), hash);
155152
self.remove_from_parent(block.parent_hash(), hash);
@@ -158,7 +155,7 @@ impl<B: Block> BlockBuffer<B> {
158155
}
159156

160157
/// Remove all children and their descendants for the given blocks and return them.
161-
fn remove_children(&mut self, parent_hashes: Vec<BlockHash>) -> Vec<RecoveredBlock<B>> {
158+
fn remove_children(&mut self, parent_hashes: Vec<BlockHash>) -> Vec<SealedBlock<B>> {
162159
// remove all parent child connection and all the child children blocks that are connected
163160
// to the discarded parent blocks.
164161
let mut remove_parent_children = parent_hashes;
@@ -184,7 +181,6 @@ mod tests {
184181
use super::*;
185182
use alloy_eips::BlockNumHash;
186183
use alloy_primitives::BlockHash;
187-
use reth_primitives_traits::RecoveredBlock;
188184
use reth_testing_utils::generators::{self, random_block, BlockParams, Rng};
189185
use std::collections::HashMap;
190186

@@ -193,10 +189,8 @@ mod tests {
193189
rng: &mut R,
194190
number: u64,
195191
parent: BlockHash,
196-
) -> RecoveredBlock<reth_ethereum_primitives::Block> {
197-
let block =
198-
random_block(rng, number, BlockParams { parent: Some(parent), ..Default::default() });
199-
block.try_recover().unwrap()
192+
) -> SealedBlock<reth_ethereum_primitives::Block> {
193+
random_block(rng, number, BlockParams { parent: Some(parent), ..Default::default() })
200194
}
201195

202196
/// Assert that all buffer collections have the same data length.
@@ -216,7 +210,7 @@ mod tests {
216210
/// Assert that the block was removed from all buffer collections.
217211
fn assert_block_removal<B: Block>(
218212
buffer: &BlockBuffer<B>,
219-
block: &RecoveredBlock<reth_ethereum_primitives::Block>,
213+
block: &SealedBlock<reth_ethereum_primitives::Block>,
220214
) {
221215
assert!(!buffer.blocks.contains_key(&block.hash()));
222216
assert!(buffer

crates/engine/tree/src/tree/metrics.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use reth_metrics::{
1717
use reth_primitives_traits::SignedTransaction;
1818
use reth_trie::updates::TrieUpdates;
1919
use revm::database::{states::bundle_state::BundleRetention, State};
20+
use revm_primitives::Address;
2021
use std::time::Instant;
2122
use tracing::{debug_span, trace};
2223

@@ -64,7 +65,7 @@ impl EngineApiMetrics {
6465
executor: E,
6566
transactions: impl Iterator<Item = Result<impl ExecutableTx<E>, BlockExecutionError>>,
6667
state_hook: Box<dyn OnStateHook>,
67-
) -> Result<BlockExecutionOutput<E::Receipt>, BlockExecutionError>
68+
) -> Result<(BlockExecutionOutput<E::Receipt>, Vec<Address>), BlockExecutionError>
6869
where
6970
DB: alloy_evm::Database,
7071
E: BlockExecutor<Evm: Evm<DB: BorrowMut<State<DB>>>, Transaction: SignedTransaction>,
@@ -74,6 +75,7 @@ impl EngineApiMetrics {
7475
// be accessible.
7576
let wrapper = MeteredStateHook { metrics: self.executor.clone(), inner_hook: state_hook };
7677

78+
let mut senders = Vec::new();
7779
let mut executor = executor.with_state_hook(Some(Box::new(wrapper)));
7880

7981
let f = || {
@@ -84,6 +86,7 @@ impl EngineApiMetrics {
8486
debug_span!(target: "engine::tree", "execute tx", tx_hash=?tx.tx().tx_hash());
8587
let enter = span.entered();
8688
trace!(target: "engine::tree", "Executing transaction");
89+
senders.push(*tx.signer());
8790
let gas_used = executor.execute_transaction(tx)?;
8891

8992
// record the tx gas used
@@ -113,7 +116,7 @@ impl EngineApiMetrics {
113116
self.executor.storage_slots_updated_histogram.record(storage_slots as f64);
114117
self.executor.bytecodes_updated_histogram.record(bytecodes as f64);
115118

116-
Ok(output)
119+
Ok((output, senders))
117120
}
118121
}
119122

0 commit comments

Comments
 (0)