Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/op-rbuilder/src/args/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pub struct FlashblocksArgs {

/// flashblock block time in milliseconds
#[arg(
long = "flashblock.block-time",
long = "flashblocks.block-time",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am changing this flag too so that all of them have the same prefix

default_value = "250",
env = "FLASHBLOCK_BLOCK_TIME"
)]
Expand Down
27 changes: 24 additions & 3 deletions crates/op-rbuilder/src/builders/context.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -448,9 +448,9 @@ impl OpPayloadBuilderCtx {
Ok(None)
}

pub fn add_builder_tx<DB>(
pub fn add_builder_tx<DB, Extra: Debug + Default>(
&self,
info: &mut ExecutionInfo,
info: &mut ExecutionInfo<Extra>,
db: &mut State<DB>,
builder_tx_gas: u64,
message: Vec<u8>,
Expand Down Expand Up @@ -531,6 +531,27 @@ impl OpPayloadBuilderCtx {
}
}

pub fn estimate_gas_for_builder_tx(input: Vec<u8>) -> 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>(
db: &mut State<DB>,
Expand Down
15 changes: 14 additions & 1 deletion crates/op-rbuilder/src/builders/flashblocks/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important note. It does not account neither yet for DA limits nor for gas limits. It is still a bit of an heuristic we have to figure out how to split gas limit and DA limit in flashblocks.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we want a builder tx for every flashblock?

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
Expand Down
2 changes: 1 addition & 1 deletion crates/op-rbuilder/src/builders/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Copy link
Contributor Author

@ferranbt ferranbt May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was missing, the flag extra-block-deadline-secs was not being used

da_config: Default::default(),
specific: S::try_from(args)?,
})
Expand Down
27 changes: 2 additions & 25 deletions crates/op-rbuilder/src/builders/standard/payload.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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<()>);

Expand Down Expand Up @@ -566,24 +564,3 @@ impl<Txs: PayloadTxsBounds> OpBuilder<'_, Txs> {
}
}
}

fn estimate_gas_for_builder_tx(input: Vec<u8>) -> 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)
}
5 changes: 3 additions & 2 deletions crates/op-rbuilder/src/tests/flashblocks/smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}

Expand Down
4 changes: 4 additions & 0 deletions crates/op-rbuilder/src/tests/framework/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,4 +402,8 @@ impl BlockGenerated {
pub fn not_includes_vec(&self, tx_hashes: Vec<B256>) -> bool {
tx_hashes.iter().all(|hash| self.not_includes(*hash))
}

pub fn num_transactions(&self) -> usize {
self.block.transactions.len()
}
}
10 changes: 9 additions & 1 deletion crates/op-rbuilder/src/tests/framework/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ impl TestHarnessBuilder {
builder_http_port,
validator_auth_rpc_port,
builder_log_path,
chain_block_time: self.chain_block_time,
})
}
}
Expand All @@ -143,6 +144,7 @@ pub struct TestHarness {
builder_http_port: u16,
validator_auth_rpc_port: u16,
builder_log_path: PathBuf,
chain_block_time: Option<u64>,
}

impl TestHarness {
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions crates/op-rbuilder/src/tests/framework/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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());
}

Expand Down
Loading