From c4f80b299754be5ff558ee709f2288c94df3621c Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 21 Mar 2023 12:27:37 +0100 Subject: [PATCH] feat: Store receipts (#1860) Co-authored-by: Bjerg --- crates/executor/src/blockchain_tree/mod.rs | 41 +----- crates/executor/src/executor.rs | 10 +- crates/net/eth-wire/src/types/receipts.rs | 61 ++++---- crates/net/network/src/message.rs | 6 +- crates/primitives/src/lib.rs | 2 +- crates/primitives/src/proofs.rs | 21 +-- crates/primitives/src/receipt.rs | 160 +++++++++++++-------- crates/storage/db/src/tables/mod.rs | 8 +- crates/storage/provider/src/post_state.rs | 8 ++ crates/storage/provider/src/transaction.rs | 146 +++++++++++++++++-- 10 files changed, 306 insertions(+), 157 deletions(-) diff --git a/crates/executor/src/blockchain_tree/mod.rs b/crates/executor/src/blockchain_tree/mod.rs index 481c60d77d..6564f06ba6 100644 --- a/crates/executor/src/blockchain_tree/mod.rs +++ b/crates/executor/src/blockchain_tree/mod.rs @@ -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 BlockchainTree /// 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 BlockchainTree ) .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)) diff --git a/crates/executor/src/executor.rs b/crates/executor/src/executor.rs index 2e56a72090..d0db0a8928 100644 --- a/crates/executor/src/executor.rs +++ b/crates/executor/src/executor.rs @@ -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 + 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::>(); + 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), diff --git a/crates/net/eth-wire/src/types/receipts.rs b/crates/net/eth-wire/src/types/receipts.rs index 52516d0b8c..87190aa688 100644 --- a/crates/net/eth-wire/src/types/receipts.rs +++ b/crates/net/eth-wire/src/types/receipts.rs @@ -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>, + pub Vec>, ); #[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, }, ], ]), diff --git a/crates/net/network/src/message.rs b/crates/net/network/src/message.rs index d14e314427..1a50a64926 100644 --- a/crates/net/network/src/message.rs +++ b/crates/net/network/src/message.rs @@ -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>), PooledTransactions(RequestResult>), NodeData(RequestResult>), - Receipts(RequestResult>>), + Receipts(RequestResult>>), } // === impl PeerResponseResult === diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 79e8bb1caa..01eb74867b 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -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}; diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index 28f7f6e8b2..ed659fe434 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -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) -> H256 { +pub fn calculate_receipt_root<'a>(receipts: impl Iterator) -> H256 { ordered_trie_root::(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()); diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index ae4303856f..0a5d264026 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -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, } 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 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 { // 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); } } diff --git a/crates/storage/db/src/tables/mod.rs b/crates/storage/db/src/tables/mod.rs index db21772ad1..8ea4979dae 100644 --- a/crates/storage/db/src/tables/mod.rs +++ b/crates/storage/db/src/tables/mod.rs @@ -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 diff --git a/crates/storage/provider/src/post_state.rs b/crates/storage/provider/src/post_state.rs index a9b1bb72f1..7137838bab 100644 --- a/crates/storage/provider/src/post_state.rs +++ b/crates/storage/provider/src/post_state.rs @@ -574,6 +574,14 @@ impl PostState { bytecodes_cursor.upsert(hash, bytecode)?; } + // write receipts + let mut receipts_cursor = tx.cursor_write::()?; + 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(()) } } diff --git a/crates/storage/provider/src/transaction.rs b/crates/storage/provider/src/transaction.rs index 98426050d0..ea3de2d98f 100644 --- a/crates/storage/provider/src/transaction.rs +++ b/crates/storage/provider/src/transaction.rs @@ -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, + 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( &self, range: impl RangeBounds + Clone, @@ -828,6 +872,14 @@ where // it is connection point for bodies getter and execution result getter. let block_bodies = self.get_or_take::(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::(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::(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::(range.clone())?; // combine them @@ -1075,6 +1135,11 @@ where if TAKE { // rm block bodies self.get_or_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::(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); + } }