Skip to content

Commit dcd3364

Browse files
committed
Add new witness calculation algo
Add DA config to builder Account for builder_tx in block_da_size.
1 parent 7b03229 commit dcd3364

File tree

1 file changed

+168
-39
lines changed

1 file changed

+168
-39
lines changed

crates/op-rbuilder/src/payload_builder_vanilla.rs

Lines changed: 168 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,23 @@
1-
use alloy_rpc_types_eth::Withdrawals;
2-
use reth::core::primitives::InMemorySize;
3-
use reth_node_api::NodePrimitives;
4-
use reth_optimism_evm::BasicOpReceiptBuilder;
5-
use reth_optimism_evm::{OpReceiptBuilder, ReceiptBuilderCtx};
6-
use reth_optimism_payload_builder::OpPayloadPrimitives;
7-
use reth_transaction_pool::PoolTransaction;
8-
use std::{fmt::Display, sync::Arc, time::Instant};
9-
101
use crate::generator::BlockPayloadJobGenerator;
112
use crate::generator::BuildArguments;
123
use crate::{
134
generator::{BlockCell, PayloadBuilder},
145
metrics::OpRBuilderMetrics,
156
tx_signer::Signer,
167
};
8+
use alloy_consensus::constants::EMPTY_WITHDRAWALS;
179
use alloy_consensus::{
1810
Eip658Value, Header, Transaction, TxEip1559, Typed2718, EMPTY_OMMER_ROOT_HASH,
1911
};
2012
use alloy_eips::merge::BEACON_NONCE;
13+
use alloy_primitives::private::alloy_rlp::Encodable;
2114
use alloy_primitives::{Address, Bytes, TxKind, B256, U256};
2215
use alloy_rpc_types_engine::PayloadId;
16+
use alloy_rpc_types_eth::Withdrawals;
2317
use op_alloy_consensus::{OpDepositReceipt, OpTypedTransaction};
2418
use reth::builder::{components::PayloadServiceBuilder, node::FullNodeTypes, BuilderContext};
19+
use reth::core::primitives::InMemorySize;
2520
use reth::payload::PayloadBuilderHandle;
26-
use reth_basic_payload_builder::commit_withdrawals;
2721
use reth_basic_payload_builder::{
2822
BasicPayloadJobGeneratorConfig, BuildOutcome, BuildOutcomeKind, PayloadConfig,
2923
};
@@ -34,19 +28,25 @@ use reth_evm::{
3428
EvmError, InvalidTxError, NextBlockEnvAttributes,
3529
};
3630
use reth_execution_types::ExecutionOutcome;
31+
use reth_node_api::NodePrimitives;
3732
use reth_node_api::NodeTypesWithEngine;
3833
use reth_node_api::TxTy;
3934
use reth_optimism_chainspec::OpChainSpec;
4035
use reth_optimism_consensus::calculate_receipt_root_no_memo_optimism;
36+
use reth_optimism_evm::BasicOpReceiptBuilder;
4137
use reth_optimism_evm::OpEvmConfig;
38+
use reth_optimism_evm::{OpReceiptBuilder, ReceiptBuilderCtx};
4239
use reth_optimism_forks::OpHardforks;
4340
use reth_optimism_node::OpEngineTypes;
41+
use reth_optimism_payload_builder::config::{OpBuilderConfig, OpDAConfig};
42+
use reth_optimism_payload_builder::OpPayloadPrimitives;
4443
use reth_optimism_payload_builder::{
4544
error::OpPayloadBuilderError,
4645
payload::{OpBuiltPayload, OpPayloadBuilderAttributes},
4746
};
48-
use reth_optimism_primitives::OpPrimitives;
49-
use reth_optimism_primitives::OpTransactionSigned;
47+
use reth_optimism_primitives::{
48+
OpPrimitives, OpTransactionSigned, ADDRESS_L2_TO_L1_MESSAGE_PASSER,
49+
};
5050
use reth_payload_builder::PayloadBuilderService;
5151
use reth_payload_builder_primitives::PayloadBuilderError;
5252
use reth_payload_primitives::PayloadBuilderAttributes;
@@ -63,13 +63,15 @@ use reth_provider::{
6363
};
6464
use reth_revm::database::StateProviderDatabase;
6565
use reth_transaction_pool::BestTransactionsAttributes;
66+
use reth_transaction_pool::PoolTransaction;
6667
use reth_transaction_pool::TransactionPool;
6768
use revm::{
6869
db::{states::bundle_state::BundleRetention, State},
6970
primitives::{ExecutionResult, ResultAndState},
7071
DatabaseCommit,
7172
};
7273
use std::error::Error as StdError;
74+
use std::{fmt::Display, sync::Arc, time::Instant};
7375
use tokio_util::sync::CancellationToken;
7476
use tracing::{info, trace, warn};
7577

@@ -183,6 +185,8 @@ pub struct OpPayloadBuilderVanilla<Pool, Client, EvmConfig, N: NodePrimitives, T
183185
pub pool: Pool,
184186
/// Node client
185187
pub client: Client,
188+
/// Settings for the builder, e.g. DA settings.
189+
pub config: OpBuilderConfig,
186190
/// The type responsible for yielding the best transactions for the payload if mempool
187191
/// transactions are allowed.
188192
pub best_transactions: Txs,
@@ -203,14 +207,33 @@ impl<Pool, Client, EvmConfig, N: NodePrimitives>
203207
client: Client,
204208
receipt_builder: Arc<dyn OpReceiptBuilder<N::SignedTx, Receipt = N::Receipt>>,
205209
) -> Self {
206-
Self {
210+
Self::with_builder_config(
207211
evm_config,
208212
builder_signer,
209213
pool,
210214
client,
215+
receipt_builder,
216+
Default::default(),
217+
)
218+
}
219+
220+
pub fn with_builder_config(
221+
evm_config: EvmConfig,
222+
builder_signer: Option<Signer>,
223+
pool: Pool,
224+
client: Client,
225+
receipt_builder: Arc<dyn OpReceiptBuilder<N::SignedTx, Receipt = N::Receipt>>,
226+
config: OpBuilderConfig,
227+
) -> Self {
228+
Self {
229+
pool,
230+
client,
231+
receipt_builder,
232+
config,
233+
evm_config,
211234
best_transactions: (),
212235
metrics: Default::default(),
213-
receipt_builder,
236+
builder_signer,
214237
}
215238
}
216239
}
@@ -301,6 +324,7 @@ where
301324

302325
let ctx = OpPayloadBuilderCtx {
303326
evm_config: self.evm_config.clone(),
327+
da_config: self.config.da_config.clone(),
304328
chain_spec: self.client.chain_spec(),
305329
config,
306330
evm_env,
@@ -421,14 +445,34 @@ impl<Txs> OpBuilder<'_, Txs> {
421445
.builder_signer()
422446
.map_or(0, |_| estimate_gas_for_builder_tx(message.clone()));
423447
let block_gas_limit = ctx.block_gas_limit() - builder_tx_gas;
448+
// Save some space in the block_da_limit for builder tx
449+
let builder_tx_da_size = ctx
450+
.estimate_builder_tx_da_size(state, builder_tx_gas, message.clone())
451+
.unwrap_or(0);
452+
let block_da_limit = ctx
453+
.da_config
454+
.max_da_block_size()
455+
.map(|da_size| da_size - builder_tx_da_size as u64);
456+
// Check that it's possible to create builder tx, considering max_da_tx_size, otherwise panic
457+
if let Some(tx_da_limit) = ctx.da_config.max_da_tx_size() {
458+
// Panic indicate max_da_tx_size misconfiguration
459+
assert!(tx_da_limit >= builder_tx_da_size as u64);
460+
}
461+
424462
if !ctx.attributes().no_tx_pool {
425463
let best_txs_start_time = Instant::now();
426464
let best_txs = best(ctx.best_transaction_attributes());
427465
ctx.metrics
428466
.transaction_pool_fetch_duration
429467
.record(best_txs_start_time.elapsed());
430468
if ctx
431-
.execute_best_transactions(&mut info, state, best_txs, block_gas_limit)?
469+
.execute_best_transactions(
470+
&mut info,
471+
state,
472+
best_txs,
473+
block_gas_limit,
474+
block_da_limit,
475+
)?
432476
.is_some()
433477
{
434478
return Ok(BuildOutcomeKind::Cancelled);
@@ -438,8 +482,6 @@ impl<Txs> OpBuilder<'_, Txs> {
438482
// Add builder tx to the block
439483
ctx.add_builder_tx(&mut info, state, builder_tx_gas, message);
440484

441-
let withdrawals_root = ctx.commit_withdrawals(state)?;
442-
443485
let state_merge_start_time = Instant::now();
444486

445487
// merge all transitions into bundle state, this would apply the withdrawal balance changes
@@ -453,12 +495,27 @@ impl<Txs> OpBuilder<'_, Txs> {
453495
.payload_num_tx
454496
.record(info.executed_transactions.len() as f64);
455497

456-
Ok(BuildOutcomeKind::Better {
457-
payload: ExecutedPayload {
458-
info,
459-
withdrawals_root,
460-
},
461-
})
498+
let withdrawals_root = if ctx.is_isthmus_active() {
499+
// withdrawals root field in block header is used for storage root of L2 predeploy
500+
// `l2tol1-message-passer`
501+
Some(
502+
state
503+
.database
504+
.as_ref()
505+
.storage_root(ADDRESS_L2_TO_L1_MESSAGE_PASSER, Default::default())?,
506+
)
507+
} else if ctx.is_canyon_active() {
508+
Some(EMPTY_WITHDRAWALS)
509+
} else {
510+
None
511+
};
512+
513+
let payload = ExecutedPayload {
514+
info,
515+
withdrawals_root,
516+
};
517+
518+
Ok(BuildOutcomeKind::Better { payload })
462519
}
463520

464521
/// Builds the payload on top of the state.
@@ -651,6 +708,8 @@ pub struct ExecutionInfo<N: NodePrimitives> {
651708
pub receipts: Vec<N::Receipt>,
652709
/// All gas used so far
653710
pub cumulative_gas_used: u64,
711+
/// Estimated DA size
712+
pub cumulative_da_bytes_used: u64,
654713
/// Tracks fees from executed mempool transactions
655714
pub total_fees: U256,
656715
}
@@ -663,16 +722,45 @@ impl<N: NodePrimitives> ExecutionInfo<N> {
663722
executed_senders: Vec::with_capacity(capacity),
664723
receipts: Vec::with_capacity(capacity),
665724
cumulative_gas_used: 0,
725+
cumulative_da_bytes_used: 0,
666726
total_fees: U256::ZERO,
667727
}
668728
}
729+
730+
/// Returns true if the transaction would exceed the block limits:
731+
/// - block gas limit: ensures the transaction still fits into the block.
732+
/// - tx DA limit: if configured, ensures the tx does not exceed the maximum allowed DA limit
733+
/// per tx.
734+
/// - block DA limit: if configured, ensures the transaction's DA size does not exceed the
735+
/// maximum allowed DA limit per block.
736+
pub fn is_tx_over_limits(
737+
&self,
738+
tx: &N::SignedTx,
739+
block_gas_limit: u64,
740+
tx_data_limit: Option<u64>,
741+
block_data_limit: Option<u64>,
742+
) -> bool {
743+
if tx_data_limit.is_some_and(|da_limit| tx.length() as u64 > da_limit) {
744+
return true;
745+
}
746+
747+
if block_data_limit
748+
.is_some_and(|da_limit| self.cumulative_da_bytes_used + (tx.length() as u64) > da_limit)
749+
{
750+
return true;
751+
}
752+
753+
self.cumulative_gas_used + tx.gas_limit() > block_gas_limit
754+
}
669755
}
670756

671757
/// Container type that holds all necessities to build a new payload.
672758
#[derive(Debug)]
673759
pub struct OpPayloadBuilderCtx<EvmConfig: ConfigureEvmEnv, ChainSpec, N: NodePrimitives> {
674760
/// The type that knows how to perform system calls and configure the evm.
675761
pub evm_config: EvmConfig,
762+
/// The DA config for the payload builder
763+
pub da_config: OpDAConfig,
676764
/// The chainspec
677765
pub chain_spec: Arc<ChainSpec>,
678766
/// How to build the payload.
@@ -802,8 +890,13 @@ where
802890
.is_holocene_active_at_timestamp(self.attributes().timestamp())
803891
}
804892

893+
/// Returns true if isthmus is active for the payload.
894+
pub fn is_isthmus_active(&self) -> bool {
895+
self.chain_spec
896+
.is_isthmus_active_at_timestamp(self.attributes().timestamp())
897+
}
898+
805899
/// Returns the chain id
806-
/// #
807900
pub fn chain_id(&self) -> u64 {
808901
self.chain_spec.chain_id()
809902
}
@@ -813,19 +906,6 @@ where
813906
self.builder_signer
814907
}
815908

816-
/// Commits the withdrawals from the payload attributes to the state.
817-
pub fn commit_withdrawals<DB>(&self, db: &mut State<DB>) -> Result<Option<B256>, ProviderError>
818-
where
819-
DB: Database<Error = ProviderError>,
820-
{
821-
commit_withdrawals(
822-
db,
823-
&self.chain_spec,
824-
self.attributes().payload_attributes.timestamp,
825-
&self.attributes().payload_attributes.withdrawals,
826-
)
827-
}
828-
829909
/// Ensure that the create2deployer is force-deployed at the canyon transition. Optimism
830910
/// blocks will always have at least a single transaction in them (the L1 info transaction),
831911
/// so we can safely assume that this will always be triggered upon the transition and that
@@ -1020,6 +1100,7 @@ where
10201100
Transaction: PoolTransaction<Consensus = EvmConfig::Transaction>,
10211101
>,
10221102
block_gas_limit: u64,
1103+
block_da_limit: Option<u64>,
10231104
) -> Result<Option<()>, PayloadBuilderError>
10241105
where
10251106
DB: Database<Error = ProviderError>,
@@ -1030,14 +1111,14 @@ where
10301111
let mut num_txs_simulated_success = 0;
10311112
let mut num_txs_simulated_fail = 0;
10321113
let base_fee = self.base_fee();
1033-
1114+
let tx_da_limit = self.da_config.max_da_tx_size();
10341115
let mut evm = self.evm_config.evm_with_env(&mut *db, self.evm_env.clone());
10351116

10361117
while let Some(tx) = best_txs.next(()) {
10371118
let tx = tx.into_consensus();
10381119
num_txs_considered += 1;
10391120
// ensure we still have capacity for this transaction
1040-
if info.cumulative_gas_used + tx.gas_limit() > block_gas_limit {
1121+
if info.is_tx_over_limits(tx.tx(), block_gas_limit, tx_da_limit, block_da_limit) {
10411122
// we can't fit this transaction into the block, so we need to mark it as
10421123
// invalid which also removes all dependent transaction from
10431124
// the iterator before we can continue
@@ -1207,6 +1288,54 @@ where
12071288
None
12081289
})
12091290
}
1291+
1292+
/// Calculates EIP 2718 builder transaction size
1293+
pub fn estimate_builder_tx_da_size<DB>(
1294+
&self,
1295+
db: &mut State<DB>,
1296+
builder_tx_gas: u64,
1297+
message: Vec<u8>,
1298+
) -> Option<usize>
1299+
where
1300+
DB: Database<Error = ProviderError>,
1301+
{
1302+
self.builder_signer()
1303+
.map(|signer| {
1304+
let base_fee = self.base_fee();
1305+
// Create message with block number for the builder to sign
1306+
let nonce = db
1307+
.load_cache_account(signer.address)
1308+
.map(|acc| acc.account_info().unwrap_or_default().nonce)
1309+
.map_err(|_| {
1310+
PayloadBuilderError::other(OpPayloadBuilderError::AccountLoadFailed(
1311+
signer.address,
1312+
))
1313+
})?;
1314+
1315+
// Create the EIP-1559 transaction
1316+
let eip1559 = OpTypedTransaction::Eip1559(TxEip1559 {
1317+
chain_id: self.chain_id(),
1318+
nonce,
1319+
gas_limit: builder_tx_gas,
1320+
max_fee_per_gas: base_fee.into(),
1321+
max_priority_fee_per_gas: 0,
1322+
to: TxKind::Call(Address::ZERO),
1323+
// Include the message as part of the transaction data
1324+
input: message.into(),
1325+
..Default::default()
1326+
});
1327+
let tx = eip1559;
1328+
// Sign the transaction
1329+
let builder_tx = signer.sign_tx(tx).map_err(PayloadBuilderError::other)?;
1330+
1331+
Ok(builder_tx.length())
1332+
})
1333+
.transpose()
1334+
.unwrap_or_else(|err: PayloadBuilderError| {
1335+
warn!(target: "payload_builder", %err, "Failed to add builder transaction");
1336+
None
1337+
})
1338+
}
12101339
}
12111340

12121341
fn estimate_gas_for_builder_tx(input: Vec<u8>) -> u64 {

0 commit comments

Comments
 (0)