diff --git a/Cargo.lock b/Cargo.lock index 3919c0d5..70136eb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1884,14 +1884,38 @@ dependencies = [ "alloy-eips", "alloy-genesis", "alloy-primitives", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", "alloy-signer", "alloy-signer-local", "alloy-sol-macro", "alloy-sol-types", + "async-trait", + "base-bundles", + "derive_more", "eyre", + "jsonrpsee", + "jsonrpsee-core", "op-alloy-network", "op-alloy-rpc-types", + "op-alloy-rpc-types-engine", + "op-revm 12.0.2", + "reth-node-api", + "reth-node-builder", + "reth-node-core", + "reth-optimism-chainspec", + "reth-optimism-node", + "reth-optimism-primitives", + "reth-optimism-rpc", + "reth-payload-builder", + "reth-rpc-api", + "reth-rpc-engine-api", + "reth-rpc-eth-types", + "reth-storage-api", + "reth-transaction-pool", + "serde", "serde_json", + "thiserror 2.0.17", ] [[package]] @@ -6272,6 +6296,7 @@ dependencies = [ "async-trait", "base-bundles", "base-flashtypes", + "base-primitives", "chrono", "clap", "clap_builder", diff --git a/crates/builder/op-rbuilder/Cargo.toml b/crates/builder/op-rbuilder/Cargo.toml index ae7df68d..e7402f5a 100644 --- a/crates/builder/op-rbuilder/Cargo.toml +++ b/crates/builder/op-rbuilder/Cargo.toml @@ -12,6 +12,7 @@ workspace = true [dependencies] base-bundles.workspace = true +base-primitives = { workspace = true, features = ["op-rbuilder"] } base-flashtypes.workspace = true reth-optimism-node.workspace = true @@ -59,6 +60,7 @@ reth-optimism-rpc.workspace = true reth-tasks.workspace = true reth-tracing-otlp = { workspace = true, optional = true } + alloy-primitives.workspace = true alloy-consensus.workspace = true alloy-contract.workspace = true @@ -199,7 +201,7 @@ testing = [ interop = [] -telemetry = [ "opentelemetry", "reth-tracing-otlp" ] +telemetry = [ "opentelemetry", "dep:reth-tracing-otlp" ] custom-engine-api = [] diff --git a/crates/builder/op-rbuilder/src/builders/builder_tx.rs b/crates/builder/op-rbuilder/src/builders/builder_tx.rs index 19170a3e..fb356733 100644 --- a/crates/builder/op-rbuilder/src/builders/builder_tx.rs +++ b/crates/builder/op-rbuilder/src/builders/builder_tx.rs @@ -33,9 +33,8 @@ use revm::{ }; use tracing::{trace, warn}; -use crate::{ - builders::context::OpPayloadBuilderCtx, primitives::reth::ExecutionInfo, tx_signer::Signer, -}; +use crate::{builders::context::OpPayloadBuilderCtx, tx_signer::Signer}; +use base_primitives::op_rbuilder::reth::ExecutionInfo; #[derive(Debug, Default)] pub struct SimulationSuccessResult { diff --git a/crates/builder/op-rbuilder/src/builders/context.rs b/crates/builder/op-rbuilder/src/builders/context.rs index 6b5d4c7f..8cf34726 100644 --- a/crates/builder/op-rbuilder/src/builders/context.rs +++ b/crates/builder/op-rbuilder/src/builders/context.rs @@ -43,12 +43,12 @@ use tracing::{debug, info, trace}; use crate::{ gas_limiter::AddressGasLimiter, metrics::OpRBuilderMetrics, - primitives::reth::{ExecutionInfo, TxnExecutionResult}, traits::PayloadTxsBounds, tx::MaybeRevertingTransaction, tx_data_store::{TxData, TxDataStore}, tx_signer::Signer, }; +use base_primitives::op_rbuilder::reth::{ExecutionInfo, TxnExecutionResult}; /// Container type that holds all necessities to build a new payload. #[derive(Debug)] diff --git a/crates/builder/op-rbuilder/src/builders/flashblocks/builder_tx.rs b/crates/builder/op-rbuilder/src/builders/flashblocks/builder_tx.rs index dea1f08d..7a71ef80 100644 --- a/crates/builder/op-rbuilder/src/builders/flashblocks/builder_tx.rs +++ b/crates/builder/op-rbuilder/src/builders/flashblocks/builder_tx.rs @@ -23,9 +23,9 @@ use crate::{ get_nonce, }, flashtestations::builder_tx::FlashtestationsBuilderTx, - primitives::reth::ExecutionInfo, tx_signer::Signer, }; +use base_primitives::op_rbuilder::reth::ExecutionInfo; sol!( // From https://github.com/Uniswap/flashblocks_number_contract/blob/main/src/FlashblockNumber.sol diff --git a/crates/builder/op-rbuilder/src/builders/flashblocks/payload.rs b/crates/builder/op-rbuilder/src/builders/flashblocks/payload.rs index 93c6ecaa..d3c450ab 100644 --- a/crates/builder/op-rbuilder/src/builders/flashblocks/payload.rs +++ b/crates/builder/op-rbuilder/src/builders/flashblocks/payload.rs @@ -4,7 +4,6 @@ use std::{ sync::Arc, time::Instant, }; - use alloy_consensus::{ BlockBody, EMPTY_OMMER_ROOT_HASH, Header, constants::EMPTY_WITHDRAWALS, proofs, }; diff --git a/crates/builder/op-rbuilder/src/builders/standard/builder_tx.rs b/crates/builder/op-rbuilder/src/builders/standard/builder_tx.rs index e623e37e..3a4f45c7 100644 --- a/crates/builder/op-rbuilder/src/builders/standard/builder_tx.rs +++ b/crates/builder/op-rbuilder/src/builders/standard/builder_tx.rs @@ -12,9 +12,9 @@ use crate::{ builder_tx::BuilderTxBase, context::OpPayloadBuilderCtx, }, flashtestations::builder_tx::FlashtestationsBuilderTx, - primitives::reth::ExecutionInfo, tx_signer::Signer, }; +use base_primitives::op_rbuilder::reth::ExecutionInfo; // This will be the end of block transaction of a regular block #[derive(Debug, Clone)] diff --git a/crates/builder/op-rbuilder/src/builders/standard/payload.rs b/crates/builder/op-rbuilder/src/builders/standard/payload.rs index d05c5ef3..030e865b 100644 --- a/crates/builder/op-rbuilder/src/builders/standard/payload.rs +++ b/crates/builder/op-rbuilder/src/builders/standard/payload.rs @@ -1,5 +1,4 @@ use std::{sync::Arc, time::Instant}; - use alloy_consensus::{ BlockBody, EMPTY_OMMER_ROOT_HASH, Header, constants::EMPTY_WITHDRAWALS, proofs, }; diff --git a/crates/builder/op-rbuilder/src/flashtestations/builder_tx.rs b/crates/builder/op-rbuilder/src/flashtestations/builder_tx.rs index 03fe213d..5420cbe1 100644 --- a/crates/builder/op-rbuilder/src/flashtestations/builder_tx.rs +++ b/crates/builder/op-rbuilder/src/flashtestations/builder_tx.rs @@ -26,9 +26,9 @@ use crate::{ IERC20Permit, IFlashtestationRegistry::{self, TEEServiceRegistered}, }, - primitives::reth::ExecutionInfo, tx_signer::Signer, }; +use base_primitives::op_rbuilder::reth::ExecutionInfo; #[derive(Debug)] pub struct FlashtestationsBuilderTxArgs { diff --git a/crates/builder/op-rbuilder/src/launcher.rs b/crates/builder/op-rbuilder/src/launcher.rs index 8f803c49..5eb97a06 100644 --- a/crates/builder/op-rbuilder/src/launcher.rs +++ b/crates/builder/op-rbuilder/src/launcher.rs @@ -20,11 +20,11 @@ use crate::{ builders::{BuilderConfig, PayloadBuilder}, metrics::{VERSION, record_flag_gauge_metrics}, monitor_tx_pool::monitor_tx_pool, - primitives::reth::engine_api_builder::OpEngineApiBuilder, revert_protection::{EthApiExtServer, RevertProtectionExt}, tx::FBPooledTransaction, tx_data_store::{BaseApiExtServer, TxDataStoreExt}, }; +use base_primitives::op_rbuilder::reth::engine_api_builder::OpEngineApiBuilder; /// Launcher for the OP builder node. #[derive(Debug)] diff --git a/crates/builder/op-rbuilder/src/lib.rs b/crates/builder/op-rbuilder/src/lib.rs index e51b57f1..dcbb0331 100644 --- a/crates/builder/op-rbuilder/src/lib.rs +++ b/crates/builder/op-rbuilder/src/lib.rs @@ -7,8 +7,8 @@ pub mod gas_limiter; pub mod launcher; pub mod metrics; mod monitor_tx_pool; -pub mod primitives; pub mod revert_protection; +pub mod telemetry; pub mod traits; pub mod tx; pub mod tx_data_store; diff --git a/crates/builder/op-rbuilder/src/primitives/mod.rs b/crates/builder/op-rbuilder/src/primitives/mod.rs deleted file mode 100644 index da146d30..00000000 --- a/crates/builder/op-rbuilder/src/primitives/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod bundle; -pub mod reth; -#[cfg(feature = "telemetry")] -pub mod telemetry; diff --git a/crates/builder/op-rbuilder/src/revert_protection.rs b/crates/builder/op-rbuilder/src/revert_protection.rs index 445d2193..4a125d82 100644 --- a/crates/builder/op-rbuilder/src/revert_protection.rs +++ b/crates/builder/op-rbuilder/src/revert_protection.rs @@ -1,7 +1,7 @@ use std::{sync::Arc, time::Instant}; - use alloy_json_rpc::RpcObject; use alloy_primitives::B256; +use base_primitives::op_rbuilder::bundle::{Bundle, BundleConditionalExt, BundleResult}; use jsonrpsee::{ core::{RpcResult, async_trait}, proc_macros::rpc, @@ -16,7 +16,6 @@ use tracing::error; use crate::{ metrics::OpRBuilderMetrics, - primitives::bundle::{Bundle, BundleResult}, tx::{FBPooledTransaction, MaybeFlashblockFilter, MaybeRevertingTransaction}, }; @@ -117,14 +116,14 @@ where self.provider.best_block_number().map_err(|_e| EthApiError::InternalEthError)?; // Only one transaction in the bundle is expected - let bundle_transaction = match bundle.transactions.len() { + let bundle_transaction = match bundle.txs.len() { 0 => { return Err(EthApiError::InvalidParams( "bundle must contain at least one transaction".into(), ) .into()); } - 1 => bundle.transactions[0].clone(), + 1 => bundle.txs[0].clone(), _ => { return Err(EthApiError::InvalidParams( "bundle must contain exactly one transaction".into(), @@ -138,7 +137,7 @@ where let recovered = recover_raw_transaction(&bundle_transaction)?; let pool_transaction = FBPooledTransaction::from(OpPooledTransaction::from_pooled(recovered)) - .with_reverted_hashes(bundle.reverting_hashes.clone().unwrap_or_default()) + .with_reverted_hashes(bundle.reverting_tx_hashes.clone()) .with_flashblock_number_min(conditional.flashblock_number_min) .with_flashblock_number_max(conditional.flashblock_number_max) .with_conditional(conditional.transaction_conditional); diff --git a/crates/builder/op-rbuilder/src/primitives/telemetry.rs b/crates/builder/op-rbuilder/src/telemetry.rs similarity index 75% rename from crates/builder/op-rbuilder/src/primitives/telemetry.rs rename to crates/builder/op-rbuilder/src/telemetry.rs index ffdb50a9..06ec0a0b 100644 --- a/crates/builder/op-rbuilder/src/primitives/telemetry.rs +++ b/crates/builder/op-rbuilder/src/telemetry.rs @@ -1,9 +1,12 @@ -use tracing_subscriber::{Layer, filter::Targets}; -use url::Url; - +#[cfg(feature = "telemetry")] use crate::args::TelemetryArgs; +#[cfg(feature = "telemetry")] +use tracing_subscriber::{filter::Targets, Layer}; +#[cfg(feature = "telemetry")] +use url::Url; /// Setup telemetry layer with sampling and custom endpoint configuration +#[cfg(feature = "telemetry")] pub fn setup_telemetry_layer( args: &TelemetryArgs, ) -> eyre::Result> { @@ -13,26 +16,20 @@ pub fn setup_telemetry_layer( return Err(eyre::eyre!("OTLP endpoint is not set")); } - // Otlp uses evn vars inside - if let Some(headers) = &args.otlp_headers { unsafe { std::env::set_var("OTEL_EXPORTER_OTLP_HEADERS", headers) }; } - // Create OTLP layer with custom configuration let otlp_layer = reth_tracing_otlp::span_layer( "op-rbuilder", &Url::parse(args.otlp_endpoint.as_ref().unwrap()).expect("Invalid OTLP endpoint"), reth_tracing_otlp::OtlpProtocol::Http, )?; - // Create a trace filter that sends more data to OTLP but less to stdout let trace_filter = Targets::new() .with_default(LevelFilter::WARN) .with_target("op_rbuilder", LevelFilter::INFO) .with_target("payload_builder", LevelFilter::DEBUG); - let filtered_layer = otlp_layer.with_filter(trace_filter); - - Ok(filtered_layer) -} + Ok(otlp_layer.with_filter(trace_filter)) +} \ No newline at end of file diff --git a/crates/builder/op-rbuilder/src/tests/framework/instance.rs b/crates/builder/op-rbuilder/src/tests/framework/instance.rs index ec45a33d..08b5d7c9 100644 --- a/crates/builder/op-rbuilder/src/tests/framework/instance.rs +++ b/crates/builder/op-rbuilder/src/tests/framework/instance.rs @@ -45,7 +45,6 @@ use tokio_util::sync::CancellationToken; use crate::{ args::OpRbuilderArgs, builders::{BuilderConfig, FlashblocksBuilder, PayloadBuilder, StandardBuilder}, - primitives::reth::engine_api_builder::OpEngineApiBuilder, revert_protection::{EthApiExtServer, RevertProtectionExt}, tests::{ EngineApi, Ipc, TEE_DEBUG_ADDRESS, TransactionPoolObserver, builder_signer, create_test_db, @@ -55,6 +54,7 @@ use crate::{ tx_data_store::TxDataStore, tx_signer::Signer, }; +use base_primitives::op_rbuilder::reth::engine_api_builder::OpEngineApiBuilder; /// Clears OTEL-related environment variables that can interfere with CLI argument parsing. /// This is necessary because clap reads env vars for args with `env = "..."` attributes, diff --git a/crates/builder/op-rbuilder/src/tests/framework/txs.rs b/crates/builder/op-rbuilder/src/tests/framework/txs.rs index 721961c4..1d6fb10d 100644 --- a/crates/builder/op-rbuilder/src/tests/framework/txs.rs +++ b/crates/builder/op-rbuilder/src/tests/framework/txs.rs @@ -1,10 +1,11 @@ use core::cmp::max; use std::{collections::VecDeque, sync::Arc}; - +use crate::{tests::funded_signer, tx::FBPooledTransaction, tx_signer::Signer}; use alloy_consensus::TxEip1559; use alloy_eips::{BlockNumberOrTag, eip1559::MIN_PROTOCOL_BASE_FEE, eip2718::Encodable2718}; use alloy_primitives::{Address, B256, Bytes, TxHash, TxKind, U256, hex}; use alloy_provider::{PendingTransactionBuilder, Provider, RootProvider}; +use base_primitives::op_rbuilder::bundle::{Bundle, BundleResult}; use dashmap::DashMap; use futures::StreamExt; use moka::future::Cache; @@ -15,12 +16,7 @@ use reth_transaction_pool::{AllTransactionsEvents, FullTransactionEvent, Transac use tokio::sync::watch; use tracing::debug; -use crate::{ - primitives::bundle::{Bundle, BundleResult}, - tests::funded_signer, - tx::FBPooledTransaction, - tx_signer::Signer, -}; +// local test helpers #[derive(Clone, Copy, Default, Debug)] pub struct BundleOpts { @@ -202,15 +198,26 @@ impl TransactionBuilder { if let Some(bundle_opts) = bundle_opts { // Send the transaction as a bundle with the bundle options + // Map the test `BundleOpts` into the API `Bundle` shape. + let block_number = if let Some(max) = bundle_opts.block_number_max { + // prefer explicit max (target) when provided + max + } else if let Some(min) = bundle_opts.block_number_min { + // otherwise use min as the target + min + } else { + 0u64 + }; + let bundle = Bundle { - transactions: vec![transaction_encoded.into()], - reverting_hashes: if with_reverted_hash { Some(vec![txn_hash]) } else { None }, - block_number_min: bundle_opts.block_number_min, - block_number_max: bundle_opts.block_number_max, + txs: vec![transaction_encoded.into()], + block_number, flashblock_number_min: bundle_opts.flashblock_number_min, flashblock_number_max: bundle_opts.flashblock_number_max, min_timestamp: bundle_opts.min_timestamp, max_timestamp: bundle_opts.max_timestamp, + reverting_tx_hashes: if with_reverted_hash { vec![txn_hash] } else { vec![] }, + ..Default::default() }; let result: BundleResult = diff --git a/crates/builder/op-rbuilder/src/tests/revert.rs b/crates/builder/op-rbuilder/src/tests/revert.rs index 26dda657..3a97eaa2 100644 --- a/crates/builder/op-rbuilder/src/tests/revert.rs +++ b/crates/builder/op-rbuilder/src/tests/revert.rs @@ -4,12 +4,12 @@ use op_alloy_network::Optimism; use crate::{ args::OpRbuilderArgs, - primitives::bundle::MAX_BLOCK_RANGE_BLOCKS, tests::{ BlockTransactionsExt, BundleOpts, ChainDriver, ChainDriverExt, LocalInstance, ONE_ETH, OpRbuilderArgsTestExt, TransactionBuilderExt, }, }; +use base_primitives::op_rbuilder::bundle::MAX_BLOCK_RANGE_BLOCKS; /// This test ensures that the transactions that get reverted and not included in the block, /// are eventually dropped from the pool once their block range is reached. diff --git a/crates/shared/primitives/Cargo.toml b/crates/shared/primitives/Cargo.toml index a472ff00..63cacf28 100644 --- a/crates/shared/primitives/Cargo.toml +++ b/crates/shared/primitives/Cargo.toml @@ -22,9 +22,33 @@ alloy-sol-macro = { workspace = true, features = ["json"], optional = true } alloy-sol-types = { workspace = true, optional = true } alloy-consensus = { workspace = true, features = ["std"], optional = true } alloy-primitives = { workspace = true, features = ["serde"], optional = true } +alloy-rpc-types-engine = { workspace = true, optional = true } +alloy-rpc-types-eth = { workspace = true, optional = true } +async-trait = { workspace = true, optional = true } op-alloy-network = { workspace = true, optional = true } +op-alloy-rpc-types-engine = { workspace = true, optional = true } alloy-signer-local = { workspace = true, optional = true } op-alloy-rpc-types = { workspace = true, optional = true } +base-bundles = { workspace = true, optional = true } +derive_more = { workspace = true, optional = true } +jsonrpsee = { workspace = true, optional = true } +jsonrpsee-core = { workspace = true, optional = true } +op-revm = { workspace = true, optional = true } +reth-node-api = { workspace = true, optional = true } +reth-node-builder = { workspace = true, optional = true } +reth-node-core = { workspace = true, optional = true } +reth-optimism-chainspec = { workspace = true, optional = true } +reth-optimism-node = { workspace = true, optional = true } +reth-optimism-primitives = { workspace = true, optional = true } +reth-optimism-rpc = { workspace = true, optional = true } +reth-payload-builder = { workspace = true, optional = true } +reth-rpc-api = { workspace = true, optional = true } +reth-rpc-engine-api = { workspace = true, optional = true } +reth-rpc-eth-types = { workspace = true, optional = true } +reth-storage-api = { workspace = true, optional = true } +reth-transaction-pool = { workspace = true, optional = true } +serde = { workspace = true, optional = true } +thiserror = { workspace = true, optional = true } [features] default = [] @@ -43,3 +67,32 @@ test-utils = [ "dep:op-alloy-rpc-types", "dep:serde_json", ] +op-rbuilder = [ + "dep:alloy-eips", + "dep:alloy-primitives", + "dep:alloy-rpc-types-engine", + "dep:alloy-rpc-types-eth", + "dep:async-trait", + "dep:base-bundles", + "dep:derive_more", + "dep:eyre", + "dep:jsonrpsee", + "dep:jsonrpsee-core", + "dep:op-alloy-rpc-types-engine", + "dep:op-revm", + "dep:reth-node-api", + "dep:reth-node-builder", + "dep:reth-node-core", + "dep:reth-optimism-chainspec", + "dep:reth-optimism-node", + "dep:reth-optimism-primitives", + "dep:reth-optimism-rpc", + "dep:reth-payload-builder", + "dep:reth-rpc-api", + "dep:reth-rpc-engine-api", + "dep:reth-rpc-eth-types", + "dep:reth-storage-api", + "dep:reth-transaction-pool", + "dep:serde", + "dep:thiserror", +] diff --git a/crates/shared/primitives/src/lib.rs b/crates/shared/primitives/src/lib.rs index 273a68db..cd05f32a 100644 --- a/crates/shared/primitives/src/lib.rs +++ b/crates/shared/primitives/src/lib.rs @@ -6,5 +6,8 @@ #[cfg(any(test, feature = "test-utils"))] pub mod test_utils; +#[cfg(feature = "op-rbuilder")] +pub mod op_rbuilder; + #[cfg(any(test, feature = "test-utils"))] pub use test_utils::*; diff --git a/crates/builder/op-rbuilder/src/primitives/bundle.rs b/crates/shared/primitives/src/op_rbuilder/bundle.rs similarity index 51% rename from crates/builder/op-rbuilder/src/primitives/bundle.rs rename to crates/shared/primitives/src/op_rbuilder/bundle.rs index ce666600..0f97c593 100644 --- a/crates/builder/op-rbuilder/src/primitives/bundle.rs +++ b/crates/shared/primitives/src/op_rbuilder/bundle.rs @@ -1,5 +1,6 @@ -use alloy_primitives::{B256, Bytes}; +use alloy_primitives::B256; use alloy_rpc_types_eth::erc4337::TransactionConditional; +pub use base_bundles::Bundle; use reth_rpc_eth_types::EthApiError; use serde::{Deserialize, Serialize}; @@ -11,111 +12,6 @@ use serde::{Deserialize, Serialize}; /// number to set the default upper bound. pub const MAX_BLOCK_RANGE_BLOCKS: u64 = 10; -/// A bundle represents a collection of transactions that should be executed -/// together with specific conditional constraints. -/// -/// Bundles allow for sophisticated transaction ordering and conditional -/// execution based on block numbers, flashblock numbers, and timestamps. They -/// are a key primitive in MEV (Maximal Extractable Value) strategies and block -/// building. -/// -/// # Validation -/// -/// The following validations are performed before adding the transaction to the -/// mempool: -/// - Block number ranges are valid (min ≤ max) -/// - Maximum block numbers are not in the past -/// - Block ranges don't exceed `MAX_BLOCK_RANGE_BLOCKS` (currently 10) -/// - There's only one transaction in the bundle -/// - Flashblock number ranges are valid (min ≤ max) -#[derive(Serialize, Deserialize, Debug, Clone, Default)] -pub struct Bundle { - /// List of raw transaction data to be included in the bundle. - /// - /// Each transaction is represented as raw bytes that will be decoded and - /// executed in the specified order when the bundle conditions are met. - #[serde(rename = "txs")] - pub transactions: Vec, - - /// Optional list of transaction hashes that are allowed to revert. - /// - /// By default, if any transaction in a bundle reverts, the entire bundle is - /// considered invalid. This field allows specific transactions to revert - /// without invalidating the bundle, enabling more sophisticated MEV - /// strategies. - #[serde(rename = "revertingTxHashes")] - pub reverting_hashes: Option>, - - /// Minimum block number at which this bundle can be included. - /// - /// If specified, the bundle will only be considered for inclusion in blocks - /// at or after this block number. This allows for scheduling bundles for - /// future execution. - #[serde( - default, - rename = "minBlockNumber", - with = "alloy_serde::quantity::opt", - skip_serializing_if = "Option::is_none" - )] - pub block_number_min: Option, - - /// Maximum block number at which this bundle can be included. - /// - /// If specified, the bundle will be considered invalid for inclusion in - /// blocks after this block number. If not specified, defaults to the - /// current block number plus `MAX_BLOCK_RANGE_BLOCKS`. - #[serde( - default, - rename = "maxBlockNumber", - with = "alloy_serde::quantity::opt", - skip_serializing_if = "Option::is_none" - )] - pub block_number_max: Option, - - /// Minimum flashblock number at which this bundle can be included. - /// - /// Flashblocks are preconfirmations that are built incrementally. This - /// field along with `maxFlashblockNumber` allows bundles to be scheduled - /// for more precise execution. - #[serde( - default, - rename = "minFlashblockNumber", - with = "alloy_serde::quantity::opt", - skip_serializing_if = "Option::is_none" - )] - pub flashblock_number_min: Option, - - /// Maximum flashblock number at which this bundle can be included. - /// - /// Similar to `minFlashblockNumber`, this sets an upper bound on which - /// flashblocks can include this bundle. - #[serde( - default, - rename = "maxFlashblockNumber", - with = "alloy_serde::quantity::opt", - skip_serializing_if = "Option::is_none" - )] - pub flashblock_number_max: Option, - - /// Minimum timestamp (Unix epoch seconds) for bundle inclusion. - /// - /// **Warning**: Not recommended for production use as it depends on the - /// builder node's clock, which may not be perfectly synchronized with - /// network time. Block number constraints are preferred for deterministic - /// behavior. - #[serde(default, rename = "minTimestamp", skip_serializing_if = "Option::is_none")] - pub min_timestamp: Option, - - /// Maximum timestamp (Unix epoch seconds) for bundle inclusion. - /// - /// **Warning**: Not recommended for production use as it depends on the - /// builder node's clock, which may not be perfectly synchronized with - /// network time. Block number constraints are preferred for deterministic - /// behavior. - #[serde(default, rename = "maxTimestamp", skip_serializing_if = "Option::is_none")] - pub max_timestamp: Option, -} - impl From for EthApiError { fn from(err: BundleConditionalError) -> Self { Self::InvalidParams(err.to_string()) @@ -153,23 +49,28 @@ pub struct BundleConditional { pub flashblock_number_max: Option, } -impl Bundle { - pub fn conditional( +pub trait BundleConditionalExt { + fn conditional( + &self, + last_block_number: u64, + ) -> Result; +} + +impl BundleConditionalExt for Bundle { + fn conditional( &self, last_block_number: u64, ) -> Result { - let mut block_number_max = self.block_number_max; - let block_number_min = self.block_number_min; + let mut block_number_min = None; + let mut block_number_max = None; + + if self.block_number != 0 { + block_number_min = Some(self.block_number); + block_number_max = Some(self.block_number); + } // Validate block number ranges if let Some(max) = block_number_max { - // Check if min > max - if let Some(min) = block_number_min - && min > max - { - return Err(BundleConditionalError::MinGreaterThanMax { min, max }); - } - // The max block cannot be a past block if max <= last_block_number { return Err(BundleConditionalError::MaxBlockInPast { @@ -191,16 +92,6 @@ impl Bundle { // If no upper bound is set, use the maximum block range let default_max = last_block_number + MAX_BLOCK_RANGE_BLOCKS; block_number_max = Some(default_max); - - // Ensure that the new max is not smaller than the min - if let Some(min) = block_number_min - && min > default_max - { - return Err(BundleConditionalError::MinTooHighForDefaultRange { - min, - max_allowed: default_max, - }); - } } // Validate flashblock number range @@ -240,14 +131,13 @@ pub struct BundleResult { #[serde(rename = "bundleHash")] pub bundle_hash: B256, } - #[cfg(test)] mod tests { use super::*; #[test] fn test_bundle_conditional_no_bounds() { - let bundle = Bundle { transactions: vec![], ..Default::default() }; + let bundle = Bundle::default(); let last_block = 1000; let result = bundle.conditional(last_block).unwrap().transaction_conditional; @@ -257,40 +147,19 @@ mod tests { } #[test] - fn test_bundle_conditional_with_valid_bounds() { - let bundle = Bundle { - block_number_max: Some(1005), - block_number_min: Some(1002), - ..Default::default() - }; + fn test_bundle_conditional_with_target_block() { + let bundle = Bundle { block_number: 1005, ..Default::default() }; let last_block = 1000; let result = bundle.conditional(last_block).unwrap().transaction_conditional; - assert_eq!(result.block_number_min, Some(1002)); + assert_eq!(result.block_number_min, Some(1005)); assert_eq!(result.block_number_max, Some(1005)); } - #[test] - fn test_bundle_conditional_min_greater_than_max() { - let bundle = Bundle { - block_number_max: Some(1005), - block_number_min: Some(1010), - ..Default::default() - }; - - let last_block = 1000; - let result = bundle.conditional(last_block); - - assert!(matches!( - result, - Err(BundleConditionalError::MinGreaterThanMax { min: 1010, max: 1005 }) - )); - } - #[test] fn test_bundle_conditional_max_in_past() { - let bundle = Bundle { block_number_max: Some(999), ..Default::default() }; + let bundle = Bundle { block_number: 999, ..Default::default() }; let last_block = 1000; let result = bundle.conditional(last_block); @@ -303,7 +172,7 @@ mod tests { #[test] fn test_bundle_conditional_max_too_high() { - let bundle = Bundle { block_number_max: Some(1020), ..Default::default() }; + let bundle = Bundle { block_number: 1020, ..Default::default() }; let last_block = 1000; let result = bundle.conditional(last_block); @@ -318,52 +187,6 @@ mod tests { )); } - #[test] - fn test_bundle_conditional_min_too_high_for_default_range() { - let bundle = Bundle { block_number_min: Some(1015), ..Default::default() }; - - let last_block = 1000; - let result = bundle.conditional(last_block); - - assert!(matches!( - result, - Err(BundleConditionalError::MinTooHighForDefaultRange { min: 1015, max_allowed: 1010 }) - )); - } - - #[test] - fn test_bundle_conditional_with_only_min() { - let bundle = Bundle { block_number_min: Some(1005), ..Default::default() }; - - let last_block = 1000; - let result = bundle.conditional(last_block).unwrap().transaction_conditional; - - assert_eq!(result.block_number_min, Some(1005)); - assert_eq!(result.block_number_max, Some(1010)); // last_block + MAX_BLOCK_RANGE_BLOCKS - } - - #[test] - fn test_bundle_conditional_with_only_max() { - let bundle = Bundle { block_number_max: Some(1008), ..Default::default() }; - - let last_block = 1000; - let result = bundle.conditional(last_block).unwrap().transaction_conditional; - - assert_eq!(result.block_number_min, None); - assert_eq!(result.block_number_max, Some(1008)); - } - - #[test] - fn test_bundle_conditional_min_lower_than_last_block() { - let bundle = Bundle { block_number_min: Some(999), ..Default::default() }; - - let last_block = 1000; - let result = bundle.conditional(last_block).unwrap().transaction_conditional; - - assert_eq!(result.block_number_min, Some(999)); - assert_eq!(result.block_number_max, Some(1010)); - } - #[test] fn test_bundle_conditional_flashblock_min_greater_than_max() { let bundle = Bundle { diff --git a/crates/shared/primitives/src/op_rbuilder/mod.rs b/crates/shared/primitives/src/op_rbuilder/mod.rs new file mode 100644 index 00000000..de6c485d --- /dev/null +++ b/crates/shared/primitives/src/op_rbuilder/mod.rs @@ -0,0 +1,2 @@ +pub mod bundle; +pub mod reth; diff --git a/crates/builder/op-rbuilder/src/primitives/reth/engine_api_builder.rs b/crates/shared/primitives/src/op_rbuilder/reth/engine_api_builder.rs similarity index 92% rename from crates/builder/op-rbuilder/src/primitives/reth/engine_api_builder.rs rename to crates/shared/primitives/src/op_rbuilder/reth/engine_api_builder.rs index 6e5f8de1..c245b9bb 100644 --- a/crates/builder/op-rbuilder/src/primitives/reth/engine_api_builder.rs +++ b/crates/shared/primitives/src/op_rbuilder/reth/engine_api_builder.rs @@ -12,13 +12,15 @@ use op_alloy_rpc_types_engine::{ OpExecutionPayloadEnvelopeV3, OpExecutionPayloadEnvelopeV4, OpExecutionPayloadV4, OpPayloadAttributes, ProtocolVersion, SuperchainSignal, }; -use reth_node_api::{AddOnsContext, EngineApiValidator, EngineTypes, NodeTypes}; +use reth_node_api::{AddOnsContext, EngineApiValidator, FullNodeComponents, NodeTypes}; use reth_node_builder::rpc::{EngineApiBuilder, PayloadValidatorBuilder}; use reth_node_core::version::{CLIENT_CODE, version_metadata}; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_node::OpEngineTypes; +use reth_optimism_primitives::OpPrimitives; pub use reth_optimism_rpc::OpEngineApi; -use reth_optimism_rpc::{OpEngineApiServer, engine::OP_ENGINE_CAPABILITIES}; +use reth_optimism_rpc::engine::OP_ENGINE_CAPABILITIES; +use reth_optimism_rpc::OpEngineApiServer; use reth_payload_builder::PayloadStore; use reth_rpc_api::IntoEngineApiRpcModule; use reth_rpc_engine_api::EngineCapabilities; @@ -44,7 +46,9 @@ where impl EngineApiBuilder for OpEngineApiBuilder where - N: NodeComponents, + N: FullNodeComponents< + Types: NodeTypes, + >, EV: PayloadValidatorBuilder, EV::Validator: EngineApiValidator<::Payload>, { @@ -155,7 +159,7 @@ where async fn get_payload_v2( &self, payload_id: PayloadId, - ) -> RpcResult<::ExecutionPayloadEnvelopeV2> { + ) -> RpcResult<::ExecutionPayloadEnvelopeV2> { self.inner.get_payload_v2(payload_id).await } @@ -221,7 +225,7 @@ where /// This follows the Optimism specs that can be found at: /// #[rpc(server, namespace = "engine", server_bounds(Engine::PayloadAttributes: jsonrpsee::core::DeserializeOwned))] -pub trait OpRbuilderEngineApi { +pub trait OpRbuilderEngineApi { /// Sends the given payload to the execution layer client, as specified for the Shanghai fork. /// /// See also @@ -382,27 +386,18 @@ pub trait OpRbuilderEngineApi { count: U64, ) -> RpcResult; - /// Signals superchain information to the Engine. - /// Returns the latest supported OP-Stack protocol version of the execution engine. - /// See also - #[method(name = "engine_signalSuperchainV1")] - async fn signal_superchain_v1(&self, _signal: SuperchainSignal) -> RpcResult; + /// For OP-Superchain v1, this returns the protocol version for the given signal. + #[method(name = "signalSuperchainV1")] + async fn signal_superchain_v1(&self, signal: SuperchainSignal) -> RpcResult; - /// Returns the execution client version information. - /// - /// Note: - /// > The `client_version` parameter identifies the consensus client. - /// - /// See also + /// For OP-Superchain v1, this returns the client version for the given version. #[method(name = "getClientVersionV1")] async fn get_client_version_v1( &self, - client_version: ClientVersionV1, + client: ClientVersionV1, ) -> RpcResult>; - /// Returns the list of Engine API methods supported by the execution layer client software. - /// - /// See also + /// Returns the capabilities of the engine. #[method(name = "exchangeCapabilities")] async fn exchange_capabilities(&self, capabilities: Vec) -> RpcResult>; } diff --git a/crates/builder/op-rbuilder/src/primitives/reth/execution.rs b/crates/shared/primitives/src/op_rbuilder/reth/execution.rs similarity index 100% rename from crates/builder/op-rbuilder/src/primitives/reth/execution.rs rename to crates/shared/primitives/src/op_rbuilder/reth/execution.rs diff --git a/crates/builder/op-rbuilder/src/primitives/reth/mod.rs b/crates/shared/primitives/src/op_rbuilder/reth/mod.rs similarity index 99% rename from crates/builder/op-rbuilder/src/primitives/reth/mod.rs rename to crates/shared/primitives/src/op_rbuilder/reth/mod.rs index ed2b38b9..e01b907a 100644 --- a/crates/builder/op-rbuilder/src/primitives/reth/mod.rs +++ b/crates/shared/primitives/src/op_rbuilder/reth/mod.rs @@ -1,3 +1,4 @@ pub mod engine_api_builder; mod execution; + pub use execution::{ExecutionInfo, TxnExecutionResult};