reth_provider/providers/database/
builder.rs

1//! Helper builder entrypoint to instantiate a [`ProviderFactory`].
2//!
3//! This also includes general purpose staging types that provide builder style functions that lead
4//! up to the intended build target.
5
6use crate::{providers::StaticFileProvider, ProviderFactory};
7use reth_db::{mdbx::DatabaseArguments, open_db_read_only, DatabaseEnv};
8use reth_db_api::{database_metrics::DatabaseMetrics, Database};
9use reth_node_types::{NodeTypes, NodeTypesWithDBAdapter};
10use std::{
11    marker::PhantomData,
12    path::{Path, PathBuf},
13    sync::Arc,
14};
15
16/// Helper type to create a [`ProviderFactory`].
17///
18/// This type is the entry point for a stage based builder.
19///
20/// The intended staging is:
21///  1. Configure the database: [`ProviderFactoryBuilder::db`]
22///  2. Configure the chainspec: `chainspec`
23///  3. Configure the [`StaticFileProvider`]: `static_file`
24#[derive(Debug)]
25pub struct ProviderFactoryBuilder<N> {
26    _types: PhantomData<N>,
27}
28
29impl<N> ProviderFactoryBuilder<N> {
30    /// Maps the [`NodeTypes`] of this builder.
31    pub fn types<T>(self) -> ProviderFactoryBuilder<T> {
32        ProviderFactoryBuilder::default()
33    }
34
35    /// Configures the database.
36    pub fn db<DB>(self, db: DB) -> TypesAnd1<N, DB> {
37        TypesAnd1::new(db)
38    }
39
40    /// Opens the database with the given chainspec and [`ReadOnlyConfig`].
41    ///
42    /// # Open a monitored instance
43    ///
44    /// This is recommended when the new read-only instance is used with an active node.
45    ///
46    /// ```no_run
47    /// use reth_chainspec::MAINNET;
48    /// use reth_node_types::NodeTypes;
49    /// use reth_provider::providers::ProviderFactoryBuilder;
50    ///
51    /// fn demo<N: NodeTypes<ChainSpec = reth_chainspec::ChainSpec>>() {
52    ///     let provider_factory = ProviderFactoryBuilder::<N>::default()
53    ///         .open_read_only(MAINNET.clone(), "datadir")
54    ///         .unwrap();
55    /// }
56    /// ```
57    ///
58    /// # Open an unmonitored instance
59    ///
60    /// This is recommended when no changes to the static files are expected (e.g. no active node)
61    ///
62    /// ```no_run
63    /// use reth_chainspec::MAINNET;
64    /// use reth_node_types::NodeTypes;
65    /// ///
66    /// use reth_provider::providers::{ProviderFactoryBuilder, ReadOnlyConfig};
67    ///
68    /// fn demo<N: NodeTypes<ChainSpec = reth_chainspec::ChainSpec>>() {
69    ///     let provider_factory = ProviderFactoryBuilder::<N>::default()
70    ///         .open_read_only(MAINNET.clone(), ReadOnlyConfig::from_datadir("datadir").no_watch())
71    ///         .unwrap();
72    /// }
73    /// ```
74    pub fn open_read_only(
75        self,
76        chainspec: Arc<N::ChainSpec>,
77        config: impl Into<ReadOnlyConfig>,
78    ) -> eyre::Result<ProviderFactory<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>>>
79    where
80        N: NodeTypes,
81    {
82        let ReadOnlyConfig { db_dir, db_args, static_files_dir, watch_static_files } =
83            config.into();
84        Ok(self
85            .db(Arc::new(open_db_read_only(db_dir, db_args)?))
86            .chainspec(chainspec)
87            .static_file(StaticFileProvider::read_only(static_files_dir, watch_static_files)?)
88            .build_provider_factory())
89    }
90}
91
92impl<N> Default for ProviderFactoryBuilder<N> {
93    fn default() -> Self {
94        Self { _types: Default::default() }
95    }
96}
97
98/// Settings for how to open the database and static files.
99///
100/// The default derivation from a path assumes the path is the datadir:
101/// [`ReadOnlyConfig::from_datadir`]
102#[derive(Debug, Clone)]
103pub struct ReadOnlyConfig {
104    /// The path to the database directory.
105    pub db_dir: PathBuf,
106    /// How to open the database
107    pub db_args: DatabaseArguments,
108    /// The path to the static file dir
109    pub static_files_dir: PathBuf,
110    /// Whether the static files should be watched for changes.
111    pub watch_static_files: bool,
112}
113
114impl ReadOnlyConfig {
115    /// Derives the [`ReadOnlyConfig`] from the datadir.
116    ///
117    /// By default this assumes the following datadir layout:
118    ///
119    /// ```text
120    ///  -`datadir`
121    ///    |__db
122    ///    |__static_files
123    /// ```
124    ///
125    /// By default this watches the static file directory for changes, see also
126    /// [`StaticFileProvider::read_only`]
127    pub fn from_datadir(datadir: impl AsRef<Path>) -> Self {
128        let datadir = datadir.as_ref();
129        Self::from_dirs(datadir.join("db"), datadir.join("static_files"))
130    }
131
132    /// Derives the [`ReadOnlyConfig`] from the database dir.
133    ///
134    /// By default this assumes the following datadir layout:
135    ///
136    /// ```text
137    ///    - db
138    ///    -static_files
139    /// ```
140    ///
141    /// By default this watches the static file directory for changes, see also
142    /// [`StaticFileProvider::read_only`]
143    ///
144    /// # Panics
145    ///
146    /// If the path does not exist
147    pub fn from_db_dir(db_dir: impl AsRef<Path>) -> Self {
148        let db_dir = db_dir.as_ref();
149        let static_files_dir = std::fs::canonicalize(db_dir)
150            .unwrap()
151            .parent()
152            .unwrap()
153            .to_path_buf()
154            .join("static_files");
155        Self::from_dirs(db_dir, static_files_dir)
156    }
157
158    /// Creates the config for the given paths.
159    ///
160    ///
161    /// By default this watches the static file directory for changes, see also
162    /// [`StaticFileProvider::read_only`]
163    pub fn from_dirs(db_dir: impl AsRef<Path>, static_files_dir: impl AsRef<Path>) -> Self {
164        Self {
165            static_files_dir: static_files_dir.as_ref().into(),
166            db_dir: db_dir.as_ref().into(),
167            db_args: Default::default(),
168            watch_static_files: true,
169        }
170    }
171
172    /// Configures the db arguments used when opening the database.
173    pub fn with_db_args(mut self, db_args: impl Into<DatabaseArguments>) -> Self {
174        self.db_args = db_args.into();
175        self
176    }
177
178    /// Configures the db directory.
179    pub fn with_db_dir(mut self, db_dir: impl Into<PathBuf>) -> Self {
180        self.db_dir = db_dir.into();
181        self
182    }
183
184    /// Configures the static file directory.
185    pub fn with_static_file_dir(mut self, static_file_dir: impl Into<PathBuf>) -> Self {
186        self.static_files_dir = static_file_dir.into();
187        self
188    }
189
190    /// Whether the static file directory should be watches for changes, see also
191    /// [`StaticFileProvider::read_only`]
192    pub const fn set_watch_static_files(&mut self, watch_static_files: bool) {
193        self.watch_static_files = watch_static_files;
194    }
195
196    /// Don't watch the static files for changes.
197    ///
198    /// This is only recommended if this is used without a running node instance that modifies
199    /// static files.
200    pub const fn no_watch(mut self) -> Self {
201        self.set_watch_static_files(false);
202        self
203    }
204}
205
206impl<T> From<T> for ReadOnlyConfig
207where
208    T: AsRef<Path>,
209{
210    fn from(value: T) -> Self {
211        Self::from_datadir(value.as_ref())
212    }
213}
214
215/// This is staging type that contains the configured types and _one_ value.
216#[derive(Debug)]
217pub struct TypesAnd1<N, Val1> {
218    _types: PhantomData<N>,
219    val_1: Val1,
220}
221
222impl<N, Val1> TypesAnd1<N, Val1> {
223    /// Creates a new instance with the given types and one value.
224    pub fn new(val_1: Val1) -> Self {
225        Self { _types: Default::default(), val_1 }
226    }
227
228    /// Configures the chainspec.
229    pub fn chainspec<C>(self, chainspec: Arc<C>) -> TypesAnd2<N, Val1, Arc<C>> {
230        TypesAnd2::new(self.val_1, chainspec)
231    }
232}
233
234/// This is staging type that contains the configured types and _two_ values.
235#[derive(Debug)]
236pub struct TypesAnd2<N, Val1, Val2> {
237    _types: PhantomData<N>,
238    val_1: Val1,
239    val_2: Val2,
240}
241
242impl<N, Val1, Val2> TypesAnd2<N, Val1, Val2> {
243    /// Creates a new instance with the given types and two values.
244    pub fn new(val_1: Val1, val_2: Val2) -> Self {
245        Self { _types: Default::default(), val_1, val_2 }
246    }
247
248    /// Returns the first value.
249    pub const fn val_1(&self) -> &Val1 {
250        &self.val_1
251    }
252
253    /// Returns the second value.
254    pub const fn val_2(&self) -> &Val2 {
255        &self.val_2
256    }
257
258    /// Configures the [`StaticFileProvider`].
259    pub fn static_file(
260        self,
261        static_file_provider: StaticFileProvider<N::Primitives>,
262    ) -> TypesAnd3<N, Val1, Val2, StaticFileProvider<N::Primitives>>
263    where
264        N: NodeTypes,
265    {
266        TypesAnd3::new(self.val_1, self.val_2, static_file_provider)
267    }
268}
269
270/// This is staging type that contains the configured types and _three_ values.
271#[derive(Debug)]
272pub struct TypesAnd3<N, Val1, Val2, Val3> {
273    _types: PhantomData<N>,
274    val_1: Val1,
275    val_2: Val2,
276    val_3: Val3,
277}
278
279impl<N, Val1, Val2, Val3> TypesAnd3<N, Val1, Val2, Val3> {
280    /// Creates a new instance with the given types and three values.
281    pub fn new(val_1: Val1, val_2: Val2, val_3: Val3) -> Self {
282        Self { _types: Default::default(), val_1, val_2, val_3 }
283    }
284}
285
286impl<N, DB> TypesAnd3<N, DB, Arc<N::ChainSpec>, StaticFileProvider<N::Primitives>>
287where
288    N: NodeTypes,
289    DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
290{
291    /// Creates the [`ProviderFactory`].
292    pub fn build_provider_factory(self) -> ProviderFactory<NodeTypesWithDBAdapter<N, DB>> {
293        let Self { _types, val_1, val_2, val_3 } = self;
294        ProviderFactory::new(val_1, val_2, val_3)
295    }
296}