From aeb2c6e7312cbb0a6285c1175ea0c42b7591c669 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Tue, 17 Feb 2026 13:15:14 -0800 Subject: [PATCH] chore(primitives): remove legacy transaction roundtrip tests (#22292) Co-authored-by: Amp --- Cargo.lock | 28 +- crates/ethereum/primitives/Cargo.toml | 10 - crates/ethereum/primitives/src/lib.rs | 4 - crates/ethereum/primitives/src/transaction.rs | 719 ------------------ 4 files changed, 12 insertions(+), 749 deletions(-) delete mode 100644 crates/ethereum/primitives/src/transaction.rs diff --git a/Cargo.lock b/Cargo.lock index 05da312241..353682df37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -419,9 +419,9 @@ dependencies = [ [[package]] name = "alloy-node-bindings" -version = "1.6.3" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ce6930ce52e43b0768dc99ceeff5cb9e673e8c9f87d926914cd028b2e3f7233" +checksum = "e5a65d1ef42da862d7a528e95c170fa3066a81f23fbb9e778c213cf5e4063a0f" dependencies = [ "alloy-genesis", "alloy-hardforks 0.2.13", @@ -2211,9 +2211,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.58" +version = "4.5.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806" +checksum = "c5caf74d17c3aec5495110c34cc3f78644bfa89af6c8993ed4de2790e49b6499" dependencies = [ "clap_builder", "clap_derive", @@ -2221,9 +2221,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.58" +version = "4.5.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2" +checksum = "370daa45065b80218950227371916a1633217ae42b2715b2287b606dcd618e24" dependencies = [ "anstream", "anstyle", @@ -7270,9 +7270,9 @@ dependencies = [ [[package]] name = "rapidhash" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84816e4c99c467e92cf984ee6328caa976dfecd33a673544489d79ca2caaefe5" +checksum = "111325c42c4bafae99e777cd77b40dea9a2b30c69e9d8c74b6eccd7fba4337de" dependencies = [ "rand 0.9.2", "rustversion", @@ -8708,17 +8708,13 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde", "arbitrary", - "derive_more", "proptest", "proptest-arbitrary-interop", - "rand 0.8.5", "rand 0.9.2", "reth-codecs", "reth-primitives-traits", "reth-zstd-compressors", - "secp256k1 0.30.0", "serde", "serde_json", "serde_with", @@ -12320,9 +12316,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.8+spec-1.1.0" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0742ff5ff03ea7e67c8ae6c93cac239e0d9784833362da3f9a9c1da8dfefcbdc" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ "winnow", ] @@ -12770,9 +12766,9 @@ checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" diff --git a/crates/ethereum/primitives/Cargo.toml b/crates/ethereum/primitives/Cargo.toml index 74d1de3e0c..52517e258c 100644 --- a/crates/ethereum/primitives/Cargo.toml +++ b/crates/ethereum/primitives/Cargo.toml @@ -26,17 +26,13 @@ alloy-rpc-types-eth = { workspace = true, optional = true } serde = { workspace = true, optional = true } [dev-dependencies] -alloy-serde.workspace = true -derive_more.workspace = true arbitrary.workspace = true proptest.workspace = true proptest-arbitrary-interop.workspace = true -rand_08.workspace = true rand.workspace = true alloy-rlp.workspace = true reth-codecs = { workspace = true, features = ["test-utils"] } reth-zstd-compressors.workspace = true -secp256k1 = { workspace = true, features = ["rand"] } alloy-consensus = { workspace = true, features = ["serde", "arbitrary"] } serde_json.workspace = true serde_with.workspace = true @@ -55,10 +51,7 @@ std = [ "alloy-eips/std", "alloy-rpc-types-eth?/std", "alloy-rlp/std", - "alloy-serde/std", - "derive_more/std", "reth-zstd-compressors/std", - "secp256k1/std", "serde_json/std", "serde_with/std", ] @@ -75,7 +68,6 @@ arbitrary = [ "reth-primitives-traits/arbitrary", "alloy-eips/arbitrary", "alloy-rpc-types-eth?/arbitrary", - "alloy-serde/arbitrary", ] serde-bincode-compat = [ "alloy-consensus/serde-bincode-compat", @@ -91,8 +83,6 @@ serde = [ "reth-codecs?/serde", "reth-primitives-traits/serde", "alloy-rpc-types-eth?/serde", - "rand_08/serde", "rand/serde", - "secp256k1/serde", ] rpc = ["dep:alloy-rpc-types-eth"] diff --git a/crates/ethereum/primitives/src/lib.rs b/crates/ethereum/primitives/src/lib.rs index 6e8f03aad6..f6c3e1dd30 100644 --- a/crates/ethereum/primitives/src/lib.rs +++ b/crates/ethereum/primitives/src/lib.rs @@ -16,10 +16,6 @@ use reth_codecs as _; mod receipt; pub use receipt::*; -/// Kept for consistency tests -#[cfg(test)] -mod transaction; - pub use alloy_consensus::{transaction::PooledTransaction, TxType}; use alloy_consensus::{TxEip4844, TxEip4844WithSidecar}; use alloy_eips::eip7594::BlobTransactionSidecarVariant; diff --git a/crates/ethereum/primitives/src/transaction.rs b/crates/ethereum/primitives/src/transaction.rs deleted file mode 100644 index a575b21b60..0000000000 --- a/crates/ethereum/primitives/src/transaction.rs +++ /dev/null @@ -1,719 +0,0 @@ -//! This file contains the legacy reth `TransactionSigned` type that has been replaced with -//! alloy's TxEnvelope To test for consistency this is kept - -extern crate alloc; - -use alloc::vec::Vec; -use alloy_consensus::{ - transaction::{RlpEcdsaDecodableTx, RlpEcdsaEncodableTx, SignerRecoverable, TxHashRef}, - EthereumTxEnvelope, SignableTransaction, Signed, TxEip1559, TxEip2930, TxEip4844, TxEip7702, - TxLegacy, TxType, Typed2718, -}; -use alloy_eips::{ - eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718, IsTyped2718}, - eip2930::AccessList, - eip7702::SignedAuthorization, -}; -use alloy_primitives::{ - bytes::BufMut, keccak256, Address, Bytes, ChainId, Signature, TxHash, TxKind, B256, U256, -}; -use alloy_rlp::{Decodable, Encodable}; -use core::hash::{Hash, Hasher}; -use reth_primitives_traits::{ - crypto::secp256k1::{recover_signer, recover_signer_unchecked}, - sync::OnceLock, - transaction::signed::RecoveryError, - InMemorySize, SignedTransaction, -}; - -macro_rules! delegate { - ($self:expr => $tx:ident.$method:ident($($arg:expr),*)) => { - match $self { - Transaction::Legacy($tx) => $tx.$method($($arg),*), - Transaction::Eip2930($tx) => $tx.$method($($arg),*), - Transaction::Eip1559($tx) => $tx.$method($($arg),*), - Transaction::Eip4844($tx) => $tx.$method($($arg),*), - Transaction::Eip7702($tx) => $tx.$method($($arg),*), - } - }; -} - -/// A raw transaction. -/// -/// Transaction types were introduced in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718). -#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::From)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))] -pub enum Transaction { - /// Legacy transaction (type `0x0`). - /// - /// Traditional Ethereum transactions, containing parameters `nonce`, `gasPrice`, `gasLimit`, - /// `to`, `value`, `data`, `v`, `r`, and `s`. - /// - /// These transactions do not utilize access lists nor do they incorporate EIP-1559 fee market - /// changes. - Legacy(TxLegacy), - /// Transaction with an [`AccessList`] ([EIP-2930](https://eips.ethereum.org/EIPS/eip-2930)), type `0x1`. - /// - /// The `accessList` specifies an array of addresses and storage keys that the transaction - /// plans to access, enabling gas savings on cross-contract calls by pre-declaring the accessed - /// contract and storage slots. - Eip2930(TxEip2930), - /// A transaction with a priority fee ([EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)), type `0x2`. - /// - /// Unlike traditional transactions, EIP-1559 transactions use an in-protocol, dynamically - /// changing base fee per gas, adjusted at each block to manage network congestion. - /// - /// - `maxPriorityFeePerGas`, specifying the maximum fee above the base fee the sender is - /// willing to pay - /// - `maxFeePerGas`, setting the maximum total fee the sender is willing to pay. - /// - /// The base fee is burned, while the priority fee is paid to the miner who includes the - /// transaction, incentivizing miners to include transactions with higher priority fees per - /// gas. - Eip1559(TxEip1559), - /// Shard Blob Transactions ([EIP-4844](https://eips.ethereum.org/EIPS/eip-4844)), type `0x3`. - /// - /// Shard Blob Transactions introduce a new transaction type called a blob-carrying transaction - /// to reduce gas costs. These transactions are similar to regular Ethereum transactions but - /// include additional data called a blob. - /// - /// Blobs are larger (~125 kB) and cheaper than the current calldata, providing an immutable - /// and read-only memory for storing transaction data. - /// - /// EIP-4844, also known as proto-danksharding, implements the framework and logic of - /// danksharding, introducing new transaction formats and verification rules. - Eip4844(TxEip4844), - /// EOA Set Code Transactions ([EIP-7702](https://eips.ethereum.org/EIPS/eip-7702)), type `0x4`. - /// - /// EOA Set Code Transactions give the ability to set contract code for an EOA in perpetuity - /// until re-assigned by the same EOA. This allows for adding smart contract functionality to - /// the EOA. - Eip7702(TxEip7702), -} - -impl Transaction { - /// Returns [`TxType`] of the transaction. - pub const fn tx_type(&self) -> TxType { - match self { - Self::Legacy(_) => TxType::Legacy, - Self::Eip2930(_) => TxType::Eip2930, - Self::Eip1559(_) => TxType::Eip1559, - Self::Eip4844(_) => TxType::Eip4844, - Self::Eip7702(_) => TxType::Eip7702, - } - } - - #[cfg(test)] - const fn input_mut(&mut self) -> &mut Bytes { - match self { - Self::Legacy(tx) => &mut tx.input, - Self::Eip2930(tx) => &mut tx.input, - Self::Eip1559(tx) => &mut tx.input, - Self::Eip4844(tx) => &mut tx.input, - Self::Eip7702(tx) => &mut tx.input, - } - } -} - -impl Typed2718 for Transaction { - fn ty(&self) -> u8 { - delegate!(self => tx.ty()) - } -} - -impl alloy_consensus::Transaction for Transaction { - fn chain_id(&self) -> Option { - delegate!(self => tx.chain_id()) - } - - fn nonce(&self) -> u64 { - delegate!(self => tx.nonce()) - } - - fn gas_limit(&self) -> u64 { - delegate!(self => tx.gas_limit()) - } - - fn gas_price(&self) -> Option { - delegate!(self => tx.gas_price()) - } - - fn max_fee_per_gas(&self) -> u128 { - delegate!(self => tx.max_fee_per_gas()) - } - - fn max_priority_fee_per_gas(&self) -> Option { - delegate!(self => tx.max_priority_fee_per_gas()) - } - - fn max_fee_per_blob_gas(&self) -> Option { - delegate!(self => tx.max_fee_per_blob_gas()) - } - - fn priority_fee_or_price(&self) -> u128 { - delegate!(self => tx.priority_fee_or_price()) - } - - fn effective_gas_price(&self, base_fee: Option) -> u128 { - delegate!(self => tx.effective_gas_price(base_fee)) - } - - fn is_dynamic_fee(&self) -> bool { - delegate!(self => tx.is_dynamic_fee()) - } - - fn kind(&self) -> alloy_primitives::TxKind { - delegate!(self => tx.kind()) - } - - fn is_create(&self) -> bool { - delegate!(self => tx.is_create()) - } - - fn value(&self) -> alloy_primitives::U256 { - delegate!(self => tx.value()) - } - - fn input(&self) -> &alloy_primitives::Bytes { - delegate!(self => tx.input()) - } - - fn access_list(&self) -> Option<&alloy_eips::eip2930::AccessList> { - delegate!(self => tx.access_list()) - } - - fn blob_versioned_hashes(&self) -> Option<&[B256]> { - delegate!(self => tx.blob_versioned_hashes()) - } - - fn authorization_list(&self) -> Option<&[alloy_eips::eip7702::SignedAuthorization]> { - delegate!(self => tx.authorization_list()) - } -} - -impl SignableTransaction for Transaction { - fn set_chain_id(&mut self, chain_id: alloy_primitives::ChainId) { - delegate!(self => tx.set_chain_id(chain_id)) - } - - fn encode_for_signing(&self, out: &mut dyn alloy_rlp::BufMut) { - delegate!(self => tx.encode_for_signing(out)) - } - - fn payload_len_for_signature(&self) -> usize { - delegate!(self => tx.payload_len_for_signature()) - } - - fn into_signed(self, signature: Signature) -> Signed { - let tx_hash = delegate!(&self => tx.tx_hash(&signature)); - Signed::new_unchecked(self, signature, tx_hash) - } -} - -impl InMemorySize for Transaction { - fn size(&self) -> usize { - delegate!(self => tx.size()) - } -} - -#[cfg(any(test, feature = "reth-codec"))] -impl reth_codecs::Compact for Transaction { - // Serializes the TxType to the buffer if necessary, returning 2 bits of the type as an - // identifier instead of the length. - fn to_compact(&self, buf: &mut B) -> usize - where - B: alloy_rlp::bytes::BufMut + AsMut<[u8]>, - { - let identifier = self.tx_type().to_compact(buf); - delegate!(self => tx.to_compact(buf)); - identifier - } - - // 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. - // - // # Panics - // - // A panic will be triggered if an identifier larger than 3 is passed from the database. For - // optimism an identifier with value [`DEPOSIT_TX_TYPE_ID`] is allowed. - fn from_compact(buf: &[u8], identifier: usize) -> (Self, &[u8]) { - let (tx_type, buf) = TxType::from_compact(buf, identifier); - - match tx_type { - TxType::Legacy => { - let (tx, buf) = TxLegacy::from_compact(buf, buf.len()); - (Self::Legacy(tx), buf) - } - TxType::Eip2930 => { - let (tx, buf) = TxEip2930::from_compact(buf, buf.len()); - (Self::Eip2930(tx), buf) - } - TxType::Eip1559 => { - let (tx, buf) = TxEip1559::from_compact(buf, buf.len()); - (Self::Eip1559(tx), buf) - } - TxType::Eip4844 => { - let (tx, buf) = TxEip4844::from_compact(buf, buf.len()); - (Self::Eip4844(tx), buf) - } - TxType::Eip7702 => { - let (tx, buf) = TxEip7702::from_compact(buf, buf.len()); - (Self::Eip7702(tx), buf) - } - } - } -} - -impl RlpEcdsaEncodableTx for Transaction { - fn rlp_encoded_fields_length(&self) -> usize { - delegate!(self => tx.rlp_encoded_fields_length()) - } - - fn rlp_encode_fields(&self, out: &mut dyn BufMut) { - delegate!(self => tx.rlp_encode_fields(out)) - } - - fn eip2718_encode_with_type(&self, signature: &Signature, _ty: u8, out: &mut dyn BufMut) { - delegate!(self => tx.eip2718_encode_with_type(signature, tx.ty(), out)) - } - - fn eip2718_encode(&self, signature: &Signature, out: &mut dyn BufMut) { - delegate!(self => tx.eip2718_encode(signature, out)) - } - - fn network_encode_with_type(&self, signature: &Signature, _ty: u8, out: &mut dyn BufMut) { - delegate!(self => tx.network_encode_with_type(signature, tx.ty(), out)) - } - - fn network_encode(&self, signature: &Signature, out: &mut dyn BufMut) { - delegate!(self => tx.network_encode(signature, out)) - } - - fn tx_hash_with_type(&self, signature: &Signature, _ty: u8) -> TxHash { - delegate!(self => tx.tx_hash_with_type(signature, tx.ty())) - } - - fn tx_hash(&self, signature: &Signature) -> TxHash { - delegate!(self => tx.tx_hash(signature)) - } -} - -/// Signed Ethereum transaction. -#[derive(Debug, Clone, Eq, derive_more::AsRef, derive_more::Deref)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(rlp))] -#[cfg_attr(feature = "test-utils", derive(derive_more::DerefMut))] -#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] -pub struct TransactionSigned { - /// Transaction hash - #[cfg_attr(feature = "serde", serde(skip))] - hash: OnceLock, - /// The transaction signature values - signature: Signature, - /// Raw transaction info - #[deref] - #[as_ref] - #[cfg_attr(feature = "test-utils", deref_mut)] - transaction: Transaction, -} - -impl TransactionSigned { - fn recalculate_hash(&self) -> B256 { - keccak256(self.encoded_2718()) - } -} - -impl Hash for TransactionSigned { - fn hash(&self, state: &mut H) { - self.signature.hash(state); - self.transaction.hash(state); - } -} - -impl PartialEq for TransactionSigned { - fn eq(&self, other: &Self) -> bool { - self.signature == other.signature && - self.transaction == other.transaction && - self.tx_hash() == other.tx_hash() - } -} - -impl TransactionSigned { - /// Creates a new signed transaction from the given transaction, signature and hash. - pub fn new(transaction: Transaction, signature: Signature, hash: B256) -> Self { - Self { hash: hash.into(), signature, transaction } - } - - /// Returns the transaction hash. - #[inline] - pub fn hash(&self) -> &B256 { - self.hash.get_or_init(|| self.recalculate_hash()) - } - - /// Splits the transaction into parts. - pub fn into_parts(self) -> (Transaction, Signature, B256) { - let hash = *self.hash.get_or_init(|| self.recalculate_hash()); - (self.transaction, self.signature, hash) - } -} - -impl Typed2718 for TransactionSigned { - fn ty(&self) -> u8 { - self.transaction.ty() - } -} - -impl alloy_consensus::Transaction for TransactionSigned { - fn chain_id(&self) -> Option { - self.transaction.chain_id() - } - - fn nonce(&self) -> u64 { - self.transaction.nonce() - } - - fn gas_limit(&self) -> u64 { - self.transaction.gas_limit() - } - - fn gas_price(&self) -> Option { - self.transaction.gas_price() - } - - fn max_fee_per_gas(&self) -> u128 { - self.transaction.max_fee_per_gas() - } - - fn max_priority_fee_per_gas(&self) -> Option { - self.transaction.max_priority_fee_per_gas() - } - - fn max_fee_per_blob_gas(&self) -> Option { - self.transaction.max_fee_per_blob_gas() - } - - fn priority_fee_or_price(&self) -> u128 { - self.transaction.priority_fee_or_price() - } - - fn effective_gas_price(&self, base_fee: Option) -> u128 { - self.transaction.effective_gas_price(base_fee) - } - - fn is_dynamic_fee(&self) -> bool { - self.transaction.is_dynamic_fee() - } - - fn kind(&self) -> TxKind { - self.transaction.kind() - } - - fn is_create(&self) -> bool { - self.transaction.is_create() - } - - fn value(&self) -> U256 { - self.transaction.value() - } - - fn input(&self) -> &Bytes { - self.transaction.input() - } - - fn access_list(&self) -> Option<&AccessList> { - self.transaction.access_list() - } - - fn blob_versioned_hashes(&self) -> Option<&[B256]> { - self.transaction.blob_versioned_hashes() - } - - fn authorization_list(&self) -> Option<&[SignedAuthorization]> { - self.transaction.authorization_list() - } -} - -impl From> for TransactionSigned { - fn from(value: Signed) -> Self { - let (tx, sig, hash) = value.into_parts(); - Self::new(tx, sig, hash) - } -} - -impl From for EthereumTxEnvelope { - fn from(value: TransactionSigned) -> Self { - let (tx, signature, hash) = value.into_parts(); - match tx { - Transaction::Legacy(tx) => Signed::new_unchecked(tx, signature, hash).into(), - Transaction::Eip2930(tx) => Signed::new_unchecked(tx, signature, hash).into(), - Transaction::Eip1559(tx) => Signed::new_unchecked(tx, signature, hash).into(), - Transaction::Eip4844(tx) => Signed::new_unchecked(tx, signature, hash).into(), - Transaction::Eip7702(tx) => Signed::new_unchecked(tx, signature, hash).into(), - } - } -} - -#[cfg(any(test, feature = "arbitrary"))] -impl<'a> arbitrary::Arbitrary<'a> for TransactionSigned { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - #[expect(unused_mut)] - let mut transaction = Transaction::arbitrary(u)?; - - let secp = secp256k1::Secp256k1::new(); - let key_pair = secp256k1::Keypair::new(&secp, &mut rand_08::thread_rng()); - let signature = reth_primitives_traits::crypto::secp256k1::sign_message( - B256::from_slice(&key_pair.secret_bytes()[..]), - transaction.signature_hash(), - ) - .unwrap(); - - Ok(Self { transaction, signature, hash: Default::default() }) - } -} - -impl InMemorySize for TransactionSigned { - fn size(&self) -> usize { - let Self { hash: _, signature, transaction } = self; - self.tx_hash().size() + signature.size() + transaction.size() - } -} - -impl Encodable2718 for TransactionSigned { - fn type_flag(&self) -> Option { - (!self.transaction.is_legacy()).then(|| self.ty()) - } - - fn encode_2718_len(&self) -> usize { - delegate!(&self.transaction => tx.eip2718_encoded_length(&self.signature)) - } - - fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { - delegate!(&self.transaction => tx.eip2718_encode(&self.signature, out)) - } - - fn trie_hash(&self) -> B256 { - *self.tx_hash() - } -} - -impl Decodable2718 for TransactionSigned { - fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result { - match ty.try_into().map_err(|_| Eip2718Error::UnexpectedType(ty))? { - TxType::Legacy => Err(Eip2718Error::UnexpectedType(0)), - TxType::Eip2930 => { - let (tx, signature) = TxEip2930::rlp_decode_with_signature(buf)?; - Ok(Self { - transaction: Transaction::Eip2930(tx), - signature, - hash: Default::default(), - }) - } - TxType::Eip1559 => { - let (tx, signature) = TxEip1559::rlp_decode_with_signature(buf)?; - Ok(Self { - transaction: Transaction::Eip1559(tx), - signature, - hash: Default::default(), - }) - } - TxType::Eip4844 => { - let (tx, signature) = TxEip4844::rlp_decode_with_signature(buf)?; - Ok(Self { - transaction: Transaction::Eip4844(tx), - signature, - hash: Default::default(), - }) - } - TxType::Eip7702 => { - let (tx, signature) = TxEip7702::rlp_decode_with_signature(buf)?; - Ok(Self { - transaction: Transaction::Eip7702(tx), - signature, - hash: Default::default(), - }) - } - } - } - - fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result { - let (tx, signature) = TxLegacy::rlp_decode_with_signature(buf)?; - Ok(Self { transaction: Transaction::Legacy(tx), signature, hash: Default::default() }) - } -} - -impl Encodable for TransactionSigned { - fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { - self.network_encode(out); - } - - fn length(&self) -> usize { - self.network_len() - } -} - -impl Decodable for TransactionSigned { - fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - Self::network_decode(buf).map_err(Into::into) - } -} - -#[cfg(any(test, feature = "reth-codec"))] -impl reth_codecs::Compact for TransactionSigned { - fn to_compact(&self, buf: &mut B) -> usize - where - B: alloy_rlp::bytes::BufMut + AsMut<[u8]>, - { - use alloy_consensus::Transaction; - - let start = buf.as_mut().len(); - - // Placeholder for bitflags. - // The first byte uses 4 bits as flags: IsCompressed[1bit], TxType[2bits], Signature[1bit] - buf.put_u8(0); - - let sig_bit = self.signature.to_compact(buf) as u8; - let zstd_bit = self.transaction.input().len() >= 32; - - let tx_bits = if zstd_bit { - let mut tmp = Vec::with_capacity(256); - reth_zstd_compressors::with_tx_compressor(|compressor| { - let tx_bits = self.transaction.to_compact(&mut tmp); - buf.put_slice(&compressor.compress(&tmp).expect("Failed to compress")); - tx_bits as u8 - }) - } else { - self.transaction.to_compact(buf) as u8 - }; - - // Replace bitflags with the actual values - buf.as_mut()[start] = sig_bit | (tx_bits << 1) | ((zstd_bit as u8) << 3); - - buf.as_mut().len() - start - } - - fn from_compact(mut buf: &[u8], _len: usize) -> (Self, &[u8]) { - use alloy_rlp::bytes::Buf; - - // The first byte uses 4 bits as flags: IsCompressed[1], TxType[2], Signature[1] - let bitflags = buf.get_u8() as usize; - - let sig_bit = bitflags & 1; - let (signature, buf) = Signature::from_compact(buf, sig_bit); - - let zstd_bit = bitflags >> 3; - let (transaction, buf) = if zstd_bit != 0 { - reth_zstd_compressors::with_tx_decompressor(|decompressor| { - // TODO: enforce that zstd is only present at a "top" level type - let transaction_type = (bitflags & 0b110) >> 1; - let (transaction, _) = - Transaction::from_compact(decompressor.decompress(buf), transaction_type); - (transaction, buf) - }) - } else { - let transaction_type = bitflags >> 1; - Transaction::from_compact(buf, transaction_type) - }; - - (Self { signature, transaction, hash: Default::default() }, buf) - } -} - -impl SignerRecoverable for TransactionSigned { - fn recover_signer(&self) -> Result { - let signature_hash = self.signature_hash(); - recover_signer(&self.signature, signature_hash) - } - - fn recover_signer_unchecked(&self) -> Result { - let signature_hash = self.signature_hash(); - recover_signer_unchecked(&self.signature, signature_hash) - } - - fn recover_unchecked_with_buf(&self, buf: &mut Vec) -> Result { - self.encode_for_signing(buf); - let signature_hash = keccak256(buf); - recover_signer_unchecked(&self.signature, signature_hash) - } -} - -impl TxHashRef for TransactionSigned { - fn tx_hash(&self) -> &TxHash { - self.hash.get_or_init(|| self.recalculate_hash()) - } -} - -impl IsTyped2718 for TransactionSigned { - fn is_type(type_id: u8) -> bool { - ::is_type(type_id) - } -} - -impl SignedTransaction for TransactionSigned {} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_consensus::EthereumTxEnvelope; - use proptest::proptest; - use proptest_arbitrary_interop::arb; - use reth_codecs::Compact; - - proptest! { - #[test] - fn test_roundtrip_compact_encode_envelope(reth_tx in arb::()) { - let mut expected_buf = Vec::::new(); - let expected_len = reth_tx.to_compact(&mut expected_buf); - - let mut actual_but = Vec::::new(); - let alloy_tx = EthereumTxEnvelope::::from(reth_tx); - let actual_len = alloy_tx.to_compact(&mut actual_but); - - assert_eq!(actual_but, expected_buf); - assert_eq!(actual_len, expected_len); - } - - #[test] - fn test_roundtrip_compact_decode_envelope(reth_tx in arb::()) { - let mut buf = Vec::::new(); - let len = reth_tx.to_compact(&mut buf); - - let (actual_tx, _) = EthereumTxEnvelope::::from_compact(&buf, len); - let expected_tx = EthereumTxEnvelope::::from(reth_tx); - - assert_eq!(actual_tx, expected_tx); - } - - #[test] - fn test_roundtrip_compact_encode_envelope_zstd(mut reth_tx in arb::()) { - // zstd only kicks in if the input is large enough - *reth_tx.transaction.input_mut() = vec![0;33].into(); - - let mut expected_buf = Vec::::new(); - let expected_len = reth_tx.to_compact(&mut expected_buf); - - let mut actual_but = Vec::::new(); - let alloy_tx = EthereumTxEnvelope::::from(reth_tx); - let actual_len = alloy_tx.to_compact(&mut actual_but); - - assert_eq!(actual_but, expected_buf); - assert_eq!(actual_len, expected_len); - } - - #[test] - fn test_roundtrip_compact_decode_envelope_zstd(mut reth_tx in arb::()) { - // zstd only kicks in if the input is large enough - *reth_tx.transaction.input_mut() = vec![0;33].into(); - - let mut buf = Vec::::new(); - let len = reth_tx.to_compact(&mut buf); - - let (actual_tx, _) = EthereumTxEnvelope::::from_compact(&buf, len); - let expected_tx = EthereumTxEnvelope::::from(reth_tx); - - assert_eq!(actual_tx, expected_tx); - } - } -}