mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: Store receipts (#1860)
Co-authored-by: Bjerg <onbjerg@users.noreply.github.com>
This commit is contained in:
@ -6,10 +6,7 @@ use reth_primitives::{BlockHash, BlockNumber, SealedBlock, SealedBlockWithSender
|
||||
use reth_provider::{
|
||||
providers::ChainState, ExecutorFactory, HeaderProvider, StateProviderFactory, Transaction,
|
||||
};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
ops::DerefMut,
|
||||
};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
pub mod block_indices;
|
||||
use block_indices::BlockIndices;
|
||||
@ -543,40 +540,12 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
/// Canonicalize the given chain and commit it to the database.
|
||||
fn commit_canonical(&mut self, chain: Chain) -> Result<(), Error> {
|
||||
let mut tx = Transaction::new(&self.externals.db)?;
|
||||
let new_tip_number = chain.tip().number;
|
||||
let new_tip_hash = chain.tip().hash;
|
||||
let first_transition_id =
|
||||
tx.get_block_transition(chain.first().number.saturating_sub(1))
|
||||
.map_err(|e| ExecError::CanonicalCommit { inner: e.to_string() })?;
|
||||
let expected_state_root = chain.tip().state_root;
|
||||
let fork_block = chain.fork_block_number();
|
||||
|
||||
let (blocks, state) = chain.into_inner();
|
||||
let num_transitions = state.transitions_count();
|
||||
|
||||
// Write state and changesets to the database
|
||||
state
|
||||
.write_to_db(tx.deref_mut(), first_transition_id)
|
||||
tx.append_blocks_with_post_state(blocks.into_values().collect(), state)
|
||||
.map_err(|e| ExecError::CanonicalCommit { inner: e.to_string() })?;
|
||||
|
||||
// Insert the blocks
|
||||
for block in blocks.into_values() {
|
||||
tx.insert_block(block)
|
||||
.map_err(|e| ExecError::CanonicalCommit { inner: e.to_string() })?;
|
||||
}
|
||||
tx.insert_hashes(
|
||||
fork_block,
|
||||
first_transition_id,
|
||||
first_transition_id + num_transitions as u64,
|
||||
new_tip_number,
|
||||
new_tip_hash,
|
||||
expected_state_root,
|
||||
)
|
||||
.map_err(|e| ExecError::CanonicalCommit { inner: e.to_string() })?;
|
||||
|
||||
// Update pipeline progress
|
||||
tx.update_pipeline_stages(new_tip_number)
|
||||
.map_err(|e| ExecError::PipelineStatusUpdate { inner: e.to_string() })?;
|
||||
|
||||
tx.commit()?;
|
||||
|
||||
Ok(())
|
||||
@ -618,10 +587,6 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
)
|
||||
.map_err(|e| ExecError::CanonicalRevert { inner: e.to_string() })?;
|
||||
|
||||
// update pipeline progress.
|
||||
tx.update_pipeline_stages(revert_until)
|
||||
.map_err(|e| ExecError::PipelineStatusUpdate { inner: e.to_string() })?;
|
||||
|
||||
tx.commit()?;
|
||||
|
||||
Ok(Chain::new(blocks_and_execution))
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use crate::post_state::PostState;
|
||||
use reth_interfaces::executor::Error;
|
||||
use reth_primitives::{
|
||||
bloom::logs_bloom, Account, Address, Block, Bloom, Bytecode, ChainSpec, Hardfork, Header, Log,
|
||||
Receipt, TransactionSigned, H256, U256,
|
||||
Account, Address, Block, Bloom, Bytecode, ChainSpec, Hardfork, Header, Log, Receipt,
|
||||
ReceiptWithBloom, TransactionSigned, H256, U256,
|
||||
};
|
||||
use reth_provider::{BlockExecutor, StateProvider};
|
||||
use reth_revm::{
|
||||
@ -420,7 +420,6 @@ where
|
||||
// receipts`.
|
||||
success: result.is_success(),
|
||||
cumulative_gas_used,
|
||||
bloom: logs_bloom(logs.iter()),
|
||||
logs,
|
||||
});
|
||||
post_state.finish_transition();
|
||||
@ -498,13 +497,14 @@ pub fn verify_receipt<'a>(
|
||||
receipts: impl Iterator<Item = &'a Receipt> + Clone,
|
||||
) -> Result<(), Error> {
|
||||
// Check receipts root.
|
||||
let receipts_root = reth_primitives::proofs::calculate_receipt_root(receipts.clone());
|
||||
let receipts_with_bloom = receipts.map(|r| r.clone().into()).collect::<Vec<ReceiptWithBloom>>();
|
||||
let receipts_root = reth_primitives::proofs::calculate_receipt_root(receipts_with_bloom.iter());
|
||||
if receipts_root != expected_receipts_root {
|
||||
return Err(Error::ReceiptRootDiff { got: receipts_root, expected: expected_receipts_root })
|
||||
}
|
||||
|
||||
// Create header log bloom.
|
||||
let logs_bloom = receipts.fold(Bloom::zero(), |bloom, r| bloom | r.bloom);
|
||||
let logs_bloom = receipts_with_bloom.iter().fold(Bloom::zero(), |bloom, r| bloom | r.bloom);
|
||||
if logs_bloom != expected_logs_bloom {
|
||||
return Err(Error::BloomLogDiff {
|
||||
expected: Box::new(expected_logs_bloom),
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
//! Implements the `GetReceipts` and `Receipts` message types.
|
||||
use reth_codecs::derive_arbitrary;
|
||||
use reth_primitives::{Receipt, H256};
|
||||
use reth_primitives::{ReceiptWithBloom, H256};
|
||||
use reth_rlp::{RlpDecodableWrapper, RlpEncodableWrapper};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
@ -22,24 +22,29 @@ pub struct GetReceipts(
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Receipts(
|
||||
/// Each receipt hash should correspond to a block hash in the request.
|
||||
pub Vec<Vec<Receipt>>,
|
||||
pub Vec<Vec<ReceiptWithBloom>>,
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::types::{message::RequestPair, GetReceipts, Receipts};
|
||||
use crate::{
|
||||
types::{message::RequestPair, GetReceipts},
|
||||
Receipts,
|
||||
};
|
||||
use hex_literal::hex;
|
||||
use reth_primitives::{Log, Receipt, TxType};
|
||||
use reth_primitives::{Log, Receipt, ReceiptWithBloom, TxType};
|
||||
use reth_rlp::{Decodable, Encodable};
|
||||
|
||||
#[test]
|
||||
fn roundtrip_eip1559() {
|
||||
let receipts = Receipts(vec![vec![Receipt {
|
||||
tx_type: TxType::EIP1559,
|
||||
success: false,
|
||||
cumulative_gas_used: 0,
|
||||
let receipts = Receipts(vec![vec![ReceiptWithBloom {
|
||||
receipt: Receipt {
|
||||
tx_type: TxType::EIP1559,
|
||||
success: false,
|
||||
cumulative_gas_used: 0,
|
||||
logs: vec![],
|
||||
},
|
||||
bloom: Default::default(),
|
||||
logs: vec![],
|
||||
}]]);
|
||||
|
||||
let mut out = vec![];
|
||||
@ -93,9 +98,8 @@ mod test {
|
||||
request_id: 1111,
|
||||
message: Receipts(vec![
|
||||
vec![
|
||||
Receipt {
|
||||
tx_type: TxType::Legacy,
|
||||
bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
|
||||
ReceiptWithBloom {
|
||||
receipt: Receipt {tx_type: TxType::Legacy,
|
||||
cumulative_gas_used: 0x1u64,
|
||||
logs: vec![
|
||||
Log {
|
||||
@ -108,7 +112,8 @@ mod test {
|
||||
},
|
||||
],
|
||||
success: false,
|
||||
},
|
||||
},bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
|
||||
}
|
||||
],
|
||||
]),
|
||||
};
|
||||
@ -127,21 +132,23 @@ mod test {
|
||||
request_id: 1111,
|
||||
message: Receipts(vec![
|
||||
vec![
|
||||
Receipt {
|
||||
tx_type: TxType::Legacy,
|
||||
ReceiptWithBloom {
|
||||
receipt: Receipt {
|
||||
tx_type: TxType::Legacy,
|
||||
cumulative_gas_used: 0x1u64,
|
||||
logs: vec![
|
||||
Log {
|
||||
address: hex!("0000000000000000000000000000000000000011").into(),
|
||||
topics: vec![
|
||||
hex!("000000000000000000000000000000000000000000000000000000000000dead").into(),
|
||||
hex!("000000000000000000000000000000000000000000000000000000000000beef").into(),
|
||||
],
|
||||
data: hex!("0100ff")[..].into(),
|
||||
},
|
||||
],
|
||||
success: false,
|
||||
},
|
||||
bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
|
||||
cumulative_gas_used: 0x1u64,
|
||||
logs: vec![
|
||||
Log {
|
||||
address: hex!("0000000000000000000000000000000000000011").into(),
|
||||
topics: vec![
|
||||
hex!("000000000000000000000000000000000000000000000000000000000000dead").into(),
|
||||
hex!("000000000000000000000000000000000000000000000000000000000000beef").into(),
|
||||
],
|
||||
data: hex!("0100ff")[..].into(),
|
||||
},
|
||||
],
|
||||
success: false,
|
||||
},
|
||||
],
|
||||
]),
|
||||
|
||||
@ -11,7 +11,9 @@ use reth_eth_wire::{
|
||||
SharedTransactions, Transactions,
|
||||
};
|
||||
use reth_interfaces::p2p::error::{RequestError, RequestResult};
|
||||
use reth_primitives::{BlockBody, Bytes, Header, PeerId, Receipt, TransactionSigned, H256};
|
||||
use reth_primitives::{
|
||||
BlockBody, Bytes, Header, PeerId, ReceiptWithBloom, TransactionSigned, H256,
|
||||
};
|
||||
use std::{
|
||||
fmt,
|
||||
sync::Arc,
|
||||
@ -199,7 +201,7 @@ pub enum PeerResponseResult {
|
||||
BlockBodies(RequestResult<Vec<BlockBody>>),
|
||||
PooledTransactions(RequestResult<Vec<TransactionSigned>>),
|
||||
NodeData(RequestResult<Vec<Bytes>>),
|
||||
Receipts(RequestResult<Vec<Vec<Receipt>>>),
|
||||
Receipts(RequestResult<Vec<Vec<ReceiptWithBloom>>>),
|
||||
}
|
||||
|
||||
// === impl PeerResponseResult ===
|
||||
|
||||
@ -63,7 +63,7 @@ pub use net::{
|
||||
SEPOLIA_BOOTNODES,
|
||||
};
|
||||
pub use peer::{PeerId, WithPeerId};
|
||||
pub use receipt::Receipt;
|
||||
pub use receipt::{Receipt, ReceiptWithBloom};
|
||||
pub use revm_primitives::JumpMap;
|
||||
pub use serde_helper::JsonU256;
|
||||
pub use storage::{StorageEntry, StorageTrieEntry};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
keccak256, Address, Bytes, GenesisAccount, Header, Log, Receipt, TransactionSigned, Withdrawal,
|
||||
H256,
|
||||
keccak256, Address, Bytes, GenesisAccount, Header, Log, ReceiptWithBloom, TransactionSigned,
|
||||
Withdrawal, H256,
|
||||
};
|
||||
use bytes::BytesMut;
|
||||
use hash_db::Hasher;
|
||||
@ -58,7 +58,7 @@ pub fn calculate_withdrawals_root<'a>(
|
||||
}
|
||||
|
||||
/// Calculates the receipt root for a header.
|
||||
pub fn calculate_receipt_root<'a>(receipts: impl Iterator<Item = &'a Receipt>) -> H256 {
|
||||
pub fn calculate_receipt_root<'a>(receipts: impl Iterator<Item = &'a ReceiptWithBloom>) -> H256 {
|
||||
ordered_trie_root::<KeccakHasher, _>(receipts.into_iter().map(|receipt| {
|
||||
let mut receipt_rlp = Vec::new();
|
||||
receipt.encode_inner(&mut receipt_rlp, false);
|
||||
@ -102,7 +102,8 @@ mod tests {
|
||||
use crate::{
|
||||
hex_literal::hex,
|
||||
proofs::{calculate_receipt_root, calculate_transaction_root, genesis_state_root},
|
||||
Address, Block, Bloom, GenesisAccount, Log, Receipt, TxType, H160, H256, U256,
|
||||
Address, Block, Bloom, GenesisAccount, Log, Receipt, ReceiptWithBloom, TxType, H160, H256,
|
||||
U256,
|
||||
};
|
||||
use reth_rlp::Decodable;
|
||||
|
||||
@ -122,12 +123,14 @@ mod tests {
|
||||
fn check_receipt_root() {
|
||||
let logs = vec![Log { address: H160::zero(), topics: vec![], data: Default::default() }];
|
||||
let bloom = Bloom(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"));
|
||||
let receipt = Receipt {
|
||||
tx_type: TxType::EIP2930,
|
||||
success: true,
|
||||
cumulative_gas_used: 102068,
|
||||
let receipt = ReceiptWithBloom {
|
||||
receipt: Receipt {
|
||||
tx_type: TxType::EIP2930,
|
||||
success: true,
|
||||
cumulative_gas_used: 102068,
|
||||
logs,
|
||||
},
|
||||
bloom,
|
||||
logs,
|
||||
};
|
||||
let receipt = vec![receipt];
|
||||
let root = calculate_receipt_root(receipt.iter());
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::{Bloom, Log, TxType};
|
||||
use crate::{bloom::logs_bloom, Bloom, Log, TxType};
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use reth_codecs::{main_codec, Compact};
|
||||
use reth_rlp::{length_of_length, Decodable, Encodable};
|
||||
@ -16,21 +16,61 @@ pub struct Receipt {
|
||||
pub success: bool,
|
||||
/// Gas used
|
||||
pub cumulative_gas_used: u64,
|
||||
/// Bloom filter.
|
||||
pub bloom: Bloom,
|
||||
/// Log send from contracts.
|
||||
pub logs: Vec<Log>,
|
||||
}
|
||||
|
||||
impl Receipt {
|
||||
/// Cacluates [`Log`]'s bloom filter. this is slow operatio and [ReceiptWithBloom] can
|
||||
/// be used to cache this value.
|
||||
pub fn bloom_slow(&self) -> Bloom {
|
||||
logs_bloom(self.logs.iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Receipt> for ReceiptWithBloom {
|
||||
fn from(receipt: Receipt) -> Self {
|
||||
let bloom = receipt.bloom_slow();
|
||||
ReceiptWithBloom { receipt, bloom }
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Receipt`] with calculated bloom filter.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Default)]
|
||||
#[main_codec]
|
||||
pub struct ReceiptWithBloom {
|
||||
/// Bloom filter build from logs.
|
||||
pub bloom: Bloom,
|
||||
/// Main receipt body
|
||||
pub receipt: Receipt,
|
||||
}
|
||||
|
||||
impl ReceiptWithBloom {
|
||||
/// Create new [ReceiptWithBloom]
|
||||
pub fn new(receipt: Receipt, bloom: Bloom) -> Self {
|
||||
Self { receipt, bloom }
|
||||
}
|
||||
|
||||
/// Consume the structure, returning only the receipt
|
||||
pub fn into_receipt(self) -> Receipt {
|
||||
self.receipt
|
||||
}
|
||||
|
||||
/// Consume the structure, returning the receipt and the bloom filter
|
||||
pub fn into_components(self) -> (Receipt, Bloom) {
|
||||
(self.receipt, self.bloom)
|
||||
}
|
||||
}
|
||||
|
||||
impl ReceiptWithBloom {
|
||||
/// Returns the rlp header for the receipt payload.
|
||||
fn receipt_rlp_header(&self) -> reth_rlp::Header {
|
||||
let mut rlp_head = reth_rlp::Header { list: true, payload_length: 0 };
|
||||
|
||||
rlp_head.payload_length += self.success.length();
|
||||
rlp_head.payload_length += self.cumulative_gas_used.length();
|
||||
rlp_head.payload_length += self.receipt.success.length();
|
||||
rlp_head.payload_length += self.receipt.cumulative_gas_used.length();
|
||||
rlp_head.payload_length += self.bloom.length();
|
||||
rlp_head.payload_length += self.logs.length();
|
||||
rlp_head.payload_length += self.receipt.logs.length();
|
||||
|
||||
rlp_head
|
||||
}
|
||||
@ -38,15 +78,15 @@ impl Receipt {
|
||||
/// Encodes the receipt data.
|
||||
fn encode_fields(&self, out: &mut dyn BufMut) {
|
||||
self.receipt_rlp_header().encode(out);
|
||||
self.success.encode(out);
|
||||
self.cumulative_gas_used.encode(out);
|
||||
self.receipt.success.encode(out);
|
||||
self.receipt.cumulative_gas_used.encode(out);
|
||||
self.bloom.encode(out);
|
||||
self.logs.encode(out);
|
||||
self.receipt.logs.encode(out);
|
||||
}
|
||||
|
||||
/// Encode receipt with or without the header data.
|
||||
pub fn encode_inner(&self, out: &mut dyn BufMut, with_header: bool) {
|
||||
if matches!(self.tx_type, TxType::Legacy) {
|
||||
if matches!(self.receipt.tx_type, TxType::Legacy) {
|
||||
self.encode_fields(out);
|
||||
return
|
||||
}
|
||||
@ -60,7 +100,7 @@ impl Receipt {
|
||||
header.encode(out);
|
||||
}
|
||||
|
||||
match self.tx_type {
|
||||
match self.receipt.tx_type {
|
||||
TxType::EIP2930 => {
|
||||
out.put_u8(0x01);
|
||||
}
|
||||
@ -86,13 +126,13 @@ impl Receipt {
|
||||
return Err(reth_rlp::DecodeError::UnexpectedString)
|
||||
}
|
||||
let started_len = b.len();
|
||||
let this = Self {
|
||||
tx_type,
|
||||
success: reth_rlp::Decodable::decode(b)?,
|
||||
cumulative_gas_used: reth_rlp::Decodable::decode(b)?,
|
||||
bloom: reth_rlp::Decodable::decode(b)?,
|
||||
logs: reth_rlp::Decodable::decode(b)?,
|
||||
};
|
||||
|
||||
let success = reth_rlp::Decodable::decode(b)?;
|
||||
let cumulative_gas_used = reth_rlp::Decodable::decode(b)?;
|
||||
let bloom = reth_rlp::Decodable::decode(b)?;
|
||||
let logs = reth_rlp::Decodable::decode(b)?;
|
||||
|
||||
let this = Self { receipt: Receipt { tx_type, success, cumulative_gas_used, logs }, bloom };
|
||||
let consumed = started_len - b.len();
|
||||
if consumed != rlp_head.payload_length {
|
||||
return Err(reth_rlp::DecodeError::ListLengthMismatch {
|
||||
@ -105,14 +145,14 @@ impl Receipt {
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Receipt {
|
||||
impl Encodable for ReceiptWithBloom {
|
||||
fn encode(&self, out: &mut dyn BufMut) {
|
||||
self.encode_inner(out, true)
|
||||
}
|
||||
fn length(&self) -> usize {
|
||||
let mut payload_len = self.receipt_length();
|
||||
// account for eip-2718 type prefix and set the list
|
||||
if matches!(self.tx_type, TxType::EIP1559 | TxType::EIP2930) {
|
||||
if matches!(self.receipt.tx_type, TxType::EIP1559 | TxType::EIP2930) {
|
||||
payload_len += 1;
|
||||
// we include a string header for typed receipts, so include the length here
|
||||
payload_len += length_of_length(payload_len);
|
||||
@ -122,7 +162,7 @@ impl Encodable for Receipt {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Receipt {
|
||||
impl Decodable for ReceiptWithBloom {
|
||||
fn decode(buf: &mut &[u8]) -> Result<Self, reth_rlp::DecodeError> {
|
||||
// a receipt is either encoded as a string (non legacy) or a list (legacy).
|
||||
// We should not consume the buffer if we are decoding a legacy receipt, so let's
|
||||
@ -170,25 +210,27 @@ mod tests {
|
||||
let expected = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff");
|
||||
|
||||
let mut data = vec![];
|
||||
let receipt = Receipt {
|
||||
tx_type: TxType::Legacy,
|
||||
let receipt = ReceiptWithBloom {
|
||||
receipt: Receipt {
|
||||
tx_type: TxType::Legacy,
|
||||
cumulative_gas_used: 0x1u64,
|
||||
logs: vec![Log {
|
||||
address: Address::from_str("0000000000000000000000000000000000000011").unwrap(),
|
||||
topics: vec![
|
||||
H256::from_str(
|
||||
"000000000000000000000000000000000000000000000000000000000000dead",
|
||||
)
|
||||
.unwrap(),
|
||||
H256::from_str(
|
||||
"000000000000000000000000000000000000000000000000000000000000beef",
|
||||
)
|
||||
.unwrap(),
|
||||
],
|
||||
data: Bytes::from_str("0100ff").unwrap().0.into(),
|
||||
}],
|
||||
success: false,
|
||||
},
|
||||
bloom: [0; 256].into(),
|
||||
cumulative_gas_used: 0x1u64,
|
||||
logs: vec![Log {
|
||||
address: Address::from_str("0000000000000000000000000000000000000011").unwrap(),
|
||||
topics: vec![
|
||||
H256::from_str(
|
||||
"000000000000000000000000000000000000000000000000000000000000dead",
|
||||
)
|
||||
.unwrap(),
|
||||
H256::from_str(
|
||||
"000000000000000000000000000000000000000000000000000000000000beef",
|
||||
)
|
||||
.unwrap(),
|
||||
],
|
||||
data: Bytes::from_str("0100ff").unwrap().0.into(),
|
||||
}],
|
||||
success: false,
|
||||
};
|
||||
|
||||
receipt.encode(&mut data);
|
||||
@ -204,28 +246,30 @@ mod tests {
|
||||
let data = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff");
|
||||
|
||||
// EIP658Receipt
|
||||
let expected = Receipt {
|
||||
tx_type: TxType::Legacy,
|
||||
let expected = ReceiptWithBloom {
|
||||
receipt: Receipt {
|
||||
tx_type: TxType::Legacy,
|
||||
cumulative_gas_used: 0x1u64,
|
||||
logs: vec![Log {
|
||||
address: Address::from_str("0000000000000000000000000000000000000011").unwrap(),
|
||||
topics: vec![
|
||||
H256::from_str(
|
||||
"000000000000000000000000000000000000000000000000000000000000dead",
|
||||
)
|
||||
.unwrap(),
|
||||
H256::from_str(
|
||||
"000000000000000000000000000000000000000000000000000000000000beef",
|
||||
)
|
||||
.unwrap(),
|
||||
],
|
||||
data: Bytes::from_str("0100ff").unwrap().0.into(),
|
||||
}],
|
||||
success: false,
|
||||
},
|
||||
bloom: [0; 256].into(),
|
||||
cumulative_gas_used: 0x1u64,
|
||||
logs: vec![Log {
|
||||
address: Address::from_str("0000000000000000000000000000000000000011").unwrap(),
|
||||
topics: vec![
|
||||
H256::from_str(
|
||||
"000000000000000000000000000000000000000000000000000000000000dead",
|
||||
)
|
||||
.unwrap(),
|
||||
H256::from_str(
|
||||
"000000000000000000000000000000000000000000000000000000000000beef",
|
||||
)
|
||||
.unwrap(),
|
||||
],
|
||||
data: Bytes::from_str("0100ff").unwrap().0.into(),
|
||||
}],
|
||||
success: false,
|
||||
};
|
||||
|
||||
let receipt = Receipt::decode(&mut &data[..]).unwrap();
|
||||
let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
|
||||
assert_eq!(receipt, expected);
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ pub enum TableType {
|
||||
}
|
||||
|
||||
/// Default tables that should be present inside database.
|
||||
pub const TABLES: [(TableType, &str); 28] = [
|
||||
pub const TABLES: [(TableType, &str); 27] = [
|
||||
(TableType::Table, CanonicalHeaders::const_name()),
|
||||
(TableType::Table, HeaderTD::const_name()),
|
||||
(TableType::Table, HeaderNumbers::const_name()),
|
||||
@ -44,7 +44,6 @@ pub const TABLES: [(TableType, &str); 28] = [
|
||||
(TableType::Table, Transactions::const_name()),
|
||||
(TableType::Table, TxHashNumber::const_name()),
|
||||
(TableType::Table, Receipts::const_name()),
|
||||
(TableType::Table, Logs::const_name()),
|
||||
(TableType::Table, PlainAccountState::const_name()),
|
||||
(TableType::DupSort, PlainStorageState::const_name()),
|
||||
(TableType::Table, Bytecodes::const_name()),
|
||||
@ -169,11 +168,6 @@ table!(
|
||||
( Receipts ) TxNumber | Receipt
|
||||
);
|
||||
|
||||
table!(
|
||||
/// (Canonical only) Stores transaction logs.
|
||||
( Logs ) TxNumber | Receipt
|
||||
);
|
||||
|
||||
table!(
|
||||
/// Stores all smart contract bytecodes.
|
||||
/// There will be multiple accounts that have same bytecode
|
||||
|
||||
@ -574,6 +574,14 @@ impl PostState {
|
||||
bytecodes_cursor.upsert(hash, bytecode)?;
|
||||
}
|
||||
|
||||
// write receipts
|
||||
let mut receipts_cursor = tx.cursor_write::<tables::Receipts>()?;
|
||||
let mut tx_num = receipts_cursor.last()?.map(|(tx_num, _)| tx_num).unwrap_or_default();
|
||||
for receipt in self.receipts.into_iter() {
|
||||
tx_num += 1;
|
||||
receipts_cursor.append(tx_num, receipt)?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -510,6 +510,49 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Append blocks and insert its post state.
|
||||
/// This will insert block data to all related tables and will update pipeline progress.
|
||||
pub fn append_blocks_with_post_state(
|
||||
&mut self,
|
||||
blocks: Vec<SealedBlockWithSenders>,
|
||||
state: PostState,
|
||||
) -> Result<(), TransactionError> {
|
||||
if blocks.is_empty() {
|
||||
return Ok(())
|
||||
}
|
||||
let tip = blocks.last().unwrap();
|
||||
let new_tip_number = tip.number;
|
||||
let new_tip_hash = tip.hash;
|
||||
let expected_state_root = tip.state_root;
|
||||
|
||||
let fork_block_number = blocks.first().unwrap().number.saturating_sub(1);
|
||||
|
||||
let first_transition_id = self.get_block_transition(fork_block_number)?;
|
||||
|
||||
let num_transitions = state.transitions_count();
|
||||
|
||||
// Write state and changesets to the database
|
||||
state.write_to_db(self.deref_mut(), first_transition_id)?;
|
||||
|
||||
// Insert the blocks
|
||||
for block in blocks {
|
||||
self.insert_block(block)?;
|
||||
}
|
||||
self.insert_hashes(
|
||||
fork_block_number,
|
||||
first_transition_id,
|
||||
first_transition_id + num_transitions as u64,
|
||||
new_tip_number,
|
||||
new_tip_hash,
|
||||
expected_state_root,
|
||||
)?;
|
||||
|
||||
// Update pipeline progress
|
||||
self.update_pipeline_stages(new_tip_number)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Insert full block and make it canonical.
|
||||
///
|
||||
/// This inserts the block and builds history related indexes. Once all blocks in a chain have
|
||||
@ -806,6 +849,7 @@ where
|
||||
/// 3. Set the local state to the value in the changeset
|
||||
///
|
||||
/// If `TAKE` is `true`, the local state will be written to the plain state tables.
|
||||
/// 5. Get all receipts from table
|
||||
fn get_take_block_execution_result_range<const TAKE: bool>(
|
||||
&self,
|
||||
range: impl RangeBounds<BlockNumber> + Clone,
|
||||
@ -828,6 +872,14 @@ where
|
||||
// it is connection point for bodies getter and execution result getter.
|
||||
let block_bodies = self.get_or_take::<tables::BlockBodies, false>(range)?;
|
||||
|
||||
// get transaction receipts
|
||||
let from_transaction_num =
|
||||
block_bodies.first().expect("already checked if there are blocks").1.first_tx_index();
|
||||
let to_transaction_num =
|
||||
block_bodies.last().expect("already checked if there are blocks").1.last_tx_index();
|
||||
let receipts =
|
||||
self.get_or_take::<tables::Receipts, TAKE>(from_transaction_num..=to_transaction_num)?;
|
||||
|
||||
// get saved previous values
|
||||
let from_storage: TransitionIdAddress = (from, Address::zero()).into();
|
||||
let to_storage: TransitionIdAddress = (to, Address::zero()).into();
|
||||
@ -969,18 +1021,25 @@ where
|
||||
let mut block_transition_iter = block_transition.into_iter();
|
||||
let mut next_transition_id = from;
|
||||
|
||||
let mut receipt_iter = receipts.into_iter();
|
||||
|
||||
// loop break if we are at the end of the blocks.
|
||||
for (_, block_body) in block_bodies.into_iter() {
|
||||
let mut block_exec_res = PostState::new();
|
||||
for _ in 0..block_body.tx_count {
|
||||
let mut block_post_state = PostState::new();
|
||||
for tx_num in block_body.tx_id_range() {
|
||||
if let Some(changes) = all_changesets.remove(&next_transition_id) {
|
||||
for mut change in changes.into_iter() {
|
||||
change
|
||||
.set_transition_id(block_exec_res.transitions_count() as TransitionId);
|
||||
block_exec_res.add_and_apply(change);
|
||||
.set_transition_id(block_post_state.transitions_count() as TransitionId);
|
||||
block_post_state.add_and_apply(change);
|
||||
}
|
||||
}
|
||||
block_exec_res.finish_transition();
|
||||
if let Some((receipt_tx_num, receipt)) = receipt_iter.next() {
|
||||
if tx_num != receipt_tx_num {
|
||||
block_post_state.add_receipt(receipt)
|
||||
}
|
||||
}
|
||||
block_post_state.finish_transition();
|
||||
next_transition_id += 1;
|
||||
}
|
||||
|
||||
@ -991,14 +1050,14 @@ where
|
||||
if let Some(changes) = all_changesets.remove(&next_transition_id) {
|
||||
for mut change in changes.into_iter() {
|
||||
change
|
||||
.set_transition_id(block_exec_res.transitions_count() as TransitionId);
|
||||
block_exec_res.add_and_apply(change);
|
||||
.set_transition_id(block_post_state.transitions_count() as TransitionId);
|
||||
block_post_state.add_and_apply(change);
|
||||
}
|
||||
block_exec_res.finish_transition();
|
||||
block_post_state.finish_transition();
|
||||
next_transition_id += 1;
|
||||
}
|
||||
}
|
||||
block_exec_results.push(block_exec_res)
|
||||
block_exec_results.push(block_post_state)
|
||||
}
|
||||
Ok(block_exec_results)
|
||||
}
|
||||
@ -1064,6 +1123,7 @@ where
|
||||
}
|
||||
// get blocks
|
||||
let blocks = self.get_take_block_range::<TAKE>(chain_spec, range.clone())?;
|
||||
let unwind_to = blocks.first().map(|b| b.number.saturating_sub(1));
|
||||
// get execution res
|
||||
let execution_res = self.get_take_block_execution_result_range::<TAKE>(range.clone())?;
|
||||
// combine them
|
||||
@ -1075,6 +1135,11 @@ where
|
||||
if TAKE {
|
||||
// rm block bodies
|
||||
self.get_or_take::<tables::BlockBodies, TAKE>(range)?;
|
||||
|
||||
// Update pipeline progress
|
||||
if let Some(fork_number) = unwind_to {
|
||||
self.update_pipeline_stages(fork_number)?;
|
||||
}
|
||||
}
|
||||
|
||||
// return them
|
||||
@ -1494,7 +1559,7 @@ mod test {
|
||||
use std::{ops::DerefMut, sync::Arc};
|
||||
|
||||
#[test]
|
||||
fn insert_get_take() {
|
||||
fn insert_block_and_hashes_get_take() {
|
||||
let db = create_test_rw_db();
|
||||
|
||||
// setup
|
||||
@ -1603,4 +1668,65 @@ mod test {
|
||||
// assert genesis state
|
||||
assert_genesis_block(&tx, genesis);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_get_take_multiblocks() {
|
||||
let db = create_test_rw_db();
|
||||
|
||||
// setup
|
||||
let mut tx = Transaction::new(db.as_ref()).unwrap();
|
||||
let chain_spec = ChainSpecBuilder::default()
|
||||
.chain(MAINNET.chain)
|
||||
.genesis(MAINNET.genesis.clone())
|
||||
.shanghai_activated()
|
||||
.build();
|
||||
|
||||
let data = BlockChainTestData::default();
|
||||
let genesis = data.genesis.clone();
|
||||
let (block1, exec_res1) = data.blocks[0].clone();
|
||||
let (block2, exec_res2) = data.blocks[1].clone();
|
||||
|
||||
insert_canonical_block(tx.deref_mut(), data.genesis.clone(), None, false).unwrap();
|
||||
|
||||
tx.put::<tables::AccountsTrie>(EMPTY_ROOT, vec![0x80]).unwrap();
|
||||
assert_genesis_block(&tx, data.genesis);
|
||||
|
||||
tx.append_blocks_with_post_state(vec![block1.clone()], exec_res1.clone()).unwrap();
|
||||
|
||||
// get one block
|
||||
let get = tx.get_block_and_execution_range(&chain_spec, 1..=1).unwrap();
|
||||
assert_eq!(get, vec![(block1.clone(), exec_res1.clone())]);
|
||||
|
||||
// take one block
|
||||
let take = tx.take_block_and_execution_range(&chain_spec, 1..=1).unwrap();
|
||||
assert_eq!(take, vec![(block1.clone(), exec_res1.clone())]);
|
||||
assert_genesis_block(&tx, genesis.clone());
|
||||
|
||||
// insert two blocks
|
||||
let mut merged_state = exec_res1.clone();
|
||||
merged_state.extend(exec_res2.clone());
|
||||
tx.append_blocks_with_post_state(
|
||||
vec![block1.clone(), block2.clone()],
|
||||
merged_state.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// get second block
|
||||
let get = tx.get_block_and_execution_range(&chain_spec, 2..=2).unwrap();
|
||||
assert_eq!(get, vec![(block2.clone(), exec_res2.clone())]);
|
||||
|
||||
// get two blocks
|
||||
let get = tx.get_block_and_execution_range(&chain_spec, 1..=2).unwrap();
|
||||
assert_eq!(
|
||||
get,
|
||||
vec![(block1.clone(), exec_res1.clone()), (block2.clone(), exec_res2.clone())]
|
||||
);
|
||||
|
||||
// take two blocks
|
||||
let get = tx.take_block_and_execution_range(&chain_spec, 1..=2).unwrap();
|
||||
assert_eq!(get, vec![(block1, exec_res1), (block2, exec_res2)]);
|
||||
|
||||
// assert genesis state
|
||||
assert_genesis_block(&tx, genesis);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user