feat!: replace TransactionSigned with alloy's (#15768)

This commit is contained in:
Matthias Seitz
2025-04-24 14:17:19 +02:00
committed by GitHub
parent cfa49f86ef
commit 4d77608be1
36 changed files with 288 additions and 1227 deletions

323
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -468,34 +468,34 @@ alloy-trie = { version = "0.8.1", default-features = false }
alloy-hardforks = "0.2.0"
alloy-consensus = { version = "0.15.0", default-features = false }
alloy-contract = { version = "0.15.0", default-features = false }
alloy-eips = { version = "0.15.0", default-features = false }
alloy-genesis = { version = "0.15.0", default-features = false }
alloy-json-rpc = { version = "0.15.0", default-features = false }
alloy-network = { version = "0.15.0", default-features = false }
alloy-network-primitives = { version = "0.15.0", default-features = false }
alloy-node-bindings = { version = "0.15.0", default-features = false }
alloy-provider = { version = "0.15.0", features = ["reqwest"], default-features = false }
alloy-pubsub = { version = "0.15.0", default-features = false }
alloy-rpc-client = { version = "0.15.0", default-features = false }
alloy-rpc-types = { version = "0.15.0", features = ["eth"], default-features = false }
alloy-rpc-types-admin = { version = "0.15.0", default-features = false }
alloy-rpc-types-anvil = { version = "0.15.0", default-features = false }
alloy-rpc-types-beacon = { version = "0.15.0", default-features = false }
alloy-rpc-types-debug = { version = "0.15.0", default-features = false }
alloy-rpc-types-engine = { version = "0.15.0", default-features = false }
alloy-rpc-types-eth = { version = "0.15.0", default-features = false }
alloy-rpc-types-mev = { version = "0.15.0", default-features = false }
alloy-rpc-types-trace = { version = "0.15.0", default-features = false }
alloy-rpc-types-txpool = { version = "0.15.0", default-features = false }
alloy-serde = { version = "0.15.0", default-features = false }
alloy-signer = { version = "0.15.0", default-features = false }
alloy-signer-local = { version = "0.15.0", default-features = false }
alloy-transport = { version = "0.15.0" }
alloy-transport-http = { version = "0.15.0", features = ["reqwest-rustls-tls"], default-features = false }
alloy-transport-ipc = { version = "0.15.0", default-features = false }
alloy-transport-ws = { version = "0.15.0", default-features = false }
alloy-consensus = { version = "0.15.3", default-features = false }
alloy-contract = { version = "0.15.3", default-features = false }
alloy-eips = { version = "0.15.3", default-features = false }
alloy-genesis = { version = "0.15.3", default-features = false }
alloy-json-rpc = { version = "0.15.3", default-features = false }
alloy-network = { version = "0.15.3", default-features = false }
alloy-network-primitives = { version = "0.15.3", default-features = false }
alloy-node-bindings = { version = "0.15.3", default-features = false }
alloy-provider = { version = "0.15.3", features = ["reqwest"], default-features = false }
alloy-pubsub = { version = "0.15.3", default-features = false }
alloy-rpc-client = { version = "0.15.3", default-features = false }
alloy-rpc-types = { version = "0.15.3", features = ["eth"], default-features = false }
alloy-rpc-types-admin = { version = "0.15.3", default-features = false }
alloy-rpc-types-anvil = { version = "0.15.3", default-features = false }
alloy-rpc-types-beacon = { version = "0.15.3", default-features = false }
alloy-rpc-types-debug = { version = "0.15.3", default-features = false }
alloy-rpc-types-engine = { version = "0.15.3", default-features = false }
alloy-rpc-types-eth = { version = "0.15.3", default-features = false }
alloy-rpc-types-mev = { version = "0.15.3", default-features = false }
alloy-rpc-types-trace = { version = "0.15.3", default-features = false }
alloy-rpc-types-txpool = { version = "0.15.3", default-features = false }
alloy-serde = { version = "0.15.3", default-features = false }
alloy-signer = { version = "0.15.3", default-features = false }
alloy-signer-local = { version = "0.15.3", default-features = false }
alloy-transport = { version = "0.15.3" }
alloy-transport-http = { version = "0.15.3", features = ["reqwest-rustls-tls"], default-features = false }
alloy-transport-ipc = { version = "0.15.3", default-features = false }
alloy-transport-ws = { version = "0.15.3", default-features = false }
# op
alloy-op-evm = { version = "0.6.0", default-features = false }

View File

@@ -215,8 +215,8 @@ impl<T: Clone + Sync + Send + 'static> Stream for ForkChoiceStream<T> {
#[cfg(test)]
mod tests {
use super::*;
use alloy_consensus::BlockBody;
use alloy_primitives::{b256, B256};
use alloy_consensus::{BlockBody, SignableTransaction, TxLegacy};
use alloy_primitives::{b256, Signature, B256};
use reth_ethereum_primitives::{Receipt, TransactionSigned, TxType};
use reth_execution_types::ExecutionOutcome;
use reth_primitives_traits::SealedBlock;
@@ -305,7 +305,7 @@ mod tests {
let block2_hash = B256::new([0x02; 32]);
// Create a default transaction to include in block1's transactions.
let tx = TransactionSigned::default();
let tx = TxLegacy::default().into_signed(Signature::test_signature()).into();
body.transactions.push(tx);
let block = SealedBlock::<alloy_consensus::Block<TransactionSigned>>::from_sealed_parts(
@@ -373,7 +373,7 @@ mod tests {
fn test_block_receipts_reorg() {
// Define block1 for the old chain segment, which will be reverted.
let mut body = BlockBody::<TransactionSigned>::default();
body.transactions.push(TransactionSigned::default());
body.transactions.push(TxLegacy::default().into_signed(Signature::test_signature()).into());
let mut old_block1 =
SealedBlock::<alloy_consensus::Block<TransactionSigned>>::from_sealed_parts(
SealedHeader::seal_slow(alloy_consensus::Header::default()),
@@ -402,7 +402,7 @@ mod tests {
// Define block2 for the new chain segment, which will be committed.
let mut body = BlockBody::<TransactionSigned>::default();
body.transactions.push(TransactionSigned::default());
body.transactions.push(TxLegacy::default().into_signed(Signature::test_signature()).into());
let mut new_block1 =
SealedBlock::<alloy_consensus::Block<TransactionSigned>>::from_sealed_parts(
SealedHeader::seal_slow(alloy_consensus::Header::default()),

View File

@@ -63,7 +63,7 @@ macro_rules! compact_types {
pub static IDENTIFIER_TYPE: std::sync::LazyLock<std::collections::HashSet<String>> = std::sync::LazyLock::new(|| {
let mut map = std::collections::HashSet::new();
$(
map.insert(type_name::<$id_ty>());
map.insert(type_name::<$id_ty>());
)*
map
});
@@ -279,6 +279,15 @@ where
Ok(())
}
/// Returns the type name for the given type.
pub fn type_name<T>() -> String {
std::any::type_name::<T>().split("::").last().unwrap_or(std::any::type_name::<T>()).to_string()
// With alloy type transition <https://github.com/paradigmxyz/reth/pull/15768> the types are renamed, we map them here to the original name so that test vector files remain consistent
let name = std::any::type_name::<T>();
match name {
"alloy_consensus::transaction::typed::EthereumTypedTransaction<alloy_consensus::transaction::eip4844::TxEip4844>" => "Transaction".to_string(),
"alloy_consensus::transaction::envelope::EthereumTxEnvelope<alloy_consensus::transaction::eip4844::TxEip4844>" => "TransactionSigned".to_string(),
name => {
name.split("::").last().unwrap_or(std::any::type_name::<T>()).to_string()
}
}
}

View File

@@ -1,5 +1,5 @@
//! Command for debugging block building.
use alloy_consensus::{BlockHeader, TxEip4844};
use alloy_consensus::BlockHeader;
use alloy_eips::{
eip2718::Encodable2718,
eip4844::{env_settings::EnvKzgSettings, BlobTransactionSidecar},
@@ -17,7 +17,7 @@ use reth_cli_runner::CliContext;
use reth_consensus::{Consensus, FullConsensus};
use reth_errors::{ConsensusError, RethResult};
use reth_ethereum_payload_builder::EthereumBuilderConfig;
use reth_ethereum_primitives::{EthPrimitives, Transaction, TransactionSigned};
use reth_ethereum_primitives::{EthPrimitives, TransactionSigned};
use reth_evm::execute::{BlockExecutorProvider, Executor};
use reth_execution_types::ExecutionOutcome;
use reth_fs_util as fs;
@@ -146,14 +146,14 @@ impl<C: ChainSpecParser<ChainSpec = ChainSpec>> Command<C> {
.try_clone_into_recovered()
.map_err(|e| eyre::eyre!("failed to recover tx: {e}"))?;
let encoded_length = match transaction.transaction() {
Transaction::Eip4844(TxEip4844 { blob_versioned_hashes, .. }) => {
let encoded_length = match transaction.inner() {
TransactionSigned::Eip4844(tx) => {
let blobs_bundle = blobs_bundle.as_mut().ok_or_else(|| {
eyre::eyre!("encountered a blob tx. `--blobs-bundle-path` must be provided")
})?;
let sidecar: BlobTransactionSidecar =
blobs_bundle.pop_sidecar(blob_versioned_hashes.len());
blobs_bundle.pop_sidecar(tx.tx().blob_versioned_hashes.len());
let pooled = transaction
.clone()

View File

@@ -29,7 +29,6 @@ use reth_evm_ethereum::EthEvmConfig;
use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes};
use reth_payload_builder_primitives::PayloadBuilderError;
use reth_payload_primitives::PayloadBuilderAttributes;
use reth_primitives_traits::SignedTransaction;
use reth_revm::{database::StateProviderDatabase, db::State};
use reth_storage_api::StateProviderFactory;
use reth_transaction_pool::{
@@ -218,7 +217,7 @@ where
// There's only limited amount of blob space available per block, so we need to check if
// the EIP-4844 can still fit in the block
if let Some(blob_tx) = tx.as_eip4844() {
let tx_blob_count = blob_tx.blob_versioned_hashes.len() as u64;
let tx_blob_count = blob_tx.tx().blob_versioned_hashes.len() as u64;
if block_blob_count + tx_blob_count > max_blob_count {
// we can't fit this _blob_ transaction into the block, so we mark it as
@@ -266,7 +265,7 @@ where
// add to the total blob gas used if the transaction successfully executed
if let Some(blob_tx) = tx.as_eip4844() {
block_blob_count += blob_tx.blob_versioned_hashes.len() as u64;
block_blob_count += blob_tx.tx().blob_versioned_hashes.len() as u64;
// if we've reached the max blob count, we can skip blob txs entirely
if block_blob_count == max_blob_count {

View File

@@ -18,25 +18,19 @@ reth-primitives-traits.workspace = true
reth-zstd-compressors = { workspace = true, optional = true }
# ethereum
alloy-evm.workspace = true
alloy-eips = { workspace = true, features = ["k256"] }
alloy-primitives.workspace = true
alloy-network = { workspace = true, optional = true }
alloy-consensus = { workspace = true, features = ["serde"] }
alloy-rlp.workspace = true
alloy-rpc-types-eth = { workspace = true, optional = true }
revm-context.workspace = true
# misc
arbitrary = { workspace = true, optional = true, features = ["derive"] }
derive_more.workspace = true
modular-bitfield = { workspace = true, optional = true }
rand_08 = { workspace = true, optional = true }
secp256k1 = { workspace = true, optional = true, features = ["rand"] }
serde = { workspace = true, optional = true }
serde_with = { workspace = true, optional = true }
[dev-dependencies]
derive_more.workspace = true
arbitrary.workspace = true
bincode.workspace = true
proptest.workspace = true
@@ -44,7 +38,6 @@ proptest-arbitrary-interop.workspace = true
rand_08.workspace = true
rand.workspace = true
reth-codecs = { workspace = true, features = ["test-utils"] }
reth-testing-utils.workspace = true
reth-zstd-compressors.workspace = true
secp256k1 = { workspace = true, features = ["rand"] }
test-fuzz.workspace = true
@@ -56,7 +49,6 @@ test-utils = [
"reth-codecs?/test-utils",
"reth-primitives-traits/test-utils",
]
alloy-compat = ["dep:alloy-network", "dep:alloy-rpc-types-eth"]
std = [
"alloy-consensus/std",
"alloy-primitives/std",
@@ -66,11 +58,8 @@ std = [
"serde?/std",
"alloy-eips/std",
"derive_more/std",
"secp256k1?/std",
"alloy-rpc-types-eth?/std",
"revm-context/std",
"alloy-evm/std",
"serde_with?/std",
"secp256k1/std",
]
reth-codec = [
"std",
@@ -80,14 +69,12 @@ reth-codec = [
]
arbitrary = [
"dep:arbitrary",
"dep:rand_08",
"dep:secp256k1",
"alloy-consensus/arbitrary",
"alloy-consensus/k256",
"alloy-primitives/arbitrary",
"reth-codecs?/arbitrary",
"reth-primitives-traits/arbitrary",
"alloy-eips/arbitrary",
"alloy-rpc-types-eth?/arbitrary",
]
serde-bincode-compat = [
"dep:serde_with",
@@ -100,11 +87,9 @@ serde = [
"alloy-consensus/serde",
"alloy-eips/serde",
"alloy-primitives/serde",
"alloy-rpc-types-eth?/serde",
"rand_08?/serde",
"reth-codecs?/serde",
"reth-primitives-traits/serde",
"revm-context/serde",
"secp256k1?/serde",
"rand/serde",
"rand_08/serde",
"secp256k1/serde",
]

View File

@@ -1,52 +0,0 @@
//! Common conversions from alloy types.
use crate::{Transaction, TransactionSigned};
use alloc::string::ToString;
use alloy_consensus::TxEnvelope;
use alloy_network::{AnyRpcTransaction, AnyTxEnvelope};
use alloy_rpc_types_eth::Transaction as AlloyRpcTransaction;
impl TryFrom<AnyRpcTransaction> for TransactionSigned {
type Error = alloy_rpc_types_eth::ConversionError;
fn try_from(tx: AnyRpcTransaction) -> Result<Self, Self::Error> {
use alloy_rpc_types_eth::ConversionError;
let tx = tx.into_inner();
let (transaction, signature, hash) = match tx.inner.into_inner() {
AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(tx)) => {
let (tx, signature, hash) = tx.into_parts();
(Transaction::Legacy(tx), signature, hash)
}
AnyTxEnvelope::Ethereum(TxEnvelope::Eip2930(tx)) => {
let (tx, signature, hash) = tx.into_parts();
(Transaction::Eip2930(tx), signature, hash)
}
AnyTxEnvelope::Ethereum(TxEnvelope::Eip1559(tx)) => {
let (tx, signature, hash) = tx.into_parts();
(Transaction::Eip1559(tx), signature, hash)
}
AnyTxEnvelope::Ethereum(TxEnvelope::Eip4844(tx)) => {
let (tx, signature, hash) = tx.into_parts();
(Transaction::Eip4844(tx.into()), signature, hash)
}
AnyTxEnvelope::Ethereum(TxEnvelope::Eip7702(tx)) => {
let (tx, signature, hash) = tx.into_parts();
(Transaction::Eip7702(tx), signature, hash)
}
_ => return Err(ConversionError::Custom("unknown transaction type".to_string())),
};
Ok(Self::new(transaction, signature, hash))
}
}
impl<T> From<AlloyRpcTransaction<T>> for TransactionSigned
where
Self: From<T>,
{
fn from(value: AlloyRpcTransaction<T>) -> Self {
value.inner.into_inner().into()
}
}

View File

@@ -14,16 +14,24 @@ extern crate alloc;
mod receipt;
pub use receipt::*;
/// Kept for concistency tests
#[cfg(test)]
mod transaction;
pub use transaction::*;
#[cfg(feature = "alloy-compat")]
mod alloy_compat;
use alloy_consensus::TxEip4844;
pub use alloy_consensus::{transaction::PooledTransaction, TxType};
/// Typed Transaction type without a signature
pub type Transaction = alloy_consensus::EthereumTypedTransaction<TxEip4844>;
/// Signed transaction.
pub type TransactionSigned = alloy_consensus::EthereumTxEnvelope<TxEip4844>;
/// Bincode-compatible serde implementations.
#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
pub mod serde_bincode_compat {
pub use super::{receipt::serde_bincode_compat::*, transaction::serde_bincode_compat::*};
pub use super::receipt::serde_bincode_compat::*;
pub use alloy_consensus::serde_bincode_compat::transaction::*;
}
/// Type alias for the ethereum block

View File

@@ -1,17 +1,17 @@
//! This file contains the legacy reth `TransactionSigned` type that has been replaced with
//! alloy's TxEnvelope To test for consistency this is kept
use alloc::vec::Vec;
pub use alloy_consensus::{transaction::PooledTransaction, TxType};
use alloy_consensus::{
transaction::{RlpEcdsaDecodableTx, RlpEcdsaEncodableTx},
BlobTransactionSidecar, EthereumTxEnvelope, SignableTransaction, Signed, TxEip1559, TxEip2930,
TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEip7702, TxEnvelope, TxLegacy, Typed2718,
TypedTransaction,
EthereumTxEnvelope, SignableTransaction, Signed, TxEip1559, TxEip2930, TxEip4844, TxEip7702,
TxLegacy, TxType, Typed2718,
};
use alloy_eips::{
eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718},
eip2930::AccessList,
eip7702::SignedAuthorization,
};
use alloy_evm::{FromRecoveredTx, FromTxWithEncoded};
use alloy_primitives::{
bytes::BufMut, keccak256, Address, Bytes, ChainId, Signature, TxHash, TxKind, B256, U256,
};
@@ -20,10 +20,9 @@ use core::hash::{Hash, Hasher};
use reth_primitives_traits::{
crypto::secp256k1::{recover_signer, recover_signer_unchecked},
sync::OnceLock,
transaction::{error::TransactionConversionError, signed::RecoveryError},
transaction::signed::RecoveryError,
InMemorySize, SignedTransaction,
};
use revm_context::TxEnv;
macro_rules! delegate {
($self:expr => $tx:ident.$method:ident($($arg:expr),*)) => {
@@ -37,19 +36,6 @@ macro_rules! delegate {
};
}
macro_rules! impl_from_signed {
($($tx:ident),*) => {
$(
impl From<Signed<$tx>> for TransactionSigned {
fn from(value: Signed<$tx>) -> Self {
let(tx,sig,hash) = value.into_parts();
Self::new(tx.into(), sig, hash)
}
}
)*
};
}
/// A raw transaction.
///
/// Transaction types were introduced in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718).
@@ -117,17 +103,6 @@ impl Transaction {
}
}
/// This sets the transaction's nonce.
pub const fn set_nonce(&mut self, nonce: u64) {
match self {
Self::Legacy(tx) => tx.nonce = nonce,
Self::Eip2930(tx) => tx.nonce = nonce,
Self::Eip1559(tx) => tx.nonce = nonce,
Self::Eip4844(tx) => tx.nonce = nonce,
Self::Eip7702(tx) => tx.nonce = nonce,
}
}
#[cfg(test)]
const fn input_mut(&mut self) -> &mut Bytes {
match self {
@@ -290,18 +265,6 @@ impl reth_codecs::Compact for Transaction {
}
}
impl From<TypedTransaction> for Transaction {
fn from(value: TypedTransaction) -> Self {
match value {
TypedTransaction::Legacy(tx) => Self::Legacy(tx),
TypedTransaction::Eip2930(tx) => Self::Eip2930(tx),
TypedTransaction::Eip1559(tx) => Self::Eip1559(tx),
TypedTransaction::Eip4844(tx) => Self::Eip4844(tx.into()),
TypedTransaction::Eip7702(tx) => Self::Eip7702(tx),
}
}
}
impl RlpEcdsaEncodableTx for Transaction {
fn rlp_encoded_fields_length(&self) -> usize {
delegate!(self => tx.rlp_encoded_fields_length())
@@ -355,21 +318,10 @@ pub struct TransactionSigned {
transaction: Transaction,
}
impl Default for TransactionSigned {
fn default() -> Self {
Self::new_unhashed(Transaction::Legacy(Default::default()), Signature::test_signature())
}
}
impl TransactionSigned {
fn recalculate_hash(&self) -> B256 {
keccak256(self.encoded_2718())
}
/// Returns the signature of the transaction
pub const fn signature(&self) -> &Signature {
&self.signature
}
}
impl Hash for TransactionSigned {
@@ -393,75 +345,12 @@ impl TransactionSigned {
Self { hash: hash.into(), signature, transaction }
}
/// Consumes the type and returns the transaction.
#[inline]
pub fn into_transaction(self) -> Transaction {
self.transaction
}
/// Returns the transaction.
#[inline]
pub const fn transaction(&self) -> &Transaction {
&self.transaction
}
/// Returns the transaction hash.
#[inline]
pub fn hash(&self) -> &B256 {
self.hash.get_or_init(|| self.recalculate_hash())
}
/// Creates a new signed transaction from the given transaction and signature without the hash.
///
/// Note: this only calculates the hash on the first [`TransactionSigned::hash`] call.
pub fn new_unhashed(transaction: Transaction, signature: Signature) -> Self {
Self { hash: Default::default(), signature, transaction }
}
/// Splits the `TransactionSigned` into its transaction and signature.
pub fn split(self) -> (Transaction, Signature) {
(self.transaction, self.signature)
}
/// Converts from an EIP-4844 transaction to a [`PooledTransaction`] with the given sidecar.
///
/// Returns an `Err` containing the original `TransactionSigned` if the transaction is not
/// EIP-4844.
pub fn try_into_pooled_eip4844(
self,
sidecar: BlobTransactionSidecar,
) -> Result<PooledTransaction, Self> {
let hash = *self.tx_hash();
Ok(match self {
// If the transaction is an EIP-4844 transaction...
Self { transaction: Transaction::Eip4844(tx), signature, .. } => {
// Construct a pooled eip488 tx with the provided sidecar.
PooledTransaction::Eip4844(Signed::new_unchecked(
TxEip4844WithSidecar { tx, sidecar },
signature,
hash,
))
}
// If the transaction is not EIP-4844, return an error with the original
// transaction.
_ => return Err(self),
})
}
/// Returns the [`TxEip4844`] if the transaction is an EIP-4844 transaction.
pub const fn as_eip4844(&self) -> Option<&TxEip4844> {
match &self.transaction {
Transaction::Eip4844(tx) => Some(tx),
_ => None,
}
}
/// Provides mutable access to the transaction.
#[cfg(feature = "test-utils")]
pub const fn transaction_mut(&mut self) -> &mut Transaction {
&mut self.transaction
}
/// Splits the transaction into parts.
pub fn into_parts(self) -> (Transaction, Signature, B256) {
let hash = *self.hash.get_or_init(|| self.recalculate_hash());
@@ -545,8 +434,6 @@ impl alloy_consensus::Transaction for TransactionSigned {
}
}
impl_from_signed!(TxLegacy, TxEip2930, TxEip1559, TxEip7702, TxEip4844, TypedTransaction);
impl From<Signed<Transaction>> for TransactionSigned {
fn from(value: Signed<Transaction>) -> Self {
let (tx, sig, hash) = value.into_parts();
@@ -554,56 +441,6 @@ impl From<Signed<Transaction>> for TransactionSigned {
}
}
impl From<Signed<TxEip4844WithSidecar>> for TransactionSigned {
fn from(value: Signed<TxEip4844WithSidecar>) -> Self {
let (tx, sig, hash) = value.into_parts();
Self::new(tx.tx.into(), sig, hash)
}
}
impl From<TxEip4844Variant> for Transaction {
fn from(variant: TxEip4844Variant) -> Self {
match variant {
TxEip4844Variant::TxEip4844(tx) => Self::Eip4844(tx),
TxEip4844Variant::TxEip4844WithSidecar(tx_with_sidecar) => {
Self::Eip4844(tx_with_sidecar.tx)
}
}
}
}
impl From<Signed<TxEip4844Variant>> for TransactionSigned {
fn from(value: Signed<TxEip4844Variant>) -> Self {
let (tx, sig, hash) = value.into_parts();
Self::new(tx.into(), sig, hash)
}
}
impl From<TxEnvelope> for TransactionSigned {
fn from(value: TxEnvelope) -> Self {
match value {
TxEnvelope::Legacy(tx) => tx.into(),
TxEnvelope::Eip2930(tx) => tx.into(),
TxEnvelope::Eip1559(tx) => tx.into(),
TxEnvelope::Eip4844(tx) => tx.into(),
TxEnvelope::Eip7702(tx) => tx.into(),
}
}
}
impl From<TransactionSigned> for TxEnvelope {
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(),
}
}
}
impl From<TransactionSigned> for EthereumTxEnvelope<TxEip4844> {
fn from(value: TransactionSigned) -> Self {
let (tx, signature, hash) = value.into_parts();
@@ -617,13 +454,6 @@ impl From<TransactionSigned> for EthereumTxEnvelope<TxEip4844> {
}
}
impl From<TransactionSigned> for Signed<Transaction> {
fn from(value: TransactionSigned) -> Self {
let (tx, sig, hash) = value.into_parts();
Self::new_unchecked(tx, sig, hash)
}
}
#[cfg(any(test, feature = "arbitrary"))]
impl<'a> arbitrary::Arbitrary<'a> for TransactionSigned {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
@@ -810,105 +640,6 @@ impl reth_codecs::Compact for TransactionSigned {
}
}
impl FromRecoveredTx<TransactionSigned> for TxEnv {
fn from_recovered_tx(tx: &TransactionSigned, sender: Address) -> Self {
match tx.as_ref() {
Transaction::Legacy(tx) => Self {
gas_limit: tx.gas_limit,
gas_price: tx.gas_price,
gas_priority_fee: None,
kind: tx.to,
value: tx.value,
data: tx.input.clone(),
chain_id: tx.chain_id,
nonce: tx.nonce,
access_list: Default::default(),
blob_hashes: Default::default(),
max_fee_per_blob_gas: Default::default(),
authorization_list: Default::default(),
tx_type: 0,
caller: sender,
},
Transaction::Eip2930(tx) => Self {
gas_limit: tx.gas_limit,
gas_price: tx.gas_price,
gas_priority_fee: None,
kind: tx.to,
value: tx.value,
data: tx.input.clone(),
chain_id: Some(tx.chain_id),
nonce: tx.nonce,
access_list: tx.access_list.clone(),
blob_hashes: Default::default(),
max_fee_per_blob_gas: Default::default(),
authorization_list: Default::default(),
tx_type: 1,
caller: sender,
},
Transaction::Eip1559(tx) => Self {
gas_limit: tx.gas_limit,
gas_price: tx.max_fee_per_gas,
gas_priority_fee: Some(tx.max_priority_fee_per_gas),
kind: tx.to,
value: tx.value,
data: tx.input.clone(),
chain_id: Some(tx.chain_id),
nonce: tx.nonce,
access_list: tx.access_list.clone(),
blob_hashes: Default::default(),
max_fee_per_blob_gas: Default::default(),
authorization_list: Default::default(),
tx_type: 2,
caller: sender,
},
Transaction::Eip4844(tx) => Self {
gas_limit: tx.gas_limit,
gas_price: tx.max_fee_per_gas,
gas_priority_fee: Some(tx.max_priority_fee_per_gas),
kind: TxKind::Call(tx.to),
value: tx.value,
data: tx.input.clone(),
chain_id: Some(tx.chain_id),
nonce: tx.nonce,
access_list: tx.access_list.clone(),
blob_hashes: tx.blob_versioned_hashes.clone(),
max_fee_per_blob_gas: tx.max_fee_per_blob_gas,
authorization_list: Default::default(),
tx_type: 3,
caller: sender,
},
Transaction::Eip7702(tx) => Self {
gas_limit: tx.gas_limit,
gas_price: tx.max_fee_per_gas,
gas_priority_fee: Some(tx.max_priority_fee_per_gas),
kind: TxKind::Call(tx.to),
value: tx.value,
data: tx.input.clone(),
chain_id: Some(tx.chain_id),
nonce: tx.nonce,
access_list: tx.access_list.clone(),
blob_hashes: Default::default(),
max_fee_per_blob_gas: Default::default(),
authorization_list: tx.authorization_list.clone(),
tx_type: 4,
caller: sender,
},
}
}
}
impl FromTxWithEncoded<TransactionSigned> for TxEnv {
fn from_encoded_tx(tx: &TransactionSigned, sender: Address, encoded: Bytes) -> Self {
match &tx.transaction {
Transaction::Legacy(tx) => Self::from_encoded_tx(tx, sender, encoded),
Transaction::Eip2930(tx) => Self::from_encoded_tx(tx, sender, encoded),
Transaction::Eip1559(tx) => Self::from_encoded_tx(tx, sender, encoded),
Transaction::Eip4844(tx) => Self::from_encoded_tx(tx, sender, encoded),
Transaction::Eip7702(tx) => Self::from_encoded_tx(tx, sender, encoded),
}
}
}
impl SignedTransaction for TransactionSigned {
fn tx_hash(&self) -> &TxHash {
self.hash.get_or_init(|| self.recalculate_hash())
@@ -929,222 +660,13 @@ impl SignedTransaction for TransactionSigned {
}
}
impl TryFrom<TransactionSigned> for PooledTransaction {
type Error = TransactionConversionError;
fn try_from(tx: TransactionSigned) -> Result<Self, Self::Error> {
let hash = *tx.tx_hash();
match tx {
TransactionSigned { transaction: Transaction::Legacy(tx), signature, .. } => {
Ok(Self::Legacy(Signed::new_unchecked(tx, signature, hash)))
}
TransactionSigned { transaction: Transaction::Eip2930(tx), signature, .. } => {
Ok(Self::Eip2930(Signed::new_unchecked(tx, signature, hash)))
}
TransactionSigned { transaction: Transaction::Eip1559(tx), signature, .. } => {
Ok(Self::Eip1559(Signed::new_unchecked(tx, signature, hash)))
}
TransactionSigned { transaction: Transaction::Eip7702(tx), signature, .. } => {
Ok(Self::Eip7702(Signed::new_unchecked(tx, signature, hash)))
}
// Not supported because missing blob sidecar
TransactionSigned { transaction: Transaction::Eip4844(_), .. } => {
Err(TransactionConversionError::UnsupportedForP2P)
}
}
}
}
impl From<PooledTransaction> for TransactionSigned {
fn from(value: PooledTransaction) -> Self {
match value {
PooledTransaction::Legacy(tx) => tx.into(),
PooledTransaction::Eip2930(tx) => tx.into(),
PooledTransaction::Eip1559(tx) => tx.into(),
PooledTransaction::Eip7702(tx) => tx.into(),
PooledTransaction::Eip4844(tx) => {
let (tx, signature, hash) = tx.into_parts();
Signed::new_unchecked(tx.tx, signature, hash).into()
}
}
}
}
/// Bincode-compatible transaction type serde implementations.
#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
pub(super) mod serde_bincode_compat {
use alloc::borrow::Cow;
use alloy_consensus::{
transaction::serde_bincode_compat::{TxEip1559, TxEip2930, TxEip7702, TxLegacy},
TxEip4844,
};
use alloy_primitives::{Signature, TxHash};
use reth_primitives_traits::{serde_bincode_compat::SerdeBincodeCompat, SignedTransaction};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_with::{DeserializeAs, SerializeAs};
/// Bincode-compatible [`super::Transaction`] serde implementation.
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[expect(missing_docs)]
pub enum Transaction<'a> {
Legacy(TxLegacy<'a>),
Eip2930(TxEip2930<'a>),
Eip1559(TxEip1559<'a>),
Eip4844(Cow<'a, TxEip4844>),
Eip7702(TxEip7702<'a>),
}
impl<'a> From<&'a super::Transaction> for Transaction<'a> {
fn from(value: &'a super::Transaction) -> Self {
match value {
super::Transaction::Legacy(tx) => Self::Legacy(TxLegacy::from(tx)),
super::Transaction::Eip2930(tx) => Self::Eip2930(TxEip2930::from(tx)),
super::Transaction::Eip1559(tx) => Self::Eip1559(TxEip1559::from(tx)),
super::Transaction::Eip4844(tx) => Self::Eip4844(Cow::Borrowed(tx)),
super::Transaction::Eip7702(tx) => Self::Eip7702(TxEip7702::from(tx)),
}
}
}
impl<'a> From<Transaction<'a>> for super::Transaction {
fn from(value: Transaction<'a>) -> Self {
match value {
Transaction::Legacy(tx) => Self::Legacy(tx.into()),
Transaction::Eip2930(tx) => Self::Eip2930(tx.into()),
Transaction::Eip1559(tx) => Self::Eip1559(tx.into()),
Transaction::Eip4844(tx) => Self::Eip4844(tx.into_owned()),
Transaction::Eip7702(tx) => Self::Eip7702(tx.into()),
}
}
}
/// Bincode-compatible [`super::TransactionSigned`] serde implementation.
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TransactionSigned<'a> {
hash: TxHash,
signature: Signature,
transaction: Transaction<'a>,
}
impl<'a> From<&'a super::TransactionSigned> for TransactionSigned<'a> {
fn from(value: &'a super::TransactionSigned) -> Self {
Self {
hash: *value.tx_hash(),
signature: value.signature,
transaction: Transaction::from(&value.transaction),
}
}
}
impl<'a> From<TransactionSigned<'a>> for super::TransactionSigned {
fn from(value: TransactionSigned<'a>) -> Self {
Self {
hash: value.hash.into(),
signature: value.signature,
transaction: value.transaction.into(),
}
}
}
impl SerializeAs<super::TransactionSigned> for TransactionSigned<'_> {
fn serialize_as<S>(
source: &super::TransactionSigned,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
TransactionSigned::from(source).serialize(serializer)
}
}
impl<'de> DeserializeAs<'de, super::TransactionSigned> for TransactionSigned<'de> {
fn deserialize_as<D>(deserializer: D) -> Result<super::TransactionSigned, D::Error>
where
D: Deserializer<'de>,
{
TransactionSigned::deserialize(deserializer).map(Into::into)
}
}
impl SerdeBincodeCompat for super::TransactionSigned {
type BincodeRepr<'a> = TransactionSigned<'a>;
fn as_repr(&self) -> Self::BincodeRepr<'_> {
self.into()
}
fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
repr.into()
}
}
#[cfg(test)]
mod tests {
use super::super::{serde_bincode_compat, Transaction, TransactionSigned};
use arbitrary::Arbitrary;
use rand::Rng;
use reth_testing_utils::generators;
#[test]
fn test_transaction_bincode_roundtrip() {
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct Data<'a> {
transaction: serde_bincode_compat::Transaction<'a>,
}
let mut bytes = [0u8; 1024];
generators::rng().fill(bytes.as_mut_slice());
let tx = Transaction::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
let data = Data { transaction: (&tx).into() };
let encoded = bincode::serialize(&data).unwrap();
let decoded: Data<'_> = bincode::deserialize(&encoded).unwrap();
assert_eq!(tx, decoded.transaction.into());
}
#[test]
fn test_transaction_signed_bincode_roundtrip() {
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct Data<'a> {
transaction: serde_bincode_compat::TransactionSigned<'a>,
}
let mut bytes = [0u8; 1024];
generators::rng().fill(bytes.as_mut_slice());
let tx =
TransactionSigned::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
let data = Data { transaction: (&tx).into() };
let encoded = bincode::serialize(&data).unwrap();
let decoded: Data<'_> = bincode::deserialize(&encoded).unwrap();
assert_eq!(tx, decoded.transaction.into());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_consensus::{
constants::LEGACY_TX_TYPE_ID, Block, EthereumTxEnvelope, Transaction as _, TxEip1559,
TxLegacy,
};
use alloy_eips::{
eip2718::{Decodable2718, Encodable2718},
eip7702::constants::SECP256K1N_HALF,
};
use alloy_primitives::{
address, b256, bytes, hex, Address, Bytes, Signature, TxKind, B256, U256,
};
use alloy_rlp::{Decodable, Encodable, Error as RlpError};
use alloy_consensus::EthereumTxEnvelope;
use proptest::proptest;
use proptest_arbitrary_interop::arb;
use reth_codecs::Compact;
use reth_primitives_traits::SignedTransaction;
use std::str::FromStr;
proptest! {
#[test]
@@ -1201,398 +723,4 @@ mod tests {
assert_eq!(actual_tx, expected_tx);
}
}
#[test]
fn eip_2_reject_high_s_value() {
// This pre-homestead transaction has a high `s` value and should be rejected by the
// `recover_signer` method:
// https://etherscan.io/getRawTx?tx=0x9e6e19637bb625a8ff3d052b7c2fe57dc78c55a15d258d77c43d5a9c160b0384
//
// Block number: 46170
let raw_tx = hex!("f86d8085746a52880082520894c93f2250589a6563f5359051c1ea25746549f0d889208686e75e903bc000801ba034b6fdc33ea520e8123cf5ac4a9ff476f639cab68980cd9366ccae7aef437ea0a0e517caa5f50e27ca0d1e9a92c503b4ccb039680c6d9d0c71203ed611ea4feb33");
let tx = TransactionSigned::decode_2718(&mut &raw_tx[..]).unwrap();
let signature = &tx.signature;
// make sure we know it's greater than SECP256K1N_HALF
assert!(signature.s() > SECP256K1N_HALF);
// recover signer, expect failure
let hash = *tx.tx_hash();
assert!(recover_signer(signature, hash).is_err());
// use unchecked, ensure it succeeds (the signature is valid if not for EIP-2)
assert!(recover_signer_unchecked(signature, hash).is_ok());
}
#[test]
fn encode_decode_raw_block() {
let bytes = hex!("f90288f90218a0fe21bb173f43067a9f90cfc59bbb6830a7a2929b5de4a61f372a9db28e87f9aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a061effbbcca94f0d3e02e5bd22e986ad57142acabf0cb3d129a6ad8d0f8752e94a0d911c25e97e27898680d242b7780b6faef30995c355a2d5de92e6b9a7212ad3aa0056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008003834c4b408252081e80a00000000000000000000000000000000000000000000000000000000000000000880000000000000000842806be9da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421f869f86702842806be9e82520894658bdf435d810c91414ec09147daa6db624063798203e880820a95a040ce7918eeb045ebf8c8b1887ca139d076bda00fa828a07881d442a72626c42da0156576a68e456e295e4c9cf67cf9f53151f329438916e0f24fc69d6bbb7fbacfc0c0");
let bytes_buf = &mut bytes.as_ref();
let block = Block::<TransactionSigned>::decode(bytes_buf).unwrap();
let mut encoded_buf = Vec::with_capacity(bytes.len());
block.encode(&mut encoded_buf);
assert_eq!(bytes[..], encoded_buf);
}
#[test]
fn empty_block_rlp() {
let body = alloy_consensus::BlockBody::<TransactionSigned>::default();
let mut buf = Vec::new();
body.encode(&mut buf);
let decoded = alloy_consensus::BlockBody::decode(&mut buf.as_slice()).unwrap();
assert_eq!(body, decoded);
}
#[test]
fn test_decode_empty_typed_tx() {
let input = [0x80u8];
let res = TransactionSigned::decode(&mut &input[..]).unwrap_err();
assert_eq!(RlpError::InputTooShort, res);
}
#[test]
fn raw_kind_encoding_sanity() {
// check the 0x80 encoding for Create
let mut buf = Vec::new();
TxKind::Create.encode(&mut buf);
assert_eq!(buf, vec![0x80]);
// check decoding
let buf = [0x80];
let decoded = TxKind::decode(&mut &buf[..]).unwrap();
assert_eq!(decoded, TxKind::Create);
}
#[test]
fn test_decode_create_goerli() {
// test that an example create tx from goerli decodes properly
let tx_bytes = hex!("b901f202f901ee05228459682f008459682f11830209bf8080b90195608060405234801561001057600080fd5b50610175806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630c49c36c14610030575b600080fd5b61003861004e565b604051610045919061011d565b60405180910390f35b60606020600052600f6020527f68656c6c6f2073746174656d696e64000000000000000000000000000000000060405260406000f35b600081519050919050565b600082825260208201905092915050565b60005b838110156100be5780820151818401526020810190506100a3565b838111156100cd576000848401525b50505050565b6000601f19601f8301169050919050565b60006100ef82610084565b6100f9818561008f565b93506101098185602086016100a0565b610112816100d3565b840191505092915050565b6000602082019050818103600083015261013781846100e4565b90509291505056fea264697066735822122051449585839a4ea5ac23cae4552ef8a96b64ff59d0668f76bfac3796b2bdbb3664736f6c63430008090033c080a0136ebffaa8fc8b9fda9124de9ccb0b1f64e90fbd44251b4c4ac2501e60b104f9a07eb2999eec6d185ef57e91ed099afb0a926c5b536f0155dd67e537c7476e1471");
let decoded = TransactionSigned::decode(&mut &tx_bytes[..]).unwrap();
assert_eq!(tx_bytes.len(), decoded.length());
assert_eq!(tx_bytes, &alloy_rlp::encode(decoded)[..]);
}
#[test]
fn test_decode_recover_mainnet_tx() {
// random mainnet tx <https://etherscan.io/tx/0x86718885c4b4218c6af87d3d0b0d83e3cc465df2a05c048aa4db9f1a6f9de91f>
let tx_bytes = hex!("02f872018307910d808507204d2cb1827d0094388c818ca8b9251b393131c08a736a67ccb19297880320d04823e2701c80c001a0cf024f4815304df2867a1a74e9d2707b6abda0337d2d54a4438d453f4160f190a07ac0e6b3bc9395b5b9c8b9e6d77204a236577a5b18467b9175c01de4faa208d9");
let decoded = TransactionSigned::decode_2718(&mut &tx_bytes[..]).unwrap();
assert_eq!(
decoded.recover_signer().unwrap(),
address!("0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5")
);
}
#[test]
// Test vector from https://sepolia.etherscan.io/tx/0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0
// Blobscan: https://sepolia.blobscan.com/tx/0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0
fn test_decode_recover_sepolia_4844_tx() {
use alloy_primitives::{address, b256};
// https://sepolia.etherscan.io/getRawTx?tx=0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0
let raw_tx = alloy_primitives::hex::decode("0x03f9011d83aa36a7820fa28477359400852e90edd0008252089411e9ca82a3a762b4b5bd264d4173a242e7a770648080c08504a817c800f8a5a0012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921aa00152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4a0013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7a001148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1a0011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e654901a0c8de4cced43169f9aa3d36506363b2d2c44f6c49fc1fd91ea114c86f3757077ea01e11fdd0d1934eda0492606ee0bb80a7bf8f35cc5f86ec60fe5031ba48bfd544").unwrap();
let decoded = TransactionSigned::decode_2718(&mut raw_tx.as_slice()).unwrap();
assert!(alloy_consensus::Typed2718::is_eip4844(&decoded));
assert_eq!(
decoded.recover_signer().ok(),
Some(address!("0xA83C816D4f9b2783761a22BA6FADB0eB0606D7B2"))
);
let tx = decoded.transaction;
assert_eq!(tx.to(), Some(address!("0x11E9CA82A3a762b4B5bd264d4173a242e7a77064")));
assert_eq!(
tx.blob_versioned_hashes(),
Some(
&[
b256!("0x012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921a"),
b256!("0x0152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4"),
b256!("0x013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7"),
b256!("0x01148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1"),
b256!("0x011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e6549"),
][..]
)
);
}
#[test]
fn decode_transaction_consumes_buffer() {
let bytes = &mut &hex!("b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469")[..];
let _transaction_res = TransactionSigned::decode(bytes).unwrap();
assert_eq!(
bytes.len(),
0,
"did not consume all bytes in the buffer, {:?} remaining",
bytes.len()
);
}
#[test]
fn decode_multiple_network_txs() {
let bytes = hex!("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18");
let transaction = Transaction::Legacy(TxLegacy {
chain_id: Some(4u64),
nonce: 2,
gas_price: 1000000000,
gas_limit: 100000,
to: Address::from_str("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap().into(),
value: U256::from(1000000000000000u64),
input: Bytes::default(),
});
let signature = Signature::new(
U256::from_str("0xeb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae")
.unwrap(),
U256::from_str("0x3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18")
.unwrap(),
false,
);
let hash = b256!("0xa517b206d2223278f860ea017d3626cacad4f52ff51030dc9a96b432f17f8d34");
test_decode_and_encode(&bytes, transaction, signature, Some(hash));
let bytes = hex!("f86b01843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac3960468702769bb01b2a00802ba0e24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0aa05406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da");
let transaction = Transaction::Legacy(TxLegacy {
chain_id: Some(4),
nonce: 1u64,
gas_price: 1000000000,
gas_limit: 100000,
to: Address::from_slice(&hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046")[..]).into(),
value: U256::from(693361000000000u64),
input: Default::default(),
});
let signature = Signature::new(
U256::from_str("0xe24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0a")
.unwrap(),
U256::from_str("0x5406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da")
.unwrap(),
false,
);
test_decode_and_encode(&bytes, transaction, signature, None);
let bytes = hex!("f86b0384773594008398968094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba0ce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071a03ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88");
let transaction = Transaction::Legacy(TxLegacy {
chain_id: Some(4),
nonce: 3,
gas_price: 2000000000,
gas_limit: 10000000,
to: Address::from_slice(&hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046")[..]).into(),
value: U256::from(1000000000000000u64),
input: Bytes::default(),
});
let signature = Signature::new(
U256::from_str("0xce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071")
.unwrap(),
U256::from_str("0x3ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88")
.unwrap(),
false,
);
test_decode_and_encode(&bytes, transaction, signature, None);
let bytes = hex!("b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469");
let transaction = Transaction::Eip1559(TxEip1559 {
chain_id: 4,
nonce: 26,
max_priority_fee_per_gas: 1500000000,
max_fee_per_gas: 1500000013,
gas_limit: 21_000,
to: Address::from_slice(&hex!("61815774383099e24810ab832a5b2a5425c154d5")[..]).into(),
value: U256::from(3000000000000000000u64),
input: Default::default(),
access_list: Default::default(),
});
let signature = Signature::new(
U256::from_str("0x59e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafd")
.unwrap(),
U256::from_str("0x016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469")
.unwrap(),
true,
);
test_decode_and_encode(&bytes, transaction, signature, None);
let bytes = hex!("f8650f84832156008287fb94cf7f9e66af820a19257a2108375b180b0ec491678204d2802ca035b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981a0612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860");
let transaction = Transaction::Legacy(TxLegacy {
chain_id: Some(4),
nonce: 15,
gas_price: 2200000000,
gas_limit: 34811,
to: Address::from_slice(&hex!("cf7f9e66af820a19257a2108375b180b0ec49167")[..]).into(),
value: U256::from(1234),
input: Bytes::default(),
});
let signature = Signature::new(
U256::from_str("0x35b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981")
.unwrap(),
U256::from_str("0x612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860")
.unwrap(),
true,
);
test_decode_and_encode(&bytes, transaction, signature, None);
}
fn test_decode_and_encode(
bytes: &[u8],
transaction: Transaction,
signature: Signature,
hash: Option<B256>,
) {
let expected = TransactionSigned::new_unhashed(transaction, signature);
if let Some(hash) = hash {
assert_eq!(hash, *expected.tx_hash());
}
assert_eq!(bytes.len(), expected.length());
let decoded = TransactionSigned::decode(&mut &bytes[..]).unwrap();
assert_eq!(expected, decoded);
assert_eq!(bytes, &alloy_rlp::encode(expected));
}
#[test]
fn decode_raw_tx_and_recover_signer() {
use alloy_primitives::hex_literal::hex;
// transaction is from ropsten
let hash: B256 =
hex!("559fb34c4a7f115db26cbf8505389475caaab3df45f5c7a0faa4abfa3835306c").into();
let signer: Address = hex!("641c5d790f862a58ec7abcfd644c0442e9c201b3").into();
let raw = hex!("f88b8212b085028fa6ae00830f424094aad593da0c8116ef7d2d594dd6a63241bccfc26c80a48318b64b000000000000000000000000641c5d790f862a58ec7abcfd644c0442e9c201b32aa0a6ef9e170bca5ffb7ac05433b13b7043de667fbb0b4a5e45d3b54fb2d6efcc63a0037ec2c05c3d60c5f5f78244ce0a3859e3a18a36c61efb061b383507d3ce19d2");
let mut pointer = raw.as_ref();
let tx = TransactionSigned::decode(&mut pointer).unwrap();
assert_eq!(*tx.tx_hash(), hash, "Expected same hash");
let recovered = tx.recover_signer().expect("Recovering signer should pass");
assert_eq!(recovered, signer);
}
#[test]
fn test_envelop_encode() {
// random tx: <https://etherscan.io/getRawTx?tx=0x9448608d36e721ef403c53b00546068a6474d6cbab6816c3926de449898e7bce>
let input = hex!("02f871018302a90f808504890aef60826b6c94ddf4c5025d1a5742cf12f74eec246d4432c295e487e09c3bbcc12b2b80c080a0f21a4eacd0bf8fea9c5105c543be5a1d8c796516875710fafafdf16d16d8ee23a001280915021bb446d1973501a67f93d2b38894a514b976e7b46dc2fe54598d76");
let decoded = TransactionSigned::decode(&mut &input[..]).unwrap();
let encoded = decoded.encoded_2718();
assert_eq!(encoded[..], input);
}
#[test]
fn test_envelop_decode() {
// random tx: <https://etherscan.io/getRawTx?tx=0x9448608d36e721ef403c53b00546068a6474d6cbab6816c3926de449898e7bce>
let input = bytes!("02f871018302a90f808504890aef60826b6c94ddf4c5025d1a5742cf12f74eec246d4432c295e487e09c3bbcc12b2b80c080a0f21a4eacd0bf8fea9c5105c543be5a1d8c796516875710fafafdf16d16d8ee23a001280915021bb446d1973501a67f93d2b38894a514b976e7b46dc2fe54598d76");
let decoded = TransactionSigned::decode_2718(&mut input.as_ref()).unwrap();
let encoded = decoded.encoded_2718();
assert_eq!(encoded, input);
}
#[test]
fn test_decode_tx() {
// some random transactions pulled from hive tests
let data = hex!("b86f02f86c0705843b9aca008506fc23ac00830124f89400000000000000000000000000000000000003160180c001a00293c713e2f1eab91c366621ff2f867e05ad7e99d4aa5d069aafeb9e1e8c9b6aa05ec6c0605ff20b57c90a6484ec3b0509e5923733d06f9b69bee9a2dabe4f1352");
let tx = TransactionSigned::decode(&mut data.as_slice()).unwrap();
let mut b = Vec::with_capacity(data.len());
tx.encode(&mut b);
assert_eq!(data.as_slice(), b.as_slice());
let data = hex!("f865048506fc23ac00830124f8940000000000000000000000000000000000000316018032a06b8fdfdcb84790816b7af85b19305f493665fe8b4e7c51ffdd7cc144cd776a60a028a09ab55def7b8d6602ba1c97a0ebbafe64ffc9c8e89520cec97a8edfb2ebe9");
let tx = TransactionSigned::decode(&mut data.as_slice()).unwrap();
let mut b = Vec::with_capacity(data.len());
tx.encode(&mut b);
assert_eq!(data.as_slice(), b.as_slice());
}
// <https://etherscan.io/tx/0x280cde7cdefe4b188750e76c888f13bd05ce9a4d7767730feefe8a0e50ca6fc4>
#[test]
fn recover_legacy_singer() {
let data = hex!("f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8");
let tx = TransactionSigned::fallback_decode(&mut data.as_slice()).unwrap();
assert_eq!(tx.ty(), LEGACY_TX_TYPE_ID);
let sender = tx.recover_signer().unwrap();
assert_eq!(sender, address!("0xa12e1462d0ceD572f396F58B6E2D03894cD7C8a4"));
}
// <https://github.com/alloy-rs/alloy/issues/141>
// <https://etherscan.io/tx/0xce4dc6d7a7549a98ee3b071b67e970879ff51b5b95d1c340bacd80fa1e1aab31>
#[test]
fn recover_enveloped() {
let data = hex!("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8");
let tx = TransactionSigned::decode_2718(&mut data.as_slice()).unwrap();
let sender = tx.recover_signer().unwrap();
assert_eq!(sender, address!("0x001e2b7dE757bA469a57bF6b23d982458a07eFcE"));
assert_eq!(tx.to(), Some(address!("0xD9e1459A7A482635700cBc20BBAF52D495Ab9C96")));
assert_eq!(tx.input().as_ref(), hex!("1b55ba3a"));
let encoded = tx.encoded_2718();
assert_eq!(encoded.as_ref(), data.to_vec());
}
// <https://github.com/paradigmxyz/reth/issues/7750>
// <https://etherscan.io/tx/0x2084b8144eea4031c2fa7dfe343498c5e665ca85ed17825f2925f0b5b01c36ac>
#[test]
fn recover_pre_eip2() {
let data = hex!("f8ea0c850ba43b7400832dc6c0942935aa0a2d2fbb791622c29eb1c117b65b7a908580b884590528a9000000000000000000000001878ace42092b7f1ae1f28d16c1272b1aa80ca4670000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000d02ab486cedc0000000000000000000000000000000000000000000000000000557fe293cabc08cf1ca05bfaf3fda0a56b49cc78b22125feb5ae6a99d2b4781f00507d8b02c173771c85a0b5da0dbe6c5bc53740d0071fc83eb17ba0f709e49e9ae7df60dee625ef51afc5");
let tx = TransactionSigned::decode_2718(&mut data.as_slice()).unwrap();
let sender = tx.recover_signer();
assert!(sender.is_err());
let sender = tx.recover_signer_unchecked().unwrap();
assert_eq!(sender, address!("0x7e9e359edf0dbacf96a9952fa63092d919b0842b"));
}
#[test]
fn transaction_signed_no_hash_zstd_codec() {
// will use same signature everywhere.
// We don't need signature to match tx, just decoded to the same signature
let signature = Signature::new(
U256::from_str("0xeb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae")
.unwrap(),
U256::from_str("0x3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18")
.unwrap(),
false,
);
let inputs: Vec<Vec<u8>> = vec![
vec![],
vec![0],
vec![255],
vec![1u8; 31],
vec![255u8; 31],
vec![1u8; 32],
vec![255u8; 32],
vec![1u8; 64],
vec![255u8; 64],
];
for input in inputs {
let transaction = Transaction::Legacy(TxLegacy {
chain_id: Some(4u64),
nonce: 2,
gas_price: 1000000000,
gas_limit: 100000,
to: Address::from_str("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap().into(),
value: U256::from(1000000000000000u64),
input: Bytes::from(input),
});
let tx = TransactionSigned::new_unhashed(transaction, signature);
test_transaction_signed_to_from_compact(tx);
}
}
fn test_transaction_signed_to_from_compact(tx: TransactionSigned) {
// zstd aware `to_compact`
let mut buff: Vec<u8> = Vec::new();
let written_bytes = tx.to_compact(&mut buff);
let (decoded, _) = TransactionSigned::from_compact(&buff, written_bytes);
assert_eq!(tx, decoded);
}
#[test]
fn create_txs_disallowed_for_eip4844() {
let data =
[3, 208, 128, 128, 123, 128, 120, 128, 129, 129, 128, 192, 129, 129, 192, 128, 128, 9];
let res = TransactionSigned::decode_2718(&mut &data[..]);
assert!(res.is_err());
}
}

View File

@@ -97,7 +97,6 @@ full = [
"network",
]
alloy-compat = ["reth-ethereum-primitives/alloy-compat"]
cli = ["dep:reth-ethereum-cli"]
consensus = [
"dep:reth-consensus",

Binary file not shown.

View File

@@ -9,7 +9,6 @@ use reth_network::{
NetworkEvent, NetworkEventListenerProvider, Peers,
};
use reth_network_api::{events::PeerEvent, PeerKind, PeersInfo};
use reth_primitives_traits::SignedTransaction;
use reth_provider::test_utils::{ExtendedAccount, MockEthProvider};
use reth_transaction_pool::{test_utils::TransactionGenerator, PoolTransaction, TransactionPool};
use std::sync::Arc;
@@ -115,7 +114,8 @@ async fn test_tx_propagation_policy_trusted_only() {
// ensure peer1 now receives the pending txs from peer0
let mut buff = Vec::with_capacity(2);
peer1_tx_listener.recv_many(&mut buff, 2).await;
buff.push(peer1_tx_listener.recv().await.unwrap());
buff.push(peer1_tx_listener.recv().await.unwrap());
assert!(buff.contains(&hash_1));
}

View File

@@ -88,9 +88,7 @@ c-kzg = [
"alloy-consensus/kzg",
"alloy-eips/kzg",
]
alloy-compat = [
"reth-ethereum-primitives/alloy-compat",
]
alloy-compat = []
test-utils = [
"reth-primitives-traits/test-utils",
"arbitrary",

View File

@@ -12,7 +12,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let raw =hex!("f88b8212b085028fa6ae00830f424094aad593da0c8116ef7d2d594dd6a63241bccfc26c80a48318b64b000000000000000000000000641c5d790f862a58ec7abcfd644c0442e9c201b32aa0a6ef9e170bca5ffb7ac05433b13b7043de667fbb0b4a5e45d3b54fb2d6efcc63a0037ec2c05c3d60c5f5f78244ce0a3859e3a18a36c61efb061b383507d3ce19d2");
let mut pointer = raw.as_ref();
let tx = TransactionSigned::decode(&mut pointer).unwrap();
tx.recover_signer().unwrap();
SignedTransaction::recover_signer(&tx).unwrap();
}
)
});

View File

@@ -4,7 +4,6 @@
//!
//! ## Feature Flags
//!
//! - `alloy-compat`: Adds compatibility conversions for certain alloy types.
//! - `arbitrary`: Adds `proptest` and `arbitrary` support for primitive types.
//! - `test-utils`: Export utilities for testing
//! - `reth-codec`: Enables db codec support for reth types including zstd compression for certain

View File

@@ -90,7 +90,6 @@ mod tests {
Itertools,
};
use reth_db_api::tables;
use reth_primitives_traits::SignedTransaction;
use reth_provider::{DatabaseProviderFactory, PruneCheckpointReader};
use reth_prune_types::{PruneCheckpoint, PruneMode, PruneProgress, PruneSegment};
use reth_stages::test_utils::{StorageKind, TestStageDB};

View File

@@ -117,7 +117,6 @@ mod tests {
Itertools,
};
use reth_db_api::tables;
use reth_primitives_traits::SignedTransaction;
use reth_provider::{DatabaseProviderFactory, PruneCheckpointReader};
use reth_prune_types::{
PruneCheckpoint, PruneInterruptReason, PruneMode, PruneProgress, PruneSegment,

View File

@@ -3,7 +3,7 @@
use alloy_consensus::{transaction::TransactionMeta, BlockHeader};
use alloy_rpc_types_eth::{BlockId, TransactionReceipt};
use reth_chainspec::{ChainSpecProvider, EthChainSpec};
use reth_primitives_traits::{BlockBody, SignedTransaction};
use reth_primitives_traits::BlockBody;
use reth_rpc_eth_api::{
helpers::{EthBlocks, LoadBlock, LoadPendingBlock, LoadReceipt, SpawnBlocking},
types::RpcTypes,

View File

@@ -1,6 +1,6 @@
//! L1 `eth` API types.
use alloy_consensus::{Transaction as _, TxEnvelope};
use alloy_consensus::{EthereumTxEnvelope, Transaction as _, TxEnvelope};
use alloy_network::{Ethereum, Network};
use alloy_primitives::Signature;
use alloy_rpc_types::TransactionRequest;
@@ -74,7 +74,8 @@ where
// Create an empty signature for the transaction.
let signature = Signature::new(Default::default(), Default::default(), false);
Ok(TransactionSigned::new_unhashed(tx.into(), signature))
// TODO(mattsse): simplify
Ok(EthereumTxEnvelope::new_unhashed(tx, signature).into())
}
fn otterscan_api_truncate_input(tx: &mut Self::Transaction) {

View File

@@ -220,7 +220,7 @@ mod tests {
models::StoredBlockBodyIndices,
};
use reth_ethereum_primitives::Block;
use reth_primitives_traits::{SealedBlock, SignedTransaction};
use reth_primitives_traits::SealedBlock;
use reth_provider::providers::StaticFileWriter;
use reth_testing_utils::generators::{
self, random_block_range, random_contract_account_range, BlockRangeParams,

View File

@@ -45,7 +45,10 @@ use utils::*;
mod tests {
use super::*;
use crate::test_utils::{StorageKind, TestStageDB};
use alloy_primitives::{address, hex_literal::hex, keccak256, BlockNumber, B256, U256};
use alloy_consensus::{SignableTransaction, TxLegacy};
use alloy_primitives::{
address, hex_literal::hex, keccak256, BlockNumber, Signature, B256, U256,
};
use alloy_rlp::Decodable;
use reth_chainspec::ChainSpecBuilder;
use reth_db::mdbx::{cursor::Cursor, RW};
@@ -502,7 +505,7 @@ mod tests {
&db,
current + 2,
Some(PipelineTarget::Unwind(89)),
&Default::default(),
&TxLegacy::default().into_signed(Signature::test_signature()).into(),
);
// Fill the gap, and ensure no unwind is necessary.
@@ -510,7 +513,7 @@ mod tests {
&db,
current + 1,
None,
&Default::default(),
&TxLegacy::default().into_signed(Signature::test_signature()).into(),
);
}

View File

@@ -172,7 +172,7 @@ mod tests {
};
use alloy_primitives::B256;
use reth_ethereum_primitives::Block;
use reth_primitives_traits::{SealedBlock, SignedTransaction};
use reth_primitives_traits::SealedBlock;
use reth_provider::{
providers::StaticFileWriter, TransactionsProvider, TransactionsProviderExt,
};

View File

@@ -376,7 +376,7 @@ mod tests {
use assert_matches::assert_matches;
use reth_db_api::cursor::DbCursorRO;
use reth_ethereum_primitives::{Block, TransactionSigned};
use reth_primitives_traits::{SealedBlock, SignedTransaction};
use reth_primitives_traits::SealedBlock;
use reth_provider::{
providers::StaticFileWriter, BlockBodyIndicesProvider, DatabaseProviderFactory,
PruneCheckpointWriter, StaticFileProviderFactory, TransactionsProvider,

View File

@@ -660,7 +660,6 @@ mod tests {
test_utils::{create_test_static_files_dir, ERROR_TEMPDIR},
};
use reth_db_api::tables;
use reth_primitives_traits::SignedTransaction;
use reth_prune_types::{PruneMode, PruneModes};
use reth_storage_errors::provider::ProviderError;
use reth_testing_utils::generators::{self, random_block, random_header, BlockParams};

View File

@@ -57,8 +57,8 @@ mod tests {
use crate::{
test_utils::create_test_provider_factory, HeaderProvider, StaticFileProviderFactory,
};
use alloy_consensus::{Header, Transaction};
use alloy_primitives::{BlockHash, TxNumber, B256, U256};
use alloy_consensus::{Header, SignableTransaction, Transaction, TxLegacy};
use alloy_primitives::{BlockHash, Signature, TxNumber, B256, U256};
use rand::seq::SliceRandom;
use reth_db::test_utils::create_test_static_files_dir;
use reth_db_api::{
@@ -315,7 +315,7 @@ mod tests {
next_tx_num: &mut u64,
) {
let mut receipt = Receipt::default();
let mut tx = TransactionSigned::default();
let mut tx = TxLegacy::default();
for block in block_range.clone() {
writer.increment_block(block).unwrap();
@@ -328,7 +328,9 @@ mod tests {
writer.append_receipt(*next_tx_num, &receipt).unwrap();
} else {
// Used as ID for validation
tx.transaction_mut().set_nonce(*next_tx_num);
tx.nonce = *next_tx_num;
let tx: TransactionSigned =
tx.clone().into_signed(Signature::test_signature()).into();
writer.append_transaction(*next_tx_num, &tx).unwrap();
}
*next_tx_num += 1;

View File

@@ -94,7 +94,7 @@ pub(crate) static TEST_BLOCK: LazyLock<SealedBlock<reth_ethereum_primitives::Blo
hex!("cf7b274520720b50e6a4c3e5c4d553101f44945396827705518ce17cb7219a42").into(),
),
BlockBody {
transactions: vec![TransactionSigned::new(
transactions: vec![TransactionSigned::new_unhashed(
Transaction::Legacy(TxLegacy {
gas_price: 10,
gas_limit: 400_000,
@@ -111,8 +111,7 @@ pub(crate) static TEST_BLOCK: LazyLock<SealedBlock<reth_ethereum_primitives::Blo
)
.unwrap(),
false,
),
b256!("0x3541dd1d17e76adeb25dcf2b0a9b60a1669219502e58dcf26a2beafbfb550397"),
)
)],
..Default::default()
},

View File

@@ -24,7 +24,6 @@ use reth_execution_types::ExecutionOutcome;
use reth_node_types::NodeTypes;
use reth_primitives_traits::{
Account, Bytecode, GotExpected, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader,
SignedTransaction,
};
use reth_prune_types::PruneModes;
use reth_stages_types::{StageCheckpoint, StageId};

View File

@@ -5,9 +5,7 @@ use alloy_primitives::{Address, Bytes, TxKind, B256, U256};
use rand::{Rng, RngCore};
use reth_chainspec::MAINNET;
use reth_ethereum_primitives::{Transaction, TransactionSigned};
use reth_primitives_traits::{
crypto::secp256k1::sign_message, transaction::signed::SignedTransaction,
};
use reth_primitives_traits::crypto::secp256k1::sign_message;
/// A generator for transactions for testing purposes.
#[derive(Debug)]

View File

@@ -13,8 +13,8 @@ use alloy_consensus::{
LEGACY_TX_TYPE_ID,
},
transaction::PooledTransaction,
EthereumTxEnvelope, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip7702, TxEnvelope,
TxLegacy, TxType, Typed2718,
EthereumTxEnvelope, Signed, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip7702,
TxEnvelope, TxLegacy, TxType, Typed2718,
};
use alloy_eips::{
eip1559::MIN_PROTOCOL_BASE_FEE,
@@ -27,10 +27,11 @@ use paste::paste;
use rand::{distr::Uniform, prelude::Distribution};
use reth_ethereum_primitives::{Transaction, TransactionSigned};
use reth_primitives_traits::{
transaction::error::{TransactionConversionError, TryFromRecoveredTransactionError},
InMemorySize, Recovered, SignedTransaction,
transaction::error::TryFromRecoveredTransactionError, InMemorySize, Recovered,
SignedTransaction,
};
use alloy_consensus::error::ValueError;
use alloy_eips::eip4844::env_settings::KzgSettings;
use rand::distr::weighted::WeightedIndex;
use std::{ops::Range, sync::Arc, time::Instant, vec::IntoIter};
@@ -667,7 +668,7 @@ impl MockTransaction {
}
impl PoolTransaction for MockTransaction {
type TryFromConsensusError = TransactionConversionError;
type TryFromConsensusError = ValueError<EthereumTxEnvelope<TxEip4844>>;
type Consensus = TransactionSigned;
@@ -918,8 +919,7 @@ impl TryFrom<Recovered<TransactionSigned>> for MockTransaction {
let hash = *transaction.tx_hash();
let size = transaction.size();
#[expect(unreachable_patterns)]
match transaction.into_transaction() {
match transaction.into_typed_transaction() {
Transaction::Legacy(TxLegacy {
chain_id,
nonce,
@@ -1046,7 +1046,6 @@ impl TryFrom<Recovered<TransactionSigned>> for MockTransaction {
size,
cost: U256::from(gas_limit) * U256::from(max_fee_per_gas) + value,
}),
tx => Err(TryFromRecoveredTransactionError::UnsupportedTransactionType(tx.ty())),
}
}
}
@@ -1167,10 +1166,12 @@ impl From<Recovered<PooledTransaction>> for MockTransaction {
impl From<MockTransaction> for Recovered<TransactionSigned> {
fn from(tx: MockTransaction) -> Self {
let signed_tx =
TransactionSigned::new(tx.clone().into(), Signature::test_signature(), *tx.hash());
Self::new_unchecked(signed_tx, tx.sender())
let hash = *tx.hash();
let sender = tx.sender();
let tx = Transaction::from(tx);
let tx: TransactionSigned =
Signed::new_unchecked(tx, Signature::test_signature(), hash).into();
Self::new_unchecked(tx, sender)
}
}

View File

@@ -8,7 +8,9 @@ use crate::{
validate::ValidPoolTransaction,
AllTransactionsEvents,
};
use alloy_consensus::{transaction::PooledTransaction, BlockHeader, Signed, Typed2718};
use alloy_consensus::{
error::ValueError, transaction::PooledTransaction, BlockHeader, Signed, Typed2718,
};
use alloy_eips::{
eip2718::Encodable2718,
eip2930::AccessList,
@@ -21,12 +23,9 @@ use alloy_eips::{
use alloy_primitives::{Address, Bytes, TxHash, TxKind, B256, U256};
use futures_util::{ready, Stream};
use reth_eth_wire_types::HandleMempoolData;
use reth_ethereum_primitives::{Transaction, TransactionSigned};
use reth_ethereum_primitives::TransactionSigned;
use reth_execution_types::ChangedAccount;
use reth_primitives_traits::{
transaction::error::TransactionConversionError, Block, InMemorySize, Recovered, SealedBlock,
SignedTransaction,
};
use reth_primitives_traits::{Block, InMemorySize, Recovered, SealedBlock, SignedTransaction};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{
@@ -1121,7 +1120,7 @@ impl<T: SignedTransaction> EthPooledTransaction<T> {
}
impl PoolTransaction for EthPooledTransaction {
type TryFromConsensusError = TransactionConversionError;
type TryFromConsensusError = ValueError<TransactionSigned>;
type Consensus = TransactionSigned;
@@ -1307,8 +1306,8 @@ impl EthPoolTransaction for EthPooledTransaction {
sidecar: &BlobTransactionSidecar,
settings: &KzgSettings,
) -> Result<(), BlobTransactionValidationError> {
match self.transaction.transaction() {
Transaction::Eip4844(tx) => tx.validate_blob(sidecar, settings),
match self.transaction.inner().as_eip4844() {
Some(tx) => tx.tx().validate_blob(sidecar, settings),
_ => Err(BlobTransactionValidationError::NotBlobTransaction(self.ty())),
}
}

View File

@@ -990,7 +990,6 @@ mod tests {
use alloy_eips::eip2718::Decodable2718;
use alloy_primitives::{hex, U256};
use reth_ethereum_primitives::PooledTransaction;
use reth_primitives_traits::SignedTransaction;
use reth_provider::test_utils::{ExtendedAccount, MockEthProvider};
fn get_transaction() -> EthPooledTransaction {

View File

@@ -10,7 +10,7 @@ use futures_util::{stream::FuturesUnordered, Future, Stream, StreamExt};
use reqwest::{Error, StatusCode};
use reth_ethereum::{
pool::{BlobStoreError, TransactionPoolExt},
primitives::{RecoveredBlock, SignedTransaction},
primitives::RecoveredBlock,
provider::CanonStateNotification,
};
use serde::{Deserialize, Serialize};

View File

@@ -15,7 +15,7 @@ use reth::{
use reth_ethereum::{
chainspec::ChainSpec,
node::{
core::{args::RpcServerArgs, node_config::NodeConfig, primitives::SignedTransaction},
core::{args::RpcServerArgs, node_config::NodeConfig},
EthereumNode,
},
provider::CanonStateSubscriptions,

View File

@@ -4,9 +4,7 @@ use alloy_primitives::{Address, B256};
use reth_ethereum::{
chainspec::ChainSpecBuilder,
node::EthereumNode,
primitives::{
transaction::signed::SignedTransaction, AlloyBlockHeader, SealedBlock, SealedHeader,
},
primitives::{AlloyBlockHeader, SealedBlock, SealedHeader},
provider::{
providers::ReadOnlyConfig, AccountReader, BlockReader, BlockSource, HeaderProvider,
ReceiptProvider, StateProvider, TransactionsProvider,

View File

@@ -172,7 +172,7 @@ pub fn sign_tx_with_key_pair(key_pair: Keypair, tx: Transaction) -> TransactionS
let signature =
sign_message(B256::from_slice(&key_pair.secret_bytes()[..]), tx.signature_hash()).unwrap();
TransactionSigned::new_unhashed(tx, signature)
tx.into_signed(signature).into()
}
/// Generates a new random [Keypair].
@@ -211,7 +211,7 @@ pub fn random_block<R: Rng>(
let tx_count = block_params.tx_count.unwrap_or_else(|| rng.random::<u8>());
let transactions: Vec<TransactionSigned> =
(0..tx_count).map(|_| random_signed_tx(rng)).collect();
let total_gas = transactions.iter().fold(0, |sum, tx| sum + tx.transaction().gas_limit());
let total_gas = transactions.iter().fold(0, |sum, tx| sum + tx.gas_limit());
// Generate ommers
let ommers_count = block_params.ommers_count.unwrap_or_else(|| rng.random_range(0..2));
@@ -488,10 +488,7 @@ mod tests {
use alloy_consensus::TxEip1559;
use alloy_eips::eip2930::AccessList;
use alloy_primitives::{hex, Signature};
use reth_primitives_traits::{
crypto::secp256k1::{public_key_to_address, sign_message},
SignedTransaction,
};
use reth_primitives_traits::crypto::secp256k1::{public_key_to_address, sign_message};
use std::str::FromStr;
#[test]
@@ -518,7 +515,7 @@ mod tests {
sign_message(B256::from_slice(&key_pair.secret_bytes()[..]), signature_hash)
.unwrap();
let signed = TransactionSigned::new_unhashed(tx.clone(), signature);
let signed: TransactionSigned = tx.clone().into_signed(signature).into();
let recovered = signed.recover_signer().unwrap();
let expected = public_key_to_address(key_pair.public_key());