mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
fix(poststate): duplicate receipts (#2632)
This commit is contained in:
@ -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>>();
|
||||
|
||||
@ -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)?;
|
||||
|
||||
@ -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(),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user