Skip to content
Open
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
38 changes: 38 additions & 0 deletions Cargo.lock

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

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ members = [
"crates/payload/builder",
"crates/payload/types",
"crates/primitives",
"crates/reference-index",
"crates/revm",
"crates/txpool",
]
Expand Down Expand Up @@ -50,6 +51,7 @@ morph-node = { path = "crates/node"}
morph-payload-builder = { path = "crates/payload/builder", default-features = false }
morph-payload-types = { path = "crates/payload/types", default-features = false }
morph-primitives = { path = "crates/primitives", default-features = false }
morph-reference-index = { path = "crates/reference-index", default-features = false }
morph-rpc = { path = "crates/rpc" }
morph-revm = { path = "crates/revm", default-features = false }
morph-txpool = { path = "crates/txpool", default-features = false }
Expand All @@ -72,6 +74,9 @@ reth-engine-primitives = { git = "https://github.com/morph-l2/reth", rev = "1b07
reth-engine-tree = { git = "https://github.com/morph-l2/reth", rev = "1b0702546633c259306017717b2938f14adfe329" }
reth-errors = { git = "https://github.com/morph-l2/reth", rev = "1b0702546633c259306017717b2938f14adfe329" }
reth-eth-wire-types = { git = "https://github.com/morph-l2/reth", rev = "1b0702546633c259306017717b2938f14adfe329" }
reth-exex = { git = "https://github.com/morph-l2/reth", rev = "1b0702546633c259306017717b2938f14adfe329" }
reth-exex-test-utils = { git = "https://github.com/morph-l2/reth", rev = "1b0702546633c259306017717b2938f14adfe329" }
reth-exex-types = { git = "https://github.com/morph-l2/reth", rev = "1b0702546633c259306017717b2938f14adfe329" }
reth-ethereum = { git = "https://github.com/morph-l2/reth", rev = "1b0702546633c259306017717b2938f14adfe329" }
reth-ethereum-cli = { git = "https://github.com/morph-l2/reth", rev = "1b0702546633c259306017717b2938f14adfe329" }
reth-ethereum-consensus = { git = "https://github.com/morph-l2/reth", rev = "1b0702546633c259306017717b2938f14adfe329" }
Expand Down
2 changes: 2 additions & 0 deletions bin/morph-reth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ morph-chainspec = { workspace = true, features = ["cli"] }
morph-consensus.workspace = true
morph-evm.workspace = true
morph-node.workspace = true
morph-reference-index.workspace = true

# Reth CLI
reth-chainspec.workspace = true
reth-cli.workspace = true
reth-cli-util.workspace = true
reth-ethereum-cli.workspace = true
Expand Down
35 changes: 33 additions & 2 deletions bin/morph-reth/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ use clap::Parser;
use morph_chainspec::{MorphChainSpec, MorphChainSpecParser};
use morph_consensus::MorphConsensus;
use morph_evm::{MorphEvmConfig, evm::MorphEvmFactory};
use morph_node::{MorphArgs, MorphNode};
use morph_node::{
MorphAddOns, MorphArgs, MorphNode,
exex::{ReferenceIndexControl, reference_index_exex},
};
use morph_reference_index::ReferenceIndexDb;
use reth_chainspec::EthChainSpec;
use reth_cli_util::sigsegv_handler;
use reth_ethereum_cli::Cli;
use reth_node_builder::Node;
use reth_rpc_server_types::DefaultRpcModuleValidator;
use std::sync::Arc;
use tracing::info;
Expand Down Expand Up @@ -43,8 +49,33 @@ fn main() {
.run_with_components::<MorphNode>(components, async move |builder, morph_args| {
info!(target: "morph::cli", "Starting Morph-Reth node");

// Open the reference index DB before launching the node so we
// can wire it into both the ExEx and the add-ons.
let chain_spec = builder.config().chain.clone();
let datadir = builder.config().datadir();
let reference_index_path = datadir.data_dir().join("morph").join("reference_index");
let chain_id = chain_spec.chain().id();
let genesis_hash = chain_spec.genesis_hash(); // from EthChainSpec trait

info!(
target: "morph::reference_index",
path = %reference_index_path.display(),
chain_id,
"opening Morph reference index database"
);
let db = ReferenceIndexDb::open(&reference_index_path, chain_id, genesis_hash)?;
let (control, startup_rx) = ReferenceIndexControl::new(db);

let exex_control = control.clone();
let node = MorphNode::new(morph_args);

let handle = builder
.node(MorphNode::new(morph_args))
.with_types::<MorphNode>()
.with_components(node.components_builder())
.with_add_ons(MorphAddOns::new().with_reference_index(control))
.install_exex("morph-reference-index", async move |ctx| {
Ok(reference_index_exex(ctx, exex_control, startup_rx))
})
.launch_with_debug_capabilities()
.await?;

Expand Down
16 changes: 10 additions & 6 deletions crates/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@ morph-evm.workspace = true
morph-payload-builder.workspace = true
morph-payload-types.workspace = true
morph-primitives = { workspace = true, features = ["reth-codec"] }
morph-reference-index.workspace = true
morph-rpc.workspace = true
morph-txpool.workspace = true

# Reth dependencies
reth-db.workspace = true
reth-db-api.workspace = true
reth-node-core.workspace = true
reth-chainspec.workspace = true
reth-engine-local.workspace = true
reth-engine-tree.workspace = true
reth-errors.workspace = true
reth-exex.workspace = true
reth-node-api.workspace = true
reth-node-builder.workspace = true
reth-node-ethereum.workspace = true
Expand All @@ -37,12 +40,15 @@ reth-primitives-traits.workspace = true
reth-provider.workspace = true
reth-rpc-builder.workspace = true
reth-rpc-eth-api.workspace = true
reth-storage-api.workspace = true
reth-tasks.workspace = true
reth-transaction-pool.workspace = true
reth-tracing.workspace = true
reth-trie.workspace = true

# Alloy
alloy-consensus.workspace = true
alloy-eips.workspace = true
alloy-genesis.workspace = true
alloy-hardforks.workspace = true
alloy-primitives.workspace = true
Expand All @@ -54,16 +60,15 @@ eyre.workspace = true
clap.workspace = true
dashmap.workspace = true
parking_lot.workspace = true
tokio = { workspace = true, features = ["sync", "rt"] }
tokio-stream.workspace = true
tracing.workspace = true

serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true

# Optional: E2E testing framework
reth-e2e-test-utils = { workspace = true, optional = true }
reth-tasks = { workspace = true, optional = true }
tokio = { workspace = true, features = ["sync"], optional = true }
alloy-eips = { workspace = true, optional = true }
alloy-rlp = { workspace = true, optional = true }
alloy-signer = { workspace = true, optional = true }
alloy-signer-local = { workspace = true, optional = true }
Expand All @@ -85,9 +90,11 @@ alloy-primitives.workspace = true
alloy-rpc-types-engine.workspace = true
alloy-rpc-types-eth.workspace = true
jsonrpsee.workspace = true
morph-chainspec.workspace = true
morph-payload-types.workspace = true
morph-primitives.workspace = true
serde_json.workspace = true
tempfile.workspace = true

[[test]]
name = "it"
Expand All @@ -98,10 +105,7 @@ required-features = ["test-utils"]
default = []
test-utils = [
"dep:reth-e2e-test-utils",
"dep:reth-tasks",
"dep:alloy-signer",
"dep:tokio",
"dep:alloy-eips",
"dep:alloy-rlp",
"dep:alloy-signer-local",
]
59 changes: 58 additions & 1 deletion crates/node/src/add_ons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

use crate::{
MorphNode,
exex::ReferenceIndexControl,
validator::{MorphEngineValidatorBuilder, MorphTreeEngineValidatorBuilder},
};
use morph_evm::MorphEvmConfig;
use morph_primitives::{Block, MorphHeader, MorphReceipt};
use morph_rpc::{MorphEthApiBuilder, MorphEthConfigApiServer, MorphEthConfigHandler};
use morph_reference_index::{DEFAULT_LAG_THRESHOLD, ReferenceIndexReader};
use morph_rpc::{
MorphEthApiBuilder, MorphEthConfigApiServer, MorphEthConfigHandler,
morph::{MorphRpc, MorphRpcHandler, MorphRpcServer},
};
use reth_node_api::{AddOnsContext, FullNodeComponents, FullNodeTypes, NodeAddOns, NodePrimitives};
use reth_node_builder::{
NodeAdapter,
Expand Down Expand Up @@ -39,6 +44,10 @@ pub struct MorphAddOns<
> {
/// Inner RPC add-ons from reth.
inner: RpcAddOns<N, EthB, PVB, NoopEngineApiBuilder, EVB, RpcMiddleware>,
/// Optional reference-index control injected by `main.rs`. When present
/// the add-on spawns startup indexing on launch and registers the
/// `morph_` RPC namespace.
reference_index: Option<ReferenceIndexControl>,
}

impl<N> MorphAddOns<NodeAdapter<N>, MorphEthApiBuilder>
Expand All @@ -59,8 +68,16 @@ where
MorphTreeEngineValidatorBuilder::new(pvb),
Identity::default(),
),
reference_index: None,
}
}

/// Attach a reference index control so the add-on can spawn startup
/// indexing and register the `morph_` RPC namespace on launch.
pub fn with_reference_index(mut self, control: ReferenceIndexControl) -> Self {
self.reference_index = Some(control);
self
}
}

impl<N> Default for MorphAddOns<NodeAdapter<N>, MorphEthApiBuilder>
Expand Down Expand Up @@ -116,6 +133,37 @@ where
}
});

// Spawn reference index startup indexing (Task A) if configured.
let reference_rpc_handler = if let Some(control) = self.reference_index {
let startup_control = control.clone();
let startup_node = ctx.node.clone();
// spawn_critical causes node shutdown on panic/error, matching the spec
// requirement that reference index startup failures are fatal.
task_executor.spawn_critical("morph reference index startup", async move {
let result = tokio::task::spawn_blocking(move || {
crate::exex::run_startup_indexing(&startup_node, &startup_control)
})
.await
.unwrap_or_else(|e| Err(eyre::eyre!("reference index startup panicked: {e}")));

match result {
Ok(()) => {}
Err(err) => {
// Propagate to spawn_critical which will shut down the node.
panic!("reference index startup failed: {err:?}");
}
}
});

let morph_rpc_ctx = MorphRpc::new(
ReferenceIndexReader::new(control.db, DEFAULT_LAG_THRESHOLD),
provider.clone(),
);
Some(MorphRpcHandler::new(morph_rpc_ctx))
} else {
None
};

// Use launch_add_ons_with to register custom Engine API and eth_config
self.inner
.launch_add_ons_with(ctx, move |container| {
Expand All @@ -133,6 +181,15 @@ where
.map_err(|e| eyre::eyre!("Failed to register eth_config handler: {}", e))?;
tracing::info!(target: "morph::node", "Morph eth_config handler registered successfully");

// Register morph_ RPC namespace (if reference index was configured).
if let Some(handler) = reference_rpc_handler {
tracing::debug!(target: "morph::node", "Registering morph_ RPC namespace");
modules
.merge_configured(handler.into_rpc())
.map_err(|e| eyre::eyre!("Failed to register morph_ RPC: {}", e))?;
tracing::info!(target: "morph::node", "morph_ RPC namespace registered");
}

// Create and register Morph L2 Engine API
tracing::debug!(target: "morph::node", "Registering Morph L2 Engine API");

Expand Down
5 changes: 5 additions & 0 deletions crates/node/src/exex/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//! Morph-specific Execution Extensions (ExEx).

pub mod reference_index;

pub use reference_index::{ReferenceIndexControl, reference_index_exex, run_startup_indexing};
Loading
Loading