mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-02-19 03:04:27 -05:00
chore(primitives): remove legacy transaction roundtrip tests (#22292)
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
committed by
GitHub
parent
477fed7a11
commit
aeb2c6e731
28
Cargo.lock
generated
28
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<ChainId> {
|
||||
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<u128> {
|
||||
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<u128> {
|
||||
delegate!(self => tx.max_priority_fee_per_gas())
|
||||
}
|
||||
|
||||
fn max_fee_per_blob_gas(&self) -> Option<u128> {
|
||||
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<u64>) -> 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<Signature> 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<Self> {
|
||||
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<B>(&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<TxHash>,
|
||||
/// 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<H: Hasher>(&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<ChainId> {
|
||||
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<u128> {
|
||||
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<u128> {
|
||||
self.transaction.max_priority_fee_per_gas()
|
||||
}
|
||||
|
||||
fn max_fee_per_blob_gas(&self) -> Option<u128> {
|
||||
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<u64>) -> 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<Signed<Transaction>> for TransactionSigned {
|
||||
fn from(value: Signed<Transaction>) -> Self {
|
||||
let (tx, sig, hash) = value.into_parts();
|
||||
Self::new(tx, sig, hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TransactionSigned> for EthereumTxEnvelope<TxEip4844> {
|
||||
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<Self> {
|
||||
#[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<u8> {
|
||||
(!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<Self> {
|
||||
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<Self> {
|
||||
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> {
|
||||
Self::network_decode(buf).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "reth-codec"))]
|
||||
impl reth_codecs::Compact for TransactionSigned {
|
||||
fn to_compact<B>(&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<Address, RecoveryError> {
|
||||
let signature_hash = self.signature_hash();
|
||||
recover_signer(&self.signature, signature_hash)
|
||||
}
|
||||
|
||||
fn recover_signer_unchecked(&self) -> Result<Address, RecoveryError> {
|
||||
let signature_hash = self.signature_hash();
|
||||
recover_signer_unchecked(&self.signature, signature_hash)
|
||||
}
|
||||
|
||||
fn recover_unchecked_with_buf(&self, buf: &mut Vec<u8>) -> Result<Address, RecoveryError> {
|
||||
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 {
|
||||
<alloy_consensus::TxEnvelope as IsTyped2718>::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::<TransactionSigned>()) {
|
||||
let mut expected_buf = Vec::<u8>::new();
|
||||
let expected_len = reth_tx.to_compact(&mut expected_buf);
|
||||
|
||||
let mut actual_but = Vec::<u8>::new();
|
||||
let alloy_tx = EthereumTxEnvelope::<TxEip4844>::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::<TransactionSigned>()) {
|
||||
let mut buf = Vec::<u8>::new();
|
||||
let len = reth_tx.to_compact(&mut buf);
|
||||
|
||||
let (actual_tx, _) = EthereumTxEnvelope::<TxEip4844>::from_compact(&buf, len);
|
||||
let expected_tx = EthereumTxEnvelope::<TxEip4844>::from(reth_tx);
|
||||
|
||||
assert_eq!(actual_tx, expected_tx);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_roundtrip_compact_encode_envelope_zstd(mut reth_tx in arb::<TransactionSigned>()) {
|
||||
// zstd only kicks in if the input is large enough
|
||||
*reth_tx.transaction.input_mut() = vec![0;33].into();
|
||||
|
||||
let mut expected_buf = Vec::<u8>::new();
|
||||
let expected_len = reth_tx.to_compact(&mut expected_buf);
|
||||
|
||||
let mut actual_but = Vec::<u8>::new();
|
||||
let alloy_tx = EthereumTxEnvelope::<TxEip4844>::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::<TransactionSigned>()) {
|
||||
// zstd only kicks in if the input is large enough
|
||||
*reth_tx.transaction.input_mut() = vec![0;33].into();
|
||||
|
||||
let mut buf = Vec::<u8>::new();
|
||||
let len = reth_tx.to_compact(&mut buf);
|
||||
|
||||
let (actual_tx, _) = EthereumTxEnvelope::<TxEip4844>::from_compact(&buf, len);
|
||||
let expected_tx = EthereumTxEnvelope::<TxEip4844>::from(reth_tx);
|
||||
|
||||
assert_eq!(actual_tx, expected_tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user