From 5c3e45cd6b00f42e456a41909ced81717b12b589 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 22 Jan 2026 11:52:56 +0100 Subject: [PATCH] fix: handle incomplete receipts gracefully in receipt root task (#21285) --- .../payload_processor/receipt_root_task.rs | 19 ++++++++++++++----- .../engine/tree/src/tree/payload_validator.rs | 14 +++++++++----- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/crates/engine/tree/src/tree/payload_processor/receipt_root_task.rs b/crates/engine/tree/src/tree/payload_processor/receipt_root_task.rs index c9e53a11b0..993a57326d 100644 --- a/crates/engine/tree/src/tree/payload_processor/receipt_root_task.rs +++ b/crates/engine/tree/src/tree/payload_processor/receipt_root_task.rs @@ -64,14 +64,11 @@ impl ReceiptRootTaskHandle { /// /// * `receipts_len` - The total number of receipts expected. This is needed to correctly order /// the trie keys according to RLP encoding rules. - /// - /// # Panics - /// - /// Panics if the number of receipts received doesn't match `receipts_len`. pub fn run(self, receipts_len: usize) { let mut builder = OrderedTrieRootEncodedBuilder::new(receipts_len); let mut aggregated_bloom = Bloom::ZERO; let mut encode_buf = Vec::new(); + let mut received_count = 0usize; for indexed_receipt in self.receipt_rx { let receipt_with_bloom = indexed_receipt.receipt.with_bloom_ref(); @@ -81,9 +78,21 @@ impl ReceiptRootTaskHandle { aggregated_bloom |= *receipt_with_bloom.bloom_ref(); builder.push_unchecked(indexed_receipt.index, &encode_buf); + received_count += 1; } - let root = builder.finalize().expect("receipt root builder incomplete"); + let Ok(root) = builder.finalize() else { + // Finalize fails if we didn't receive exactly `receipts_len` receipts. This can + // happen if execution was aborted early (e.g., invalid transaction encountered). + // We return without sending a result, allowing the caller to handle the abort. + tracing::error!( + target: "engine::tree::payload_processor", + expected = receipts_len, + received = received_count, + "Receipt root task received incomplete receipts, execution likely aborted" + ); + return; + }; let _ = self.result_tx.send((root, aggregated_bloom)); } } diff --git a/crates/engine/tree/src/tree/payload_validator.rs b/crates/engine/tree/src/tree/payload_validator.rs index e02fb2f9e4..138fad5b16 100644 --- a/crates/engine/tree/src/tree/payload_validator.rs +++ b/crates/engine/tree/src/tree/payload_validator.rs @@ -479,11 +479,15 @@ where let block = self.convert_to_block(input)?.with_senders(senders); // Wait for the receipt root computation to complete. - let receipt_root_bloom = Some( - receipt_root_rx - .blocking_recv() - .expect("receipt root task dropped sender without result"), - ); + let receipt_root_bloom = receipt_root_rx + .blocking_recv() + .inspect_err(|_| { + tracing::error!( + target: "engine::tree::payload_validator", + "Receipt root task dropped sender without result, receipt root calculation likely aborted" + ); + }) + .ok(); let hashed_state = ensure_ok_post_block!( self.validate_post_execution(