From 8efe441cc0226abb4a374d022a57a1dd2bc7b396 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 17 Jan 2025 04:22:21 +0400 Subject: [PATCH] feat: use `reth-ethereum-primitives` (#13830) --- Cargo.lock | 25 +- bin/reth-bench/Cargo.toml | 2 +- .../src/commands/debug_cmd/build_block.rs | 11 +- crates/chain-state/src/test_utils.rs | 7 +- crates/engine/util/Cargo.toml | 1 - crates/engine/util/src/reorg.rs | 8 +- crates/ethereum/evm/src/execute.rs | 2 +- crates/ethereum/payload/src/lib.rs | 15 +- crates/ethereum/primitives/Cargo.toml | 26 +- .../ethereum/primitives/src/alloy_compat.rs | 44 + crates/ethereum/primitives/src/lib.rs | 3 + crates/ethereum/primitives/src/receipt.rs | 136 ++ crates/ethereum/primitives/src/transaction.rs | 329 +++- crates/evm/execution-types/Cargo.toml | 5 +- crates/net/downloaders/Cargo.toml | 1 - .../downloaders/src/receipt_file_client.rs | 11 +- crates/net/network/src/transactions/mod.rs | 2 +- .../network/src/transactions/validation.rs | 5 +- .../network/tests/it/big_pooled_txs_req.rs | 1 + crates/net/network/tests/it/txgossip.rs | 3 +- crates/node/core/Cargo.toml | 2 +- crates/optimism/cli/Cargo.toml | 1 - crates/optimism/cli/src/ovm_file_codec.rs | 76 +- crates/optimism/cli/src/receipt_file_codec.rs | 101 +- crates/optimism/consensus/Cargo.toml | 2 +- crates/optimism/evm/Cargo.toml | 1 - crates/optimism/evm/src/l1.rs | 8 +- crates/optimism/node/Cargo.toml | 1 - crates/optimism/payload/Cargo.toml | 1 - crates/optimism/payload/src/builder.rs | 7 +- crates/optimism/primitives/Cargo.toml | 1 + crates/optimism/primitives/src/receipt.rs | 193 ++ crates/optimism/rpc/Cargo.toml | 1 - crates/optimism/storage/Cargo.toml | 1 - crates/optimism/storage/src/lib.rs | 4 +- crates/primitives/Cargo.toml | 86 +- crates/primitives/src/alloy_compat.rs | 189 -- crates/primitives/src/block.rs | 2 +- crates/primitives/src/lib.rs | 3 - crates/primitives/src/proofs.rs | 11 +- crates/primitives/src/receipt.rs | 523 +---- crates/primitives/src/transaction/mod.rs | 1701 +---------------- crates/primitives/src/transaction/pooled.rs | 3 +- crates/primitives/src/transaction/tx_type.rs | 303 +-- .../src/segments/user/transaction_lookup.rs | 3 +- crates/rpc/rpc/src/debug.rs | 6 +- crates/rpc/rpc/src/eth/bundle.rs | 4 +- .../rpc/rpc/src/eth/helpers/pending_block.rs | 2 +- crates/rpc/rpc/src/eth/helpers/types.rs | 3 +- .../stages/src/stages/hashing_storage.rs | 3 +- crates/stages/stages/src/stages/tx_lookup.rs | 7 +- .../codecs/src/alloy/transaction/optimism.rs | 2 +- crates/storage/db-api/Cargo.toml | 1 - crates/storage/db/Cargo.toml | 2 +- crates/storage/provider/Cargo.toml | 1 - .../src/providers/blockchain_provider.rs | 6 +- .../provider/src/providers/database/mod.rs | 7 +- .../storage/provider/src/test_utils/mock.rs | 6 +- crates/transaction-pool/src/test_utils/gen.rs | 10 +- .../transaction-pool/src/test_utils/mock.rs | 4 +- crates/transaction-pool/src/traits.rs | 10 +- .../src/mined_sidecar.rs | 6 +- examples/custom-beacon-withdrawals/Cargo.toml | 4 +- examples/custom-dev-node/src/main.rs | 4 +- examples/db-access/Cargo.toml | 1 + examples/db-access/src/main.rs | 14 +- testing/testing-utils/src/generators.rs | 2 +- 67 files changed, 941 insertions(+), 3025 deletions(-) create mode 100644 crates/ethereum/primitives/src/alloy_compat.rs delete mode 100644 crates/primitives/src/alloy_compat.rs diff --git a/Cargo.lock b/Cargo.lock index 09258315c..1aa711563 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3050,6 +3050,7 @@ dependencies = [ "reth-node-ethereum", "reth-node-types", "reth-primitives", + "reth-primitives-traits", "reth-provider", ] @@ -5453,7 +5454,6 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", "alloy-serde", - "arbitrary", "derive_more", "op-alloy-consensus", "serde", @@ -7444,9 +7444,13 @@ version = "1.1.5" dependencies = [ "alloy-consensus", "alloy-eips", + "alloy-network", "alloy-primitives", "alloy-rlp", + "alloy-rpc-types", + "alloy-serde", "arbitrary", + "bincode", "derive_more", "modular-bitfield", "once_cell", @@ -7455,7 +7459,9 @@ dependencies = [ "rand 0.8.5", "reth-codecs", "reth-primitives-traits", + "reth-testing-utils", "reth-zstd-compressors", + "revm-primitives", "secp256k1", "serde", "test-fuzz", @@ -8552,42 +8558,27 @@ dependencies = [ "alloy-consensus", "alloy-eips", "alloy-genesis", - "alloy-network", "alloy-primitives", "alloy-rlp", - "alloy-rpc-types", - "alloy-serde", "alloy-trie", "arbitrary", "assert_matches", - "bincode", - "bytes", "c-kzg", "codspeed-criterion-compat", "derive_more", - "modular-bitfield", "once_cell", - "op-alloy-consensus", - "op-alloy-rpc-types", "pprof", "proptest", "proptest-arbitrary-interop", - "rand 0.8.5", "reth-chainspec", "reth-codecs", "reth-ethereum-forks", + "reth-ethereum-primitives", "reth-primitives-traits", "reth-static-file-types", - "reth-testing-utils", "reth-trie-common", - "reth-zstd-compressors", - "revm-primitives", - "rstest", - "secp256k1", "serde", "serde_json", - "serde_with", - "test-fuzz", ] [[package]] diff --git a/bin/reth-bench/Cargo.toml b/bin/reth-bench/Cargo.toml index 018207613..65ea9fb90 100644 --- a/bin/reth-bench/Cargo.toml +++ b/bin/reth-bench/Cargo.toml @@ -96,7 +96,7 @@ min-info-logs = ["tracing/release_max_level_info"] min-debug-logs = ["tracing/release_max_level_debug"] min-trace-logs = ["tracing/release_max_level_trace"] -optimism = ["reth-primitives/optimism", "reth-node-core/optimism"] +optimism = ["reth-node-core/optimism"] # no-op feature flag for switching between the `optimism` and default functionality in CI matrices ethereum = [] diff --git a/bin/reth/src/commands/debug_cmd/build_block.rs b/bin/reth/src/commands/debug_cmd/build_block.rs index 396fe6214..88f2b322b 100644 --- a/bin/reth/src/commands/debug_cmd/build_block.rs +++ b/bin/reth/src/commands/debug_cmd/build_block.rs @@ -24,8 +24,11 @@ use reth_execution_types::ExecutionOutcome; use reth_fs_util as fs; use reth_node_api::{BlockTy, EngineApiMessageVersion, PayloadBuilderAttributes}; use reth_node_ethereum::{consensus::EthBeaconConsensus, EthEvmConfig, EthExecutorProvider}; -use reth_primitives::{EthPrimitives, SealedBlock, SealedHeader, Transaction, TransactionSigned}; -use reth_primitives_traits::Block as _; +use reth_primitives::{ + transaction::SignedTransactionIntoRecoveredExt, EthPrimitives, SealedBlock, SealedHeader, + Transaction, TransactionSigned, +}; +use reth_primitives_traits::{Block as _, SignedTransaction}; use reth_provider::{ providers::{BlockchainProvider, ProviderNodeTypes}, BlockHashReader, BlockReader, BlockWriter, ChainSpecProvider, ProviderFactory, @@ -163,7 +166,7 @@ impl> Command { for tx_bytes in &self.transactions { debug!(target: "reth::cli", bytes = ?tx_bytes, "Decoding transaction"); let transaction = TransactionSigned::decode(&mut &Bytes::from_str(tx_bytes)?[..])? - .into_ecrecovered() + .try_ecrecovered() .ok_or_else(|| eyre::eyre!("failed to recover tx"))?; let encoded_length = match &transaction.transaction { @@ -183,7 +186,7 @@ impl> Command { let encoded_length = pooled.encode_2718_len(); // insert the blob into the store - blob_store.insert(transaction.hash(), sidecar)?; + blob_store.insert(*transaction.tx_hash(), sidecar)?; encoded_length } diff --git a/crates/chain-state/src/test_utils.rs b/crates/chain-state/src/test_utils.rs index 9f8135b2a..1eb68e670 100644 --- a/crates/chain-state/src/test_utils.rs +++ b/crates/chain-state/src/test_utils.rs @@ -4,7 +4,9 @@ use crate::{ in_memory::ExecutedBlock, CanonStateNotification, CanonStateNotifications, CanonStateSubscriptions, }; -use alloy_consensus::{Header, Transaction as _, TxEip1559, EMPTY_ROOT_HASH}; +use alloy_consensus::{ + Header, SignableTransaction, Transaction as _, TxEip1559, TxReceipt, EMPTY_ROOT_HASH, +}; use alloy_eips::{ eip1559::{ETHEREUM_BLOCK_GAS_LIMIT, INITIAL_BASE_FEE}, eip7685::Requests, @@ -17,6 +19,7 @@ use reth_chainspec::{ChainSpec, EthereumHardfork, MIN_TRANSACTION_GAS}; use reth_execution_types::{Chain, ExecutionOutcome}; use reth_primitives::{ proofs::{calculate_receipt_root, calculate_transaction_root, calculate_withdrawals_root}, + transaction::SignedTransactionIntoRecoveredExt, BlockBody, EthPrimitives, NodePrimitives, Receipt, Receipts, RecoveredBlock, RecoveredTx, SealedBlock, SealedHeader, Transaction, TransactionSigned, }; @@ -131,7 +134,7 @@ impl TestBlockBuilder { cumulative_gas_used: (idx as u64 + 1) * MIN_TRANSACTION_GAS, ..Default::default() } - .with_bloom() + .into_with_bloom() }) .collect::>(); diff --git a/crates/engine/util/Cargo.toml b/crates/engine/util/Cargo.toml index 1d1a524e2..3e5f333e8 100644 --- a/crates/engine/util/Cargo.toml +++ b/crates/engine/util/Cargo.toml @@ -52,7 +52,6 @@ tracing.workspace = true [features] optimism = [ - "reth-primitives/optimism", "reth-provider/optimism", "revm-primitives/optimism", ] diff --git a/crates/engine/util/src/reorg.rs b/crates/engine/util/src/reorg.rs index 2136c92a0..4f8ed0ce3 100644 --- a/crates/engine/util/src/reorg.rs +++ b/crates/engine/util/src/reorg.rs @@ -21,7 +21,7 @@ use reth_payload_validator::ExecutionPayloadValidator; use reth_primitives::{ proofs, transaction::SignedTransactionIntoRecoveredExt, Block, BlockBody, Receipt, Receipts, }; -use reth_primitives_traits::block::Block as _; +use reth_primitives_traits::{block::Block as _, SignedTransaction}; use reth_provider::{BlockReader, ExecutionOutcome, ProviderError, StateProviderFactory}; use reth_revm::{ database::StateProviderDatabase, @@ -329,19 +329,19 @@ where let exec_result = match evm.transact() { Ok(result) => result, error @ Err(EVMError::Transaction(_) | EVMError::Header(_)) => { - trace!(target: "engine::stream::reorg", hash = %tx.hash(), ?error, "Error executing transaction from next block"); + trace!(target: "engine::stream::reorg", hash = %tx.tx_hash(), ?error, "Error executing transaction from next block"); continue } // Treat error as fatal Err(error) => { return Err(RethError::Execution(BlockExecutionError::Validation( - BlockValidationError::EVM { hash: tx.hash(), error: Box::new(error) }, + BlockValidationError::EVM { hash: *tx.tx_hash(), error: Box::new(error) }, ))) } }; evm.db_mut().commit(exec_result.state); - if let Some(blob_tx) = tx.transaction.as_eip4844() { + if let Some(blob_tx) = tx.as_eip4844() { sum_blob_gas_used += blob_tx.blob_gas(); versioned_hashes.extend(blob_tx.blob_versioned_hashes.clone()); } diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index 99975734e..8bc327227 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -22,7 +22,7 @@ use reth_evm::{ ConfigureEvm, TxEnvOverrides, }; use reth_primitives::{EthPrimitives, Receipt, RecoveredBlock}; -use reth_primitives_traits::BlockBody; +use reth_primitives_traits::{BlockBody, SignedTransaction}; use reth_revm::db::State; use revm_primitives::{ db::{Database, DatabaseCommit}, diff --git a/crates/ethereum/payload/src/lib.rs b/crates/ethereum/payload/src/lib.rs index 35c612e64..7d172784e 100644 --- a/crates/ethereum/payload/src/lib.rs +++ b/crates/ethereum/payload/src/lib.rs @@ -9,7 +9,7 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![allow(clippy::useless_let_if_seq)] -use alloy_consensus::{Header, EMPTY_OMMER_ROOT_HASH}; +use alloy_consensus::{Header, Transaction, Typed2718, EMPTY_OMMER_ROOT_HASH}; use alloy_eips::{ eip4844::MAX_DATA_GAS_PER_BLOCK, eip6110, eip7685::Requests, eip7840::BlobParams, merge::BEACON_NONCE, @@ -33,7 +33,7 @@ use reth_primitives::{ Block, BlockBody, EthereumHardforks, InvalidTransactionError, Receipt, RecoveredBlock, TransactionSigned, }; -use reth_primitives_traits::Block as _; +use reth_primitives_traits::{Block as _, SignedTransaction}; use reth_revm::database::StateProviderDatabase; use reth_storage_api::StateProviderFactory; use reth_transaction_pool::{ @@ -250,7 +250,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.transaction.as_eip4844() { + if let Some(blob_tx) = tx.as_eip4844() { let tx_blob_gas = blob_tx.blob_gas(); if sum_blob_gas_used + tx_blob_gas > MAX_DATA_GAS_PER_BLOCK { // we can't fit this _blob_ transaction into the block, so we mark it as @@ -306,7 +306,7 @@ where evm.db_mut().commit(state); // add to the total blob gas used if the transaction successfully executed - if let Some(blob_tx) = tx.transaction.as_eip4844() { + if let Some(blob_tx) = tx.as_eip4844() { let tx_blob_gas = blob_tx.blob_gas(); sum_blob_gas_used += tx_blob_gas; @@ -332,9 +332,8 @@ where })); // update add to total fees - let miner_fee = tx - .effective_tip_per_gas(Some(base_fee)) - .expect("fee is always valid; execution succeeded"); + let miner_fee = + tx.effective_tip_per_gas(base_fee).expect("fee is always valid; execution succeeded"); total_fees += U256::from(miner_fee) * U256::from(gas_used); // append sender and transaction to the respective lists @@ -419,7 +418,7 @@ where // grab the blob sidecars from the executed txs blob_sidecars = pool .get_all_blobs_exact( - executed_txs.iter().filter(|tx| tx.is_eip4844()).map(|tx| tx.hash()).collect(), + executed_txs.iter().filter(|tx| tx.is_eip4844()).map(|tx| *tx.tx_hash()).collect(), ) .map_err(PayloadBuilderError::other)?; diff --git a/crates/ethereum/primitives/Cargo.toml b/crates/ethereum/primitives/Cargo.toml index ede63a449..e4f21c95d 100644 --- a/crates/ethereum/primitives/Cargo.toml +++ b/crates/ethereum/primitives/Cargo.toml @@ -20,8 +20,12 @@ reth-zstd-compressors = { workspace = true, optional = true } # ethereum alloy-eips.workspace = true alloy-primitives.workspace = true +alloy-network = { workspace = true, optional = true } alloy-consensus = { workspace = true, features = ["serde"] } +alloy-serde = { workspace = true, optional = true } alloy-rlp.workspace = true +alloy-rpc-types = { workspace = true, optional = true } +revm-primitives.workspace = true # misc arbitrary = { workspace = true, optional = true, features = ["derive"] } @@ -34,16 +38,20 @@ serde.workspace = true [dev-dependencies] arbitrary.workspace = true +bincode.workspace = true proptest.workspace = true proptest-arbitrary-interop.workspace = true rand.workspace = true -reth-codecs.workspace = true +reth-codecs = { workspace = true, features = ["test-utils"] } +reth-testing-utils.workspace = true reth-zstd-compressors.workspace = true -secp256k1.workspace = true +secp256k1 = { workspace = true, features = ["rand"] } test-fuzz.workspace = true +alloy-consensus = { workspace = true, features = ["serde", "arbitrary"] } [features] default = ["std"] +alloy-compat = ["dep:alloy-network", "dep:alloy-serde", "dep:alloy-rpc-types"] std = [ "alloy-consensus/std", "alloy-primitives/std", @@ -54,7 +62,9 @@ std = [ "alloy-eips/std", "derive_more/std", "secp256k1?/std", - "once_cell/std" + "once_cell/std", + "revm-primitives/std", + "alloy-serde?/std" ] reth-codec = [ "std", @@ -70,5 +80,13 @@ arbitrary = [ "alloy-primitives/arbitrary", "reth-codecs?/arbitrary", "reth-primitives-traits/arbitrary", - "alloy-eips/arbitrary" + "alloy-eips/arbitrary", + "revm-primitives/arbitrary", + "alloy-rpc-types?/arbitrary", + "alloy-serde?/arbitrary" +] +serde-bincode-compat = [ + "alloy-consensus/serde-bincode-compat", + "alloy-eips/serde-bincode-compat", + "reth-primitives-traits/serde-bincode-compat" ] diff --git a/crates/ethereum/primitives/src/alloy_compat.rs b/crates/ethereum/primitives/src/alloy_compat.rs new file mode 100644 index 000000000..6dba43025 --- /dev/null +++ b/crates/ethereum/primitives/src/alloy_compat.rs @@ -0,0 +1,44 @@ +//! Common conversions from alloy types. + +use crate::{Transaction, TransactionSigned}; +use alloc::string::ToString; +use alloy_consensus::TxEnvelope; +use alloy_network::{AnyRpcTransaction, AnyTxEnvelope}; +use alloy_serde::WithOtherFields; + +impl TryFrom for TransactionSigned { + type Error = alloy_rpc_types::ConversionError; + + fn try_from(tx: AnyRpcTransaction) -> Result { + use alloy_rpc_types::ConversionError; + + let WithOtherFields { inner: tx, other: _ } = tx; + + #[allow(unreachable_patterns)] + let (transaction, signature, hash) = match tx.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 { transaction, signature, hash: hash.into() }) + } +} diff --git a/crates/ethereum/primitives/src/lib.rs b/crates/ethereum/primitives/src/lib.rs index 4c0b42a51..79ccdf4e9 100644 --- a/crates/ethereum/primitives/src/lib.rs +++ b/crates/ethereum/primitives/src/lib.rs @@ -16,3 +16,6 @@ pub use receipt::*; mod transaction; pub use transaction::*; + +#[cfg(feature = "alloy-compat")] +mod alloy_compat; diff --git a/crates/ethereum/primitives/src/receipt.rs b/crates/ethereum/primitives/src/receipt.rs index 75ae92b44..491a544ef 100644 --- a/crates/ethereum/primitives/src/receipt.rs +++ b/crates/ethereum/primitives/src/receipt.rs @@ -184,3 +184,139 @@ impl InMemorySize for Receipt { } impl reth_primitives_traits::Receipt for Receipt {} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_eips::eip2718::Encodable2718; + use alloy_primitives::{address, b256, bytes, hex_literal::hex, Bytes}; + use reth_codecs::Compact; + + #[test] + fn test_decode_receipt() { + reth_codecs::test_utils::test_decode::(&hex!( + "c428b52ffd23fc42696156b10200f034792b6a94c3850215c2fef7aea361a0c31b79d9a32652eefc0d4e2e730036061cff7344b6fc6132b50cda0ed810a991ae58ef013150c12b2522533cb3b3a8b19b7786a8b5ff1d3cdc84225e22b02def168c8858df" + )); + } + + // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 + #[test] + fn encode_legacy_receipt() { + let expected = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); + + let mut data = Vec::with_capacity(expected.length()); + let receipt = ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::Legacy, + cumulative_gas_used: 0x1u64, + logs: vec![Log::new_unchecked( + address!("0000000000000000000000000000000000000011"), + vec![ + b256!("000000000000000000000000000000000000000000000000000000000000dead"), + b256!("000000000000000000000000000000000000000000000000000000000000beef"), + ], + bytes!("0100ff"), + )], + success: false, + }, + logs_bloom: [0; 256].into(), + }; + + receipt.encode(&mut data); + + // check that the rlp length equals the length of the expected rlp + assert_eq!(receipt.length(), expected.len()); + assert_eq!(data, expected); + } + + // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 + #[test] + fn decode_legacy_receipt() { + let data = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); + + // EIP658Receipt + let expected = ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::Legacy, + cumulative_gas_used: 0x1u64, + logs: vec![Log::new_unchecked( + address!("0000000000000000000000000000000000000011"), + vec![ + b256!("000000000000000000000000000000000000000000000000000000000000dead"), + b256!("000000000000000000000000000000000000000000000000000000000000beef"), + ], + bytes!("0100ff"), + )], + success: false, + }, + logs_bloom: [0; 256].into(), + }; + + let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap(); + assert_eq!(receipt, expected); + } + + #[test] + fn gigantic_receipt() { + let receipt = Receipt { + cumulative_gas_used: 16747627, + success: true, + tx_type: TxType::Legacy, + logs: vec![ + Log::new_unchecked( + address!("4bf56695415f725e43c3e04354b604bcfb6dfb6e"), + vec![b256!("c69dc3d7ebff79e41f525be431d5cd3cc08f80eaf0f7819054a726eeb7086eb9")], + Bytes::from(vec![1; 0xffffff]), + ), + Log::new_unchecked( + address!("faca325c86bf9c2d5b413cd7b90b209be92229c2"), + vec![b256!("8cca58667b1e9ffa004720ac99a3d61a138181963b294d270d91c53d36402ae2")], + Bytes::from(vec![1; 0xffffff]), + ), + ], + }; + + let mut data = vec![]; + receipt.to_compact(&mut data); + let (decoded, _) = Receipt::from_compact(&data[..], data.len()); + assert_eq!(decoded, receipt); + } + + #[test] + fn test_encode_2718_length() { + let receipt = ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::Eip1559, + success: true, + cumulative_gas_used: 21000, + logs: vec![], + }, + logs_bloom: Bloom::default(), + }; + + let encoded = receipt.encoded_2718(); + assert_eq!( + encoded.len(), + receipt.encode_2718_len(), + "Encoded length should match the actual encoded data length" + ); + + // Test for legacy receipt as well + let legacy_receipt = ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::Legacy, + success: true, + cumulative_gas_used: 21000, + logs: vec![], + }, + logs_bloom: Bloom::default(), + }; + + let legacy_encoded = legacy_receipt.encoded_2718(); + assert_eq!( + legacy_encoded.len(), + legacy_receipt.encode_2718_len(), + "Encoded length for legacy receipt should match the actual encoded data length" + ); + } +} diff --git a/crates/ethereum/primitives/src/transaction.rs b/crates/ethereum/primitives/src/transaction.rs index b466ca3c3..65cc894fc 100644 --- a/crates/ethereum/primitives/src/transaction.rs +++ b/crates/ethereum/primitives/src/transaction.rs @@ -1,7 +1,8 @@ use alloc::vec::Vec; use alloy_consensus::{ - transaction::RlpEcdsaTx, SignableTransaction, Signed, TxEip1559, TxEip2930, TxEip4844, - TxEip7702, TxLegacy, TxType, Typed2718, + transaction::{PooledTransaction, RlpEcdsaTx}, + BlobTransactionSidecar, SignableTransaction, Signed, TxEip1559, TxEip2930, TxEip4844, + TxEip4844WithSidecar, TxEip7702, TxLegacy, TxType, Typed2718, TypedTransaction, }; use alloy_eips::{ eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}, @@ -18,8 +19,10 @@ use once_cell as _; use once_cell::sync::OnceCell as OnceLock; use reth_primitives_traits::{ crypto::secp256k1::{recover_signer, recover_signer_unchecked}, - InMemorySize, SignedTransaction, + transaction::error::TransactionConversionError, + FillTxEnv, InMemorySize, SignedTransaction, }; +use revm_primitives::{AuthorizationList, TxEnv}; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::sync::OnceLock; @@ -101,6 +104,17 @@ impl Transaction { Self::Eip7702(_) => TxType::Eip7702, } } + + /// This sets the transaction's nonce. + pub 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, + } + } } impl Typed2718 for Transaction { @@ -253,6 +267,18 @@ impl reth_codecs::Compact for Transaction { } } +impl From 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), + } + } +} + /// Signed Ethereum transaction. #[derive(Debug, Clone, Eq, Serialize, Deserialize, derive_more::AsRef, derive_more::Deref)] #[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(rlp))] @@ -269,6 +295,12 @@ pub struct TransactionSigned { pub 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()) @@ -291,12 +323,50 @@ impl PartialEq for TransactionSigned { } 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 } + } + /// 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 } } + + /// 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 { + 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, + } + } } impl Typed2718 for TransactionSigned { @@ -561,6 +631,85 @@ impl reth_codecs::Compact for TransactionSigned { } } +impl FillTxEnv for TransactionSigned { + fn fill_tx_env(&self, tx_env: &mut TxEnv, sender: Address) { + tx_env.caller = sender; + match self.as_ref() { + Transaction::Legacy(tx) => { + tx_env.gas_limit = tx.gas_limit; + tx_env.gas_price = U256::from(tx.gas_price); + tx_env.gas_priority_fee = None; + tx_env.transact_to = tx.to; + tx_env.value = tx.value; + tx_env.data = tx.input.clone(); + tx_env.chain_id = tx.chain_id; + tx_env.nonce = Some(tx.nonce); + tx_env.access_list.clear(); + tx_env.blob_hashes.clear(); + tx_env.max_fee_per_blob_gas.take(); + tx_env.authorization_list = None; + } + Transaction::Eip2930(tx) => { + tx_env.gas_limit = tx.gas_limit; + tx_env.gas_price = U256::from(tx.gas_price); + tx_env.gas_priority_fee = None; + tx_env.transact_to = tx.to; + tx_env.value = tx.value; + tx_env.data = tx.input.clone(); + tx_env.chain_id = Some(tx.chain_id); + tx_env.nonce = Some(tx.nonce); + tx_env.access_list.clone_from(&tx.access_list.0); + tx_env.blob_hashes.clear(); + tx_env.max_fee_per_blob_gas.take(); + tx_env.authorization_list = None; + } + Transaction::Eip1559(tx) => { + tx_env.gas_limit = tx.gas_limit; + tx_env.gas_price = U256::from(tx.max_fee_per_gas); + tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas)); + tx_env.transact_to = tx.to; + tx_env.value = tx.value; + tx_env.data = tx.input.clone(); + tx_env.chain_id = Some(tx.chain_id); + tx_env.nonce = Some(tx.nonce); + tx_env.access_list.clone_from(&tx.access_list.0); + tx_env.blob_hashes.clear(); + tx_env.max_fee_per_blob_gas.take(); + tx_env.authorization_list = None; + } + Transaction::Eip4844(tx) => { + tx_env.gas_limit = tx.gas_limit; + tx_env.gas_price = U256::from(tx.max_fee_per_gas); + tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas)); + tx_env.transact_to = TxKind::Call(tx.to); + tx_env.value = tx.value; + tx_env.data = tx.input.clone(); + tx_env.chain_id = Some(tx.chain_id); + tx_env.nonce = Some(tx.nonce); + tx_env.access_list.clone_from(&tx.access_list.0); + tx_env.blob_hashes.clone_from(&tx.blob_versioned_hashes); + tx_env.max_fee_per_blob_gas = Some(U256::from(tx.max_fee_per_blob_gas)); + tx_env.authorization_list = None; + } + Transaction::Eip7702(tx) => { + tx_env.gas_limit = tx.gas_limit; + tx_env.gas_price = U256::from(tx.max_fee_per_gas); + tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas)); + tx_env.transact_to = tx.to.into(); + tx_env.value = tx.value; + tx_env.data = tx.input.clone(); + tx_env.chain_id = Some(tx.chain_id); + tx_env.nonce = Some(tx.nonce); + tx_env.access_list.clone_from(&tx.access_list.0); + tx_env.blob_hashes.clear(); + tx_env.max_fee_per_blob_gas.take(); + tx_env.authorization_list = + Some(AuthorizationList::Signed(tx.authorization_list.clone())); + } + } + } +} + impl SignedTransaction for TransactionSigned { fn tx_hash(&self) -> &TxHash { self.hash.get_or_init(|| self.recalculate_hash()) @@ -582,6 +731,180 @@ impl SignedTransaction for TransactionSigned { } } +impl TryFrom for PooledTransaction { + type Error = TransactionConversionError; + + fn try_from(tx: TransactionSigned) -> Result { + 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> for TransactionSigned +where + T: Into, +{ + fn from(value: Signed) -> Self { + let (tx, signature, hash) = value.into_parts(); + Self { transaction: tx.into(), signature, hash: hash.into() } + } +} + +impl From 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(feature = "serde-bincode-compat")] +pub mod serde_bincode_compat { + use alloc::borrow::Cow; + use alloy_consensus::{ + transaction::serde_bincode_compat::{TxEip1559, TxEip2930, TxEip7702, TxLegacy}, + TxEip4844, + }; + use alloy_primitives::{PrimitiveSignature as Signature, TxHash}; + use reth_primitives_traits::{serde_bincode_compat::SerdeBincodeCompat, SignedTransaction}; + use serde::{Deserialize, Serialize}; + + /// Bincode-compatible [`super::Transaction`] serde implementation. + #[derive(Debug, Serialize, Deserialize)] + #[allow(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> 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, Serialize, 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> for super::TransactionSigned { + fn from(value: TransactionSigned<'a>) -> Self { + Self { + hash: value.hash.into(), + signature: value.signature, + transaction: value.transaction.into(), + } + } + } + impl SerdeBincodeCompat for super::TransactionSigned { + type BincodeRepr<'a> = TransactionSigned<'a>; + } + + #[cfg(test)] + mod tests { + use super::super::{serde_bincode_compat, Transaction, TransactionSigned}; + use arbitrary::Arbitrary; + use rand::Rng; + use reth_testing_utils::generators; + use serde::{Deserialize, Serialize}; + + #[test] + fn test_transaction_bincode_roundtrip() { + #[derive(Debug, Serialize, 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, Serialize, 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::*; diff --git a/crates/evm/execution-types/Cargo.toml b/crates/evm/execution-types/Cargo.toml index 5a415f0b8..f7372e7f2 100644 --- a/crates/evm/execution-types/Cargo.toml +++ b/crates/evm/execution-types/Cargo.toml @@ -36,7 +36,7 @@ reth-ethereum-primitives.workspace = true [features] default = ["std"] -optimism = ["reth-primitives/optimism", "revm/optimism"] +optimism = ["revm/optimism"] serde = [ "dep:serde", "rand/serde", @@ -55,7 +55,8 @@ serde-bincode-compat = [ "reth-primitives-traits/serde-bincode-compat", "serde_with", "alloy-eips/serde-bincode-compat", - "alloy-consensus/serde-bincode-compat", + "alloy-consensus/serde-bincode-compat", + "reth-ethereum-primitives/serde-bincode-compat" ] std = [ "reth-primitives/std", diff --git a/crates/net/downloaders/Cargo.toml b/crates/net/downloaders/Cargo.toml index f4cc134ec..2945e9d2a 100644 --- a/crates/net/downloaders/Cargo.toml +++ b/crates/net/downloaders/Cargo.toml @@ -73,7 +73,6 @@ tempfile.workspace = true [features] optimism = [ - "reth-primitives/optimism", "reth-db?/optimism", "reth-db-api?/optimism", "reth-provider/optimism" diff --git a/crates/net/downloaders/src/receipt_file_client.rs b/crates/net/downloaders/src/receipt_file_client.rs index 0cdd8bc62..6f53a79cb 100644 --- a/crates/net/downloaders/src/receipt_file_client.rs +++ b/crates/net/downloaders/src/receipt_file_client.rs @@ -240,14 +240,15 @@ mod test { struct MockReceiptContainer(Option); impl TryFrom for ReceiptWithBlockNumber { - type Error = &'static str; + type Error = FileClientError; fn try_from(exported_receipt: MockReceipt) -> Result { let MockReceipt { tx_type, status, cumulative_gas_used, logs, block_number: number } = exported_receipt; #[allow(clippy::needless_update)] let receipt = Receipt { - tx_type: TxType::try_from(tx_type.to_be_bytes()[0])?, + tx_type: TxType::try_from(tx_type.to_be_bytes()[0]) + .map_err(|err| FileClientError::Rlp(err.into(), vec![tx_type]))?, success: status != 0, cumulative_gas_used, logs, @@ -276,11 +277,7 @@ mod test { .0; src.advance(src.len() - buf_slice.len()); - Ok(Some( - receipt - .map(|receipt| receipt.try_into().map_err(FileClientError::from)) - .transpose()?, - )) + Ok(Some(receipt.map(|receipt| receipt.try_into()).transpose()?)) } } diff --git a/crates/net/network/src/transactions/mod.rs b/crates/net/network/src/transactions/mod.rs index 9cbc8e5b0..a1c6ceb5d 100644 --- a/crates/net/network/src/transactions/mod.rs +++ b/crates/net/network/src/transactions/mod.rs @@ -2181,7 +2181,7 @@ mod tests { }); assert!(transactions .transactions_by_peers - .get(&signed_tx.hash()) + .get(signed_tx.tx_hash()) .unwrap() .contains(handle1.peer_id())); diff --git a/crates/net/network/src/transactions/validation.rs b/crates/net/network/src/transactions/validation.rs index 0b5547e2c..beea8677b 100644 --- a/crates/net/network/src/transactions/validation.rs +++ b/crates/net/network/src/transactions/validation.rs @@ -356,7 +356,7 @@ mod test { #[test] fn eth68_announcement_unrecognized_tx_type() { let types = vec![ - TxType::MAX_RESERVED_EIP as u8 + 1, // the first type isn't valid + TxType::Eip7702 as u8 + 1, // the first type isn't valid TxType::Legacy as u8, ]; let sizes = vec![MAX_MESSAGE_SIZE, MAX_MESSAGE_SIZE]; @@ -391,8 +391,7 @@ mod test { #[test] fn eth68_announcement_too_small_tx() { - let types = - vec![TxType::MAX_RESERVED_EIP as u8, TxType::Legacy as u8, TxType::Eip2930 as u8]; + let types = vec![TxType::Eip7702 as u8, TxType::Legacy as u8, TxType::Eip2930 as u8]; let sizes = vec![ 0, // the first length isn't valid 0, // neither is the second diff --git a/crates/net/network/tests/it/big_pooled_txs_req.rs b/crates/net/network/tests/it/big_pooled_txs_req.rs index 328229e87..d0c0f3368 100644 --- a/crates/net/network/tests/it/big_pooled_txs_req.rs +++ b/crates/net/network/tests/it/big_pooled_txs_req.rs @@ -7,6 +7,7 @@ use reth_network::{ use reth_network_api::{NetworkInfo, Peers}; use reth_network_p2p::sync::{NetworkSyncUpdater, SyncState}; use reth_primitives::TransactionSigned; +use reth_primitives_traits::SignedTransaction; use reth_provider::test_utils::MockEthProvider; use reth_transaction_pool::{ test_utils::{testing_pool, MockTransaction}, diff --git a/crates/net/network/tests/it/txgossip.rs b/crates/net/network/tests/it/txgossip.rs index f20ef6470..c04bb3d90 100644 --- a/crates/net/network/tests/it/txgossip.rs +++ b/crates/net/network/tests/it/txgossip.rs @@ -9,6 +9,7 @@ use rand::thread_rng; use reth_network::{test_utils::Testnet, NetworkEvent, NetworkEventListenerProvider}; use reth_network_api::{events::PeerEvent, PeersInfo}; use reth_primitives::TransactionSigned; +use reth_primitives_traits::SignedTransaction; use reth_provider::test_utils::{ExtendedAccount, MockEthProvider}; use reth_transaction_pool::{test_utils::TransactionGenerator, PoolTransaction, TransactionPool}; @@ -95,7 +96,7 @@ async fn test_4844_tx_gossip_penalization() { let peer0_reputation_after = peer1.peer_handle().peer_by_id(*peer0.peer_id()).await.unwrap().reputation(); assert_ne!(peer0_reputation_before, peer0_reputation_after); - assert_eq!(received, txs[1].transaction().hash()); + assert_eq!(received, *txs[1].transaction().tx_hash()); // this will return an [`Empty`] error because blob txs are disallowed to be broadcasted assert!(peer1_tx_listener.try_recv().is_err()); diff --git a/crates/node/core/Cargo.toml b/crates/node/core/Cargo.toml index 7d4a417be..57762554b 100644 --- a/crates/node/core/Cargo.toml +++ b/crates/node/core/Cargo.toml @@ -76,7 +76,7 @@ proptest.workspace = true tokio.workspace = true [features] -optimism = ["reth-primitives/optimism", "reth-db/optimism"] +optimism = ["reth-db/optimism"] # Features for vergen to generate correct env vars jemalloc = ["reth-cli-util/jemalloc"] asm-keccak = ["reth-primitives/asm-keccak", "alloy-primitives/asm-keccak"] diff --git a/crates/optimism/cli/Cargo.toml b/crates/optimism/cli/Cargo.toml index 3cd2edeec..243dc303b 100644 --- a/crates/optimism/cli/Cargo.toml +++ b/crates/optimism/cli/Cargo.toml @@ -83,7 +83,6 @@ optimism = [ "alloy-consensus", "dep:derive_more", "dep:serde", - "reth-primitives/optimism", "reth-optimism-evm/optimism", "reth-provider/optimism", "reth-node-core/optimism", diff --git a/crates/optimism/cli/src/ovm_file_codec.rs b/crates/optimism/cli/src/ovm_file_codec.rs index b2e1e6c4c..32e909837 100644 --- a/crates/optimism/cli/src/ovm_file_codec.rs +++ b/crates/optimism/cli/src/ovm_file_codec.rs @@ -1,6 +1,6 @@ use alloy_consensus::{ transaction::{from_eip155_value, RlpEcdsaTx}, - Header, TxEip1559, TxEip2930, TxEip4844, TxEip7702, TxLegacy, + Header, TxEip1559, TxEip2930, TxEip7702, TxLegacy, }; use alloy_eips::{ eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}, @@ -12,9 +12,8 @@ use alloy_primitives::{ }; use alloy_rlp::{Decodable, Error as RlpError, RlpDecodable}; use derive_more::{AsRef, Deref}; -use op_alloy_consensus::TxDeposit; +use op_alloy_consensus::{OpTxType, OpTypedTransaction, TxDeposit}; use reth_downloaders::file_client::FileClientError; -use reth_primitives::transaction::{Transaction, TxType}; use serde::{Deserialize, Serialize}; use tokio_util::codec::Decoder; @@ -83,17 +82,7 @@ pub struct TransactionSigned { /// Raw transaction info #[deref] #[as_ref] - pub transaction: Transaction, -} - -impl Default for TransactionSigned { - fn default() -> Self { - Self { - hash: Default::default(), - signature: Signature::test_signature(), - transaction: Default::default(), - } - } + pub transaction: OpTypedTransaction, } impl AsRef for TransactionSigned { @@ -113,7 +102,10 @@ impl TransactionSigned { /// Create a new signed transaction from a transaction and its signature. /// /// This will also calculate the transaction hash using its encoding. - pub fn from_transaction_and_signature(transaction: Transaction, signature: Signature) -> Self { + pub fn from_transaction_and_signature( + transaction: OpTypedTransaction, + signature: Signature, + ) -> Self { let mut initial_tx = Self { transaction, hash: Default::default(), signature }; initial_tx.hash = initial_tx.recalculate_hash(); initial_tx @@ -190,7 +182,7 @@ impl TransactionSigned { // so decoding methods do not need to manually advance the buffer pub fn decode_rlp_legacy_transaction(data: &mut &[u8]) -> alloy_rlp::Result { let (transaction, hash, signature) = Self::decode_rlp_legacy_transaction_tuple(data)?; - let signed = Self { transaction: Transaction::Legacy(transaction), hash, signature }; + let signed = Self { transaction: OpTypedTransaction::Legacy(transaction), hash, signature }; Ok(signed) } } @@ -229,55 +221,58 @@ impl Decodable for TransactionSigned { impl Encodable2718 for TransactionSigned { fn type_flag(&self) -> Option { match self.transaction.tx_type() { - TxType::Legacy => None, + OpTxType::Legacy => None, tx_type => Some(tx_type as u8), } } fn encode_2718_len(&self) -> usize { match &self.transaction { - Transaction::Legacy(legacy_tx) => legacy_tx.eip2718_encoded_length(&self.signature), - Transaction::Eip2930(access_list_tx) => { + OpTypedTransaction::Legacy(legacy_tx) => { + legacy_tx.eip2718_encoded_length(&self.signature) + } + OpTypedTransaction::Eip2930(access_list_tx) => { access_list_tx.eip2718_encoded_length(&self.signature) } - Transaction::Eip1559(dynamic_fee_tx) => { + OpTypedTransaction::Eip1559(dynamic_fee_tx) => { dynamic_fee_tx.eip2718_encoded_length(&self.signature) } - Transaction::Eip4844(blob_tx) => blob_tx.eip2718_encoded_length(&self.signature), - Transaction::Eip7702(set_code_tx) => { + OpTypedTransaction::Eip7702(set_code_tx) => { set_code_tx.eip2718_encoded_length(&self.signature) } - Transaction::Deposit(deposit_tx) => deposit_tx.eip2718_encoded_length(), + OpTypedTransaction::Deposit(deposit_tx) => deposit_tx.eip2718_encoded_length(), } } fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { - self.transaction.eip2718_encode(&self.signature, out) + match &self.transaction { + OpTypedTransaction::Legacy(tx) => tx.eip2718_encode(&self.signature, out), + OpTypedTransaction::Eip2930(tx) => tx.eip2718_encode(&self.signature, out), + OpTypedTransaction::Eip1559(tx) => tx.eip2718_encode(&self.signature, out), + OpTypedTransaction::Eip7702(tx) => tx.eip2718_encode(&self.signature, out), + OpTypedTransaction::Deposit(tx) => tx.encode_2718(out), + } } } impl Decodable2718 for TransactionSigned { fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result { match ty.try_into().map_err(|_| Eip2718Error::UnexpectedType(ty))? { - TxType::Legacy => Err(Eip2718Error::UnexpectedType(0)), - TxType::Eip2930 => { + OpTxType::Legacy => Err(Eip2718Error::UnexpectedType(0)), + OpTxType::Eip2930 => { let (tx, signature, hash) = TxEip2930::rlp_decode_signed(buf)?.into_parts(); - Ok(Self { transaction: Transaction::Eip2930(tx), signature, hash }) + Ok(Self { transaction: OpTypedTransaction::Eip2930(tx), signature, hash }) } - TxType::Eip1559 => { + OpTxType::Eip1559 => { let (tx, signature, hash) = TxEip1559::rlp_decode_signed(buf)?.into_parts(); - Ok(Self { transaction: Transaction::Eip1559(tx), signature, hash }) + Ok(Self { transaction: OpTypedTransaction::Eip1559(tx), signature, hash }) } - TxType::Eip7702 => { + OpTxType::Eip7702 => { let (tx, signature, hash) = TxEip7702::rlp_decode_signed(buf)?.into_parts(); - Ok(Self { transaction: Transaction::Eip7702(tx), signature, hash }) + Ok(Self { transaction: OpTypedTransaction::Eip7702(tx), signature, hash }) } - TxType::Eip4844 => { - let (tx, signature, hash) = TxEip4844::rlp_decode_signed(buf)?.into_parts(); - Ok(Self { transaction: Transaction::Eip4844(tx), signature, hash }) - } - TxType::Deposit => Ok(Self::from_transaction_and_signature( - Transaction::Deposit(TxDeposit::rlp_decode(buf)?), + OpTxType::Deposit => Ok(Self::from_transaction_and_signature( + OpTypedTransaction::Deposit(TxDeposit::rlp_decode(buf)?), TxDeposit::signature(), )), } @@ -291,8 +286,9 @@ impl Decodable2718 for TransactionSigned { #[cfg(test)] mod tests { use crate::ovm_file_codec::TransactionSigned; + use alloy_consensus::Typed2718; use alloy_primitives::{address, hex, TxKind, B256, U256}; - use reth_primitives::transaction::Transaction; + use op_alloy_consensus::OpTypedTransaction; const DEPOSIT_FUNCTION_SELECTOR: [u8; 4] = [0xb6, 0xb5, 0x5f, 0x25]; use alloy_rlp::Decodable; @@ -305,7 +301,7 @@ mod tests { // Verify deposit transaction let deposit_tx = match &deposit_decoded.transaction { - Transaction::Legacy(ref tx) => tx, + OpTypedTransaction::Legacy(ref tx) => tx, _ => panic!("Expected legacy transaction for NFT deposit"), }; @@ -345,7 +341,7 @@ mod tests { assert!(system_decoded.is_legacy()); let system_tx = match &system_decoded.transaction { - Transaction::Legacy(ref tx) => tx, + OpTypedTransaction::Legacy(ref tx) => tx, _ => panic!("Expected Legacy transaction"), }; diff --git a/crates/optimism/cli/src/receipt_file_codec.rs b/crates/optimism/cli/src/receipt_file_codec.rs index e307b10ac..f89b95590 100644 --- a/crates/optimism/cli/src/receipt_file_codec.rs +++ b/crates/optimism/cli/src/receipt_file_codec.rs @@ -5,9 +5,9 @@ use alloy_primitives::{ Address, Bloom, Bytes, B256, }; use alloy_rlp::{Decodable, RlpDecodable}; -use op_alloy_consensus::OpDepositReceipt; +use op_alloy_consensus::{OpDepositReceipt, OpTxType}; use reth_optimism_primitives::OpReceipt; -use reth_primitives::{Log, Receipt, TxType}; +use reth_primitives::{Log, Receipt}; use tokio_util::codec::Decoder; use reth_downloaders::{file_client::FileClientError, receipt_file_client::ReceiptWithBlockNumber}; @@ -92,49 +92,27 @@ pub struct OpGethReceipt { #[rlp(trailing)] struct OpGethReceiptContainer(Option); -impl TryFrom for Receipt { - type Error = &'static str; +impl TryFrom for OpReceipt { + type Error = FileClientError; fn try_from(exported_receipt: OpGethReceipt) -> Result { let OpGethReceipt { tx_type, status, cumulative_gas_used, logs, .. } = exported_receipt; - #[allow(clippy::needless_update)] - Ok(Self { - tx_type: TxType::try_from(tx_type.to_be_bytes()[0])?, - success: status != 0, - cumulative_gas_used, - logs, - ..Default::default() - }) - } -} - -impl TryFrom for OpReceipt { - type Error = &'static str; - - fn try_from(exported_receipt: OpGethReceipt) -> Result { - let Receipt { - tx_type, - success, - cumulative_gas_used, - logs, - deposit_nonce, - deposit_receipt_version, - } = exported_receipt.try_into()?; + let tx_type = OpTxType::try_from(tx_type.to_be_bytes()[0]) + .map_err(|e| FileClientError::Rlp(e.into(), vec![tx_type]))?; let receipt = - alloy_consensus::Receipt { status: success.into(), cumulative_gas_used, logs }; + alloy_consensus::Receipt { status: (status != 0).into(), cumulative_gas_used, logs }; match tx_type { - TxType::Legacy => Ok(Self::Legacy(receipt)), - TxType::Eip2930 => Ok(Self::Eip2930(receipt)), - TxType::Eip1559 => Ok(Self::Eip1559(receipt)), - TxType::Eip7702 => Ok(Self::Eip7702(receipt)), - TxType::Eip4844 => Err("EIP-4844 receipts are not supported for OP"), - TxType::Deposit => Ok(Self::Deposit(OpDepositReceipt { + OpTxType::Legacy => Ok(Self::Legacy(receipt)), + OpTxType::Eip2930 => Ok(Self::Eip2930(receipt)), + OpTxType::Eip1559 => Ok(Self::Eip1559(receipt)), + OpTxType::Eip7702 => Ok(Self::Eip7702(receipt)), + OpTxType::Deposit => Ok(Self::Deposit(OpDepositReceipt { inner: receipt, - deposit_nonce, - deposit_receipt_version, + deposit_nonce: None, + deposit_receipt_version: None, })), } } @@ -142,6 +120,7 @@ impl TryFrom for OpReceipt { #[cfg(test)] pub(crate) mod test { + use alloy_consensus::{Receipt, TxReceipt}; use alloy_primitives::{hex, LogData}; use super::*; @@ -156,12 +135,12 @@ pub(crate) mod test { let receipt = receipt_block_1(); OpGethReceipt { - tx_type: receipt.receipt.tx_type as u8, + tx_type: receipt.receipt.tx_type() as u8, post_state: Bytes::default(), - status: receipt.receipt.success as u64, - cumulative_gas_used: receipt.receipt.cumulative_gas_used, + status: receipt.receipt.status() as u64, + cumulative_gas_used: receipt.receipt.cumulative_gas_used(), bloom: Bloom::from(hex!("00000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000400000000000100000000000000200000000002000000000000001000000000000000000004000000000000000000000000000040000400000100400000000000000100000000000000000000000000000020000000000000000000000000000000000000000000000001000000000000000000000100000000000000000000000000000000000000000000000000000000000000088000000080000000000010000000000000000000000000000800008000120000000000000000000000000000000002000")), - logs: receipt.receipt.logs, + logs: receipt.receipt.logs().to_vec(), tx_hash: B256::from(hex!("5e77a04531c7c107af1882d76cbff9486d0a9aa53701c30888509d4f5f2b003a")), contract_address: Address::from(hex!("0000000000000000000000000000000000000000")), gas_used: 202813, block_hash: B256::from(hex!("bee7192e575af30420cae0c7776304ac196077ee72b048970549e4f08e875453")), block_number: receipt.number, @@ -173,7 +152,7 @@ pub(crate) mod test { } } - pub(crate) fn receipt_block_1() -> ReceiptWithBlockNumber { + pub(crate) fn receipt_block_1() -> ReceiptWithBlockNumber { let log_1 = Log { address: Address::from(hex!("8ce8c13d816fe6daf12d6fd9e4952e1fc88850af")), data: LogData::new( @@ -233,20 +212,16 @@ pub(crate) mod test { .unwrap(), }; - let mut receipt = Receipt { - tx_type: TxType::Legacy, - success: true, + let receipt = OpReceipt::Legacy(Receipt { + status: true.into(), cumulative_gas_used: 202813, - ..Default::default() - }; - // #[allow(clippy::needless_update)] not recognised, ..Default::default() needed so optimism - // feature must not be brought into scope - receipt.logs = vec![log_1, log_2, log_3]; + logs: vec![log_1, log_2, log_3], + }); ReceiptWithBlockNumber { receipt, number: 1 } } - pub(crate) fn receipt_block_2() -> ReceiptWithBlockNumber { + pub(crate) fn receipt_block_2() -> ReceiptWithBlockNumber { let log_1 = Log { address: Address::from(hex!("8ce8c13d816fe6daf12d6fd9e4952e1fc88850af")), data: LogData::new( @@ -285,20 +260,16 @@ pub(crate) mod test { .unwrap(), }; - let mut receipt = Receipt { - tx_type: TxType::Legacy, - success: true, + let receipt = OpReceipt::Legacy(Receipt { + status: true.into(), cumulative_gas_used: 116237, - ..Default::default() - }; - // #[allow(clippy::needless_update)] not recognised, ..Default::default() needed so optimism - // feature must not be brought into scope - receipt.logs = vec![log_1, log_2]; + logs: vec![log_1, log_2], + }); ReceiptWithBlockNumber { receipt, number: 2 } } - pub(crate) fn receipt_block_3() -> ReceiptWithBlockNumber { + pub(crate) fn receipt_block_3() -> ReceiptWithBlockNumber { let log_1 = Log { address: Address::from(hex!("8ce8c13d816fe6daf12d6fd9e4952e1fc88850af")), data: LogData::new( @@ -337,15 +308,11 @@ pub(crate) mod test { .unwrap(), }; - let mut receipt = Receipt { - tx_type: TxType::Legacy, - success: true, + let receipt = OpReceipt::Legacy(Receipt { + status: true.into(), cumulative_gas_used: 116237, - ..Default::default() - }; - // #[allow(clippy::needless_update)] not recognised, ..Default::default() needed so optimism - // feature must not be brought into scope - receipt.logs = vec![log_1, log_2]; + logs: vec![log_1, log_2], + }); ReceiptWithBlockNumber { receipt, number: 3 } } diff --git a/crates/optimism/consensus/Cargo.toml b/crates/optimism/consensus/Cargo.toml index 31925620c..7f2378a5b 100644 --- a/crates/optimism/consensus/Cargo.toml +++ b/crates/optimism/consensus/Cargo.toml @@ -55,4 +55,4 @@ std = [ "alloy-trie/std", "op-alloy-consensus/std", ] -optimism = ["reth-primitives/optimism", "reth-optimism-primitives/optimism"] +optimism = ["reth-optimism-primitives/optimism"] diff --git a/crates/optimism/evm/Cargo.toml b/crates/optimism/evm/Cargo.toml index 19b63d9fe..ea3fe19bd 100644 --- a/crates/optimism/evm/Cargo.toml +++ b/crates/optimism/evm/Cargo.toml @@ -78,7 +78,6 @@ std = [ "reth-consensus-common/std", ] optimism = [ - "reth-primitives/optimism", "reth-execution-types/optimism", "reth-optimism-consensus/optimism", "revm/optimism", diff --git a/crates/optimism/evm/src/l1.rs b/crates/optimism/evm/src/l1.rs index 033d632b5..129c4c35d 100644 --- a/crates/optimism/evm/src/l1.rs +++ b/crates/optimism/evm/src/l1.rs @@ -312,7 +312,8 @@ mod tests { use alloy_eips::eip2718::Decodable2718; use reth_optimism_chainspec::OP_MAINNET; use reth_optimism_forks::OpHardforks; - use reth_primitives::{Block, BlockBody, TransactionSigned}; + use reth_optimism_primitives::OpTransactionSigned; + use reth_primitives::{Block, BlockBody}; use super::*; @@ -320,10 +321,9 @@ mod tests { fn sanity_l1_block() { use alloy_consensus::Header; use alloy_primitives::{hex_literal::hex, Bytes}; - use reth_primitives::TransactionSigned; let bytes = Bytes::from_static(&hex!("7ef9015aa044bae9d41b8380d781187b426c6fe43df5fb2fb57bd4466ef6a701e1f01e015694deaddeaddeaddeaddeaddeaddeaddeaddead000194420000000000000000000000000000000000001580808408f0d18001b90104015d8eb900000000000000000000000000000000000000000000000000000000008057650000000000000000000000000000000000000000000000000000000063d96d10000000000000000000000000000000000000000000000000000000000009f35273d89754a1e0387b89520d989d3be9c37c1f32495a88faf1ea05c61121ab0d1900000000000000000000000000000000000000000000000000000000000000010000000000000000000000002d679b567db6187c0c8323fa982cfb88b74dbcc7000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240")); - let l1_info_tx = TransactionSigned::decode_2718(&mut bytes.as_ref()).unwrap(); + let l1_info_tx = OpTransactionSigned::decode_2718(&mut bytes.as_ref()).unwrap(); let mock_block = Block { header: Header::default(), body: BlockBody { transactions: vec![l1_info_tx], ..Default::default() }, @@ -351,7 +351,7 @@ mod tests { // https://optimistic.etherscan.io/getRawTx?tx=0x88501da5d5ca990347c2193be90a07037af1e3820bb40774c8154871c7669150 const TX: [u8; 251] = hex!("7ef8f8a0a539eb753df3b13b7e386e147d45822b67cb908c9ddc5618e3dbaa22ed00850b94deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e2000000558000c5fc50000000000000000000000006605a89f00000000012a10d90000000000000000000000000000000000000000000000000000000af39ac3270000000000000000000000000000000000000000000000000000000d5ea528d24e582fa68786f080069bdbfe06a43f8e67bfd31b8e4d8a8837ba41da9a82a54a0000000000000000000000006887246668a3b87f54deb3b94ba47a6f63f32985"); - let tx = TransactionSigned::decode_2718(&mut TX.as_slice()).unwrap(); + let tx = OpTransactionSigned::decode_2718(&mut TX.as_slice()).unwrap(); let block = Block { body: BlockBody { transactions: vec![tx], ..Default::default() }, ..Default::default() diff --git a/crates/optimism/node/Cargo.toml b/crates/optimism/node/Cargo.toml index 25adb3d10..8a597b9ae 100644 --- a/crates/optimism/node/Cargo.toml +++ b/crates/optimism/node/Cargo.toml @@ -89,7 +89,6 @@ futures.workspace = true [features] default = ["reth-codec"] optimism = [ - "reth-primitives/optimism", "reth-provider/optimism", "reth-optimism-evm/optimism", "reth-optimism-payload-builder/optimism", diff --git a/crates/optimism/payload/Cargo.toml b/crates/optimism/payload/Cargo.toml index 69104acd7..2f7b20af3 100644 --- a/crates/optimism/payload/Cargo.toml +++ b/crates/optimism/payload/Cargo.toml @@ -55,7 +55,6 @@ sha2.workspace = true [features] optimism = [ - "reth-primitives/optimism", "reth-provider/optimism", "reth-optimism-evm/optimism", "revm/optimism", diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index fe2aa5bb2..3f8c72ece 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -25,10 +25,9 @@ use reth_payload_builder_primitives::PayloadBuilderError; use reth_payload_primitives::PayloadBuilderAttributes; use reth_payload_util::{NoopPayloadTransactions, PayloadTransactions}; use reth_primitives::{ - proofs, transaction::SignedTransactionIntoRecoveredExt, Block, BlockBody, RecoveredBlock, - SealedHeader, TxType, + proofs, transaction::SignedTransactionIntoRecoveredExt, Block, BlockBody, SealedHeader, }; -use reth_primitives_traits::block::Block as _; +use reth_primitives_traits::{block::Block as _, RecoveredBlock}; use reth_provider::{ HashedPostStateProvider, ProviderError, StateProofProvider, StateProviderFactory, StateRootProvider, @@ -864,7 +863,7 @@ where } // A sequencer's block should never contain blob or deposit transactions from the pool. - if tx.is_eip4844() || tx.tx_type() == TxType::Deposit as u8 { + if tx.is_eip4844() || tx.tx_type() == OpTxType::Deposit { best_txs.mark_invalid(tx.signer(), tx.nonce()); continue } diff --git a/crates/optimism/primitives/Cargo.toml b/crates/optimism/primitives/Cargo.toml index 5e1fd1731..6632cc53b 100644 --- a/crates/optimism/primitives/Cargo.toml +++ b/crates/optimism/primitives/Cargo.toml @@ -49,6 +49,7 @@ rstest.workspace = true arbitrary.workspace = true secp256k1 = { workspace = true, features = ["rand"] } proptest.workspace = true +rand.workspace = true [features] default = ["std"] diff --git a/crates/optimism/primitives/src/receipt.rs b/crates/optimism/primitives/src/receipt.rs index b235dfe5f..74f9a4e49 100644 --- a/crates/optimism/primitives/src/receipt.rs +++ b/crates/optimism/primitives/src/receipt.rs @@ -308,4 +308,197 @@ mod compact { (receipt.into(), buf) } } + + #[cfg(test)] + #[test] + fn test_ensure_backwards_compatibility() { + use reth_codecs::{test_utils::UnusedBits, validate_bitflag_backwards_compat}; + + assert_eq!(CompactOpReceipt::bitflag_encoded_bytes(), 2); + validate_bitflag_backwards_compat!(CompactOpReceipt<'_>, UnusedBits::NotZero); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_eips::eip2718::Encodable2718; + use alloy_primitives::{address, b256, bytes, hex_literal::hex, Bytes}; + use alloy_rlp::Encodable; + use reth_codecs::Compact; + + #[test] + fn test_decode_receipt() { + reth_codecs::test_utils::test_decode::(&hex!( + "c30328b52ffd23fc426961a00105007eb0042307705a97e503562eacf2b95060cce9de6de68386b6c155b73a9650021a49e2f8baad17f30faff5899d785c4c0873e45bc268bcf07560106424570d11f9a59e8f3db1efa4ceec680123712275f10d92c3411e1caaa11c7c5d591bc11487168e09934a9986848136da1b583babf3a7188e3aed007a1520f1cf4c1ca7d3482c6c28d37c298613c70a76940008816c4c95644579fd08471dc34732fd0f24" + )); + } + + // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 + #[test] + fn encode_legacy_receipt() { + let expected = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); + + let mut data = Vec::with_capacity(expected.length()); + let receipt = ReceiptWithBloom { + receipt: OpReceipt::Legacy(Receipt { + status: Eip658Value::Eip658(false), + cumulative_gas_used: 0x1, + logs: vec![Log::new_unchecked( + address!("0000000000000000000000000000000000000011"), + vec![ + b256!("000000000000000000000000000000000000000000000000000000000000dead"), + b256!("000000000000000000000000000000000000000000000000000000000000beef"), + ], + bytes!("0100ff"), + )], + }), + logs_bloom: [0; 256].into(), + }; + + receipt.encode(&mut data); + + // check that the rlp length equals the length of the expected rlp + assert_eq!(receipt.length(), expected.len()); + assert_eq!(data, expected); + } + + // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 + #[test] + fn decode_legacy_receipt() { + let data = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); + + // EIP658Receipt + let expected = ReceiptWithBloom { + receipt: OpReceipt::Legacy(Receipt { + status: Eip658Value::Eip658(false), + cumulative_gas_used: 0x1, + logs: vec![Log::new_unchecked( + address!("0000000000000000000000000000000000000011"), + vec![ + b256!("000000000000000000000000000000000000000000000000000000000000dead"), + b256!("000000000000000000000000000000000000000000000000000000000000beef"), + ], + bytes!("0100ff"), + )], + }), + logs_bloom: [0; 256].into(), + }; + + let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap(); + assert_eq!(receipt, expected); + } + + #[test] + fn decode_deposit_receipt_regolith_roundtrip() { + let data = hex!("b901107ef9010c0182b741b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0833d3bbf"); + + // Deposit Receipt (post-regolith) + let expected = ReceiptWithBloom { + receipt: OpReceipt::Deposit(OpDepositReceipt { + inner: Receipt { + status: Eip658Value::Eip658(true), + cumulative_gas_used: 46913, + logs: vec![], + }, + deposit_nonce: Some(4012991), + deposit_receipt_version: None, + }), + logs_bloom: [0; 256].into(), + }; + + let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap(); + assert_eq!(receipt, expected); + + let mut buf = Vec::with_capacity(data.len()); + receipt.encode(&mut buf); + assert_eq!(buf, &data[..]); + } + + #[test] + fn decode_deposit_receipt_canyon_roundtrip() { + let data = hex!("b901117ef9010d0182b741b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0833d3bbf01"); + + // Deposit Receipt (post-regolith) + let expected = ReceiptWithBloom { + receipt: OpReceipt::Deposit(OpDepositReceipt { + inner: Receipt { + status: Eip658Value::Eip658(true), + cumulative_gas_used: 46913, + logs: vec![], + }, + deposit_nonce: Some(4012991), + deposit_receipt_version: Some(1), + }), + logs_bloom: [0; 256].into(), + }; + + let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap(); + assert_eq!(receipt, expected); + + let mut buf = Vec::with_capacity(data.len()); + expected.encode(&mut buf); + assert_eq!(buf, &data[..]); + } + + #[test] + fn gigantic_receipt() { + let receipt = OpReceipt::Legacy(Receipt { + status: Eip658Value::Eip658(true), + cumulative_gas_used: 16747627, + logs: vec![ + Log::new_unchecked( + address!("4bf56695415f725e43c3e04354b604bcfb6dfb6e"), + vec![b256!("c69dc3d7ebff79e41f525be431d5cd3cc08f80eaf0f7819054a726eeb7086eb9")], + Bytes::from(vec![1; 0xffffff]), + ), + Log::new_unchecked( + address!("faca325c86bf9c2d5b413cd7b90b209be92229c2"), + vec![b256!("8cca58667b1e9ffa004720ac99a3d61a138181963b294d270d91c53d36402ae2")], + Bytes::from(vec![1; 0xffffff]), + ), + ], + }); + + let mut data = vec![]; + receipt.to_compact(&mut data); + let (decoded, _) = OpReceipt::from_compact(&data[..], data.len()); + assert_eq!(decoded, receipt); + } + + #[test] + fn test_encode_2718_length() { + let receipt = ReceiptWithBloom { + receipt: OpReceipt::Eip1559(Receipt { + status: Eip658Value::Eip658(true), + cumulative_gas_used: 21000, + logs: vec![], + }), + logs_bloom: Bloom::default(), + }; + + let encoded = receipt.encoded_2718(); + assert_eq!( + encoded.len(), + receipt.encode_2718_len(), + "Encoded length should match the actual encoded data length" + ); + + // Test for legacy receipt as well + let legacy_receipt = ReceiptWithBloom { + receipt: OpReceipt::Legacy(Receipt { + status: Eip658Value::Eip658(true), + cumulative_gas_used: 21000, + logs: vec![], + }), + logs_bloom: Bloom::default(), + }; + + let legacy_encoded = legacy_receipt.encoded_2718(); + assert_eq!( + legacy_encoded.len(), + legacy_receipt.encode_2718_len(), + "Encoded length for legacy receipt should match the actual encoded data length" + ); + } } diff --git a/crates/optimism/rpc/Cargo.toml b/crates/optimism/rpc/Cargo.toml index 1bc4071f1..13da62a14 100644 --- a/crates/optimism/rpc/Cargo.toml +++ b/crates/optimism/rpc/Cargo.toml @@ -70,7 +70,6 @@ reth-optimism-chainspec.workspace = true [features] optimism = [ "reth-optimism-evm/optimism", - "reth-primitives/optimism", "reth-provider/optimism", "revm/optimism", "reth-optimism-consensus/optimism", diff --git a/crates/optimism/storage/Cargo.toml b/crates/optimism/storage/Cargo.toml index b72e9c287..ae2bf1375 100644 --- a/crates/optimism/storage/Cargo.toml +++ b/crates/optimism/storage/Cargo.toml @@ -21,7 +21,6 @@ reth-stages-types.workspace = true [features] optimism = [ - "reth-primitives/optimism", "reth-codecs/op", "reth-db-api/optimism" ] diff --git a/crates/optimism/storage/src/lib.rs b/crates/optimism/storage/src/lib.rs index 0db8f4e20..3f13133dd 100644 --- a/crates/optimism/storage/src/lib.rs +++ b/crates/optimism/storage/src/lib.rs @@ -16,7 +16,7 @@ mod tests { CompactClientVersion, CompactU256, CompactU64, StoredBlockBodyIndices, StoredBlockWithdrawals, }; - use reth_primitives::{Account, Receipt}; + use reth_primitives::Account; use reth_prune_types::{PruneCheckpoint, PruneMode, PruneSegment}; use reth_stages_types::{ AccountHashingCheckpoint, CheckpointBlockRange, EntitiesCheckpoint, ExecutionCheckpoint, @@ -39,7 +39,6 @@ mod tests { assert_eq!(PruneCheckpoint::bitflag_encoded_bytes(), 1); assert_eq!(PruneMode::bitflag_encoded_bytes(), 1); assert_eq!(PruneSegment::bitflag_encoded_bytes(), 1); - assert_eq!(Receipt::bitflag_encoded_bytes(), 2); assert_eq!(StageCheckpoint::bitflag_encoded_bytes(), 1); assert_eq!(StageUnitCheckpoint::bitflag_encoded_bytes(), 1); assert_eq!(StoredBlockBodyIndices::bitflag_encoded_bytes(), 1); @@ -62,7 +61,6 @@ mod tests { validate_bitflag_backwards_compat!(PruneCheckpoint, UnusedBits::NotZero); validate_bitflag_backwards_compat!(PruneMode, UnusedBits::Zero); validate_bitflag_backwards_compat!(PruneSegment, UnusedBits::Zero); - validate_bitflag_backwards_compat!(Receipt, UnusedBits::NotZero); validate_bitflag_backwards_compat!(StageCheckpoint, UnusedBits::NotZero); validate_bitflag_backwards_compat!(StageUnitCheckpoint, UnusedBits::Zero); validate_bitflag_backwards_compat!(StoredBlockBodyIndices, UnusedBits::Zero); diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 2ccaf4b0d..0206035bf 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -13,48 +13,24 @@ workspace = true [dependencies] # reth +reth-ethereum-primitives.workspace = true reth-primitives-traits = { workspace = true, features = ["serde"] } reth-ethereum-forks.workspace = true reth-static-file-types.workspace = true -revm-primitives = { workspace = true, features = ["serde"] } -reth-codecs = { workspace = true, optional = true } -reth-zstd-compressors = { workspace = true, optional = true } # ethereum alloy-consensus.workspace = true -alloy-network = { workspace = true, optional = true } alloy-primitives = { workspace = true, features = ["rand", "rlp"] } -alloy-rlp = { workspace = true, features = ["arrayvec"] } -alloy-rpc-types = { workspace = true, optional = true } -alloy-serde = { workspace = true, optional = true } alloy-eips = { workspace = true, features = ["serde"] } alloy-trie = { workspace = true, features = ["serde"] } -# optimism -op-alloy-rpc-types = { workspace = true, optional = true } -op-alloy-consensus = { workspace = true, features = [ - "arbitrary", - "serde", -], optional = true } - # for eip-4844 c-kzg = { workspace = true, features = ["serde"], optional = true } -# crypto -secp256k1 = { workspace = true, features = [ - "global-context", - "recovery", - "rand", -], optional = true } - # misc -bytes.workspace = true derive_more.workspace = true -modular-bitfield = { workspace = true, optional = true } once_cell.workspace = true -rand = { workspace = true, optional = true } serde.workspace = true -serde_with = { workspace = true, optional = true } # arbitrary utils arbitrary = { workspace = true, features = ["derive"], optional = true } @@ -62,30 +38,20 @@ arbitrary = { workspace = true, features = ["derive"], optional = true } [dev-dependencies] # eth reth-chainspec = { workspace = true, features = ["arbitrary"] } -reth-codecs = { workspace = true, features = ["test-utils"] } reth-primitives-traits = { workspace = true, features = ["arbitrary", "test-utils"] } -reth-testing-utils.workspace = true reth-trie-common = { workspace = true, features = ["arbitrary"] } -revm-primitives = { workspace = true, features = ["arbitrary"] } +alloy-rlp.workspace = true alloy-eips = { workspace = true, features = ["arbitrary"] } alloy-genesis.workspace = true arbitrary = { workspace = true, features = ["derive"] } -secp256k1 = { workspace = true, features = [ - "global-context", - "recovery", - "rand", -] } assert_matches.workspace = true -bincode.workspace = true proptest-arbitrary-interop.workspace = true proptest.workspace = true -rand.workspace = true serde_json.workspace = true -test-fuzz.workspace = true -rstest.workspace = true +reth-codecs.workspace = true criterion.workspace = true pprof = { workspace = true, features = [ @@ -102,50 +68,36 @@ std = [ "alloy-eips/std", "alloy-genesis/std", "alloy-primitives/std", - "alloy-serde?/std", "once_cell/std", - "revm-primitives/std", "serde/std", "alloy-trie/std", - "serde_with?/std", - "alloy-rlp/std", "reth-ethereum-forks/std", - "bytes/std", "derive_more/std", - "reth-zstd-compressors?/std", - "secp256k1?/std", "reth-trie-common/std", - "op-alloy-consensus?/std", - "op-alloy-rpc-types?/std", "serde_json/std", - "reth-chainspec/std" + "reth-chainspec/std", + "reth-ethereum-primitives/std", + "alloy-rlp/std" ] reth-codec = [ - "dep:reth-codecs", - "dep:reth-zstd-compressors", - "dep:modular-bitfield", "std", + "std", "reth-primitives-traits/reth-codec", + "reth-ethereum-primitives/reth-codec" ] -asm-keccak = ["alloy-primitives/asm-keccak", "revm-primitives/asm-keccak"] +asm-keccak = ["alloy-primitives/asm-keccak"] arbitrary = [ "dep:arbitrary", "alloy-eips/arbitrary", - "rand", "reth-codec", "reth-ethereum-forks/arbitrary", "reth-primitives-traits/arbitrary", - "revm-primitives/arbitrary", "reth-chainspec/arbitrary", "alloy-consensus/arbitrary", "alloy-primitives/arbitrary", - "alloy-rpc-types?/arbitrary", - "alloy-serde?/arbitrary", - "op-alloy-consensus?/arbitrary", - "op-alloy-rpc-types?/arbitrary", - "reth-codecs?/arbitrary", "alloy-trie/arbitrary", "reth-trie-common/arbitrary", - "dep:secp256k1" + "reth-ethereum-primitives/arbitrary", + "reth-codecs/arbitrary" ] secp256k1 = [ "reth-primitives-traits/secp256k1", @@ -154,33 +106,23 @@ c-kzg = [ "dep:c-kzg", "alloy-consensus/kzg", "alloy-eips/kzg", - "revm-primitives/c-kzg", -] -optimism = [ - "dep:op-alloy-consensus", - "reth-codecs?/op", - "revm-primitives/optimism", ] alloy-compat = [ - "dep:alloy-rpc-types", - "dep:alloy-serde", - "dep:op-alloy-rpc-types", - "dep:alloy-network", + "reth-ethereum-primitives/alloy-compat" ] test-utils = [ "reth-primitives-traits/test-utils", "reth-chainspec/test-utils", - "reth-codecs?/test-utils", "reth-trie-common/test-utils", "arbitrary", + "reth-codecs/test-utils" ] serde-bincode-compat = [ - "serde_with", "alloy-eips/serde-bincode-compat", "alloy-consensus/serde-bincode-compat", - "op-alloy-consensus?/serde-bincode-compat", "reth-primitives-traits/serde-bincode-compat", "reth-trie-common/serde-bincode-compat", + "reth-ethereum-primitives/serde-bincode-compat" ] [[bench]] diff --git a/crates/primitives/src/alloy_compat.rs b/crates/primitives/src/alloy_compat.rs deleted file mode 100644 index a269ebfb7..000000000 --- a/crates/primitives/src/alloy_compat.rs +++ /dev/null @@ -1,189 +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_serde::WithOtherFields; - -use op_alloy_rpc_types as _; - -impl TryFrom for TransactionSigned { - type Error = alloy_rpc_types::ConversionError; - - fn try_from(tx: AnyRpcTransaction) -> Result { - use alloy_rpc_types::ConversionError; - - let WithOtherFields { inner: tx, other: _ } = tx; - - #[allow(unreachable_patterns)] - let (transaction, signature, hash) = match tx.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) - } - #[cfg(feature = "optimism")] - AnyTxEnvelope::Unknown(alloy_network::UnknownTxEnvelope { hash, inner }) => { - use alloy_consensus::{Transaction as _, Typed2718}; - - if inner.ty() == crate::TxType::Deposit { - let fields: op_alloy_rpc_types::OpTransactionFields = inner - .fields - .clone() - .deserialize_into::() - .map_err(|e| ConversionError::Custom(e.to_string()))?; - ( - Transaction::Deposit(op_alloy_consensus::TxDeposit { - source_hash: fields.source_hash.ok_or_else(|| { - ConversionError::Custom("MissingSourceHash".to_string()) - })?, - from: tx.from, - to: revm_primitives::TxKind::from(inner.to()), - mint: fields.mint.filter(|n| *n != 0), - value: inner.value(), - gas_limit: inner.gas_limit(), - is_system_transaction: fields.is_system_tx.unwrap_or(false), - input: inner.input().clone(), - }), - op_alloy_consensus::TxDeposit::signature(), - hash, - ) - } else { - return Err(ConversionError::Custom("unknown transaction type".to_string())) - } - } - _ => return Err(ConversionError::Custom("unknown transaction type".to_string())), - }; - - Ok(Self { transaction, signature, hash: hash.into() }) - } -} - -#[cfg(test)] -#[cfg(feature = "optimism")] -mod tests { - use super::*; - use alloy_primitives::{address, Address, B256, U256}; - use revm_primitives::TxKind; - - #[test] - fn optimism_deposit_tx_conversion_no_mint() { - let input = r#"{ - "blockHash": "0xef664d656f841b5ad6a2b527b963f1eb48b97d7889d742f6cbff6950388e24cd", - "blockNumber": "0x73a78fd", - "depositReceiptVersion": "0x1", - "from": "0x36bde71c97b33cc4729cf772ae268934f7ab70b2", - "gas": "0xc27a8", - "gasPrice": "0x0", - "hash": "0x0bf1845c5d7a82ec92365d5027f7310793d53004f3c86aa80965c67bf7e7dc80", - "input": -"0xd764ad0b000100000000000000000000000000000000000000000000000000000001cf5400000000000000000000000099c9fc46f92e8a1c0dec1b1747d010903e884be100000000000000000000000042000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e40166a07a0000000000000000000000000994206dfe8de6ec6920ff4d779b0d950605fb53000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd52000000000000000000000000ca74f404e0c7bfa35b13b511097df966d5a65597000000000000000000000000ca74f404e0c7bfa35b13b511097df966d5a65597000000000000000000000000000000000000000000000216614199391dbba2ba00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" -, "mint": "0x0", - "nonce": "0x74060", - "r": "0x0", - "s": "0x0", - "sourceHash": "0x074adb22f2e6ed9bdd31c52eefc1f050e5db56eb85056450bccd79a6649520b3", - "to": "0x4200000000000000000000000000000000000007", - "transactionIndex": "0x1", - "type": "0x7e", - "v": "0x0", - "value": "0x0" - }"#; - let alloy_tx: WithOtherFields> = - serde_json::from_str(input).expect("failed to deserialize"); - - let TransactionSigned { transaction: reth_tx, .. } = - alloy_tx.try_into().expect("failed to convert"); - if let Transaction::Deposit(deposit_tx) = reth_tx { - assert_eq!( - deposit_tx.source_hash, - "0x074adb22f2e6ed9bdd31c52eefc1f050e5db56eb85056450bccd79a6649520b3" - .parse::() - .unwrap() - ); - assert_eq!( - deposit_tx.from, - "0x36bde71c97b33cc4729cf772ae268934f7ab70b2".parse::
().unwrap() - ); - assert_eq!( - deposit_tx.to, - TxKind::from(address!("4200000000000000000000000000000000000007")) - ); - assert_eq!(deposit_tx.mint, None); - assert_eq!(deposit_tx.value, U256::ZERO); - assert_eq!(deposit_tx.gas_limit, 796584); - assert!(!deposit_tx.is_system_transaction); - } else { - panic!("Expected Deposit transaction"); - } - } - - #[test] - fn optimism_deposit_tx_conversion_mint() { - let input = r#"{ - "blockHash": "0x7194f63b105e93fb1a27c50d23d62e422d4185a68536c55c96284911415699b2", - "blockNumber": "0x73a82cc", - "depositReceiptVersion": "0x1", - "from": "0x36bde71c97b33cc4729cf772ae268934f7ab70b2", - "gas": "0x7812e", - "gasPrice": "0x0", - "hash": "0xf7e83886d3c6864f78e01c453ebcd57020c5795d96089e8f0e0b90a467246ddb", - "input": -"0xd764ad0b000100000000000000000000000000000000000000000000000000000001cf5f00000000000000000000000099c9fc46f92e8a1c0dec1b1747d010903e884be100000000000000000000000042000000000000000000000000000000000000100000000000000000000000000000000000000000000000239c2e16a5ca5900000000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e41635f5fd0000000000000000000000002ce910fbba65b454bbaf6a18c952a70f3bcd82990000000000000000000000002ce910fbba65b454bbaf6a18c952a70f3bcd82990000000000000000000000000000000000000000000000239c2e16a5ca590000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" -, "mint": "0x239c2e16a5ca590000", - "nonce": "0x7406b", - "r": "0x0", - "s": "0x0", - "sourceHash": "0xe0358cd2b2686d297c5c859646a613124a874fb9d9c4a2c88636a46a65c06e48", - "to": "0x4200000000000000000000000000000000000007", - "transactionIndex": "0x1", - "type": "0x7e", - "v": "0x0", - "value": "0x239c2e16a5ca590000" - }"#; - let alloy_tx: WithOtherFields> = - serde_json::from_str(input).expect("failed to deserialize"); - - let TransactionSigned { transaction: reth_tx, .. } = - alloy_tx.try_into().expect("failed to convert"); - - if let Transaction::Deposit(deposit_tx) = reth_tx { - assert_eq!( - deposit_tx.source_hash, - "0xe0358cd2b2686d297c5c859646a613124a874fb9d9c4a2c88636a46a65c06e48" - .parse::() - .unwrap() - ); - assert_eq!( - deposit_tx.from, - "0x36bde71c97b33cc4729cf772ae268934f7ab70b2".parse::
().unwrap() - ); - assert_eq!( - deposit_tx.to, - TxKind::from(address!("4200000000000000000000000000000000000007")) - ); - assert_eq!(deposit_tx.mint, Some(656890000000000000000)); - assert_eq!(deposit_tx.value, U256::from(0x239c2e16a5ca590000_u128)); - assert_eq!(deposit_tx.gas_limit, 491822); - assert!(!deposit_tx.is_system_transaction); - } else { - panic!("Expected Deposit transaction"); - } - } -} diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 04d594000..f30ce2c70 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -1,4 +1,4 @@ -use crate::TransactionSigned; +use reth_ethereum_primitives::TransactionSigned; #[cfg(any(test, feature = "arbitrary"))] pub use reth_primitives_traits::test_utils::{generate_valid_header, valid_header_strategy}; diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index b61314124..3d05fb9bf 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -21,8 +21,6 @@ extern crate alloc; -#[cfg(feature = "alloy-compat")] -mod alloy_compat; mod block; pub mod proofs; mod receipt; @@ -69,7 +67,6 @@ pub use c_kzg as kzg; /// Read more: #[cfg(feature = "serde-bincode-compat")] pub mod serde_bincode_compat { - pub use super::transaction::{serde_bincode_compat as transaction, serde_bincode_compat::*}; pub use reth_primitives_traits::serde_bincode_compat::*; } diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index 10ffb437d..1d4a95e73 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -1,6 +1,7 @@ //! Helper function for calculating Merkle proofs and hashes. use crate::Receipt; +use alloy_consensus::TxReceipt; use alloy_eips::eip2718::Encodable2718; use alloy_primitives::B256; pub use alloy_trie::root::ordered_trie_root_with_encoder; @@ -31,20 +32,15 @@ pub fn calculate_receipt_root_no_memo(receipts: &[&Receipt]) -> B256 { #[cfg(test)] mod tests { use super::*; - use crate::Block; + use crate::{Block, TxType}; use alloy_consensus::EMPTY_ROOT_HASH; use alloy_genesis::GenesisAccount; - use alloy_primitives::{b256, hex_literal::hex, Address, U256}; + use alloy_primitives::{b256, bloom, hex_literal::hex, Address, Log, LogData, U256}; use alloy_rlp::Decodable; use reth_chainspec::{HOLESKY, MAINNET, SEPOLIA}; use reth_trie_common::root::{state_root_ref_unhashed, state_root_unhashed}; use std::collections::HashMap; - #[cfg(not(feature = "optimism"))] - use crate::TxType; - #[cfg(not(feature = "optimism"))] - use alloy_primitives::{bloom, Log, LogData}; - #[test] fn check_transaction_root() { let data = &hex!("f90262f901f9a092230ce5476ae868e98c7979cfc165a93f8b6ad1922acf2df62e340916efd49da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa02307107a867056ca33b5087e77c4174f47625e48fb49f1c70ced34890ddd88f3a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba0c598f69a5674cae9337261b669970e24abc0b46e6d284372a239ec8ccbf20b0ab901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8618203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0"); @@ -55,7 +51,6 @@ mod tests { assert_eq!(block.transactions_root, tx_root, "Must be the same"); } - #[cfg(not(feature = "optimism"))] #[test] fn check_receipt_root_optimism() { use alloy_consensus::ReceiptWithBloom; diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 0faf361ac..0d946dc45 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -1,287 +1,13 @@ use alloc::{vec, vec::Vec}; -use reth_primitives_traits::InMemorySize; - -use alloy_consensus::{ - Eip2718EncodableReceipt, Eip658Value, ReceiptWithBloom, RlpDecodableReceipt, - RlpEncodableReceipt, TxReceipt, Typed2718, -}; -use alloy_primitives::{Bloom, Log, B256}; -use alloy_rlp::{Decodable, Encodable, Header, RlpDecodable, RlpEncodable}; -use bytes::BufMut; +use alloy_primitives::B256; use derive_more::{DerefMut, From, IntoIterator}; use serde::{Deserialize, Serialize}; -use crate::TxType; - /// Retrieves gas spent by transactions as a vector of tuples (transaction index, gas used). pub use reth_primitives_traits::receipt::gas_spent_by_transactions; /// Receipt containing result of transaction execution. -#[derive( - Clone, Debug, PartialEq, Eq, Default, RlpEncodable, RlpDecodable, Serialize, Deserialize, -)] -#[cfg_attr(any(test, feature = "reth-codec"), derive(reth_codecs::CompactZstd))] -#[cfg_attr(any(test, feature = "reth-codec"), reth_zstd( - compressor = reth_zstd_compressors::RECEIPT_COMPRESSOR, - decompressor = reth_zstd_compressors::RECEIPT_DECOMPRESSOR -))] -#[rlp(trailing)] -pub struct Receipt { - /// Receipt type. - pub tx_type: TxType, - /// If transaction is executed successfully. - /// - /// This is the `statusCode` - pub success: bool, - /// Gas used - pub cumulative_gas_used: u64, - /// Log send from contracts. - pub logs: Vec, - /// Deposit nonce for Optimism deposit transactions - #[cfg(feature = "optimism")] - pub deposit_nonce: Option, - /// Deposit receipt version for Optimism deposit transactions - /// - /// - /// The deposit receipt version was introduced in Canyon to indicate an update to how - /// receipt hashes should be computed when set. The state transition process - /// ensures this is only set for post-Canyon deposit transactions. - #[cfg(feature = "optimism")] - pub deposit_receipt_version: Option, -} - -impl Receipt { - /// Calculates [`Log`]'s bloom filter. this is slow operation and [`ReceiptWithBloom`] can - /// be used to cache this value. - pub fn bloom_slow(&self) -> Bloom { - alloy_primitives::logs_bloom(self.logs.iter()) - } - - /// Calculates the bloom filter for the receipt and returns the [`ReceiptWithBloom`] container - /// type. - pub fn with_bloom(self) -> ReceiptWithBloom { - self.into() - } - - /// Calculates the bloom filter for the receipt and returns the [`ReceiptWithBloom`] - /// container type. - pub fn with_bloom_ref(&self) -> ReceiptWithBloom<&Self> { - self.into() - } - - /// Returns length of RLP-encoded receipt fields with the given [`Bloom`] without an RLP header. - pub fn rlp_encoded_fields_length(&self, bloom: &Bloom) -> usize { - let len = self.success.length() + - self.cumulative_gas_used.length() + - bloom.length() + - self.logs.length(); - - #[cfg(feature = "optimism")] - if self.tx_type == TxType::Deposit { - let mut len = len; - - if let Some(deposit_nonce) = self.deposit_nonce { - len += deposit_nonce.length(); - } - if let Some(deposit_receipt_version) = self.deposit_receipt_version { - len += deposit_receipt_version.length(); - } - - return len - } - - len - } - - /// RLP-encodes receipt fields with the given [`Bloom`] without an RLP header. - pub fn rlp_encode_fields(&self, bloom: &Bloom, out: &mut dyn BufMut) { - self.success.encode(out); - self.cumulative_gas_used.encode(out); - bloom.encode(out); - self.logs.encode(out); - - #[cfg(feature = "optimism")] - if self.tx_type == TxType::Deposit { - if let Some(nonce) = self.deposit_nonce { - nonce.encode(out); - } - if let Some(version) = self.deposit_receipt_version { - version.encode(out); - } - } - } - - /// Returns RLP header for inner encoding. - pub fn rlp_header_inner(&self, bloom: &Bloom) -> Header { - Header { list: true, payload_length: self.rlp_encoded_fields_length(bloom) } - } - - fn decode_receipt_with_bloom( - buf: &mut &[u8], - tx_type: TxType, - ) -> alloy_rlp::Result> { - let b = &mut &**buf; - let rlp_head = alloy_rlp::Header::decode(b)?; - if !rlp_head.list { - return Err(alloy_rlp::Error::UnexpectedString) - } - let started_len = b.len(); - - let success = Decodable::decode(b)?; - let cumulative_gas_used = Decodable::decode(b)?; - let bloom = Decodable::decode(b)?; - let logs = Decodable::decode(b)?; - - let receipt = match tx_type { - #[cfg(feature = "optimism")] - TxType::Deposit => { - let remaining = |b: &[u8]| rlp_head.payload_length - (started_len - b.len()) > 0; - let deposit_nonce = remaining(b).then(|| Decodable::decode(b)).transpose()?; - let deposit_receipt_version = - remaining(b).then(|| Decodable::decode(b)).transpose()?; - - Self { - tx_type, - success, - cumulative_gas_used, - logs, - deposit_nonce, - deposit_receipt_version, - } - } - _ => Self { - tx_type, - success, - cumulative_gas_used, - logs, - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - }, - }; - - let this = ReceiptWithBloom { receipt, logs_bloom: bloom }; - let consumed = started_len - b.len(); - if consumed != rlp_head.payload_length { - return Err(alloy_rlp::Error::ListLengthMismatch { - expected: rlp_head.payload_length, - got: consumed, - }) - } - *buf = *b; - Ok(this) - } -} - -impl Eip2718EncodableReceipt for Receipt { - fn eip2718_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize { - self.rlp_header_inner(bloom).length_with_payload() + - !matches!(self.tx_type, TxType::Legacy) as usize // account for type prefix - } - - fn eip2718_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) { - if !matches!(self.tx_type, TxType::Legacy) { - out.put_u8(self.tx_type as u8); - } - self.rlp_header_inner(bloom).encode(out); - self.rlp_encode_fields(bloom, out); - } -} - -impl RlpEncodableReceipt for Receipt { - fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize { - let mut len = self.eip2718_encoded_length_with_bloom(bloom); - if !matches!(self.tx_type, TxType::Legacy) { - len += Header { - list: false, - payload_length: self.eip2718_encoded_length_with_bloom(bloom), - } - .length(); - } - - len - } - - fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) { - if !matches!(self.tx_type, TxType::Legacy) { - Header { list: false, payload_length: self.eip2718_encoded_length_with_bloom(bloom) } - .encode(out); - } - self.eip2718_encode_with_bloom(bloom, out); - } -} - -impl RlpDecodableReceipt for Receipt { - fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result> { - let header_buf = &mut &**buf; - let header = Header::decode(header_buf)?; - - if header.list { - return Self::decode_receipt_with_bloom(buf, TxType::Legacy); - } - - *buf = *header_buf; - - let remaining = buf.len(); - let tx_type = TxType::decode(buf)?; - let this = Self::decode_receipt_with_bloom(buf, tx_type)?; - - if buf.len() + header.payload_length != remaining { - return Err(alloy_rlp::Error::UnexpectedLength); - } - - Ok(this) - } -} - -impl TxReceipt for Receipt { - type Log = Log; - - fn status_or_post_state(&self) -> Eip658Value { - self.success.into() - } - - fn status(&self) -> bool { - self.success - } - - fn bloom(&self) -> Bloom { - alloy_primitives::logs_bloom(self.logs.iter()) - } - - fn cumulative_gas_used(&self) -> u64 { - self.cumulative_gas_used - } - - fn logs(&self) -> &[Log] { - &self.logs - } -} - -impl Typed2718 for Receipt { - fn ty(&self) -> u8 { - self.tx_type as u8 - } -} - -impl reth_primitives_traits::Receipt for Receipt {} - -impl InMemorySize for Receipt { - /// Calculates a heuristic for the in-memory size of the [Receipt]. - #[inline] - fn size(&self) -> usize { - let total_size = self.tx_type.size() + - core::mem::size_of::() + - core::mem::size_of::() + - self.logs.capacity() * core::mem::size_of::(); - - #[cfg(feature = "optimism")] - return total_size + 2 * core::mem::size_of::>(); - #[cfg(not(feature = "optimism"))] - total_size - } -} +pub use reth_ethereum_primitives::Receipt; /// A collection of receipts organized as a two-dimensional vector. #[derive( @@ -342,248 +68,3 @@ impl Default for Receipts { Self { receipt_vec: Vec::new() } } } - -#[cfg(any(test, feature = "arbitrary"))] -impl<'a> arbitrary::Arbitrary<'a> for Receipt { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - let tx_type = TxType::arbitrary(u)?; - let success = bool::arbitrary(u)?; - let cumulative_gas_used = u64::arbitrary(u)?; - let logs = Vec::::arbitrary(u)?; - - // Only receipts for deposit transactions may contain a deposit nonce - #[cfg(feature = "optimism")] - let (deposit_nonce, deposit_receipt_version) = if tx_type == TxType::Deposit { - let deposit_nonce = Option::::arbitrary(u)?; - let deposit_nonce_version = - deposit_nonce.map(|_| Option::::arbitrary(u)).transpose()?.flatten(); - (deposit_nonce, deposit_nonce_version) - } else { - (None, None) - }; - - Ok(Self { - tx_type, - success, - cumulative_gas_used, - logs, - #[cfg(feature = "optimism")] - deposit_nonce, - #[cfg(feature = "optimism")] - deposit_receipt_version, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_eips::eip2718::Encodable2718; - use alloy_primitives::{address, b256, bytes, hex_literal::hex, Bytes}; - use reth_codecs::Compact; - - #[test] - fn test_decode_receipt() { - #[cfg(not(feature = "optimism"))] - reth_codecs::test_utils::test_decode::(&hex!( - "c428b52ffd23fc42696156b10200f034792b6a94c3850215c2fef7aea361a0c31b79d9a32652eefc0d4e2e730036061cff7344b6fc6132b50cda0ed810a991ae58ef013150c12b2522533cb3b3a8b19b7786a8b5ff1d3cdc84225e22b02def168c8858df" - )); - #[cfg(feature = "optimism")] - reth_codecs::test_utils::test_decode::(&hex!( - "c30328b52ffd23fc426961a00105007eb0042307705a97e503562eacf2b95060cce9de6de68386b6c155b73a9650021a49e2f8baad17f30faff5899d785c4c0873e45bc268bcf07560106424570d11f9a59e8f3db1efa4ceec680123712275f10d92c3411e1caaa11c7c5d591bc11487168e09934a9986848136da1b583babf3a7188e3aed007a1520f1cf4c1ca7d3482c6c28d37c298613c70a76940008816c4c95644579fd08471dc34732fd0f24" - )); - } - - // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 - #[test] - fn encode_legacy_receipt() { - let expected = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); - - let mut data = Vec::with_capacity(expected.length()); - let receipt = ReceiptWithBloom { - receipt: Receipt { - tx_type: TxType::Legacy, - cumulative_gas_used: 0x1u64, - logs: vec![Log::new_unchecked( - address!("0000000000000000000000000000000000000011"), - vec![ - b256!("000000000000000000000000000000000000000000000000000000000000dead"), - b256!("000000000000000000000000000000000000000000000000000000000000beef"), - ], - bytes!("0100ff"), - )], - success: false, - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - }, - logs_bloom: [0; 256].into(), - }; - - receipt.encode(&mut data); - - // check that the rlp length equals the length of the expected rlp - assert_eq!(receipt.length(), expected.len()); - assert_eq!(data, expected); - } - - // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 - #[test] - fn decode_legacy_receipt() { - let data = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); - - // EIP658Receipt - let expected = ReceiptWithBloom { - receipt: Receipt { - tx_type: TxType::Legacy, - cumulative_gas_used: 0x1u64, - logs: vec![Log::new_unchecked( - address!("0000000000000000000000000000000000000011"), - vec![ - b256!("000000000000000000000000000000000000000000000000000000000000dead"), - b256!("000000000000000000000000000000000000000000000000000000000000beef"), - ], - bytes!("0100ff"), - )], - success: false, - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - }, - logs_bloom: [0; 256].into(), - }; - - let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap(); - assert_eq!(receipt, expected); - } - - #[cfg(feature = "optimism")] - #[test] - fn decode_deposit_receipt_regolith_roundtrip() { - let data = hex!("b901107ef9010c0182b741b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0833d3bbf"); - - // Deposit Receipt (post-regolith) - let expected = ReceiptWithBloom { - receipt: Receipt { - tx_type: TxType::Deposit, - cumulative_gas_used: 46913, - logs: vec![], - success: true, - deposit_nonce: Some(4012991), - deposit_receipt_version: None, - }, - logs_bloom: [0; 256].into(), - }; - - let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap(); - assert_eq!(receipt, expected); - - let mut buf = Vec::with_capacity(data.len()); - receipt.encode(&mut buf); - assert_eq!(buf, &data[..]); - } - - #[cfg(feature = "optimism")] - #[test] - fn decode_deposit_receipt_canyon_roundtrip() { - let data = hex!("b901117ef9010d0182b741b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0833d3bbf01"); - - // Deposit Receipt (post-regolith) - let expected = ReceiptWithBloom { - receipt: Receipt { - tx_type: TxType::Deposit, - cumulative_gas_used: 46913, - logs: vec![], - success: true, - deposit_nonce: Some(4012991), - deposit_receipt_version: Some(1), - }, - logs_bloom: [0; 256].into(), - }; - - let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap(); - assert_eq!(receipt, expected); - - let mut buf = Vec::with_capacity(data.len()); - expected.encode(&mut buf); - assert_eq!(buf, &data[..]); - } - - #[test] - fn gigantic_receipt() { - let receipt = Receipt { - cumulative_gas_used: 16747627, - success: true, - tx_type: TxType::Legacy, - logs: vec![ - Log::new_unchecked( - address!("4bf56695415f725e43c3e04354b604bcfb6dfb6e"), - vec![b256!("c69dc3d7ebff79e41f525be431d5cd3cc08f80eaf0f7819054a726eeb7086eb9")], - Bytes::from(vec![1; 0xffffff]), - ), - Log::new_unchecked( - address!("faca325c86bf9c2d5b413cd7b90b209be92229c2"), - vec![b256!("8cca58667b1e9ffa004720ac99a3d61a138181963b294d270d91c53d36402ae2")], - Bytes::from(vec![1; 0xffffff]), - ), - ], - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - }; - - let mut data = vec![]; - receipt.to_compact(&mut data); - let (decoded, _) = Receipt::from_compact(&data[..], data.len()); - assert_eq!(decoded, receipt); - } - - #[test] - fn test_encode_2718_length() { - let receipt = ReceiptWithBloom { - receipt: Receipt { - tx_type: TxType::Eip1559, - success: true, - cumulative_gas_used: 21000, - logs: vec![], - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - }, - logs_bloom: Bloom::default(), - }; - - let encoded = receipt.encoded_2718(); - assert_eq!( - encoded.len(), - receipt.encode_2718_len(), - "Encoded length should match the actual encoded data length" - ); - - // Test for legacy receipt as well - let legacy_receipt = ReceiptWithBloom { - receipt: Receipt { - tx_type: TxType::Legacy, - success: true, - cumulative_gas_used: 21000, - logs: vec![], - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - }, - logs_bloom: Bloom::default(), - }; - - let legacy_encoded = legacy_receipt.encoded_2718(); - assert_eq!( - legacy_encoded.len(), - legacy_receipt.encode_2718_len(), - "Encoded length for legacy receipt should match the actual encoded data length" - ); - } -} diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 6189eb10c..3e5c48a46 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -1,30 +1,8 @@ //! Transaction types. use crate::RecoveredTx; -use alloc::vec::Vec; pub use alloy_consensus::transaction::PooledTransaction; -use alloy_consensus::{ - transaction::RlpEcdsaTx, SignableTransaction, Signed, Transaction as _, TxEip1559, TxEip2930, - TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEip7702, TxEnvelope, TxLegacy, Typed2718, - TypedTransaction, -}; -use alloy_eips::{ - eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}, - eip2930::AccessList, - eip4844::BlobTransactionSidecar, - eip7702::SignedAuthorization, -}; -use alloy_primitives::{ - keccak256, Address, Bytes, ChainId, PrimitiveSignature as Signature, TxHash, TxKind, B256, U256, -}; -use alloy_rlp::{Decodable, Encodable, Header}; -use core::hash::{Hash, Hasher}; -use derive_more::{AsRef, Deref}; use once_cell as _; -#[cfg(feature = "optimism")] -use op_alloy_consensus::DepositTransaction; -#[cfg(feature = "optimism")] -use op_alloy_consensus::TxDeposit; pub use pooled::PooledTransactionsElementEcRecovered; pub use reth_primitives_traits::{ sync::{LazyLock, OnceLock}, @@ -36,9 +14,6 @@ pub use reth_primitives_traits::{ }, FillTxEnv, WithEncoded, }; -use reth_primitives_traits::{InMemorySize, SignedTransaction}; -use revm_primitives::{AuthorizationList, TxEnv}; -use serde::{Deserialize, Serialize}; pub use signature::{recover_signer, recover_signer_unchecked}; pub use tx_type::TxType; @@ -50,1680 +25,20 @@ pub mod util; mod pooled; mod tx_type; -/// A raw transaction. -/// -/// Transaction types were introduced in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718). -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, derive_more::From)] -#[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 temporarily set contract code for an - /// EOA for a single transaction. This allows for temporarily adding smart contract - /// functionality to the EOA. - Eip7702(TxEip7702), - /// Optimism deposit transaction. - #[cfg(feature = "optimism")] - Deposit(TxDeposit), -} - -#[cfg(feature = "optimism")] -impl DepositTransaction for Transaction { - fn source_hash(&self) -> Option { - match self { - Self::Deposit(tx) => tx.source_hash(), - _ => None, - } - } - fn mint(&self) -> Option { - match self { - Self::Deposit(tx) => tx.mint(), - _ => None, - } - } - fn is_system_transaction(&self) -> bool { - match self { - Self::Deposit(tx) => tx.is_system_transaction(), - _ => false, - } - } - fn is_deposit(&self) -> bool { - matches!(self, Self::Deposit(_)) - } -} - -#[cfg(any(test, feature = "arbitrary"))] -impl<'a> arbitrary::Arbitrary<'a> for Transaction { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - let mut tx = match TxType::arbitrary(u)? { - TxType::Legacy => { - let tx = TxLegacy::arbitrary(u)?; - Self::Legacy(tx) - } - TxType::Eip2930 => { - let tx = TxEip2930::arbitrary(u)?; - Self::Eip2930(tx) - } - TxType::Eip1559 => { - let tx = TxEip1559::arbitrary(u)?; - Self::Eip1559(tx) - } - TxType::Eip4844 => { - let tx = TxEip4844::arbitrary(u)?; - Self::Eip4844(tx) - } - - TxType::Eip7702 => { - let tx = TxEip7702::arbitrary(u)?; - Self::Eip7702(tx) - } - #[cfg(feature = "optimism")] - TxType::Deposit => { - let tx = TxDeposit::arbitrary(u)?; - Self::Deposit(tx) - } - }; - - // Otherwise we might overflow when calculating `v` on `recalculate_hash` - if let Some(chain_id) = tx.chain_id() { - tx.set_chain_id(chain_id % (u64::MAX / 2 - 36)); - } - - Ok(tx) - } -} - -impl Typed2718 for Transaction { - fn ty(&self) -> u8 { - match self { - Self::Legacy(tx) => tx.ty(), - Self::Eip2930(tx) => tx.ty(), - Self::Eip1559(tx) => tx.ty(), - Self::Eip4844(tx) => tx.ty(), - Self::Eip7702(tx) => tx.ty(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.ty(), - } - } -} - -// === impl Transaction === - -impl Transaction { - /// Heavy operation that return signature hash over rlp encoded transaction. - /// It is only for signature signing or signer recovery. - pub fn signature_hash(&self) -> B256 { - match self { - Self::Legacy(tx) => tx.signature_hash(), - Self::Eip2930(tx) => tx.signature_hash(), - Self::Eip1559(tx) => tx.signature_hash(), - Self::Eip4844(tx) => tx.signature_hash(), - Self::Eip7702(tx) => tx.signature_hash(), - #[cfg(feature = "optimism")] - Self::Deposit(_) => B256::ZERO, - } - } - - /// Sets the transaction's chain id to the provided value. - pub fn set_chain_id(&mut self, chain_id: u64) { - match self { - Self::Legacy(TxLegacy { chain_id: ref mut c, .. }) => *c = Some(chain_id), - Self::Eip2930(TxEip2930 { chain_id: ref mut c, .. }) | - Self::Eip1559(TxEip1559 { chain_id: ref mut c, .. }) | - Self::Eip4844(TxEip4844 { chain_id: ref mut c, .. }) | - Self::Eip7702(TxEip7702 { chain_id: ref mut c, .. }) => *c = chain_id, - #[cfg(feature = "optimism")] - Self::Deposit(_) => { /* noop */ } - } - } - - /// Get the transaction's type - 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(feature = "optimism")] - Self::Deposit(_) => TxType::Deposit, - } - } - - /// Returns the blob gas used for all blobs of the EIP-4844 transaction if it is an EIP-4844 - /// transaction. - /// - /// This is the number of blobs times the - /// [`DATA_GAS_PER_BLOB`](alloy_eips::eip4844::DATA_GAS_PER_BLOB) a single blob consumes. - pub fn blob_gas_used(&self) -> Option { - self.as_eip4844().map(TxEip4844::blob_gas) - } - - /// Returns the effective miner gas tip cap (`gasTipCap`) for the given base fee: - /// `min(maxFeePerGas - baseFee, maxPriorityFeePerGas)` - /// - /// If the base fee is `None`, the `max_priority_fee_per_gas`, or gas price for non-EIP1559 - /// transactions is returned. - /// - /// Returns `None` if the basefee is higher than the [`Transaction::max_fee_per_gas`]. - pub fn effective_tip_per_gas(&self, base_fee: Option) -> Option { - let base_fee = match base_fee { - Some(base_fee) => base_fee as u128, - None => return Some(self.priority_fee_or_price()), - }; - - let max_fee_per_gas = self.max_fee_per_gas(); - - // Check if max_fee_per_gas is less than base_fee - if max_fee_per_gas < base_fee { - return None - } - - // Calculate the difference between max_fee_per_gas and base_fee - let fee = max_fee_per_gas - base_fee; - - // Compare the fee with max_priority_fee_per_gas (or gas price for non-EIP1559 transactions) - if let Some(priority_fee) = self.max_priority_fee_per_gas() { - Some(fee.min(priority_fee)) - } else { - Some(fee) - } - } - - /// This encodes the transaction _without_ the signature, and is only suitable for creating a - /// hash intended for signing. - pub fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) { - match self { - Self::Legacy(tx) => tx.encode_for_signing(out), - Self::Eip2930(tx) => tx.encode_for_signing(out), - Self::Eip1559(tx) => tx.encode_for_signing(out), - Self::Eip4844(tx) => tx.encode_for_signing(out), - Self::Eip7702(tx) => tx.encode_for_signing(out), - #[cfg(feature = "optimism")] - Self::Deposit(_) => {} - } - } - - /// Produces EIP-2718 encoding of the transaction - pub fn eip2718_encode(&self, signature: &Signature, out: &mut dyn bytes::BufMut) { - match self { - Self::Legacy(legacy_tx) => { - // do nothing w/ with_header - legacy_tx.eip2718_encode(signature, out); - } - Self::Eip2930(access_list_tx) => { - access_list_tx.eip2718_encode(signature, out); - } - Self::Eip1559(dynamic_fee_tx) => { - dynamic_fee_tx.eip2718_encode(signature, out); - } - Self::Eip4844(blob_tx) => blob_tx.eip2718_encode(signature, out), - Self::Eip7702(set_code_tx) => { - set_code_tx.eip2718_encode(signature, out); - } - #[cfg(feature = "optimism")] - Self::Deposit(deposit_tx) => deposit_tx.encode_2718(out), - } - } - - /// This sets the transaction's gas limit. - pub fn set_gas_limit(&mut self, gas_limit: u64) { - match self { - Self::Legacy(tx) => tx.gas_limit = gas_limit, - Self::Eip2930(tx) => tx.gas_limit = gas_limit, - Self::Eip1559(tx) => tx.gas_limit = gas_limit, - Self::Eip4844(tx) => tx.gas_limit = gas_limit, - Self::Eip7702(tx) => tx.gas_limit = gas_limit, - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.gas_limit = gas_limit, - } - } - - /// This sets the transaction's nonce. - pub 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(feature = "optimism")] - Self::Deposit(_) => { /* noop */ } - } - } - - /// This sets the transaction's value. - pub fn set_value(&mut self, value: U256) { - match self { - Self::Legacy(tx) => tx.value = value, - Self::Eip2930(tx) => tx.value = value, - Self::Eip1559(tx) => tx.value = value, - Self::Eip4844(tx) => tx.value = value, - Self::Eip7702(tx) => tx.value = value, - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.value = value, - } - } - - /// This sets the transaction's input field. - pub fn set_input(&mut self, input: Bytes) { - match self { - Self::Legacy(tx) => tx.input = input, - Self::Eip2930(tx) => tx.input = input, - Self::Eip1559(tx) => tx.input = input, - Self::Eip4844(tx) => tx.input = input, - Self::Eip7702(tx) => tx.input = input, - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.input = input, - } - } - - /// Returns true if the transaction is a legacy transaction. - #[inline] - pub const fn is_legacy(&self) -> bool { - matches!(self, Self::Legacy(_)) - } - - /// Returns true if the transaction is an EIP-2930 transaction. - #[inline] - pub const fn is_eip2930(&self) -> bool { - matches!(self, Self::Eip2930(_)) - } - - /// Returns true if the transaction is an EIP-1559 transaction. - #[inline] - pub const fn is_eip1559(&self) -> bool { - matches!(self, Self::Eip1559(_)) - } - - /// Returns true if the transaction is an EIP-4844 transaction. - #[inline] - pub const fn is_eip4844(&self) -> bool { - matches!(self, Self::Eip4844(_)) - } - - /// Returns true if the transaction is an EIP-7702 transaction. - #[inline] - pub const fn is_eip7702(&self) -> bool { - matches!(self, Self::Eip7702(_)) - } - - /// Returns the [`TxLegacy`] variant if the transaction is a legacy transaction. - pub const fn as_legacy(&self) -> Option<&TxLegacy> { - match self { - Self::Legacy(tx) => Some(tx), - _ => None, - } - } - - /// Returns the [`TxEip2930`] variant if the transaction is an EIP-2930 transaction. - pub const fn as_eip2930(&self) -> Option<&TxEip2930> { - match self { - Self::Eip2930(tx) => Some(tx), - _ => None, - } - } - - /// Returns the [`TxEip1559`] variant if the transaction is an EIP-1559 transaction. - pub const fn as_eip1559(&self) -> Option<&TxEip1559> { - match self { - Self::Eip1559(tx) => Some(tx), - _ => None, - } - } - - /// Returns the [`TxEip4844`] variant if the transaction is an EIP-4844 transaction. - pub const fn as_eip4844(&self) -> Option<&TxEip4844> { - match self { - Self::Eip4844(tx) => Some(tx), - _ => None, - } - } - - /// Returns the [`TxEip7702`] variant if the transaction is an EIP-7702 transaction. - pub const fn as_eip7702(&self) -> Option<&TxEip7702> { - match self { - Self::Eip7702(tx) => Some(tx), - _ => None, - } - } -} - -impl InMemorySize for Transaction { - /// Calculates a heuristic for the in-memory size of the [Transaction]. - #[inline] - fn size(&self) -> usize { - match self { - Self::Legacy(tx) => tx.size(), - Self::Eip2930(tx) => tx.size(), - Self::Eip1559(tx) => tx.size(), - Self::Eip4844(tx) => tx.size(), - Self::Eip7702(tx) => tx.size(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.size(), - } - } -} - -#[cfg(any(test, feature = "reth-codec"))] -impl reth_codecs::Compact for Transaction { - // Serializes the TxType to the buffer if necessary, returning 2 bits of the type as an - // identifier instead of the length. - fn to_compact(&self, buf: &mut B) -> usize - where - B: bytes::BufMut + AsMut<[u8]>, - { - let identifier = self.tx_type().to_compact(buf); - match self { - Self::Legacy(tx) => { - tx.to_compact(buf); - } - Self::Eip2930(tx) => { - tx.to_compact(buf); - } - Self::Eip1559(tx) => { - tx.to_compact(buf); - } - Self::Eip4844(tx) => { - tx.to_compact(buf); - } - Self::Eip7702(tx) => { - tx.to_compact(buf); - } - #[cfg(feature = "optimism")] - Self::Deposit(tx) => { - 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 a identifier with value [`DEPOSIT_TX_TYPE_ID`] is allowed. - fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) { - use bytes::Buf; - - match identifier { - reth_codecs::txtype::COMPACT_IDENTIFIER_LEGACY => { - let (tx, buf) = TxLegacy::from_compact(buf, buf.len()); - (Self::Legacy(tx), buf) - } - reth_codecs::txtype::COMPACT_IDENTIFIER_EIP2930 => { - let (tx, buf) = TxEip2930::from_compact(buf, buf.len()); - (Self::Eip2930(tx), buf) - } - reth_codecs::txtype::COMPACT_IDENTIFIER_EIP1559 => { - let (tx, buf) = TxEip1559::from_compact(buf, buf.len()); - (Self::Eip1559(tx), buf) - } - reth_codecs::txtype::COMPACT_EXTENDED_IDENTIFIER_FLAG => { - // An identifier of 3 indicates that the transaction type did not fit into - // the backwards compatible 2 bit identifier, their transaction types are - // larger than 2 bits (eg. 4844 and Deposit Transactions). In this case, - // we need to read the concrete transaction type from the buffer by - // reading the full 8 bits (single byte) and match on this transaction type. - let identifier = buf.get_u8(); - match identifier { - alloy_consensus::constants::EIP4844_TX_TYPE_ID => { - let (tx, buf) = TxEip4844::from_compact(buf, buf.len()); - (Self::Eip4844(tx), buf) - } - alloy_consensus::constants::EIP7702_TX_TYPE_ID => { - let (tx, buf) = TxEip7702::from_compact(buf, buf.len()); - (Self::Eip7702(tx), buf) - } - #[cfg(feature = "optimism")] - op_alloy_consensus::DEPOSIT_TX_TYPE_ID => { - let (tx, buf) = TxDeposit::from_compact(buf, buf.len()); - (Self::Deposit(tx), buf) - } - _ => unreachable!( - "Junk data in database: unknown Transaction variant: {identifier}" - ), - } - } - _ => unreachable!("Junk data in database: unknown Transaction variant: {identifier}"), - } - } -} - -impl Default for Transaction { - fn default() -> Self { - Self::Legacy(TxLegacy::default()) - } -} - -impl alloy_consensus::Transaction for Transaction { - fn chain_id(&self) -> Option { - match self { - Self::Legacy(tx) => tx.chain_id(), - Self::Eip2930(tx) => tx.chain_id(), - Self::Eip1559(tx) => tx.chain_id(), - Self::Eip4844(tx) => tx.chain_id(), - Self::Eip7702(tx) => tx.chain_id(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.chain_id(), - } - } - - fn nonce(&self) -> u64 { - match self { - Self::Legacy(tx) => tx.nonce(), - Self::Eip2930(tx) => tx.nonce(), - Self::Eip1559(tx) => tx.nonce(), - Self::Eip4844(tx) => tx.nonce(), - Self::Eip7702(tx) => tx.nonce(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.nonce(), - } - } - - fn gas_limit(&self) -> u64 { - match self { - Self::Legacy(tx) => tx.gas_limit(), - Self::Eip2930(tx) => tx.gas_limit(), - Self::Eip1559(tx) => tx.gas_limit(), - Self::Eip4844(tx) => tx.gas_limit(), - Self::Eip7702(tx) => tx.gas_limit(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.gas_limit(), - } - } - - fn gas_price(&self) -> Option { - match self { - Self::Legacy(tx) => tx.gas_price(), - Self::Eip2930(tx) => tx.gas_price(), - Self::Eip1559(tx) => tx.gas_price(), - Self::Eip4844(tx) => tx.gas_price(), - Self::Eip7702(tx) => tx.gas_price(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.gas_price(), - } - } - - fn max_fee_per_gas(&self) -> u128 { - match self { - Self::Legacy(tx) => tx.max_fee_per_gas(), - Self::Eip2930(tx) => tx.max_fee_per_gas(), - Self::Eip1559(tx) => tx.max_fee_per_gas(), - Self::Eip4844(tx) => tx.max_fee_per_gas(), - Self::Eip7702(tx) => tx.max_fee_per_gas(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.max_fee_per_gas(), - } - } - - fn max_priority_fee_per_gas(&self) -> Option { - match self { - Self::Legacy(tx) => tx.max_priority_fee_per_gas(), - Self::Eip2930(tx) => tx.max_priority_fee_per_gas(), - Self::Eip1559(tx) => tx.max_priority_fee_per_gas(), - Self::Eip4844(tx) => tx.max_priority_fee_per_gas(), - Self::Eip7702(tx) => tx.max_priority_fee_per_gas(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.max_priority_fee_per_gas(), - } - } - - fn max_fee_per_blob_gas(&self) -> Option { - match self { - Self::Legacy(tx) => tx.max_fee_per_blob_gas(), - Self::Eip2930(tx) => tx.max_fee_per_blob_gas(), - Self::Eip1559(tx) => tx.max_fee_per_blob_gas(), - Self::Eip4844(tx) => tx.max_fee_per_blob_gas(), - Self::Eip7702(tx) => tx.max_fee_per_blob_gas(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.max_fee_per_blob_gas(), - } - } - - fn priority_fee_or_price(&self) -> u128 { - match self { - Self::Legacy(tx) => tx.priority_fee_or_price(), - Self::Eip2930(tx) => tx.priority_fee_or_price(), - Self::Eip1559(tx) => tx.priority_fee_or_price(), - Self::Eip4844(tx) => tx.priority_fee_or_price(), - Self::Eip7702(tx) => tx.priority_fee_or_price(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.priority_fee_or_price(), - } - } - - fn effective_gas_price(&self, base_fee: Option) -> u128 { - match self { - Self::Legacy(tx) => tx.effective_gas_price(base_fee), - Self::Eip2930(tx) => tx.effective_gas_price(base_fee), - Self::Eip1559(tx) => tx.effective_gas_price(base_fee), - Self::Eip4844(tx) => tx.effective_gas_price(base_fee), - Self::Eip7702(tx) => tx.effective_gas_price(base_fee), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.effective_gas_price(base_fee), - } - } - - fn is_dynamic_fee(&self) -> bool { - match self { - Self::Legacy(_) | Self::Eip2930(_) => false, - Self::Eip1559(_) | Self::Eip4844(_) | Self::Eip7702(_) => true, - #[cfg(feature = "optimism")] - Self::Deposit(_) => false, - } - } - - fn kind(&self) -> TxKind { - match self { - Self::Legacy(tx) => tx.kind(), - Self::Eip2930(tx) => tx.kind(), - Self::Eip1559(tx) => tx.kind(), - Self::Eip4844(tx) => tx.kind(), - Self::Eip7702(tx) => tx.kind(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.kind(), - } - } - - fn is_create(&self) -> bool { - match self { - Self::Legacy(tx) => tx.is_create(), - Self::Eip2930(tx) => tx.is_create(), - Self::Eip1559(tx) => tx.is_create(), - Self::Eip4844(tx) => tx.is_create(), - Self::Eip7702(tx) => tx.is_create(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.is_create(), - } - } - - fn value(&self) -> U256 { - match self { - Self::Legacy(tx) => tx.value(), - Self::Eip2930(tx) => tx.value(), - Self::Eip1559(tx) => tx.value(), - Self::Eip4844(tx) => tx.value(), - Self::Eip7702(tx) => tx.value(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.value(), - } - } - - fn input(&self) -> &Bytes { - match self { - Self::Legacy(tx) => tx.input(), - Self::Eip2930(tx) => tx.input(), - Self::Eip1559(tx) => tx.input(), - Self::Eip4844(tx) => tx.input(), - Self::Eip7702(tx) => tx.input(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.input(), - } - } - - fn access_list(&self) -> Option<&AccessList> { - match self { - Self::Legacy(tx) => tx.access_list(), - Self::Eip2930(tx) => tx.access_list(), - Self::Eip1559(tx) => tx.access_list(), - Self::Eip4844(tx) => tx.access_list(), - Self::Eip7702(tx) => tx.access_list(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.access_list(), - } - } - - fn blob_versioned_hashes(&self) -> Option<&[B256]> { - match self { - Self::Legacy(tx) => tx.blob_versioned_hashes(), - Self::Eip2930(tx) => tx.blob_versioned_hashes(), - Self::Eip1559(tx) => tx.blob_versioned_hashes(), - Self::Eip4844(tx) => tx.blob_versioned_hashes(), - Self::Eip7702(tx) => tx.blob_versioned_hashes(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.blob_versioned_hashes(), - } - } - - fn authorization_list(&self) -> Option<&[SignedAuthorization]> { - match self { - Self::Legacy(tx) => tx.authorization_list(), - Self::Eip2930(tx) => tx.authorization_list(), - Self::Eip1559(tx) => tx.authorization_list(), - Self::Eip4844(tx) => tx.authorization_list(), - Self::Eip7702(tx) => tx.authorization_list(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.authorization_list(), - } - } -} - -impl From for Transaction { - fn from(value: TxEip4844Variant) -> Self { - match value { - TxEip4844Variant::TxEip4844(tx) => tx.into(), - TxEip4844Variant::TxEip4844WithSidecar(tx) => tx.tx.into(), - } - } -} - -impl From for Transaction { - fn from(value: TypedTransaction) -> Self { - match value { - TypedTransaction::Legacy(tx) => tx.into(), - TypedTransaction::Eip2930(tx) => tx.into(), - TypedTransaction::Eip1559(tx) => tx.into(), - TypedTransaction::Eip4844(tx) => tx.into(), - TypedTransaction::Eip7702(tx) => tx.into(), - } - } -} - /// Signed transaction. -#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(rlp))] -#[derive(Debug, Clone, Eq, AsRef, Deref, Serialize, Deserialize)] -pub struct TransactionSigned { - /// Transaction hash - #[serde(skip)] - pub hash: OnceLock, - /// The transaction signature values - pub signature: Signature, - /// Raw transaction info - #[deref] - #[as_ref] - pub transaction: Transaction, -} - -impl Default for TransactionSigned { - fn default() -> Self { - Self { - hash: Default::default(), - signature: Signature::test_signature(), - transaction: Default::default(), - } - } -} - -impl AsRef for TransactionSigned { - fn as_ref(&self) -> &Self { - self - } -} - -impl Hash for TransactionSigned { - fn hash(&self, state: &mut H) { - self.signature.hash(state); - self.transaction.hash(state); - } -} - -impl PartialEq for TransactionSigned { - fn eq(&self, other: &Self) -> bool { - self.signature == other.signature && - self.transaction == other.transaction && - self.tx_hash() == other.tx_hash() - } -} - -impl Typed2718 for TransactionSigned { - fn ty(&self) -> u8 { - self.deref().ty() - } -} - -// === impl TransactionSigned === - -impl TransactionSigned { - /// Creates a new signed transaction from the given parts. - pub fn new(transaction: Transaction, signature: Signature, hash: B256) -> Self { - Self { hash: hash.into(), signature, transaction } - } - - /// 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 } - } - - /// Transaction - pub const fn transaction(&self) -> &Transaction { - &self.transaction - } - - /// Tries to convert a [`TransactionSigned`] into a [`PooledTransaction`]. - /// - /// This function used as a helper to convert from a decoded p2p broadcast message to - /// [`PooledTransaction`]. Since EIP4844 variants are disallowed to be broadcasted on - /// p2p, return an err if `tx` is [`Transaction::Eip4844`]. - pub fn try_into_pooled(self) -> Result { - let hash = self.hash(); - match self { - Self { transaction: Transaction::Legacy(tx), signature, .. } => { - Ok(PooledTransaction::Legacy(Signed::new_unchecked(tx, signature, hash))) - } - Self { transaction: Transaction::Eip2930(tx), signature, .. } => { - Ok(PooledTransaction::Eip2930(Signed::new_unchecked(tx, signature, hash))) - } - Self { transaction: Transaction::Eip1559(tx), signature, .. } => { - Ok(PooledTransaction::Eip1559(Signed::new_unchecked(tx, signature, hash))) - } - Self { transaction: Transaction::Eip7702(tx), signature, .. } => { - Ok(PooledTransaction::Eip7702(Signed::new_unchecked(tx, signature, hash))) - } - // Not supported because missing blob sidecar - tx @ Self { transaction: Transaction::Eip4844(_), .. } => Err(tx), - #[cfg(feature = "optimism")] - // Not supported because deposit transactions are never pooled - tx @ Self { transaction: Transaction::Deposit(_), .. } => Err(tx), - } - } - - /// 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 { - let hash = self.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), - }) - } - - /// Transaction hash. Used to identify transaction. - pub fn hash(&self) -> TxHash { - *self.tx_hash() - } - - /// Returns the [`RecoveredTx`] transaction with the given sender. - #[inline] - pub const fn with_signer(self, signer: Address) -> RecoveredTx { - RecoveredTx::new_unchecked(self, signer) - } - - /// Consumes the type, recover signer and return [`RecoveredTx`] - /// - /// Returns `None` if the transaction's signature is invalid, see also [`Self::recover_signer`]. - pub fn into_ecrecovered(self) -> Option> { - let signer = self.recover_signer()?; - Some(RecoveredTx::new_unchecked(self, signer)) - } - - /// Consumes the type, recover signer and return [`RecoveredTx`] _without - /// ensuring that the signature has a low `s` value_ (EIP-2). - /// - /// Returns `None` if the transaction's signature is invalid, see also - /// [`Self::recover_signer_unchecked`]. - pub fn into_ecrecovered_unchecked(self) -> Option> { - let signer = self.recover_signer_unchecked()?; - Some(RecoveredTx::new_unchecked(self, signer)) - } - - /// Tries to recover signer and return [`RecoveredTx`]. _without ensuring that - /// the signature has a low `s` value_ (EIP-2). - /// - /// Returns `Err(Self)` if the transaction's signature is invalid, see also - /// [`Self::recover_signer_unchecked`]. - pub fn try_into_ecrecovered_unchecked(self) -> Result, Self> { - match self.recover_signer_unchecked() { - None => Err(self), - Some(signer) => Ok(RecoveredTx::new_unchecked(self, signer)), - } - } - - /// Calculate transaction hash, eip2728 transaction does not contain rlp header and start with - /// tx type. - pub fn recalculate_hash(&self) -> B256 { - keccak256(self.encoded_2718()) - } - - /// Splits the transaction into parts. - pub fn into_parts(self) -> (Transaction, Signature, B256) { - let hash = self.hash(); - (self.transaction, self.signature, hash) - } -} - -impl SignedTransaction for TransactionSigned { - fn tx_hash(&self) -> &TxHash { - self.hash.get_or_init(|| self.recalculate_hash()) - } - - fn signature(&self) -> &Signature { - &self.signature - } - - fn recover_signer(&self) -> Option
{ - // Optimism's Deposit transaction does not have a signature. Directly return the - // `from` address. - #[cfg(feature = "optimism")] - if let Transaction::Deposit(TxDeposit { from, .. }) = self.transaction { - return Some(from) - } - let signature_hash = self.signature_hash(); - recover_signer(&self.signature, signature_hash) - } - - fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec) -> Option
{ - // Optimism's Deposit transaction does not have a signature. Directly return the - // `from` address. - #[cfg(feature = "optimism")] - if let Transaction::Deposit(TxDeposit { from, .. }) = self.transaction { - return Some(from) - } - self.encode_for_signing(buf); - let signature_hash = keccak256(buf); - recover_signer_unchecked(&self.signature, signature_hash) - } -} - -impl reth_primitives_traits::FillTxEnv for TransactionSigned { - fn fill_tx_env(&self, tx_env: &mut TxEnv, sender: Address) { - #[cfg(feature = "optimism")] - let envelope = alloy_eips::eip2718::Encodable2718::encoded_2718(self); - - tx_env.caller = sender; - match self.as_ref() { - Transaction::Legacy(tx) => { - tx_env.gas_limit = tx.gas_limit; - tx_env.gas_price = U256::from(tx.gas_price); - tx_env.gas_priority_fee = None; - tx_env.transact_to = tx.to; - tx_env.value = tx.value; - tx_env.data = tx.input.clone(); - tx_env.chain_id = tx.chain_id; - tx_env.nonce = Some(tx.nonce); - tx_env.access_list.clear(); - tx_env.blob_hashes.clear(); - tx_env.max_fee_per_blob_gas.take(); - tx_env.authorization_list = None; - } - Transaction::Eip2930(tx) => { - tx_env.gas_limit = tx.gas_limit; - tx_env.gas_price = U256::from(tx.gas_price); - tx_env.gas_priority_fee = None; - tx_env.transact_to = tx.to; - tx_env.value = tx.value; - tx_env.data = tx.input.clone(); - tx_env.chain_id = Some(tx.chain_id); - tx_env.nonce = Some(tx.nonce); - tx_env.access_list.clone_from(&tx.access_list.0); - tx_env.blob_hashes.clear(); - tx_env.max_fee_per_blob_gas.take(); - tx_env.authorization_list = None; - } - Transaction::Eip1559(tx) => { - tx_env.gas_limit = tx.gas_limit; - tx_env.gas_price = U256::from(tx.max_fee_per_gas); - tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas)); - tx_env.transact_to = tx.to; - tx_env.value = tx.value; - tx_env.data = tx.input.clone(); - tx_env.chain_id = Some(tx.chain_id); - tx_env.nonce = Some(tx.nonce); - tx_env.access_list.clone_from(&tx.access_list.0); - tx_env.blob_hashes.clear(); - tx_env.max_fee_per_blob_gas.take(); - tx_env.authorization_list = None; - } - Transaction::Eip4844(tx) => { - tx_env.gas_limit = tx.gas_limit; - tx_env.gas_price = U256::from(tx.max_fee_per_gas); - tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas)); - tx_env.transact_to = TxKind::Call(tx.to); - tx_env.value = tx.value; - tx_env.data = tx.input.clone(); - tx_env.chain_id = Some(tx.chain_id); - tx_env.nonce = Some(tx.nonce); - tx_env.access_list.clone_from(&tx.access_list.0); - tx_env.blob_hashes.clone_from(&tx.blob_versioned_hashes); - tx_env.max_fee_per_blob_gas = Some(U256::from(tx.max_fee_per_blob_gas)); - tx_env.authorization_list = None; - } - Transaction::Eip7702(tx) => { - tx_env.gas_limit = tx.gas_limit; - tx_env.gas_price = U256::from(tx.max_fee_per_gas); - tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas)); - tx_env.transact_to = tx.to.into(); - tx_env.value = tx.value; - tx_env.data = tx.input.clone(); - tx_env.chain_id = Some(tx.chain_id); - tx_env.nonce = Some(tx.nonce); - tx_env.access_list.clone_from(&tx.access_list.0); - tx_env.blob_hashes.clear(); - tx_env.max_fee_per_blob_gas.take(); - tx_env.authorization_list = - Some(AuthorizationList::Signed(tx.authorization_list.clone())); - } - #[cfg(feature = "optimism")] - Transaction::Deposit(tx) => { - tx_env.access_list.clear(); - tx_env.gas_limit = tx.gas_limit; - tx_env.gas_price = U256::ZERO; - tx_env.gas_priority_fee = None; - tx_env.transact_to = tx.to; - tx_env.value = tx.value; - tx_env.data = tx.input.clone(); - tx_env.chain_id = None; - tx_env.nonce = None; - tx_env.authorization_list = None; - - tx_env.optimism = revm_primitives::OptimismFields { - source_hash: Some(tx.source_hash), - mint: tx.mint, - is_system_transaction: Some(tx.is_system_transaction), - enveloped_tx: Some(envelope.into()), - }; - return; - } - } - - #[cfg(feature = "optimism")] - if !self.is_deposit() { - tx_env.optimism = revm_primitives::OptimismFields { - source_hash: None, - mint: None, - is_system_transaction: Some(false), - enveloped_tx: Some(envelope.into()), - } - } - } -} - -impl InMemorySize for TransactionSigned { - /// Calculate a heuristic for the in-memory size of the [`TransactionSigned`]. - #[inline] - fn size(&self) -> usize { - self.hash().size() + self.transaction.size() + self.signature().size() - } -} - -impl alloy_consensus::Transaction for TransactionSigned { - fn chain_id(&self) -> Option { - self.deref().chain_id() - } - - fn nonce(&self) -> u64 { - self.deref().nonce() - } - - fn gas_limit(&self) -> u64 { - self.deref().gas_limit() - } - - fn gas_price(&self) -> Option { - self.deref().gas_price() - } - - fn max_fee_per_gas(&self) -> u128 { - self.deref().max_fee_per_gas() - } - - fn max_priority_fee_per_gas(&self) -> Option { - self.deref().max_priority_fee_per_gas() - } - - fn max_fee_per_blob_gas(&self) -> Option { - self.deref().max_fee_per_blob_gas() - } - - fn priority_fee_or_price(&self) -> u128 { - self.deref().priority_fee_or_price() - } - - fn effective_gas_price(&self, base_fee: Option) -> u128 { - self.deref().effective_gas_price(base_fee) - } - - fn is_dynamic_fee(&self) -> bool { - self.deref().is_dynamic_fee() - } - - fn kind(&self) -> TxKind { - self.deref().kind() - } - - fn is_create(&self) -> bool { - self.deref().is_create() - } - - fn value(&self) -> U256 { - self.deref().value() - } - - fn input(&self) -> &Bytes { - self.deref().input() - } - - fn access_list(&self) -> Option<&AccessList> { - self.deref().access_list() - } - - fn blob_versioned_hashes(&self) -> Option<&[B256]> { - alloy_consensus::Transaction::blob_versioned_hashes(self.deref()) - } - - fn authorization_list(&self) -> Option<&[SignedAuthorization]> { - self.deref().authorization_list() - } -} - -impl From> for TransactionSigned { - fn from(recovered: RecoveredTx) -> Self { - recovered.into_tx() - } -} - -impl From> for TransactionSigned { - fn from(recovered: RecoveredTx) -> Self { - recovered.into_tx().into() - } -} - -impl TryFrom for PooledTransaction { - type Error = TransactionConversionError; - - fn try_from(tx: TransactionSigned) -> Result { - tx.try_into_pooled().map_err(|_| TransactionConversionError::UnsupportedForP2P) - } -} - -impl From for TransactionSigned { - fn from(tx: PooledTransaction) -> Self { - match tx { - PooledTransaction::Legacy(signed) => signed.into(), - PooledTransaction::Eip2930(signed) => signed.into(), - PooledTransaction::Eip1559(signed) => signed.into(), - PooledTransaction::Eip4844(signed) => signed.into(), - PooledTransaction::Eip7702(signed) => signed.into(), - } - } -} - -impl Encodable for TransactionSigned { - /// This encodes the transaction _with_ the signature, and an rlp header. - /// - /// For legacy transactions, it encodes the transaction data: - /// `rlp(tx-data)` - /// - /// For EIP-2718 typed transactions, it encodes the transaction type followed by the rlp of the - /// transaction: - /// `rlp(tx-type || rlp(tx-data))` - fn encode(&self, out: &mut dyn bytes::BufMut) { - self.network_encode(out); - } - - fn length(&self) -> usize { - let mut payload_length = self.encode_2718_len(); - if !Encodable2718::is_legacy(self) { - payload_length += Header { list: false, payload_length }.length(); - } - - payload_length - } -} - -impl Decodable for TransactionSigned { - /// This `Decodable` implementation only supports decoding rlp encoded transactions as it's used - /// by p2p. - /// - /// The p2p encoding format always includes an RLP header, although the type RLP header depends - /// on whether or not the transaction is a legacy transaction. - /// - /// If the transaction is a legacy transaction, it is just encoded as a RLP list: - /// `rlp(tx-data)`. - /// - /// If the transaction is a typed transaction, it is encoded as a RLP string: - /// `rlp(tx-type || rlp(tx-data))` - /// - /// This can be used for decoding all signed transactions in p2p `BlockBodies` responses. - /// - /// This cannot be used for decoding EIP-4844 transactions in p2p `PooledTransactions`, since - /// the EIP-4844 variant of [`TransactionSigned`] does not include the blob sidecar. - /// - /// For a method suitable for decoding pooled transactions, see [`PooledTransaction`]. - /// - /// CAUTION: Due to a quirk in [`Header::decode`], this method will succeed even if a typed - /// transaction is encoded in this format, and does not start with a RLP header: - /// `tx-type || rlp(tx-data)`. - /// - /// This is because [`Header::decode`] does not advance the buffer, and returns a length-1 - /// string header if the first byte is less than `0xf7`. - fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - Self::network_decode(buf).map_err(Into::into) - } -} - -impl Encodable2718 for TransactionSigned { - fn type_flag(&self) -> Option { - match self.transaction.tx_type() { - TxType::Legacy => None, - tx_type => Some(tx_type as u8), - } - } - - fn encode_2718_len(&self) -> usize { - match &self.transaction { - Transaction::Legacy(legacy_tx) => legacy_tx.eip2718_encoded_length(&self.signature), - Transaction::Eip2930(access_list_tx) => { - access_list_tx.eip2718_encoded_length(&self.signature) - } - Transaction::Eip1559(dynamic_fee_tx) => { - dynamic_fee_tx.eip2718_encoded_length(&self.signature) - } - Transaction::Eip4844(blob_tx) => blob_tx.eip2718_encoded_length(&self.signature), - Transaction::Eip7702(set_code_tx) => { - set_code_tx.eip2718_encoded_length(&self.signature) - } - #[cfg(feature = "optimism")] - Transaction::Deposit(deposit_tx) => deposit_tx.eip2718_encoded_length(), - } - } - - fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { - self.transaction.eip2718_encode(&self.signature, out) - } - - fn trie_hash(&self) -> B256 { - self.hash() - } -} - -impl Decodable2718 for TransactionSigned { - fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result { - match ty.try_into().map_err(|_| Eip2718Error::UnexpectedType(ty))? { - TxType::Legacy => Err(Eip2718Error::UnexpectedType(0)), - TxType::Eip2930 => { - let (tx, signature) = TxEip2930::rlp_decode_with_signature(buf)?; - Ok(Self { - transaction: Transaction::Eip2930(tx), - signature, - hash: Default::default(), - }) - } - TxType::Eip1559 => { - let (tx, signature) = TxEip1559::rlp_decode_with_signature(buf)?; - Ok(Self { - transaction: Transaction::Eip1559(tx), - signature, - hash: Default::default(), - }) - } - TxType::Eip7702 => { - let (tx, signature) = TxEip7702::rlp_decode_with_signature(buf)?; - Ok(Self { - transaction: Transaction::Eip7702(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(), - }) - } - #[cfg(feature = "optimism")] - TxType::Deposit => Ok(Self::new_unhashed( - Transaction::Deposit(TxDeposit::rlp_decode(buf)?), - TxDeposit::signature(), - )), - } - } - - fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result { - let (tx, signature) = TxLegacy::rlp_decode_with_signature(buf)?; - Ok(Self { transaction: Transaction::Legacy(tx), signature, hash: Default::default() }) - } -} - -#[cfg(any(test, feature = "reth-codec"))] -impl reth_codecs::Compact for TransactionSigned { - fn to_compact(&self, buf: &mut B) -> usize - where - B: bytes::BufMut + AsMut<[u8]>, - { - 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); - if cfg!(feature = "std") { - reth_zstd_compressors::TRANSACTION_COMPRESSOR.with(|compressor| { - let mut compressor = compressor.borrow_mut(); - let tx_bits = self.transaction.to_compact(&mut tmp); - buf.put_slice(&compressor.compress(&tmp).expect("Failed to compress")); - tx_bits as u8 - }) - } else { - let mut compressor = reth_zstd_compressors::create_tx_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 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 { - if cfg!(feature = "std") { - reth_zstd_compressors::TRANSACTION_DECOMPRESSOR.with(|decompressor| { - let mut decompressor = decompressor.borrow_mut(); - - // 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 mut decompressor = reth_zstd_compressors::create_tx_decompressor(); - 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) - } -} - -macro_rules! impl_from_signed { - ($($tx:ident),*) => { - $( - impl From> for TransactionSigned { - fn from(value: Signed<$tx>) -> Self { - let(tx,sig,hash) = value.into_parts(); - Self::new(tx.into(), sig, hash) - } - } - )* - }; -} - -impl_from_signed!(TxLegacy, TxEip2930, TxEip1559, TxEip7702, TxEip4844, TypedTransaction); - -impl From> for TransactionSigned { - fn from(value: Signed) -> Self { - let (tx, sig, hash) = value.into_parts(); - Self::new(tx, sig, hash) - } -} - -impl From> for TransactionSigned { - fn from(value: Signed) -> Self { - let (tx, sig, hash) = value.into_parts(); - Self::new(tx.tx.into(), sig, hash) - } -} - -impl From> for TransactionSigned { - fn from(value: Signed) -> Self { - let (tx, sig, hash) = value.into_parts(); - Self::new(tx.into(), sig, hash) - } -} - -impl From 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 for Signed { - 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 { - #[allow(unused_mut)] - let mut transaction = Transaction::arbitrary(u)?; - - let secp = secp256k1::Secp256k1::new(); - let key_pair = secp256k1::Keypair::new(&secp, &mut rand::thread_rng()); - let signature = reth_primitives_traits::crypto::secp256k1::sign_message( - B256::from_slice(&key_pair.secret_bytes()[..]), - transaction.signature_hash(), - ) - .unwrap(); - - #[cfg(feature = "optimism")] - // Both `Some(0)` and `None` values are encoded as empty string byte. This introduces - // ambiguity in roundtrip tests. Patch the mint value of deposit transaction here, so that - // it's `None` if zero. - if let Transaction::Deposit(ref mut tx_deposit) = transaction { - if tx_deposit.mint == Some(0) { - tx_deposit.mint = None; - } - } - - #[cfg(feature = "optimism")] - let signature = if transaction.is_deposit() { TxDeposit::signature() } else { signature }; - Ok(Self::new_unhashed(transaction, signature)) - } -} +pub use reth_ethereum_primitives::{Transaction, TransactionSigned}; /// Type alias kept for backward compatibility. pub type TransactionSignedEcRecovered = RecoveredTx; -/// Bincode-compatible transaction type serde implementations. -#[cfg(feature = "serde-bincode-compat")] -pub mod serde_bincode_compat { - use alloc::borrow::Cow; - use alloy_consensus::{ - transaction::serde_bincode_compat::{TxEip1559, TxEip2930, TxEip7702, TxLegacy}, - TxEip4844, - }; - use alloy_primitives::{PrimitiveSignature as Signature, TxHash}; - use reth_primitives_traits::serde_bincode_compat::SerdeBincodeCompat; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use serde_with::{DeserializeAs, SerializeAs}; - - /// Bincode-compatible [`super::Transaction`] serde implementation. - /// - /// Intended to use with the [`serde_with::serde_as`] macro in the following way: - /// ```rust - /// use reth_primitives::{serde_bincode_compat, Transaction}; - /// use serde::{Deserialize, Serialize}; - /// use serde_with::serde_as; - /// - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct Data { - /// #[serde_as(as = "serde_bincode_compat::transaction::Transaction")] - /// transaction: Transaction, - /// } - /// ``` - #[derive(Debug, Serialize, Deserialize)] - #[allow(missing_docs)] - pub enum Transaction<'a> { - Legacy(TxLegacy<'a>), - Eip2930(TxEip2930<'a>), - Eip1559(TxEip1559<'a>), - Eip4844(Cow<'a, TxEip4844>), - Eip7702(TxEip7702<'a>), - #[cfg(feature = "optimism")] - Deposit(op_alloy_consensus::serde_bincode_compat::TxDeposit<'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)), - #[cfg(feature = "optimism")] - super::Transaction::Deposit(tx) => { - Self::Deposit(op_alloy_consensus::serde_bincode_compat::TxDeposit::from(tx)) - } - } - } - } - - impl<'a> From> 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()), - #[cfg(feature = "optimism")] - Transaction::Deposit(tx) => Self::Deposit(tx.into()), - } - } - } - - impl SerializeAs for Transaction<'_> { - fn serialize_as(source: &super::Transaction, serializer: S) -> Result - where - S: Serializer, - { - Transaction::from(source).serialize(serializer) - } - } - - impl<'de> DeserializeAs<'de, super::Transaction> for Transaction<'de> { - fn deserialize_as(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Transaction::deserialize(deserializer).map(Into::into) - } - } - - /// Bincode-compatible [`super::TransactionSigned`] serde implementation. - /// - /// Intended to use with the [`serde_with::serde_as`] macro in the following way: - /// ```rust - /// use reth_primitives::{serde_bincode_compat, TransactionSigned}; - /// use serde::{Deserialize, Serialize}; - /// use serde_with::serde_as; - /// - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct Data { - /// #[serde_as(as = "serde_bincode_compat::transaction::TransactionSigned")] - /// transaction: TransactionSigned, - /// } - /// ``` - #[derive(Debug, Serialize, 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.hash(), - signature: value.signature, - transaction: Transaction::from(&value.transaction), - } - } - } - - impl<'a> From> for super::TransactionSigned { - fn from(value: TransactionSigned<'a>) -> Self { - Self { - hash: value.hash.into(), - signature: value.signature, - transaction: value.transaction.into(), - } - } - } - - impl SerializeAs for TransactionSigned<'_> { - fn serialize_as( - source: &super::TransactionSigned, - serializer: S, - ) -> Result - where - S: Serializer, - { - TransactionSigned::from(source).serialize(serializer) - } - } - - impl<'de> DeserializeAs<'de, super::TransactionSigned> for TransactionSigned<'de> { - fn deserialize_as(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - TransactionSigned::deserialize(deserializer).map(Into::into) - } - } - - impl SerdeBincodeCompat for super::TransactionSigned { - type BincodeRepr<'a> = TransactionSigned<'a>; - } - - #[cfg(test)] - mod tests { - use super::super::{serde_bincode_compat, Transaction, TransactionSigned}; - use arbitrary::Arbitrary; - use rand::Rng; - use reth_testing_utils::generators; - use serde::{Deserialize, Serialize}; - use serde_with::serde_as; - - #[test] - fn test_transaction_bincode_roundtrip() { - #[serde_as] - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - struct Data { - #[serde_as(as = "serde_bincode_compat::Transaction")] - transaction: Transaction, - } - - let mut bytes = [0u8; 1024]; - generators::rng().fill(bytes.as_mut_slice()); - let data = Data { - transaction: Transaction::arbitrary(&mut arbitrary::Unstructured::new(&bytes)) - .unwrap(), - }; - - let encoded = bincode::serialize(&data).unwrap(); - let decoded: Data = bincode::deserialize(&encoded).unwrap(); - assert_eq!(decoded, data); - } - - #[test] - fn test_transaction_signed_bincode_roundtrip() { - #[serde_as] - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - struct Data { - #[serde_as(as = "serde_bincode_compat::TransactionSigned")] - transaction: TransactionSigned, - } - - let mut bytes = [0u8; 1024]; - generators::rng().fill(bytes.as_mut_slice()); - let data = Data { - transaction: TransactionSigned::arbitrary(&mut arbitrary::Unstructured::new( - &bytes, - )) - .unwrap(), - }; - - let encoded = bincode::serialize(&data).unwrap(); - let decoded: Data = bincode::deserialize(&encoded).unwrap(); - assert_eq!(decoded, data); - } - } -} - #[cfg(test)] mod tests { - use crate::{ - transaction::{TxEip1559, TxKind, TxLegacy}, - Transaction, TransactionSigned, - }; - use alloy_consensus::Transaction as _; + use crate::{Transaction, TransactionSigned}; + use alloy_consensus::{Transaction as _, TxEip1559, TxLegacy}; use alloy_eips::eip2718::{Decodable2718, Encodable2718}; use alloy_primitives::{ - address, b256, bytes, hex, Address, Bytes, PrimitiveSignature as Signature, B256, U256, + address, b256, bytes, hex, Address, Bytes, PrimitiveSignature as Signature, TxKind, B256, + U256, }; use alloy_rlp::{Decodable, Encodable, Error as RlpError}; use reth_chainspec::MIN_TRANSACTION_GAS; @@ -1782,7 +97,7 @@ mod tests { // 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!(decoded.is_eip4844()); + assert!(alloy_consensus::Typed2718::is_eip4844(&decoded)); let from = decoded.recover_signer(); assert_eq!(from, Some(address!("A83C816D4f9b2783761a22BA6FADB0eB0606D7B2"))); @@ -1926,7 +241,7 @@ mod tests { ) { let expected = TransactionSigned::new_unhashed(transaction, signature); if let Some(hash) = hash { - assert_eq!(hash, expected.hash()); + assert_eq!(hash, *expected.tx_hash()); } assert_eq!(bytes.len(), expected.length()); @@ -1947,7 +262,7 @@ mod tests { let mut pointer = raw.as_ref(); let tx = TransactionSigned::decode(&mut pointer).unwrap(); - assert_eq!(tx.hash(), hash, "Expected same hash"); + assert_eq!(*tx.tx_hash(), hash, "Expected same hash"); assert_eq!(tx.recover_signer(), Some(signer), "Recovering signer should pass."); } diff --git a/crates/primitives/src/transaction/pooled.rs b/crates/primitives/src/transaction/pooled.rs index 183d0af07..4e483f1d0 100644 --- a/crates/primitives/src/transaction/pooled.rs +++ b/crates/primitives/src/transaction/pooled.rs @@ -12,10 +12,9 @@ mod tests { use super::*; use alloy_consensus::{transaction::RlpEcdsaTx, Transaction as _, TxLegacy}; use alloy_eips::eip2718::Decodable2718; - use alloy_primitives::{address, hex}; + use alloy_primitives::{address, hex, Bytes}; use alloy_rlp::Decodable; use assert_matches::assert_matches; - use bytes::Bytes; #[test] fn invalid_legacy_pooled_decoding_input_too_short() { diff --git a/crates/primitives/src/transaction/tx_type.rs b/crates/primitives/src/transaction/tx_type.rs index cbd7f1c1e..d8e23f979 100644 --- a/crates/primitives/src/transaction/tx_type.rs +++ b/crates/primitives/src/transaction/tx_type.rs @@ -1,16 +1,3 @@ -use alloy_consensus::{ - constants::{ - EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, - LEGACY_TX_TYPE_ID, - }, - Typed2718, -}; -use alloy_primitives::{U64, U8}; -use alloy_rlp::{Decodable, Encodable}; -use derive_more::Display; -use reth_primitives_traits::InMemorySize; -use serde::{Deserialize, Serialize}; - /// Transaction Type /// /// Currently being used as 2-bit type when encoding it to `reth_codecs::Compact` on @@ -18,292 +5,4 @@ use serde::{Deserialize, Serialize}; /// database format. /// /// Other required changes when adding a new type can be seen on [PR#3953](https://github.com/paradigmxyz/reth/pull/3953/files). -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Default, - Serialize, - Deserialize, - Hash, - Display, -)] -#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))] -#[display("tx type: {_variant}")] -pub enum TxType { - /// Legacy transaction pre EIP-2929 - #[default] - #[display("legacy (0)")] - Legacy = 0_isize, - /// AccessList transaction - #[display("eip2930 (1)")] - Eip2930 = 1_isize, - /// Transaction with Priority fee - #[display("eip1559 (2)")] - Eip1559 = 2_isize, - /// Shard Blob Transactions - EIP-4844 - #[display("eip4844 (3)")] - Eip4844 = 3_isize, - /// EOA Contract Code Transactions - EIP-7702 - #[display("eip7702 (4)")] - Eip7702 = 4_isize, - /// Optimism Deposit transaction. - #[cfg(feature = "optimism")] - #[display("deposit (126)")] - Deposit = 126_isize, -} - -impl TxType { - /// The max type reserved by an EIP. - pub const MAX_RESERVED_EIP: Self = Self::Eip7702; - - /// Check if the transaction type has an access list. - pub const fn has_access_list(&self) -> bool { - match self { - Self::Legacy => false, - Self::Eip2930 | Self::Eip1559 | Self::Eip4844 | Self::Eip7702 => true, - #[cfg(feature = "optimism")] - Self::Deposit => false, - } - } -} - -impl Typed2718 for TxType { - fn ty(&self) -> u8 { - (*self).into() - } -} - -impl InMemorySize for TxType { - /// Calculates a heuristic for the in-memory size of the [`TxType`]. - #[inline] - fn size(&self) -> usize { - core::mem::size_of::() - } -} - -impl From for u8 { - fn from(value: TxType) -> Self { - match value { - TxType::Legacy => LEGACY_TX_TYPE_ID, - TxType::Eip2930 => EIP2930_TX_TYPE_ID, - TxType::Eip1559 => EIP1559_TX_TYPE_ID, - TxType::Eip4844 => EIP4844_TX_TYPE_ID, - TxType::Eip7702 => EIP7702_TX_TYPE_ID, - #[cfg(feature = "optimism")] - TxType::Deposit => op_alloy_consensus::DEPOSIT_TX_TYPE_ID, - } - } -} - -impl From for U8 { - fn from(value: TxType) -> Self { - Self::from(u8::from(value)) - } -} - -impl TryFrom for TxType { - type Error = &'static str; - - fn try_from(value: u8) -> Result { - #[cfg(feature = "optimism")] - if value == Self::Deposit { - return Ok(Self::Deposit) - } - - if value == Self::Legacy { - return Ok(Self::Legacy) - } else if value == Self::Eip2930 { - return Ok(Self::Eip2930) - } else if value == Self::Eip1559 { - return Ok(Self::Eip1559) - } else if value == Self::Eip4844 { - return Ok(Self::Eip4844) - } else if value == Self::Eip7702 { - return Ok(Self::Eip7702) - } - - Err("invalid tx type") - } -} - -impl TryFrom for TxType { - type Error = &'static str; - - fn try_from(value: u64) -> Result { - let value: u8 = value.try_into().map_err(|_| "invalid tx type")?; - Self::try_from(value) - } -} - -impl TryFrom for TxType { - type Error = &'static str; - - fn try_from(value: U64) -> Result { - value.to::().try_into() - } -} - -#[cfg(any(test, feature = "reth-codec"))] -impl reth_codecs::Compact for TxType { - fn to_compact(&self, buf: &mut B) -> usize - where - B: bytes::BufMut + AsMut<[u8]>, - { - use reth_codecs::txtype::*; - - match self { - Self::Legacy => COMPACT_IDENTIFIER_LEGACY, - Self::Eip2930 => COMPACT_IDENTIFIER_EIP2930, - Self::Eip1559 => COMPACT_IDENTIFIER_EIP1559, - Self::Eip4844 => { - buf.put_u8(EIP4844_TX_TYPE_ID); - COMPACT_EXTENDED_IDENTIFIER_FLAG - } - Self::Eip7702 => { - buf.put_u8(EIP7702_TX_TYPE_ID); - COMPACT_EXTENDED_IDENTIFIER_FLAG - } - #[cfg(feature = "optimism")] - Self::Deposit => { - buf.put_u8(op_alloy_consensus::DEPOSIT_TX_TYPE_ID); - COMPACT_EXTENDED_IDENTIFIER_FLAG - } - } - } - - // 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. - fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) { - use bytes::Buf; - ( - match identifier { - reth_codecs::txtype::COMPACT_IDENTIFIER_LEGACY => Self::Legacy, - reth_codecs::txtype::COMPACT_IDENTIFIER_EIP2930 => Self::Eip2930, - reth_codecs::txtype::COMPACT_IDENTIFIER_EIP1559 => Self::Eip1559, - reth_codecs::txtype::COMPACT_EXTENDED_IDENTIFIER_FLAG => { - let extended_identifier = buf.get_u8(); - match extended_identifier { - EIP4844_TX_TYPE_ID => Self::Eip4844, - EIP7702_TX_TYPE_ID => Self::Eip7702, - #[cfg(feature = "optimism")] - op_alloy_consensus::DEPOSIT_TX_TYPE_ID => Self::Deposit, - _ => panic!("Unsupported TxType identifier: {extended_identifier}"), - } - } - _ => panic!("Unknown identifier for TxType: {identifier}"), - }, - buf, - ) - } -} - -impl PartialEq for TxType { - fn eq(&self, other: &u8) -> bool { - *self as u8 == *other - } -} - -impl PartialEq for u8 { - fn eq(&self, other: &TxType) -> bool { - *self == *other as Self - } -} - -impl Encodable for TxType { - fn encode(&self, out: &mut dyn bytes::BufMut) { - (*self as u8).encode(out); - } - - fn length(&self) -> usize { - 1 - } -} - -impl Decodable for TxType { - fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - let ty = u8::decode(buf)?; - - Self::try_from(ty).map_err(alloy_rlp::Error::Custom) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_primitives::hex; - use reth_codecs::{txtype::*, Compact}; - use rstest::rstest; - - #[rstest] - #[case(U64::from(LEGACY_TX_TYPE_ID), Ok(TxType::Legacy))] - #[case(U64::from(EIP2930_TX_TYPE_ID), Ok(TxType::Eip2930))] - #[case(U64::from(EIP1559_TX_TYPE_ID), Ok(TxType::Eip1559))] - #[case(U64::from(EIP4844_TX_TYPE_ID), Ok(TxType::Eip4844))] - #[case(U64::from(EIP7702_TX_TYPE_ID), Ok(TxType::Eip7702))] - #[cfg_attr( - feature = "optimism", - case(U64::from(op_alloy_consensus::DEPOSIT_TX_TYPE_ID), Ok(TxType::Deposit)) - )] - #[case(U64::MAX, Err("invalid tx type"))] - fn test_u64_to_tx_type(#[case] input: U64, #[case] expected: Result) { - let tx_type_result = TxType::try_from(input); - assert_eq!(tx_type_result, expected); - } - - #[rstest] - #[case(TxType::Legacy, COMPACT_IDENTIFIER_LEGACY, vec![])] - #[case(TxType::Eip2930, COMPACT_IDENTIFIER_EIP2930, vec![])] - #[case(TxType::Eip1559, COMPACT_IDENTIFIER_EIP1559, vec![])] - #[case(TxType::Eip4844, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![EIP4844_TX_TYPE_ID])] - #[case(TxType::Eip7702, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![EIP7702_TX_TYPE_ID])] - #[cfg_attr(feature = "optimism", case(TxType::Deposit, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![op_alloy_consensus::DEPOSIT_TX_TYPE_ID]))] - fn test_txtype_to_compact( - #[case] tx_type: TxType, - #[case] expected_identifier: usize, - #[case] expected_buf: Vec, - ) { - let mut buf = vec![]; - let identifier = tx_type.to_compact(&mut buf); - - assert_eq!(identifier, expected_identifier, "Unexpected identifier for TxType {tx_type:?}",); - assert_eq!(buf, expected_buf, "Unexpected buffer for TxType {tx_type:?}",); - } - - #[rstest] - #[case(TxType::Legacy, COMPACT_IDENTIFIER_LEGACY, vec![])] - #[case(TxType::Eip2930, COMPACT_IDENTIFIER_EIP2930, vec![])] - #[case(TxType::Eip1559, COMPACT_IDENTIFIER_EIP1559, vec![])] - #[case(TxType::Eip4844, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![EIP4844_TX_TYPE_ID])] - #[case(TxType::Eip7702, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![EIP7702_TX_TYPE_ID])] - #[cfg_attr(feature = "optimism", case(TxType::Deposit, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![op_alloy_consensus::DEPOSIT_TX_TYPE_ID]))] - fn test_txtype_from_compact( - #[case] expected_type: TxType, - #[case] identifier: usize, - #[case] buf: Vec, - ) { - let (actual_type, remaining_buf) = TxType::from_compact(&buf, identifier); - - assert_eq!(actual_type, expected_type, "Unexpected TxType for identifier {identifier}"); - assert!(remaining_buf.is_empty(), "Buffer not fully consumed for identifier {identifier}"); - } - - #[rstest] - #[case(&hex!("80"), Ok(TxType::Legacy))] - #[case(&[EIP2930_TX_TYPE_ID], Ok(TxType::Eip2930))] - #[case(&[EIP1559_TX_TYPE_ID], Ok(TxType::Eip1559))] - #[case(&[EIP4844_TX_TYPE_ID], Ok(TxType::Eip4844))] - #[case(&[EIP7702_TX_TYPE_ID], Ok(TxType::Eip7702))] - #[case(&[u8::MAX], Err(alloy_rlp::Error::InputTooShort))] - #[cfg_attr(feature = "optimism", case(&[op_alloy_consensus::DEPOSIT_TX_TYPE_ID], Ok(TxType::Deposit)))] - fn decode_tx_type(#[case] input: &[u8], #[case] expected: Result) { - let tx_type_result = TxType::decode(&mut &input[..]); - assert_eq!(tx_type_result, expected) - } -} +pub use alloy_consensus::TxType; diff --git a/crates/prune/prune/src/segments/user/transaction_lookup.rs b/crates/prune/prune/src/segments/user/transaction_lookup.rs index 2629c217f..73ba14236 100644 --- a/crates/prune/prune/src/segments/user/transaction_lookup.rs +++ b/crates/prune/prune/src/segments/user/transaction_lookup.rs @@ -117,6 +117,7 @@ mod tests { Itertools, }; use reth_db::tables; + use reth_primitives_traits::SignedTransaction; use reth_provider::{DatabaseProviderFactory, PruneCheckpointReader}; use reth_prune_types::{ PruneCheckpoint, PruneInterruptReason, PruneMode, PruneProgress, PruneSegment, @@ -141,7 +142,7 @@ mod tests { for block in &blocks { tx_hash_numbers.reserve_exact(block.transaction_count()); for transaction in &block.body().transactions { - tx_hash_numbers.push((transaction.hash(), tx_hash_numbers.len() as u64)); + tx_hash_numbers.push((*transaction.tx_hash(), tx_hash_numbers.len() as u64)); } } let tx_hash_numbers_len = tx_hash_numbers.len(); diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index efcb040fb..59637ce81 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -37,7 +37,7 @@ use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult}; use reth_tasks::pool::BlockingTaskGuard; use revm::{ db::{CacheDB, State}, - primitives::{db::DatabaseCommit, Env}, + primitives::db::DatabaseCommit, }; use revm_inspectors::tracing::{ FourByteInspector, MuxInspector, TracingInspector, TracingInspectorConfig, TransactionContext, @@ -450,7 +450,7 @@ where tx_env.clone(), &mut inspector, )?; - let env = Env::boxed( + let env = revm_primitives::Env::boxed( evm_env.cfg_env_with_handler_cfg.cfg_env, evm_env.block_env, tx_env, @@ -785,7 +785,7 @@ where self.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?; let state = res.state.clone(); - let env = Env::boxed( + let env = revm_primitives::Env::boxed( evm_env.cfg_env_with_handler_cfg.cfg_env, evm_env.block_env, tx_env, diff --git a/crates/rpc/rpc/src/eth/bundle.rs b/crates/rpc/rpc/src/eth/bundle.rs index e44c5634d..be431f603 100644 --- a/crates/rpc/rpc/src/eth/bundle.rs +++ b/crates/rpc/rpc/src/eth/bundle.rs @@ -1,6 +1,6 @@ //! `Eth` bundle implementation and helpers. -use alloy_consensus::{BlockHeader, Transaction as _}; +use alloy_consensus::{BlockHeader, EnvKzgSettings, Transaction as _}; use alloy_eips::eip4844::MAX_DATA_GAS_PER_BLOCK; use alloy_primitives::{Keccak256, U256}; use alloy_rpc_types_mev::{EthCallBundle, EthCallBundleResponse, EthCallBundleTransactionResult}; @@ -23,7 +23,7 @@ use revm::{ db::{CacheDB, DatabaseCommit, DatabaseRef}, primitives::ResultAndState, }; -use revm_primitives::{EnvKzgSettings, SpecId}; +use revm_primitives::SpecId; use std::sync::Arc; /// `Eth` bundle implementation. diff --git a/crates/rpc/rpc/src/eth/helpers/pending_block.rs b/crates/rpc/rpc/src/eth/helpers/pending_block.rs index c7d77d4a9..b40fff97f 100644 --- a/crates/rpc/rpc/src/eth/helpers/pending_block.rs +++ b/crates/rpc/rpc/src/eth/helpers/pending_block.rs @@ -1,6 +1,6 @@ //! Support for building a pending block with transactions from local view of mempool. -use alloy_consensus::{constants::EMPTY_WITHDRAWALS, Header, EMPTY_OMMER_ROOT_HASH}; +use alloy_consensus::{constants::EMPTY_WITHDRAWALS, Header, Transaction, EMPTY_OMMER_ROOT_HASH}; use alloy_eips::{eip7685::EMPTY_REQUESTS_HASH, merge::BEACON_NONCE}; use alloy_primitives::U256; use reth_chainspec::{EthChainSpec, EthereumHardforks}; diff --git a/crates/rpc/rpc/src/eth/helpers/types.rs b/crates/rpc/rpc/src/eth/helpers/types.rs index 48a6d0d8a..4678a3bdc 100644 --- a/crates/rpc/rpc/src/eth/helpers/types.rs +++ b/crates/rpc/rpc/src/eth/helpers/types.rs @@ -6,6 +6,7 @@ use alloy_primitives::PrimitiveSignature as Signature; use alloy_rpc_types::TransactionRequest; use alloy_rpc_types_eth::{Transaction, TransactionInfo}; use reth_primitives::{RecoveredTx, TransactionSigned}; +use reth_primitives_traits::SignedTransaction; use reth_rpc_eth_api::EthApiTypes; use reth_rpc_eth_types::EthApiError; use reth_rpc_types_compat::TransactionCompat; @@ -43,7 +44,7 @@ where tx_info: TransactionInfo, ) -> Result { let from = tx.signer(); - let hash = tx.hash(); + let hash = *tx.tx_hash(); let TransactionSigned { transaction, signature, .. } = tx.into_tx(); let inner: TxEnvelope = match transaction { diff --git a/crates/stages/stages/src/stages/hashing_storage.rs b/crates/stages/stages/src/stages/hashing_storage.rs index c9b959e25..7967aa854 100644 --- a/crates/stages/stages/src/stages/hashing_storage.rs +++ b/crates/stages/stages/src/stages/hashing_storage.rs @@ -220,6 +220,7 @@ mod tests { models::StoredBlockBodyIndices, }; use reth_primitives::SealedBlock; + use reth_primitives_traits::SignedTransaction; use reth_provider::providers::StaticFileWriter; use reth_testing_utils::generators::{ self, random_block_range, random_contract_account_range, BlockRangeParams, @@ -356,7 +357,7 @@ mod tests { progress.body().transactions.iter().try_for_each( |transaction| -> Result<(), reth_db::DatabaseError> { tx.put::( - transaction.hash(), + *transaction.tx_hash(), next_tx_num, )?; tx.put::(next_tx_num, transaction.clone())?; diff --git a/crates/stages/stages/src/stages/tx_lookup.rs b/crates/stages/stages/src/stages/tx_lookup.rs index 4e3f4a877..872af3baf 100644 --- a/crates/stages/stages/src/stages/tx_lookup.rs +++ b/crates/stages/stages/src/stages/tx_lookup.rs @@ -387,7 +387,7 @@ mod tests { for block in &blocks[..=max_processed_block] { for transaction in &block.body().transactions { if block.number > max_pruned_block { - tx_hash_numbers.push((transaction.hash(), tx_hash_number)); + tx_hash_numbers.push((*transaction.tx_hash(), tx_hash_number)); } tx_hash_number += 1; } @@ -552,7 +552,10 @@ mod tests { for tx_id in body.tx_num_range() { let transaction = provider.transaction_by_id(tx_id)?.expect("no transaction entry"); - assert_eq!(Some(tx_id), provider.transaction_id(transaction.hash())?); + assert_eq!( + Some(tx_id), + provider.transaction_id(*transaction.tx_hash())? + ); } } } diff --git a/crates/storage/codecs/src/alloy/transaction/optimism.rs b/crates/storage/codecs/src/alloy/transaction/optimism.rs index 1bef8c565..7b4cf5705 100644 --- a/crates/storage/codecs/src/alloy/transaction/optimism.rs +++ b/crates/storage/codecs/src/alloy/transaction/optimism.rs @@ -107,7 +107,7 @@ impl crate::Compact for OpTxType { match extended_identifier { EIP7702_TX_TYPE_ID => Self::Eip7702, op_alloy_consensus::DEPOSIT_TX_TYPE_ID => Self::Deposit, - _ => panic!("Unsupported TxType identifier: {extended_identifier}"), + _ => panic!("Unsupported OpTxType identifier: {extended_identifier}"), } } _ => panic!("Unknown identifier for TxType: {identifier}"), diff --git a/crates/storage/db-api/Cargo.toml b/crates/storage/db-api/Cargo.toml index 671b67d6e..b15b9473f 100644 --- a/crates/storage/db-api/Cargo.toml +++ b/crates/storage/db-api/Cargo.toml @@ -87,7 +87,6 @@ arbitrary = [ "reth-optimism-primitives?/arbitrary" ] optimism = [ - "reth-primitives/optimism", "reth-codecs/op", "reth-optimism-primitives?/optimism", "op", diff --git a/crates/storage/db/Cargo.toml b/crates/storage/db/Cargo.toml index b3d4610f2..3dc1bb138 100644 --- a/crates/storage/db/Cargo.toml +++ b/crates/storage/db/Cargo.toml @@ -112,7 +112,7 @@ arbitrary = [ "reth-stages-types/arbitrary", "alloy-consensus/arbitrary", ] -optimism = ["reth-primitives/optimism", "reth-db-api/optimism"] +optimism = ["reth-db-api/optimism"] op = ["reth-db-api/op"] disable-lock = [] diff --git a/crates/storage/provider/Cargo.toml b/crates/storage/provider/Cargo.toml index 0955821b4..9405ffada 100644 --- a/crates/storage/provider/Cargo.toml +++ b/crates/storage/provider/Cargo.toml @@ -88,7 +88,6 @@ alloy-consensus.workspace = true [features] optimism = [ - "reth-primitives/optimism", "reth-execution-types/optimism", "reth-optimism-primitives", "reth-codecs/op", diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider.rs index 8b8e486e1..75ad402b0 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider.rs @@ -2402,7 +2402,7 @@ mod tests { let mut in_memory_blocks: std::collections::VecDeque<_> = in_memory_blocks.into(); $( - let tx_hash = |block: &SealedBlock| block.body().transactions[0].hash(); + let tx_hash = |block: &SealedBlock| *block.body().transactions[0].tx_hash(); let tx_num = |block: &SealedBlock| { database_blocks .iter() @@ -2726,7 +2726,7 @@ mod tests { // above, we do not see it. assert!(matches!( old_transaction_hash_fn( - to_be_persisted_tx.hash(), + *to_be_persisted_tx.tx_hash(), provider.canonical_in_memory_state(), provider.database.clone() ), @@ -2743,7 +2743,7 @@ mod tests { assert!(matches!( correct_transaction_hash_fn( - to_be_persisted_tx.hash(), + *to_be_persisted_tx.tx_hash(), provider.canonical_in_memory_state(), provider.database ), diff --git a/crates/storage/provider/src/providers/database/mod.rs b/crates/storage/provider/src/providers/database/mod.rs index 4c781b304..ab44da3ae 100644 --- a/crates/storage/provider/src/providers/database/mod.rs +++ b/crates/storage/provider/src/providers/database/mod.rs @@ -708,7 +708,7 @@ mod tests { if sender == block.body().transactions[0].recover_signer().unwrap() ); assert_matches!( - provider.transaction_id(block.body().transactions[0].hash()), + provider.transaction_id(*block.body().transactions[0].tx_hash()), Ok(Some(0)) ); } @@ -726,7 +726,10 @@ mod tests { Ok(_) ); assert_matches!(provider.transaction_sender(0), Ok(None)); - assert_matches!(provider.transaction_id(block.body().transactions[0].hash()), Ok(None)); + assert_matches!( + provider.transaction_id(*block.body().transactions[0].tx_hash()), + Ok(None) + ); } } diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index 8c3b6422e..113c2c509 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -253,7 +253,7 @@ impl TransactionsProvider for MockEthProvider { let tx_number = lock .values() .flat_map(|block| &block.body.transactions) - .position(|tx| tx.hash() == tx_hash) + .position(|tx| *tx.tx_hash() == tx_hash) .map(|pos| pos as TxNumber); Ok(tx_number) @@ -280,7 +280,7 @@ impl TransactionsProvider for MockEthProvider { fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult> { Ok(self.blocks.lock().iter().find_map(|(_, block)| { - block.body.transactions.iter().find(|tx| tx.hash() == hash).cloned() + block.body.transactions.iter().find(|tx| *tx.tx_hash() == hash).cloned() })) } @@ -291,7 +291,7 @@ impl TransactionsProvider for MockEthProvider { let lock = self.blocks.lock(); for (block_hash, block) in lock.iter() { for (index, tx) in block.body.transactions.iter().enumerate() { - if tx.hash() == hash { + if *tx.tx_hash() == hash { let meta = TransactionMeta { tx_hash: hash, index: index as u64, diff --git a/crates/transaction-pool/src/test_utils/gen.rs b/crates/transaction-pool/src/test_utils/gen.rs index 3110997d6..feb50050c 100644 --- a/crates/transaction-pool/src/test_utils/gen.rs +++ b/crates/transaction-pool/src/test_utils/gen.rs @@ -1,10 +1,12 @@ use crate::EthPooledTransaction; -use alloy_consensus::{TxEip1559, TxEip4844, TxLegacy}; +use alloy_consensus::{SignableTransaction, TxEip1559, TxEip4844, TxLegacy}; use alloy_eips::{eip1559::MIN_PROTOCOL_BASE_FEE, eip2718::Encodable2718, eip2930::AccessList}; use alloy_primitives::{Address, Bytes, TxKind, B256, U256}; use rand::Rng; use reth_chainspec::MAINNET; -use reth_primitives::{Transaction, TransactionSigned}; +use reth_primitives::{ + transaction::SignedTransactionIntoRecoveredExt, Transaction, TransactionSigned, +}; use reth_primitives_traits::crypto::secp256k1::sign_message; /// A generator for transactions for testing purposes. @@ -99,12 +101,12 @@ impl TransactionGenerator { /// Generates and returns a pooled EIP-1559 transaction with a random signer. pub fn gen_eip1559_pooled(&mut self) -> EthPooledTransaction { - self.gen_eip1559().into_ecrecovered().unwrap().try_into().unwrap() + self.gen_eip1559().try_into_ecrecovered().unwrap().try_into().unwrap() } /// Generates and returns a pooled EIP-4844 transaction with a random signer. pub fn gen_eip4844_pooled(&mut self) -> EthPooledTransaction { - let tx = self.gen_eip4844().into_ecrecovered().unwrap(); + let tx = self.gen_eip4844().try_into_ecrecovered().unwrap(); let encoded_length = tx.encode_2718_len(); EthPooledTransaction::new(tx, encoded_length) } diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index 98fa167f0..1cd4445e8 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -32,7 +32,7 @@ use reth_primitives::{ transaction::{SignedTransactionIntoRecoveredExt, TryFromRecoveredTransactionError}, PooledTransaction, RecoveredTx, Transaction, TransactionSigned, TxType, }; -use reth_primitives_traits::InMemorySize; +use reth_primitives_traits::{InMemorySize, SignedTransaction}; use std::{ops::Range, sync::Arc, time::Instant, vec::IntoIter}; /// A transaction pool implementation using [`MockOrdering`] for transaction ordering. @@ -909,7 +909,7 @@ impl TryFrom> for MockTransaction { fn try_from(tx: RecoveredTx) -> Result { let sender = tx.signer(); let transaction = tx.into_tx(); - let hash = transaction.hash(); + let hash = *transaction.tx_hash(); let size = transaction.size(); #[allow(unreachable_patterns)] diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index f7e2f310e..38f3c7564 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -7,7 +7,7 @@ use crate::{ }; use alloy_consensus::{ constants::{EIP1559_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID}, - BlockHeader, Transaction as _, Typed2718, + BlockHeader, Signed, Transaction as _, Typed2718, }; use alloy_eips::{ eip2718::Encodable2718, @@ -1249,7 +1249,8 @@ impl From> for EthPooledTransaction { // include the blob sidecar let (tx, sig, hash) = tx.into_parts(); let (tx, blob) = tx.into_parts(); - let tx = TransactionSigned::new(tx.into(), sig, hash); + let tx = Signed::new_unchecked(tx, sig, hash); + let tx = TransactionSigned::from(tx); let tx = RecoveredTx::new_unchecked(tx, signer); let mut pooled = Self::new(tx, encoded_length); pooled.blob_sidecar = EthBlobTransactionSidecar::Present(blob); @@ -1279,9 +1280,8 @@ impl PoolTransaction for EthPooledTransaction { tx: RecoveredTx, ) -> Result, Self::TryFromConsensusError> { let (tx, signer) = tx.into_parts(); - let pooled = tx - .try_into_pooled() - .map_err(|_| TryFromRecoveredTransactionError::BlobSidecarMissing)?; + let pooled = + tx.try_into().map_err(|_| TryFromRecoveredTransactionError::BlobSidecarMissing)?; Ok(RecoveredTx::new_unchecked(pooled, signer)) } diff --git a/examples/beacon-api-sidecar-fetcher/src/mined_sidecar.rs b/examples/beacon-api-sidecar-fetcher/src/mined_sidecar.rs index 32a4e910a..2f5f68218 100644 --- a/examples/beacon-api-sidecar-fetcher/src/mined_sidecar.rs +++ b/examples/beacon-api-sidecar-fetcher/src/mined_sidecar.rs @@ -1,6 +1,7 @@ use crate::BeaconSidecarConfig; use alloy_consensus::{ transaction::PooledTransaction, BlockHeader, Signed, Transaction as _, TxEip4844WithSidecar, + Typed2718, }; use alloy_primitives::B256; use alloy_rpc_types_beacon::sidecar::{BeaconBlobBundle, SidecarIterator}; @@ -8,6 +9,7 @@ use eyre::Result; use futures_util::{stream::FuturesUnordered, Future, Stream, StreamExt}; use reqwest::{Error, StatusCode}; use reth::{ + core::primitives::SignedTransaction, primitives::RecoveredBlock, providers::CanonStateNotification, transaction_pool::{BlobStoreError, TransactionPoolExt}, @@ -112,7 +114,7 @@ where return } - match self.pool.get_all_blobs_exact(txs.iter().map(|(tx, _)| tx.hash()).collect()) { + match self.pool.get_all_blobs_exact(txs.iter().map(|(tx, _)| *tx.tx_hash()).collect()) { Ok(blobs) => { actions_to_queue.reserve_exact(txs.len()); for ((tx, _), sidecar) in txs.iter().zip(blobs.into_iter()) { @@ -199,7 +201,7 @@ where .transactions() .filter(|tx| tx.is_eip4844()) .map(|tx| { - let transaction_hash = tx.hash(); + let transaction_hash = *tx.tx_hash(); let block_metadata = BlockMetadata { block_hash: new.tip().hash(), block_number: new.tip().number(), diff --git a/examples/custom-beacon-withdrawals/Cargo.toml b/examples/custom-beacon-withdrawals/Cargo.toml index c396ca11d..9a8152b49 100644 --- a/examples/custom-beacon-withdrawals/Cargo.toml +++ b/examples/custom-beacon-withdrawals/Cargo.toml @@ -21,6 +21,4 @@ alloy-consensus.workspace = true eyre.workspace = true [features] -optimism = [ - "reth-primitives/optimism" -] \ No newline at end of file +optimism = [] \ No newline at end of file diff --git a/examples/custom-dev-node/src/main.rs b/examples/custom-dev-node/src/main.rs index 7688c1ce2..a1eaeefe5 100644 --- a/examples/custom-dev-node/src/main.rs +++ b/examples/custom-dev-node/src/main.rs @@ -15,7 +15,7 @@ use reth::{ tasks::TaskManager, }; use reth_chainspec::ChainSpec; -use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig}; +use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig, primitives::SignedTransaction}; use reth_node_ethereum::EthereumNode; #[tokio::main] @@ -51,7 +51,7 @@ async fn main() -> eyre::Result<()> { let head = notifications.next().await.unwrap(); let tx = &head.tip().body().transactions().next().unwrap(); - assert_eq!(tx.hash(), hash); + assert_eq!(*tx.tx_hash(), hash); println!("mined transaction: {hash}"); Ok(()) } diff --git a/examples/db-access/Cargo.toml b/examples/db-access/Cargo.toml index ec278ac1c..ff48c75b3 100644 --- a/examples/db-access/Cargo.toml +++ b/examples/db-access/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true reth-chainspec.workspace = true reth-db.workspace = true reth-primitives.workspace = true +reth-primitives-traits.workspace = true reth-provider.workspace = true reth-node-ethereum.workspace = true reth-node-types.workspace = true diff --git a/examples/db-access/src/main.rs b/examples/db-access/src/main.rs index bda1ea26c..0b5ca570a 100644 --- a/examples/db-access/src/main.rs +++ b/examples/db-access/src/main.rs @@ -6,6 +6,7 @@ use reth_db::{open_db_read_only, DatabaseEnv}; use reth_node_ethereum::EthereumNode; use reth_node_types::NodeTypesWithDBAdapter; use reth_primitives::{SealedBlock, SealedHeader, TransactionSigned}; +use reth_primitives_traits::transaction::signed::SignedTransaction; use reth_provider::{ providers::StaticFileProvider, AccountReader, BlockReader, BlockSource, HeaderProvider, ProviderFactory, ReceiptProvider, StateProvider, TransactionsProvider, @@ -95,17 +96,17 @@ fn txs_provider_example // Can query the tx by hash let tx_by_hash = - provider.transaction_by_hash(tx.hash())?.ok_or(eyre::eyre!("txhash not found"))?; + provider.transaction_by_hash(*tx.tx_hash())?.ok_or(eyre::eyre!("txhash not found"))?; assert_eq!(tx, tx_by_hash); // Can query the tx by hash with info about the block it was included in let (tx, meta) = provider - .transaction_by_hash_with_meta(tx.hash())? + .transaction_by_hash_with_meta(*tx.tx_hash())? .ok_or(eyre::eyre!("txhash not found"))?; - assert_eq!(tx.hash(), meta.tx_hash); + assert_eq!(*tx.tx_hash(), meta.tx_hash); // Can reverse lookup the key too - let id = provider.transaction_id(tx.hash())?.ok_or(eyre::eyre!("txhash not found"))?; + let id = provider.transaction_id(*tx.tx_hash())?.ok_or(eyre::eyre!("txhash not found"))?; assert_eq!(id, txid); // Can find the block of a transaction given its key @@ -181,8 +182,9 @@ fn receipts_provider_example< // Can query receipt by txhash too let tx = provider.transaction_by_id(txid)?.unwrap(); - let receipt_by_hash = - provider.receipt_by_hash(tx.hash())?.ok_or(eyre::eyre!("tx receipt by hash not found"))?; + let receipt_by_hash = provider + .receipt_by_hash(*tx.tx_hash())? + .ok_or(eyre::eyre!("tx receipt by hash not found"))?; assert_eq!(receipt, receipt_by_hash); // Can query all the receipts in a block diff --git a/testing/testing-utils/src/generators.rs b/testing/testing-utils/src/generators.rs index 307ef1cda..895155cc7 100644 --- a/testing/testing-utils/src/generators.rs +++ b/testing/testing-utils/src/generators.rs @@ -1,6 +1,6 @@ //! Generators for different data structures like block headers, block bodies and ranges of those. -use alloy_consensus::{Block, Header, Transaction as _, TxLegacy}; +use alloy_consensus::{Block, Header, SignableTransaction, Transaction as _, TxLegacy}; use alloy_eips::{ eip1898::BlockWithParent, eip4895::{Withdrawal, Withdrawals},