chore(op): type safety in computation of op tx metadata (#10619)

This commit is contained in:
Emilia Hane
2024-09-05 23:21:09 +02:00
committed by GitHub
parent 5af62b75d4
commit 0c2b496503
11 changed files with 312 additions and 148 deletions

2
Cargo.lock generated
View File

@ -7929,6 +7929,7 @@ dependencies = [
"alloy-primitives",
"jsonrpsee-types",
"op-alloy-network",
"op-alloy-rpc-types",
"parking_lot 0.12.3",
"reqwest",
"reth-chainspec",
@ -7937,6 +7938,7 @@ dependencies = [
"reth-network-api",
"reth-node-api",
"reth-node-builder",
"reth-optimism-chainspec",
"reth-primitives",
"reth-provider",
"reth-rpc",

View File

@ -8,11 +8,15 @@ pub trait OptimismHardforks: EthereumHardforks {
self.fork(OptimismHardfork::Bedrock).active_at_block(block_number)
}
/// Convenience method to check if [`Ecotone`](OptimismHardfork::Ecotone) is active at a given
/// timestamp.
/// Returns `true` if [`Ecotone`](OptimismHardfork::Ecotone) is active at given block timestamp.
fn is_ecotone_active_at_timestamp(&self, timestamp: u64) -> bool {
self.fork(OptimismHardfork::Ecotone).active_at_timestamp(timestamp)
}
/// Returns `true` if [`Ecotone`](OptimismHardfork::Ecotone) is active at given block timestamp.
fn is_fjord_active_at_timestamp(&self, timestamp: u64) -> bool {
self.fork(OptimismHardfork::Ecotone).active_at_timestamp(timestamp)
}
}
impl OptimismHardforks for ChainHardforks {}

View File

@ -33,6 +33,7 @@ reth-chainspec.workspace = true
alloy-primitives.workspace = true
op-alloy-network.workspace = true
revm.workspace = true
op-alloy-rpc-types.workspace = true
# async
parking_lot.workspace = true
@ -47,6 +48,9 @@ serde_json.workspace = true
thiserror.workspace = true
tracing.workspace = true
[dev-dependencies]
reth-optimism-chainspec.workspace = true
[features]
optimism = [
"reth-evm-optimism/optimism",

View File

@ -1,5 +1,6 @@
//! RPC errors specific to OP.
use reth_evm_optimism::OptimismBlockExecutionError;
use reth_primitives::revm_primitives::{InvalidTransaction, OptimismInvalidTransaction};
use reth_rpc_eth_api::AsEthApiError;
use reth_rpc_eth_types::EthApiError;
@ -12,6 +13,9 @@ pub enum OpEthApiError {
/// L1 ethereum error.
#[error(transparent)]
Eth(#[from] EthApiError),
/// EVM error originating from invalid optimism data.
#[error(transparent)]
Evm(#[from] OptimismBlockExecutionError),
/// Thrown when calculating L1 gas fee.
#[error("failed to calculate l1 gas fee")]
L1BlockFeeError,
@ -35,11 +39,10 @@ impl AsEthApiError for OpEthApiError {
impl From<OpEthApiError> for jsonrpsee_types::error::ErrorObject<'static> {
fn from(err: OpEthApiError) -> Self {
match err {
OpEthApiError::Eth(err) => err.into(),
OpEthApiError::L1BlockFeeError | OpEthApiError::L1BlockGasError => {
internal_rpc_err(err.to_string())
}
OpEthApiError::InvalidTransaction(err) => err.into(),
OpEthApiError::Eth(_) | OpEthApiError::InvalidTransaction(_) => err.into(),
OpEthApiError::Evm(_) |
OpEthApiError::L1BlockFeeError |
OpEthApiError::L1BlockGasError => internal_rpc_err(err.to_string()),
}
}
}

View File

@ -1,6 +1,7 @@
//! Loads and formats OP block RPC response.
use reth_node_api::FullNodeComponents;
use reth_chainspec::ChainSpec;
use reth_node_api::{FullNodeComponents, NodeTypes};
use reth_primitives::TransactionMeta;
use reth_provider::{BlockReaderIdExt, HeaderProvider};
use reth_rpc_eth_api::{
@ -13,13 +14,12 @@ use reth_rpc_eth_api::{
use reth_rpc_eth_types::{EthStateCache, ReceiptBuilder};
use reth_rpc_types::{AnyTransactionReceipt, BlockId};
use crate::{op_receipt_fields, OpEthApi, OpEthApiError};
use crate::{OpEthApi, OpEthApiError};
impl<N> EthBlocks for OpEthApi<N>
where
Self: LoadBlock + EthApiSpec + LoadTransaction,
Self::Error: From<OpEthApiError>,
N: FullNodeComponents,
Self: EthApiSpec + LoadBlock<Error = OpEthApiError> + LoadTransaction,
N: FullNodeComponents<Types: NodeTypes<ChainSpec = ChainSpec>>,
{
#[inline]
fn provider(&self) -> impl HeaderProvider {
@ -41,14 +41,15 @@ where
let timestamp = block.timestamp;
let block = block.unseal();
let l1_block_info = reth_evm_optimism::extract_l1_info(&block).ok();
let l1_block_info =
reth_evm_optimism::extract_l1_info(&block).map_err(OpEthApiError::from)?;
let receipts = block
.body
.into_iter()
.zip(receipts.iter())
.enumerate()
.map(|(idx, (ref tx, receipt))| {
.map(|(idx, (ref tx, receipt))| -> Result<_, _> {
let meta = TransactionMeta {
tx_hash: tx.hash,
index: idx as u64,
@ -59,14 +60,13 @@ where
timestamp,
};
let optimism_tx_meta =
self.build_op_tx_meta(tx, l1_block_info.clone(), timestamp)?;
let op_tx_meta =
self.build_op_receipt_meta(tx, l1_block_info.clone(), receipt)?;
ReceiptBuilder::new(tx, meta, receipt, &receipts)
.map(|builder| {
op_receipt_fields(builder, tx, receipt, optimism_tx_meta).build()
})
.map_err(Self::Error::from_eth_err)
Ok(ReceiptBuilder::new(tx, meta, receipt, &receipts)
.map_err(Self::Error::from_eth_err)?
.add_other_fields(op_tx_meta.into())
.build())
})
.collect::<Result<Vec<_>, Self::Error>>();
return receipts.map(Some)

View File

@ -10,7 +10,6 @@ pub mod rpc;
use std::{fmt, sync::Arc};
use crate::eth::rpc::SequencerClient;
use alloy_primitives::U256;
use op_alloy_network::AnyNetwork;
use reth_chainspec::ChainSpec;
@ -19,8 +18,8 @@ use reth_network_api::NetworkInfo;
use reth_node_api::{BuilderProvider, FullNodeComponents, FullNodeTypes, NodeTypes};
use reth_node_builder::EthApiBuilderCtx;
use reth_provider::{
BlockIdReader, BlockNumReader, BlockReaderIdExt, ChainSpecProvider, HeaderProvider,
StageCheckpointReader, StateProviderFactory,
BlockIdReader, BlockNumReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider,
HeaderProvider, StageCheckpointReader, StateProviderFactory,
};
use reth_rpc::eth::{core::EthApiInner, DevSigner};
use reth_rpc_eth_api::{
@ -37,7 +36,7 @@ use reth_tasks::{
};
use reth_transaction_pool::TransactionPool;
use crate::OpEthApiError;
use crate::{eth::rpc::SequencerClient, OpEthApiError};
/// Adapter for [`EthApiInner`], which holds all the data required to serve core `eth_` API.
pub type EthApiNodeBackend<N> = EthApiInner<
@ -63,7 +62,12 @@ pub struct OpEthApi<N: FullNodeComponents> {
sequencer_client: Arc<parking_lot::RwLock<Option<SequencerClient>>>,
}
impl<N: FullNodeComponents> OpEthApi<N> {
impl<N> OpEthApi<N>
where
N: FullNodeComponents<
Provider: BlockReaderIdExt + ChainSpecProvider + CanonStateSubscriptions + Clone + 'static,
>,
{
/// Creates a new instance for given context.
#[allow(clippy::type_complexity)]
pub fn with_spawner(ctx: &EthApiBuilderCtx<N>) -> Self {

View File

@ -1,21 +1,24 @@
//! Loads and formats OP receipt RPC response.
use reth_node_api::FullNodeComponents;
use op_alloy_rpc_types::{receipt::L1BlockInfo, OptimismTransactionReceiptFields};
use reth_chainspec::{ChainSpec, OptimismHardforks};
use reth_evm_optimism::RethL1BlockInfo;
use reth_node_api::{FullNodeComponents, NodeTypes};
use reth_primitives::{Receipt, TransactionMeta, TransactionSigned};
use reth_provider::ChainSpecProvider;
use reth_rpc_eth_api::{
helpers::{EthApiSpec, LoadReceipt, LoadTransaction},
FromEthApiError,
};
use reth_rpc_eth_types::{EthApiError, EthStateCache, ReceiptBuilder};
use reth_rpc_types::{optimism::OptimismTransactionReceiptFields, AnyTransactionReceipt};
use reth_rpc_types::AnyTransactionReceipt;
use crate::{OpEthApi, OpEthApiError, OptimismTxMeta};
use crate::{OpEthApi, OpEthApiError};
impl<N> LoadReceipt for OpEthApi<N>
where
Self: EthApiSpec + LoadTransaction,
Self::Error: From<OpEthApiError>,
N: FullNodeComponents,
Self: EthApiSpec + LoadTransaction<Error = OpEthApiError>,
N: FullNodeComponents<Types: NodeTypes<ChainSpec = ChainSpec>>,
{
#[inline]
fn cache(&self) -> &EthStateCache {
@ -35,57 +38,263 @@ where
.ok_or(Self::Error::from_eth_err(EthApiError::UnknownBlockNumber))?;
let block = block.unseal();
let l1_block_info = reth_evm_optimism::extract_l1_info(&block).ok();
let optimism_tx_meta = self.build_op_tx_meta(&tx, l1_block_info, block.timestamp)?;
let l1_block_info =
reth_evm_optimism::extract_l1_info(&block).map_err(OpEthApiError::from)?;
let resp_builder = ReceiptBuilder::new(&tx, meta, &receipt, &receipts)
.map_err(Self::Error::from_eth_err)?;
let resp_builder = op_receipt_fields(resp_builder, &tx, &receipt, optimism_tx_meta);
let op_receipt_meta = self
.build_op_receipt_meta(&tx, l1_block_info, &receipt)
.map_err(OpEthApiError::from)?;
Ok(resp_builder.build())
let receipt_resp = ReceiptBuilder::new(&tx, meta, &receipt, &receipts)
.map_err(Self::Error::from_eth_err)?
.add_other_fields(op_receipt_meta.into())
.build();
Ok(receipt_resp)
}
}
/// Applies OP specific fields to a receipt builder.
pub fn op_receipt_fields(
resp_builder: ReceiptBuilder,
tx: &TransactionSigned,
receipt: &Receipt,
optimism_tx_meta: OptimismTxMeta,
) -> ReceiptBuilder {
let mut op_fields = OptimismTransactionReceiptFields::default();
impl<N> OpEthApi<N>
where
N: FullNodeComponents<Types: NodeTypes<ChainSpec = ChainSpec>>,
{
/// Builds a receipt w.r.t. chain spec.
pub fn build_op_receipt_meta(
&self,
tx: &TransactionSigned,
l1_block_info: revm::L1BlockInfo,
receipt: &Receipt,
) -> Result<OptimismTransactionReceiptFields, OpEthApiError> {
Ok(OpReceiptFieldsBuilder::default()
.l1_block_info(&self.inner.provider().chain_spec(), tx, l1_block_info)?
.deposit_nonce(receipt.deposit_nonce)
.deposit_version(receipt.deposit_receipt_version)
.build())
}
}
if tx.is_deposit() {
op_fields.deposit_nonce = receipt.deposit_nonce;
op_fields.deposit_receipt_version = receipt.deposit_receipt_version;
} else if let Some(l1_block_info) = optimism_tx_meta.l1_block_info {
// always present
op_fields.l1_block_info.l1_fee = optimism_tx_meta.l1_fee;
op_fields.l1_block_info.l1_gas_price = Some(l1_block_info.l1_base_fee.saturating_to());
op_fields.l1_block_info.l1_gas_used = optimism_tx_meta.l1_data_gas.map(|dg| {
dg.saturating_add(
l1_block_info.l1_fee_overhead.unwrap_or_default().saturating_to::<u128>(),
)
});
/// L1 fee and data gas for a non-deposit transaction, or deposit nonce and receipt version for a
/// deposit transaction.
#[derive(Debug, Default, Clone)]
pub struct OpReceiptFieldsBuilder {
/// Block timestamp.
pub l1_block_timestamp: u64,
/// The L1 fee for transaction.
pub l1_fee: Option<u128>,
/// L1 gas used by transaction.
pub l1_data_gas: Option<u128>,
/// L1 fee scalar.
pub l1_fee_scalar: Option<f64>,
/* ---------------------------------------- Bedrock ---------------------------------------- */
/// The base fee of the L1 origin block.
pub l1_base_fee: Option<u128>,
/* --------------------------------------- Regolith ---------------------------------------- */
/// Deposit nonce, if this is a deposit transaction.
pub deposit_nonce: Option<u64>,
/* ---------------------------------------- Canyon ----------------------------------------- */
/// Deposit receipt version, if this is a deposit transaction.
pub deposit_receipt_version: Option<u64>,
/* ---------------------------------------- Ecotone ---------------------------------------- */
/// The current L1 fee scalar.
pub l1_base_fee_scalar: Option<u128>,
/// The current L1 blob base fee.
pub l1_blob_base_fee: Option<u128>,
/// The current L1 blob base fee scalar.
pub l1_blob_base_fee_scalar: Option<u128>,
}
// we know if we're __pre__ Ecotone by checking the l1 fee overhead value which is
// None if ecotone is active
if l1_block_info.l1_fee_overhead.is_some() {
// only pre Ecotone
op_fields.l1_block_info.l1_fee_scalar =
Some(f64::from(l1_block_info.l1_base_fee_scalar) / 1_000_000.0);
} else {
// base fee scalar is enabled post Ecotone
op_fields.l1_block_info.l1_base_fee_scalar =
Some(l1_block_info.l1_base_fee_scalar.saturating_to());
impl OpReceiptFieldsBuilder {
/// Returns a new builder.
pub fn new(block_timestamp: u64) -> Self {
Self { l1_block_timestamp: block_timestamp, ..Default::default() }
}
/// Applies [`L1BlockInfo`](revm::L1BlockInfo).
pub fn l1_block_info(
mut self,
chain_spec: &ChainSpec,
tx: &TransactionSigned,
l1_block_info: revm::L1BlockInfo,
) -> Result<Self, OpEthApiError> {
let raw_tx = tx.envelope_encoded();
let timestamp = self.l1_block_timestamp;
self.l1_fee = Some(
l1_block_info
.l1_tx_data_fee(chain_spec, timestamp, &raw_tx, tx.is_deposit())
.map_err(|_| OpEthApiError::L1BlockFeeError)?
.saturating_to(),
);
self.l1_data_gas = Some(
l1_block_info
.l1_data_gas(chain_spec, timestamp, &raw_tx)
.map_err(|_| OpEthApiError::L1BlockGasError)?
.saturating_add(l1_block_info.l1_fee_overhead.unwrap_or_default())
.saturating_to(),
);
self.l1_fee_scalar = (!chain_spec.hardforks.is_ecotone_active_at_timestamp(timestamp))
.then_some(f64::from(l1_block_info.l1_base_fee_scalar) / 1_000_000.0);
self.l1_base_fee = Some(l1_block_info.l1_base_fee.saturating_to());
self.l1_base_fee_scalar = Some(l1_block_info.l1_base_fee_scalar.saturating_to());
self.l1_blob_base_fee = l1_block_info.l1_blob_base_fee.map(|fee| fee.saturating_to());
self.l1_blob_base_fee_scalar =
l1_block_info.l1_blob_base_fee_scalar.map(|scalar| scalar.saturating_to());
Ok(self)
}
/// Applies deposit transaction metadata: deposit nonce.
pub const fn deposit_nonce(mut self, nonce: Option<u64>) -> Self {
self.deposit_nonce = nonce;
self
}
/// Applies deposit transaction metadata: deposit receipt version.
pub const fn deposit_version(mut self, version: Option<u64>) -> Self {
self.deposit_receipt_version = version;
self
}
/// Builds the [`OptimismTransactionReceiptFields`] object.
pub const fn build(self) -> OptimismTransactionReceiptFields {
let Self {
l1_block_timestamp: _, // used to compute other fields
l1_fee,
l1_data_gas: l1_gas_used,
l1_fee_scalar,
l1_base_fee: l1_gas_price,
deposit_nonce,
deposit_receipt_version,
l1_base_fee_scalar,
l1_blob_base_fee,
l1_blob_base_fee_scalar,
} = self;
OptimismTransactionReceiptFields {
l1_block_info: L1BlockInfo {
l1_gas_price,
l1_gas_used,
l1_fee,
l1_fee_scalar,
l1_base_fee_scalar,
l1_blob_base_fee,
l1_blob_base_fee_scalar,
},
deposit_nonce,
deposit_receipt_version,
}
// 4844 post Ecotone
op_fields.l1_block_info.l1_blob_base_fee =
l1_block_info.l1_blob_base_fee.map(|v| v.saturating_to());
op_fields.l1_block_info.l1_blob_base_fee_scalar =
l1_block_info.l1_blob_base_fee_scalar.map(|v| v.saturating_to());
}
resp_builder.add_other_fields(op_fields.into())
}
#[cfg(test)]
mod test {
use alloy_primitives::hex;
use reth_optimism_chainspec::OP_MAINNET;
use reth_primitives::Block;
use super::*;
/// OP Mainnet transaction at index 0 in block 124665056.
///
/// <https://optimistic.etherscan.io/tx/0x312e290cf36df704a2217b015d6455396830b0ce678b860ebfcc30f41403d7b1>
const TX_SET_L1_BLOCK_OP_MAINNET_BLOCK_124665056: [u8; 251] = hex!("7ef8f8a0683079df94aa5b9cf86687d739a60a9b4f0835e520ec4d664e2e415dca17a6df94deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e200000146b000f79c500000000000000040000000066d052e700000000013ad8a3000000000000000000000000000000000000000000000000000000003ef1278700000000000000000000000000000000000000000000000000000000000000012fdf87b89884a61e74b322bbcf60386f543bfae7827725efaaf0ab1de2294a590000000000000000000000006887246668a3b87f54deb3b94ba47a6f63f32985");
/// OP Mainnet transaction at index 1 in block 124665056.
///
/// <https://optimistic.etherscan.io/tx/0x1059e8004daff32caa1f1b1ef97fe3a07a8cf40508f5b835b66d9420d87c4a4a>
const TX_1_OP_MAINNET_BLOCK_124665056: [u8; 1176] = hex!("02f904940a8303fba78401d6d2798401db2b6d830493e0943e6f4f7866654c18f536170780344aa8772950b680b904246a761202000000000000000000000000087000a300de7200382b55d40045000000e5d60e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000022482ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c0000000000000000000000000000000000000000000000049b9ca9a6943400000000000000000000000000000000000000000000000000000000000000000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024b6b55f250000000000000000000000000000000000000000000000049b9ca9a694340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415ec214a3950bea839a7e6fbb0ba1540ac2076acd50820e2d5ef83d0902cdffb24a47aff7de5190290769c4f0a9c6fabf63012986a0d590b1b571547a8c7050ea1b00000000000000000000000000000000000000000000000000000000000000c080a06db770e6e25a617fe9652f0958bd9bd6e49281a53036906386ed39ec48eadf63a07f47cf51a4a40b4494cf26efc686709a9b03939e20ee27e59682f5faa536667e");
/// Timestamp of OP mainnet block 124665056.
///
/// <https://optimistic.etherscan.io/block/124665056>
const BLOCK_124665056_TIMESTAMP: u64 = 1724928889;
/// L1 block info for transaction at index 1 in block 124665056.
///
/// <https://optimistic.etherscan.io/tx/0x1059e8004daff32caa1f1b1ef97fe3a07a8cf40508f5b835b66d9420d87c4a4a>
const TX_META_TX_1_OP_MAINNET_BLOCK_124665056: OptimismTransactionReceiptFields =
OptimismTransactionReceiptFields {
l1_block_info: L1BlockInfo {
l1_gas_price: Some(1055991687), // since bedrock l1 base fee
l1_gas_used: Some(4471),
l1_fee: Some(24681034813),
l1_fee_scalar: None,
l1_base_fee_scalar: Some(5227),
l1_blob_base_fee: Some(1),
l1_blob_base_fee_scalar: Some(1014213),
},
deposit_nonce: None,
deposit_receipt_version: None,
};
#[test]
fn op_receipt_fields_from_block_and_tx() {
// rig
let tx_0 = TransactionSigned::decode_enveloped(
&mut TX_SET_L1_BLOCK_OP_MAINNET_BLOCK_124665056.as_slice(),
)
.unwrap();
let tx_1 =
TransactionSigned::decode_enveloped(&mut TX_1_OP_MAINNET_BLOCK_124665056.as_slice())
.unwrap();
let block = Block { body: [tx_0, tx_1.clone()].to_vec(), ..Default::default() };
let l1_block_info =
reth_evm_optimism::extract_l1_info(&block).expect("should extract l1 info");
// test
assert!(OP_MAINNET.hardforks.is_fjord_active_at_timestamp(BLOCK_124665056_TIMESTAMP));
let receipt_meta = OpReceiptFieldsBuilder::new(BLOCK_124665056_TIMESTAMP)
.l1_block_info(&OP_MAINNET, &tx_1, l1_block_info)
.expect("should parse revm l1 info")
.build();
let L1BlockInfo {
l1_gas_price,
l1_gas_used,
l1_fee,
l1_fee_scalar,
l1_base_fee_scalar,
l1_blob_base_fee,
l1_blob_base_fee_scalar,
} = receipt_meta.l1_block_info;
assert_eq!(
l1_gas_price, TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_gas_price,
"incorrect l1 base fee (former gas price)"
);
assert_eq!(
l1_gas_used, TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_gas_used,
"incorrect l1 gas used"
);
assert_eq!(
l1_fee, TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_fee,
"incorrect l1 fee"
);
assert_eq!(
l1_fee_scalar, TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_fee_scalar,
"incorrect l1 fee scalar"
);
assert_eq!(
l1_base_fee_scalar,
TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_base_fee_scalar,
"incorrect l1 base fee scalar"
);
assert_eq!(
l1_blob_base_fee,
TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_blob_base_fee,
"incorrect l1 blob base fee"
);
assert_eq!(
l1_blob_base_fee_scalar,
TX_META_TX_1_OP_MAINNET_BLOCK_124665056.l1_block_info.l1_blob_base_fee_scalar,
"incorrect l1 blob base fee scalar"
);
}
}

View File

@ -1,20 +1,16 @@
//! Loads and formats OP transaction RPC response.
use alloy_primitives::{Bytes, B256};
use reth_evm_optimism::RethL1BlockInfo;
use reth_node_api::FullNodeComponents;
use reth_primitives::TransactionSigned;
use reth_provider::{BlockReaderIdExt, TransactionsProvider};
use reth_rpc_eth_api::{
helpers::{EthApiSpec, EthSigner, EthTransactions, LoadTransaction, SpawnBlocking},
EthApiTypes, FromEthApiError,
helpers::{EthSigner, EthTransactions, LoadTransaction, SpawnBlocking},
FromEthApiError,
};
use reth_rpc_eth_types::{utils::recover_raw_transaction, EthStateCache};
use reth_transaction_pool::{PoolTransaction, TransactionOrigin, TransactionPool};
use revm::L1BlockInfo;
use crate::{eth::rpc::SequencerClient, OpEthApi, OpEthApiError};
use crate::{eth::rpc::SequencerClient, OpEthApi};
impl<N> EthTransactions for OpEthApi<N>
where
@ -76,67 +72,6 @@ where
}
}
/// L1 fee and data gas for a transaction, along with the L1 block info.
#[derive(Debug, Default, Clone)]
pub struct OptimismTxMeta {
/// The L1 block info.
pub l1_block_info: Option<L1BlockInfo>,
/// The L1 fee for the block.
pub l1_fee: Option<u128>,
/// The L1 data gas for the block.
pub l1_data_gas: Option<u128>,
}
impl OptimismTxMeta {
/// Creates a new [`OptimismTxMeta`].
pub const fn new(
l1_block_info: Option<L1BlockInfo>,
l1_fee: Option<u128>,
l1_data_gas: Option<u128>,
) -> Self {
Self { l1_block_info, l1_fee, l1_data_gas }
}
}
impl<N> OpEthApi<N>
where
Self: EthApiSpec + LoadTransaction,
<Self as EthApiTypes>::Error: From<OpEthApiError>,
N: FullNodeComponents,
{
/// Builds [`OptimismTxMeta`] object using the provided [`TransactionSigned`], L1 block
/// info and block timestamp. The [`L1BlockInfo`] is used to calculate the l1 fee and l1 data
/// gas for the transaction. If the [`L1BlockInfo`] is not provided, the meta info will be
/// empty.
pub fn build_op_tx_meta(
&self,
tx: &TransactionSigned,
l1_block_info: Option<L1BlockInfo>,
block_timestamp: u64,
) -> Result<OptimismTxMeta, <Self as EthApiTypes>::Error> {
let Some(l1_block_info) = l1_block_info else { return Ok(OptimismTxMeta::default()) };
let (l1_fee, l1_data_gas) = if tx.is_deposit() {
(None, None)
} else {
let envelope_buf = tx.envelope_encoded();
let inner_l1_fee = l1_block_info
.l1_tx_data_fee(&self.chain_spec(), block_timestamp, &envelope_buf, tx.is_deposit())
.map_err(|_| OpEthApiError::L1BlockFeeError)?;
let inner_l1_data_gas = l1_block_info
.l1_data_gas(&self.chain_spec(), block_timestamp, &envelope_buf)
.map_err(|_| OpEthApiError::L1BlockGasError)?;
(
Some(inner_l1_fee.saturating_to::<u128>()),
Some(inner_l1_data_gas.saturating_to::<u128>()),
)
};
Ok(OptimismTxMeta::new(Some(l1_block_info), l1_fee, l1_data_gas))
}
}
impl<N> OpEthApi<N>
where
N: FullNodeComponents,

View File

@ -14,4 +14,4 @@ pub mod error;
pub mod eth;
pub use error::OpEthApiError;
pub use eth::{receipt::op_receipt_fields, transaction::OptimismTxMeta, OpEthApi};
pub use eth::OpEthApi;

View File

@ -131,6 +131,9 @@ pub const EMPTY_ROOT_HASH: B256 =
/// From address from Optimism system txs: `0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001`
pub const OP_SYSTEM_TX_FROM_ADDR: Address = address!("deaddeaddeaddeaddeaddeaddeaddeaddead0001");
/// To address from Optimism system txs: `0x4200000000000000000000000000000000000015`
pub const OP_SYSTEM_TX_TO_ADDR: Address = address!("4200000000000000000000000000000000000015");
/// Transactions root of empty receipts set.
pub const EMPTY_RECEIPTS: B256 = EMPTY_ROOT_HASH;