Skip to content

Commit 362c85f

Browse files
committed
feat: make metadata receipts optional
Receipts are almost unused other than returning the nonce of the from field for deposit transactions. This change makes the entire receipts field optional for flashblocks. This means that deposit transactions will not return a nonce from the RPC until the canonical block is processed. While this will change RPC behavior, it will decrease flashblock size significantly, and the tradeoff of size vs a few hundred ms latency to reading the nonce of a deposit transaction is easily worth it.
1 parent 54abd9f commit 362c85f

File tree

3 files changed

+74
-72
lines changed

3 files changed

+74
-72
lines changed

crates/flashblocks/src/processor.rs

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,9 @@ where
333333
.iter()
334334
.map(|flashblock| flashblock.metadata.receipts.clone())
335335
.fold(HashMap::default(), |mut acc, receipts| {
336-
acc.extend(receipts);
336+
if let Some(receipts) = receipts {
337+
acc.extend(receipts);
338+
}
337339
acc
338340
});
339341

@@ -407,16 +409,15 @@ where
407409
pending_blocks_builder.with_transaction_sender(tx_hash, sender);
408410
pending_blocks_builder.increment_nonce(sender);
409411

410-
let receipt = receipt_by_hash
411-
.get(&tx_hash)
412-
.cloned()
413-
.ok_or(eyre!("missing receipt for {:?}", tx_hash))?;
412+
let receipt = receipt_by_hash.get(&tx_hash).cloned();
414413

415414
let recovered_transaction = Recovered::new_unchecked(transaction.clone(), sender);
416415
let envelope = recovered_transaction.clone().convert::<OpTxEnvelope>();
417416

418417
// Build Transaction
419-
let (deposit_receipt_version, deposit_nonce) = if transaction.is_deposit() {
418+
let (deposit_receipt_version, deposit_nonce) = if transaction.is_deposit()
419+
&& let Some(receipt) = &receipt
420+
{
420421
let deposit_receipt = receipt
421422
.as_deposit_receipt()
422423
.ok_or(eyre!("deposit transaction, non deposit receipt"))?;
@@ -453,40 +454,43 @@ where
453454
pending_blocks_builder.with_transaction(rpc_txn);
454455

455456
// Receipt Generation
456-
let op_receipt = prev_pending_blocks
457-
.as_ref()
458-
.and_then(|pending_blocks| pending_blocks.get_receipt(tx_hash))
459-
.unwrap_or_else(|| {
460-
let meta = TransactionMeta {
461-
tx_hash,
462-
index: idx as u64,
463-
block_hash: header.hash(),
464-
block_number: block.number,
465-
base_fee: block.base_fee_per_gas,
466-
excess_blob_gas: block.excess_blob_gas,
467-
timestamp: block.timestamp,
468-
};
469-
470-
let input: ConvertReceiptInput<'_, OpPrimitives> = ConvertReceiptInput {
471-
receipt: receipt.clone(),
472-
tx: Recovered::new_unchecked(transaction, sender),
473-
gas_used: receipt.cumulative_gas_used() - gas_used,
474-
next_log_index,
475-
meta,
476-
};
477-
478-
OpReceiptBuilder::new(
479-
self.client.chain_spec().as_ref(),
480-
input,
481-
&mut l1_block_info,
482-
)
483-
.unwrap()
484-
.build()
485-
});
486-
487-
pending_blocks_builder.with_receipt(tx_hash, op_receipt);
488-
gas_used = receipt.cumulative_gas_used();
489-
next_log_index += receipt.logs().len();
457+
if let Some(receipt) = receipt {
458+
let op_receipt = prev_pending_blocks
459+
.as_ref()
460+
.and_then(|pending_blocks| pending_blocks.get_receipt(tx_hash))
461+
.unwrap_or_else(|| {
462+
let meta = TransactionMeta {
463+
tx_hash,
464+
index: idx as u64,
465+
block_hash: header.hash(),
466+
block_number: block.number,
467+
base_fee: block.base_fee_per_gas,
468+
excess_blob_gas: block.excess_blob_gas,
469+
timestamp: block.timestamp,
470+
};
471+
472+
let input: ConvertReceiptInput<'_, OpPrimitives> =
473+
ConvertReceiptInput {
474+
receipt: receipt.clone(),
475+
tx: Recovered::new_unchecked(transaction, sender),
476+
gas_used: receipt.cumulative_gas_used() - gas_used,
477+
next_log_index,
478+
meta,
479+
};
480+
481+
OpReceiptBuilder::new(
482+
self.client.chain_spec().as_ref(),
483+
input,
484+
&mut l1_block_info,
485+
)
486+
.unwrap()
487+
.build()
488+
});
489+
490+
pending_blocks_builder.with_receipt(tx_hash, op_receipt);
491+
gas_used = receipt.cumulative_gas_used();
492+
next_log_index += receipt.logs().len();
493+
}
490494

491495
let mut should_execute_transaction = true;
492496
if let Some(state) =

crates/flashblocks/tests/state.rs

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ impl TestHarness {
215215

216216
struct FlashblockBuilder<'a> {
217217
transactions: Vec<Bytes>,
218-
receipts: HashMap<B256, OpReceipt>,
218+
receipts: Option<HashMap<B256, OpReceipt>>,
219219
harness: &'a TestHarness,
220220
canonical_block_number: Option<BlockNumber>,
221221
index: u64,
@@ -226,7 +226,7 @@ impl<'a> FlashblockBuilder<'a> {
226226
Self {
227227
canonical_block_number: None,
228228
transactions: vec![L1_BLOCK_INFO_DEPOSIT_TX.clone()],
229-
receipts: {
229+
receipts: Some({
230230
let mut receipts = alloy_primitives::map::HashMap::default();
231231
receipts.insert(
232232
L1_BLOCK_INFO_DEPOSIT_TX_HASH,
@@ -241,7 +241,7 @@ impl<'a> FlashblockBuilder<'a> {
241241
}),
242242
);
243243
receipts
244-
},
244+
}),
245245
index: 0,
246246
harness,
247247
}
@@ -250,13 +250,13 @@ impl<'a> FlashblockBuilder<'a> {
250250
Self {
251251
canonical_block_number: None,
252252
transactions: Vec::new(),
253-
receipts: HashMap::default(),
253+
receipts: Some(HashMap::default()),
254254
harness,
255255
index,
256256
}
257257
}
258258

259-
fn with_receipts(&mut self, receipts: HashMap<B256, OpReceipt>) -> &mut Self {
259+
fn with_receipts(&mut self, receipts: Option<HashMap<B256, OpReceipt>>) -> &mut Self {
260260
self.receipts = receipts;
261261
self
262262
}
@@ -269,14 +269,16 @@ impl<'a> FlashblockBuilder<'a> {
269269
for txn in transactions.iter() {
270270
cumulative_gas_used = cumulative_gas_used + txn.gas_limit();
271271
self.transactions.push(txn.encoded_2718().into());
272-
self.receipts.insert(
273-
txn.hash().clone(),
274-
OpReceipt::Eip1559(Receipt {
275-
status: true.into(),
276-
cumulative_gas_used,
277-
logs: vec![],
278-
}),
279-
);
272+
if let Some(ref mut receipts) = self.receipts {
273+
receipts.insert(
274+
txn.hash().clone(),
275+
OpReceipt::Eip1559(Receipt {
276+
status: true.into(),
277+
cumulative_gas_used,
278+
logs: vec![],
279+
}),
280+
);
281+
}
280282
}
281283
self
282284
}
@@ -715,29 +717,25 @@ async fn test_nonce_uses_pending_canon_block_instead_of_latest() {
715717
}
716718

717719
#[tokio::test]
718-
async fn test_missing_receipts_will_not_process() {
720+
async fn test_metadata_receipts_are_optional() {
721+
// Test to ensure that receipts are optional in the metadata
722+
// and deposit receipts return None for nonce until the canonical block is processed
719723
let test = TestHarness::new().await;
720724

721-
test.send_flashblock(FlashblockBuilder::new_base(&test).build()).await;
725+
// Send a flashblock with no receipts (only deposit transaction)
726+
test.send_flashblock(FlashblockBuilder::new_base(&test).with_receipts(None).build()).await;
722727

723-
let current_block = test.flashblocks.get_pending_blocks().get_block(true);
724-
725-
test.send_flashblock(
726-
FlashblockBuilder::new(&test, 1)
727-
.with_transactions(vec![test.build_transaction_to_send_eth(
728-
User::Alice,
729-
User::Bob,
730-
100,
731-
)])
732-
.with_receipts(HashMap::default()) // Clear the receipts
733-
.build(),
734-
)
735-
.await;
728+
// Verify the block was created with the deposit transaction
729+
let pending_block =
730+
test.flashblocks.get_pending_blocks().get_block(true).expect("block should be created");
731+
assert_eq!(pending_block.transactions.len(), 1);
736732

737-
let pending_block = test.flashblocks.get_pending_blocks().get_block(true);
733+
// Check that the deposit transaction has None for deposit_nonce
734+
let deposit_tx = &pending_block.transactions.as_transactions().unwrap()[0];
735+
assert_eq!(deposit_tx.deposit_nonce, None, "deposit_nonce should be None when no receipts");
738736

739-
// When the flashblock is invalid, the chain doesn't progress
740-
assert_eq!(pending_block.unwrap().hash(), current_block.unwrap().hash());
737+
// Canonical blocks trivially have the deposit_nonce since they are handled by canonical RPC
738+
// logic.
741739
}
742740

743741
#[tokio::test]

crates/flashtypes/src/metadata.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
88
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Default)]
99
pub struct Metadata {
1010
/// Transaction receipts indexed by hash.
11-
pub receipts: HashMap<B256, OpReceipt>,
11+
pub receipts: Option<HashMap<B256, OpReceipt>>,
1212
/// Updated account balances.
1313
pub new_account_balances: HashMap<Address, U256>,
1414
/// Block number this flashblock belongs to.

0 commit comments

Comments
 (0)