Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce OpPooledTransaction #13548

Merged
merged 1 commit into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/optimism/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,14 @@ revm = { workspace = true, features = ["secp256k1", "blst", "c-kzg"] }
# ethereum
alloy-eips.workspace = true
alloy-primitives.workspace = true
op-alloy-consensus.workspace = true
op-alloy-rpc-types-engine.workspace = true
alloy-rpc-types-engine.workspace = true
alloy-consensus.workspace = true

# misc
clap.workspace = true
derive_more.workspace = true
serde.workspace = true
eyre.workspace = true
parking_lot.workspace = true
Expand Down
181 changes: 176 additions & 5 deletions crates/optimism/node/src/txpool.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
//! OP transaction pool types
use alloy_consensus::{BlockHeader, Transaction};
use alloy_consensus::{
BlobTransactionSidecar, BlobTransactionValidationError, BlockHeader, Transaction, Typed2718,
};
use alloy_eips::eip2718::Encodable2718;
use alloy_primitives::{Address, TxHash, TxKind, U256};
use op_alloy_consensus::OpTypedTransaction;
use parking_lot::RwLock;
use reth_chainspec::ChainSpec;
use reth_node_api::{Block, BlockBody};
use reth_optimism_evm::RethL1BlockInfo;
use reth_primitives::{GotExpected, InvalidTransactionError, SealedBlock, TransactionSigned};
use reth_optimism_primitives::OpTransactionSigned;
use reth_primitives::{
transaction::TransactionConversionError, GotExpected, InvalidTransactionError, RecoveredTx,
SealedBlock, TransactionSigned,
};
use reth_primitives_traits::SignedTransaction;
use reth_provider::{BlockReaderIdExt, StateProviderFactory};
use reth_revm::L1BlockInfo;
use reth_transaction_pool::{
CoinbaseTipOrdering, EthPoolTransaction, EthPooledTransaction, EthTransactionValidator, Pool,
TransactionOrigin, TransactionValidationOutcome, TransactionValidationTaskExecutor,
TransactionValidator,
CoinbaseTipOrdering, EthBlobTransactionSidecar, EthPoolTransaction, EthPooledTransaction,
EthTransactionValidator, Pool, PoolTransaction, TransactionOrigin,
TransactionValidationOutcome, TransactionValidationTaskExecutor, TransactionValidator,
};
use revm::primitives::{AccessList, KzgSettings};
use std::sync::{
atomic::{AtomicU64, Ordering},
Arc,
Expand All @@ -25,6 +35,167 @@ pub type OpTransactionPool<Client, S> = Pool<
S,
>;

/// Pool transaction for OP.
#[derive(Debug, Clone, derive_more::Deref)]
pub struct OpPooledTransaction(EthPooledTransaction<OpTransactionSigned>);

impl From<RecoveredTx<op_alloy_consensus::OpPooledTransaction>> for OpPooledTransaction {
fn from(tx: RecoveredTx<op_alloy_consensus::OpPooledTransaction>) -> Self {
let encoded_len = tx.encode_2718_len();
let tx = tx.map_transaction(|tx| tx.into());
Self(EthPooledTransaction::new(tx, encoded_len))
}
}

impl TryFrom<RecoveredTx<OpTransactionSigned>> for OpPooledTransaction {
type Error = TransactionConversionError;

fn try_from(value: RecoveredTx<OpTransactionSigned>) -> Result<Self, Self::Error> {
let (tx, signer) = value.to_components();
let pooled: RecoveredTx<op_alloy_consensus::OpPooledTransaction> =
RecoveredTx::from_signed_transaction(tx.try_into()?, signer);
Ok(pooled.into())
}
}

impl From<OpPooledTransaction> for RecoveredTx<OpTransactionSigned> {
fn from(value: OpPooledTransaction) -> Self {
value.0.transaction
}
}

impl PoolTransaction for OpPooledTransaction {
type TryFromConsensusError = <Self as TryFrom<RecoveredTx<Self::Consensus>>>::Error;
type Consensus = OpTransactionSigned;
type Pooled = op_alloy_consensus::OpPooledTransaction;

fn clone_into_consensus(&self) -> RecoveredTx<Self::Consensus> {
self.transaction().clone()
}

fn try_consensus_into_pooled(
tx: RecoveredTx<Self::Consensus>,
) -> Result<RecoveredTx<Self::Pooled>, Self::TryFromConsensusError> {
let (tx, signer) = tx.to_components();
Ok(RecoveredTx::from_signed_transaction(tx.try_into()?, signer))
}

fn hash(&self) -> &TxHash {
self.transaction.tx_hash()
}

fn sender(&self) -> Address {
self.transaction.signer()
}

fn sender_ref(&self) -> &Address {
self.transaction.signer_ref()
}

fn nonce(&self) -> u64 {
self.transaction.nonce()
}

fn cost(&self) -> &U256 {
&self.cost
}

fn gas_limit(&self) -> u64 {
self.transaction.gas_limit()
}

fn max_fee_per_gas(&self) -> u128 {
self.transaction.transaction.max_fee_per_gas()
}

fn access_list(&self) -> Option<&AccessList> {
self.transaction.access_list()
}

fn max_priority_fee_per_gas(&self) -> Option<u128> {
self.transaction.transaction.max_priority_fee_per_gas()
}

fn max_fee_per_blob_gas(&self) -> Option<u128> {
self.transaction.max_fee_per_blob_gas()
}

fn effective_tip_per_gas(&self, base_fee: u64) -> Option<u128> {
self.transaction.effective_tip_per_gas(base_fee)
}

fn priority_fee_or_price(&self) -> u128 {
self.transaction.priority_fee_or_price()
}

fn kind(&self) -> TxKind {
self.transaction.kind()
}

fn is_create(&self) -> bool {
self.transaction.is_create()
}

fn input(&self) -> &[u8] {
self.transaction.input()
}

fn size(&self) -> usize {
self.transaction.transaction.input().len()
}

fn tx_type(&self) -> u8 {
self.transaction.ty()
}

fn encoded_length(&self) -> usize {
self.encoded_length
}

fn chain_id(&self) -> Option<u64> {
self.transaction.chain_id()
}
}

impl EthPoolTransaction for OpPooledTransaction {
fn take_blob(&mut self) -> EthBlobTransactionSidecar {
EthBlobTransactionSidecar::None
}

fn blob_count(&self) -> usize {
0
}

fn try_into_pooled_eip4844(
self,
_sidecar: Arc<BlobTransactionSidecar>,
) -> Option<RecoveredTx<Self::Pooled>> {
None
}

fn try_from_eip4844(
_tx: RecoveredTx<Self::Consensus>,
_sidecar: BlobTransactionSidecar,
) -> Option<Self> {
None
}

fn validate_blob(
&self,
_sidecar: &BlobTransactionSidecar,
_settings: &KzgSettings,
) -> Result<(), BlobTransactionValidationError> {
Err(BlobTransactionValidationError::NotBlobTransaction(self.tx_type()))
}

fn authorization_count(&self) -> usize {
match &self.transaction.transaction {
OpTypedTransaction::Eip7702(tx) => tx.authorization_list.len(),
_ => 0,
}
}
}

/// Validator for Optimism transactions.
#[derive(Debug, Clone)]
pub struct OpTransactionValidator<Client, Tx> {
Expand Down
8 changes: 4 additions & 4 deletions crates/transaction-pool/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1192,20 +1192,20 @@ pub trait EthPoolTransaction: PoolTransaction {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EthPooledTransaction<T = TransactionSigned> {
/// `EcRecovered` transaction, the consensus format.
pub(crate) transaction: RecoveredTx<T>,
pub transaction: RecoveredTx<T>,

/// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`.
/// For legacy transactions: `gas_price * gas_limit + tx_value`.
/// For EIP-4844 blob transactions: `max_fee_per_gas * gas_limit + tx_value +
/// max_blob_fee_per_gas * blob_gas_used`.
pub(crate) cost: U256,
pub cost: U256,

/// This is the RLP length of the transaction, computed when the transaction is added to the
/// pool.
pub(crate) encoded_length: usize,
pub encoded_length: usize,

/// The blob side car for this transaction
pub(crate) blob_sidecar: EthBlobTransactionSidecar,
pub blob_sidecar: EthBlobTransactionSidecar,
}

impl<T: SignedTransaction> EthPooledTransaction<T> {
Expand Down
Loading