Skip to content

Commit b866e29

Browse files
committed
fix builder gas and da calculation
1 parent bcb40aa commit b866e29

File tree

5 files changed

+98
-44
lines changed

5 files changed

+98
-44
lines changed

crates/op-rbuilder/src/builders/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ impl<ExtraCtx: Debug + Default> OpPayloadBuilderCtx<ExtraCtx> {
385385
best_txs: &mut impl PayloadTxsBounds,
386386
block_gas_limit: u64,
387387
block_da_limit: Option<u64>,
388+
block_da_footprint_limit: Option<u64>,
388389
) -> Result<Option<()>, PayloadBuilderError> {
389390
let execute_txs_start_time = Instant::now();
390391
let mut num_txs_considered = 0;
@@ -470,6 +471,7 @@ impl<ExtraCtx: Debug + Default> OpPayloadBuilderCtx<ExtraCtx> {
470471
block_da_limit,
471472
tx.gas_limit(),
472473
info.da_footprint_scalar,
474+
block_da_footprint_limit,
473475
) {
474476
// we can't fit this transaction into the block, so we need to mark it as
475477
// invalid which also removes all dependent transaction from

crates/op-rbuilder/src/builders/flashblocks/payload.rs

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -85,20 +85,30 @@ pub struct FlashblocksExtraCtx {
8585
target_gas_for_batch: u64,
8686
/// Total DA bytes left for the current flashblock
8787
target_da_for_batch: Option<u64>,
88+
/// Total DA footprint left for the current flashblock
89+
target_da_footprint_for_batch: Option<u64>,
8890
/// Gas limit per flashblock
8991
gas_per_batch: u64,
9092
/// DA bytes limit per flashblock
9193
da_per_batch: Option<u64>,
94+
/// DA footprint limit per flashblock
95+
da_footprint_per_batch: Option<u64>,
9296
/// Whether to disable state root calculation for each flashblock
9397
disable_state_root: bool,
9498
}
9599

96100
impl FlashblocksExtraCtx {
97-
fn next(self, target_gas_for_batch: u64, target_da_for_batch: Option<u64>) -> Self {
101+
fn next(
102+
self,
103+
target_gas_for_batch: u64,
104+
target_da_for_batch: Option<u64>,
105+
target_da_footprint_for_batch: Option<u64>,
106+
) -> Self {
98107
Self {
99108
flashblock_index: self.flashblock_index + 1,
100109
target_gas_for_batch,
101110
target_da_for_batch,
111+
target_da_footprint_for_batch,
102112
..self
103113
}
104114
}
@@ -344,28 +354,18 @@ where
344354
ctx.metrics.sequencer_tx_gauge.set(sequencer_tx_time);
345355

346356
// We add first builder tx right after deposits
347-
let builder_txs = if ctx.attributes().no_tx_pool {
348-
vec![]
349-
} else {
350-
match self.builder_tx.add_builder_txs(
351-
&state_provider,
352-
&mut info,
353-
&ctx,
354-
&mut state,
355-
false,
356-
) {
357-
Ok(builder_txs) => builder_txs,
358-
Err(e) => {
359-
error!(target: "payload_builder", "Error adding builder txs to fallback block: {}", e);
360-
vec![]
361-
}
362-
}
357+
if !ctx.attributes().no_tx_pool
358+
&& let Err(e) =
359+
self.builder_tx
360+
.add_builder_txs(&state_provider, &mut info, &ctx, &mut state, false)
361+
{
362+
error!(
363+
target: "payload_builder",
364+
"Error adding builder txs to fallback block: {}",
365+
e
366+
);
363367
};
364368

365-
// We subtract gas limit and da limit for builder transaction from the whole limit
366-
let builder_tx_gas = builder_txs.iter().fold(0, |acc, tx| acc + tx.gas_used);
367-
let builder_tx_da_size: u64 = builder_txs.iter().fold(0, |acc, tx| acc + tx.da_size);
368-
369369
let (payload, fb_payload) = build_block(
370370
&mut state,
371371
&ctx,
@@ -438,34 +438,33 @@ where
438438
.first_flashblock_time_offset
439439
.record(first_flashblock_offset.as_millis() as f64);
440440
let gas_per_batch = ctx.block_gas_limit() / flashblocks_per_block;
441-
let target_gas_for_batch = gas_per_batch;
442441
let da_per_batch = ctx
443442
.da_config
444443
.max_da_block_size()
445444
.map(|da_limit| da_limit / flashblocks_per_block);
446445
// Check that builder tx won't affect fb limit too much
447446
if let Some(da_limit) = da_per_batch {
448447
// We error if we can't insert any tx aside from builder tx in flashblock
449-
if da_limit / 2 < builder_tx_da_size {
448+
if info.cumulative_da_bytes_used >= da_limit {
450449
error!(
451450
"Builder tx da size subtraction caused max_da_block_size to be 0. No transaction would be included."
452451
);
453452
}
454453
}
455-
let mut target_da_for_batch = da_per_batch;
454+
let da_footprint_per_batch = info
455+
.da_footprint_scalar
456+
.map(|_| ctx.block_gas_limit() / flashblocks_per_block);
456457

457-
// Account for already included builder tx
458-
if let Some(da_limit) = target_da_for_batch.as_mut() {
459-
*da_limit = da_limit.saturating_sub(builder_tx_da_size);
460-
}
461458
let extra_ctx = FlashblocksExtraCtx {
462459
flashblock_index: 1,
463460
target_flashblock_count: flashblocks_per_block,
464-
target_gas_for_batch: target_gas_for_batch.saturating_sub(builder_tx_gas),
465-
target_da_for_batch,
461+
target_gas_for_batch: gas_per_batch,
462+
target_da_for_batch: da_per_batch,
466463
gas_per_batch,
467464
da_per_batch,
465+
da_footprint_per_batch,
468466
disable_state_root,
467+
target_da_footprint_for_batch: da_footprint_per_batch,
469468
};
470469

471470
let mut fb_cancel = block_cancel.child_token();
@@ -613,6 +612,7 @@ where
613612
let flashblock_index = ctx.flashblock_index();
614613
let mut target_gas_for_batch = ctx.extra_ctx.target_gas_for_batch;
615614
let mut target_da_for_batch = ctx.extra_ctx.target_da_for_batch;
615+
let mut target_da_footprint_for_batch = ctx.extra_ctx.target_da_footprint_for_batch;
616616

617617
info!(
618618
target: "payload_builder",
@@ -623,6 +623,7 @@ where
623623
target_da = target_da_for_batch,
624624
da_used = info.cumulative_da_bytes_used,
625625
block_gas_used = ctx.block_gas_limit(),
626+
target_da_footprint = target_da_footprint_for_batch,
626627
"Building flashblock",
627628
);
628629
let flashblock_build_start_time = Instant::now();
@@ -639,15 +640,30 @@ where
639640
}
640641
};
641642

642-
let builder_tx_gas = builder_txs.iter().fold(0, |acc, tx| acc + tx.gas_used);
643-
let builder_tx_da_size: u64 = builder_txs.iter().fold(0, |acc, tx| acc + tx.da_size);
643+
// only reserve builder tx gas / da size that has not been committed yet
644+
// committed builder txs would have counted towards the gas / da used
645+
let builder_tx_gas = builder_txs
646+
.iter()
647+
.filter(|tx| !tx.is_top_of_block)
648+
.fold(0, |acc, tx| acc + tx.gas_used);
649+
let builder_tx_da_size: u64 = builder_txs
650+
.iter()
651+
.filter(|tx| !tx.is_top_of_block)
652+
.fold(0, |acc, tx| acc + tx.da_size);
644653
target_gas_for_batch = target_gas_for_batch.saturating_sub(builder_tx_gas);
645654

646655
// saturating sub just in case, we will log an error if da_limit too small for builder_tx_da_size
647656
if let Some(da_limit) = target_da_for_batch.as_mut() {
648657
*da_limit = da_limit.saturating_sub(builder_tx_da_size);
649658
}
650659

660+
if let (Some(footprint), Some(scalar)) = (
661+
target_da_footprint_for_batch.as_mut(),
662+
info.da_footprint_scalar,
663+
) {
664+
*footprint = footprint.saturating_sub(builder_tx_da_size.saturating_mul(scalar as u64));
665+
}
666+
651667
let best_txs_start_time = Instant::now();
652668
best_txs.refresh_iterator(
653669
BestPayloadTransactions::new(
@@ -671,6 +687,7 @@ where
671687
best_txs,
672688
target_gas_for_batch.min(ctx.block_gas_limit()),
673689
target_da_for_batch,
690+
target_da_footprint_for_batch,
674691
)
675692
.wrap_err("failed to execute best transactions")?;
676693
// Extract last transactions
@@ -779,10 +796,21 @@ where
779796

780797
let target_gas_for_batch =
781798
ctx.extra_ctx.target_gas_for_batch + ctx.extra_ctx.gas_per_batch;
782-
let next_extra_ctx = ctx
783-
.extra_ctx
784-
.clone()
785-
.next(target_gas_for_batch, target_da_for_batch);
799+
800+
if let (Some(footprint), Some(da_footprint_limit)) = (
801+
target_da_footprint_for_batch.as_mut(),
802+
ctx.extra_ctx.da_footprint_per_batch,
803+
) {
804+
*footprint += da_footprint_limit;
805+
}
806+
807+
info!(target_da_for_batch, "next da batch");
808+
809+
let next_extra_ctx = ctx.extra_ctx.clone().next(
810+
target_gas_for_batch,
811+
target_da_for_batch,
812+
target_da_footprint_for_batch,
813+
);
786814

787815
info!(
788816
target: "payload_builder",

crates/op-rbuilder/src/builders/standard/payload.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,14 @@ impl<Txs: PayloadTxsBounds> OpBuilder<'_, Txs> {
385385
}
386386
da_limit
387387
});
388+
let block_da_footprint = info.da_footprint_scalar
389+
.map(|da_footprint_scalar| {
390+
let da_footprint_limit = ctx.block_gas_limit().saturating_sub(builder_tx_da_size.saturating_mul(da_footprint_scalar as u64));
391+
if da_footprint_limit == 0 {
392+
error!("Builder tx da size subtraction caused max_da_footprint to be 0. No transaction would be included.");
393+
}
394+
da_footprint_limit
395+
});
388396

389397
if !ctx.attributes().no_tx_pool {
390398
let best_txs_start_time = Instant::now();
@@ -404,6 +412,7 @@ impl<Txs: PayloadTxsBounds> OpBuilder<'_, Txs> {
404412
&mut best_txs,
405413
block_gas_limit,
406414
block_da_limit,
415+
block_da_footprint,
407416
)?
408417
.is_some()
409418
{

crates/op-rbuilder/src/primitives/reth/execution.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ impl<T: Debug + Default> ExecutionInfo<T> {
6565
/// per tx.
6666
/// - block DA limit: if configured, ensures the transaction's DA size does not exceed the
6767
/// maximum allowed DA limit per block.
68+
#[allow(clippy::too_many_arguments)]
6869
pub fn is_tx_over_limits(
6970
&self,
7071
tx_da_size: u64,
@@ -73,12 +74,20 @@ impl<T: Debug + Default> ExecutionInfo<T> {
7374
block_data_limit: Option<u64>,
7475
tx_gas_limit: u64,
7576
da_footprint_gas_scalar: Option<u16>,
77+
block_da_footprint_limit: Option<u64>,
7678
) -> Result<(), TxnExecutionResult> {
7779
if tx_data_limit.is_some_and(|da_limit| tx_da_size > da_limit) {
7880
return Err(TxnExecutionResult::TransactionDALimitExceeded);
7981
}
8082
let total_da_bytes_used = self.cumulative_da_bytes_used.saturating_add(tx_da_size);
81-
83+
tracing::info!(
84+
tx_da_size,
85+
tx_data_limit,
86+
block_data_limit,
87+
block_da_footprint_limit,
88+
total_da_bytes_used,
89+
"da limits"
90+
);
8291
if block_data_limit.is_some_and(|da_limit| total_da_bytes_used > da_limit) {
8392
return Err(TxnExecutionResult::BlockDALimitExceeded(
8493
self.cumulative_da_bytes_used,
@@ -91,7 +100,7 @@ impl<T: Debug + Default> ExecutionInfo<T> {
91100
if let Some(da_footprint_gas_scalar) = da_footprint_gas_scalar {
92101
let tx_da_footprint =
93102
total_da_bytes_used.saturating_mul(da_footprint_gas_scalar as u64);
94-
if tx_da_footprint > block_gas_limit {
103+
if tx_da_footprint > block_da_footprint_limit.unwrap_or(block_gas_limit) {
95104
return Err(TxnExecutionResult::BlockDALimitExceeded(
96105
total_da_bytes_used,
97106
tx_da_size,

crates/op-rbuilder/src/tests/data_availability.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ async fn block_fill(rbuilder: LocalInstance) -> eyre::Result<()> {
6464
let driver = rbuilder.driver().await?;
6565

6666
// Set block big enough so it could fit 3 transactions without tx size limit
67-
// Deposit transactions also count towards DA and there is one deposit txn in this block too
6867
let call = driver
6968
.provider()
7069
.raw_request::<(i32, i32), bool>("miner_setMaxDASize".into(), (0, 100 * 4))
@@ -83,31 +82,38 @@ async fn block_fill(rbuilder: LocalInstance) -> eyre::Result<()> {
8382
.with_max_priority_fee_per_gas(50)
8483
.send()
8584
.await?;
86-
let unfit_tx_3 = driver.create_transaction().send().await?;
85+
let fit_tx_3 = driver
86+
.create_transaction()
87+
.with_max_priority_fee_per_gas(50)
88+
.send()
89+
.await?;
90+
let unfit_tx_4 = driver.create_transaction().send().await?;
8791

8892
let block = driver.build_new_block_with_current_timestamp(None).await?;
8993

9094
if_standard! {
9195
// Now the first 2 txs will fit into the block
9296
assert!(block.includes(fit_tx_1.tx_hash()), "tx should be in block");
9397
assert!(block.includes(fit_tx_2.tx_hash()), "tx should be in block");
98+
assert!(block.includes(fit_tx_3.tx_hash()), "tx should be in block");
9499
}
95100

96101
if_flashblocks! {
97102
// in flashblocks the DA quota is divided by the number of flashblocks
98103
// so we will include only one tx in the block because not all of them
99104
// will fit within DA quote / flashblocks count.
100105
assert!(block.includes(fit_tx_1.tx_hash()), "tx should be in block");
101-
assert!(!block.includes(fit_tx_2.tx_hash()), "tx should not be in block");
106+
assert!(block.includes(fit_tx_2.tx_hash()), "tx should be in block");
107+
assert!(!block.includes(fit_tx_3.tx_hash()), "tx should not be in block");
102108
}
103109

104110
assert!(
105-
!block.includes(unfit_tx_3.tx_hash()),
111+
!block.includes(unfit_tx_4.tx_hash()),
106112
"unfit tx should not be in block"
107113
);
108114
assert!(
109-
driver.latest_full().await?.transactions.len() == 4,
110-
"builder + deposit + 2 valid txs should be in the block"
115+
driver.latest_full().await?.transactions.len() == 5,
116+
"builder + deposit + 3 valid txs should be in the block"
111117
);
112118

113119
Ok(())

0 commit comments

Comments
 (0)