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}