Skip to content

Commit 375fd56

Browse files
prestwichclaude
andauthored
refactor(node-config): remove reth deps, add FromEnv to RPC configs (#113)
* refactor(node-config): remove reth deps, dead fields, and port/ipc config Strip SignetNodeConfig of reth-coupled fields (static_path, http_port, ws_port, ipc_endpoint) and methods (static_file_ro/rw, chain_spec, spec_id). Remove reth, reth-chainspec, trevm, and signet-evm dependencies from Cargo.toml. Simplify test_utils to match the reduced API surface. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(rpc): add ServeConfigEnv with FromEnv for env-based RPC transport config Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(rpc): add StorageRpcConfigEnv with FromEnv for env-based RPC config Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(host-rpc): add HostRpcConfig with FromEnv for env-based host config Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(node-tests): adapt to SignetNodeConfig changes, inline IPC path Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(rpc): error on invalid bind addr, fix doctests, add rustdoc examples Make ServeConfigEnv-to-ServeConfig conversion fallible (TryFrom) so that malformed SIGNET_HTTP_ADDR / SIGNET_WS_ADDR values surface an error instead of silently falling back to 0.0.0.0. Replace `ignore` doctest on HostRpcConfig with a compilable `no_run` example. Add rustdoc examples to StorageRpcConfigEnv and ServeConfigEnv. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(rpc): document env var defaults and add 0-to-disable for gas oracle prices Address review feedback: the three gas oracle price fields (default_gas_price, ignore_price, max_price) now treat 0 as a sentinel to disable the guardrail (mapping to None), rather than silently overriding None with the default. All FromEnv desc strings across StorageRpcConfigEnv, ServeConfigEnv, and HostRpcConfig now document the default value or behavior when unset. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3935687 commit 375fd56

15 files changed

Lines changed: 379 additions & 146 deletions

File tree

crates/host-rpc/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ signet-extract.workspace = true
1515
signet-types.workspace = true
1616

1717
alloy.workspace = true
18+
init4-bin-base.workspace = true
1819
futures-util.workspace = true
1920
metrics.workspace = true
2021
thiserror.workspace = true

crates/host-rpc/src/config.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use crate::{DEFAULT_BACKFILL_BATCH_SIZE, DEFAULT_BUFFER_CAPACITY, RpcHostNotifierBuilder};
2+
use alloy::providers::RootProvider;
3+
use init4_bin_base::utils::{calc::SlotCalculator, from_env::FromEnv, provider::PubSubConfig};
4+
5+
/// Environment-based configuration for the RPC host notifier.
6+
///
7+
/// # Environment Variables
8+
///
9+
/// - `SIGNET_HOST_URL` – WebSocket or IPC URL for the host EL client (required)
10+
/// - `SIGNET_HOST_BUFFER_CAPACITY` – Local chain view size (default: 64)
11+
/// - `SIGNET_HOST_BACKFILL_BATCH_SIZE` – Blocks per backfill batch (default: 32)
12+
///
13+
/// # Example
14+
///
15+
/// ```no_run
16+
/// # #[tokio::main]
17+
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
18+
/// use signet_host_rpc::HostRpcConfig;
19+
/// use init4_bin_base::utils::{calc::SlotCalculator, from_env::FromEnv};
20+
///
21+
/// let config = HostRpcConfig::from_env().unwrap();
22+
/// let slot_calculator = SlotCalculator::new(0, 1_606_824_023, 12);
23+
/// let builder = config.into_builder(slot_calculator).await?;
24+
/// # Ok(())
25+
/// # }
26+
/// ```
27+
#[derive(Debug, Clone, FromEnv)]
28+
pub struct HostRpcConfig {
29+
/// WebSocket or IPC connection to the host execution layer client.
30+
#[from_env(var = "SIGNET_HOST_URL", desc = "Host EL pubsub URL (ws:// or ipc) [required]")]
31+
provider: PubSubConfig,
32+
/// Local chain view buffer capacity.
33+
#[from_env(
34+
var = "SIGNET_HOST_BUFFER_CAPACITY",
35+
desc = "Chain view buffer capacity [default: 64]",
36+
optional
37+
)]
38+
buffer_capacity: Option<usize>,
39+
/// Blocks per backfill RPC batch.
40+
#[from_env(
41+
var = "SIGNET_HOST_BACKFILL_BATCH_SIZE",
42+
desc = "Backfill batch size [default: 32]",
43+
optional
44+
)]
45+
backfill_batch_size: Option<u64>,
46+
}
47+
48+
impl HostRpcConfig {
49+
/// Connect to the host provider and build an [`RpcHostNotifierBuilder`].
50+
///
51+
/// Uses `slot_calculator` for genesis timestamp rather than
52+
/// duplicating that setting.
53+
pub async fn into_builder(
54+
self,
55+
slot_calculator: SlotCalculator,
56+
) -> Result<RpcHostNotifierBuilder<RootProvider>, alloy::transports::TransportError> {
57+
let provider = self.provider.connect().await?;
58+
Ok(RpcHostNotifierBuilder::new(provider)
59+
.with_buffer_capacity(self.buffer_capacity.unwrap_or(DEFAULT_BUFFER_CAPACITY))
60+
.with_backfill_batch_size(
61+
self.backfill_batch_size.unwrap_or(DEFAULT_BACKFILL_BATCH_SIZE),
62+
)
63+
.with_genesis_timestamp(slot_calculator.start_timestamp()))
64+
}
65+
}

crates/host-rpc/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ pub(crate) const DEFAULT_BACKFILL_BATCH_SIZE: u64 = 32;
1919
mod builder;
2020
pub use builder::RpcHostNotifierBuilder;
2121

22+
mod config;
23+
pub use config::HostRpcConfig;
24+
2225
mod error;
2326
pub use error::RpcHostError;
2427

crates/node-config/Cargo.toml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,22 @@ repository.workspace = true
1010

1111
[dependencies]
1212
signet-blobber.workspace = true
13-
signet-evm.workspace = true
1413
signet-storage.workspace = true
1514
signet-types.workspace = true
1615

1716
init4-bin-base.workspace = true
1817

19-
reth.workspace = true
20-
reth-chainspec.workspace = true
2118
alloy.workspace = true
2219

2320
eyre.workspace = true
2421
reqwest.workspace = true
2522
serde.workspace = true
2623
tokio-util.workspace = true
2724
tracing.workspace = true
28-
trevm.workspace = true
2925
signet-genesis.workspace = true
3026

31-
tempfile = { workspace = true, optional = true }
3227

3328
[features]
34-
test_utils = ["dep:tempfile"]
29+
test_utils = []
3530
postgres = ["signet-storage/postgres"]
3631
sqlite = ["signet-storage/sqlite"]

crates/node-config/src/core.rs

Lines changed: 3 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,11 @@
11
use crate::StorageConfig;
22
use alloy::genesis::Genesis;
33
use init4_bin_base::utils::{calc::SlotCalculator, from_env::FromEnv};
4-
use reth::primitives::NodePrimitives;
5-
use reth::providers::providers::StaticFileProvider;
6-
use reth_chainspec::ChainSpec;
74
use signet_blobber::BlobFetcherConfig;
85
use signet_genesis::GenesisSpec;
96
use signet_types::constants::{ConfigError, SignetSystemConstants};
10-
use std::{
11-
borrow::Cow,
12-
fmt::Display,
13-
path::PathBuf,
14-
sync::{Arc, OnceLock},
15-
};
7+
use std::{borrow::Cow, fmt::Display, sync::OnceLock};
168
use tracing::warn;
17-
use trevm::revm::primitives::hardfork::SpecId;
18-
19-
/// Defines the default port for serving Signet Node JSON RPC requests over http.
20-
pub const SIGNET_NODE_DEFAULT_HTTP_PORT: u16 = 5959u16;
219

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

31-
/// Path to the static files for reth StaticFileProviders.
32-
#[from_env(var = "SIGNET_STATIC_PATH", desc = "Path to the static files", infallible)]
33-
static_path: Cow<'static, str>,
3419
/// Unified storage configuration (hot + cold MDBX paths).
3520
#[from_env(infallible)]
3621
storage: StorageConfig,
@@ -42,20 +27,6 @@ pub struct SignetNodeConfig {
4227
optional
4328
)]
4429
forward_url: Option<Cow<'static, str>>,
45-
/// RPC port to serve JSON-RPC requests
46-
#[from_env(var = "RPC_PORT", desc = "RPC port to serve JSON-RPC requests", optional)]
47-
http_port: Option<u16>,
48-
/// Websocket port to serve JSON-RPC requests
49-
#[from_env(var = "WS_RPC_PORT", desc = "Websocket port to serve JSON-RPC requests", optional)]
50-
ws_port: Option<u16>,
51-
/// IPC endpoint to serve JSON-RPC requests
52-
#[from_env(
53-
var = "IPC_ENDPOINT",
54-
desc = "IPC endpoint to serve JSON-RPC requests",
55-
infallible,
56-
optional
57-
)]
58-
ipc_endpoint: Option<Cow<'static, str>>,
5930

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

8354
impl SignetNodeConfig {
8455
/// Create a new Signet Node configuration.
85-
#[allow(clippy::too_many_arguments)]
8656
pub const fn new(
8757
block_extractor: BlobFetcherConfig,
88-
static_path: Cow<'static, str>,
8958
storage: StorageConfig,
9059
forward_url: Option<Cow<'static, str>>,
91-
rpc_port: u16,
92-
ws_port: u16,
93-
ipc_endpoint: Option<Cow<'static, str>>,
9460
genesis: GenesisSpec,
9561
slot_calculator: SlotCalculator,
9662
) -> Self {
9763
Self {
9864
block_extractor,
99-
static_path,
10065
storage,
10166
forward_url,
102-
http_port: Some(rpc_port),
103-
ws_port: Some(ws_port),
104-
ipc_endpoint,
10567
genesis,
10668
slot_calculator,
107-
backfill_max_blocks: None, // Uses default of 10,000 via accessor
69+
backfill_max_blocks: None,
10870
}
10971
}
11072

@@ -133,26 +95,6 @@ impl SignetNodeConfig {
13395
self.slot_calculator
13496
}
13597

136-
/// Get the static path as a str.
137-
pub fn static_path_str(&self) -> &str {
138-
&self.static_path
139-
}
140-
141-
/// Get the static path.
142-
pub fn static_path(&self) -> PathBuf {
143-
self.static_path.as_ref().to_owned().into()
144-
}
145-
146-
/// Get the static file provider for read-only access.
147-
pub fn static_file_ro<N: NodePrimitives>(&self) -> eyre::Result<StaticFileProvider<N>> {
148-
StaticFileProvider::read_only(self.static_path(), true).map_err(Into::into)
149-
}
150-
151-
/// Get the static file provider for read-write access.
152-
pub fn static_file_rw<N: NodePrimitives>(&self) -> eyre::Result<StaticFileProvider<N>> {
153-
StaticFileProvider::read_write(self.static_path()).map_err(Into::into)
154-
}
155-
15698
/// Get the storage configuration.
15799
pub const fn storage(&self) -> &StorageConfig {
158100
&self.storage
@@ -167,65 +109,17 @@ impl SignetNodeConfig {
167109
.ok()
168110
}
169111

170-
/// Returns the port for serving JSON RPC requests for Signet Node.
171-
pub const fn http_port(&self) -> u16 {
172-
if let Some(port) = self.http_port {
173-
return port;
174-
}
175-
SIGNET_NODE_DEFAULT_HTTP_PORT
176-
}
177-
178-
/// Set the HTTP port for serving JSON RPC requests for Signet Node.
179-
pub const fn set_http_port(&mut self, port: u16) {
180-
self.http_port = Some(port);
181-
}
182-
183-
/// Returns the port for serving Websocket RPC requests for Signet Node.
184-
pub const fn ws_port(&self) -> u16 {
185-
if let Some(port) = self.ws_port {
186-
return port;
187-
}
188-
SIGNET_NODE_DEFAULT_HTTP_PORT + 1
189-
}
190-
191-
/// Set the websocket port for serving JSON RPC requests for Signet.
192-
pub const fn set_ws_port(&mut self, port: u16) {
193-
self.ws_port = Some(port);
194-
}
195-
196-
/// Returns the IPC endpoint for serving JSON RPC requests for Signet, if any.
197-
pub fn ipc_endpoint(&self) -> Option<&str> {
198-
self.ipc_endpoint.as_deref()
199-
}
200-
201-
/// Set the IPC endpoint for serving JSON RPC requests for Signet Node.
202-
pub fn set_ipc_endpoint(&mut self, endpoint: Cow<'static, str>) {
203-
self.ipc_endpoint = Some(endpoint);
204-
}
205-
206112
/// Returns the rollup genesis configuration if any has been loaded.
207113
pub fn genesis(&self) -> &'static Genesis {
208114
static ONCE: OnceLock<Cow<'static, Genesis>> = OnceLock::new();
209115
ONCE.get_or_init(|| self.genesis.load_genesis().expect("Failed to load genesis").rollup)
210116
}
211117

212-
/// Create a new chain spec for the Signet Node chain.
213-
pub fn chain_spec(&self) -> &Arc<ChainSpec> {
214-
static SPEC: OnceLock<Arc<ChainSpec>> = OnceLock::new();
215-
SPEC.get_or_init(|| Arc::new(self.genesis().clone().into()))
216-
}
217-
218118
/// Get the system constants for the Signet Node chain.
219119
pub fn constants(&self) -> Result<SignetSystemConstants, ConfigError> {
220120
SignetSystemConstants::try_from_genesis(self.genesis())
221121
}
222122

223-
/// Get the current spec id for the Signet Node chain.
224-
pub fn spec_id(&self, block: u64, timestamp: u64) -> SpecId {
225-
signet_evm::EthereumHardfork::active_hardforks(&self.genesis().config, block, timestamp)
226-
.spec_id()
227-
}
228-
229123
/// Get the maximum number of blocks to process per backfill batch.
230124
/// Returns `Some(10_000)` by default if not configured, to avoid OOM
231125
/// during mainnet sync (reth's default of 500K is too aggressive).
@@ -244,15 +138,11 @@ mod defaults {
244138
fn default() -> Self {
245139
Self {
246140
block_extractor: BlobFetcherConfig::new(Cow::Borrowed("")),
247-
static_path: Cow::Borrowed(""),
248141
storage: StorageConfig::new(Cow::Borrowed(""), Cow::Borrowed("")),
249142
forward_url: None,
250-
http_port: Some(SIGNET_NODE_DEFAULT_HTTP_PORT),
251-
ws_port: Some(SIGNET_NODE_DEFAULT_HTTP_PORT + 1),
252-
ipc_endpoint: None,
253143
genesis: GenesisSpec::Known(KnownChains::Test),
254144
slot_calculator: SlotCalculator::new(0, 0, 12),
255-
backfill_max_blocks: None, // Uses default of 10,000 via accessor
145+
backfill_max_blocks: None,
256146
}
257147
}
258148
}

crates/node-config/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
1313

1414
mod core;
15-
pub use core::{SIGNET_NODE_DEFAULT_HTTP_PORT, SignetNodeConfig};
15+
pub use core::SignetNodeConfig;
1616

1717
// NB: RPC config merging (previously `merge_rpc_configs`) is now the
1818
// responsibility of the host adapter crate (e.g. `signet-host-reth`).

crates/node-config/src/test_utils.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,17 @@ use signet_blobber::BlobFetcherConfig;
44
use signet_genesis::GenesisSpec;
55
use signet_types::constants::KnownChains;
66
use std::borrow::Cow;
7-
use tempfile::tempdir;
87

98
/// Make a test config
10-
pub fn test_config() -> SignetNodeConfig {
11-
let mut tempdir = tempdir().unwrap().keep();
12-
tempdir.push("signet.ipc");
13-
14-
// Make a new test config with the IPC endpoint set to the tempdir.
15-
let mut cfg = TEST_CONFIG;
16-
cfg.set_ipc_endpoint(Cow::Owned(format!("{}", tempdir.to_string_lossy())));
17-
cfg
9+
pub const fn test_config() -> SignetNodeConfig {
10+
TEST_CONFIG
1811
}
1912

2013
/// Test SignetNodeConfig
2114
const TEST_CONFIG: SignetNodeConfig = SignetNodeConfig::new(
2215
BlobFetcherConfig::new(Cow::Borrowed("")),
23-
Cow::Borrowed("NOP"),
2416
StorageConfig::new(Cow::Borrowed("NOP"), Cow::Borrowed("NOP")),
2517
None,
26-
31391, // NOP
27-
31392, // NOP
28-
Some(Cow::Borrowed("/trethNOP")),
2918
GenesisSpec::Known(KnownChains::Test),
3019
SlotCalculator::new(0, 0, 12),
3120
);

crates/node-tests/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ signet-zenith.workspace = true
2929
alloy.workspace = true
3030
eyre.workspace = true
3131
reqwest.workspace = true
32+
tempfile.workspace = true
3233
tokio.workspace = true
3334
tracing.workspace = true
3435
tracing-subscriber.workspace = true

crates/node-tests/src/context.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ impl SignetTestContext {
143143
pub async fn new() -> (Self, JoinHandle<eyre::Result<()>>) {
144144
let cfg = test_config();
145145
let blob_source = MemoryBlobSource::new();
146+
let ipc_dir = tempfile::tempdir().unwrap().keep();
147+
let ipc_path = ipc_dir.join("signet.ipc");
148+
let ipc_endpoint = ipc_path.to_string_lossy().into_owned();
146149

147150
// set up Signet Node storage
148151
let constants = cfg.constants().unwrap();
@@ -206,7 +209,7 @@ impl SignetTestContext {
206209
http_cors: None,
207210
ws: vec![],
208211
ws_cors: None,
209-
ipc: cfg.ipc_endpoint().map(ToOwned::to_owned),
212+
ipc: Some(ipc_endpoint.clone()),
210213
};
211214

212215
let (node, mut node_status) = SignetNodeBuilder::new(cfg.clone())
@@ -238,7 +241,7 @@ impl SignetTestContext {
238241
.with_nonce_management(SimpleNonceManager::default())
239242
.with_chain_id(constants.ru_chain_id())
240243
.wallet(wallet)
241-
.connect(cfg.ipc_endpoint().unwrap())
244+
.connect(&ipc_endpoint)
242245
.await
243246
.unwrap();
244247

0 commit comments

Comments
 (0)