Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["crates/*"]
resolver = "2"

[workspace.package]
version = "0.14.1"
version = "0.14.2"
edition = "2024"
rust-version = "1.88"
authors = ["init4"]
Expand Down
17 changes: 7 additions & 10 deletions crates/node-tests/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use reth_db::{PlainAccountState, transaction::DbTxMut};
use reth_exex_test_utils::{Adapter, TestExExHandle, TmpDB as TmpDb};
use reth_node_api::FullNodeComponents;
use signet_db::DbProviderExt;
use signet_node::SignetNode;
use signet_node::SignetNodeBuilder;
use signet_node_config::test_utils::test_config;
use signet_node_types::{NodeStatus, SignetNodeTypes};
use signet_test_utils::contracts::counter::COUNTER_DEPLOY_CODE;
Expand Down Expand Up @@ -104,15 +104,12 @@ impl SignetTestContext {

let alias_oracle: Arc<Mutex<HashSet<Address>>> = Arc::new(Mutex::new(HashSet::default()));

// instantiate Signet Node, booting rpc
let (node, mut node_status) = SignetNode::new(
ctx,
cfg.clone(),
factory.clone(),
Arc::clone(&alias_oracle),
Default::default(),
)
.unwrap();
let (node, mut node_status) = SignetNodeBuilder::new(cfg.clone())
.with_ctx(ctx)
.with_factory(factory.clone())
.with_alias_oracle(Arc::clone(&alias_oracle))
.build()
.unwrap();

// Spawn the node, and wait for it to indicate RPC readiness.
let node = tokio::spawn(node.start());
Expand Down
17 changes: 7 additions & 10 deletions crates/node-tests/tests/db.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use alloy::primitives::{Address, hex, map::HashSet};
use alloy::primitives::hex;
use reth::providers::BlockReader;
use serial_test::serial;
use signet_node::SignetNode;
use signet_node::SignetNodeBuilder;
use signet_node_config::test_utils::test_config;
use signet_node_tests::utils::create_test_provider_factory_with_chain_spec;
use std::sync::Arc;
Expand All @@ -17,14 +17,11 @@ async fn test_genesis() {
assert_eq!(chain_spec.genesis().config.chain_id, consts.unwrap().ru_chain_id());

let factory = create_test_provider_factory_with_chain_spec(chain_spec.clone());
let (_, _) = SignetNode::<_, _, HashSet<Address>>::new(
ctx,
cfg.clone(),
factory.clone(),
Default::default(),
Default::default(),
)
.unwrap();
let (_, _) = SignetNodeBuilder::new(cfg.clone())
.with_ctx(ctx)
.with_factory(factory.clone())
.build()
.unwrap();

let genesis_block = factory.provider().unwrap().block_by_number(0).unwrap().unwrap();

Expand Down
323 changes: 323 additions & 0 deletions crates/node/src/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
#![allow(clippy::type_complexity)]

use crate::{GENESIS_JOURNAL_HASH, SignetNode};
use eyre::OptionExt;
use reth::{
primitives::EthPrimitives,
providers::{BlockHashReader, ProviderFactory, StateProviderFactory},
};
use reth_db::transaction::DbTxMut;
use reth_db_common::init;
use reth_exex::ExExContext;
use reth_node_api::{FullNodeComponents, NodeTypes};
use signet_block_processor::AliasOracleFactory;
use signet_db::DbProviderExt;
use signet_node_config::SignetNodeConfig;
use signet_node_types::{NodeStatus, NodeTypesDbTrait, SignetNodeTypes};
use std::sync::Arc;

/// A type that does not implement [`AliasOracleFactory`].
#[derive(Debug, Clone, Copy)]
pub struct NotAnAof;

/// A type that does not implement [`NodeTypesDbTrait`].
#[derive(Debug, Clone, Copy)]
pub struct NotADb;

/// Builder for [`SignetNode`]. This is the main way to create a signet node.
///
/// The builder requires the following components to be set before building:
/// - An [`ExExContext`], via [`Self::with_ctx`].
/// - A [`ProviderFactory`] for the signet node's database.
/// - This can be provided directly via [`Self::with_factory`].
/// - Or created from a database implementing [`NodeTypesDbTrait`] via
/// [`Self::with_db`].
/// - If not set directly, can be created from the config via
/// [`Self::with_config_db`].
/// - An [`AliasOracleFactory`], via [`Self::with_alias_oracle`].
/// - If not set, a default one will be created from the [`ExExContext`]'s
/// provider.
/// - A `reqwest::Client`, via [`Self::with_client`].
/// - If not set, a default client will be created.
pub struct SignetNodeBuilder<Host = (), Db = NotADb, Aof = NotAnAof> {
config: SignetNodeConfig,
alias_oracle: Option<Aof>,
ctx: Option<Host>,
factory: Option<Db>,
client: Option<reqwest::Client>,
}

impl<Host, Db, Aof> core::fmt::Debug for SignetNodeBuilder<Host, Db, Aof> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SignetNodeBuilder").finish_non_exhaustive()
}
}

impl SignetNodeBuilder {
/// Create a new SignetNodeBuilder instance.
pub const fn new(config: SignetNodeConfig) -> Self {
Self { config, alias_oracle: None, ctx: None, factory: None, client: None }
}
}

impl<Host, Db, Aof> SignetNodeBuilder<Host, Db, Aof> {
/// Set the DB for the signet node.
pub fn with_db<NewDb: NodeTypesDbTrait>(
self,
db: NewDb,
) -> eyre::Result<SignetNodeBuilder<Host, ProviderFactory<SignetNodeTypes<NewDb>>, Aof>> {
let factory = ProviderFactory::new(
db,
self.config.chain_spec().clone(),
self.config.static_file_rw()?,
);

Ok(SignetNodeBuilder {
config: self.config,
alias_oracle: self.alias_oracle,
ctx: self.ctx,
factory: Some(factory),
client: self.client,
})
}

/// Set the DB for the signet node from config, opening the mdbx database.
pub fn with_config_db(
self,
) -> eyre::Result<
SignetNodeBuilder<Host, ProviderFactory<SignetNodeTypes<Arc<reth_db::DatabaseEnv>>>, Aof>,
> {
let factory = ProviderFactory::new_with_database_path(
self.config.database_path(),
self.config.chain_spec().clone(),
reth_db::mdbx::DatabaseArguments::default(),
self.config.static_file_rw().unwrap(),
)?;
Ok(SignetNodeBuilder {
config: self.config,
alias_oracle: self.alias_oracle,
ctx: self.ctx,
factory: Some(factory),
client: self.client,
})
}

/// Set the provider factory for the signet node.
///
/// This is an alternative to [`Self::with_db`] and
/// [`Self::with_config_db`].
pub fn with_factory<NewDb>(
self,
factory: ProviderFactory<SignetNodeTypes<NewDb>>,
) -> SignetNodeBuilder<Host, ProviderFactory<SignetNodeTypes<NewDb>>, Aof>
where
NewDb: NodeTypesDbTrait,
{
SignetNodeBuilder {
config: self.config,
alias_oracle: self.alias_oracle,
ctx: self.ctx,
factory: Some(factory),
client: self.client,
}
}

/// Set the [`ExExContext`] for the signet node.
pub fn with_ctx<NewHost>(
self,
ctx: ExExContext<NewHost>,
) -> SignetNodeBuilder<ExExContext<NewHost>, Db, Aof>
where
NewHost: FullNodeComponents,
NewHost::Types: NodeTypes<Primitives = EthPrimitives>,
{
SignetNodeBuilder {
config: self.config,
alias_oracle: self.alias_oracle,
ctx: Some(ctx),
factory: self.factory,
client: self.client,
}
}

/// Set the [`AliasOracleFactory`] for the signet node.
pub fn with_alias_oracle<NewAof: AliasOracleFactory>(
self,
alias_oracle: NewAof,
) -> SignetNodeBuilder<Host, Db, NewAof> {
SignetNodeBuilder {
config: self.config,
alias_oracle: Some(alias_oracle),
ctx: self.ctx,
factory: self.factory,
client: self.client,
}
}

/// Set the reqwest client for the signet node.
pub fn with_client(mut self, client: reqwest::Client) -> SignetNodeBuilder<Host, Db, Aof> {
self.client = Some(client);
self
}
}

impl<Host, Db, Aof> SignetNodeBuilder<ExExContext<Host>, ProviderFactory<SignetNodeTypes<Db>>, Aof>
where
Host: FullNodeComponents,
Host::Types: NodeTypes<Primitives = EthPrimitives>,
Db: NodeTypesDbTrait,
{
/// Prebuild checks for the signet node builder. Shared by all build
/// commands.
fn prebuild(&mut self) -> eyre::Result<()> {
self.client.get_or_insert_default();
self.ctx.as_ref().ok_or_eyre("Launch context must be set")?;
let factory = self.factory.as_ref().ok_or_eyre("Provider factory must be set")?;

// This check appears redundant with the same check made in
// `init_genesis`, but is not. We init the genesis DB state but then we
// drop some of it, and reuse those tables for our own nefarious
// purposes. If we attempt to drop those tables AFTER we have reused
// them, we will get a key deser error (as the tables will contain keys
// the old schema does not permit). This check ensures we only attempt
// to drop the tables once.
if matches!(
factory.block_hash(0),
Ok(None)
| Err(reth::providers::ProviderError::MissingStaticFileBlock(
reth::primitives::StaticFileSegment::Headers,
0
))
) {
init::init_genesis(factory)?;

factory.provider_rw()?.update(
|writer: &mut reth::providers::DatabaseProviderRW<Db, SignetNodeTypes<Db>>| {
writer.tx_mut().clear::<reth_db::tables::HashedAccounts>()?;
writer.tx_mut().clear::<reth_db::tables::HashedStorages>()?;
writer.tx_mut().clear::<reth_db::tables::AccountsTrie>()?;

writer.tx_ref().put::<signet_db::JournalHashes>(0, GENESIS_JOURNAL_HASH)?;
// we do not need to pre-populate the `ZenithHeaders` or
// `SignetEvents` tables, as missing data is legal in those
// tables

Ok(())
},
)?;
}

Ok(())
}
}

impl<Host> SignetNodeBuilder<ExExContext<Host>, NotADb, NotAnAof>
where
Host: FullNodeComponents,
Host::Types: NodeTypes<Primitives = EthPrimitives>,
{
/// Build the node. This performs the following steps:
///
/// - Runs prebuild checks.
/// - Inits the rollup DB from genesis if needed.
/// - Creates a default `AliasOracleFactory` from the host DB.
///
/// # Panics
///
/// If called outside a tokio runtime.
pub fn build(
self,
) -> eyre::Result<(
SignetNode<Host, Arc<reth_db::DatabaseEnv>, Box<dyn StateProviderFactory>>,
tokio::sync::watch::Receiver<NodeStatus>,
)> {
self.with_config_db()?.build()
}
}

impl<Host, Aof> SignetNodeBuilder<ExExContext<Host>, NotADb, Aof>
where
Host: FullNodeComponents,
Host::Types: NodeTypes<Primitives = EthPrimitives>,
Aof: AliasOracleFactory,
{
/// Build the node. This performs the following steps:
///
/// - Runs prebuild checks.
/// - Inits the rollup DB from genesis if needed.
///
/// # Panics
///
/// If called outside a tokio runtime.
pub fn build(
self,
) -> eyre::Result<(
SignetNode<Host, Arc<reth_db::DatabaseEnv>, Aof>,
tokio::sync::watch::Receiver<NodeStatus>,
)> {
self.with_config_db()?.build()
}
}

impl<Host, Db> SignetNodeBuilder<ExExContext<Host>, ProviderFactory<SignetNodeTypes<Db>>, NotAnAof>
where
Host: FullNodeComponents<Provider: StateProviderFactory>,
Host::Types: NodeTypes<Primitives = EthPrimitives>,
Db: NodeTypesDbTrait,
{
/// Build the node. This performs the following steps:
///
/// - Runs prebuild checks.
/// - Inits the rollup DB from genesis if needed.
/// - Creates a default `AliasOracleFactory` from the host DB.
///
/// # Panics
///
/// If called outside a tokio runtime.
pub fn build(
mut self,
) -> eyre::Result<(SignetNode<Host, Db>, tokio::sync::watch::Receiver<NodeStatus>)> {
self.prebuild()?;
// This allows the node to look up contract status.
let ctx = self.ctx.unwrap();
let provider = ctx.provider().clone();
let alias_oracle: Box<dyn StateProviderFactory> = Box::new(provider);

SignetNode::new_unsafe(
ctx,
self.config,
self.factory.unwrap(),
alias_oracle,
self.client.unwrap(),
)
}
}

impl<Host, Db, Aof> SignetNodeBuilder<ExExContext<Host>, ProviderFactory<SignetNodeTypes<Db>>, Aof>
where
Host: FullNodeComponents,
Host::Types: NodeTypes<Primitives = EthPrimitives>,
Db: NodeTypesDbTrait,
Aof: AliasOracleFactory,
{
/// Build the node. This performs the following steps:
///
/// - Runs prebuild checks.
/// - Inits the rollup DB from genesis if needed.
///
/// # Panics
///
/// If called outside a tokio runtime.
pub fn build(
mut self,
) -> eyre::Result<(SignetNode<Host, Db, Aof>, tokio::sync::watch::Receiver<NodeStatus>)> {
self.prebuild()?;
SignetNode::new_unsafe(
self.ctx.unwrap(),
self.config,
self.factory.unwrap(),
self.alias_oracle.unwrap(),
self.client.unwrap(),
)
}
}
Loading