feat(jovian): track da footprint block limit. Update basefee calculation (#19048)

Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
This commit is contained in:
theo
2025-10-22 19:28:23 -04:00
committed by GitHub
parent 346ef408a4
commit bcef01ce47
11 changed files with 358 additions and 59 deletions

13
Cargo.lock generated
View File

@@ -253,9 +253,9 @@ dependencies = [
[[package]]
name = "alloy-evm"
version = "0.22.3"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb19405755c6f94c9bb856f2b1449767074b7e2002e1ab2be0a79b9b28db322"
checksum = "83ce19ea6140497670b1b7e721f9a9ce88022fe475a5e4e6a68a403499cca209"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -370,9 +370,9 @@ dependencies = [
[[package]]
name = "alloy-op-evm"
version = "0.22.3"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f059cf29d7f15b3e6581ceb6eda06a16d8ed4b55adc02b0677add3fd381db6bb"
checksum = "7d7aeaf6051f53880a65b547c43e3b05ee42f68236b1f43f013abfe4eadc47bb"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -6127,9 +6127,9 @@ dependencies = [
[[package]]
name = "op-revm"
version = "11.1.2"
version = "11.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d721c4c196273dd135ea5b823cd573ea8735cd3c5f2c19fcb91ee3af655351"
checksum = "a33ab6a7bbcfffcbf784de78f14593b6389003f5c69653fcffcc163459a37d69"
dependencies = [
"auto_impl",
"revm",
@@ -9435,6 +9435,7 @@ version = "1.8.2"
dependencies = [
"alloy-consensus",
"alloy-eips",
"alloy-evm",
"alloy-primitives",
"alloy-rlp",
"alloy-rpc-types-debug",

View File

@@ -478,14 +478,14 @@ revm-inspector = { version = "11.1.0", default-features = false }
revm-context = { version = "10.1.0", default-features = false }
revm-context-interface = { version = "11.1.0", default-features = false }
revm-database-interface = { version = "8.0.1", default-features = false }
op-revm = { version = "11.1.0", default-features = false }
op-revm = { version = "11.2.0", default-features = false }
revm-inspectors = "0.31.0"
# eth
alloy-chains = { version = "0.2.5", default-features = false }
alloy-dyn-abi = "1.4.1"
alloy-eip2124 = { version = "0.2.0", default-features = false }
alloy-evm = { version = "0.22.0", default-features = false }
alloy-evm = { version = "0.22.4", default-features = false }
alloy-primitives = { version = "1.4.1", default-features = false, features = ["map-foldhash"] }
alloy-rlp = { version = "0.3.10", default-features = false, features = ["core-net"] }
alloy-sol-macro = "1.4.1"
@@ -523,7 +523,7 @@ alloy-transport-ipc = { version = "1.0.41", default-features = false }
alloy-transport-ws = { version = "1.0.41", default-features = false }
# op
alloy-op-evm = { version = "0.22.0", default-features = false }
alloy-op-evm = { version = "0.22.4", default-features = false }
alloy-op-hardforks = "0.4.0"
op-alloy-rpc-types = { version = "0.21.0", default-features = false }
op-alloy-rpc-types-engine = { version = "0.21.0", default-features = false }

View File

@@ -1,26 +1,13 @@
//! Base fee related utilities for Optimism chains.
use core::cmp::max;
use alloy_consensus::BlockHeader;
use alloy_eips::calc_next_block_base_fee;
use op_alloy_consensus::{decode_holocene_extra_data, decode_jovian_extra_data, EIP1559ParamError};
use reth_chainspec::{BaseFeeParams, EthChainSpec};
use reth_optimism_forks::OpHardforks;
fn next_base_fee_params<H: BlockHeader>(
chain_spec: impl EthChainSpec + OpHardforks,
parent: &H,
timestamp: u64,
denominator: u32,
elasticity: u32,
) -> u64 {
let base_fee_params = if elasticity == 0 && denominator == 0 {
chain_spec.base_fee_params_at_timestamp(timestamp)
} else {
BaseFeeParams::new(denominator as u128, elasticity as u128)
};
parent.next_block_base_fee(base_fee_params).unwrap_or_default()
}
/// Extracts the Holocene 1599 parameters from the encoded extra data from the parent header.
///
/// Caution: Caller must ensure that holocene is active in the parent header.
@@ -36,7 +23,13 @@ where
{
let (elasticity, denominator) = decode_holocene_extra_data(parent.extra_data())?;
Ok(next_base_fee_params(chain_spec, parent, timestamp, denominator, elasticity))
let base_fee_params = if elasticity == 0 && denominator == 0 {
chain_spec.base_fee_params_at_timestamp(timestamp)
} else {
BaseFeeParams::new(denominator as u128, elasticity as u128)
};
Ok(parent.next_block_base_fee(base_fee_params).unwrap_or_default())
}
/// Extracts the Jovian 1599 parameters from the encoded extra data from the parent header.
@@ -57,8 +50,22 @@ where
{
let (elasticity, denominator, min_base_fee) = decode_jovian_extra_data(parent.extra_data())?;
let next_base_fee =
next_base_fee_params(chain_spec, parent, timestamp, denominator, elasticity);
let base_fee_params = if elasticity == 0 && denominator == 0 {
chain_spec.base_fee_params_at_timestamp(timestamp)
} else {
BaseFeeParams::new(denominator as u128, elasticity as u128)
};
// Starting from Jovian, we use the maximum of the gas used and the blob gas used to calculate
// the next base fee.
let gas_used = max(parent.gas_used(), parent.blob_gas_used().unwrap_or_default());
let next_base_fee = calc_next_block_base_fee(
gas_used,
parent.gas_limit(),
parent.base_fee_per_gas().unwrap_or_default(),
base_fee_params,
);
if next_base_fee < min_base_fee {
return Ok(min_base_fee);
@@ -66,3 +73,127 @@ where
Ok(next_base_fee)
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use op_alloy_consensus::encode_jovian_extra_data;
use reth_chainspec::{ChainSpec, ForkCondition, Hardfork};
use reth_optimism_forks::OpHardfork;
use crate::{OpChainSpec, BASE_SEPOLIA};
use super::*;
const JOVIAN_TIMESTAMP: u64 = 1900000000;
fn get_chainspec() -> Arc<OpChainSpec> {
let mut base_sepolia_spec = BASE_SEPOLIA.inner.clone();
base_sepolia_spec
.hardforks
.insert(OpHardfork::Jovian.boxed(), ForkCondition::Timestamp(JOVIAN_TIMESTAMP));
Arc::new(OpChainSpec {
inner: ChainSpec {
chain: base_sepolia_spec.chain,
genesis: base_sepolia_spec.genesis,
genesis_header: base_sepolia_spec.genesis_header,
..Default::default()
},
})
}
#[test]
fn test_next_base_fee_jovian_blob_gas_used_greater_than_gas_used() {
let chain_spec = get_chainspec();
let mut parent = chain_spec.genesis_header().clone();
let timestamp = JOVIAN_TIMESTAMP;
const GAS_LIMIT: u64 = 10_000_000_000;
const BLOB_GAS_USED: u64 = 5_000_000_000;
const GAS_USED: u64 = 1_000_000_000;
const MIN_BASE_FEE: u64 = 100_000_000;
parent.extra_data =
encode_jovian_extra_data([0; 8].into(), BaseFeeParams::base_sepolia(), MIN_BASE_FEE)
.unwrap();
parent.blob_gas_used = Some(BLOB_GAS_USED);
parent.gas_used = GAS_USED;
parent.gas_limit = GAS_LIMIT;
let expected_base_fee = calc_next_block_base_fee(
BLOB_GAS_USED,
parent.gas_limit(),
parent.base_fee_per_gas().unwrap_or_default(),
BaseFeeParams::base_sepolia(),
);
assert_eq!(
expected_base_fee,
compute_jovian_base_fee(chain_spec, &parent, timestamp).unwrap()
);
assert_ne!(
expected_base_fee,
calc_next_block_base_fee(
GAS_USED,
parent.gas_limit(),
parent.base_fee_per_gas().unwrap_or_default(),
BaseFeeParams::base_sepolia(),
)
)
}
#[test]
fn test_next_base_fee_jovian_blob_gas_used_less_than_gas_used() {
let chain_spec = get_chainspec();
let mut parent = chain_spec.genesis_header().clone();
let timestamp = JOVIAN_TIMESTAMP;
const GAS_LIMIT: u64 = 10_000_000_000;
const BLOB_GAS_USED: u64 = 100_000_000;
const GAS_USED: u64 = 1_000_000_000;
const MIN_BASE_FEE: u64 = 100_000_000;
parent.extra_data =
encode_jovian_extra_data([0; 8].into(), BaseFeeParams::base_sepolia(), MIN_BASE_FEE)
.unwrap();
parent.blob_gas_used = Some(BLOB_GAS_USED);
parent.gas_used = GAS_USED;
parent.gas_limit = GAS_LIMIT;
let expected_base_fee = calc_next_block_base_fee(
GAS_USED,
parent.gas_limit(),
parent.base_fee_per_gas().unwrap_or_default(),
BaseFeeParams::base_sepolia(),
);
assert_eq!(
expected_base_fee,
compute_jovian_base_fee(chain_spec, &parent, timestamp).unwrap()
);
}
#[test]
fn test_next_base_fee_jovian_min_base_fee() {
let chain_spec = get_chainspec();
let mut parent = chain_spec.genesis_header().clone();
let timestamp = JOVIAN_TIMESTAMP;
const GAS_LIMIT: u64 = 10_000_000_000;
const BLOB_GAS_USED: u64 = 100_000_000;
const GAS_USED: u64 = 1_000_000_000;
const MIN_BASE_FEE: u64 = 5_000_000_000;
parent.extra_data =
encode_jovian_extra_data([0; 8].into(), BaseFeeParams::base_sepolia(), MIN_BASE_FEE)
.unwrap();
parent.blob_gas_used = Some(BLOB_GAS_USED);
parent.gas_used = GAS_USED;
parent.gas_limit = GAS_LIMIT;
let expected_base_fee = MIN_BASE_FEE;
assert_eq!(
expected_base_fee,
compute_jovian_base_fee(chain_spec, &parent, timestamp).unwrap()
);
}
}

View File

@@ -46,7 +46,7 @@ impl<ChainSpec: OpHardforks> OpBlockAssembler<ChainSpec> {
evm_env,
execution_ctx: ctx,
transactions,
output: BlockExecutionResult { receipts, gas_used, .. },
output: BlockExecutionResult { receipts, gas_used, blob_gas_used, requests: _ },
bundle_state,
state_root,
state_provider,
@@ -80,7 +80,11 @@ impl<ChainSpec: OpHardforks> OpBlockAssembler<ChainSpec> {
};
let (excess_blob_gas, blob_gas_used) =
if self.chain_spec.is_ecotone_active_at_timestamp(timestamp) {
if self.chain_spec.is_jovian_active_at_timestamp(timestamp) {
// In jovian, we're using the blob gas used field to store the current da
// footprint's value.
(Some(0), Some(*blob_gas_used))
} else if self.chain_spec.is_ecotone_active_at_timestamp(timestamp) {
(Some(0), Some(0))
} else {
(None, None)

View File

@@ -38,6 +38,9 @@ pub enum L1BlockInfoError {
/// Operator fee constant conversion error
#[error("could not convert operator fee constant")]
OperatorFeeConstantConversion,
/// DA foootprint gas scalar constant conversion error
#[error("could not convert DA footprint gas scalar constant")]
DaFootprintGasScalarConversion,
/// Optimism hardforks not active
#[error("Optimism hardforks are not active")]
HardforksNotActive,

View File

@@ -2,7 +2,7 @@
use crate::{error::L1BlockInfoError, revm_spec_by_timestamp_after_bedrock, OpBlockExecutionError};
use alloy_consensus::Transaction;
use alloy_primitives::{hex, U256};
use alloy_primitives::{hex, U16, U256};
use op_revm::L1BlockInfo;
use reth_execution_errors::BlockExecutionError;
use reth_optimism_forks::OpHardforks;
@@ -14,6 +14,10 @@ const L1_BLOCK_ECOTONE_SELECTOR: [u8; 4] = hex!("440a5e20");
/// The function selector of the "setL1BlockValuesIsthmus" function in the `L1Block` contract.
const L1_BLOCK_ISTHMUS_SELECTOR: [u8; 4] = hex!("098999be");
/// The function selector of the "setL1BlockValuesJovian" function in the `L1Block` contract.
/// This is the first 4 bytes of `keccak256("setL1BlockValuesJovian()")`.
const L1_BLOCK_JOVIAN_SELECTOR: [u8; 4] = hex!("3db6be2b");
/// Extracts the [`L1BlockInfo`] from the L2 block. The L1 info transaction is always the first
/// transaction in the L2 block.
///
@@ -52,11 +56,14 @@ pub fn extract_l1_info_from_tx<T: Transaction>(
/// If the input is shorter than 4 bytes.
pub fn parse_l1_info(input: &[u8]) -> Result<L1BlockInfo, OpBlockExecutionError> {
// Parse the L1 info transaction into an L1BlockInfo struct, depending on the function selector.
// There are currently 3 variants:
// There are currently 4 variants:
// - Jovian
// - Isthmus
// - Ecotone
// - Bedrock
if input[0..4] == L1_BLOCK_ISTHMUS_SELECTOR {
if input[0..4] == L1_BLOCK_JOVIAN_SELECTOR {
parse_l1_info_tx_jovian(input[4..].as_ref())
} else if input[0..4] == L1_BLOCK_ISTHMUS_SELECTOR {
parse_l1_info_tx_isthmus(input[4..].as_ref())
} else if input[0..4] == L1_BLOCK_ECOTONE_SELECTOR {
parse_l1_info_tx_ecotone(input[4..].as_ref())
@@ -88,14 +95,12 @@ pub fn parse_l1_info_tx_bedrock(data: &[u8]) -> Result<L1BlockInfo, OpBlockExecu
let l1_fee_scalar = U256::try_from_be_slice(&data[224..256])
.ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::FeeScalarConversion))?;
let l1block = L1BlockInfo {
Ok(L1BlockInfo {
l1_base_fee,
l1_fee_overhead: Some(l1_fee_overhead),
l1_base_fee_scalar: l1_fee_scalar,
..Default::default()
};
Ok(l1block)
})
}
/// Updates the L1 block values for an Ecotone upgraded chain.
@@ -142,15 +147,13 @@ pub fn parse_l1_info_tx_ecotone(data: &[u8]) -> Result<L1BlockInfo, OpBlockExecu
let l1_blob_base_fee = U256::try_from_be_slice(&data[64..96])
.ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BlobBaseFeeConversion))?;
let l1block = L1BlockInfo {
Ok(L1BlockInfo {
l1_base_fee,
l1_base_fee_scalar,
l1_blob_base_fee: Some(l1_blob_base_fee),
l1_blob_base_fee_scalar: Some(l1_blob_base_fee_scalar),
..Default::default()
};
Ok(l1block)
})
}
/// Updates the L1 block values for an Isthmus upgraded chain.
@@ -205,7 +208,7 @@ pub fn parse_l1_info_tx_isthmus(data: &[u8]) -> Result<L1BlockInfo, OpBlockExecu
OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::OperatorFeeConstantConversion)
})?;
let l1block = L1BlockInfo {
Ok(L1BlockInfo {
l1_base_fee,
l1_base_fee_scalar,
l1_blob_base_fee: Some(l1_blob_base_fee),
@@ -213,9 +216,78 @@ pub fn parse_l1_info_tx_isthmus(data: &[u8]) -> Result<L1BlockInfo, OpBlockExecu
operator_fee_scalar: Some(operator_fee_scalar),
operator_fee_constant: Some(operator_fee_constant),
..Default::default()
};
})
}
Ok(l1block)
/// Updates the L1 block values for an Jovian upgraded chain.
/// Params are packed and passed in as raw msg.data instead of ABI to reduce calldata size.
/// Params are expected to be in the following order:
/// 1. _baseFeeScalar L1 base fee scalar
/// 2. _blobBaseFeeScalar L1 blob base fee scalar
/// 3. _sequenceNumber Number of L2 blocks since epoch start.
/// 4. _timestamp L1 timestamp.
/// 5. _number L1 blocknumber.
/// 6. _basefee L1 base fee.
/// 7. _blobBaseFee L1 blob base fee.
/// 8. _hash L1 blockhash.
/// 9. _batcherHash Versioned hash to authenticate batcher by.
/// 10. _operatorFeeScalar Operator fee scalar
/// 11. _operatorFeeConstant Operator fee constant
/// 12. _daFootprintGasScalar DA footprint gas scalar
pub fn parse_l1_info_tx_jovian(data: &[u8]) -> Result<L1BlockInfo, OpBlockExecutionError> {
if data.len() != 174 {
return Err(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::UnexpectedCalldataLength));
}
// https://github.com/ethereum-optimism/op-geth/blob/60038121c7571a59875ff9ed7679c48c9f73405d/core/types/rollup_cost.go#L317-L328
//
// data layout assumed for Ecotone:
// offset type varname
// 0 <selector>
// 4 uint32 _basefeeScalar (start offset in this scope)
// 8 uint32 _blobBaseFeeScalar
// 12 uint64 _sequenceNumber,
// 20 uint64 _timestamp,
// 28 uint64 _l1BlockNumber
// 36 uint256 _basefee,
// 68 uint256 _blobBaseFee,
// 100 bytes32 _hash,
// 132 bytes32 _batcherHash,
// 164 uint32 _operatorFeeScalar
// 168 uint64 _operatorFeeConstant
// 176 uint16 _daFootprintGasScalar
let l1_base_fee_scalar = U256::try_from_be_slice(&data[..4])
.ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BaseFeeScalarConversion))?;
let l1_blob_base_fee_scalar = U256::try_from_be_slice(&data[4..8]).ok_or({
OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BlobBaseFeeScalarConversion)
})?;
let l1_base_fee = U256::try_from_be_slice(&data[32..64])
.ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BaseFeeConversion))?;
let l1_blob_base_fee = U256::try_from_be_slice(&data[64..96])
.ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BlobBaseFeeConversion))?;
let operator_fee_scalar = U256::try_from_be_slice(&data[160..164]).ok_or({
OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::OperatorFeeScalarConversion)
})?;
let operator_fee_constant = U256::try_from_be_slice(&data[164..172]).ok_or({
OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::OperatorFeeConstantConversion)
})?;
let da_footprint_gas_scalar: u16 = U16::try_from_be_slice(&data[172..174])
.ok_or({
OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::DaFootprintGasScalarConversion)
})?
.to();
Ok(L1BlockInfo {
l1_base_fee,
l1_base_fee_scalar,
l1_blob_base_fee: Some(l1_blob_base_fee),
l1_blob_base_fee_scalar: Some(l1_blob_base_fee_scalar),
operator_fee_scalar: Some(operator_fee_scalar),
operator_fee_constant: Some(operator_fee_constant),
da_footprint_gas_scalar: Some(da_footprint_gas_scalar),
..Default::default()
})
}
/// An extension trait for [`L1BlockInfo`] that allows us to calculate the L1 cost of a transaction
@@ -282,6 +354,7 @@ mod tests {
use super::*;
use alloy_consensus::{Block, BlockBody};
use alloy_eips::eip2718::Decodable2718;
use alloy_primitives::keccak256;
use reth_optimism_chainspec::OP_MAINNET;
use reth_optimism_forks::OpHardforks;
use reth_optimism_primitives::OpTransactionSigned;
@@ -308,6 +381,12 @@ mod tests {
assert_eq!(l1_info.l1_blob_base_fee_scalar, None);
}
#[test]
fn test_verify_set_jovian() {
let hash = &keccak256("setL1BlockValuesJovian()")[..4];
assert_eq!(hash, L1_BLOCK_JOVIAN_SELECTOR)
}
#[test]
fn sanity_l1_block_ecotone() {
// rig
@@ -408,4 +487,33 @@ mod tests {
assert_eq!(l1_block_info.operator_fee_scalar, operator_fee_scalar);
assert_eq!(l1_block_info.operator_fee_constant, operator_fee_constant);
}
#[test]
fn parse_l1_info_jovian() {
// L1 block info from a devnet with Isthmus activated
const DATA: &[u8] = &hex!(
"3db6be2b00000558000c5fc500000000000000030000000067a9f765000000000000002900000000000000000000000000000000000000000000000000000000006a6d09000000000000000000000000000000000000000000000000000000000000000172fcc8e8886636bdbe96ba0e4baab67ea7e7811633f52b52e8cf7a5123213b6f000000000000000000000000d3f2c5afb2d76f5579f326b0cd7da5f5a4126c3500004e2000000000000001f4dead"
);
// expected l1 block info verified against expected l1 fee and operator fee for tx.
let l1_base_fee = U256::from(6974729);
let l1_base_fee_scalar = U256::from(1368);
let l1_blob_base_fee = Some(U256::from(1));
let l1_blob_base_fee_scalar = Some(U256::from(810949));
let operator_fee_scalar = Some(U256::from(20000));
let operator_fee_constant = Some(U256::from(500));
let da_footprint_gas_scalar: Option<u16> = Some(U16::from(0xdead).to());
// test
let l1_block_info = parse_l1_info(DATA).unwrap();
assert_eq!(l1_block_info.l1_base_fee, l1_base_fee);
assert_eq!(l1_block_info.l1_base_fee_scalar, l1_base_fee_scalar);
assert_eq!(l1_block_info.l1_blob_base_fee, l1_blob_base_fee);
assert_eq!(l1_block_info.l1_blob_base_fee_scalar, l1_blob_base_fee_scalar);
assert_eq!(l1_block_info.operator_fee_scalar, operator_fee_scalar);
assert_eq!(l1_block_info.operator_fee_constant, operator_fee_constant);
assert_eq!(l1_block_info.da_footprint_gas_scalar, da_footprint_gas_scalar);
}
}

View File

@@ -44,6 +44,7 @@ op-alloy-consensus.workspace = true
alloy-rpc-types-engine.workspace = true
alloy-rpc-types-debug.workspace = true
alloy-consensus.workspace = true
alloy-evm.workspace = true
# misc
derive_more.workspace = true

View File

@@ -1,5 +1,4 @@
//! Optimism payload builder implementation.
use crate::{
config::{OpBuilderConfig, OpDAConfig},
error::OpPayloadBuilderError,
@@ -7,6 +6,7 @@ use crate::{
OpAttributes, OpPayloadBuilderAttributes, OpPayloadPrimitives,
};
use alloy_consensus::{BlockHeader, Transaction, Typed2718};
use alloy_evm::Evm as AlloyEvm;
use alloy_primitives::{B256, U256};
use alloy_rpc_types_debug::ExecutionWitness;
use alloy_rpc_types_engine::PayloadId;
@@ -14,10 +14,12 @@ use reth_basic_payload_builder::*;
use reth_chain_state::ExecutedBlock;
use reth_chainspec::{ChainSpecProvider, EthChainSpec};
use reth_evm::{
block::BlockExecutorFor,
execute::{
BlockBuilder, BlockBuilderOutcome, BlockExecutionError, BlockExecutor, BlockValidationError,
},
ConfigureEvm, Database, Evm,
op_revm::{constants::L1_BLOCK_CONTRACT, L1BlockInfo},
ConfigureEvm, Database,
};
use reth_execution_types::ExecutionOutcome;
use reth_optimism_forks::OpHardforks;
@@ -340,6 +342,11 @@ impl<Txs> OpBuilder<'_, Txs> {
let mut db = State::builder().with_database(db).with_bundle_update().build();
// Load the L1 block contract into the database cache. If the L1 block contract is not
// pre-loaded the database will panic when trying to fetch the DA footprint gas
// scalar.
db.load_cache_account(L1_BLOCK_CONTRACT).map_err(BlockExecutionError::other)?;
let mut builder = ctx.block_builder(&mut db)?;
// 1. apply pre-execution changes
@@ -509,17 +516,27 @@ impl ExecutionInfo {
tx_data_limit: Option<u64>,
block_data_limit: Option<u64>,
tx_gas_limit: u64,
da_footprint_gas_scalar: Option<u16>,
) -> bool {
if tx_data_limit.is_some_and(|da_limit| tx_da_size > da_limit) {
return true;
}
if block_data_limit
.is_some_and(|da_limit| self.cumulative_da_bytes_used + tx_da_size > da_limit)
{
let total_da_bytes_used = self.cumulative_da_bytes_used.saturating_add(tx_da_size);
if block_data_limit.is_some_and(|da_limit| total_da_bytes_used > da_limit) {
return true;
}
// Post Jovian: the tx DA footprint must be less than the block gas limit
if let Some(da_footprint_gas_scalar) = da_footprint_gas_scalar {
let tx_da_footprint =
total_da_bytes_used.saturating_mul(da_footprint_gas_scalar as u64);
if tx_da_footprint > block_gas_limit {
return true;
}
}
self.cumulative_gas_used + tx_gas_limit > block_gas_limit
}
}
@@ -586,7 +603,13 @@ where
pub fn block_builder<'a, DB: Database>(
&'a self,
db: &'a mut State<DB>,
) -> Result<impl BlockBuilder<Primitives = Evm::Primitives> + 'a, PayloadBuilderError> {
) -> Result<
impl BlockBuilder<
Primitives = Evm::Primitives,
Executor: BlockExecutorFor<'a, Evm::BlockExecutorFactory, DB>,
> + 'a,
PayloadBuilderError,
> {
self.evm_config
.builder_for_next_block(
db,
@@ -649,14 +672,18 @@ where
/// Executes the given best transactions and updates the execution info.
///
/// Returns `Ok(Some(())` if the job was cancelled.
pub fn execute_best_transactions(
pub fn execute_best_transactions<Builder>(
&self,
info: &mut ExecutionInfo,
builder: &mut impl BlockBuilder<Primitives = Evm::Primitives>,
builder: &mut Builder,
mut best_txs: impl PayloadTransactions<
Transaction: PoolTransaction<Consensus = TxTy<Evm::Primitives>> + OpPooledTx,
>,
) -> Result<Option<()>, PayloadBuilderError> {
) -> Result<Option<()>, PayloadBuilderError>
where
Builder: BlockBuilder<Primitives = Evm::Primitives>,
<<Builder::Executor as BlockExecutor>::Evm as AlloyEvm>::DB: Database,
{
let block_gas_limit = builder.evm_mut().block().gas_limit();
let block_da_limit = self.da_config.max_da_block_size();
let tx_da_limit = self.da_config.max_da_tx_size();
@@ -666,12 +693,23 @@ where
let interop = tx.interop_deadline();
let tx_da_size = tx.estimated_da_size();
let tx = tx.into_consensus();
let da_footprint_gas_scalar = self
.chain_spec
.is_jovian_active_at_timestamp(self.attributes().timestamp())
.then_some(
L1BlockInfo::fetch_da_footprint_gas_scalar(builder.evm_mut().db_mut()).expect(
"DA footprint should always be available from the database post jovian",
),
);
if info.is_tx_over_limits(
tx_da_size,
block_gas_limit,
tx_da_limit,
block_da_limit,
tx.gas_limit(),
da_footprint_gas_scalar,
) {
// we can't fit this transaction into the block, so we need to mark it as
// invalid which also removes all dependent transaction from

View File

@@ -131,10 +131,14 @@ pub struct OpReceiptFieldsBuilder {
pub l1_blob_base_fee: Option<u128>,
/// The current L1 blob base fee scalar.
pub l1_blob_base_fee_scalar: Option<u128>,
/* ---------------------------------------- Isthmus ---------------------------------------- */
/// The current operator fee scalar.
pub operator_fee_scalar: Option<u128>,
/// The current L1 blob base fee scalar.
pub operator_fee_constant: Option<u128>,
/* ---------------------------------------- Jovian ----------------------------------------- */
/// The current DA footprint gas scalar.
pub da_footprint_gas_scalar: Option<u16>,
}
impl OpReceiptFieldsBuilder {
@@ -154,6 +158,7 @@ impl OpReceiptFieldsBuilder {
l1_blob_base_fee_scalar: None,
operator_fee_scalar: None,
operator_fee_constant: None,
da_footprint_gas_scalar: None,
}
}
@@ -205,6 +210,8 @@ impl OpReceiptFieldsBuilder {
l1_block_info.operator_fee_constant.map(|constant| constant.saturating_to());
}
self.da_footprint_gas_scalar = l1_block_info.da_footprint_gas_scalar;
Ok(self)
}
@@ -236,6 +243,7 @@ impl OpReceiptFieldsBuilder {
l1_blob_base_fee_scalar,
operator_fee_scalar,
operator_fee_constant,
da_footprint_gas_scalar,
} = self;
OpTransactionReceiptFields {
@@ -249,7 +257,7 @@ impl OpReceiptFieldsBuilder {
l1_blob_base_fee_scalar,
operator_fee_scalar,
operator_fee_constant,
da_footprint_gas_scalar: None,
da_footprint_gas_scalar,
},
deposit_nonce,
deposit_receipt_version,
@@ -409,7 +417,7 @@ mod test {
l1_blob_base_fee_scalar,
operator_fee_scalar,
operator_fee_constant,
..
da_footprint_gas_scalar,
} = receipt_meta.l1_block_info;
assert_eq!(
@@ -453,6 +461,11 @@ mod test {
TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.operator_fee_constant,
"incorrect operator fee constant"
);
assert_eq!(
da_footprint_gas_scalar,
TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.da_footprint_gas_scalar,
"incorrect da footprint gas scalar"
);
}
#[test]
@@ -540,7 +553,7 @@ mod test {
l1_blob_base_fee_scalar,
operator_fee_scalar,
operator_fee_constant,
..
da_footprint_gas_scalar,
} = receipt_meta.l1_block_info;
assert_eq!(l1_gas_price, Some(14121491676), "incorrect l1 base fee (former gas price)");
@@ -552,5 +565,6 @@ mod test {
assert_eq!(l1_blob_base_fee_scalar, Some(1055762), "incorrect l1 blob base fee scalar");
assert_eq!(operator_fee_scalar, None, "incorrect operator fee scalar");
assert_eq!(operator_fee_constant, None, "incorrect operator fee constant");
assert_eq!(da_footprint_gas_scalar, None, "incorrect da footprint gas scalar");
}
}

View File

@@ -143,8 +143,8 @@ where
self.block_info.timestamp.store(header.timestamp(), Ordering::Relaxed);
self.block_info.number.store(header.number(), Ordering::Relaxed);
if let Some(Ok(cost_addition)) = tx.map(reth_optimism_evm::extract_l1_info_from_tx) {
*self.block_info.l1_block_info.write() = cost_addition;
if let Some(Ok(l1_block_info)) = tx.map(reth_optimism_evm::extract_l1_info_from_tx) {
*self.block_info.l1_block_info.write() = l1_block_info;
}
if self.chain_spec().is_interop_active_at_timestamp(header.timestamp()) {

View File

@@ -1,5 +1,4 @@
//! Compatibility functions for rpc `Transaction` type.
use crate::{
fees::{CallFees, CallFeesError},
RpcHeader, RpcReceipt, RpcTransaction, RpcTxReq, RpcTypes, SignableTxRequest,