diff --git a/src/main.rs b/src/main.rs index d07ba485..2fe81a6c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use bera_reth::{ consensus::BerachainBeaconConsensus, evm::BerachainEvmFactory, node::{BerachainNode, evm::config::BerachainEvmConfig}, + pool::config::berachain_txpool_defaults, version::init_bera_version, }; use clap::Parser; @@ -35,6 +36,10 @@ fn main() { .try_init() .expect("engine defaults must be set before CLI parsing"); + berachain_txpool_defaults() + .try_init() + .expect("txpool defaults must be set before CLI parsing"); + // Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided. if std::env::var_os("RUST_BACKTRACE").is_none() { unsafe { std::env::set_var("RUST_BACKTRACE", "1") }; diff --git a/src/pool/config.rs b/src/pool/config.rs new file mode 100644 index 00000000..bb639dc4 --- /dev/null +++ b/src/pool/config.rs @@ -0,0 +1,214 @@ +//! Berachain transaction pool defaults. +//! +//! Pinned against reth `v1.11.4`. Upstream regression tests fail on dependency bumps +//! when Reth changes a default — review, then adopt (update pins) or override. + +use reth_node_core::args::{DefaultTxPoolValues, TxPoolArgs}; +use std::time::Duration; + +/// BRIP-0010: Osaka does not adopt EIP-7594 (PeerDAS); reject v1 sidecars, keep EIP-4844. +pub const BERACHAIN_ACCEPTS_EIP7594: bool = false; + +pub const SUBPOOL_MAX_TXS: usize = 10_000; +pub const SUBPOOL_MAX_SIZE_MB: usize = 20; +pub const MAX_ACCOUNT_SLOTS: usize = 16; +pub const PRICE_BUMP: u128 = 10; +pub const BLOB_REPLACE_PRICE_BUMP: u128 = 100; +/// Inherited from reth v1.11.4 (`MIN_PROTOCOL_BASE_FEE`) as the txpool protocol floor (in wei), +/// independent of Berachain's chain-spec minimum base fee (1 gwei). +pub const MINIMAL_PROTOCOL_BASEFEE: u64 = 7; +pub const ENFORCED_GAS_LIMIT: u64 = 30_000_000; +pub const MAX_TX_INPUT_BYTES: usize = 128 * 1024; +pub const MAX_QUEUED_LIFETIME: Duration = Duration::from_secs(3 * 60 * 60); + +/// Inherited from reth v1.11.4 (`DefaultTxPoolValues::default().max_batch_size`). +/// No upstream named constant; validated live in `tests/reth_txpool_upstream.rs`. +#[doc(hidden)] +pub const INHERITED_TXPOOL_MAX_BATCH_SIZE: usize = 1; + +struct BerachainPinnedTxPoolLimits; + +impl BerachainPinnedTxPoolLimits { + fn apply_to_defaults(self, defaults: DefaultTxPoolValues) -> DefaultTxPoolValues { + defaults + .with_pending_max_count(SUBPOOL_MAX_TXS) + .with_pending_max_size(SUBPOOL_MAX_SIZE_MB) + .with_basefee_max_count(SUBPOOL_MAX_TXS) + .with_basefee_max_size(SUBPOOL_MAX_SIZE_MB) + .with_queued_max_count(SUBPOOL_MAX_TXS) + .with_queued_max_size(SUBPOOL_MAX_SIZE_MB) + .with_blobpool_max_count(SUBPOOL_MAX_TXS) + .with_blobpool_max_size(SUBPOOL_MAX_SIZE_MB) + .with_max_account_slots(MAX_ACCOUNT_SLOTS) + .with_price_bump(PRICE_BUMP) + .with_minimal_protocol_basefee(MINIMAL_PROTOCOL_BASEFEE) + .with_enforced_gas_limit(ENFORCED_GAS_LIMIT) + .with_blob_transaction_price_bump(BLOB_REPLACE_PRICE_BUMP) + .with_max_tx_input_bytes(MAX_TX_INPUT_BYTES) + .with_max_queued_lifetime(MAX_QUEUED_LIFETIME) + } + + #[cfg(test)] + fn apply_to_args(self, args: &mut TxPoolArgs) { + args.pending_max_count = SUBPOOL_MAX_TXS; + args.pending_max_size = SUBPOOL_MAX_SIZE_MB; + args.basefee_max_count = SUBPOOL_MAX_TXS; + args.basefee_max_size = SUBPOOL_MAX_SIZE_MB; + args.queued_max_count = SUBPOOL_MAX_TXS; + args.queued_max_size = SUBPOOL_MAX_SIZE_MB; + args.blobpool_max_count = SUBPOOL_MAX_TXS; + args.blobpool_max_size = SUBPOOL_MAX_SIZE_MB; + args.max_account_slots = MAX_ACCOUNT_SLOTS; + args.price_bump = PRICE_BUMP; + args.minimal_protocol_basefee = MINIMAL_PROTOCOL_BASEFEE; + args.enforced_gas_limit = ENFORCED_GAS_LIMIT; + args.blob_transaction_price_bump = BLOB_REPLACE_PRICE_BUMP; + args.max_tx_input_bytes = MAX_TX_INPUT_BYTES; + args.max_queued_lifetime = MAX_QUEUED_LIFETIME; + } +} + +/// Berachain txpool defaults applied before CLI parsing in `main.rs`. +pub fn berachain_txpool_defaults() -> DefaultTxPoolValues { + BerachainPinnedTxPoolLimits.apply_to_defaults(DefaultTxPoolValues::default()) +} + +/// Asserts every txpool CLI field pinned by [`berachain_txpool_defaults`]. +#[doc(hidden)] +pub fn assert_berachain_pinned_txpool_args(args: &TxPoolArgs) { + assert_eq!(args.pending_max_count, SUBPOOL_MAX_TXS); + assert_eq!(args.pending_max_size, SUBPOOL_MAX_SIZE_MB); + assert_eq!(args.basefee_max_count, SUBPOOL_MAX_TXS); + assert_eq!(args.basefee_max_size, SUBPOOL_MAX_SIZE_MB); + assert_eq!(args.queued_max_count, SUBPOOL_MAX_TXS); + assert_eq!(args.queued_max_size, SUBPOOL_MAX_SIZE_MB); + assert_eq!(args.blobpool_max_count, SUBPOOL_MAX_TXS); + assert_eq!(args.blobpool_max_size, SUBPOOL_MAX_SIZE_MB); + assert_eq!(args.max_account_slots, MAX_ACCOUNT_SLOTS); + assert_eq!(args.price_bump, PRICE_BUMP); + assert_eq!(args.minimal_protocol_basefee, MINIMAL_PROTOCOL_BASEFEE); + assert_eq!(args.enforced_gas_limit, ENFORCED_GAS_LIMIT); + assert_eq!(args.blob_transaction_price_bump, BLOB_REPLACE_PRICE_BUMP); + assert_eq!(args.max_tx_input_bytes, MAX_TX_INPUT_BYTES); + assert_eq!(args.max_queued_lifetime, MAX_QUEUED_LIFETIME); +} + +/// Asserts unpinned txpool CLI fields match current Reth upstream defaults. +#[doc(hidden)] +pub fn assert_inherited_unpinned_match_reth_upstream_constants(args: &TxPoolArgs) { + use reth_transaction_pool::{ + blobstore::disk::DEFAULT_MAX_CACHED_BLOBS, + pool::{NEW_TX_LISTENER_BUFFER_SIZE, PENDING_TX_LISTENER_BUFFER_SIZE}, + DEFAULT_TXPOOL_ADDITIONAL_VALIDATION_TASKS, MAX_NEW_PENDING_TXS_NOTIFICATIONS, + }; + + assert_eq!(args.additional_validation_tasks, DEFAULT_TXPOOL_ADDITIONAL_VALIDATION_TASKS); + assert_eq!(args.max_cached_entries, DEFAULT_MAX_CACHED_BLOBS); + assert_eq!(args.pending_tx_listener_buffer_size, PENDING_TX_LISTENER_BUFFER_SIZE); + assert_eq!(args.new_tx_listener_buffer_size, NEW_TX_LISTENER_BUFFER_SIZE); + assert_eq!(args.max_new_pending_txs_notifications, MAX_NEW_PENDING_TXS_NOTIFICATIONS); + assert_eq!(args.max_batch_size, INHERITED_TXPOOL_MAX_BATCH_SIZE); + assert!(!args.disable_blobs_support); + assert_eq!(args.blob_cache_size, None); + assert_eq!(args.minimum_priority_fee, None); + assert_eq!(args.max_tx_gas_limit, None); + assert!(!args.no_locals); + assert!(args.locals.is_empty()); + assert!(!args.no_local_transactions_propagation); + assert_eq!(args.transactions_backup_path, None); + assert!(!args.disable_transactions_backup); +} + +#[cfg(test)] +fn berachain_txpool_args() -> TxPoolArgs { + let mut args = TxPoolArgs::default(); + BerachainPinnedTxPoolLimits.apply_to_args(&mut args); + args +} + +#[cfg(test)] +mod tests { + use super::*; + use reth_node_core::cli::config::RethTransactionPoolConfig; + use reth_transaction_pool::{PriceBumpConfig, SubPoolLimit}; + + const SUBPOOL_MAX_BYTES: usize = SUBPOOL_MAX_SIZE_MB * 1024 * 1024; + + /// Fails when a pinned Berachain limit no longer matches the current Reth upstream default. + /// On failure: adopt Reth's new value (update constants) or keep Berachain's choice (drop assertion). + #[test] + fn berachain_pinned_limits_match_reth_upstream() { + use alloy_eips::eip1559::{ETHEREUM_BLOCK_GAS_LIMIT_30M, MIN_PROTOCOL_BASE_FEE}; + use reth_transaction_pool::{ + maintain::MAX_QUEUED_TRANSACTION_LIFETIME, validate::DEFAULT_MAX_TX_INPUT_BYTES, + DEFAULT_PRICE_BUMP, REPLACE_BLOB_PRICE_BUMP, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, + TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, TXPOOL_SUBPOOL_MAX_TXS_DEFAULT, + }; + + assert_eq!(SUBPOOL_MAX_TXS, TXPOOL_SUBPOOL_MAX_TXS_DEFAULT); + assert_eq!(SUBPOOL_MAX_SIZE_MB, TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT); + assert_eq!(MAX_ACCOUNT_SLOTS, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER); + assert_eq!(PRICE_BUMP, DEFAULT_PRICE_BUMP); + assert_eq!(BLOB_REPLACE_PRICE_BUMP, REPLACE_BLOB_PRICE_BUMP); + assert_eq!(MINIMAL_PROTOCOL_BASEFEE, MIN_PROTOCOL_BASE_FEE); + assert_eq!(ENFORCED_GAS_LIMIT, ETHEREUM_BLOCK_GAS_LIMIT_30M); + assert_eq!(MAX_TX_INPUT_BYTES, DEFAULT_MAX_TX_INPUT_BYTES); + assert_eq!(MAX_QUEUED_LIFETIME, MAX_QUEUED_TRANSACTION_LIFETIME); + } + + /// Fails when Reth changes an unpinned txpool default that Berachain still inherits. + #[test] + fn berachain_inherited_unpinned_limits_match_reth_upstream() { + assert_inherited_unpinned_match_reth_upstream_constants(&berachain_txpool_args()); + } + + #[test] + fn berachain_pinned_txpool_args_regression() { + assert_berachain_pinned_txpool_args(&berachain_txpool_args()); + } + + #[test] + fn berachain_pool_config_limits_regression() { + let args = berachain_txpool_args(); + assert_berachain_pinned_txpool_args(&args); + let config = args.pool_config(); + + assert_eq!( + config.pending_limit, + SubPoolLimit::new(SUBPOOL_MAX_TXS, SUBPOOL_MAX_BYTES) + ); + assert_eq!( + config.basefee_limit, + SubPoolLimit::new(SUBPOOL_MAX_TXS, SUBPOOL_MAX_BYTES) + ); + assert_eq!( + config.queued_limit, + SubPoolLimit::new(SUBPOOL_MAX_TXS, SUBPOOL_MAX_BYTES) + ); + assert_eq!( + config.blob_limit, + SubPoolLimit::new(SUBPOOL_MAX_TXS, SUBPOOL_MAX_BYTES) + ); + assert_eq!(config.max_account_slots, MAX_ACCOUNT_SLOTS); + assert_eq!( + config.price_bumps, + PriceBumpConfig { + default_price_bump: PRICE_BUMP, + replace_blob_tx_price_bump: BLOB_REPLACE_PRICE_BUMP, + } + ); + assert_eq!(config.minimal_protocol_basefee, MINIMAL_PROTOCOL_BASEFEE); + assert_eq!(config.gas_limit, ENFORCED_GAS_LIMIT); + assert_eq!(config.minimum_priority_fee, None); + assert_eq!(config.max_queued_lifetime, MAX_QUEUED_LIFETIME); + assert_eq!(config.blob_cache_size, None); + assert!(!config.local_transactions_config.no_exemptions); + assert!(config.local_transactions_config.local_addresses.is_empty()); + assert!(config.local_transactions_config.propagate_local_transactions); + } + + #[test] + fn berachain_rejects_eip7594_sidecars() { + assert!(!BERACHAIN_ACCEPTS_EIP7594); + } +} diff --git a/src/pool/mod.rs b/src/pool/mod.rs index d1d05cac..adef120c 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -1,9 +1,10 @@ pub mod min_priority_fee; +pub mod config; pub mod transaction; use crate::{ chainspec::BerachainChainSpec, - pool::{min_priority_fee::MinPriorityFeeValidator, transaction::BerachainPooledTransaction}, + pool::{min_priority_fee::MinPriorityFeeValidator, transaction::BerachainPooledTransaction, config::BERACHAIN_ACCEPTS_EIP7594,}, primitives::BerachainPrimitives, }; use alloy_consensus::BlockHeader; @@ -72,9 +73,7 @@ where let validator = TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone(), evm_config) .set_eip4844(!blobs_disabled) - // BRIP-0010 Osaka does not adopt EIP-7594 (PeerDAS); keep EIP-4844 sidecars - // accepted across forks and reject EIP-7594 v1 sidecars. - .no_eip7594() + .set_eip7594(BERACHAIN_ACCEPTS_EIP7594) .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes) .kzg_settings(ctx.kzg_settings()?) .with_local_transactions_config(pool_config.local_transactions_config.clone()) diff --git a/tests/reth_txpool_upstream.rs b/tests/reth_txpool_upstream.rs new file mode 100644 index 00000000..b83dce06 --- /dev/null +++ b/tests/reth_txpool_upstream.rs @@ -0,0 +1,12 @@ +//! Validates inherited defaults that Reth does not export as named constants. +//! +//! Must remain the only test in this binary so `TxPoolArgs::default()` observes upstream +//! defaults before Berachain calls `try_init`. + +use bera_reth::pool::config::INHERITED_TXPOOL_MAX_BATCH_SIZE; +use reth_node_core::args::TxPoolArgs; + +#[test] +fn inherited_max_batch_size_matches_reth_upstream() { + assert_eq!(TxPoolArgs::default().max_batch_size, INHERITED_TXPOOL_MAX_BATCH_SIZE); +} diff --git a/tests/txpool_defaults.rs b/tests/txpool_defaults.rs new file mode 100644 index 00000000..fef52fa9 --- /dev/null +++ b/tests/txpool_defaults.rs @@ -0,0 +1,21 @@ +//! Integration test for the `main.rs` txpool default initialization path. +//! +//! Runs in a separate process so `DefaultTxPoolValues::try_init` is not preceded by +//! `TxPoolArgs::default()` initializing the global with upstream defaults. + +use bera_reth::pool::config::{ + assert_berachain_pinned_txpool_args, assert_inherited_unpinned_match_reth_upstream_constants, + berachain_txpool_defaults, +}; +use reth_node_core::args::TxPoolArgs; + +#[test] +fn berachain_txpool_defaults_regression() { + berachain_txpool_defaults() + .try_init() + .expect("txpool defaults must initialize before CLI parsing"); + + let berachain = TxPoolArgs::default(); + assert_berachain_pinned_txpool_args(&berachain); + assert_inherited_unpinned_match_reth_upstream_constants(&berachain); +}