mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: add StorageWriter standalone type (#9507)
This commit is contained in:
@ -63,6 +63,11 @@ impl PruneModes {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether there is any kind of receipt pruning configuration.
|
||||
pub fn has_receipts_pruning(&self) -> bool {
|
||||
self.receipts.is_some() || !self.receipts_log_filter.is_empty()
|
||||
}
|
||||
|
||||
/// Returns true if all prune modes are set to [`None`].
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self == &Self::none()
|
||||
|
||||
@ -20,3 +20,6 @@ pub mod lockfile;
|
||||
|
||||
/// Provider error
|
||||
pub mod provider;
|
||||
|
||||
/// Writer error
|
||||
pub mod writer;
|
||||
|
||||
@ -139,6 +139,9 @@ pub enum ProviderError {
|
||||
/// Storage lock error.
|
||||
#[error(transparent)]
|
||||
StorageLockError(#[from] crate::lockfile::StorageLockError),
|
||||
/// Storage writer error.
|
||||
#[error(transparent)]
|
||||
StorageWriterError(#[from] crate::writer::StorageWriterError),
|
||||
}
|
||||
|
||||
impl From<reth_fs_util::FsPathError> for ProviderError {
|
||||
|
||||
15
crates/storage/errors/src/writer.rs
Normal file
15
crates/storage/errors/src/writer.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use crate::db::DatabaseError;
|
||||
|
||||
/// `StorageWriter` related errors
|
||||
#[derive(Clone, Debug, thiserror_no_std::Error, PartialEq, Eq)]
|
||||
pub enum StorageWriterError {
|
||||
/// Database writer is missing
|
||||
#[error("Database writer is missing")]
|
||||
MissingDatabaseWriter,
|
||||
/// Static file writer is missing
|
||||
#[error("Static file writer is missing")]
|
||||
MissingStaticFileWriter,
|
||||
/// Database-related errors.
|
||||
#[error(transparent)]
|
||||
Database(#[from] DatabaseError),
|
||||
}
|
||||
@ -1,67 +1,28 @@
|
||||
use crate::{
|
||||
providers::StaticFileProviderRWRefMut, DatabaseProviderRW, StateChanges, StateReverts,
|
||||
StateWriter,
|
||||
};
|
||||
use reth_db::{tables, Database};
|
||||
use reth_db_api::{
|
||||
cursor::{DbCursorRO, DbCursorRW},
|
||||
transaction::{DbTx, DbTxMut},
|
||||
providers::StaticFileProviderRWRefMut, writer::StorageWriter, DatabaseProviderRW, StateChanges,
|
||||
StateReverts, StateWriter,
|
||||
};
|
||||
use reth_db::Database;
|
||||
pub use reth_execution_types::*;
|
||||
use reth_primitives::StaticFileSegment;
|
||||
use reth_storage_errors::provider::{ProviderError, ProviderResult};
|
||||
use reth_storage_errors::provider::ProviderResult;
|
||||
pub use revm::db::states::OriginalValuesKnown;
|
||||
|
||||
impl StateWriter for ExecutionOutcome {
|
||||
fn write_to_storage<DB>(
|
||||
self,
|
||||
provider_rw: &DatabaseProviderRW<DB>,
|
||||
mut static_file_producer: Option<StaticFileProviderRWRefMut<'_>>,
|
||||
static_file_producer: Option<StaticFileProviderRWRefMut<'_>>,
|
||||
is_value_known: OriginalValuesKnown,
|
||||
) -> ProviderResult<()>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
let tx = provider_rw.tx_ref();
|
||||
let (plain_state, reverts) = self.bundle.into_plain_state_and_reverts(is_value_known);
|
||||
|
||||
StateReverts(reverts).write_to_db(provider_rw, self.first_block)?;
|
||||
|
||||
// write receipts
|
||||
let mut bodies_cursor = tx.cursor_read::<tables::BlockBodyIndices>()?;
|
||||
let mut receipts_cursor = tx.cursor_write::<tables::Receipts>()?;
|
||||
|
||||
// ATTENTION: Any potential future refactor or change to how this loop works should keep in
|
||||
// mind that the static file producer must always call `increment_block` even if the block
|
||||
// has no receipts. Keeping track of the exact block range of the segment is needed for
|
||||
// consistency, querying and file range segmentation.
|
||||
let blocks = self.receipts.into_iter().enumerate();
|
||||
for (idx, receipts) in blocks {
|
||||
let block_number = self.first_block + idx as u64;
|
||||
let first_tx_index = bodies_cursor
|
||||
.seek_exact(block_number)?
|
||||
.map(|(_, indices)| indices.first_tx_num())
|
||||
.ok_or_else(|| ProviderError::BlockBodyIndicesNotFound(block_number))?;
|
||||
|
||||
if let Some(static_file_producer) = &mut static_file_producer {
|
||||
// Increment block on static file header.
|
||||
static_file_producer.increment_block(StaticFileSegment::Receipts, block_number)?;
|
||||
let receipts = receipts.into_iter().enumerate().map(|(tx_idx, receipt)| {
|
||||
Ok((
|
||||
first_tx_index + tx_idx as u64,
|
||||
receipt
|
||||
.expect("receipt should not be filtered when saving to static files."),
|
||||
))
|
||||
});
|
||||
static_file_producer.append_receipts(receipts)?;
|
||||
} else if !receipts.is_empty() {
|
||||
for (tx_idx, receipt) in receipts.into_iter().enumerate() {
|
||||
if let Some(receipt) = receipt {
|
||||
receipts_cursor.append(first_tx_index + tx_idx as u64, receipt)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
StorageWriter::new(Some(provider_rw), static_file_producer)
|
||||
.append_receipts_from_blocks(self.first_block, self.receipts.into_iter())?;
|
||||
|
||||
StateChanges(plain_state).write_to_db(provider_rw)?;
|
||||
|
||||
@ -73,11 +34,12 @@ impl StateWriter for ExecutionOutcome {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{test_utils::create_test_provider_factory, AccountReader};
|
||||
use reth_db::test_utils::create_test_rw_db;
|
||||
use reth_db::{tables, test_utils::create_test_rw_db};
|
||||
use reth_db_api::{
|
||||
cursor::DbDupCursorRO,
|
||||
cursor::{DbCursorRO, DbDupCursorRO},
|
||||
database::Database,
|
||||
models::{AccountBeforeTx, BlockNumberAddress},
|
||||
transaction::{DbTx, DbTxMut},
|
||||
};
|
||||
use reth_primitives::{
|
||||
keccak256, Account, Address, Receipt, Receipts, StorageEntry, B256, U256,
|
||||
|
||||
@ -36,6 +36,9 @@ pub use reth_execution_types::*;
|
||||
pub mod bundle_state;
|
||||
pub use bundle_state::{OriginalValuesKnown, StateChanges, StateReverts};
|
||||
|
||||
/// Writer standalone type.
|
||||
pub mod writer;
|
||||
|
||||
pub(crate) fn to_range<R: std::ops::RangeBounds<u64>>(bounds: R) -> std::ops::Range<u64> {
|
||||
let start = match bounds.start_bound() {
|
||||
std::ops::Bound::Included(&v) => v,
|
||||
|
||||
@ -113,6 +113,11 @@ impl<TX> DatabaseProvider<TX> {
|
||||
pub const fn static_file_provider(&self) -> &StaticFileProvider {
|
||||
&self.static_file_provider
|
||||
}
|
||||
|
||||
/// Returns reference to prune modes.
|
||||
pub const fn prune_modes_ref(&self) -> &PruneModes {
|
||||
&self.prune_modes
|
||||
}
|
||||
}
|
||||
|
||||
impl<TX: DbTxMut> DatabaseProvider<TX> {
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
use crate::providers::static_file::metrics::StaticFileProviderOperation;
|
||||
|
||||
use super::{
|
||||
manager::StaticFileProviderInner, metrics::StaticFileProviderMetrics, StaticFileProvider,
|
||||
};
|
||||
use crate::providers::static_file::metrics::StaticFileProviderOperation;
|
||||
use dashmap::mapref::one::RefMut;
|
||||
use reth_codecs::Compact;
|
||||
use reth_db_api::models::CompactU256;
|
||||
|
||||
30
crates/storage/provider/src/writer/database.rs
Normal file
30
crates/storage/provider/src/writer/database.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use reth_db::{
|
||||
cursor::{DbCursorRO, DbCursorRW},
|
||||
tables,
|
||||
};
|
||||
use reth_errors::ProviderResult;
|
||||
use reth_primitives::{BlockNumber, Receipt, TxNumber};
|
||||
use reth_storage_api::ReceiptWriter;
|
||||
|
||||
pub(crate) struct DatabaseWriter<'a, W>(pub(crate) &'a mut W);
|
||||
|
||||
impl<'a, W> ReceiptWriter for DatabaseWriter<'a, W>
|
||||
where
|
||||
W: DbCursorRO<tables::Receipts> + DbCursorRW<tables::Receipts>,
|
||||
{
|
||||
fn append_block_receipts(
|
||||
&mut self,
|
||||
first_tx_index: TxNumber,
|
||||
_: BlockNumber,
|
||||
receipts: Vec<Option<Receipt>>,
|
||||
) -> ProviderResult<()> {
|
||||
if !receipts.is_empty() {
|
||||
for (tx_idx, receipt) in receipts.into_iter().enumerate() {
|
||||
if let Some(receipt) = receipt {
|
||||
self.0.append(first_tx_index + tx_idx as u64, receipt)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
157
crates/storage/provider/src/writer/mod.rs
Normal file
157
crates/storage/provider/src/writer/mod.rs
Normal file
@ -0,0 +1,157 @@
|
||||
use crate::{providers::StaticFileProviderRWRefMut, DatabaseProviderRW};
|
||||
use reth_db::{
|
||||
cursor::DbCursorRO,
|
||||
tables,
|
||||
transaction::{DbTx, DbTxMut},
|
||||
Database,
|
||||
};
|
||||
use reth_errors::{ProviderError, ProviderResult};
|
||||
use reth_primitives::BlockNumber;
|
||||
use reth_storage_api::ReceiptWriter;
|
||||
use reth_storage_errors::writer::StorageWriterError;
|
||||
use static_file::StaticFileWriter;
|
||||
|
||||
mod database;
|
||||
mod static_file;
|
||||
use database::DatabaseWriter;
|
||||
|
||||
enum StorageType<C = (), S = ()> {
|
||||
Database(C),
|
||||
StaticFile(S),
|
||||
}
|
||||
|
||||
/// [`StorageWriter`] is responsible for managing the writing to either database, static file or
|
||||
/// both.
|
||||
#[derive(Debug)]
|
||||
pub struct StorageWriter<'a, 'b, DB: Database> {
|
||||
database_writer: Option<&'a DatabaseProviderRW<DB>>,
|
||||
static_file_writer: Option<StaticFileProviderRWRefMut<'b>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, DB: Database> StorageWriter<'a, 'b, DB> {
|
||||
/// Creates a new instance of [`StorageWriter`].
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `database_writer`: An optional reference to a database writer.
|
||||
/// - `static_file_writer`: An optional mutable reference to a static file writer.
|
||||
pub const fn new(
|
||||
database_writer: Option<&'a DatabaseProviderRW<DB>>,
|
||||
static_file_writer: Option<StaticFileProviderRWRefMut<'b>>,
|
||||
) -> Self {
|
||||
Self { database_writer, static_file_writer }
|
||||
}
|
||||
|
||||
/// Creates a new instance of [`StorageWriter`] from a database writer.
|
||||
pub const fn from_database_writer(database_writer: &'a DatabaseProviderRW<DB>) -> Self {
|
||||
Self::new(Some(database_writer), None)
|
||||
}
|
||||
|
||||
/// Creates a new instance of [`StorageWriter`] from a static file writer.
|
||||
pub const fn from_static_file_writer(
|
||||
static_file_writer: StaticFileProviderRWRefMut<'b>,
|
||||
) -> Self {
|
||||
Self::new(None, Some(static_file_writer))
|
||||
}
|
||||
|
||||
/// Returns a reference to the database writer.
|
||||
///
|
||||
/// # Panics
|
||||
/// If the database writer is not set.
|
||||
fn database_writer(&self) -> &DatabaseProviderRW<DB> {
|
||||
self.database_writer.as_ref().expect("should exist")
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the static file writer.
|
||||
///
|
||||
/// # Panics
|
||||
/// If the static file writer is not set.
|
||||
fn static_file_writer(&mut self) -> &mut StaticFileProviderRWRefMut<'b> {
|
||||
self.static_file_writer.as_mut().expect("should exist")
|
||||
}
|
||||
|
||||
/// Ensures that the database writer is set.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok(())` if the database writer is set.
|
||||
/// - `Err(StorageWriterError::MissingDatabaseWriter)` if the database writer is not set.
|
||||
const fn ensure_database_writer(&self) -> Result<(), StorageWriterError> {
|
||||
if self.database_writer.is_none() {
|
||||
return Err(StorageWriterError::MissingDatabaseWriter)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensures that the static file writer is set.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok(())` if the static file writer is set.
|
||||
/// - `Err(StorageWriterError::MissingStaticFileWriter)` if the static file writer is not set.
|
||||
const fn ensure_static_file_writer(&self) -> Result<(), StorageWriterError> {
|
||||
if self.static_file_writer.is_none() {
|
||||
return Err(StorageWriterError::MissingStaticFileWriter)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Appends receipts block by block.
|
||||
///
|
||||
/// ATTENTION: If called from [`StorageWriter`] without a static file producer, it will always
|
||||
/// write them to database. Otherwise, it will look into the pruning configuration to decide.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `initial_block_number`: The starting block number.
|
||||
/// - `blocks`: An iterator over blocks, each block having a vector of optional receipts. If
|
||||
/// `receipt` is `None`, it has been pruned.
|
||||
pub fn append_receipts_from_blocks(
|
||||
mut self,
|
||||
initial_block_number: BlockNumber,
|
||||
blocks: impl Iterator<Item = Vec<Option<reth_primitives::Receipt>>>,
|
||||
) -> ProviderResult<()> {
|
||||
self.ensure_database_writer()?;
|
||||
let mut bodies_cursor =
|
||||
self.database_writer().tx_ref().cursor_read::<tables::BlockBodyIndices>()?;
|
||||
|
||||
// We write receipts to database in two situations:
|
||||
// * If we are in live sync. In this case, `StorageWriter` is built without a static file
|
||||
// writer.
|
||||
// * If there is any kind of receipt pruning
|
||||
let mut storage_type = if self.static_file_writer.is_none() ||
|
||||
self.database_writer().prune_modes_ref().has_receipts_pruning()
|
||||
{
|
||||
StorageType::Database(
|
||||
self.database_writer().tx_ref().cursor_write::<tables::Receipts>()?,
|
||||
)
|
||||
} else {
|
||||
self.ensure_static_file_writer()?;
|
||||
StorageType::StaticFile(self.static_file_writer())
|
||||
};
|
||||
|
||||
for (idx, receipts) in blocks.enumerate() {
|
||||
let block_number = initial_block_number + idx as u64;
|
||||
|
||||
let first_tx_index = bodies_cursor
|
||||
.seek_exact(block_number)?
|
||||
.map(|(_, indices)| indices.first_tx_num())
|
||||
.ok_or_else(|| ProviderError::BlockBodyIndicesNotFound(block_number))?;
|
||||
|
||||
match &mut storage_type {
|
||||
StorageType::Database(cursor) => {
|
||||
DatabaseWriter(cursor).append_block_receipts(
|
||||
first_tx_index,
|
||||
block_number,
|
||||
receipts,
|
||||
)?;
|
||||
}
|
||||
StorageType::StaticFile(sf) => {
|
||||
StaticFileWriter(*sf).append_block_receipts(
|
||||
first_tx_index,
|
||||
block_number,
|
||||
receipts,
|
||||
)?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
26
crates/storage/provider/src/writer/static_file.rs
Normal file
26
crates/storage/provider/src/writer/static_file.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use crate::providers::StaticFileProviderRWRefMut;
|
||||
use reth_errors::ProviderResult;
|
||||
use reth_primitives::{BlockNumber, Receipt, StaticFileSegment, TxNumber};
|
||||
use reth_storage_api::ReceiptWriter;
|
||||
|
||||
pub(crate) struct StaticFileWriter<'a, W>(pub(crate) &'a mut W);
|
||||
|
||||
impl<'a> ReceiptWriter for StaticFileWriter<'a, StaticFileProviderRWRefMut<'_>> {
|
||||
fn append_block_receipts(
|
||||
&mut self,
|
||||
first_tx_index: TxNumber,
|
||||
block_number: BlockNumber,
|
||||
receipts: Vec<Option<Receipt>>,
|
||||
) -> ProviderResult<()> {
|
||||
// Increment block on static file header.
|
||||
self.0.increment_block(StaticFileSegment::Receipts, block_number)?;
|
||||
let receipts = receipts.into_iter().enumerate().map(|(tx_idx, receipt)| {
|
||||
Ok((
|
||||
first_tx_index + tx_idx as u64,
|
||||
receipt.expect("receipt should not be filtered when saving to static files."),
|
||||
))
|
||||
});
|
||||
self.0.append_receipts(receipts)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
use crate::BlockIdReader;
|
||||
use reth_primitives::{BlockHashOrNumber, BlockId, BlockNumberOrTag, Receipt, TxHash, TxNumber};
|
||||
use reth_primitives::{
|
||||
BlockHashOrNumber, BlockId, BlockNumber, BlockNumberOrTag, Receipt, TxHash, TxNumber,
|
||||
};
|
||||
use reth_storage_errors::provider::ProviderResult;
|
||||
use std::ops::RangeBounds;
|
||||
|
||||
@ -66,3 +68,20 @@ pub trait ReceiptProviderIdExt: ReceiptProvider + BlockIdReader {
|
||||
self.receipts_by_block_id(number_or_tag.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Writer trait for writing [`Receipt`] data.
|
||||
pub trait ReceiptWriter {
|
||||
/// Appends receipts for a block.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `first_tx_index`: The transaction number of the first receipt in the block.
|
||||
/// - `block_number`: The block number to which the receipts belong.
|
||||
/// - `receipts`: A vector of optional receipts in the block. If `None`, it means they were
|
||||
/// pruned.
|
||||
fn append_block_receipts(
|
||||
&mut self,
|
||||
first_tx_index: TxNumber,
|
||||
block_number: BlockNumber,
|
||||
receipts: Vec<Option<Receipt>>,
|
||||
) -> ProviderResult<()>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user