From 21bc75df39ee823b3ae9f59623a55d6d74dcd99f Mon Sep 17 00:00:00 2001 From: tedison <76473430+edisontim@users.noreply.github.com> Date: Sun, 24 Nov 2024 04:08:36 -0500 Subject: [PATCH] feat: start implementing OpTransaction (#12529) Co-authored-by: Emilia Hane --- crates/optimism/primitives/Cargo.toml | 8 +- crates/optimism/primitives/src/lib.rs | 4 +- .../primitives/src/transaction/mod.rs | 173 ++++++++++++++++++ .../src/{ => transaction}/tx_type.rs | 0 crates/primitives/src/transaction/mod.rs | 6 + crates/primitives/src/transaction/tx_type.rs | 20 +- 6 files changed, 204 insertions(+), 7 deletions(-) create mode 100644 crates/optimism/primitives/src/transaction/mod.rs rename crates/optimism/primitives/src/{ => transaction}/tx_type.rs (100%) diff --git a/crates/optimism/primitives/Cargo.toml b/crates/optimism/primitives/Cargo.toml index 33f936b2f..e7200c40e 100644 --- a/crates/optimism/primitives/Cargo.toml +++ b/crates/optimism/primitives/Cargo.toml @@ -16,7 +16,7 @@ workspace = true reth-node-types.workspace = true reth-primitives.workspace = true reth-primitives-traits.workspace = true -reth-codecs = { workspace = true, optional = true } +reth-codecs = { workspace = true, optional = true, features = ["optimism"] } # ethereum alloy-primitives.workspace = true @@ -34,7 +34,7 @@ serde = { workspace = true, optional = true } # misc derive_more.workspace = true -# test-utils +# test arbitrary = { workspace = true, features = ["derive"], optional = true } [dev-dependencies] @@ -66,7 +66,7 @@ serde = [ "alloy-consensus/serde", "alloy-eips/serde", "bytes/serde", - "reth-codecs/serde", + "reth-codecs?/serde", "op-alloy-consensus/serde", ] arbitrary = [ @@ -78,4 +78,4 @@ arbitrary = [ "alloy-consensus/arbitrary", "alloy-eips/arbitrary", "alloy-primitives/arbitrary", -] \ No newline at end of file +] diff --git a/crates/optimism/primitives/src/lib.rs b/crates/optimism/primitives/src/lib.rs index 334440ea1..0f4608a8e 100644 --- a/crates/optimism/primitives/src/lib.rs +++ b/crates/optimism/primitives/src/lib.rs @@ -9,9 +9,9 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod bedrock; -pub mod tx_type; +pub mod transaction; -pub use tx_type::OpTxType; +pub use transaction::{tx_type::OpTxType, OpTransaction}; use alloy_consensus::Header; use reth_node_types::NodePrimitives; diff --git a/crates/optimism/primitives/src/transaction/mod.rs b/crates/optimism/primitives/src/transaction/mod.rs new file mode 100644 index 000000000..070b3d984 --- /dev/null +++ b/crates/optimism/primitives/src/transaction/mod.rs @@ -0,0 +1,173 @@ +//! Wrapper of [`OpTypedTransaction`], that implements reth database encoding [`Compact`]. + +pub mod tx_type; + +use alloy_primitives::{bytes, Bytes, TxKind, Uint, B256}; + +use alloy_consensus::{constants::EIP7702_TX_TYPE_ID, TxLegacy}; +use alloy_eips::{eip2930::AccessList, eip7702::SignedAuthorization}; +use derive_more::{Deref, From}; +use op_alloy_consensus::{OpTypedTransaction, DEPOSIT_TX_TYPE_ID}; +use reth_codecs::Compact; +use reth_primitives::transaction::{ + COMPACT_EXTENDED_IDENTIFIER_FLAG, COMPACT_IDENTIFIER_EIP1559, COMPACT_IDENTIFIER_EIP2930, + COMPACT_IDENTIFIER_LEGACY, +}; +use reth_primitives_traits::InMemorySize; + +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq, Deref, Hash, From)] +/// Optimistic transaction. +pub struct OpTransaction(OpTypedTransaction); + +impl Default for OpTransaction { + fn default() -> Self { + Self(OpTypedTransaction::Legacy(TxLegacy::default())) + } +} + +impl Compact for OpTransaction { + fn to_compact(&self, out: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + match &self.0 { + OpTypedTransaction::Legacy(tx) => tx.to_compact(out), + OpTypedTransaction::Eip2930(tx) => tx.to_compact(out), + OpTypedTransaction::Eip1559(tx) => tx.to_compact(out), + OpTypedTransaction::Eip7702(tx) => tx.to_compact(out), + OpTypedTransaction::Deposit(tx) => tx.to_compact(out), + } + } + + fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) { + use bytes::Buf; + + match identifier { + COMPACT_IDENTIFIER_LEGACY => { + let (tx, buf) = TxLegacy::from_compact(buf, buf.len()); + (Self(OpTypedTransaction::Legacy(tx)), buf) + } + COMPACT_IDENTIFIER_EIP2930 => { + let (tx, buf) = + alloy_consensus::transaction::TxEip2930::from_compact(buf, buf.len()); + (Self(OpTypedTransaction::Eip2930(tx)), buf) + } + COMPACT_IDENTIFIER_EIP1559 => { + let (tx, buf) = + alloy_consensus::transaction::TxEip1559::from_compact(buf, buf.len()); + (Self(OpTypedTransaction::Eip1559(tx)), buf) + } + COMPACT_EXTENDED_IDENTIFIER_FLAG => { + // An identifier of 3 indicates that the transaction type did not fit into + // the backwards compatible 2 bit identifier, their transaction types are + // larger than 2 bits (eg. 4844 and Deposit Transactions). In this case, + // we need to read the concrete transaction type from the buffer by + // reading the full 8 bits (single byte) and match on this transaction type. + let identifier = buf.get_u8(); + match identifier { + EIP7702_TX_TYPE_ID => { + let (tx, buf) = + alloy_consensus::transaction::TxEip7702::from_compact(buf, buf.len()); + (Self(OpTypedTransaction::Eip7702(tx)), buf) + } + DEPOSIT_TX_TYPE_ID => { + let (tx, buf) = op_alloy_consensus::TxDeposit::from_compact(buf, buf.len()); + (Self(OpTypedTransaction::Deposit(tx)), buf) + } + _ => unreachable!( + "Junk data in database: unknown Transaction variant: {identifier}" + ), + } + } + _ => unreachable!("Junk data in database: unknown Transaction variant: {identifier}"), + } + } +} + +impl alloy_consensus::Transaction for OpTransaction { + fn chain_id(&self) -> Option { + self.0.chain_id() + } + + fn nonce(&self) -> u64 { + self.0.nonce() + } + + fn gas_limit(&self) -> u64 { + self.0.gas_limit() + } + + fn gas_price(&self) -> Option { + self.0.gas_price() + } + + fn max_fee_per_gas(&self) -> u128 { + self.0.max_fee_per_gas() + } + + fn max_priority_fee_per_gas(&self) -> Option { + self.0.max_priority_fee_per_gas() + } + + fn max_fee_per_blob_gas(&self) -> Option { + self.0.max_fee_per_blob_gas() + } + + fn priority_fee_or_price(&self) -> u128 { + self.0.priority_fee_or_price() + } + + fn kind(&self) -> TxKind { + self.0.kind() + } + + fn value(&self) -> Uint<256, 4> { + self.0.value() + } + + fn input(&self) -> &Bytes { + self.0.input() + } + + fn ty(&self) -> u8 { + self.0.ty() + } + + fn access_list(&self) -> Option<&AccessList> { + self.0.access_list() + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + self.0.blob_versioned_hashes() + } + + fn authorization_list(&self) -> Option<&[SignedAuthorization]> { + self.0.authorization_list() + } + + fn is_dynamic_fee(&self) -> bool { + self.0.is_dynamic_fee() + } + + fn effective_gas_price(&self, base_fee: Option) -> u128 { + self.0.effective_gas_price(base_fee) + } + + fn effective_tip_per_gas(&self, base_fee: u64) -> Option { + self.0.effective_tip_per_gas(base_fee) + } +} + +impl InMemorySize for OpTransaction { + fn size(&self) -> usize { + match &self.0 { + OpTypedTransaction::Legacy(tx) => tx.size(), + OpTypedTransaction::Eip2930(tx) => tx.size(), + OpTypedTransaction::Eip1559(tx) => tx.size(), + OpTypedTransaction::Eip7702(tx) => tx.size(), + OpTypedTransaction::Deposit(tx) => tx.size(), + } + } +} diff --git a/crates/optimism/primitives/src/tx_type.rs b/crates/optimism/primitives/src/transaction/tx_type.rs similarity index 100% rename from crates/optimism/primitives/src/tx_type.rs rename to crates/optimism/primitives/src/transaction/tx_type.rs diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index ca03bfe4f..b8a3f4a71 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -56,6 +56,12 @@ pub mod signature; pub(crate) mod util; +#[cfg(any(test, feature = "reth-codec"))] +pub use tx_type::{ + COMPACT_EXTENDED_IDENTIFIER_FLAG, COMPACT_IDENTIFIER_EIP1559, COMPACT_IDENTIFIER_EIP2930, + COMPACT_IDENTIFIER_LEGACY, +}; + /// Expected number of transactions where we can expect a speed-up by recovering the senders in /// parallel. pub static PARALLEL_SENDER_RECOVERY_THRESHOLD: LazyLock = diff --git a/crates/primitives/src/transaction/tx_type.rs b/crates/primitives/src/transaction/tx_type.rs index 0e344374d..784a976ab 100644 --- a/crates/primitives/src/transaction/tx_type.rs +++ b/crates/primitives/src/transaction/tx_type.rs @@ -8,6 +8,24 @@ use derive_more::Display; use reth_primitives_traits::InMemorySize; use serde::{Deserialize, Serialize}; +/// Identifier parameter for legacy transaction +#[cfg(any(test, feature = "reth-codec"))] +pub const COMPACT_IDENTIFIER_LEGACY: usize = 0; + +/// Identifier parameter for EIP-2930 transaction +#[cfg(any(test, feature = "reth-codec"))] +pub const COMPACT_IDENTIFIER_EIP2930: usize = 1; + +/// Identifier parameter for EIP-1559 transaction +#[cfg(any(test, feature = "reth-codec"))] +pub const COMPACT_IDENTIFIER_EIP1559: usize = 2; + +/// For backwards compatibility purposes only 2 bits of the type are encoded in the identifier +/// parameter. In the case of a [`COMPACT_EXTENDED_IDENTIFIER_FLAG`], the full transaction type is +/// read from the buffer as a single byte. +#[cfg(any(test, feature = "reth-codec"))] +pub const COMPACT_EXTENDED_IDENTIFIER_FLAG: usize = 3; + /// Transaction Type /// /// Currently being used as 2-bit type when encoding it to `reth_codecs::Compact` on @@ -256,7 +274,7 @@ impl Decodable for TxType { mod tests { use super::*; use alloy_primitives::hex; - use reth_codecs::{txtype::*, Compact}; + use reth_codecs::Compact; use reth_primitives_traits::TxType as _; use rstest::rstest;