#![allow(clippy::type_complexity)]
use crate::{NodeStatus, SignetNode};
use eyre::OptionExt;
use signet_blobber::CacheHandle;
use signet_block_processor::AliasOracleFactory;
use signet_cold::BlockData;
use signet_hot::db::{HotDbRead, UnsafeDbWrite};
use signet_node_config::SignetNodeConfig;
use signet_node_types::HostNotifier;
use signet_rpc::{ServeConfig, StorageRpcConfig};
use signet_storage::{HistoryRead, HistoryWrite, HotKv, HotKvRead, UnifiedStorage};
use std::sync::Arc;
use tracing::info;
use trevm::revm::database::DBErrorMarker;
#[derive(Debug, Clone, Copy)]
pub struct NotAnAof;
#[derive(Debug, Clone, Copy)]
pub struct NotAStorage;
pub struct SignetNodeBuilder<Notifier = (), Storage = NotAStorage, Aof = NotAnAof> {
config: SignetNodeConfig,
alias_oracle: Option<Aof>,
notifier: Option<Notifier>,
storage: Option<Storage>,
client: Option<reqwest::Client>,
blob_cacher: Option<CacheHandle>,
serve_config: Option<ServeConfig>,
rpc_config: Option<StorageRpcConfig>,
}
impl<Notifier, Storage, Aof> core::fmt::Debug for SignetNodeBuilder<Notifier, Storage, Aof> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SignetNodeBuilder").finish_non_exhaustive()
}
}
impl SignetNodeBuilder {
pub const fn new(config: SignetNodeConfig) -> Self {
Self {
config,
alias_oracle: None,
notifier: None,
storage: None,
client: None,
blob_cacher: None,
serve_config: None,
rpc_config: None,
}
}
}
impl<Notifier, Storage, Aof> SignetNodeBuilder<Notifier, Storage, Aof> {
pub fn with_storage<H: HotKv>(
self,
storage: Arc<UnifiedStorage<H>>,
) -> SignetNodeBuilder<Notifier, Arc<UnifiedStorage<H>>, Aof> {
SignetNodeBuilder {
config: self.config,
alias_oracle: self.alias_oracle,
notifier: self.notifier,
storage: Some(storage),
client: self.client,
blob_cacher: self.blob_cacher,
serve_config: self.serve_config,
rpc_config: self.rpc_config,
}
}
pub fn with_notifier<N: HostNotifier>(self, notifier: N) -> SignetNodeBuilder<N, Storage, Aof> {
SignetNodeBuilder {
config: self.config,
alias_oracle: self.alias_oracle,
notifier: Some(notifier),
storage: self.storage,
client: self.client,
blob_cacher: self.blob_cacher,
serve_config: self.serve_config,
rpc_config: self.rpc_config,
}
}
pub fn with_alias_oracle<NewAof: AliasOracleFactory>(
self,
alias_oracle: NewAof,
) -> SignetNodeBuilder<Notifier, Storage, NewAof> {
SignetNodeBuilder {
config: self.config,
alias_oracle: Some(alias_oracle),
notifier: self.notifier,
storage: self.storage,
client: self.client,
blob_cacher: self.blob_cacher,
serve_config: self.serve_config,
rpc_config: self.rpc_config,
}
}
pub fn with_client(mut self, client: reqwest::Client) -> Self {
self.client = Some(client);
self
}
pub fn with_blob_cacher(mut self, blob_cacher: CacheHandle) -> Self {
self.blob_cacher = Some(blob_cacher);
self
}
pub fn with_serve_config(mut self, serve_config: ServeConfig) -> Self {
self.serve_config = Some(serve_config);
self
}
pub const fn with_rpc_config(mut self, rpc_config: StorageRpcConfig) -> Self {
self.rpc_config = Some(rpc_config);
self
}
}
impl<N, H, Aof> SignetNodeBuilder<N, Arc<UnifiedStorage<H>>, Aof>
where
N: HostNotifier,
H: HotKv + Clone + Send + Sync + 'static,
<H::RoTx as HotKvRead>::Error: DBErrorMarker,
Aof: AliasOracleFactory,
{
async fn prebuild(&mut self) -> eyre::Result<()> {
self.client.get_or_insert_default();
self.notifier.as_ref().ok_or_eyre("Notifier must be set")?;
let storage = self.storage.as_ref().ok_or_eyre("Storage must be set")?;
let reader = storage.reader()?;
let has_hot_genesis = reader.has_block(0)?;
drop(reader);
if !has_hot_genesis {
let genesis = self.config.genesis();
let hardforks = signet_genesis::genesis_hardforks(genesis);
let writer = storage.hot().writer()?;
writer.load_genesis(genesis, &hardforks)?;
writer.commit()?;
info!("loaded genesis into hot storage");
}
let has_cold_genesis = storage.cold_reader().get_latest_block().await?.is_some();
if !has_cold_genesis {
let reader = storage.reader()?;
let genesis_header =
reader.get_header(0)?.ok_or_eyre("genesis header missing from hot storage")?;
drop(reader);
let genesis_block = BlockData::new(genesis_header, vec![], vec![], vec![], None);
storage.cold().append_block(genesis_block).await?;
info!("loaded genesis into cold storage");
}
Ok(())
}
pub async fn build(
mut self,
) -> eyre::Result<(SignetNode<N, H, Aof>, tokio::sync::watch::Receiver<NodeStatus>)> {
self.prebuild().await?;
SignetNode::new_unsafe(
self.notifier.expect("enforced by typestate"),
self.config,
self.storage.expect("enforced by typestate"),
self.alias_oracle.expect("enforced by typestate"),
self.client.expect("set by prebuild"),
self.blob_cacher.ok_or_eyre("blob cacher must be set")?,
self.serve_config.ok_or_eyre("serve config must be set")?,
self.rpc_config.ok_or_eyre("rpc config must be set")?,
)
}
}