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
301 changes: 147 additions & 154 deletions Cargo.lock

Large diffs are not rendered by default.

73 changes: 43 additions & 30 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -149,44 +149,56 @@ op-alloy-rpc-types-engine = { version = "0.22.0", features = ["serde"] }
op-revm = { version = "12.0.2"}

# Reth dependencies
reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", default-features = false }
reth-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-node-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-evm-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-tracing = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", features = [
reth = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3", default-features = false }
reth-chainspec = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-node-ethereum = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-evm-ethereum = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-evm = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-primitives = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-tracing = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-ethereum = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3", features = [
"full",
] }
reth-cli-util = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-chain-state = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-ethereum-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-node-core = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-db-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", features = [
reth-cli-util = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-chain-state = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-primitives-traits = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-ethereum-primitives = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-node-api = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-node-core = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-db = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-db-api = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-provider = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3", features = [
"test-utils",
] }
reth-ethereum-consensus = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-stages = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-ethereum-consensus = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-stages = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-basic-payload-builder = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-consensus-common = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-errors = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-payload-builder = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-payload-builder-primitives = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-payload-primitives = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-revm = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-storage-api = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-transaction-pool = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-node-builder = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-ethereum-payload-builder = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-ethereum-engine-primitives = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }

# OP Reth dependencies
reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-optimism-consensus = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-optimism-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-optimism-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-optimism-node = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-optimism-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-optimism-chainspec = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-optimism-cli = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-optimism-consensus = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-optimism-evm = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-optimism-forks = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-optimism-node = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-optimism-payload-builder = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-optimism-primitives = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-optimism-rpc = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }

# Reth test dependencies
reth-exex-test-utils = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-testing-utils = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-exex-test-utils = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }
reth-testing-utils = { git = "https://github.com/web3x3/reth", branch = "metis-1.9.3" }

# Revmc dependencies
revmc = { git = "https://github.com/paradigmxyz/revmc", features = [
Expand Down Expand Up @@ -292,3 +304,4 @@ inherits = "profiling"
inherits = "release"
lto = "fat"
codegen-units = 1

13 changes: 13 additions & 0 deletions crates/chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ alloy-eips.workspace = true
alloy-sol-types.workspace = true
alloy-genesis.workspace = true
alloy-consensus.workspace = true
alloy-rlp.workspace = true

revm.workspace = true

Expand All @@ -40,6 +41,18 @@ reth-primitives-traits.workspace = true
reth-ethereum-primitives.workspace = true
reth-node-api.workspace = true
reth-node-core.workspace = true
reth-basic-payload-builder.workspace = true
reth-consensus-common.workspace = true
reth-errors.workspace = true
reth-payload-builder.workspace = true
reth-payload-builder-primitives.workspace = true
reth-payload-primitives.workspace = true
reth-revm.workspace = true
reth-storage-api.workspace = true
reth-transaction-pool.workspace = true
reth-node-builder.workspace = true
reth-ethereum-payload-builder.workspace = true
reth-ethereum-engine-primitives.workspace = true

# Alloy OP
alloy-op-evm.workspace = true
Expand Down
12 changes: 9 additions & 3 deletions crates/chain/bin/metis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::ne
use clap::Parser;
use metis_chain::hook_provider::HookExecutorBuilder;
use metis_chain::op_provider::OpParallelNode;
use metis_chain::parallel_payload_builder::ParallelPayloadBuilderBuilder;
use metis_chain::provider::ParallelExecutorBuilder;
use reth::builder::components::BasicPayloadServiceBuilder;
use reth::cli::Cli;
use reth_node_ethereum::EthereumNode;
use reth_node_ethereum::node::EthereumAddOns;
Expand All @@ -24,7 +26,7 @@ fn main() {
if std::env::var_os("ENABLE_OP_EXECUTOR").is_some() {
if let Err(err) = OpCli::<OpChainSpecParser, RollupArgs>::parse().run(
async move |builder, rollup_args| {
info!(target: "reth::cli", "Launching node");
info!(target: "metis::cli", "Launching node");
if std::env::var_os("ENABLE_PARALLEL_EXECUTOR").is_some() {
let handle = builder.node(OpParallelNode::new(OpNode::new(rollup_args)));
handle.launch().await?.wait_for_node_exit().await
Expand All @@ -44,9 +46,13 @@ fn main() {
// Use the default ethereum node types
.with_types::<EthereumNode>()
// Configure the components of the node
// use default ethereum components but use our parallel executor.
// Use our parallel executor AND parallel payload builder
.with_components(
EthereumNode::components().executor(ParallelExecutorBuilder::default()),
EthereumNode::components()
.executor(ParallelExecutorBuilder::default())
.payload(BasicPayloadServiceBuilder::new(
ParallelPayloadBuilderBuilder::default(),
)),
)
.with_add_ons(EthereumAddOns::default());
handle.launch().await?.wait_for_node_exit().await
Expand Down
6 changes: 6 additions & 0 deletions crates/chain/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
pub mod hook_provider;
pub mod op_provider;
pub mod parallel_execution_context;
pub mod parallel_payload_builder;
pub mod provider;
pub mod state;

// Re-export important types for external use
pub use parallel_execution_context::{ExecutionMode, ParallelExecutionContext};
pub use provider::BatchExecutable;
12 changes: 10 additions & 2 deletions crates/chain/src/op_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,8 @@ where
})?
.into_iter()
.map(|r| {
self.evm_mut().db_mut().commit(r.state);
// Commit state from result_and_state
self.evm_mut().db_mut().commit(r.result_and_state.state);
total_gas_used += &r.receipt.cumulative_gas_used;
match &r.receipt.tx_type {
TxType::Legacy => OpReceipt::Legacy(Receipt::from(r.receipt)),
Expand All @@ -325,10 +326,17 @@ where
fn post_execution(&mut self) -> Result<(), BlockExecutionError> {
let balance_increments =
post_block_balance_increments::<Header>(&self.spec, self.evm().block(), &[], None);

// CRITICAL: Sort balance_increments by address to ensure deterministic commit order.
// HashMap iteration order is non-deterministic, which can cause different state roots
// across nodes. Use BTreeMap to maintain sorted order.
let sorted_balance_increments: std::collections::BTreeMap<_, _> =
balance_increments.into_iter().collect();

// increment balances
self.evm_mut()
.db_mut()
.increment_balances(balance_increments.clone())
.increment_balances(sorted_balance_increments)
.map_err(|_| BlockValidationError::IncrementBalanceFailed)?;

Ok(())
Expand Down
157 changes: 157 additions & 0 deletions crates/chain/src/parallel_execution_context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//! Parallel Execution Context - Execution state management
//!
//! This module tracks the execution context and ensures post_execution
//! is called exactly once per block execution.

use alloy_primitives::TxHash;
use std::collections::HashMap;

/// Execution mode for parallel block executor
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExecutionMode {
/// Payload Builder mode - constructing new blocks (parallel execution)
Builder,
/// Payload Validator mode - validating received blocks (serial execution)
Validator,
/// Block Import mode - importing/syncing blocks (parallel execution)
Import,
}

/// Cached result from parallel execution
#[derive(Debug, Clone)]
pub struct CachedExecutionResult {
pub gas_used: u64,
pub receipt: reth_primitives::Receipt,
}

/// Context for tracking parallel execution state
#[derive(Debug)]
pub struct ParallelExecutionContext {
/// Whether post_execution has been called for this block
pub post_execution_called: bool,
/// Current execution mode
pub execution_mode: ExecutionMode,
/// Cache of parallel execution results (tx_hash -> result)
/// Used in payload builder to avoid re-executing transactions
pub execution_cache: HashMap<TxHash, CachedExecutionResult>,
}

impl ParallelExecutionContext {
/// Create a new execution context
pub fn new(mode: ExecutionMode) -> Self {
Self {
post_execution_called: false,
execution_mode: mode,
execution_cache: HashMap::new(),
}
}

/// Check if parallel execution should be used based on mode
pub fn should_execute_in_parallel(&self) -> bool {
matches!(
self.execution_mode,
ExecutionMode::Builder | ExecutionMode::Import
)
}

/// Reset the context for a new block
pub fn reset(&mut self) {
self.post_execution_called = false;
self.execution_cache.clear();
}

/// Mark post_execution as called
pub fn mark_post_execution_called(&mut self) {
self.post_execution_called = true;
}

/// Cache execution result for a transaction
pub fn cache_result(
&mut self,
tx_hash: TxHash,
gas_used: u64,
receipt: reth_primitives::Receipt,
) {
self.execution_cache
.insert(tx_hash, CachedExecutionResult { gas_used, receipt });
}

/// Get cached execution result
pub fn get_cached_result(&self, tx_hash: &TxHash) -> Option<&CachedExecutionResult> {
self.execution_cache.get(tx_hash)
}
}

impl Default for ParallelExecutionContext {
fn default() -> Self {
// Default to Builder mode (parallel execution)
Self::new(ExecutionMode::Builder)
}
}

// Thread-local global cache for parallel execution results
// This is used by payload builder to pass results to the executor
use std::cell::RefCell;

thread_local! {
static GLOBAL_EXECUTION_CACHE: RefCell<HashMap<TxHash, CachedExecutionResult>> = RefCell::new(HashMap::new());
}

/// Set global cache with parallel execution results
/// Called by payload builder before serial execution loop
pub fn set_global_cache(
tx_hashes: &[TxHash],
results: &[metis_pe::TxExecutionResult<metis_primitives::HaltReason>],
) {
GLOBAL_EXECUTION_CACHE.with(|cache| {
let mut cache = cache.borrow_mut();
cache.clear();
for (tx_hash, result) in tx_hashes.iter().zip(results.iter()) {
cache.insert(
*tx_hash,
CachedExecutionResult {
gas_used: result.receipt.cumulative_gas_used,
receipt: result.receipt.clone(),
},
);
}
tracing::debug!(target: "metis::parallel",
"Set global cache with {} parallel execution results",
cache.len()
);
});
}

/// Get cached result from global cache
/// Called by executor during execute_transaction
pub fn get_global_cached_result(tx_hash: &TxHash) -> Option<CachedExecutionResult> {
GLOBAL_EXECUTION_CACHE.with(|cache| cache.borrow().get(tx_hash).cloned())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_execution_modes() {
let builder_ctx = ParallelExecutionContext::new(ExecutionMode::Builder);
assert!(builder_ctx.should_execute_in_parallel());
assert!(!builder_ctx.post_execution_called);

let validator_ctx = ParallelExecutionContext::new(ExecutionMode::Validator);
assert!(!validator_ctx.should_execute_in_parallel());

let import_ctx = ParallelExecutionContext::new(ExecutionMode::Import);
assert!(import_ctx.should_execute_in_parallel());
}

#[test]
fn test_context_reset() {
let mut ctx = ParallelExecutionContext::default();
ctx.mark_post_execution_called();
assert!(ctx.post_execution_called);

ctx.reset();
assert!(!ctx.post_execution_called);
}
}
Loading
Loading