refactor: improve state writing functions for db provider (#12885)

This commit is contained in:
Arsenii Kulikov
2024-11-26 22:26:22 +04:00
committed by GitHub
parent 83af493179
commit 38cf6c900d
3 changed files with 18 additions and 107 deletions

View File

@ -15,7 +15,7 @@ use crate::{
HeaderSyncGapProvider, HistoricalStateProvider, HistoricalStateProviderRef, HistoryWriter,
LatestStateProvider, LatestStateProviderRef, OriginalValuesKnown, ProviderError,
PruneCheckpointReader, PruneCheckpointWriter, RevertsInit, StageCheckpointReader,
StateChangeWriter, StateCommitmentProvider, StateProviderBox, StateReader, StateWriter,
StateChangeWriter, StateCommitmentProvider, StateProviderBox, StateWriter,
StaticFileProviderFactory, StatsReader, StorageLocation, StorageReader, StorageTrieWriter,
TransactionVariant, TransactionsProvider, TransactionsProviderExt, TrieWriter,
WithdrawalsProvider,
@ -679,94 +679,6 @@ impl<TX: DbTx + 'static, N: NodeTypesForProvider> DatabaseProvider<TX, N> {
})
}
/// Return the last N blocks of state, recreating the [`ExecutionOutcome`].
///
/// 1. Iterate over the [`BlockBodyIndices`][tables::BlockBodyIndices] table to get all the
/// transaction ids.
/// 2. Iterate over the [`StorageChangeSets`][tables::StorageChangeSets] table and the
/// [`AccountChangeSets`][tables::AccountChangeSets] tables in reverse order to reconstruct
/// the changesets.
/// - In order to have both the old and new values in the changesets, we also access the
/// plain state tables.
/// 3. While iterating over the changeset tables, if we encounter a new account or storage slot,
/// we:
/// 1. Take the old value from the changeset
/// 2. Take the new value from the plain state
/// 3. Save the old value to the local state
/// 4. While iterating over the changeset tables, if we encounter an account/storage slot we
/// have seen before we:
/// 1. Take the old value from the changeset
/// 2. Take the new value from the local state
/// 3. Set the local state to the value in the changeset
///
/// If the range is empty, or there are no blocks for the given range, then this returns `None`.
pub fn get_state(
&self,
range: RangeInclusive<BlockNumber>,
) -> ProviderResult<Option<ExecutionOutcome>> {
if range.is_empty() {
return Ok(None)
}
let start_block_number = *range.start();
// We are not removing block meta as it is used to get block changesets.
let block_bodies = self.get::<tables::BlockBodyIndices>(range.clone())?;
// get transaction receipts
let Some(from_transaction_num) = block_bodies.first().map(|bodies| bodies.1.first_tx_num())
else {
return Ok(None)
};
let Some(to_transaction_num) = block_bodies.last().map(|bodies| bodies.1.last_tx_num())
else {
return Ok(None)
};
let storage_range = BlockNumberAddress::range(range.clone());
let storage_changeset = self.get::<tables::StorageChangeSets>(storage_range)?;
let account_changeset = self.get::<tables::AccountChangeSets>(range)?;
// This is not working for blocks that are not at tip. as plain state is not the last
// state of end range. We should rename the functions or add support to access
// History state. Accessing history state can be tricky but we are not gaining
// anything.
let mut plain_accounts_cursor = self.tx.cursor_read::<tables::PlainAccountState>()?;
let mut plain_storage_cursor = self.tx.cursor_dup_read::<tables::PlainStorageState>()?;
let (state, reverts) = self.populate_bundle_state(
account_changeset,
storage_changeset,
&mut plain_accounts_cursor,
&mut plain_storage_cursor,
)?;
// iterate over block body and create ExecutionResult
let mut receipt_iter =
self.get::<tables::Receipts>(from_transaction_num..=to_transaction_num)?.into_iter();
let mut receipts = Vec::with_capacity(block_bodies.len());
// loop break if we are at the end of the blocks.
for (_, block_body) in block_bodies {
let mut block_receipts = Vec::with_capacity(block_body.tx_count as usize);
for _ in block_body.tx_num_range() {
if let Some((_, receipt)) = receipt_iter.next() {
block_receipts.push(Some(receipt));
}
}
receipts.push(block_receipts);
}
Ok(Some(ExecutionOutcome::new_init(
state,
reverts,
Vec::new(),
receipts.into(),
start_block_number,
Vec::new(),
)))
}
/// Populate a [`BundleStateInit`] and [`RevertsInit`] using cursors over the
/// [`PlainAccountState`] and [`PlainStorageState`] tables, based on the given storage and
/// account changesets.
@ -2039,9 +1951,11 @@ impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> StateChangeWriter
/// 1. Take the old value from the changeset
/// 2. Take the new value from the local state
/// 3. Set the local state to the value in the changeset
fn remove_state(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<()> {
fn remove_state_above(&self, block: BlockNumber) -> ProviderResult<()> {
let range = block + 1..=self.last_block_number()?;
if range.is_empty() {
return Ok(())
return Ok(());
}
// We are not removing block meta as it is used to get block changesets.
@ -2131,7 +2045,9 @@ impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> StateChangeWriter
/// 1. Take the old value from the changeset
/// 2. Take the new value from the local state
/// 3. Set the local state to the value in the changeset
fn take_state(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<ExecutionOutcome> {
fn take_state_above(&self, block: BlockNumber) -> ProviderResult<ExecutionOutcome> {
let range = block + 1..=self.last_block_number()?;
if range.is_empty() {
return Ok(ExecutionOutcome::default())
}
@ -2672,12 +2588,6 @@ impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> HistoryWriter for DatabaseProvi
}
}
impl<TX: DbTx + 'static, N: NodeTypesForProvider> StateReader for DatabaseProvider<TX, N> {
fn get_state(&self, block: BlockNumber) -> ProviderResult<Option<ExecutionOutcome>> {
self.get_state(block..=block)
}
}
impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider + 'static> BlockExecutionWriter
for DatabaseProvider<TX, N>
{
@ -2691,7 +2601,7 @@ impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider + 'static> BlockExecu
self.unwind_trie_state_range(range.clone())?;
// get execution res
let execution_state = self.take_state(range.clone())?;
let execution_state = self.take_state_above(block)?;
let blocks = self.sealed_block_with_senders_range(range)?;
@ -2712,10 +2622,10 @@ impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider + 'static> BlockExecu
) -> ProviderResult<()> {
let range = block + 1..=self.last_block_number()?;
self.unwind_trie_state_range(range.clone())?;
self.unwind_trie_state_range(range)?;
// remove execution res
self.remove_state(range)?;
self.remove_state_above(block)?;
// remove block bodies it is needed for both get block range and get block execution results
// that is why it is deleted afterwards.