Skip to content

Commit efdf58f

Browse files
prestwichclaude
andcommitted
feat: add HostNotifier trait and decouple node from ExExContext
Introduces the HostNotifier trait to abstract chain notification sources, enabling the node to work with different host environments beyond ExEx. Adds signet-node-types crate with notification types and trait definition. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f71bbdd commit efdf58f

17 files changed

Lines changed: 468 additions & 480 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ signet-genesis = { version = "0.16.0-rc.7", path = "crates/genesis" }
4040
signet-node = { version = "0.16.0-rc.7", path = "crates/node" }
4141
signet-node-config = { version = "0.16.0-rc.7", path = "crates/node-config" }
4242
signet-node-tests = { version = "0.16.0-rc.7", path = "crates/node-tests" }
43+
signet-node-types = { version = "0.16.0-rc.7", path = "crates/node-types" }
4344
signet-rpc = { version = "0.16.0-rc.7", path = "crates/rpc" }
4445

4546
init4-bin-base = { version = "0.18.0-rc.8", features = ["alloy"] }

crates/node-config/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ init4-bin-base.workspace = true
1818

1919
reth.workspace = true
2020
reth-chainspec.workspace = true
21-
reth-exex.workspace = true
22-
reth-node-api.workspace = true
2321
alloy.workspace = true
2422

2523
eyre.workspace = true

crates/node-config/src/core.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
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;
45
use reth::providers::providers::StaticFileProvider;
56
use reth_chainspec::ChainSpec;
6-
use reth_node_api::NodePrimitives;
77
use signet_blobber::BlobFetcherConfig;
88
use signet_genesis::GenesisSpec;
99
use signet_types::constants::{ConfigError, SignetSystemConstants};

crates/node-config/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
mod core;
1515
pub use core::{SIGNET_NODE_DEFAULT_HTTP_PORT, SignetNodeConfig};
1616

17-
mod rpc;
17+
// NB: RPC config merging (previously `merge_rpc_configs`) is now the
18+
// responsibility of the host adapter crate (e.g. `signet-host-reth`).
1819

1920
mod storage;
2021
pub use storage::StorageConfig;

crates/node-config/src/rpc.rs

Lines changed: 0 additions & 29 deletions
This file was deleted.

crates/node-types/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "signet-node-types"
3+
description = "Trait abstractions for the signet node's host chain interface."
4+
version.workspace = true
5+
edition.workspace = true
6+
rust-version.workspace = true
7+
authors.workspace = true
8+
license.workspace = true
9+
homepage.workspace = true
10+
repository.workspace = true
11+
12+
[dependencies]
13+
signet-extract.workspace = true

crates/node-types/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# signet-node-types
2+
3+
Trait abstractions for the signet node's host chain interface.

crates/node-types/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![doc = include_str!("../README.md")]
2+
#![warn(
3+
missing_copy_implementations,
4+
missing_debug_implementations,
5+
missing_docs,
6+
unreachable_pub,
7+
clippy::missing_const_for_fn,
8+
rustdoc::all
9+
)]
10+
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
11+
#![deny(unused_must_use, rust_2018_idioms)]
12+
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
13+
14+
mod notification;
15+
pub use notification::{HostNotification, HostNotificationKind};
16+
17+
mod notifier;
18+
pub use notifier::HostNotifier;
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
use std::sync::Arc;
2+
3+
/// A notification from the host chain, bundling a chain event with
4+
/// point-in-time block tag data. The safe/finalized block numbers are
5+
/// intentionally snapshotted at notification creation time rather than
6+
/// fetched live, because rollup safe/finalized tags are only updated
7+
/// after block processing completes.
8+
///
9+
/// # Examples
10+
///
11+
/// ```
12+
/// # use std::sync::Arc;
13+
/// # use signet_node_types::{HostNotification, HostNotificationKind};
14+
/// # fn example<C: core::fmt::Debug>(chain: Arc<C>) {
15+
/// let notification = HostNotification {
16+
/// kind: HostNotificationKind::ChainCommitted { new: chain },
17+
/// safe_block_number: Some(100),
18+
/// finalized_block_number: Some(90),
19+
/// };
20+
///
21+
/// // Access the committed chain via the shortcut method.
22+
/// assert!(notification.committed_chain().is_some());
23+
/// assert!(notification.reverted_chain().is_none());
24+
/// # }
25+
/// ```
26+
#[derive(Debug, Clone)]
27+
pub struct HostNotification<C> {
28+
/// The chain event (commit, revert, or reorg).
29+
pub kind: HostNotificationKind<C>,
30+
/// The host chain "safe" block number at the time of this notification.
31+
pub safe_block_number: Option<u64>,
32+
/// The host chain "finalized" block number at the time of this
33+
/// notification.
34+
pub finalized_block_number: Option<u64>,
35+
}
36+
37+
impl<C> HostNotification<C> {
38+
/// Returns the committed chain, if any. Shortcut for
39+
/// `self.kind.committed_chain()`.
40+
pub const fn committed_chain(&self) -> Option<&Arc<C>> {
41+
self.kind.committed_chain()
42+
}
43+
44+
/// Returns the reverted chain, if any. Shortcut for
45+
/// `self.kind.reverted_chain()`.
46+
pub const fn reverted_chain(&self) -> Option<&Arc<C>> {
47+
self.kind.reverted_chain()
48+
}
49+
}
50+
51+
/// The kind of chain event in a [`HostNotification`].
52+
///
53+
/// # Examples
54+
///
55+
/// ```
56+
/// # use std::sync::Arc;
57+
/// # use signet_node_types::HostNotificationKind;
58+
/// # fn example<C: core::fmt::Debug>(old: Arc<C>, new: Arc<C>) {
59+
/// let kind = HostNotificationKind::ChainReorged {
60+
/// old: old.clone(),
61+
/// new: new.clone(),
62+
/// };
63+
/// # }
64+
/// ```
65+
#[derive(Debug, Clone)]
66+
pub enum HostNotificationKind<C> {
67+
/// A new chain segment was committed.
68+
ChainCommitted {
69+
/// The newly committed chain segment.
70+
new: Arc<C>,
71+
},
72+
/// A chain segment was reverted.
73+
ChainReverted {
74+
/// The reverted chain segment.
75+
old: Arc<C>,
76+
},
77+
/// A chain reorg occurred: one segment was reverted and replaced by
78+
/// another.
79+
ChainReorged {
80+
/// The reverted chain segment.
81+
old: Arc<C>,
82+
/// The newly committed chain segment.
83+
new: Arc<C>,
84+
},
85+
}
86+
87+
impl<C> HostNotificationKind<C> {
88+
/// Returns the committed chain, if any.
89+
///
90+
/// Returns `Some` for [`ChainCommitted`] and [`ChainReorged`], `None`
91+
/// for [`ChainReverted`].
92+
///
93+
/// [`ChainCommitted`]: HostNotificationKind::ChainCommitted
94+
/// [`ChainReorged`]: HostNotificationKind::ChainReorged
95+
/// [`ChainReverted`]: HostNotificationKind::ChainReverted
96+
pub const fn committed_chain(&self) -> Option<&Arc<C>> {
97+
match self {
98+
Self::ChainCommitted { new } | Self::ChainReorged { new, .. } => Some(new),
99+
Self::ChainReverted { .. } => None,
100+
}
101+
}
102+
103+
/// Returns the reverted chain, if any.
104+
///
105+
/// Returns `Some` for [`ChainReverted`] and [`ChainReorged`], `None`
106+
/// for [`ChainCommitted`].
107+
///
108+
/// [`ChainReverted`]: HostNotificationKind::ChainReverted
109+
/// [`ChainReorged`]: HostNotificationKind::ChainReorged
110+
/// [`ChainCommitted`]: HostNotificationKind::ChainCommitted
111+
pub const fn reverted_chain(&self) -> Option<&Arc<C>> {
112+
match self {
113+
Self::ChainReverted { old } | Self::ChainReorged { old, .. } => Some(old),
114+
Self::ChainCommitted { .. } => None,
115+
}
116+
}
117+
}

crates/node-types/src/notifier.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use crate::HostNotification;
2+
use core::future::Future;
3+
use signet_extract::Extractable;
4+
5+
/// Abstraction over a host chain notification source.
6+
///
7+
/// Drives the signet node's main loop: yielding chain events, controlling
8+
/// backfill, and sending feedback. All block data comes from notifications;
9+
/// the backend handles hash resolution internally.
10+
///
11+
/// # Implementors
12+
///
13+
/// - `signet-host-reth`: wraps reth's `ExExContext`
14+
///
15+
/// # Implementing
16+
///
17+
/// Implementations must uphold the following contract:
18+
///
19+
/// 1. **`set_head`** — called once at startup before the first
20+
/// [`next_notification`]. The backend must resolve the block number to a
21+
/// hash (falling back to genesis if the number is not yet available) and
22+
/// begin delivering notifications from that point.
23+
/// 2. **`next_notification`** — must yield notifications in host-chain order.
24+
/// Returning `None` signals a clean shutdown.
25+
/// 3. **`set_backfill_thresholds`** — may be called at any time. Passing
26+
/// `None` should restore the backend's default batch size.
27+
/// 4. **`send_finished_height`** — may be called after processing each
28+
/// notification batch. The backend resolves the block number to a hash
29+
/// internally. Sending a height that has already been acknowledged is a
30+
/// no-op.
31+
///
32+
/// [`next_notification`]: HostNotifier::next_notification
33+
pub trait HostNotifier: Send + Sync {
34+
/// A chain segment — contiguous blocks with receipts.
35+
type Chain: Extractable;
36+
37+
/// The error type for fallible operations.
38+
type Error: core::error::Error + Send + Sync + 'static;
39+
40+
/// Yield the next notification. `None` signals host shutdown.
41+
fn next_notification(
42+
&mut self,
43+
) -> impl Future<Output = Option<Result<HostNotification<Self::Chain>, Self::Error>>> + Send;
44+
45+
/// Set the head position, requesting backfill from this block number.
46+
/// The backend resolves the block number to a block hash internally.
47+
fn set_head(&mut self, block_number: u64);
48+
49+
/// Configure backfill batch size limits. `None` means use the backend's
50+
/// default.
51+
fn set_backfill_thresholds(&mut self, max_blocks: Option<u64>);
52+
53+
/// Signal that processing is complete up to this host block number.
54+
/// The backend resolves the block number to a block hash internally.
55+
fn send_finished_height(&self, block_number: u64) -> Result<(), Self::Error>;
56+
}

0 commit comments

Comments
 (0)