feat(primitives): add Receipts to encapsulate Vec<Vec<Option<Receipt>> (#4626)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Thomas Coratger
2023-09-27 20:11:19 +02:00
committed by GitHub
parent 61a1ac95ad
commit 2138a8b587
11 changed files with 153 additions and 70 deletions

View File

@ -6,7 +6,9 @@ use reth_db::{
transaction::{DbTx, DbTxMut},
};
use reth_interfaces::{db::DatabaseError, RethError};
use reth_primitives::{stage::StageId, Account, Bytecode, ChainSpec, StorageEntry, H256, U256};
use reth_primitives::{
stage::StageId, Account, Bytecode, ChainSpec, Receipts, StorageEntry, H256, U256,
};
use reth_provider::{
bundle_state::{BundleStateInit, RevertsInit},
BundleStateWithReceipts, DatabaseProviderRW, HashingWriter, HistoryWriter, OriginalValuesKnown,
@ -145,7 +147,7 @@ pub fn insert_genesis_state<DB: Database>(
state_init,
all_reverts_init,
contracts.into_iter().collect(),
vec![],
Receipts::new(),
0,
);

View File

@ -25,7 +25,7 @@ use reth_primitives::{
EMPTY_WITHDRAWALS, ETHEREUM_BLOCK_GAS_LIMIT, RETH_CLIENT_VERSION, SLOT_DURATION,
},
proofs, Block, BlockNumberOrTag, ChainSpec, Header, IntoRecoveredTransaction, Receipt,
SealedBlock, Withdrawal, EMPTY_OMMER_ROOT, H256, U256,
Receipts, SealedBlock, Withdrawal, EMPTY_OMMER_ROOT, H256, U256,
};
use reth_provider::{BlockReaderIdExt, BlockSource, BundleStateWithReceipts, StateProviderFactory};
use reth_revm::{
@ -787,7 +787,11 @@ where
// 4788 contract call
db.merge_transitions(BundleRetention::PlainState);
let bundle = BundleStateWithReceipts::new(db.take_bundle(), vec![receipts], block_number);
let bundle = BundleStateWithReceipts::new(
db.take_bundle(),
Receipts::from_vec(vec![receipts]),
block_number,
);
let receipts_root = bundle.receipts_root_slow(block_number).expect("Number is in range");
let logs_bloom = bundle.block_logs_bloom(block_number).expect("Number is in range");
@ -907,7 +911,8 @@ where
db.merge_transitions(BundleRetention::PlainState);
// calculate the state root
let bundle_state = BundleStateWithReceipts::new(db.take_bundle(), vec![], block_number);
let bundle_state =
BundleStateWithReceipts::new(db.take_bundle(), Receipts::new(), block_number);
let state_root = state.state_root(&bundle_state)?;
let header = Header {

View File

@ -83,7 +83,7 @@ pub use prune::{
PruneBatchSizes, PruneCheckpoint, PruneMode, PruneModes, PrunePart, PrunePartError,
ReceiptsLogPruneConfig, MINIMUM_PRUNING_DISTANCE,
};
pub use receipt::{Receipt, ReceiptWithBloom, ReceiptWithBloomRef};
pub use receipt::{Receipt, ReceiptWithBloom, ReceiptWithBloomRef, Receipts};
pub use revm_primitives::JumpMap;
pub use serde_helper::JsonU256;
pub use storage::StorageEntry;

View File

@ -26,6 +26,9 @@ pub enum PrunePartError {
/// Invalid configuration of a prune part.
#[error("The configuration provided for {0} is invalid.")]
Configuration(PrunePart),
/// Receipts have been pruned
#[error("Receipts have been pruned")]
ReceiptsPruned,
}
#[cfg(test)]

View File

@ -1,12 +1,16 @@
use crate::{
bloom::logs_bloom,
compression::{RECEIPT_COMPRESSOR, RECEIPT_DECOMPRESSOR},
Bloom, Log, TxType,
proofs::calculate_receipt_root_ref,
Bloom, Log, PrunePartError, TxType, H256,
};
use bytes::{Buf, BufMut, BytesMut};
use reth_codecs::{main_codec, Compact, CompactZstd};
use reth_rlp::{length_of_length, Decodable, Encodable};
use std::cmp::Ordering;
use std::{
cmp::Ordering,
ops::{Deref, DerefMut},
};
/// Receipt containing result of transaction execution.
#[main_codec(zstd)]
@ -44,6 +48,95 @@ impl Receipt {
}
}
/// A collection of receipts organized as a two-dimensional vector.
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct Receipts {
/// A two-dimensional vector of optional `Receipt` instances.
pub receipt_vec: Vec<Vec<Option<Receipt>>>,
}
impl Receipts {
/// Create a new `Receipts` instance with an empty vector.
pub fn new() -> Self {
Self { receipt_vec: vec![] }
}
/// Create a new `Receipts` instance from an existing vector.
pub fn from_vec(vec: Vec<Vec<Option<Receipt>>>) -> Self {
Self { receipt_vec: vec }
}
/// Returns the length of the `Receipts` vector.
pub fn len(&self) -> usize {
self.receipt_vec.len()
}
/// Returns `true` if the `Receipts` vector is empty.
pub fn is_empty(&self) -> bool {
self.receipt_vec.is_empty()
}
/// Push a new vector of receipts into the `Receipts` collection.
pub fn push(&mut self, receipts: Vec<Option<Receipt>>) {
self.receipt_vec.push(receipts);
}
/// Retrieves the receipt root for all recorded receipts from index.
pub fn root_slow(&self, index: usize) -> Option<H256> {
Some(calculate_receipt_root_ref(
&self.receipt_vec[index].iter().map(Option::as_ref).collect::<Option<Vec<_>>>()?,
))
}
/// Retrieves gas spent by transactions as a vector of tuples (transaction index, gas used).
pub fn gas_spent_by_tx(&self) -> Result<Vec<(u64, u64)>, PrunePartError> {
self.last()
.map(|block_r| {
block_r
.iter()
.enumerate()
.map(|(id, tx_r)| {
if let Some(receipt) = tx_r.as_ref() {
Ok((id as u64, receipt.cumulative_gas_used))
} else {
Err(PrunePartError::ReceiptsPruned)
}
})
.collect::<Result<Vec<_>, PrunePartError>>()
})
.unwrap_or(Ok(vec![]))
}
}
impl Deref for Receipts {
type Target = Vec<Vec<Option<Receipt>>>;
fn deref(&self) -> &Self::Target {
&self.receipt_vec
}
}
impl DerefMut for Receipts {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.receipt_vec
}
}
impl IntoIterator for Receipts {
type Item = Vec<Option<Receipt>>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.receipt_vec.into_iter()
}
}
impl FromIterator<Vec<Option<Receipt>>> for Receipts {
fn from_iter<I: IntoIterator<Item = Vec<Option<Receipt>>>>(iter: I) -> Self {
Self::from_vec(iter.into_iter().collect())
}
}
impl From<Receipt> for ReceiptWithBloom {
fn from(receipt: Receipt) -> Self {
let bloom = receipt.bloom_slow();

View File

@ -12,8 +12,8 @@ use reth_interfaces::{
};
use reth_primitives::{
Address, Block, BlockNumber, Bloom, ChainSpec, Hardfork, Header, PruneMode, PruneModes,
PrunePartError, Receipt, ReceiptWithBloom, TransactionSigned, H256, MINIMUM_PRUNING_DISTANCE,
U256,
PrunePartError, Receipt, ReceiptWithBloom, Receipts, TransactionSigned, H256,
MINIMUM_PRUNING_DISTANCE, U256,
};
use reth_provider::{
BlockExecutor, BlockExecutorStats, BundleStateWithReceipts, PrunableBlockExecutor,
@ -57,7 +57,7 @@ pub struct EVMProcessor<'a> {
/// The inner vector stores receipts ordered by transaction number.
///
/// If receipt is None it means it is pruned.
receipts: Vec<Vec<Option<Receipt>>>,
receipts: Receipts,
/// First block will be initialized to `None`
/// and be set to the block number of first block executed.
first_block: Option<BlockNumber>,
@ -86,7 +86,7 @@ impl<'a> EVMProcessor<'a> {
chain_spec,
evm,
stack: InspectorStack::new(InspectorStackConfig::default()),
receipts: Vec::new(),
receipts: Receipts::new(),
first_block: None,
tip: None,
prune_modes: PruneModes::none(),
@ -119,7 +119,7 @@ impl<'a> EVMProcessor<'a> {
chain_spec,
evm,
stack: InspectorStack::new(InspectorStackConfig::default()),
receipts: Vec::new(),
receipts: Receipts::new(),
first_block: None,
tip: None,
prune_modes: PruneModes::none(),
@ -352,24 +352,7 @@ impl<'a> EVMProcessor<'a> {
return Err(BlockValidationError::BlockGasUsed {
got: cumulative_gas_used,
expected: block.gas_used,
gas_spent_by_tx: self
.receipts
.last()
.map(|block_r| {
block_r
.iter()
.enumerate()
.map(|(id, tx_r)| {
(
id as u64,
tx_r.as_ref()
.expect("receipts have not been pruned")
.cumulative_gas_used,
)
})
.collect()
})
.unwrap_or_default(),
gas_spent_by_tx: self.receipts.gas_spent_by_tx()?,
}
.into())
}

View File

@ -4,8 +4,8 @@ use crate::eth::error::{EthApiError, EthResult};
use core::fmt::Debug;
use reth_primitives::{
constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE},
proofs, Block, ChainSpec, Header, IntoRecoveredTransaction, Receipt, SealedBlock, SealedHeader,
EMPTY_OMMER_ROOT, H256, U256,
proofs, Block, ChainSpec, Header, IntoRecoveredTransaction, Receipt, Receipts, SealedBlock,
SealedHeader, EMPTY_OMMER_ROOT, H256, U256,
};
use reth_provider::{BundleStateWithReceipts, ChainSpecProvider, StateProviderFactory};
use reth_revm::{
@ -187,7 +187,11 @@ impl PendingBlockEnv {
// merge all transitions into bundle state.
db.merge_transitions(BundleRetention::PlainState);
let bundle = BundleStateWithReceipts::new(db.take_bundle(), vec![receipts], block_number);
let bundle = BundleStateWithReceipts::new(
db.take_bundle(),
Receipts::from_vec(vec![receipts]),
block_number,
);
let receipts_root = bundle.receipts_root_slow(block_number).expect("Block is present");
let logs_bloom = bundle.block_logs_bloom(block_number).expect("Block is present");

View File

@ -5,8 +5,8 @@ use reth_db::{
};
use reth_interfaces::db::DatabaseError;
use reth_primitives::{
bloom::logs_bloom, keccak256, proofs::calculate_receipt_root_ref, Account, Address,
BlockNumber, Bloom, Bytecode, Log, Receipt, StorageEntry, H256, U256,
bloom::logs_bloom, keccak256, Account, Address, BlockNumber, Bloom, Bytecode, Log, Receipt,
Receipts, StorageEntry, H256, U256,
};
use reth_revm_primitives::{
db::states::BundleState, into_reth_acc, into_revm_acc, primitives::AccountInfo,
@ -31,7 +31,7 @@ pub struct BundleStateWithReceipts {
/// The inner vector stores receipts ordered by transaction number.
///
/// If receipt is None it means it is pruned.
receipts: Vec<Vec<Option<Receipt>>>,
receipts: Receipts,
/// First block of bundle state.
first_block: BlockNumber,
}
@ -48,11 +48,7 @@ pub type RevertsInit = HashMap<BlockNumber, HashMap<Address, AccountRevertInit>>
impl BundleStateWithReceipts {
/// Create Bundle State.
pub fn new(
bundle: BundleState,
receipts: Vec<Vec<Option<Receipt>>>,
first_block: BlockNumber,
) -> Self {
pub fn new(bundle: BundleState, receipts: Receipts, first_block: BlockNumber) -> Self {
Self { bundle, receipts, first_block }
}
@ -61,7 +57,7 @@ impl BundleStateWithReceipts {
state_init: BundleStateInit,
revert_init: RevertsInit,
contracts_init: Vec<(H256, Bytecode)>,
receipts: Vec<Vec<Option<Receipt>>>,
receipts: Receipts,
first_block: BlockNumber,
) -> Self {
// sort reverts by block number
@ -167,7 +163,7 @@ impl BundleStateWithReceipts {
/// # Example
///
/// ```
/// use reth_primitives::{Account, U256};
/// use reth_primitives::{Account, U256, Receipts};
/// use reth_provider::BundleStateWithReceipts;
/// use reth_db::{test_utils::create_test_rw_db, database::Database};
/// use std::collections::HashMap;
@ -187,7 +183,7 @@ impl BundleStateWithReceipts {
/// )]),
/// HashMap::from([]),
/// vec![],
/// vec![],
/// Receipts::new(),
/// 0,
/// );
///
@ -240,14 +236,11 @@ impl BundleStateWithReceipts {
/// Note: this function calculated Bloom filters for every receipt and created merkle trees
/// of receipt. This is a expensive operation.
pub fn receipts_root_slow(&self, block_number: BlockNumber) -> Option<H256> {
let index = self.block_number_to_index(block_number)?;
let block_receipts =
self.receipts[index].iter().map(Option::as_ref).collect::<Option<Vec<_>>>()?;
Some(calculate_receipt_root_ref(&block_receipts))
self.receipts.root_slow(self.block_number_to_index(block_number)?)
}
/// Return reference to receipts.
pub fn receipts(&self) -> &Vec<Vec<Option<Receipt>>> {
pub fn receipts(&self) -> &Receipts {
&self.receipts
}
@ -325,7 +318,7 @@ impl BundleStateWithReceipts {
// split is done as [0, num) and [num, len]
let (_, this) = self.receipts.split_at(num_of_detached_block as usize);
self.receipts = this.to_vec().clone();
self.receipts = Receipts::from_vec(this.to_vec().clone());
self.bundle.take_n_reverts(num_of_detached_block as usize);
self.first_block = block_number + 1;
@ -340,7 +333,7 @@ impl BundleStateWithReceipts {
/// In most cases this would be true.
pub fn extend(&mut self, other: Self) {
self.bundle.extend(other.bundle);
self.receipts.extend(other.receipts);
self.receipts.extend(other.receipts.receipt_vec);
}
/// Write bundle state to database.
@ -393,7 +386,7 @@ mod tests {
transaction::DbTx,
DatabaseEnv,
};
use reth_primitives::{Address, Receipt, StorageEntry, H256, MAINNET, U256};
use reth_primitives::{Address, Receipt, Receipts, StorageEntry, H256, MAINNET, U256};
use reth_revm_primitives::{into_reth_acc, primitives::HashMap};
use revm::{
db::{
@ -612,7 +605,7 @@ mod tests {
state.merge_transitions(BundleRetention::Reverts);
BundleStateWithReceipts::new(state.take_bundle(), Vec::new(), 1)
BundleStateWithReceipts::new(state.take_bundle(), Receipts::new(), 1)
.write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes)
.expect("Could not write bundle state to DB");
@ -712,7 +705,7 @@ mod tests {
)]));
state.merge_transitions(BundleRetention::Reverts);
BundleStateWithReceipts::new(state.take_bundle(), Vec::new(), 2)
BundleStateWithReceipts::new(state.take_bundle(), Receipts::new(), 2)
.write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes)
.expect("Could not write bundle state to DB");
@ -779,7 +772,7 @@ mod tests {
},
)]));
init_state.merge_transitions(BundleRetention::Reverts);
BundleStateWithReceipts::new(init_state.take_bundle(), Vec::new(), 0)
BundleStateWithReceipts::new(init_state.take_bundle(), Receipts::new(), 0)
.write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes)
.expect("Could not write init bundle state to DB");
@ -926,7 +919,7 @@ mod tests {
let bundle = state.take_bundle();
BundleStateWithReceipts::new(bundle, Vec::new(), 1)
BundleStateWithReceipts::new(bundle, Receipts::new(), 1)
.write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes)
.expect("Could not write bundle state to DB");
@ -1092,7 +1085,7 @@ mod tests {
},
)]));
init_state.merge_transitions(BundleRetention::Reverts);
BundleStateWithReceipts::new(init_state.take_bundle(), Vec::new(), 0)
BundleStateWithReceipts::new(init_state.take_bundle(), Receipts::new(), 0)
.write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes)
.expect("Could not write init bundle state to DB");
@ -1139,7 +1132,7 @@ mod tests {
// Commit block #1 changes to the database.
state.merge_transitions(BundleRetention::Reverts);
BundleStateWithReceipts::new(state.take_bundle(), Vec::new(), 1)
BundleStateWithReceipts::new(state.take_bundle(), Receipts::new(), 1)
.write_to_db(provider.tx_ref(), OriginalValuesKnown::Yes)
.expect("Could not write bundle state to DB");
@ -1171,7 +1164,7 @@ mod tests {
fn revert_to_indices() {
let base = BundleStateWithReceipts {
bundle: BundleState::default(),
receipts: vec![vec![Some(Receipt::default()); 2]; 7],
receipts: Receipts::from_vec(vec![vec![Some(Receipt::default()); 2]; 7]),
first_block: 10,
};

View File

@ -360,7 +360,7 @@ pub enum ChainSplit {
#[cfg(test)]
mod tests {
use super::*;
use reth_primitives::{H160, H256};
use reth_primitives::{Receipts, H160, H256};
use reth_revm_primitives::{
db::BundleState,
primitives::{AccountInfo, HashMap},
@ -406,7 +406,7 @@ mod tests {
vec![vec![(H160([2; 20]), None, vec![])]],
vec![],
),
vec![vec![]],
Receipts::from_vec(vec![vec![]]),
1,
);
@ -416,7 +416,7 @@ mod tests {
vec![vec![(H160([3; 20]), None, vec![])]],
vec![],
),
vec![vec![]],
Receipts::from_vec(vec![vec![]]),
2,
);

View File

@ -368,7 +368,7 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> DatabaseProvider<'this, TX> {
state,
reverts,
Vec::new(),
receipts,
reth_primitives::Receipts::from_vec(receipts),
start_block_number,
))
}

View File

@ -3,7 +3,7 @@
use crate::{BundleStateWithReceipts, DatabaseProviderRW};
use reth_db::{database::Database, models::StoredBlockBodyIndices, tables};
use reth_primitives::{
hex_literal::hex, Account, BlockNumber, Bytes, Header, Log, Receipt, SealedBlock,
hex_literal::hex, Account, BlockNumber, Bytes, Header, Log, Receipt, Receipts, SealedBlock,
SealedBlockWithSenders, StorageEntry, TxType, Withdrawal, H160, H256, U256,
};
use reth_rlp::Decodable;
@ -129,7 +129,7 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, BundleStateWithReceip
]),
)]),
vec![],
vec![vec![Some(Receipt {
Receipts::from_vec(vec![vec![Some(Receipt {
tx_type: TxType::EIP2930,
success: true,
cumulative_gas_used: 300,
@ -138,7 +138,7 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, BundleStateWithReceip
topics: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2)],
data: Bytes::default(),
}],
})]],
})]]),
number,
);
@ -185,7 +185,7 @@ fn block2(
)]),
)]),
vec![],
vec![vec![Some(Receipt {
Receipts::from_vec(vec![vec![Some(Receipt {
tx_type: TxType::EIP1559,
success: false,
cumulative_gas_used: 400,
@ -194,7 +194,7 @@ fn block2(
topics: vec![H256::from_low_u64_be(3), H256::from_low_u64_be(4)],
data: Bytes::default(),
}],
})]],
})]]),
number,
);
(SealedBlockWithSenders { block, senders: vec![H160([0x31; 20])] }, bundle)