reth_blockchain_tree/
externals.rs

1//! Blockchain tree externals.
2
3use alloy_primitives::{BlockHash, BlockNumber};
4use reth_consensus::FullConsensus;
5use reth_db::{static_file::BlockHashMask, tables};
6use reth_db_api::{cursor::DbCursorRO, transaction::DbTx};
7use reth_node_types::NodeTypesWithDB;
8use reth_primitives::StaticFileSegment;
9use reth_provider::{
10    providers::ProviderNodeTypes, ChainStateBlockReader, ChainStateBlockWriter, ProviderFactory,
11    StaticFileProviderFactory, StatsReader,
12};
13use reth_storage_errors::provider::ProviderResult;
14use std::{collections::BTreeMap, sync::Arc};
15
16pub use reth_provider::providers::{NodeTypesForTree, TreeNodeTypes};
17
18/// A container for external components.
19///
20/// This is a simple container for external components used throughout the blockchain tree
21/// implementation:
22///
23/// - A handle to the database
24/// - A handle to the consensus engine
25/// - The executor factory to execute blocks with
26#[derive(Debug)]
27pub struct TreeExternals<N: NodeTypesWithDB, E> {
28    /// The provider factory, used to commit the canonical chain, or unwind it.
29    pub(crate) provider_factory: ProviderFactory<N>,
30    /// The consensus engine.
31    pub(crate) consensus: Arc<dyn FullConsensus>,
32    /// The executor factory to execute blocks with.
33    pub(crate) executor_factory: E,
34}
35
36impl<N: ProviderNodeTypes, E> TreeExternals<N, E> {
37    /// Create new tree externals.
38    pub fn new(
39        provider_factory: ProviderFactory<N>,
40        consensus: Arc<dyn FullConsensus>,
41        executor_factory: E,
42    ) -> Self {
43        Self { provider_factory, consensus, executor_factory }
44    }
45}
46
47impl<N: ProviderNodeTypes, E> TreeExternals<N, E> {
48    /// Fetches the latest canonical block hashes by walking backwards from the head.
49    ///
50    /// Returns the hashes sorted by increasing block numbers
51    pub(crate) fn fetch_latest_canonical_hashes(
52        &self,
53        num_hashes: usize,
54    ) -> ProviderResult<BTreeMap<BlockNumber, BlockHash>> {
55        // Fetch the latest canonical hashes from the database
56        let mut hashes = self
57            .provider_factory
58            .provider()?
59            .tx_ref()
60            .cursor_read::<tables::CanonicalHeaders>()?
61            .walk_back(None)?
62            .take(num_hashes)
63            .collect::<Result<BTreeMap<BlockNumber, BlockHash>, _>>()?;
64
65        // Fetch the same number of latest canonical hashes from the static_files and merge them
66        // with the database hashes. It is needed due to the fact that we're writing
67        // directly to static_files in pipeline sync, but to the database in live sync,
68        // which means that the latest canonical hashes in the static file might be more recent
69        // than in the database, and vice versa, or even some ranges of the latest
70        // `num_hashes` blocks may be in database, and some ranges in static_files.
71        let static_file_provider = self.provider_factory.static_file_provider();
72        let total_headers = static_file_provider.count_entries::<tables::Headers>()? as u64;
73        if total_headers > 0 {
74            let range =
75                total_headers.saturating_sub(1).saturating_sub(num_hashes as u64)..total_headers;
76
77            hashes.extend(range.clone().zip(static_file_provider.fetch_range_with_predicate(
78                StaticFileSegment::Headers,
79                range,
80                |cursor, number| cursor.get_one::<BlockHashMask>(number.into()),
81                |_| true,
82            )?));
83        }
84
85        // We may have fetched more than `num_hashes` hashes, so we need to truncate the result to
86        // the requested number.
87        let hashes = hashes.into_iter().rev().take(num_hashes).collect();
88        Ok(hashes)
89    }
90
91    pub(crate) fn fetch_latest_finalized_block_number(
92        &self,
93    ) -> ProviderResult<Option<BlockNumber>> {
94        self.provider_factory.provider()?.last_finalized_block_number()
95    }
96
97    pub(crate) fn save_finalized_block_number(
98        &self,
99        block_number: BlockNumber,
100    ) -> ProviderResult<()> {
101        let provider_rw = self.provider_factory.provider_rw()?;
102        provider_rw.save_finalized_block_number(block_number)?;
103        provider_rw.commit()?;
104        Ok(())
105    }
106}