fix(poststate): duplicate receipts (#2632)

This commit is contained in:
Roman Krasiuk
2023-05-12 20:31:01 +03:00
committed by GitHub
parent 047f1e513c
commit 8c1a1e0e06
8 changed files with 107 additions and 72 deletions

View File

@ -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::<Vec<ReceiptWithBloom>>();

View File

@ -622,12 +622,15 @@ fn build_payload<Pool, Client>(
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<Pool, Client>(
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)?;

View File

@ -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 transactions gas limit, Tg, and the gas utilised in this block prior,
// must be no greater than the blocks 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(),
)?;
}

View File

@ -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);
}

View File

@ -131,15 +131,15 @@ impl Chain {
/// Attachment includes block number, block hash, transaction hash and transaction index.
pub fn receipts_with_attachment(&self) -> Vec<BlockReceipts> {
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

View File

@ -77,7 +77,7 @@ pub struct PostState {
/// New code created during the execution
bytecode: BTreeMap<H256, Bytecode>,
/// The receipt(s) of the executed transaction(s).
receipts: Vec<Receipt>,
receipts: BTreeMap<BlockNumber, Vec<Receipt>>,
}
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<Item = &Log> + '_ {
self.receipts().iter().flat_map(|r| r.logs.iter())
pub fn logs(&self, block: BlockNumber) -> impl Iterator<Item = &Log> {
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::<tables::BlockBodyIndices>()?;
let mut receipts_cursor = tx.cursor_write::<tables::Receipts>()?;
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();

View File

@ -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)
}

View File

@ -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);
}
}
}