diff --git a/crates/op-rbuilder/src/args/op.rs b/crates/op-rbuilder/src/args/op.rs index 136e58ad1..d4b92e589 100644 --- a/crates/op-rbuilder/src/args/op.rs +++ b/crates/op-rbuilder/src/args/op.rs @@ -95,7 +95,7 @@ pub struct FlashblocksArgs { /// flashblock block time in milliseconds #[arg( - long = "flashblock.block-time", + long = "flashblocks.block-time", default_value = "250", env = "FLASHBLOCK_BLOCK_TIME" )] diff --git a/crates/op-rbuilder/src/builders/context.rs b/crates/op-rbuilder/src/builders/context.rs index dd500de31..162446091 100644 --- a/crates/op-rbuilder/src/builders/context.rs +++ b/crates/op-rbuilder/src/builders/context.rs @@ -1,5 +1,5 @@ use alloy_consensus::{Eip658Value, Transaction, TxEip1559}; -use alloy_eips::{Encodable2718, Typed2718}; +use alloy_eips::{eip7623::TOTAL_COST_FLOOR_PER_TOKEN, Encodable2718, Typed2718}; use alloy_op_evm::block::receipt_builder::OpReceiptBuilder; use alloy_primitives::{Address, Bytes, TxKind, U256}; use alloy_rpc_types_eth::Withdrawals; @@ -448,9 +448,9 @@ impl OpPayloadBuilderCtx { Ok(None) } - pub fn add_builder_tx( + pub fn add_builder_tx( &self, - info: &mut ExecutionInfo, + info: &mut ExecutionInfo, db: &mut State, builder_tx_gas: u64, message: Vec, @@ -531,6 +531,27 @@ impl OpPayloadBuilderCtx { } } +pub fn estimate_gas_for_builder_tx(input: Vec) -> u64 { + // Count zero and non-zero bytes + let (zero_bytes, nonzero_bytes) = input.iter().fold((0, 0), |(zeros, nonzeros), &byte| { + if byte == 0 { + (zeros + 1, nonzeros) + } else { + (zeros, nonzeros + 1) + } + }); + + // Calculate gas cost (4 gas per zero byte, 16 gas per non-zero byte) + let zero_cost = zero_bytes * 4; + let nonzero_cost = nonzero_bytes * 16; + + // Tx gas should be not less than floor gas https://eips.ethereum.org/EIPS/eip-7623 + let tokens_in_calldata = zero_bytes + nonzero_bytes * 4; + let floor_gas = 21_000 + tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN; + + std::cmp::max(zero_cost + nonzero_cost + 21_000, floor_gas) +} + /// Creates signed builder tx to Address::ZERO and specified message as input pub fn signed_builder_tx( db: &mut State, diff --git a/crates/op-rbuilder/src/builders/flashblocks/payload.rs b/crates/op-rbuilder/src/builders/flashblocks/payload.rs index 128c9a68d..8c37bbb35 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/payload.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/payload.rs @@ -4,7 +4,7 @@ use std::{sync::Arc, time::Instant}; use super::{config::FlashblocksConfig, wspub::WebSocketPublisher}; use crate::{ builders::{ - context::OpPayloadBuilderCtx, + context::{estimate_gas_for_builder_tx, OpPayloadBuilderCtx}, flashblocks::config::FlashBlocksConfigExt, generator::{BlockCell, BuildArguments}, BuilderConfig, @@ -257,6 +257,13 @@ where } }); + let message = format!("Block Number: {}", ctx.block_number()) + .as_bytes() + .to_vec(); + let builder_tx_gas = ctx + .builder_signer() + .map_or(0, |_| estimate_gas_for_builder_tx(message.clone())); + // Process flashblocks in a blocking loop loop { // Block on receiving a message, break on cancellation or closed channel @@ -340,6 +347,12 @@ where return Ok(()); } + // If it is the last flashblocks, add the builder txn to the block if enabled + if flashblock_count == self.config.flashblocks_per_block() - 1 { + // TODO: Account for DA size limits + ctx.add_builder_tx(&mut info, &mut db, builder_tx_gas, message.clone()); + } + let total_block_built_duration = Instant::now(); let build_result = build_block(db, &ctx, &mut info); ctx.metrics diff --git a/crates/op-rbuilder/src/builders/mod.rs b/crates/op-rbuilder/src/builders/mod.rs index 9cbc4a0f0..66ad7436d 100644 --- a/crates/op-rbuilder/src/builders/mod.rs +++ b/crates/op-rbuilder/src/builders/mod.rs @@ -147,7 +147,7 @@ where builder_signer: args.builder_signer, revert_protection: args.enable_revert_protection, block_time: Duration::from_millis(args.chain_block_time), - block_time_leeway: Duration::from_millis(500), + block_time_leeway: Duration::from_secs(args.extra_block_deadline_secs), da_config: Default::default(), specific: S::try_from(args)?, }) diff --git a/crates/op-rbuilder/src/builders/standard/payload.rs b/crates/op-rbuilder/src/builders/standard/payload.rs index 96c280a3c..35e16ff8d 100644 --- a/crates/op-rbuilder/src/builders/standard/payload.rs +++ b/crates/op-rbuilder/src/builders/standard/payload.rs @@ -1,9 +1,7 @@ use alloy_consensus::{ constants::EMPTY_WITHDRAWALS, proofs, BlockBody, Header, EMPTY_OMMER_ROOT_HASH, }; -use alloy_eips::{ - eip7623::TOTAL_COST_FLOOR_PER_TOKEN, eip7685::EMPTY_REQUESTS_HASH, merge::BEACON_NONCE, -}; +use alloy_eips::{eip7685::EMPTY_REQUESTS_HASH, merge::BEACON_NONCE}; use alloy_primitives::U256; use reth::payload::PayloadBuilderAttributes; use reth_basic_payload_builder::{BuildOutcome, BuildOutcomeKind, MissingPayloadBehaviour}; @@ -38,7 +36,7 @@ use crate::{ traits::{ClientBounds, NodeBounds, PayloadTxsBounds, PoolBounds}, }; -use super::super::context::OpPayloadBuilderCtx; +use super::super::context::{estimate_gas_for_builder_tx, OpPayloadBuilderCtx}; pub struct StandardPayloadBuilderBuilder(pub BuilderConfig<()>); @@ -566,24 +564,3 @@ impl OpBuilder<'_, Txs> { } } } - -fn estimate_gas_for_builder_tx(input: Vec) -> u64 { - // Count zero and non-zero bytes - let (zero_bytes, nonzero_bytes) = input.iter().fold((0, 0), |(zeros, nonzeros), &byte| { - if byte == 0 { - (zeros + 1, nonzeros) - } else { - (zeros, nonzeros + 1) - } - }); - - // Calculate gas cost (4 gas per zero byte, 16 gas per non-zero byte) - let zero_cost = zero_bytes * 4; - let nonzero_cost = nonzero_bytes * 16; - - // Tx gas should be not less than floor gas https://eips.ethereum.org/EIPS/eip-7623 - let tokens_in_calldata = zero_bytes + nonzero_bytes * 4; - let floor_gas = 21_000 + tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN; - - std::cmp::max(zero_cost + nonzero_cost + 21_000, floor_gas) -} diff --git a/crates/op-rbuilder/src/tests/flashblocks/smoke.rs b/crates/op-rbuilder/src/tests/flashblocks/smoke.rs index 6b972eb5f..c500c40c0 100644 --- a/crates/op-rbuilder/src/tests/flashblocks/smoke.rs +++ b/crates/op-rbuilder/src/tests/flashblocks/smoke.rs @@ -9,7 +9,6 @@ use tokio_util::sync::CancellationToken; use crate::tests::TestHarnessBuilder; #[tokio::test] -#[ignore = "Flashblocks tests need more work"] async fn chain_produces_blocks() -> eyre::Result<()> { let harness = TestHarnessBuilder::new("flashbots_chain_produces_blocks") .with_flashblocks_port(1239) @@ -49,7 +48,9 @@ async fn chain_produces_blocks() -> eyre::Result<()> { let _ = harness.send_valid_transaction().await?; } - generator.generate_block().await?; + let generated_block = generator.generate_block().await?; + assert_eq!(generated_block.num_transactions(), 7); // 5 normal txn + deposit + builder txn + tokio::time::sleep(std::time::Duration::from_secs(1)).await; } diff --git a/crates/op-rbuilder/src/tests/framework/blocks.rs b/crates/op-rbuilder/src/tests/framework/blocks.rs index bc4f9004c..2c50f788c 100644 --- a/crates/op-rbuilder/src/tests/framework/blocks.rs +++ b/crates/op-rbuilder/src/tests/framework/blocks.rs @@ -402,4 +402,8 @@ impl BlockGenerated { pub fn not_includes_vec(&self, tx_hashes: Vec) -> bool { tx_hashes.iter().all(|hash| self.not_includes(*hash)) } + + pub fn num_transactions(&self) -> usize { + self.block.transactions.len() + } } diff --git a/crates/op-rbuilder/src/tests/framework/harness.rs b/crates/op-rbuilder/src/tests/framework/harness.rs index 30fb084ee..cd5ee0ee9 100644 --- a/crates/op-rbuilder/src/tests/framework/harness.rs +++ b/crates/op-rbuilder/src/tests/framework/harness.rs @@ -133,6 +133,7 @@ impl TestHarnessBuilder { builder_http_port, validator_auth_rpc_port, builder_log_path, + chain_block_time: self.chain_block_time, }) } } @@ -143,6 +144,7 @@ pub struct TestHarness { builder_http_port: u16, validator_auth_rpc_port: u16, builder_log_path: PathBuf, + chain_block_time: Option, } impl TestHarness { @@ -173,7 +175,13 @@ impl TestHarness { let engine_api = EngineApi::new_with_port(self.builder_auth_rpc_port).unwrap(); let validation_api = Some(EngineApi::new_with_port(self.validator_auth_rpc_port).unwrap()); - let mut generator = BlockGenerator::new(engine_api, validation_api, false, 1, None); + let mut generator = BlockGenerator::new( + engine_api, + validation_api, + false, + self.chain_block_time.map_or(1, |time| time / 1000), // in seconds + None, + ); generator.init().await?; Ok(generator) diff --git a/crates/op-rbuilder/src/tests/framework/op.rs b/crates/op-rbuilder/src/tests/framework/op.rs index 30aae7a87..f007b33d0 100644 --- a/crates/op-rbuilder/src/tests/framework/op.rs +++ b/crates/op-rbuilder/src/tests/framework/op.rs @@ -153,7 +153,7 @@ impl Service for OpRbuilderConfig { } if let Some(flashblocks_port) = &self.flashblocks_port { - cmd.arg("--flashblocks.enabled").arg("true"); + cmd.arg("--flashblocks.enabled"); cmd.arg("--flashblocks.addr").arg("127.0.0.1"); cmd.arg("--flashblocks.port") .arg(flashblocks_port.to_string()); @@ -165,7 +165,7 @@ impl Service for OpRbuilderConfig { } if let Some(flashbots_block_time) = self.flashbots_block_time { - cmd.arg("--rollup.flashblock-block-time") + cmd.arg("--flashblocks.block-time") .arg(flashbots_block_time.to_string()); }