diff --git a/crates/consensus/auto-seal/src/task.rs b/crates/consensus/auto-seal/src/task.rs index ea1ee693a..322a2aee7 100644 --- a/crates/consensus/auto-seal/src/task.rs +++ b/crates/consensus/auto-seal/src/task.rs @@ -178,11 +178,11 @@ where // clear all transactions from pool pool.remove_transactions(body.iter().map(|tx| tx.hash())); - header.receipts_root = if post_state.receipts().is_empty() { + let receipts = post_state.receipts(header.number); + header.receipts_root = if receipts.is_empty() { EMPTY_RECEIPTS } else { - let receipts_with_bloom = post_state - .receipts() + let receipts_with_bloom = receipts .iter() .map(|r| r.clone().into()) .collect::>(); diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 078779b49..3d532937c 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -622,12 +622,15 @@ fn build_payload( cumulative_gas_used += gas_used; // Push transaction changeset and calculate header bloom filter for receipt. - post_state.add_receipt(Receipt { - tx_type: tx.tx_type(), - success: result.is_success(), - cumulative_gas_used, - logs: result.logs().into_iter().map(into_reth_log).collect(), - }); + post_state.add_receipt( + block_number, + Receipt { + tx_type: tx.tx_type(), + success: result.is_success(), + cumulative_gas_used, + logs: result.logs().into_iter().map(into_reth_log).collect(), + }, + ); // update add to total fees let miner_fee = tx @@ -654,8 +657,8 @@ fn build_payload( attributes.withdrawals, )?; - let receipts_root = post_state.receipts_root(); - let logs_bloom = post_state.logs_bloom(); + let receipts_root = post_state.receipts_root(block_number); + let logs_bloom = post_state.logs_bloom(block_number); // calculate the state root let state_root = db.db.0.state_root(post_state)?; diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index cfc58bae4..9dc6dd72f 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -223,7 +223,7 @@ where self.init_env(&block.header, total_difficulty); let mut cumulative_gas_used = 0; - let mut post_state = PostState::with_tx_capacity(block.body.len()); + let mut post_state = PostState::with_tx_capacity(block.number, block.body.len()); for (transaction, sender) in block.body.iter().zip(senders.into_iter()) { // The sum of the transaction’s gas limit, Tg, and the gas utilised in this block prior, // must be no greater than the block’s gasLimit. @@ -249,15 +249,18 @@ where cumulative_gas_used += result.gas_used(); // Push transaction changeset and calculate header bloom filter for receipt. - post_state.add_receipt(Receipt { - tx_type: transaction.tx_type(), - // Success flag was added in `EIP-658: Embedding transaction status code in - // receipts`. - success: result.is_success(), - cumulative_gas_used, - // convert to reth log - logs: result.into_logs().into_iter().map(into_reth_log).collect(), - }); + post_state.add_receipt( + block.number, + Receipt { + tx_type: transaction.tx_type(), + // Success flag was added in `EIP-658: Embedding transaction status code in + // receipts`. + success: result.is_success(), + cumulative_gas_used, + // convert to reth log + logs: result.into_logs().into_iter().map(into_reth_log).collect(), + }, + ); } Ok((post_state, cumulative_gas_used)) @@ -311,7 +314,7 @@ where verify_receipt( block.header.receipts_root, block.header.logs_bloom, - post_state.receipts().iter(), + post_state.receipts(block.number).iter(), )?; } diff --git a/crates/rpc/rpc/src/eth/cache.rs b/crates/rpc/rpc/src/eth/cache.rs index 2b3f21a16..6cb63e6ad 100644 --- a/crates/rpc/rpc/src/eth/cache.rs +++ b/crates/rpc/rpc/src/eth/cache.rs @@ -505,7 +505,7 @@ where if blocks.len() == 1 { let block_receipts = BlockReceipts { block_hash: blocks[0].hash, - receipts: state.receipts().to_vec(), + receipts: state.receipts(blocks[0].number).to_vec(), }; receipts.push(block_receipts); } diff --git a/crates/storage/provider/src/chain.rs b/crates/storage/provider/src/chain.rs index d98aa7728..ae088273e 100644 --- a/crates/storage/provider/src/chain.rs +++ b/crates/storage/provider/src/chain.rs @@ -131,15 +131,15 @@ impl Chain { /// Attachment includes block number, block hash, transaction hash and transaction index. pub fn receipts_with_attachment(&self) -> Vec { let mut receipt_attch = Vec::new(); - let mut receipts = self.state().receipts().iter(); for (block_num, block) in self.blocks().iter() { - let block_num_hash = BlockNumHash::new(*block_num, block.hash()); + let mut receipts = self.state.receipts(*block_num).iter(); let mut tx_receipts = Vec::new(); for tx in block.body.iter() { if let Some(receipt) = receipts.next() { tx_receipts.push((tx.hash(), receipt.clone())); } } + let block_num_hash = BlockNumHash::new(*block_num, block.hash()); receipt_attch.push(BlockReceipts { block: block_num_hash, tx_receipts }); } receipt_attch diff --git a/crates/storage/provider/src/post_state/mod.rs b/crates/storage/provider/src/post_state/mod.rs index 7ee9d5777..e5b965bbc 100644 --- a/crates/storage/provider/src/post_state/mod.rs +++ b/crates/storage/provider/src/post_state/mod.rs @@ -77,7 +77,7 @@ pub struct PostState { /// New code created during the execution bytecode: BTreeMap, /// The receipt(s) of the executed transaction(s). - receipts: Vec, + receipts: BTreeMap>, } impl PostState { @@ -87,8 +87,8 @@ impl PostState { } /// Create an empty [PostState] with pre-allocated space for a certain amount of transactions. - pub fn with_tx_capacity(txs: usize) -> Self { - Self { receipts: Vec::with_capacity(txs), ..Default::default() } + pub fn with_tx_capacity(block: BlockNumber, txs: usize) -> Self { + Self { receipts: BTreeMap::from([(block, Vec::with_capacity(txs))]), ..Default::default() } } /// Return the current size of the poststate. @@ -150,25 +150,25 @@ impl PostState { } /// Get the receipts for the transactions executed to form this [PostState]. - pub fn receipts(&self) -> &[Receipt] { - &self.receipts + pub fn receipts(&self, block: BlockNumber) -> &[Receipt] { + self.receipts.get(&block).map(Vec::as_slice).unwrap_or(&[]) } /// Returns an iterator over all logs in this [PostState]. - pub fn logs(&self) -> impl Iterator + '_ { - self.receipts().iter().flat_map(|r| r.logs.iter()) + pub fn logs(&self, block: BlockNumber) -> impl Iterator { + self.receipts(block).iter().flat_map(|r| r.logs.iter()) } /// Returns the logs bloom for all recorded logs. - pub fn logs_bloom(&self) -> Bloom { - logs_bloom(self.logs()) + pub fn logs_bloom(&self, block: BlockNumber) -> Bloom { + logs_bloom(self.logs(block)) } /// 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()) + pub fn receipts_root(&self, block: BlockNumber) -> H256 { + calculate_receipt_root_ref(self.receipts(block)) } /// Hash all changed accounts and storage entries that are currently stored in the post state. @@ -298,6 +298,9 @@ impl PostState { }); } } + + // Revert receipts + self.receipts.retain(|block_number, _| *block_number <= target_block_number); } /// Reverts each change up to and including any change that is part of `transition_id`. @@ -322,6 +325,7 @@ impl PostState { // Remove all changes in the returned post-state that were not reverted non_reverted_state.storage_changes.retain_above(revert_to_block); non_reverted_state.account_changes.retain_above(revert_to_block); + non_reverted_state.receipts.retain(|block_number, _| *block_number > revert_to_block); non_reverted_state } @@ -400,8 +404,8 @@ impl PostState { /// Add a transaction receipt to the post-state. /// /// Transactions should always include their receipts in the post-state. - pub fn add_receipt(&mut self, receipt: Receipt) { - self.receipts.push(receipt); + pub fn add_receipt(&mut self, block: BlockNumber, receipt: Receipt) { + self.receipts.entry(block).or_default().push(receipt); } /// Write changeset history to the database. @@ -507,17 +511,15 @@ impl PostState { } // Write the receipts of the transactions + let mut bodies_cursor = tx.cursor_read::()?; let mut receipts_cursor = tx.cursor_write::()?; - let mut next_tx_num = - if let Some(last_tx) = receipts_cursor.last()?.map(|(tx_num, _)| tx_num) { - last_tx + 1 - } else { - // The very first tx - 0 - }; - for receipt in self.receipts.into_iter() { - receipts_cursor.append(next_tx_num, receipt)?; - next_tx_num += 1; + for (block, receipts) in self.receipts { + let (_, body_indices) = bodies_cursor.seek_exact(block)?.expect("body indices exist"); + let tx_range = body_indices.tx_num_range(); + assert_eq!(receipts.len(), tx_range.clone().count(), "Receipt length mismatch"); + for (tx_num, receipt) in tx_range.zip(receipts) { + receipts_cursor.append(tx_num, receipt)?; + } } Ok(()) @@ -841,6 +843,23 @@ mod tests { ); } + #[test] + fn receipts_split_at() { + let mut state = PostState::new(); + (1..=4).for_each(|block| { + state.add_receipt(block, Receipt::default()); + }); + let state2 = state.split_at(2); + assert_eq!( + state.receipts, + BTreeMap::from([(1, vec![Receipt::default()]), (2, vec![Receipt::default()])]) + ); + assert_eq!( + state2.receipts, + BTreeMap::from([(3, vec![Receipt::default()]), (4, vec![Receipt::default()])]) + ); + } + #[test] fn wiped_revert() { let address = Address::random(); diff --git a/crates/storage/provider/src/test_utils/blocks.rs b/crates/storage/provider/src/test_utils/blocks.rs index 8c57b7a79..a9349698c 100644 --- a/crates/storage/provider/src/test_utils/blocks.rs +++ b/crates/storage/provider/src/test_utils/blocks.rs @@ -112,16 +112,19 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, PostState) { BTreeMap::from([(U256::from(5), (U256::ZERO, U256::from(10)))]), ); - post_state.add_receipt(Receipt { - tx_type: TxType::EIP2930, - success: true, - cumulative_gas_used: 300, - logs: vec![Log { - address: H160([0x60; 20]), - topics: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2)], - data: Bytes::default(), - }], - }); + post_state.add_receipt( + number, + Receipt { + tx_type: TxType::EIP2930, + success: true, + cumulative_gas_used: 300, + logs: vec![Log { + address: H160([0x60; 20]), + topics: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2)], + data: Bytes::default(), + }], + }, + ); (SealedBlockWithSenders { block, senders: vec![H160([0x30; 20])] }, post_state) } @@ -152,16 +155,19 @@ fn block2(number: BlockNumber, parent_hash: H256) -> (SealedBlockWithSenders, Po H160([0x60; 20]), BTreeMap::from([(U256::from(5), (U256::from(10), U256::from(15)))]), ); - post_state.add_receipt(Receipt { - tx_type: TxType::EIP1559, - success: false, - cumulative_gas_used: 400, - logs: vec![Log { - address: H160([0x61; 20]), - topics: vec![H256::from_low_u64_be(3), H256::from_low_u64_be(4)], - data: Bytes::default(), - }], - }); + post_state.add_receipt( + number, + Receipt { + tx_type: TxType::EIP1559, + success: false, + cumulative_gas_used: 400, + logs: vec![Log { + address: H160([0x61; 20]), + topics: vec![H256::from_low_u64_be(3), H256::from_low_u64_be(4)], + data: Bytes::default(), + }], + }, + ); (SealedBlockWithSenders { block, senders: vec![H160([0x31; 20])] }, post_state) } diff --git a/crates/storage/provider/src/transaction.rs b/crates/storage/provider/src/transaction.rs index e74223e61..1de582394 100644 --- a/crates/storage/provider/src/transaction.rs +++ b/crates/storage/provider/src/transaction.rs @@ -496,9 +496,6 @@ where let new_tip = blocks.last().unwrap(); let new_tip_number = new_tip.number; - // Write state and changesets to the database - state.write_to_db(self.deref_mut())?; - let first_number = blocks.first().unwrap().number; let last = blocks.last().unwrap(); @@ -511,6 +508,10 @@ where self.insert_block(block)?; } + // Write state and changesets to the database. + // Must be written after blocks because of the receipt lookup. + state.write_to_db(self.deref_mut())?; + self.insert_hashes(first_number..=last_block_number, last_block_hash, expected_state_root)?; // Update pipeline progress @@ -953,7 +954,10 @@ where for (block_number, block_body) in block_bodies.into_iter() { for _ in block_body.tx_num_range() { if let Some((_, receipt)) = receipt_iter.next() { - block_states.entry(block_number).or_default().add_receipt(receipt); + block_states + .entry(block_number) + .or_default() + .add_receipt(block_number, receipt); } } }