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
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ This changelog covers Firehose-specific changes only. For upstream reth changes,

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased

### Changed

- `reth` no longer installs a Firehose execution extension (ExEx). It only emitted `FIRE INIT`
and the genesis block; blocks are traced by the engine. Both are now emitted at startup before
the consensus engine exists, so no block can be traced ahead of `FIRE INIT`, and the node
stops writing the ExEx write-ahead log.

### Removed

- `reth_firehose::run_exex` and `reth_firehose::emit_genesis_block_if_empty`. Call
`reth_firehose::init_blockchain(&provider)` from the node builder's `on_component_initialized`
hook instead. `reth-firehose` no longer depends on `reth-exex`.

## reth-v2.7.0-fh3.1

### Changed
Expand Down
3 changes: 0 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions bin/reth/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ fn main() {
EthereumNode::components().executor(FirehoseExecutorBuilder::default()),
)
.with_add_ons(EthereumAddOns::default())
.install_exex("firehose", |ctx| async move {
Ok(async move { reth_firehose::run_exex(ctx).await })
})
.on_component_initialized(|node| reth_firehose::init_blockchain(&node.provider))
.launch_with_debug_capabilities()
.await?;

Expand Down
3 changes: 0 additions & 3 deletions crates/firehose/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ firehose-tracer.workspace = true

# reth
reth-chainspec.workspace = true
reth-ethereum-forks.workspace = true
reth-evm.workspace = true
reth-evm-ethereum.workspace = true
reth-execution-types.workspace = true
reth-exex.workspace = true
reth-node-api.workspace = true
reth-ethereum-primitives.workspace = true
reth-primitives-traits.workspace = true
Expand All @@ -38,7 +36,6 @@ alloy-rlp.workspace = true

# std
eyre.workspace = true
futures.workspace = true
rayon = { workspace = true, optional = true }

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/firehose/src/block_tracer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl FirehoseBlockTracer<GlobalTracerGuard> {
///
/// The genesis block (block 0) never goes through this guard: reth writes it directly to
/// the database without executing it, and it is emitted standalone via
/// [`crate::runner::emit_genesis_block_if_empty`] at startup.
/// [`crate::init::emit_genesis_block_on_empty_chain`] at startup.
///
/// Takes a [`SealedBlock`] rather than a `RecoveredBlock` so the guard can be started before
/// transaction senders have been recovered. Block-level data read by the mapper is signer-free.
Expand Down
77 changes: 77 additions & 0 deletions crates/firehose/src/init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use crate::{mapper, prelude::*};
use reth_chainspec::EthChainSpec;
use reth_provider::{BlockReader, ChainSpecProvider};

/// Emits `FIRE INIT` and, on an empty chain, the genesis block.
///
/// Call it once from the node builder's `on_component_initialized` hook. The tracer panics on a
/// block that starts before `FIRE INIT`, and the consensus engine can execute blocks before the
/// `on_node_started` hook runs.
pub fn init_blockchain<P>(provider: &P) -> eyre::Result<()>
where
P: BlockReader + ChainSpecProvider<ChainSpec: EthChainSpec>,
<P::Block as reth_primitives_traits::Block>::Header: BlockHeader + alloy_primitives::Sealable,
<<P::Block as reth_primitives_traits::Block>::Body as reth_primitives_traits::BlockBody>::OmmerHeader:
BlockHeader + alloy_primitives::Sealable,
{
let chain_spec = provider.chain_spec();
crate::tracer().on_blockchain_init(
"reth",
env!("CARGO_PKG_VERSION"),
firehose_tracer::config::ChainConfig::new(chain_spec.chain().id()),
);

emit_genesis_block_on_empty_chain(provider, chain_spec.genesis())
}

/// Emits the genesis block (block 0) to the Firehose stream if the chain is empty (head still
/// at genesis).
///
/// Reth never executes the genesis block — it is written straight to the database during init —
/// so no execution hook ever fires for it. Without this, a Firehose stream started from scratch
/// begins at block 1 and downstream consumers waiting for block 0 stall forever. Mirrors geth's
/// `OnGenesisBlock` hook: emitted with the full genesis alloc, only when starting on an empty
/// chain.
///
/// Call it once at node startup, after `on_blockchain_init` and before any block is traced.
/// Embedders that report their own node name or version in `FIRE INIT` call this directly instead
/// of [`init_blockchain`].
pub fn emit_genesis_block_on_empty_chain<P>(
provider: &P,
genesis: &alloy_genesis::Genesis,
) -> eyre::Result<()>
where
P: BlockReader,
<P::Block as reth_primitives_traits::Block>::Header: BlockHeader + alloy_primitives::Sealable,
<<P::Block as reth_primitives_traits::Block>::Body as reth_primitives_traits::BlockBody>::OmmerHeader:
BlockHeader + alloy_primitives::Sealable,
{
use reth_primitives_traits::Block as _;

// OP-stack chains may place their genesis at a non-zero height (e.g. OP mainnet's
// post-bedrock genesis); `genesis.number` carries it, defaulting to 0.
let genesis_number = genesis.number.unwrap_or(0);
if provider.best_block_number()? != genesis_number {
return Ok(());
}

let genesis_block = provider
.block(genesis_number.into())?
.ok_or_else(|| eyre::eyre!("genesis block {genesis_number} not found in database"))?
.seal_slow();

crate::tracer().on_genesis_block(
firehose_tracer::types::BlockEvent {
block: mapper::to_block_data(&genesis_block),
// Genesis is finalized by definition.
finalized: Some(firehose_tracer::types::FinalizedBlockRef {
number: genesis_number,
hash: Some(genesis_block.hash()),
}),
flash_block: None,
},
mapper::to_genesis_alloc(genesis),
);

Ok(())
}
8 changes: 4 additions & 4 deletions crates/firehose/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
//! Firehose crate providing blockchain data processing modules.
//!
//! This crate contains modules for inspection, mapping, prelude utilities, and running tasks.
//! This crate contains modules for inspection, mapping, prelude utilities, and node startup.

/// Block-level drop guard that manages the Firehose tracer lifecycle across validation.
pub mod block_tracer;
/// Executor module with Firehose-aware block executors and EVM configs.
pub mod executor;
/// Resolves which finalized block a Firehose block event may advertise.
pub mod finality;
/// Startup module emitting `FIRE INIT` and the genesis block.
pub mod init;
/// Inspector module for analyzing blockchain data.
pub mod inspector;
/// Mapper module for transforming blockchain data.
pub mod mapper;
/// Prelude module with common imports and utilities.
pub mod prelude;
/// Runner module for executing processing tasks.
pub mod runner;

pub use block_tracer::{FirehoseBlockTracer, GlobalTracerGuard};
pub use executor::{
Expand All @@ -25,8 +25,8 @@ pub use executor::{
};
pub use finality::finalized_ref_for_block;
pub use firehose_tracer::types::FinalizedBlockRef;
pub use init::{emit_genesis_block_on_empty_chain, init_blockchain};
pub use inspector::{FramelessTx, PostTxGasAccounting};
pub use runner::{emit_genesis_block_if_empty, emit_genesis_block_on_empty_chain, run_exex};

use std::{
io::Write,
Expand Down
8 changes: 1 addition & 7 deletions crates/firehose/src/mapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use firehose_tracer::types::{
AccessTuple, BlockData, GenesisAlloc, SetCodeAuthorization, TxEvent, UncleData, WithdrawalData,
};
use reth_primitives_traits::{Block as BlockTrait, BlockBody};
use reth_provider::{AccountReader, ProviderResult};
use reth_provider::AccountReader;

pub(crate) fn to_genesis_alloc(genesis: &Genesis) -> GenesisAlloc {
genesis
Expand Down Expand Up @@ -124,12 +124,6 @@ where
.collect()
}

pub(crate) fn to_finalized_ref(
block_ref: ProviderResult<Option<alloy_eips::BlockNumHash>>,
) -> Option<firehose_tracer::types::FinalizedBlockRef> {
finalized_ref_from_num_hash(block_ref.ok().flatten())
}

/// Builds a Firehose [`FinalizedBlockRef`](firehose_tracer::types::FinalizedBlockRef) from the
/// finalized block's number/hash, if known.
///
Expand Down
Loading
Loading