|
| 1 | +#![allow(clippy::type_complexity)] |
| 2 | + |
| 3 | +use crate::{GENESIS_JOURNAL_HASH, SignetNode}; |
| 4 | +use eyre::OptionExt; |
| 5 | +use reth::{ |
| 6 | + primitives::EthPrimitives, |
| 7 | + providers::{BlockHashReader, ProviderFactory, StateProviderFactory}, |
| 8 | +}; |
| 9 | +use reth_db::transaction::DbTxMut; |
| 10 | +use reth_db_common::init; |
| 11 | +use reth_exex::ExExContext; |
| 12 | +use reth_node_api::{FullNodeComponents, NodeTypes}; |
| 13 | +use signet_block_processor::AliasOracleFactory; |
| 14 | +use signet_db::DbProviderExt; |
| 15 | +use signet_node_config::SignetNodeConfig; |
| 16 | +use signet_node_types::{NodeStatus, NodeTypesDbTrait, SignetNodeTypes}; |
| 17 | +use std::sync::Arc; |
| 18 | + |
| 19 | +/// A type that does not implement [`AliasOracleFactory`]. |
| 20 | +#[derive(Debug, Clone, Copy)] |
| 21 | +pub struct NotAnAof; |
| 22 | + |
| 23 | +/// A type that does not implement [`NodeTypesDbTrait`]. |
| 24 | +#[derive(Debug, Clone, Copy)] |
| 25 | +pub struct NotADb; |
| 26 | + |
| 27 | +/// Builder for [`SignetNode`]. This is the main way to create a signet node. |
| 28 | +/// |
| 29 | +/// The builder requires the following components to be set before building: |
| 30 | +/// - An [`ExExContext`], via [`Self::with_ctx`]. |
| 31 | +/// - A [`ProviderFactory`] for the signet node's database. |
| 32 | +/// - This can be provided directly via [`Self::with_factory`]. |
| 33 | +/// - Or created from a database implementing [`NodeTypesDbTrait`] via |
| 34 | +/// [`Self::with_db`]. |
| 35 | +/// - If not set directly, can be created from the config via |
| 36 | +/// [`Self::with_config_db`]. |
| 37 | +/// - An [`AliasOracleFactory`], via [`Self::with_alias_oracle`]. |
| 38 | +/// - If not set, a default one will be created from the [`ExExContext`]'s |
| 39 | +/// provider. |
| 40 | +/// - A `reqwest::Client`, via [`Self::with_client`]. |
| 41 | +/// - If not set, a default client will be created. |
| 42 | +pub struct SignetNodeBuilder<Host = (), Db = NotADb, Aof = NotAnAof> { |
| 43 | + config: SignetNodeConfig, |
| 44 | + alias_oracle: Option<Aof>, |
| 45 | + ctx: Option<Host>, |
| 46 | + factory: Option<Db>, |
| 47 | + client: Option<reqwest::Client>, |
| 48 | +} |
| 49 | + |
| 50 | +impl<Host, Db, Aof> core::fmt::Debug for SignetNodeBuilder<Host, Db, Aof> { |
| 51 | + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 52 | + f.debug_struct("SignetNodeBuilder").finish_non_exhaustive() |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +impl SignetNodeBuilder { |
| 57 | + /// Create a new SignetNodeBuilder instance. |
| 58 | + pub const fn new(config: SignetNodeConfig) -> Self { |
| 59 | + Self { config, alias_oracle: None, ctx: None, factory: None, client: None } |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +impl<Host, Db, Aof> SignetNodeBuilder<Host, Db, Aof> { |
| 64 | + /// Set the DB for the signet node. |
| 65 | + pub fn with_db<NewDb: NodeTypesDbTrait>( |
| 66 | + self, |
| 67 | + db: NewDb, |
| 68 | + ) -> eyre::Result<SignetNodeBuilder<Host, ProviderFactory<SignetNodeTypes<NewDb>>, Aof>> { |
| 69 | + let factory = ProviderFactory::new( |
| 70 | + db, |
| 71 | + self.config.chain_spec().clone(), |
| 72 | + self.config.static_file_rw()?, |
| 73 | + ); |
| 74 | + |
| 75 | + Ok(SignetNodeBuilder { |
| 76 | + config: self.config, |
| 77 | + alias_oracle: self.alias_oracle, |
| 78 | + ctx: self.ctx, |
| 79 | + factory: Some(factory), |
| 80 | + client: self.client, |
| 81 | + }) |
| 82 | + } |
| 83 | + |
| 84 | + /// Set the DB for the signet node from config, opening the mdbx database. |
| 85 | + pub fn with_config_db( |
| 86 | + self, |
| 87 | + ) -> eyre::Result< |
| 88 | + SignetNodeBuilder<Host, ProviderFactory<SignetNodeTypes<Arc<reth_db::DatabaseEnv>>>, Aof>, |
| 89 | + > { |
| 90 | + let factory = ProviderFactory::new_with_database_path( |
| 91 | + self.config.database_path(), |
| 92 | + self.config.chain_spec().clone(), |
| 93 | + reth_db::mdbx::DatabaseArguments::default(), |
| 94 | + self.config.static_file_rw().unwrap(), |
| 95 | + )?; |
| 96 | + Ok(SignetNodeBuilder { |
| 97 | + config: self.config, |
| 98 | + alias_oracle: self.alias_oracle, |
| 99 | + ctx: self.ctx, |
| 100 | + factory: Some(factory), |
| 101 | + client: self.client, |
| 102 | + }) |
| 103 | + } |
| 104 | + |
| 105 | + /// Set the provider factory for the signet node. |
| 106 | + /// |
| 107 | + /// This is an alternative to [`Self::with_db`] and |
| 108 | + /// [`Self::with_config_db`]. |
| 109 | + pub fn with_factory<NewDb>( |
| 110 | + self, |
| 111 | + factory: ProviderFactory<SignetNodeTypes<NewDb>>, |
| 112 | + ) -> SignetNodeBuilder<Host, ProviderFactory<SignetNodeTypes<NewDb>>, Aof> |
| 113 | + where |
| 114 | + NewDb: NodeTypesDbTrait, |
| 115 | + { |
| 116 | + SignetNodeBuilder { |
| 117 | + config: self.config, |
| 118 | + alias_oracle: self.alias_oracle, |
| 119 | + ctx: self.ctx, |
| 120 | + factory: Some(factory), |
| 121 | + client: self.client, |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + /// Set the [`ExExContext`] for the signet node. |
| 126 | + pub fn with_ctx<NewHost>( |
| 127 | + self, |
| 128 | + ctx: ExExContext<NewHost>, |
| 129 | + ) -> SignetNodeBuilder<ExExContext<NewHost>, Db, Aof> |
| 130 | + where |
| 131 | + NewHost: FullNodeComponents, |
| 132 | + NewHost::Types: NodeTypes<Primitives = EthPrimitives>, |
| 133 | + { |
| 134 | + SignetNodeBuilder { |
| 135 | + config: self.config, |
| 136 | + alias_oracle: self.alias_oracle, |
| 137 | + ctx: Some(ctx), |
| 138 | + factory: self.factory, |
| 139 | + client: self.client, |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + /// Set the [`AliasOracleFactory`] for the signet node. |
| 144 | + pub fn with_alias_oracle<NewAof: AliasOracleFactory>( |
| 145 | + self, |
| 146 | + alias_oracle: NewAof, |
| 147 | + ) -> SignetNodeBuilder<Host, Db, NewAof> { |
| 148 | + SignetNodeBuilder { |
| 149 | + config: self.config, |
| 150 | + alias_oracle: Some(alias_oracle), |
| 151 | + ctx: self.ctx, |
| 152 | + factory: self.factory, |
| 153 | + client: self.client, |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + /// Set the reqwest client for the signet node. |
| 158 | + pub fn with_client(mut self, client: reqwest::Client) -> SignetNodeBuilder<Host, Db, Aof> { |
| 159 | + self.client = Some(client); |
| 160 | + self |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +impl<Host, Db, Aof> SignetNodeBuilder<ExExContext<Host>, ProviderFactory<SignetNodeTypes<Db>>, Aof> |
| 165 | +where |
| 166 | + Host: FullNodeComponents, |
| 167 | + Host::Types: NodeTypes<Primitives = EthPrimitives>, |
| 168 | + Db: NodeTypesDbTrait, |
| 169 | +{ |
| 170 | + /// Prebuild checks for the signet node builder. Shared by all build |
| 171 | + /// commands. |
| 172 | + fn prebuild(&mut self) -> eyre::Result<()> { |
| 173 | + self.client.get_or_insert_default(); |
| 174 | + self.ctx.as_ref().ok_or_eyre("Launch context must be set")?; |
| 175 | + let factory = self.factory.as_ref().ok_or_eyre("Provider factory must be set")?; |
| 176 | + |
| 177 | + // This check appears redundant with the same check made in |
| 178 | + // `init_genesis`, but is not. We init the genesis DB state but then we |
| 179 | + // drop some of it, and reuse those tables for our own nefarious |
| 180 | + // purposes. If we attempt to drop those tables AFTER we have reused |
| 181 | + // them, we will get a key deser error (as the tables will contain keys |
| 182 | + // the old schema does not permit). This check ensures we only attempt |
| 183 | + // to drop the tables once. |
| 184 | + if matches!( |
| 185 | + factory.block_hash(0), |
| 186 | + Ok(None) |
| 187 | + | Err(reth::providers::ProviderError::MissingStaticFileBlock( |
| 188 | + reth::primitives::StaticFileSegment::Headers, |
| 189 | + 0 |
| 190 | + )) |
| 191 | + ) { |
| 192 | + init::init_genesis(factory)?; |
| 193 | + |
| 194 | + factory.provider_rw()?.update( |
| 195 | + |writer: &mut reth::providers::DatabaseProviderRW<Db, SignetNodeTypes<Db>>| { |
| 196 | + writer.tx_mut().clear::<reth_db::tables::HashedAccounts>()?; |
| 197 | + writer.tx_mut().clear::<reth_db::tables::HashedStorages>()?; |
| 198 | + writer.tx_mut().clear::<reth_db::tables::AccountsTrie>()?; |
| 199 | + |
| 200 | + writer.tx_ref().put::<signet_db::JournalHashes>(0, GENESIS_JOURNAL_HASH)?; |
| 201 | + // we do not need to pre-populate the `ZenithHeaders` or |
| 202 | + // `SignetEvents` tables, as missing data is legal in those |
| 203 | + // tables |
| 204 | + |
| 205 | + Ok(()) |
| 206 | + }, |
| 207 | + )?; |
| 208 | + } |
| 209 | + |
| 210 | + Ok(()) |
| 211 | + } |
| 212 | +} |
| 213 | + |
| 214 | +impl<Host> SignetNodeBuilder<ExExContext<Host>, NotADb, NotAnAof> |
| 215 | +where |
| 216 | + Host: FullNodeComponents, |
| 217 | + Host::Types: NodeTypes<Primitives = EthPrimitives>, |
| 218 | +{ |
| 219 | + /// Build the node. This performs the following steps: |
| 220 | + /// |
| 221 | + /// - Runs prebuild checks. |
| 222 | + /// - Inits the rollup DB from genesis if needed. |
| 223 | + /// - Creates a default `AliasOracleFactory` from the host DB. |
| 224 | + /// |
| 225 | + /// # Panics |
| 226 | + /// |
| 227 | + /// If called outside a tokio runtime. |
| 228 | + pub fn build( |
| 229 | + self, |
| 230 | + ) -> eyre::Result<( |
| 231 | + SignetNode<Host, Arc<reth_db::DatabaseEnv>, Box<dyn StateProviderFactory>>, |
| 232 | + tokio::sync::watch::Receiver<NodeStatus>, |
| 233 | + )> { |
| 234 | + self.with_config_db()?.build() |
| 235 | + } |
| 236 | +} |
| 237 | + |
| 238 | +impl<Host, Aof> SignetNodeBuilder<ExExContext<Host>, NotADb, Aof> |
| 239 | +where |
| 240 | + Host: FullNodeComponents, |
| 241 | + Host::Types: NodeTypes<Primitives = EthPrimitives>, |
| 242 | + Aof: AliasOracleFactory, |
| 243 | +{ |
| 244 | + /// Build the node. This performs the following steps: |
| 245 | + /// |
| 246 | + /// - Runs prebuild checks. |
| 247 | + /// - Inits the rollup DB from genesis if needed. |
| 248 | + /// |
| 249 | + /// # Panics |
| 250 | + /// |
| 251 | + /// If called outside a tokio runtime. |
| 252 | + pub fn build( |
| 253 | + self, |
| 254 | + ) -> eyre::Result<( |
| 255 | + SignetNode<Host, Arc<reth_db::DatabaseEnv>, Aof>, |
| 256 | + tokio::sync::watch::Receiver<NodeStatus>, |
| 257 | + )> { |
| 258 | + self.with_config_db()?.build() |
| 259 | + } |
| 260 | +} |
| 261 | + |
| 262 | +impl<Host, Db> SignetNodeBuilder<ExExContext<Host>, ProviderFactory<SignetNodeTypes<Db>>, NotAnAof> |
| 263 | +where |
| 264 | + Host: FullNodeComponents<Provider: StateProviderFactory>, |
| 265 | + Host::Types: NodeTypes<Primitives = EthPrimitives>, |
| 266 | + Db: NodeTypesDbTrait, |
| 267 | +{ |
| 268 | + /// Build the node. This performs the following steps: |
| 269 | + /// |
| 270 | + /// - Runs prebuild checks. |
| 271 | + /// - Inits the rollup DB from genesis if needed. |
| 272 | + /// - Creates a default `AliasOracleFactory` from the host DB. |
| 273 | + /// |
| 274 | + /// # Panics |
| 275 | + /// |
| 276 | + /// If called outside a tokio runtime. |
| 277 | + pub fn build( |
| 278 | + mut self, |
| 279 | + ) -> eyre::Result<(SignetNode<Host, Db>, tokio::sync::watch::Receiver<NodeStatus>)> { |
| 280 | + self.prebuild()?; |
| 281 | + // This allows the node to look up contract status. |
| 282 | + let ctx = self.ctx.unwrap(); |
| 283 | + let provider = ctx.provider().clone(); |
| 284 | + let alias_oracle: Box<dyn StateProviderFactory> = Box::new(provider); |
| 285 | + |
| 286 | + SignetNode::new_unsafe( |
| 287 | + ctx, |
| 288 | + self.config, |
| 289 | + self.factory.unwrap(), |
| 290 | + alias_oracle, |
| 291 | + self.client.unwrap(), |
| 292 | + ) |
| 293 | + } |
| 294 | +} |
| 295 | + |
| 296 | +impl<Host, Db, Aof> SignetNodeBuilder<ExExContext<Host>, ProviderFactory<SignetNodeTypes<Db>>, Aof> |
| 297 | +where |
| 298 | + Host: FullNodeComponents, |
| 299 | + Host::Types: NodeTypes<Primitives = EthPrimitives>, |
| 300 | + Db: NodeTypesDbTrait, |
| 301 | + Aof: AliasOracleFactory, |
| 302 | +{ |
| 303 | + /// Build the node. This performs the following steps: |
| 304 | + /// |
| 305 | + /// - Runs prebuild checks. |
| 306 | + /// - Inits the rollup DB from genesis if needed. |
| 307 | + /// |
| 308 | + /// # Panics |
| 309 | + /// |
| 310 | + /// If called outside a tokio runtime. |
| 311 | + pub fn build( |
| 312 | + mut self, |
| 313 | + ) -> eyre::Result<(SignetNode<Host, Db, Aof>, tokio::sync::watch::Receiver<NodeStatus>)> { |
| 314 | + self.prebuild()?; |
| 315 | + SignetNode::new_unsafe( |
| 316 | + self.ctx.unwrap(), |
| 317 | + self.config, |
| 318 | + self.factory.unwrap(), |
| 319 | + self.alias_oracle.unwrap(), |
| 320 | + self.client.unwrap(), |
| 321 | + ) |
| 322 | + } |
| 323 | +} |
0 commit comments