From 33c825a9e51d68b18eee6d6d32f173c77974ff0b Mon Sep 17 00:00:00 2001 From: Francis Li Date: Thu, 4 Dec 2025 10:37:30 -0800 Subject: [PATCH 1/6] Update to use op-alloy flashblock types (#328) --- Cargo.toml | 10 ++-- .../src/builders/flashblocks/payload.rs | 60 +++++++++++-------- .../builders/flashblocks/payload_handler.rs | 4 +- .../src/builders/flashblocks/wspub.rs | 6 +- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2a1dd0a04..ac4f60fc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -167,11 +167,11 @@ rollup-boost = { git = "https://github.com/flashbots/rollup-boost", tag = "v0.7. # optimism alloy-op-evm = { version = "0.23.0", default-features = false } alloy-op-hardforks = "0.4.4" -op-alloy-rpc-types = { version = "0.22.0", default-features = false } -op-alloy-rpc-types-engine = { version = "0.22.0", default-features = false } -op-alloy-rpc-jsonrpsee = { version = "0.22.0", default-features = false } -op-alloy-network = { version = "0.22.0", default-features = false } -op-alloy-consensus = { version = "0.22.0", default-features = false } +op-alloy-rpc-types = { version = "0.22.4", default-features = false } +op-alloy-rpc-types-engine = { version = "0.22.4", default-features = false } +op-alloy-rpc-jsonrpsee = { version = "0.22.4", default-features = false } +op-alloy-network = { version = "0.22.4", default-features = false } +op-alloy-consensus = { version = "0.22.4", default-features = false } op-alloy-flz = { version = "0.13.1", default-features = false } async-trait = { version = "0.1.83" } diff --git a/crates/op-rbuilder/src/builders/flashblocks/payload.rs b/crates/op-rbuilder/src/builders/flashblocks/payload.rs index 1d5d17854..3c4fbcce0 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/payload.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/payload.rs @@ -16,20 +16,24 @@ use alloy_consensus::{ BlockBody, EMPTY_OMMER_ROOT_HASH, Header, constants::EMPTY_WITHDRAWALS, proofs, }; use alloy_eips::{Encodable2718, eip7685::EMPTY_REQUESTS_HASH, merge::BEACON_NONCE}; -use alloy_primitives::{Address, B256, U256, map::foldhash::HashMap}; +use alloy_primitives::{Address, B256, U256}; use core::time::Duration; use eyre::WrapErr as _; +use op_alloy_rpc_types_engine::{ + OpFlashblockPayload, OpFlashblockPayloadBase, OpFlashblockPayloadDelta, + OpFlashblockPayloadMetadata, +}; use reth::payload::PayloadBuilderAttributes; use reth_basic_payload_builder::BuildOutcome; use reth_chain_state::ExecutedBlock; use reth_chainspec::EthChainSpec; use reth_evm::{ConfigureEvm, execute::BlockBuilder}; -use reth_node_api::{Block, NodePrimitives, PayloadBuilderError}; +use reth_node_api::{Block, PayloadBuilderError}; use reth_optimism_consensus::{calculate_receipt_root_no_memo_optimism, isthmus}; use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes}; use reth_optimism_forks::OpHardforks; use reth_optimism_node::{OpBuiltPayload, OpPayloadBuilderAttributes}; -use reth_optimism_primitives::{OpPrimitives, OpReceipt, OpTransactionSigned}; +use reth_optimism_primitives::{OpReceipt, OpTransactionSigned}; use reth_payload_util::BestPayloadTransactions; use reth_primitives_traits::RecoveredBlock; use reth_provider::{ @@ -42,11 +46,8 @@ use reth_revm::{ use reth_transaction_pool::TransactionPool; use reth_trie::{HashedPostState, updates::TrieUpdates}; use revm::Database; -use rollup_boost::{ - ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1, FlashblocksPayloadV1, -}; -use serde::{Deserialize, Serialize}; use std::{ + collections::BTreeMap, ops::{Div, Rem}, sync::Arc, time::Instant, @@ -55,6 +56,24 @@ use tokio::sync::mpsc; use tokio_util::sync::CancellationToken; use tracing::{debug, error, info, metadata::Level, span, warn}; +/// Converts a reth OpReceipt to an op-alloy OpReceipt +/// TODO: remove this once reth updates to use the op-alloy defined type as well. +fn convert_receipt(receipt: &OpReceipt) -> op_alloy_consensus::OpReceipt { + match receipt { + OpReceipt::Legacy(r) => op_alloy_consensus::OpReceipt::Legacy(r.clone()), + OpReceipt::Eip2930(r) => op_alloy_consensus::OpReceipt::Eip2930(r.clone()), + OpReceipt::Eip1559(r) => op_alloy_consensus::OpReceipt::Eip1559(r.clone()), + OpReceipt::Eip7702(r) => op_alloy_consensus::OpReceipt::Eip7702(r.clone()), + OpReceipt::Deposit(r) => { + op_alloy_consensus::OpReceipt::Deposit(op_alloy_consensus::OpDepositReceipt { + inner: r.inner.clone(), + deposit_nonce: r.deposit_nonce, + deposit_receipt_version: r.deposit_receipt_version, + }) + } + } +} + type NextBestFlashblocksTxs = BestFlashblocksTxs< ::Transaction, Box< @@ -939,13 +958,6 @@ where } } -#[derive(Debug, Serialize, Deserialize)] -struct FlashblocksMetadata { - receipts: HashMap::Receipt>, - new_account_balances: HashMap, - block_number: u64, -} - fn execute_pre_steps( state: &mut State, ctx: &OpPayloadBuilderCtx, @@ -971,7 +983,7 @@ pub(super) fn build_block( ctx: &OpPayloadBuilderCtx, info: &mut ExecutionInfo, calculate_state_root: bool, -) -> Result<(OpBuiltPayload, FlashblocksPayloadV1), PayloadBuilderError> +) -> Result<(OpBuiltPayload, OpFlashblockPayload), PayloadBuilderError> where DB: Database + AsRef

, P: StateRootProvider + HashedPostStateProvider + StorageRootProvider, @@ -1138,16 +1150,16 @@ where let receipts_with_hash = new_transactions .iter() .zip(new_receipts.iter()) - .map(|(tx, receipt)| (tx.tx_hash(), receipt.clone())) - .collect::>(); + .map(|(tx, receipt)| (tx.tx_hash(), convert_receipt(receipt))) + .collect::>(); let new_account_balances = state .bundle_state .state .iter() .filter_map(|(address, account)| account.info.as_ref().map(|info| (*address, info.balance))) - .collect::>(); + .collect::>(); - let metadata: FlashblocksMetadata = FlashblocksMetadata { + let metadata = OpFlashblockPayloadMetadata { receipts: receipts_with_hash, new_account_balances, block_number: ctx.parent().number + 1, @@ -1156,10 +1168,10 @@ where let (_, blob_gas_used) = ctx.blob_fields(info); // Prepare the flashblocks message - let fb_payload = FlashblocksPayloadV1 { + let fb_payload = OpFlashblockPayload { payload_id: ctx.payload_id(), index: 0, - base: Some(ExecutionPayloadBaseV1 { + base: Some(OpFlashblockPayloadBase { parent_beacon_block_root: ctx .attributes() .payload_attributes @@ -1172,9 +1184,9 @@ where gas_limit: ctx.block_gas_limit(), timestamp: ctx.attributes().payload_attributes.timestamp, extra_data: ctx.extra_data()?, - base_fee_per_gas: ctx.base_fee().try_into().unwrap(), + base_fee_per_gas: U256::from(ctx.base_fee()), }), - diff: ExecutionPayloadFlashblockDeltaV1 { + diff: OpFlashblockPayloadDelta { state_root, receipts_root, logs_bloom, @@ -1185,7 +1197,7 @@ where withdrawals_root: withdrawals_root.unwrap_or_default(), blob_gas_used, }, - metadata: serde_json::to_value(&metadata).unwrap_or_default(), + metadata, }; // We clean bundle and place initial state transaction back diff --git a/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs b/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs index 96b6f683a..8613abc86 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/payload_handler.rs @@ -9,6 +9,7 @@ use alloy_evm::eth::receipt_builder::ReceiptBuilderCtx; use alloy_primitives::B64; use eyre::{WrapErr as _, bail}; use op_alloy_consensus::OpTxEnvelope; +use op_alloy_rpc_types_engine::OpFlashblockPayload; use reth::revm::{State, database::StateProviderDatabase}; use reth_basic_payload_builder::PayloadConfig; use reth_evm::FromRecoveredTx; @@ -19,7 +20,6 @@ use reth_optimism_node::{OpEngineTypes, OpPayloadBuilderAttributes}; use reth_optimism_payload_builder::OpBuiltPayload; use reth_optimism_primitives::{OpReceipt, OpTransactionSigned}; use reth_payload_builder::EthPayloadBuilderAttributes; -use rollup_boost::FlashblocksPayloadV1; use std::sync::Arc; use tokio::sync::mpsc; use tracing::warn; @@ -135,7 +135,7 @@ fn execute_flashblock( ctx: OpPayloadSyncerCtx, client: Client, cancel: tokio_util::sync::CancellationToken, -) -> eyre::Result<(OpBuiltPayload, FlashblocksPayloadV1)> +) -> eyre::Result<(OpBuiltPayload, OpFlashblockPayload)> where Client: ClientBounds, { diff --git a/crates/op-rbuilder/src/builders/flashblocks/wspub.rs b/crates/op-rbuilder/src/builders/flashblocks/wspub.rs index e2f003dc1..acdbf0c28 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/wspub.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/wspub.rs @@ -5,7 +5,7 @@ use core::{ }; use futures::SinkExt; use futures_util::StreamExt; -use rollup_boost::FlashblocksPayloadV1; +use op_alloy_rpc_types_engine::OpFlashblockPayload; use std::{io, net::TcpListener, sync::Arc}; use tokio::{ net::TcpStream, @@ -25,7 +25,7 @@ use crate::metrics::OpRBuilderMetrics; /// A WebSockets publisher that accepts connections from client websockets and broadcasts to them /// updates about new flashblocks. It maintains a count of sent messages and active subscriptions. /// -/// This is modelled as a `futures::Sink` that can be used to send `FlashblocksPayloadV1` messages. +/// This is modelled as a `futures::Sink` that can be used to send `OpFlashblockPayload` messages. pub(super) struct WebSocketPublisher { sent: Arc, subs: Arc, @@ -59,7 +59,7 @@ impl WebSocketPublisher { }) } - pub(super) fn publish(&self, payload: &FlashblocksPayloadV1) -> io::Result { + pub(super) fn publish(&self, payload: &OpFlashblockPayload) -> io::Result { // Serialize the payload to a UTF-8 string // serialize only once, then just copy around only a pointer // to the serialized data for each subscription. From 7749480e680a8da13dfa8e45c3f39ab9c9add0e4 Mon Sep 17 00:00:00 2001 From: noot <36753753+noot@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:11:22 -0800 Subject: [PATCH 2/6] deps: use op-alloy types instead of rollup-boost (#344) * use rollup-boost-core crate * use rollup-boost-types * Update Cargo.toml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * use op-alloy types, remove rollup-boost entirely * cleanup * cleanup --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Cargo.lock | 641 +++--------------- Cargo.toml | 3 - crates/op-rbuilder/Cargo.toml | 4 +- .../op-rbuilder/src/tests/framework/driver.rs | 10 +- .../src/tests/framework/instance.rs | 35 +- 5 files changed, 101 insertions(+), 592 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 31d6372b6..a19b21c1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,7 +576,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", "wasmtimer", ] @@ -623,7 +623,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", "url", "wasmtimer", @@ -999,7 +999,7 @@ dependencies = [ "serde_json", "thiserror 2.0.17", "tokio", - "tower 0.5.2", + "tower", "tracing", "url", "wasmtimer", @@ -1015,7 +1015,7 @@ dependencies = [ "alloy-transport", "reqwest", "serde_json", - "tower 0.5.2", + "tower", "tracing", "url", ] @@ -1734,40 +1734,13 @@ dependencies = [ "fs_extra", ] -[[package]] -name = "axum" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" -dependencies = [ - "async-trait", - "axum-core 0.4.5", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "itoa", - "matchit 0.7.3", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper", - "tower 0.5.2", - "tower-layer", - "tower-service", -] - [[package]] name = "axum" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425" dependencies = [ - "axum-core 0.5.5", + "axum-core", "bytes", "form_urlencoded", "futures-util", @@ -1777,7 +1750,7 @@ dependencies = [ "hyper", "hyper-util", "itoa", - "matchit 0.8.4", + "matchit", "memchr", "mime", "percent-encoding", @@ -1788,32 +1761,12 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", ] -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", -] - [[package]] name = "axum-core" version = "0.5.5" @@ -1839,17 +1792,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" -[[package]] -name = "backoff" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" -dependencies = [ - "getrandom 0.2.16", - "instant", - "rand 0.8.5", -] - [[package]] name = "backon" version = "1.6.0" @@ -3705,12 +3647,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - [[package]] name = "enr" version = "0.13.0" @@ -5134,15 +5070,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "interprocess" version = "2.2.3" @@ -5291,20 +5218,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jsonrpsee" -version = "0.25.1" -source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" -dependencies = [ - "jsonrpsee-core 0.25.1", - "jsonrpsee-http-client 0.25.1", - "jsonrpsee-proc-macros 0.25.1", - "jsonrpsee-server 0.25.1", - "jsonrpsee-types 0.25.1", - "tokio", - "tracing", -] - [[package]] name = "jsonrpsee" version = "0.26.0" @@ -5312,11 +5225,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f3f48dc3e6b8bd21e15436c1ddd0bc22a6a54e8ec46fedd6adf3425f396ec6a" dependencies = [ "jsonrpsee-client-transport", - "jsonrpsee-core 0.26.0", - "jsonrpsee-http-client 0.26.0", - "jsonrpsee-proc-macros 0.26.0", - "jsonrpsee-server 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", "jsonrpsee-wasm-client", "jsonrpsee-ws-client", "tokio", @@ -5334,7 +5247,7 @@ dependencies = [ "futures-util", "gloo-net", "http", - "jsonrpsee-core 0.26.0", + "jsonrpsee-core", "pin-project", "rustls", "rustls-pki-types", @@ -5348,30 +5261,6 @@ dependencies = [ "url", ] -[[package]] -name = "jsonrpsee-core" -version = "0.25.1" -source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "jsonrpsee-types 0.25.1", - "parking_lot", - "pin-project", - "rand 0.9.2", - "rustc-hash 2.1.1", - "serde", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tower 0.5.2", - "tracing", -] - [[package]] name = "jsonrpsee-core" version = "0.26.0" @@ -5385,7 +5274,7 @@ dependencies = [ "http", "http-body", "http-body-util", - "jsonrpsee-types 0.26.0", + "jsonrpsee-types", "parking_lot", "pin-project", "rand 0.9.2", @@ -5395,33 +5284,11 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", "wasm-bindgen-futures", ] -[[package]] -name = "jsonrpsee-http-client" -version = "0.25.1" -source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" -dependencies = [ - "base64 0.22.1", - "http-body", - "hyper", - "hyper-rustls", - "hyper-util", - "jsonrpsee-core 0.25.1", - "jsonrpsee-types 0.25.1", - "rustls", - "rustls-platform-verifier", - "serde", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tower 0.5.2", - "url", -] - [[package]] name = "jsonrpsee-http-client" version = "0.26.0" @@ -5433,30 +5300,18 @@ dependencies = [ "hyper", "hyper-rustls", "hyper-util", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee-core", + "jsonrpsee-types", "rustls", "rustls-platform-verifier", "serde", "serde_json", "thiserror 2.0.17", "tokio", - "tower 0.5.2", + "tower", "url", ] -[[package]] -name = "jsonrpsee-proc-macros" -version = "0.25.1" -source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" -dependencies = [ - "heck", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "jsonrpsee-proc-macros" version = "0.26.0" @@ -5470,32 +5325,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "jsonrpsee-server" -version = "0.25.1" -source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" -dependencies = [ - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "jsonrpsee-core 0.25.1", - "jsonrpsee-types 0.25.1", - "pin-project", - "route-recognizer", - "serde", - "serde_json", - "soketto", - "thiserror 2.0.17", - "tokio", - "tokio-stream", - "tokio-util", - "tower 0.5.2", - "tracing", -] - [[package]] name = "jsonrpsee-server" version = "0.26.0" @@ -5508,8 +5337,8 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee-core", + "jsonrpsee-types", "pin-project", "route-recognizer", "serde", @@ -5519,21 +5348,10 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", - "tower 0.5.2", + "tower", "tracing", ] -[[package]] -name = "jsonrpsee-types" -version = "0.25.1" -source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" -dependencies = [ - "http", - "serde", - "serde_json", - "thiserror 2.0.17", -] - [[package]] name = "jsonrpsee-types" version = "0.26.0" @@ -5553,9 +5371,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7902885de4779f711a95d82c8da2d7e5f9f3a7c7cfa44d51c067fd1c29d72a3c" dependencies = [ "jsonrpsee-client-transport", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", - "tower 0.5.2", + "jsonrpsee-core", + "jsonrpsee-types", + "tower", ] [[package]] @@ -5566,9 +5384,9 @@ checksum = "9b6fceceeb05301cc4c065ab3bd2fa990d41ff4eb44e4ca1b30fa99c057c3e79" dependencies = [ "http", "jsonrpsee-client-transport", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", - "tower 0.5.2", + "jsonrpsee-core", + "jsonrpsee-types", + "tower", "url", ] @@ -6199,15 +6017,6 @@ dependencies = [ "hashbrown 0.15.5", ] -[[package]] -name = "lru" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" -dependencies = [ - "hashbrown 0.16.1", -] - [[package]] name = "lru-slab" version = "0.1.2" @@ -6289,12 +6098,6 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - [[package]] name = "matchit" version = "0.8.4" @@ -6364,18 +6167,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd7399781913e5393588a8d8c6a2867bf85fb38eaf2502fdce465aad2dc6f034" dependencies = [ "base64 0.22.1", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", "indexmap 2.12.1", - "ipnet", "metrics", "metrics-util 0.19.1", "quanta", "thiserror 1.0.69", - "tokio", - "tracing", ] [[package]] @@ -6421,15 +6217,11 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8496cc523d1f94c1385dd8f0f0c2c480b2b8aeccb5b7e4485ad6365523ae376" dependencies = [ - "aho-corasick", "crossbeam-epoch", "crossbeam-utils", "hashbrown 0.15.5", - "indexmap 2.12.1", "metrics", - "ordered-float", "quanta", - "radix_trie", "rand 0.9.2", "rand_xoshiro", "sketches-ddsketch", @@ -6703,15 +6495,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - [[package]] name = "nix" version = "0.26.4" @@ -7040,7 +6823,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ef9114426b16172254555aad34a8ea96c01895e40da92f5d12ea680a1baeaa7" dependencies = [ "alloy-primitives 1.4.1", - "jsonrpsee 0.26.0", + "jsonrpsee", ] [[package]] @@ -7123,9 +6906,9 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "jsonrpsee 0.26.0", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee", + "jsonrpsee-core", + "jsonrpsee-types", "k256", "macros", "metrics", @@ -7137,7 +6920,7 @@ dependencies = [ "op-alloy-rpc-types", "op-alloy-rpc-types-engine", "op-revm", - "opentelemetry 0.31.0", + "opentelemetry", "p2p", "parking_lot", "rand 0.9.2", @@ -7190,7 +6973,6 @@ dependencies = [ "reth-trie", "revm", "rlimit", - "rollup-boost", "secp256k1 0.30.0", "serde", "serde_json", @@ -7207,7 +6989,7 @@ dependencies = [ "tokio", "tokio-tungstenite", "tokio-util", - "tower 0.5.2", + "tower", "tracing", "tracing-subscriber 0.3.20", "url", @@ -7277,20 +7059,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "opentelemetry" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "236e667b670a5cdf90c258f5a55794ec5ac5027e960c224bff8367a59e1e6426" -dependencies = [ - "futures-core", - "futures-sink", - "js-sys", - "pin-project-lite", - "thiserror 2.0.17", - "tracing", -] - [[package]] name = "opentelemetry" version = "0.31.0" @@ -7305,20 +7073,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "opentelemetry-http" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8863faf2910030d139fb48715ad5ff2f35029fc5f244f6d5f689ddcf4d26253" -dependencies = [ - "async-trait", - "bytes", - "http", - "opentelemetry 0.28.0", - "reqwest", - "tracing", -] - [[package]] name = "opentelemetry-http" version = "0.31.0" @@ -7328,30 +7082,8 @@ dependencies = [ "async-trait", "bytes", "http", - "opentelemetry 0.31.0", - "reqwest", -] - -[[package]] -name = "opentelemetry-otlp" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bef114c6d41bea83d6dc60eb41720eedd0261a67af57b66dd2b84ac46c01d91" -dependencies = [ - "async-trait", - "futures-core", - "http", - "opentelemetry 0.28.0", - "opentelemetry-http 0.28.0", - "opentelemetry-proto 0.28.0", - "opentelemetry_sdk 0.28.0", - "prost 0.13.5", + "opentelemetry", "reqwest", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tonic 0.12.3", - "tracing", ] [[package]] @@ -7361,43 +7093,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2366db2dca4d2ad033cad11e6ee42844fd727007af5ad04a1730f4cb8163bf" dependencies = [ "http", - "opentelemetry 0.31.0", - "opentelemetry-http 0.31.0", - "opentelemetry-proto 0.31.0", - "opentelemetry_sdk 0.31.0", - "prost 0.14.1", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", "reqwest", "thiserror 2.0.17", "tokio", - "tonic 0.14.2", + "tonic", "tracing", ] -[[package]] -name = "opentelemetry-proto" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f8870d3024727e99212eb3bb1762ec16e255e3e6f58eeb3dc8db1aa226746d" -dependencies = [ - "base64 0.22.1", - "hex", - "opentelemetry 0.28.0", - "opentelemetry_sdk 0.28.0", - "prost 0.13.5", - "serde", - "tonic 0.12.3", -] - [[package]] name = "opentelemetry-proto" version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" dependencies = [ - "opentelemetry 0.31.0", - "opentelemetry_sdk 0.31.0", - "prost 0.14.1", - "tonic 0.14.2", + "opentelemetry", + "opentelemetry_sdk", + "prost", + "tonic", "tonic-prost", ] @@ -7407,27 +7124,6 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e62e29dfe041afb8ed2a6c9737ab57db4907285d999ef8ad3a59092a36bdc846" -[[package]] -name = "opentelemetry_sdk" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84dfad6042089c7fc1f6118b7040dc2eb4ab520abbf410b79dc481032af39570" -dependencies = [ - "async-trait", - "futures-channel", - "futures-executor", - "futures-util", - "glob", - "opentelemetry 0.28.0", - "percent-encoding", - "rand 0.8.5", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tokio-stream", - "tracing", -] - [[package]] name = "opentelemetry_sdk" version = "0.31.0" @@ -7437,7 +7133,7 @@ dependencies = [ "futures-channel", "futures-executor", "futures-util", - "opentelemetry 0.31.0", + "opentelemetry", "percent-encoding", "rand 0.9.2", "thiserror 2.0.17", @@ -7449,15 +7145,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "ordered-float" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" -dependencies = [ - "num-traits", -] - [[package]] name = "p256" version = "0.13.2" @@ -8046,16 +7733,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "prost" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" -dependencies = [ - "bytes", - "prost-derive 0.13.5", -] - [[package]] name = "prost" version = "0.14.1" @@ -8063,20 +7740,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" dependencies = [ "bytes", - "prost-derive 0.14.1", -] - -[[package]] -name = "prost-derive" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn 2.0.111", + "prost-derive", ] [[package]] @@ -8223,16 +7887,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - [[package]] name = "rand" version = "0.8.5" @@ -8519,7 +8173,7 @@ dependencies = [ "tokio-native-tls", "tokio-rustls", "tokio-util", - "tower 0.5.2", + "tower", "tower-http", "tower-service", "url", @@ -9652,7 +9306,7 @@ dependencies = [ "alloy-rpc-types-debug", "eyre", "futures", - "jsonrpsee 0.26.0", + "jsonrpsee", "pretty_assertions", "reth-engine-primitives", "reth-evm", @@ -9678,14 +9332,14 @@ dependencies = [ "futures", "futures-util", "interprocess", - "jsonrpsee 0.26.0", + "jsonrpsee", "pin-project", "serde_json", "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", - "tower 0.5.2", + "tower", "tracing", ] @@ -9936,7 +9590,7 @@ dependencies = [ "eyre", "fdlimit", "futures", - "jsonrpsee 0.26.0", + "jsonrpsee", "rayon", "reth-basic-payload-builder", "reth-chain-state", @@ -10135,7 +9789,7 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea8 dependencies = [ "eyre", "http", - "jsonrpsee-server 0.26.0", + "jsonrpsee-server", "metrics", "metrics-exporter-prometheus 0.16.2", "metrics-process", @@ -10146,7 +9800,7 @@ dependencies = [ "reth-tasks", "tikv-jemalloc-ctl", "tokio", - "tower 0.5.2", + "tower", "tracing", ] @@ -10468,9 +10122,9 @@ dependencies = [ "derive_more", "eyre", "futures", - "jsonrpsee 0.26.0", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee", + "jsonrpsee-core", + "jsonrpsee-types", "metrics", "op-alloy-consensus", "op-alloy-network", @@ -10506,7 +10160,7 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", ] @@ -10855,8 +10509,8 @@ dependencies = [ "http-body", "hyper", "itertools 0.14.0", - "jsonrpsee 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee", + "jsonrpsee-types", "jsonwebtoken", "parking_lot", "pin-project", @@ -10895,7 +10549,7 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", "tracing-futures", ] @@ -10920,7 +10574,7 @@ dependencies = [ "alloy-rpc-types-trace", "alloy-rpc-types-txpool", "alloy-serde", - "jsonrpsee 0.26.0", + "jsonrpsee", "reth-chain-state", "reth-engine-primitives", "reth-network-peers", @@ -10937,7 +10591,7 @@ dependencies = [ "alloy-provider", "dyn-clone", "http", - "jsonrpsee 0.26.0", + "jsonrpsee", "metrics", "pin-project", "reth-chain-state", @@ -10962,7 +10616,7 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tokio-util", - "tower 0.5.2", + "tower", "tower-http", "tracing", ] @@ -10980,7 +10634,7 @@ dependencies = [ "alloy-signer", "auto_impl", "dyn-clone", - "jsonrpsee-types 0.26.0", + "jsonrpsee-types", "op-alloy-consensus", "op-alloy-network", "op-alloy-rpc-types", @@ -11003,8 +10657,8 @@ dependencies = [ "alloy-primitives 1.4.1", "alloy-rpc-types-engine", "async-trait", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee-core", + "jsonrpsee-types", "metrics", "parking_lot", "reth-chainspec", @@ -11044,8 +10698,8 @@ dependencies = [ "auto_impl", "dyn-clone", "futures", - "jsonrpsee 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee", + "jsonrpsee-types", "parking_lot", "reth-chain-state", "reth-chainspec", @@ -11085,8 +10739,8 @@ dependencies = [ "derive_more", "futures", "itertools 0.14.0", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee-core", + "jsonrpsee-types", "metrics", "rand 0.9.2", "reqwest", @@ -11122,9 +10776,9 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea8 dependencies = [ "alloy-rpc-types-engine", "http", - "jsonrpsee-http-client 0.26.0", + "jsonrpsee-http-client", "pin-project", - "tower 0.5.2", + "tower", "tower-http", "tracing", ] @@ -11137,8 +10791,8 @@ dependencies = [ "alloy-eips", "alloy-primitives 1.4.1", "alloy-rpc-types-engine", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee-core", + "jsonrpsee-types", "reth-errors", "reth-network-api", "serde", @@ -11373,12 +11027,12 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea8 dependencies = [ "clap", "eyre", - "opentelemetry 0.31.0", - "opentelemetry-otlp 0.31.0", + "opentelemetry", + "opentelemetry-otlp", "opentelemetry-semantic-conventions", - "opentelemetry_sdk 0.31.0", + "opentelemetry_sdk", "tracing", - "tracing-opentelemetry 0.32.0", + "tracing-opentelemetry", "tracing-subscriber 0.3.20", "url", ] @@ -11868,60 +11522,6 @@ dependencies = [ "chrono", ] -[[package]] -name = "rollup-boost" -version = "0.1.0" -source = "git+https://github.com/flashbots/rollup-boost?tag=v0.7.11#196237bab2a02298de994b439e0455abb1ac512f" -dependencies = [ - "alloy-primitives 1.4.1", - "alloy-rpc-types-engine", - "alloy-rpc-types-eth", - "alloy-serde", - "backoff", - "bytes", - "clap", - "dashmap 6.1.0", - "dotenvy", - "eyre", - "futures", - "http", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "jsonrpsee 0.25.1", - "lru 0.16.2", - "metrics", - "metrics-derive", - "metrics-exporter-prometheus 0.16.2", - "metrics-util 0.19.1", - "moka", - "op-alloy-rpc-types-engine", - "opentelemetry 0.28.0", - "opentelemetry-otlp 0.28.0", - "opentelemetry_sdk 0.28.0", - "parking_lot", - "paste", - "reth-optimism-payload-builder", - "rustls", - "serde", - "serde_json", - "sha2", - "thiserror 2.0.17", - "tokio", - "tokio-tungstenite", - "tokio-util", - "tower 0.5.2", - "tower-http", - "tracing", - "tracing-opentelemetry 0.29.0", - "tracing-subscriber 0.3.20", - "url", - "uuid", - "vergen", - "vergen-git2", -] - [[package]] name = "route-recognizer" version = "0.3.1" @@ -13128,7 +12728,7 @@ dependencies = [ name = "tdx-quote-provider" version = "0.1.0" dependencies = [ - "axum 0.8.7", + "axum", "clap", "dotenvy", "eyre", @@ -13435,12 +13035,10 @@ checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" dependencies = [ "futures-util", "log", - "native-tls", "rustls", "rustls-native-certs", "rustls-pki-types", "tokio", - "tokio-native-tls", "tokio-rustls", "tungstenite", "webpki-roots 0.26.11", @@ -13553,36 +13151,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" -[[package]] -name = "tonic" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" -dependencies = [ - "async-stream", - "async-trait", - "axum 0.7.9", - "base64 0.22.1", - "bytes", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "prost 0.13.5", - "socket2 0.5.10", - "tokio", - "tokio-stream", - "tower 0.4.13", - "tower-layer", - "tower-service", - "tracing", -] - [[package]] name = "tonic" version = "0.14.2" @@ -13603,7 +13171,7 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", @@ -13616,28 +13184,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" dependencies = [ "bytes", - "prost 0.14.1", - "tonic 0.14.2", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand 0.8.5", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", + "prost", + "tonic", ] [[package]] @@ -13684,7 +13232,7 @@ dependencies = [ "pin-project-lite", "tokio", "tokio-util", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", @@ -13792,24 +13340,6 @@ dependencies = [ "tracing-subscriber 0.3.20", ] -[[package]] -name = "tracing-opentelemetry" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721f2d2569dce9f3dfbbddee5906941e953bfcdf736a62da3377f5751650cc36" -dependencies = [ - "js-sys", - "once_cell", - "opentelemetry 0.28.0", - "opentelemetry_sdk 0.28.0", - "smallvec", - "tracing", - "tracing-core", - "tracing-log", - "tracing-subscriber 0.3.20", - "web-time", -] - [[package]] name = "tracing-opentelemetry" version = "0.32.0" @@ -13817,8 +13347,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e6e5658463dd88089aba75c7791e1d3120633b1bfde22478b28f625a9bb1b8e" dependencies = [ "js-sys", - "opentelemetry 0.31.0", - "opentelemetry_sdk 0.31.0", + "opentelemetry", + "opentelemetry_sdk", "rustversion", "smallvec", "thiserror 2.0.17", @@ -13960,7 +13490,6 @@ dependencies = [ "http", "httparse", "log", - "native-tls", "rand 0.9.2", "rustls", "rustls-pki-types", diff --git a/Cargo.toml b/Cargo.toml index ac4f60fc2..529b1d90b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -161,9 +161,6 @@ alloy-trie = { version = "0.9.1" } alloy-hardforks = "0.4.4" -# rollup-boost -rollup-boost = { git = "https://github.com/flashbots/rollup-boost", tag = "v0.7.11" } - # optimism alloy-op-evm = { version = "0.23.0", default-features = false } alloy-op-hardforks = "0.4.4" diff --git a/crates/op-rbuilder/Cargo.toml b/crates/op-rbuilder/Cargo.toml index c312f6431..6008f33f1 100644 --- a/crates/op-rbuilder/Cargo.toml +++ b/crates/op-rbuilder/Cargo.toml @@ -125,14 +125,12 @@ rand = "0.9.0" tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json"] } shellexpand = "3.1" serde_yaml = { version = "0.9" } -moka = "0.12" +moka = { version = "0.12", features = ["future"] } http = "1.0" sha3 = "0.10" reqwest = "0.12.23" k256 = "0.13.4" -rollup-boost.workspace = true - nanoid = { version = "0.4", optional = true } reth-ipc = { workspace = true, optional = true } tar = { version = "0.4", optional = true } diff --git a/crates/op-rbuilder/src/tests/framework/driver.rs b/crates/op-rbuilder/src/tests/framework/driver.rs index 5246bd5e8..a9c0a8ca0 100644 --- a/crates/op-rbuilder/src/tests/framework/driver.rs +++ b/crates/op-rbuilder/src/tests/framework/driver.rs @@ -9,7 +9,6 @@ use op_alloy_consensus::{OpTypedTransaction, TxDeposit}; use op_alloy_network::Optimism; use op_alloy_rpc_types::Transaction; use reth_optimism_node::OpPayloadAttributes; -use rollup_boost::OpExecutionPayloadEnvelope; use super::{EngineApi, Ipc, LocalInstance, TransactionBuilder}; use crate::{ @@ -219,13 +218,8 @@ impl ChainDriver { .await; } - let payload = - OpExecutionPayloadEnvelope::V4(self.engine_api.get_payload(payload_id).await?); - - let OpExecutionPayloadEnvelope::V4(payload) = payload else { - return Err(eyre::eyre!("Expected V4 payload, got something else")); - }; - + let payload: op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV4 = + self.engine_api.get_payload(payload_id).await?; let payload = payload.execution_payload; if self diff --git a/crates/op-rbuilder/src/tests/framework/instance.rs b/crates/op-rbuilder/src/tests/framework/instance.rs index 8c7184917..6250d76a3 100644 --- a/crates/op-rbuilder/src/tests/framework/instance.rs +++ b/crates/op-rbuilder/src/tests/framework/instance.rs @@ -29,6 +29,7 @@ use hyper_util::rt::TokioIo; use moka::future::Cache; use nanoid::nanoid; use op_alloy_network::Optimism; +use op_alloy_rpc_types_engine::OpFlashblockPayload; use parking_lot::Mutex; use reth::{ args::{DatadirArgs, NetworkArgs, RpcServerArgs}, @@ -44,7 +45,6 @@ use reth_optimism_node::{ }; use reth_optimism_rpc::OpEthApiBuilder; use reth_transaction_pool::{AllTransactionsEvents, TransactionPool}; -use rollup_boost::FlashblocksPayloadV1; use std::{ net::SocketAddr, sync::{Arc, LazyLock}, @@ -384,7 +384,7 @@ async fn spawn_attestation_provider() -> eyre::Result { /// This provides a reusable way to capture and inspect flashblocks that are produced /// during test execution, eliminating the need for duplicate WebSocket listening code. pub struct FlashblocksListener { - pub flashblocks: Arc>>, + pub flashblocks: Arc>>, pub cancellation_token: CancellationToken, pub handle: JoinHandle>, } @@ -392,7 +392,7 @@ pub struct FlashblocksListener { impl FlashblocksListener { /// Create a new flashblocks listener that connects to the given WebSocket URL. /// - /// The listener will automatically parse incoming messages as FlashblocksPayloadV1. + /// The listener will automatically parse incoming messages as OpFlashblockPayload. fn new(flashblocks_ws_url: String) -> Self { let flashblocks = Arc::new(Mutex::new(Vec::new())); let cancellation_token = CancellationToken::new(); @@ -425,12 +425,12 @@ impl FlashblocksListener { } /// Get a snapshot of all received flashblocks - pub fn get_flashblocks(&self) -> Vec { + pub fn get_flashblocks(&self) -> Vec { self.flashblocks.lock().clone() } /// Find a flashblock by index - pub fn find_flashblock(&self, index: u64) -> Option { + pub fn find_flashblock(&self, index: u64) -> Option { self.flashblocks .lock() .iter() @@ -440,28 +440,19 @@ impl FlashblocksListener { /// Check if any flashblock contains the given transaction hash pub fn contains_transaction(&self, tx_hash: &B256) -> bool { - let tx_hash_str = format!("{tx_hash:#x}"); - self.flashblocks.lock().iter().any(|fb| { - if let Some(receipts) = fb.metadata.get("receipts") - && let Some(receipts_obj) = receipts.as_object() - { - return receipts_obj.contains_key(&tx_hash_str); - } - false - }) + self.flashblocks + .lock() + .iter() + .any(|fb| fb.metadata.receipts.contains_key(tx_hash)) } /// Find which flashblock index contains the given transaction hash pub fn find_transaction_flashblock(&self, tx_hash: &B256) -> Option { - let tx_hash_str = format!("{tx_hash:#x}"); self.flashblocks.lock().iter().find_map(|fb| { - if let Some(receipts) = fb.metadata.get("receipts") - && let Some(receipts_obj) = receipts.as_object() - && receipts_obj.contains_key(&tx_hash_str) - { - return Some(fb.index); - } - None + fb.metadata + .receipts + .contains_key(tx_hash) + .then_some(fb.index) }) } From 73053fd499fdb737afa616da1b3ef55c9d2023c8 Mon Sep 17 00:00:00 2001 From: shana Date: Tue, 6 Jan 2026 19:51:52 -0800 Subject: [PATCH 3/6] Add flashblock timing configuration options (#348) * fix flashblocks timing and tests * formatting --- crates/op-rbuilder/src/args/op.rs | 31 ++- .../src/builders/flashblocks/config.rs | 37 ++- .../src/builders/flashblocks/payload.rs | 254 ++++++++++++++++-- .../src/builders/flashblocks/service.rs | 4 +- .../src/flashtestations/tx_manager.rs | 2 +- crates/op-rbuilder/src/tests/flashblocks.rs | 233 ++++++++++++++++ .../src/tests/framework/instance.rs | 52 +++- 7 files changed, 569 insertions(+), 44 deletions(-) diff --git a/crates/op-rbuilder/src/args/op.rs b/crates/op-rbuilder/src/args/op.rs index 4e3d0308e..73bf00625 100644 --- a/crates/op-rbuilder/src/args/op.rs +++ b/crates/op-rbuilder/src/args/op.rs @@ -144,7 +144,7 @@ pub struct FlashblocksArgs { /// building time before calculating number of fbs. #[arg( long = "flashblocks.leeway-time", - default_value = "75", + default_value = "0", env = "FLASHBLOCK_LEEWAY_TIME" )] pub flashblocks_leeway_time: u64, @@ -176,6 +176,35 @@ pub struct FlashblocksArgs { )] pub flashblocks_number_contract_use_permit: bool, + /// Build flashblock at the end of the flashblock interval + #[arg( + long = "flashblocks.build-at-interval-end", + env = "FLASHBLOCK_BUILD_AT_INTERVAL_END", + default_value = "false" + )] + pub flashblocks_build_at_interval_end: bool, + + /// Offset in milliseconds for when to send flashblocks. + /// Positive values send late, negative values send early. + /// Example: -20 sends 20ms early, 20 sends 20ms late. + #[arg( + long = "flashblocks.send-offset-ms", + env = "FLASHBLOCK_SEND_OFFSET_MS", + default_value = "0", + allow_hyphen_values = true + )] + pub flashblocks_send_offset_ms: i64, + + /// Time in milliseconds to build the last flashblock early before the end of the slot + /// This serves as a buffer time to account for the last flashblock being delayed + /// at the end of the slot due to processing the final block + #[arg( + long = "flashblocks.end-buffer-ms", + env = "FLASHBLOCK_END_BUFFER_MS", + default_value = "0" + )] + pub flashblocks_end_buffer_ms: u64, + /// Flashblocks p2p configuration #[command(flatten)] pub p2p: FlashblocksP2pArgs, diff --git a/crates/op-rbuilder/src/builders/flashblocks/config.rs b/crates/op-rbuilder/src/builders/flashblocks/config.rs index cdc6ae915..364c68115 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/config.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/config.rs @@ -37,10 +37,21 @@ pub struct FlashblocksConfig { /// The address of the flashblocks number contract. /// /// If set a builder tx will be added to the start of every flashblock instead of the regular builder tx. - pub flashblocks_number_contract_address: Option

, + pub number_contract_address: Option
, /// whether to use permit signatures for the contract calls - pub flashblocks_number_contract_use_permit: bool, + pub number_contract_use_permit: bool, + + /// Build flashblock at the end of the flashblock interval + pub build_at_interval_end: bool, + + /// Offset in milliseconds for when to send flashblocks. + /// Positive values send late, negative values send early. + pub send_offset_ms: i64, + + /// Time in milliseconds to build the last flashblock early before the end of the slot. + /// This serves as a buffer time to account for the last flashblock being delayed. + pub end_buffer_ms: u64, /// Whether to enable the p2p node for flashblocks pub p2p_enabled: bool, @@ -63,11 +74,14 @@ impl Default for FlashblocksConfig { Self { ws_addr: SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 1111), interval: Duration::from_millis(250), - leeway_time: Duration::from_millis(50), + leeway_time: Duration::from_millis(0), fixed: false, disable_state_root: false, - flashblocks_number_contract_address: None, - flashblocks_number_contract_use_permit: false, + number_contract_address: None, + number_contract_use_permit: false, + build_at_interval_end: false, + send_offset_ms: 0, + end_buffer_ms: 0, p2p_enabled: false, p2p_port: 9009, p2p_private_key_file: None, @@ -94,11 +108,9 @@ impl TryFrom for FlashblocksConfig { let disable_state_root = args.flashblocks.flashblocks_disable_state_root; - let flashblocks_number_contract_address = - args.flashblocks.flashblocks_number_contract_address; + let number_contract_address = args.flashblocks.flashblocks_number_contract_address; - let flashblocks_number_contract_use_permit = - args.flashblocks.flashblocks_number_contract_use_permit; + let number_contract_use_permit = args.flashblocks.flashblocks_number_contract_use_permit; Ok(Self { ws_addr, @@ -106,8 +118,11 @@ impl TryFrom for FlashblocksConfig { leeway_time, fixed, disable_state_root, - flashblocks_number_contract_address, - flashblocks_number_contract_use_permit, + number_contract_address, + number_contract_use_permit, + build_at_interval_end: args.flashblocks.flashblocks_build_at_interval_end, + send_offset_ms: args.flashblocks.flashblocks_send_offset_ms, + end_buffer_ms: args.flashblocks.flashblocks_end_buffer_ms, p2p_enabled: args.flashblocks.p2p.p2p_enabled, p2p_port: args.flashblocks.p2p.p2p_port, p2p_private_key_file: args.flashblocks.p2p.p2p_private_key_file, diff --git a/crates/op-rbuilder/src/builders/flashblocks/payload.rs b/crates/op-rbuilder/src/builders/flashblocks/payload.rs index 3c4fbcce0..992398bcc 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/payload.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/payload.rs @@ -87,6 +87,17 @@ type NextBestFlashblocksTxs = BestFlashblocksTxs< >, >; +/// Timing information for flashblock building +#[derive(Debug, Clone, Copy)] +pub(super) struct FlashblocksTiming { + /// Number of flashblocks to build in this block + pub flashblocks_per_block: u64, + /// Time until the first flashblock should be built + pub first_flashblock_offset: Duration, + /// Total time available for flashblock building (deadline) + pub flashblocks_deadline: Duration, +} + #[derive(Debug, Default, Clone)] pub(super) struct FlashblocksExecutionInfo { /// Index of the last consumed flashblock @@ -437,9 +448,25 @@ where // return early since we don't need to build a block with transactions from the pool return Ok(()); } - // We adjust our flashblocks timings based on time_drift if dynamic adjustment enable - let (flashblocks_per_block, first_flashblock_offset) = - self.calculate_flashblocks(timestamp); + // We adjust our flashblocks timings based on time the fcu block building signal arrived + let (flashblocks_per_block, first_flashblock_offset, flashblocks_deadline) = + if self.config.specific.build_at_interval_end { + let timing = self.calculate_flashblocks_timing(timestamp); + ( + timing.flashblocks_per_block, + timing.first_flashblock_offset, + timing.flashblocks_deadline, + ) + } else { + let (flashblocks_per_block, first_flashblock_offset) = + self.calculate_flashblocks(timestamp); + ( + flashblocks_per_block, + first_flashblock_offset, + self.config.block_time, + ) + }; + info!( target: "payload_builder", message = "Performed flashblocks timing derivation", @@ -497,11 +524,22 @@ where )); let interval = self.config.specific.interval; let (tx, mut rx) = mpsc::channel((self.config.flashblocks_per_block() + 1) as usize); + let build_at_interval_end = self.config.specific.build_at_interval_end; tokio::spawn({ let block_cancel = block_cancel.clone(); async move { + // If NOT building at interval end, send immediate signal to build first + // flashblock right away (preserves current default behavior). + // Otherwise, wait for first_flashblock_offset before first build. + if !build_at_interval_end && tx.send(fb_cancel.clone()).await.is_err() { + error!( + target: "payload_builder", + "Did not trigger first flashblock build due to payload building error or block building being cancelled"); + return; + } + let mut timer = tokio::time::interval_at( tokio::time::Instant::now() .checked_add(first_flashblock_offset) @@ -509,6 +547,12 @@ where interval, ); + // Set deadline to ensure the last flashblock will be built before the leeway time + let deadline_sleep = async { + tokio::time::sleep(flashblocks_deadline).await; + }; + tokio::pin!(deadline_sleep); + loop { tokio::select! { _ = timer.tick() => { @@ -516,15 +560,30 @@ where fb_cancel.cancel(); fb_cancel = block_cancel.child_token(); // this will tick at first_flashblock_offset, - // starting the second flashblock + // starting the next flashblock if tx.send(fb_cancel.clone()).await.is_err() { // receiver channel was dropped, return. // this will only happen if the `build_payload` function returns, // due to payload building error or the main cancellation token being // cancelled. + error!( + target: "payload_builder", + "Did not trigger next flashblock build due to payload building error or block building being cancelled", + ); return; } } + _ = &mut deadline_sleep => { + // Deadline reached (with leeway applied to end). Cancel current payload building job + fb_cancel.cancel(); + if tx.send(block_cancel.child_token()).await.is_err() { + error!( + target: "payload_builder", + "Did not trigger next flashblock build due to payload building error or block building being cancelled", + ); + } + return; + } _ = block_cancel.cancelled() => { return; } @@ -535,6 +594,25 @@ where // Process flashblocks in a blocking loop loop { + // Wait for signal before building flashblock. + // If build_at_interval_end is false, an immediate signal is sent so we don't wait. + // If build_at_interval_end is true, we wait for the timer tick (first_flashblock_offset). + tokio::select! { + Some(new_fb_cancel) = rx.recv() => { + ctx = ctx.with_cancel(new_fb_cancel); + }, + _ = block_cancel.cancelled() => { + self.record_flashblocks_metrics( + &ctx, + &info, + flashblocks_per_block, + &span, + "Payload building complete, cancelled before first flashblock", + ); + return Ok(()); + } + } + let fb_span = if span.is_none() { tracing::Span::none() } else { @@ -557,7 +635,7 @@ where return Ok(()); } - // build first flashblock immediately + // Build flashblock after receiving signal let next_flashblocks_ctx = match self .build_next_flashblock( &ctx, @@ -594,21 +672,7 @@ where } }; - tokio::select! { - Some(fb_cancel) = rx.recv() => { - ctx = ctx.with_cancel(fb_cancel).with_extra_ctx(next_flashblocks_ctx); - }, - _ = block_cancel.cancelled() => { - self.record_flashblocks_metrics( - &ctx, - &info, - flashblocks_per_block, - &span, - "Payload building complete, channel closed or job cancelled", - ); - return Ok(()); - } - } + ctx = ctx.with_extra_ctx(next_flashblocks_ctx); } } @@ -877,6 +941,7 @@ where /// Calculate number of flashblocks. /// If dynamic is enabled this function will take time drift into the account. + /// TODO: deprecate this flashblocks timing calculation pub(super) fn calculate_flashblocks(&self, timestamp: u64) -> (u64, Duration) { if self.config.specific.fixed { return ( @@ -936,6 +1001,155 @@ where ) } } + + /// Calculate number of flashblocks and time until first flashblock and deadline for building flashblocks + /// If dynamic is enabled this function will take time drift of FCU arrival into the account. + pub(super) fn calculate_flashblocks_timing(&self, timestamp: u64) -> FlashblocksTiming { + let offset_delta = self.config.specific.send_offset_ms.unsigned_abs(); + if self.config.specific.fixed { + let offset = if self.config.specific.send_offset_ms > 0 { + self.config + .specific + .interval + .saturating_add(Duration::from_millis(offset_delta)) + } else { + self.config + .specific + .interval + .saturating_sub(Duration::from_millis(offset_delta)) + }; + return FlashblocksTiming { + flashblocks_per_block: self.config.flashblocks_per_block(), + first_flashblock_offset: offset, + flashblocks_deadline: self + .config + .block_time + .saturating_sub(Duration::from_millis(self.config.specific.end_buffer_ms)), + }; + } + + // FLASHBLOCK TIMING SCENARIOS + // =========================== + + // Block time = 1000ms, Flashblock interval (fb_time) = 250ms + // Target: 4 flashblocks per block + + // Timeline: Block starts at timestamp T, ends at T+1000ms + // |<------------------- block_time (1000ms) ------------------->| + + // SCENARIO 1: IDEAL - FCU arrives on time (delay = 0) + // ───────────────────────────────────────────────────── + // T T+1000ms + // │ │ + // FCU(a) ▼ │ + // arrives ├────────────┬────────────┬────────────┬────────────┤ + // │ FB 1 │ FB 2 │ FB 3 │ FB 4 │ + // │ 250ms │ 250ms │ 250ms │ 250ms │ + // └────────────┴────────────┴────────────┴────────────┘ + + // Result: 4 flashblocks, each 250ms + + // SCENARIO 2: LATE FCU - delay < fb_time (e.g., delay = 100ms) + // ───────────────────────────────────────────────────────────── + // T T+1000ms + // │ │ + // │ delay │ │ + // │◄──100ms──►│ │ + // │ ▼ FCU(a) arrives │ + // ├───────────┼────────┬────────────┬────────────┬──────────┤ + // │ (missed) │ FB 1 │ FB 2 │ FB 3 │ FB 4 │ + // │ │ 150ms │ 250ms │ 250ms │ 250ms │ + // │ │(shrunk)│ │ │ │ + // └───────────┴────────┴────────────┴────────────┴──────────┘ + // │◄─────── remaining time: 900ms ─────────────►│ + + // Result: 4 flashblocks, but FB 1 is shrunk (only 150ms) + // first_flashblock_offset = delay % fb_time = 100 % 250 = 100ms remaining + + // SCENARIO 3: VERY LATE FCU - block_time - fb_time < delay (e.g., delay = 800ms) + // ────────────────────────────────────────────────────────────────────────────── + // T T+1000ms + // │ │ + // │◄─────────────── delay (800ms) ────────────────►│ │ + // │ ▼ FCU(a) │ + // ├─────────────────────────────────────────────────┼────────┤ + // │ (missed - too late) │ FB 1 │ + // │ │ 200ms │ + // │ │ │ + // └─────────────────────────────────────────────────┴────────┘ + // │◄─200ms─►│ + + // Result: Only 1 flashblock possible (200ms remaining < 250ms interval) + let target_time = std::time::SystemTime::UNIX_EPOCH + Duration::from_secs(timestamp); + let now = std::time::SystemTime::now(); + let Ok(remaining_time) = target_time.duration_since(now) else { + error!( + target: "payload_builder", + message = "FCU arrived too late or system clock are unsynced", + ?target_time, + ?now, + ); + return FlashblocksTiming { + flashblocks_per_block: self.config.flashblocks_per_block(), + first_flashblock_offset: self.config.specific.interval, + flashblocks_deadline: self + .config + .block_time + .saturating_sub(Duration::from_millis(self.config.specific.end_buffer_ms)), + }; + }; + self.metrics.flashblocks_time_drift.record( + self.config + .block_time + .as_millis() + .saturating_sub(remaining_time.as_millis()) as f64, + ); + debug!( + target: "payload_builder", + message = "Time delay for building round", + ?target_time, + delay = self.config.block_time.as_millis().saturating_sub(remaining_time.as_millis()), + ?timestamp + ); + // This is extra check to ensure that we would account at least for block time in case we have any timer discrepancies. + let remaining_time = remaining_time.min(self.config.block_time).as_millis() as u64; + let interval = self.config.specific.interval.as_millis() as u64; + let first_flashblock_offset = remaining_time.rem(interval); + let (flashblocks_per_block, offset) = if first_flashblock_offset == 0 { + // We have perfect division, so we use interval as first fb offset + ( + remaining_time.div(interval), + Duration::from_millis(interval), + ) + } else { + // Non-perfect division, set the first flashblock offset to the remainder of the division + ( + remaining_time.div(interval) + 1, + Duration::from_millis(first_flashblock_offset), + ) + }; + // Apply send_offset_ms to the timer start time. + // Positive values = send later, negative values = send earlier. + let deadline = Duration::from_millis( + remaining_time.saturating_sub(self.config.specific.end_buffer_ms), + ); + let (adjusted_offset, adjusted_deadline) = if self.config.specific.send_offset_ms >= 0 { + ( + offset.saturating_add(Duration::from_millis(offset_delta)), + deadline.saturating_add(Duration::from_millis(offset_delta)), + ) + } else { + ( + offset.saturating_sub(Duration::from_millis(offset_delta)), + deadline.saturating_sub(Duration::from_millis(offset_delta)), + ) + }; + FlashblocksTiming { + flashblocks_per_block, + first_flashblock_offset: adjusted_offset, + flashblocks_deadline: adjusted_deadline, + } + } } #[async_trait::async_trait] diff --git a/crates/op-rbuilder/src/builders/flashblocks/service.rs b/crates/op-rbuilder/src/builders/flashblocks/service.rs index 2c1e684bc..b0563421a 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/service.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/service.rs @@ -196,9 +196,9 @@ where if let Some(builder_signer) = signer && let Some(flashblocks_number_contract_address) = - self.0.specific.flashblocks_number_contract_address + self.0.specific.number_contract_address { - let use_permit = self.0.specific.flashblocks_number_contract_use_permit; + let use_permit = self.0.specific.number_contract_use_permit; self.spawn_payload_builder_service( ctx, pool, diff --git a/crates/op-rbuilder/src/flashtestations/tx_manager.rs b/crates/op-rbuilder/src/flashtestations/tx_manager.rs index d6412bf6d..a306cb1b6 100644 --- a/crates/op-rbuilder/src/flashtestations/tx_manager.rs +++ b/crates/op-rbuilder/src/flashtestations/tx_manager.rs @@ -66,7 +66,7 @@ impl TxManager { attestation: Vec, extra_registration_data: Bytes, ) -> Result<(), TxManagerError> { - info!(target: "flashtestations", "funding TEE address at {}", self.tee_service_signer.address); + info!(target: "flashtestations", "registering TEE address at {}", self.tee_service_signer.address); let quote_bytes = Bytes::from(attestation); let wallet = PrivateKeySigner::from_bytes(&self.builder_signer.secret.secret_bytes().into()) diff --git a/crates/op-rbuilder/src/tests/flashblocks.rs b/crates/op-rbuilder/src/tests/flashblocks.rs index c60e9d5ad..357a6c41e 100644 --- a/crates/op-rbuilder/src/tests/flashblocks.rs +++ b/crates/op-rbuilder/src/tests/flashblocks.rs @@ -660,3 +660,236 @@ fn verify_user_tx_hashes( ); } } + +/// Test that build_at_interval_end causes flashblocks to be built after the interval +/// instead of immediately. With this flag enabled, the first flashblock should be +/// delayed by first_flashblock_offset. +#[rb_test(flashblocks, args = OpRbuilderArgs { + chain_block_time: 1000, + flashblocks: FlashblocksArgs { + enabled: true, + flashblocks_port: 1239, + flashblocks_addr: "127.0.0.1".into(), + flashblocks_block_time: 250, + flashblocks_end_buffer_ms: 50, + flashblocks_fixed: false, + flashblocks_build_at_interval_end: true, + ..Default::default() + }, + ..Default::default() +})] +async fn build_at_interval_end_basic(rbuilder: LocalInstance) -> eyre::Result<()> { + let driver = rbuilder.driver().await?; + let flashblocks_listener = rbuilder.spawn_flashblocks_listener(); + + for _ in 0..5 { + for _ in 0..3 { + let _ = driver + .create_transaction() + .random_valid_transfer() + .send() + .await?; + } + let block = driver.build_new_block_with_current_timestamp(None).await?; + assert_eq!(block.transactions.len(), 6); // 3 normal txn + deposit + 2 builder txn + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + + let flashblocks = flashblocks_listener.get_flashblocks(); + // With build_at_interval_end, the deadline_sleep controls when to stop + // Expected: ~4 flashblocks per block with end_buffer_ms applied at the end + assert_eq!( + 25, + flashblocks.len(), + "Expected 15 flashblocks, got {:#?}", + flashblocks.len() + ); + + flashblocks_listener.stop().await +} + +/// Test build_at_interval_end with send_offset_ms combined +#[rb_test(flashblocks, args = OpRbuilderArgs { + chain_block_time: 1000, + flashblocks: FlashblocksArgs { + enabled: true, + flashblocks_port: 1239, + flashblocks_addr: "127.0.0.1".into(), + flashblocks_block_time: 250, + flashblocks_end_buffer_ms: 50, + flashblocks_fixed: false, + flashblocks_build_at_interval_end: true, + flashblocks_send_offset_ms: -25, // Send 25ms earlier + ..Default::default() + }, + ..Default::default() +})] +async fn build_at_interval_end_with_offset(rbuilder: LocalInstance) -> eyre::Result<()> { + let driver: ChainDriver = rbuilder.driver().await?; + let flashblocks_listener = rbuilder.spawn_flashblocks_listener(); + + for _ in 0..5 { + for _ in 0..3 { + let _ = driver + .create_transaction() + .random_valid_transfer() + .send() + .await?; + } + let block = driver.build_new_block_with_current_timestamp(None).await?; + assert_eq!(block.transactions.len(), 6); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + + let flashblocks = flashblocks_listener.get_flashblocks(); + // Combined flags should still produce reasonable flashblock count + assert_eq!( + 25, + flashblocks.len(), + "Expected 15 flashblocks, got {:#?}", + flashblocks.len() + ); + + flashblocks_listener.stop().await +} + +/// Test fixed mode with build_at_interval_end and end_buffer_ms +/// This verifies that the deadline is correctly calculated with the buffer +#[rb_test(flashblocks, args = OpRbuilderArgs { + chain_block_time: 1000, + flashblocks: FlashblocksArgs { + enabled: true, + flashblocks_port: 1239, + flashblocks_addr: "127.0.0.1".into(), + flashblocks_block_time: 250, + flashblocks_leeway_time: 0, + flashblocks_fixed: true, + flashblocks_build_at_interval_end: true, + flashblocks_end_buffer_ms: 100, // Stop building 100ms early + ..Default::default() + }, + ..Default::default() +})] +async fn fixed_mode_with_end_buffer(rbuilder: LocalInstance) -> eyre::Result<()> { + let driver = rbuilder.driver().await?; + let flashblocks_listener = rbuilder.spawn_flashblocks_listener(); + + for _ in 0..3 { + for _ in 0..2 { + let _ = driver + .create_transaction() + .random_valid_transfer() + .send() + .await?; + } + let block = driver.build_new_block_with_current_timestamp(None).await?; + assert!(block.transactions.len() >= 4); // deposit + builder tx + user txs + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + + let flashblocks = flashblocks_listener.get_flashblocks(); + // With end_buffer_ms=100 and interval=250, we should get 4 + 1 (fallback) flashblocks per block + assert_eq!( + 15, + flashblocks.len(), + "Expected 15 flashblocks with end_buffer, got {:#?}", + flashblocks.len() + ); + + flashblocks_listener.stop().await +} + +/// Test dynamic mode with significant FCU delay (700ms into 1000ms block) +/// Should produce fewer flashblocks due to less remaining time +#[rb_test(flashblocks, args = OpRbuilderArgs { + chain_block_time: 1000, + flashblocks: FlashblocksArgs { + enabled: true, + flashblocks_port: 1239, + flashblocks_addr: "127.0.0.1".into(), + flashblocks_block_time: 200, + flashblocks_end_buffer_ms: 50, + flashblocks_build_at_interval_end: true, + ..Default::default() + }, + ..Default::default() +})] +async fn dynamic_mode_late_fcu_reduces_flashblocks(rbuilder: LocalInstance) -> eyre::Result<()> { + let driver = rbuilder.driver().await?; + let flashblocks_listener = rbuilder.spawn_flashblocks_listener(); + + // Send a transaction + let _ = driver + .create_transaction() + .random_valid_transfer() + .send() + .await?; + + // Build block with 700ms delay - only 300ms remaining for a 200ms interval + // Should produce only 2 flashblocks + let block = driver + .build_new_block_with_current_timestamp(Some(Duration::from_millis(700))) + .await?; + + assert!(block.transactions.len() >= 2); // deposit + at least builder tx + + let flashblocks = flashblocks_listener.get_flashblocks(); + + // With only ~300ms remaining and 200ms interval, we should get 2 + 1 (fallback) flashblocks + assert_eq!( + 3, + flashblocks.len(), + "Expected 3 flashblocks with 700ms FCU delay, got {:#?}", + flashblocks.len() + ); + + flashblocks_listener.stop().await +} + +/// Test with 0 end_buffer, build_at_interval_end, and negative offset +/// Ensures all flashblocks are built when negative offset gives margin before deadline +#[rb_test(flashblocks, args = OpRbuilderArgs { + chain_block_time: 1000, + flashblocks: FlashblocksArgs { + enabled: true, + flashblocks_port: 1239, + flashblocks_addr: "127.0.0.1".into(), + flashblocks_block_time: 250, + flashblocks_fixed: true, + flashblocks_build_at_interval_end: true, + flashblocks_send_offset_ms: -50, // Send 50ms earlier to ensure all FBs complete before deadline + ..Default::default() + }, + ..Default::default() +})] +async fn build_at_interval_end_negative_offset(rbuilder: LocalInstance) -> eyre::Result<()> { + let driver = rbuilder.driver().await?; + let flashblocks_listener = rbuilder.spawn_flashblocks_listener(); + + for _ in 0..3 { + for _ in 0..2 { + let _ = driver + .create_transaction() + .random_valid_transfer() + .send() + .await?; + } + let block = driver.build_new_block_with_current_timestamp(None).await?; + assert!(block.transactions.len() >= 4); // deposit + builder tx + user txs + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + + let flashblocks = flashblocks_listener.get_flashblocks(); + + // With 1000ms block time, 250ms interval, -50ms offset: + // First FB at 200ms, then 450, 700, 950ms. Deadline at 950ms. + // All 4 flashblocks + 1 fallback = 5 per block, 3 blocks = 15 total + assert_eq!( + 15, + flashblocks.len(), + "Expected 15 flashblocks (5 per block * 3 blocks), got {}", + flashblocks.len() + ); + + flashblocks_listener.stop().await +} diff --git a/crates/op-rbuilder/src/tests/framework/instance.rs b/crates/op-rbuilder/src/tests/framework/instance.rs index 6250d76a3..16179df8c 100644 --- a/crates/op-rbuilder/src/tests/framework/instance.rs +++ b/crates/op-rbuilder/src/tests/framework/instance.rs @@ -48,6 +48,7 @@ use reth_transaction_pool::{AllTransactionsEvents, TransactionPool}; use std::{ net::SocketAddr, sync::{Arc, LazyLock}, + time::Instant, }; use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle}; use tokio_tungstenite::{connect_async, tungstenite::Message}; @@ -379,14 +380,22 @@ async fn spawn_attestation_provider() -> eyre::Result { Ok(service) } +/// A flashblock payload with its receive timestamp +#[derive(Debug, Clone)] +pub struct TimestampedFlashblock { + pub payload: OpFlashblockPayload, + pub received_at: Instant, +} + /// A utility for listening to flashblocks WebSocket messages during tests. /// /// This provides a reusable way to capture and inspect flashblocks that are produced /// during test execution, eliminating the need for duplicate WebSocket listening code. pub struct FlashblocksListener { - pub flashblocks: Arc>>, + pub flashblocks: Arc>>, pub cancellation_token: CancellationToken, pub handle: JoinHandle>, + pub start_time: Instant, } impl FlashblocksListener { @@ -396,6 +405,7 @@ impl FlashblocksListener { fn new(flashblocks_ws_url: String) -> Self { let flashblocks = Arc::new(Mutex::new(Vec::new())); let cancellation_token = CancellationToken::new(); + let start_time = Instant::now(); let flashblocks_clone = flashblocks.clone(); let cancellation_token_clone = cancellation_token.clone(); @@ -410,8 +420,12 @@ impl FlashblocksListener { break Ok(()); } Some(Ok(Message::Text(text))) = read.next() => { - let fb = serde_json::from_str(&text).unwrap(); - flashblocks_clone.lock().push(fb); + let payload = serde_json::from_str(&text).unwrap(); + let timestamped = TimestampedFlashblock { + payload, + received_at: Instant::now(), + }; + flashblocks_clone.lock().push(timestamped); } } } @@ -421,21 +435,40 @@ impl FlashblocksListener { flashblocks, cancellation_token, handle, + start_time, } } - /// Get a snapshot of all received flashblocks + /// Get a snapshot of all received flashblocks (payloads only) pub fn get_flashblocks(&self) -> Vec { + self.flashblocks + .lock() + .iter() + .map(|tf| tf.payload.clone()) + .collect() + } + + /// Get a snapshot of all received flashblocks with timestamps + pub fn get_timestamped_flashblocks(&self) -> Vec { self.flashblocks.lock().clone() } + /// Get the time elapsed since the listener was started for each flashblock + pub fn get_flashblock_timings(&self) -> Vec { + self.flashblocks + .lock() + .iter() + .map(|tf| tf.received_at.duration_since(self.start_time)) + .collect() + } + /// Find a flashblock by index pub fn find_flashblock(&self, index: u64) -> Option { self.flashblocks .lock() .iter() - .find(|fb| fb.index == index) - .cloned() + .find(|tf| tf.payload.index == index) + .map(|tf| tf.payload.clone()) } /// Check if any flashblock contains the given transaction hash @@ -443,16 +476,17 @@ impl FlashblocksListener { self.flashblocks .lock() .iter() - .any(|fb| fb.metadata.receipts.contains_key(tx_hash)) + .any(|fb| fb.payload.metadata.receipts.contains_key(tx_hash)) } /// Find which flashblock index contains the given transaction hash pub fn find_transaction_flashblock(&self, tx_hash: &B256) -> Option { self.flashblocks.lock().iter().find_map(|fb| { - fb.metadata + fb.payload + .metadata .receipts .contains_key(tx_hash) - .then_some(fb.index) + .then_some(fb.payload.index) }) } From 7cb93f8b9c0675c9e4cc248f3377a279b5259d62 Mon Sep 17 00:00:00 2001 From: Ash Kunda <18058966+akundaz@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:48:18 -0500 Subject: [PATCH 4/6] chore: set builder name in reth_builder_info (#352) --- crates/op-rbuilder/src/metrics.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/op-rbuilder/src/metrics.rs b/crates/op-rbuilder/src/metrics.rs index c381fed82..cd974e52a 100644 --- a/crates/op-rbuilder/src/metrics.rs +++ b/crates/op-rbuilder/src/metrics.rs @@ -265,7 +265,8 @@ pub struct VersionInfo { impl VersionInfo { /// This exposes op-rbuilder's version information over prometheus. pub fn register_version_metrics(&self) { - let labels: [(&str, &str); 8] = [ + let labels: [(&str, &str); 9] = [ + ("builder_flavor", "op-rbuilder"), ("version", self.version), ("build_timestamp", self.build_timestamp), ("cargo_features", self.cargo_features), From feed8bcd7d2dca3c0a464e6f6668d324395b8391 Mon Sep 17 00:00:00 2001 From: Himess <95512809+Himess@users.noreply.github.com> Date: Thu, 8 Jan 2026 01:48:54 +0300 Subject: [PATCH 5/6] feat(tests): add BuilderTxValidation utility for validating builder transactions (#347) * feat(tests): add BuilderTxValidation utility for validating builder transactions Adds `BuilderTxValidation` trait to test framework that provides: - `find_builder_txs()`: Returns info about builder transactions in a block - `has_builder_tx()`: Quick check if block contains builder transactions - `assert_builder_tx_count()`: Assert expected number of builder transactions Also adds `block_includes_builder_transaction` test demonstrating the utility. Closes #88 * test: add BuilderTxValidation assertions to existing tests Updates smoke, flashblocks, and revert tests to use the BuilderTxValidation utility for validating builder transactions in blocks: - smoke.rs: Added builder tx count assertions to chain_produces_blocks, chain_produces_big_tx_with_gas_limit, and chain_produces_big_tx_without_gas_limit - flashblocks.rs: Added builder tx count assertions to smoke_dynamic_base, smoke_dynamic_unichain, smoke_classic_unichain, and smoke_classic_base - revert.rs: Added builder tx validation to monitor_transaction_gc, disabled, and allow_reverted_transactions_without_bundle tests * fix: move builder tx validation before transactions move Fixes borrow of partially moved value error by calling assert_builder_tx_count before block.transactions is moved. --- crates/op-rbuilder/src/tests/flashblocks.rs | 21 +++++- .../op-rbuilder/src/tests/framework/utils.rs | 57 ++++++++++++++++ crates/op-rbuilder/src/tests/revert.rs | 18 ++++- crates/op-rbuilder/src/tests/smoke.rs | 68 ++++++++++++++++++- 4 files changed, 159 insertions(+), 5 deletions(-) diff --git a/crates/op-rbuilder/src/tests/flashblocks.rs b/crates/op-rbuilder/src/tests/flashblocks.rs index 357a6c41e..2947c788f 100644 --- a/crates/op-rbuilder/src/tests/flashblocks.rs +++ b/crates/op-rbuilder/src/tests/flashblocks.rs @@ -9,8 +9,9 @@ use std::time::Duration; use crate::{ args::{FlashblocksArgs, OpRbuilderArgs}, tests::{ - BlockTransactionsExt, BundleOpts, ChainDriver, FLASHBLOCKS_NUMBER_ADDRESS, LocalInstance, - TransactionBuilderExt, flashblocks_number_contract::FlashblocksNumber, + BlockTransactionsExt, BuilderTxValidation, BundleOpts, ChainDriver, + FLASHBLOCKS_NUMBER_ADDRESS, LocalInstance, TransactionBuilderExt, + flashblocks_number_contract::FlashblocksNumber, }, }; @@ -43,6 +44,10 @@ async fn smoke_dynamic_base(rbuilder: LocalInstance) -> eyre::Result<()> { } let block = driver.build_new_block_with_current_timestamp(None).await?; assert_eq!(block.transactions.len(), 8, "Got: {:?}", block.transactions); // 5 normal txn + deposit + 2 builder txn + + // Validate builder transactions using BuilderTxValidation + block.assert_builder_tx_count(2); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; } @@ -81,6 +86,10 @@ async fn smoke_dynamic_unichain(rbuilder: LocalInstance) -> eyre::Result<()> { } let block = driver.build_new_block_with_current_timestamp(None).await?; assert_eq!(block.transactions.len(), 8, "Got: {:?}", block.transactions); // 5 normal txn + deposit + 2 builder txn + + // Validate builder transactions using BuilderTxValidation + block.assert_builder_tx_count(2); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; } @@ -119,6 +128,10 @@ async fn smoke_classic_unichain(rbuilder: LocalInstance) -> eyre::Result<()> { } let block = driver.build_new_block().await?; assert_eq!(block.transactions.len(), 8, "Got: {:?}", block.transactions); // 5 normal txn + deposit + 2 builder txn + + // Validate builder transactions using BuilderTxValidation + block.assert_builder_tx_count(2); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; } @@ -157,6 +170,10 @@ async fn smoke_classic_base(rbuilder: LocalInstance) -> eyre::Result<()> { } let block = driver.build_new_block().await?; assert_eq!(block.transactions.len(), 8, "Got: {:?}", block.transactions); // 5 normal txn + deposit + 2 builder txn + + // Validate builder transactions using BuilderTxValidation + block.assert_builder_tx_count(2); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; } diff --git a/crates/op-rbuilder/src/tests/framework/utils.rs b/crates/op-rbuilder/src/tests/framework/utils.rs index 99772de19..1a1658c8d 100644 --- a/crates/op-rbuilder/src/tests/framework/utils.rs +++ b/crates/op-rbuilder/src/tests/framework/utils.rs @@ -9,6 +9,7 @@ use crate::{ tx_signer::Signer, }; use alloy_eips::Encodable2718; +use alloy_network::TransactionResponse; use alloy_primitives::{Address, B256, BlockHash, TxHash, TxKind, U256, hex}; use alloy_rpc_types_eth::{Block, BlockTransactionHashes}; use alloy_sol_types::SolCall; @@ -272,10 +273,66 @@ impl ChainDriverExt for ChainDriver

{ } } +/// Result of builder transaction validation in a block. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BuilderTxInfo { + /// Number of builder transactions found in the block. + pub count: usize, + /// Indices of builder transactions within the block. + pub indices: Vec, +} + +impl BuilderTxInfo { + /// Returns true if the block contains at least one builder transaction. + pub fn has_builder_tx(&self) -> bool { + self.count > 0 + } +} + pub trait BlockTransactionsExt { fn includes(&self, txs: &impl AsTxs) -> bool; } +/// Extension trait for validating builder transactions in blocks. +pub trait BuilderTxValidation { + /// Checks if the block contains builder transactions from the configured builder address. + /// Returns information about builder transactions found in the block. + fn find_builder_txs(&self) -> BuilderTxInfo; + + /// Returns true if the block contains at least one builder transaction. + fn has_builder_tx(&self) -> bool { + self.find_builder_txs().has_builder_tx() + } + + /// Asserts that the block contains exactly the expected number of builder transactions. + fn assert_builder_tx_count(&self, expected: usize) { + let info = self.find_builder_txs(); + assert_eq!( + info.count, expected, + "Expected {} builder transaction(s), found {} at indices {:?}", + expected, info.count, info.indices + ); + } +} + +impl BuilderTxValidation for Block { + fn find_builder_txs(&self) -> BuilderTxInfo { + let builder_address = builder_signer().address; + let mut indices = Vec::new(); + + for (idx, tx) in self.transactions.txns().enumerate() { + if tx.from() == builder_address { + indices.push(idx); + } + } + + BuilderTxInfo { + count: indices.len(), + indices, + } + } +} + impl BlockTransactionsExt for Block { fn includes(&self, txs: &impl AsTxs) -> bool { txs.as_txs() diff --git a/crates/op-rbuilder/src/tests/revert.rs b/crates/op-rbuilder/src/tests/revert.rs index 76e1c5b9b..8ed792c9d 100644 --- a/crates/op-rbuilder/src/tests/revert.rs +++ b/crates/op-rbuilder/src/tests/revert.rs @@ -6,8 +6,8 @@ use crate::{ args::OpRbuilderArgs, primitives::bundle::MAX_BLOCK_RANGE_BLOCKS, tests::{ - BlockTransactionsExt, BundleOpts, ChainDriver, ChainDriverExt, LocalInstance, ONE_ETH, - OpRbuilderArgsTestExt, TransactionBuilderExt, + BlockTransactionsExt, BuilderTxValidation, BundleOpts, ChainDriver, ChainDriverExt, + LocalInstance, ONE_ETH, OpRbuilderArgsTestExt, TransactionBuilderExt, }, }; @@ -53,6 +53,14 @@ async fn monitor_transaction_gc(rbuilder: LocalInstance) -> eyre::Result<()> { assert_eq!(generated_block.transactions.len(), 3); } + // Validate builder transactions using BuilderTxValidation + if_standard! { + generated_block.assert_builder_tx_count(1); + } + if_flashblocks! { + generated_block.assert_builder_tx_count(2); + } + // since we created the 10 transactions with increasing block ranges, as we generate blocks // one transaction will be gc on each block. // transactions from [0, i] should be dropped, transactions from [i+1, 10] should be queued @@ -88,6 +96,9 @@ async fn disabled(rbuilder: LocalInstance) -> eyre::Result<()> { assert!(block.includes(valid_tx.tx_hash())); assert!(block.includes(reverting_tx.tx_hash())); + + // Validate builder transactions are present + assert!(block.has_builder_tx()); } Ok(()) @@ -396,6 +407,9 @@ async fn allow_reverted_transactions_without_bundle(rbuilder: LocalInstance) -> assert!(block.includes(valid_tx.tx_hash())); assert!(block.includes(reverting_tx.tx_hash())); + + // Validate builder transactions are present + assert!(block.has_builder_tx()); } Ok(()) diff --git a/crates/op-rbuilder/src/tests/smoke.rs b/crates/op-rbuilder/src/tests/smoke.rs index 63cfa77b7..c379b51e8 100644 --- a/crates/op-rbuilder/src/tests/smoke.rs +++ b/crates/op-rbuilder/src/tests/smoke.rs @@ -1,6 +1,6 @@ use crate::{ args::OpRbuilderArgs, - tests::{LocalInstance, TransactionBuilderExt}, + tests::{BuilderTxValidation, LocalInstance, TransactionBuilderExt}, }; use alloy_primitives::TxHash; @@ -34,6 +34,15 @@ async fn chain_produces_blocks(rbuilder: LocalInstance) -> eyre::Result<()> { // the deposit transaction and the block generator's transaction for _ in 0..SAMPLE_SIZE { let block = driver.build_new_block_with_current_timestamp(None).await?; + + // Validate builder transactions are present (must be done before moving transactions) + if_standard! { + block.assert_builder_tx_count(1); + } + if_flashblocks! { + block.assert_builder_tx_count(2); + } + let transactions = block.transactions; if_standard! { @@ -73,6 +82,14 @@ async fn chain_produces_blocks(rbuilder: LocalInstance) -> eyre::Result<()> { let block = driver.build_new_block_with_current_timestamp(None).await?; + // Validate builder transactions are present (must be done before moving transactions) + if_standard! { + block.assert_builder_tx_count(1); + } + if_flashblocks! { + block.assert_builder_tx_count(2); + } + let txs = block.transactions; if_standard! { @@ -223,6 +240,15 @@ async fn chain_produces_big_tx_with_gas_limit(rbuilder: LocalInstance) -> eyre:: .expect("Failed to send transaction"); let block = driver.build_new_block_with_current_timestamp(None).await?; + + // Validate builder transactions are present (must be done before moving transactions) + if_standard! { + block.assert_builder_tx_count(1); + } + if_flashblocks! { + block.assert_builder_tx_count(2); + } + let txs = block.transactions; if_standard! { @@ -272,6 +298,15 @@ async fn chain_produces_big_tx_without_gas_limit(rbuilder: LocalInstance) -> eyr .expect("Failed to send transaction"); let block = driver.build_new_block_with_current_timestamp(None).await?; + + // Validate builder transactions are present (must be done before moving transactions) + if_standard! { + block.assert_builder_tx_count(1); + } + if_flashblocks! { + block.assert_builder_tx_count(2); + } + let txs = block.transactions; // assert we included the tx @@ -296,3 +331,34 @@ async fn chain_produces_big_tx_without_gas_limit(rbuilder: LocalInstance) -> eyr Ok(()) } + +/// Validates that each block contains builder transactions using the +/// BuilderTxValidation utility. +#[rb_test] +async fn block_includes_builder_transaction(rbuilder: LocalInstance) -> eyre::Result<()> { + let driver = rbuilder.driver().await?; + + const SAMPLE_SIZE: usize = 5; + + for _ in 0..SAMPLE_SIZE { + let block = driver.build_new_block_with_current_timestamp(None).await?; + + // Validate that the block contains builder transactions + assert!( + block.has_builder_tx(), + "Block should contain at least one builder transaction" + ); + + // Standard builder: 1 builder tx + // Flashblocks builder: 2 builder txs (fallback + flashblock number) + if_standard! { + block.assert_builder_tx_count(1); + } + + if_flashblocks! { + block.assert_builder_tx_count(2); + } + } + + Ok(()) +} From 8e9e9b1aaf875f34ccd432c0d60c2c44e19d1361 Mon Sep 17 00:00:00 2001 From: shana Date: Wed, 7 Jan 2026 16:03:22 -0800 Subject: [PATCH 6/6] release: 0.2.14 (#350) --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a19b21c1d..654dcfefd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6868,7 +6868,7 @@ dependencies = [ [[package]] name = "op-rbuilder" -version = "0.2.13" +version = "0.2.14" dependencies = [ "alloy-consensus", "alloy-contract", @@ -7159,7 +7159,7 @@ dependencies = [ [[package]] name = "p2p" -version = "0.2.13" +version = "0.2.14" dependencies = [ "derive_more", "eyre", diff --git a/Cargo.toml b/Cargo.toml index 529b1d90b..b053153a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace.package] -version = "0.2.13" +version = "0.2.14" edition = "2024" rust-version = "1.88" license = "MIT OR Apache-2.0"