Skip to content
1 change: 1 addition & 0 deletions crates/host-rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ signet-extract.workspace = true
signet-types.workspace = true

alloy.workspace = true
init4-bin-base.workspace = true
futures-util.workspace = true
metrics.workspace = true
thiserror.workspace = true
Expand Down
65 changes: 65 additions & 0 deletions crates/host-rpc/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::{DEFAULT_BACKFILL_BATCH_SIZE, DEFAULT_BUFFER_CAPACITY, RpcHostNotifierBuilder};
use alloy::providers::RootProvider;
use init4_bin_base::utils::{calc::SlotCalculator, from_env::FromEnv, provider::PubSubConfig};

/// Environment-based configuration for the RPC host notifier.
///
/// # Environment Variables
///
/// - `SIGNET_HOST_URL` – WebSocket or IPC URL for the host EL client (required)
/// - `SIGNET_HOST_BUFFER_CAPACITY` – Local chain view size (default: 64)
/// - `SIGNET_HOST_BACKFILL_BATCH_SIZE` – Blocks per backfill batch (default: 32)
///
/// # Example
///
/// ```no_run
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use signet_host_rpc::HostRpcConfig;
/// use init4_bin_base::utils::{calc::SlotCalculator, from_env::FromEnv};
///
/// let config = HostRpcConfig::from_env().unwrap();
/// let slot_calculator = SlotCalculator::new(0, 1_606_824_023, 12);
/// let builder = config.into_builder(slot_calculator).await?;
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone, FromEnv)]
pub struct HostRpcConfig {
/// WebSocket or IPC connection to the host execution layer client.
#[from_env(var = "SIGNET_HOST_URL", desc = "Host EL pubsub URL (ws:// or ipc) [required]")]
provider: PubSubConfig,
/// Local chain view buffer capacity.
#[from_env(
var = "SIGNET_HOST_BUFFER_CAPACITY",
desc = "Chain view buffer capacity [default: 64]",
optional
)]
buffer_capacity: Option<usize>,
/// Blocks per backfill RPC batch.
#[from_env(
var = "SIGNET_HOST_BACKFILL_BATCH_SIZE",
desc = "Backfill batch size [default: 32]",
optional
)]
backfill_batch_size: Option<u64>,
}

impl HostRpcConfig {
/// Connect to the host provider and build an [`RpcHostNotifierBuilder`].
///
/// Uses `slot_calculator` for genesis timestamp rather than
/// duplicating that setting.
pub async fn into_builder(
self,
slot_calculator: SlotCalculator,
) -> Result<RpcHostNotifierBuilder<RootProvider>, alloy::transports::TransportError> {
let provider = self.provider.connect().await?;
Ok(RpcHostNotifierBuilder::new(provider)
.with_buffer_capacity(self.buffer_capacity.unwrap_or(DEFAULT_BUFFER_CAPACITY))
.with_backfill_batch_size(
self.backfill_batch_size.unwrap_or(DEFAULT_BACKFILL_BATCH_SIZE),
)
.with_genesis_timestamp(slot_calculator.start_timestamp()))
}
}
3 changes: 3 additions & 0 deletions crates/host-rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub(crate) const DEFAULT_BACKFILL_BATCH_SIZE: u64 = 32;
mod builder;
pub use builder::RpcHostNotifierBuilder;

mod config;
pub use config::HostRpcConfig;

mod error;
pub use error::RpcHostError;

Expand Down
7 changes: 1 addition & 6 deletions crates/node-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,22 @@ repository.workspace = true

[dependencies]
signet-blobber.workspace = true
signet-evm.workspace = true
signet-storage.workspace = true
signet-types.workspace = true

init4-bin-base.workspace = true

reth.workspace = true
reth-chainspec.workspace = true
alloy.workspace = true

eyre.workspace = true
reqwest.workspace = true
serde.workspace = true
tokio-util.workspace = true
tracing.workspace = true
trevm.workspace = true
signet-genesis.workspace = true

tempfile = { workspace = true, optional = true }

[features]
test_utils = ["dep:tempfile"]
test_utils = []
postgres = ["signet-storage/postgres"]
sqlite = ["signet-storage/sqlite"]
116 changes: 3 additions & 113 deletions crates/node-config/src/core.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,11 @@
use crate::StorageConfig;
use alloy::genesis::Genesis;
use init4_bin_base::utils::{calc::SlotCalculator, from_env::FromEnv};
use reth::primitives::NodePrimitives;
use reth::providers::providers::StaticFileProvider;
use reth_chainspec::ChainSpec;
use signet_blobber::BlobFetcherConfig;
use signet_genesis::GenesisSpec;
use signet_types::constants::{ConfigError, SignetSystemConstants};
use std::{
borrow::Cow,
fmt::Display,
path::PathBuf,
sync::{Arc, OnceLock},
};
use std::{borrow::Cow, fmt::Display, sync::OnceLock};
use tracing::warn;
use trevm::revm::primitives::hardfork::SpecId;

/// Defines the default port for serving Signet Node JSON RPC requests over http.
pub const SIGNET_NODE_DEFAULT_HTTP_PORT: u16 = 5959u16;

/// Configuration for a Signet Node instance. Contains system contract and signer
/// information.
Expand All @@ -28,9 +16,6 @@ pub struct SignetNodeConfig {
#[from_env(infallible)]
block_extractor: BlobFetcherConfig,

/// Path to the static files for reth StaticFileProviders.
#[from_env(var = "SIGNET_STATIC_PATH", desc = "Path to the static files", infallible)]
static_path: Cow<'static, str>,
/// Unified storage configuration (hot + cold MDBX paths).
#[from_env(infallible)]
storage: StorageConfig,
Expand All @@ -42,20 +27,6 @@ pub struct SignetNodeConfig {
optional
)]
forward_url: Option<Cow<'static, str>>,
/// RPC port to serve JSON-RPC requests
#[from_env(var = "RPC_PORT", desc = "RPC port to serve JSON-RPC requests", optional)]
http_port: Option<u16>,
/// Websocket port to serve JSON-RPC requests
#[from_env(var = "WS_RPC_PORT", desc = "Websocket port to serve JSON-RPC requests", optional)]
ws_port: Option<u16>,
/// IPC endpoint to serve JSON-RPC requests
#[from_env(
var = "IPC_ENDPOINT",
desc = "IPC endpoint to serve JSON-RPC requests",
infallible,
optional
)]
ipc_endpoint: Option<Cow<'static, str>>,

/// Configuration loaded from genesis file, or known genesis.
genesis: GenesisSpec,
Expand All @@ -82,29 +53,20 @@ impl Display for SignetNodeConfig {

impl SignetNodeConfig {
/// Create a new Signet Node configuration.
#[allow(clippy::too_many_arguments)]
pub const fn new(
block_extractor: BlobFetcherConfig,
static_path: Cow<'static, str>,
storage: StorageConfig,
forward_url: Option<Cow<'static, str>>,
rpc_port: u16,
ws_port: u16,
ipc_endpoint: Option<Cow<'static, str>>,
genesis: GenesisSpec,
slot_calculator: SlotCalculator,
) -> Self {
Self {
block_extractor,
static_path,
storage,
forward_url,
http_port: Some(rpc_port),
ws_port: Some(ws_port),
ipc_endpoint,
genesis,
slot_calculator,
backfill_max_blocks: None, // Uses default of 10,000 via accessor
backfill_max_blocks: None,
}
}

Expand Down Expand Up @@ -133,26 +95,6 @@ impl SignetNodeConfig {
self.slot_calculator
}

/// Get the static path as a str.
pub fn static_path_str(&self) -> &str {
&self.static_path
}

/// Get the static path.
pub fn static_path(&self) -> PathBuf {
self.static_path.as_ref().to_owned().into()
}

/// Get the static file provider for read-only access.
pub fn static_file_ro<N: NodePrimitives>(&self) -> eyre::Result<StaticFileProvider<N>> {
StaticFileProvider::read_only(self.static_path(), true).map_err(Into::into)
}

/// Get the static file provider for read-write access.
pub fn static_file_rw<N: NodePrimitives>(&self) -> eyre::Result<StaticFileProvider<N>> {
StaticFileProvider::read_write(self.static_path()).map_err(Into::into)
}

/// Get the storage configuration.
pub const fn storage(&self) -> &StorageConfig {
&self.storage
Expand All @@ -167,65 +109,17 @@ impl SignetNodeConfig {
.ok()
}

/// Returns the port for serving JSON RPC requests for Signet Node.
pub const fn http_port(&self) -> u16 {
if let Some(port) = self.http_port {
return port;
}
SIGNET_NODE_DEFAULT_HTTP_PORT
}

/// Set the HTTP port for serving JSON RPC requests for Signet Node.
pub const fn set_http_port(&mut self, port: u16) {
self.http_port = Some(port);
}

/// Returns the port for serving Websocket RPC requests for Signet Node.
pub const fn ws_port(&self) -> u16 {
if let Some(port) = self.ws_port {
return port;
}
SIGNET_NODE_DEFAULT_HTTP_PORT + 1
}

/// Set the websocket port for serving JSON RPC requests for Signet.
pub const fn set_ws_port(&mut self, port: u16) {
self.ws_port = Some(port);
}

/// Returns the IPC endpoint for serving JSON RPC requests for Signet, if any.
pub fn ipc_endpoint(&self) -> Option<&str> {
self.ipc_endpoint.as_deref()
}

/// Set the IPC endpoint for serving JSON RPC requests for Signet Node.
pub fn set_ipc_endpoint(&mut self, endpoint: Cow<'static, str>) {
self.ipc_endpoint = Some(endpoint);
}

/// Returns the rollup genesis configuration if any has been loaded.
pub fn genesis(&self) -> &'static Genesis {
static ONCE: OnceLock<Cow<'static, Genesis>> = OnceLock::new();
ONCE.get_or_init(|| self.genesis.load_genesis().expect("Failed to load genesis").rollup)
}

/// Create a new chain spec for the Signet Node chain.
pub fn chain_spec(&self) -> &Arc<ChainSpec> {
static SPEC: OnceLock<Arc<ChainSpec>> = OnceLock::new();
SPEC.get_or_init(|| Arc::new(self.genesis().clone().into()))
}

/// Get the system constants for the Signet Node chain.
pub fn constants(&self) -> Result<SignetSystemConstants, ConfigError> {
SignetSystemConstants::try_from_genesis(self.genesis())
}

/// Get the current spec id for the Signet Node chain.
pub fn spec_id(&self, block: u64, timestamp: u64) -> SpecId {
signet_evm::EthereumHardfork::active_hardforks(&self.genesis().config, block, timestamp)
.spec_id()
}

/// Get the maximum number of blocks to process per backfill batch.
/// Returns `Some(10_000)` by default if not configured, to avoid OOM
/// during mainnet sync (reth's default of 500K is too aggressive).
Expand All @@ -244,15 +138,11 @@ mod defaults {
fn default() -> Self {
Self {
block_extractor: BlobFetcherConfig::new(Cow::Borrowed("")),
static_path: Cow::Borrowed(""),
storage: StorageConfig::new(Cow::Borrowed(""), Cow::Borrowed("")),
forward_url: None,
http_port: Some(SIGNET_NODE_DEFAULT_HTTP_PORT),
ws_port: Some(SIGNET_NODE_DEFAULT_HTTP_PORT + 1),
ipc_endpoint: None,
genesis: GenesisSpec::Known(KnownChains::Test),
slot_calculator: SlotCalculator::new(0, 0, 12),
backfill_max_blocks: None, // Uses default of 10,000 via accessor
backfill_max_blocks: None,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/node-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

mod core;
pub use core::{SIGNET_NODE_DEFAULT_HTTP_PORT, SignetNodeConfig};
pub use core::SignetNodeConfig;

// NB: RPC config merging (previously `merge_rpc_configs`) is now the
// responsibility of the host adapter crate (e.g. `signet-host-reth`).
Expand Down
15 changes: 2 additions & 13 deletions crates/node-config/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,17 @@ use signet_blobber::BlobFetcherConfig;
use signet_genesis::GenesisSpec;
use signet_types::constants::KnownChains;
use std::borrow::Cow;
use tempfile::tempdir;

/// Make a test config
pub fn test_config() -> SignetNodeConfig {
let mut tempdir = tempdir().unwrap().keep();
tempdir.push("signet.ipc");

// Make a new test config with the IPC endpoint set to the tempdir.
let mut cfg = TEST_CONFIG;
cfg.set_ipc_endpoint(Cow::Owned(format!("{}", tempdir.to_string_lossy())));
cfg
pub const fn test_config() -> SignetNodeConfig {
TEST_CONFIG
}

/// Test SignetNodeConfig
const TEST_CONFIG: SignetNodeConfig = SignetNodeConfig::new(
BlobFetcherConfig::new(Cow::Borrowed("")),
Cow::Borrowed("NOP"),
StorageConfig::new(Cow::Borrowed("NOP"), Cow::Borrowed("NOP")),
None,
31391, // NOP
31392, // NOP
Some(Cow::Borrowed("/trethNOP")),
GenesisSpec::Known(KnownChains::Test),
SlotCalculator::new(0, 0, 12),
);
1 change: 1 addition & 0 deletions crates/node-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ signet-zenith.workspace = true
alloy.workspace = true
eyre.workspace = true
reqwest.workspace = true
tempfile.workspace = true
tokio.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
Expand Down
7 changes: 5 additions & 2 deletions crates/node-tests/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ impl SignetTestContext {
pub async fn new() -> (Self, JoinHandle<eyre::Result<()>>) {
let cfg = test_config();
let blob_source = MemoryBlobSource::new();
let ipc_dir = tempfile::tempdir().unwrap().keep();
let ipc_path = ipc_dir.join("signet.ipc");
let ipc_endpoint = ipc_path.to_string_lossy().into_owned();

// set up Signet Node storage
let constants = cfg.constants().unwrap();
Expand Down Expand Up @@ -206,7 +209,7 @@ impl SignetTestContext {
http_cors: None,
ws: vec![],
ws_cors: None,
ipc: cfg.ipc_endpoint().map(ToOwned::to_owned),
ipc: Some(ipc_endpoint.clone()),
};

let (node, mut node_status) = SignetNodeBuilder::new(cfg.clone())
Expand Down Expand Up @@ -238,7 +241,7 @@ impl SignetTestContext {
.with_nonce_management(SimpleNonceManager::default())
.with_chain_id(constants.ru_chain_id())
.wallet(wallet)
.connect(cfg.ipc_endpoint().unwrap())
.connect(&ipc_endpoint)
.await
.unwrap();

Expand Down
Loading