Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
81eef23
docs: add host context adapter design spec
prestwich Mar 13, 2026
06c5e92
docs: add host context adapter implementation plan
prestwich Mar 13, 2026
da68def
docs: change Extractable metadata methods to return Option
prestwich Mar 13, 2026
db92e38
chore: enable local SDK path overrides for Extractable changes
prestwich Mar 13, 2026
9d8248a
feat: add signet-node-types crate with HostNotifier trait
prestwich Mar 13, 2026
993b671
feat: add signet-host-reth crate with RethHostNotifier
prestwich Mar 13, 2026
4f81b4f
refactor(node-config): remove ExEx dependency
prestwich Mar 13, 2026
ae36a74
refactor(node): replace ExExContext with HostNotifier trait
prestwich Mar 13, 2026
9a67a64
refactor: make block processor generic over Extractable
prestwich Mar 13, 2026
ce6fa50
test: update signet-node-tests for HostNotifier API
prestwich Mar 13, 2026
e43bee3
fix(node-tests): build ServeConfig from Signet test config
prestwich Mar 13, 2026
875672a
refactor(host-reth): use sealed_header instead of num_hash_slow in se…
prestwich Mar 13, 2026
a013d39
chore: point SDK patch overrides to PR branch on GitHub
prestwich Mar 13, 2026
9e318dd
fix: adapt to BlockAndReceipts struct from signet-sdk update
prestwich Mar 13, 2026
ea63ade
feat: add signet-host-rpc crate with RpcHostNotifier
prestwich Mar 13, 2026
a526497
chore: bump SDK deps to 0.16.0-rc.14
prestwich Mar 13, 2026
4c84379
docs: add async AliasOracle implementation plan
prestwich Mar 13, 2026
684a759
refactor: make AliasOracle::should_alias async via RPITIT
prestwich Mar 13, 2026
4750976
fix: address PR #100 review feedback
prestwich Mar 13, 2026
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
39 changes: 21 additions & 18 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,25 @@ incremental = false
signet-blobber = { version = "0.16.0-rc.7", path = "crates/blobber" }
signet-block-processor = { version = "0.16.0-rc.7", path = "crates/block-processor" }
signet-genesis = { version = "0.16.0-rc.7", path = "crates/genesis" }
signet-host-reth = { version = "0.16.0-rc.7", path = "crates/host-reth" }
signet-host-rpc = { version = "0.16.0-rc.7", path = "crates/host-rpc" }
signet-node = { version = "0.16.0-rc.7", path = "crates/node" }
signet-node-config = { version = "0.16.0-rc.7", path = "crates/node-config" }
signet-node-tests = { version = "0.16.0-rc.7", path = "crates/node-tests" }
signet-node-types = { version = "0.16.0-rc.7", path = "crates/node-types" }
signet-rpc = { version = "0.16.0-rc.7", path = "crates/rpc" }

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

signet-bundle = "0.16.0-rc.11"
signet-constants = "0.16.0-rc.11"
signet-evm = "0.16.0-rc.11"
signet-extract = "0.16.0-rc.11"
signet-test-utils = "0.16.0-rc.11"
signet-tx-cache = "0.16.0-rc.11"
signet-types = "0.16.0-rc.11"
signet-zenith = "0.16.0-rc.11"
signet-journal = "0.16.0-rc.11"
signet-bundle = "0.16.0-rc.14"
signet-constants = "0.16.0-rc.14"
signet-evm = "0.16.0-rc.14"
signet-extract = "0.16.0-rc.14"
signet-test-utils = "0.16.0-rc.14"
signet-tx-cache = "0.16.0-rc.14"
signet-types = "0.16.0-rc.14"
signet-zenith = "0.16.0-rc.14"
signet-journal = "0.16.0-rc.14"
signet-storage = "0.6.4"
signet-cold = "0.6.4"
signet-hot = "0.6.4"
Expand Down Expand Up @@ -119,14 +122,14 @@ signet-hot = { git = "https://github.com/init4tech/storage.git", branch = "james
signet-storage = { git = "https://github.com/init4tech/storage.git", branch = "james/eng-1978" }
signet-storage-types = { git = "https://github.com/init4tech/storage.git", branch = "james/eng-1978" }

# signet-bundle = { path = "../sdk/crates/bundle"}
# signet-constants = { path = "../sdk/crates/constants"}
# signet-evm = { path = "../sdk/crates/evm"}
# signet-extract = { path = "../sdk/crates/extract"}
# signet-journal = { path = "../sdk/crates/journal"}
# signet-test-utils = { path = "../sdk/crates/test-utils"}
# signet-tx-cache = { path = "../sdk/crates/tx-cache"}
# signet-types = { path = "../sdk/crates/types"}
# signet-zenith = { path = "../sdk/crates/zenith"}
signet-bundle = { git = "https://github.com/init4tech/signet-sdk.git", branch = "feat/extractable-metadata"}
signet-constants = { git = "https://github.com/init4tech/signet-sdk.git", branch = "feat/extractable-metadata"}
signet-evm = { git = "https://github.com/init4tech/signet-sdk.git", branch = "feat/extractable-metadata"}
signet-extract = { git = "https://github.com/init4tech/signet-sdk.git", branch = "feat/extractable-metadata"}
signet-journal = { git = "https://github.com/init4tech/signet-sdk.git", branch = "feat/extractable-metadata"}
signet-test-utils = { git = "https://github.com/init4tech/signet-sdk.git", branch = "feat/extractable-metadata"}
signet-tx-cache = { git = "https://github.com/init4tech/signet-sdk.git", branch = "feat/extractable-metadata"}
signet-types = { git = "https://github.com/init4tech/signet-sdk.git", branch = "feat/extractable-metadata"}
signet-zenith = { git = "https://github.com/init4tech/signet-sdk.git", branch = "feat/extractable-metadata"}

# init4-bin-base = { path = "../shared" }
11 changes: 5 additions & 6 deletions crates/blobber/src/blobs/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use alloy::eips::eip7691::MAX_BLOBS_PER_BLOCK_ELECTRA;
use alloy::eips::merge::EPOCH_SLOTS;
use alloy::primitives::{B256, Bytes, keccak256};
use core::fmt;
use reth::transaction_pool::TransactionPool;
use reth::{network::cache::LruMap, primitives::Receipt};
use reth::{network::cache::LruMap, transaction_pool::TransactionPool};
use signet_extract::ExtractedEvent;
use signet_zenith::Zenith::BlockSubmitted;
use signet_zenith::ZenithBlock;
Expand Down Expand Up @@ -75,10 +74,10 @@ impl<Coder> CacheHandle<Coder> {

/// Fetch the blobs using [`Self::fetch_blobs`] and decode them to get the
/// Zenith block data using the provided coder.
pub async fn fetch_and_decode(
pub async fn fetch_and_decode<R>(
&self,
slot: usize,
extract: &ExtractedEvent<'_, Receipt, BlockSubmitted>,
extract: &ExtractedEvent<'_, R, BlockSubmitted>,
) -> BlobberResult<Bytes>
where
Coder: SidecarCoder + Default,
Expand Down Expand Up @@ -116,11 +115,11 @@ impl<Coder> CacheHandle<Coder> {
/// decoded (e.g., due to a malformatted blob).
/// - `Err(FetchError)` if there was an unrecoverable error fetching the
/// blobs.
pub async fn signet_block(
pub async fn signet_block<R>(
&self,
host_block_number: u64,
slot: usize,
extract: &ExtractedEvent<'_, Receipt, BlockSubmitted>,
extract: &ExtractedEvent<'_, R, BlockSubmitted>,
) -> FetchResult<ZenithBlock>
where
Coder: SidecarCoder + Default,
Expand Down
2 changes: 1 addition & 1 deletion crates/blobber/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ mod error;
pub use error::{BlobberError, BlobberResult};

mod shim;
pub use shim::ExtractableChainShim;
pub use shim::{ExtractableChainShim, RecoveredBlockShim};

#[cfg(test)]
mod test {
Expand Down
8 changes: 5 additions & 3 deletions crates/blobber/src/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use alloy::consensus::Block;
use reth::providers::Chain;
use signet_extract::{Extractable, HasTxns};
use signet_extract::{BlockAndReceipts, Extractable, HasTxns};
use signet_types::primitives::TransactionSigned;

/// A type alias for Reth's recovered block with a signed transaction.
Expand Down Expand Up @@ -32,14 +32,16 @@ impl<'a> Extractable for ExtractableChainShim<'a> {
type Block = RecoveredBlockShim;
type Receipt = reth::primitives::Receipt;

fn blocks_and_receipts(&self) -> impl Iterator<Item = (&Self::Block, &Vec<Self::Receipt>)> {
fn blocks_and_receipts(
&self,
) -> impl Iterator<Item = BlockAndReceipts<'_, Self::Block, Self::Receipt>> {
self.chain.blocks_and_receipts().map(|(block, receipts)| {
// SAFETY: because the shim is repr(transparent), the memory layout
// of `RecoveredBlockShim` is the same as `RethRecovered`, so we
// can safely transmute the reference.
let block =
unsafe { std::mem::transmute::<&'a RethRecovered, &RecoveredBlockShim>(block) };
(block, receipts)
BlockAndReceipts { block, receipts }
})
}
}
Expand Down
8 changes: 5 additions & 3 deletions crates/block-processor/src/alias.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use alloy::primitives::{Address, map::HashSet};
use core::future::Future;
use std::sync::{Arc, Mutex};

/// Simple trait to allow checking if an address should be aliased.
pub trait AliasOracle {
/// Returns true if the given address is an alias.
fn should_alias(&self, address: Address) -> eyre::Result<bool>;
fn should_alias(&self, address: Address) -> impl Future<Output = eyre::Result<bool>> + Send;
}

impl AliasOracle for HashSet<Address> {
fn should_alias(&self, address: Address) -> eyre::Result<bool> {
Ok(self.contains(&address))
fn should_alias(&self, address: Address) -> impl Future<Output = eyre::Result<bool>> + Send {
let result = Ok(self.contains(&address));
async move { result }
}
}

Expand Down
18 changes: 9 additions & 9 deletions crates/block-processor/src/v1/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use alloy::{
use core::fmt;
use eyre::{ContextCompat, WrapErr};
use init4_bin_base::utils::calc::SlotCalculator;
use signet_blobber::{CacheHandle, ExtractableChainShim};
use signet_blobber::CacheHandle;
use signet_constants::SignetSystemConstants;
use signet_evm::{BlockResult, EthereumHardfork, EvmNeedsCfg, SignetDriver};
use signet_extract::Extracts;
use signet_extract::{Extractable, Extracts};
use signet_hot::{
db::HotDbRead,
model::{HotKv, HotKvRead, RevmRead},
Expand Down Expand Up @@ -104,8 +104,8 @@ where
}

/// Check if the given address should be aliased.
fn should_alias(&self, address: Address) -> eyre::Result<bool> {
self.alias_oracle.create()?.should_alias(address)
async fn should_alias(&self, address: Address) -> eyre::Result<bool> {
self.alias_oracle.create()?.should_alias(address).await
}

/// Process a single extracted block, returning an [`ExecutedBlock`].
Expand All @@ -119,9 +119,9 @@ where
host_height = block_extracts.host_block.number(),
has_ru_block = block_extracts.submitted.is_some(),
))]
pub async fn process_block(
pub async fn process_block<C: Extractable>(
&self,
block_extracts: &Extracts<'_, ExtractableChainShim<'_>>,
block_extracts: &Extracts<'_, C>,
) -> eyre::Result<ExecutedBlock> {
metrics::record_extracts(block_extracts);
self.run_evm(block_extracts).await
Expand All @@ -147,9 +147,9 @@ where
/// Run the EVM for a single block extraction, returning the fully
/// assembled [`ExecutedBlock`].
#[instrument(skip_all)]
async fn run_evm(
async fn run_evm<C: Extractable>(
&self,
block_extracts: &Extracts<'_, ExtractableChainShim<'_>>,
block_extracts: &Extracts<'_, C>,
) -> eyre::Result<ExecutedBlock> {
let start_time = std::time::Instant::now();
let spec_id = self.hardforks.spec_id();
Expand Down Expand Up @@ -189,7 +189,7 @@ where
let mut to_alias: HashSet<Address> = Default::default();
for transact in block_extracts.transacts() {
let addr = transact.host_sender();
if !to_alias.contains(&addr) && self.should_alias(addr)? {
if !to_alias.contains(&addr) && self.should_alias(addr).await? {
to_alias.insert(addr);
}
}
Expand Down
30 changes: 30 additions & 0 deletions crates/host-reth/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "signet-host-reth"
description = "Reth ExEx implementation of the `HostNotifier` trait for signet-node."
version.workspace = true
edition.workspace = true
rust-version.workspace = true
authors.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true

[dependencies]
signet-node-types.workspace = true
signet-blobber.workspace = true
signet-extract.workspace = true
signet-rpc.workspace = true
signet-block-processor.workspace = true
signet-types.workspace = true

alloy.workspace = true
reth.workspace = true
reth-exex.workspace = true
reth-node-api.workspace = true
reth-stages-types.workspace = true

eyre.workspace = true
futures-util.workspace = true
thiserror.workspace = true
tokio.workspace = true
tracing.workspace = true
3 changes: 3 additions & 0 deletions crates/host-reth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# signet-host-reth

Reth ExEx implementation of the `HostNotifier` trait for signet-node.
47 changes: 26 additions & 21 deletions crates/node/src/alias.rs → crates/host-reth/src/alias.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloy::{consensus::constants::KECCAK_EMPTY, primitives::Address};
use core::fmt;
use core::{fmt, future::Future};
use eyre::OptionExt;
use reth::providers::{StateProviderBox, StateProviderFactory};
use signet_block_processor::{AliasOracle, AliasOracleFactory};
Expand All @@ -17,27 +17,32 @@ impl fmt::Debug for RethAliasOracle {
}

impl AliasOracle for RethAliasOracle {
fn should_alias(&self, address: Address) -> eyre::Result<bool> {
// No account at this address.
let Some(acct) = self.0.basic_account(&address)? else { return Ok(false) };
// Get the bytecode hash for this account.
let bch = match acct.bytecode_hash {
Some(hash) => hash,
// No bytecode hash; not a contract.
None => return Ok(false),
};
// No code at this address.
if bch == KECCAK_EMPTY {
return Ok(false);
}
// Fetch the code associated with this bytecode hash.
let code = self
.0
.bytecode_by_hash(&bch)?
.ok_or_eyre("code not found. This indicates a corrupted database")?;
fn should_alias(&self, address: Address) -> impl Future<Output = eyre::Result<bool>> + Send {
// Sync DB calls run inline on the async runtime. This matches reth's
// existing pattern of synchronous database access on async tasks.
let result = (|| {
// No account at this address.
let Some(acct) = self.0.basic_account(&address)? else { return Ok(false) };
// Get the bytecode hash for this account.
let bch = match acct.bytecode_hash {
Some(hash) => hash,
// No bytecode hash; not a contract.
None => return Ok(false),
};
// No code at this address.
if bch == KECCAK_EMPTY {
return Ok(false);
}
// Fetch the code associated with this bytecode hash.
let code = self
.0
.bytecode_by_hash(&bch)?
.ok_or_eyre("code not found. This indicates a corrupted database")?;

// If not a 7702 delegation contract, alias it.
Ok(!code.is_eip7702())
// If not a 7702 delegation contract, alias it.
Ok(!code.is_eip7702())
})();
async move { result }
}
}

Expand Down
59 changes: 59 additions & 0 deletions crates/host-reth/src/chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use alloy::{consensus::Block, consensus::BlockHeader};
use reth::primitives::{EthPrimitives, RecoveredBlock};
use reth::providers::Chain;
use signet_blobber::RecoveredBlockShim;
use signet_extract::{BlockAndReceipts, Extractable};
use signet_types::primitives::TransactionSigned;
use std::sync::Arc;

/// Reth's recovered block type, aliased for readability.
type RethRecovered = RecoveredBlock<Block<TransactionSigned>>;

/// An owning wrapper around reth's [`Chain`] that implements [`Extractable`]
/// with O(1) metadata accessors.
#[derive(Debug)]
pub struct RethChain {
inner: Arc<Chain<EthPrimitives>>,
}

impl RethChain {
/// Wrap a reth chain.
pub const fn new(chain: Arc<Chain<EthPrimitives>>) -> Self {
Self { inner: chain }
}
}

impl Extractable for RethChain {
type Block = RecoveredBlockShim;
type Receipt = reth::primitives::Receipt;

fn blocks_and_receipts(
&self,
) -> impl Iterator<Item = BlockAndReceipts<'_, Self::Block, Self::Receipt>> {
self.inner.blocks_and_receipts().map(|(block, receipts)| {
// SAFETY: `RecoveredBlockShim` is `#[repr(transparent)]` over
// `RethRecovered`, so these types have identical memory layouts.
// The lifetime of the reference is tied to `self.inner` (the
// `Arc<Chain>`), which outlives the returned iterator.
let block =
unsafe { std::mem::transmute::<&RethRecovered, &RecoveredBlockShim>(block) };
BlockAndReceipts { block, receipts }
})
}

fn first_number(&self) -> Option<u64> {
Some(self.inner.first().number())
}

fn tip_number(&self) -> Option<u64> {
Some(self.inner.tip().number())
}

fn len(&self) -> usize {
self.inner.len()
}

fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}
Loading