From 42afcbd75a5d5a9f8c9f63d4f93df89c0f85a716 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 30 Sep 2024 19:36:38 +0400 Subject: [PATCH] impl `Encodable2718`, `Decodable2718` for `TransactionSigned` (#11218) --- Cargo.lock | 58 +-- bin/reth/Cargo.toml | 1 + .../src/commands/debug_cmd/build_block.rs | 3 +- crates/optimism/evm/Cargo.toml | 2 + crates/optimism/evm/src/l1.rs | 5 +- crates/optimism/node/Cargo.toml | 1 + crates/optimism/node/src/txpool.rs | 6 +- crates/optimism/payload/Cargo.toml | 1 + crates/optimism/payload/src/payload.rs | 12 +- crates/optimism/rpc/Cargo.toml | 1 + crates/optimism/rpc/src/eth/receipt.rs | 11 +- crates/primitives/src/block.rs | 6 +- crates/primitives/src/proofs.rs | 4 +- crates/primitives/src/transaction/compat.rs | 6 +- crates/primitives/src/transaction/mod.rs | 334 +++++------------- crates/primitives/src/transaction/pooled.rs | 30 +- .../primitives/src/transaction/signature.rs | 3 +- crates/rpc/rpc-eth-api/Cargo.toml | 2 +- .../rpc-eth-api/src/helpers/transaction.rs | 5 +- crates/rpc/rpc-types-compat/Cargo.toml | 1 + .../rpc-types-compat/src/engine/payload.rs | 17 +- crates/rpc/rpc/Cargo.toml | 1 + crates/rpc/rpc/src/debug.rs | 3 +- crates/rpc/rpc/src/eth/helpers/signer.rs | 3 +- crates/transaction-pool/src/test_utils/gen.rs | 4 +- crates/transaction-pool/src/traits.rs | 4 +- 26 files changed, 209 insertions(+), 315 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index abe3bb8597d5..0da2c2d5917b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1601,9 +1601,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.22" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0" +checksum = "3bbb537bb4a30b90362caddba8f360c0a56bc13d3a5570028e7197204cb54a17" dependencies = [ "jobserver", "libc", @@ -1858,9 +1858,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.12.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" +checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" dependencies = [ "cfg-if", "cpufeatures", @@ -4460,7 +4460,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", - "redox_syscall 0.5.6", + "redox_syscall 0.5.7", ] [[package]] @@ -5088,9 +5088,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "oorandom" @@ -5329,7 +5332,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.6", + "redox_syscall 0.5.7", "smallvec", "windows-targets 0.52.6", ] @@ -5745,7 +5748,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "rusty-fork", "tempfile", "unarray", @@ -6022,9 +6025,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] @@ -6042,14 +6045,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -6063,13 +6066,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -6080,9 +6083,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "regress" @@ -6154,6 +6157,7 @@ name = "reth" version = "1.0.7" dependencies = [ "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", @@ -7997,6 +8001,7 @@ name = "reth-optimism-evm" version = "1.0.7" dependencies = [ "alloy-consensus", + "alloy-eips", "alloy-genesis", "alloy-primitives", "reth-chainspec", @@ -8031,6 +8036,7 @@ dependencies = [ name = "reth-optimism-node" version = "1.0.7" dependencies = [ + "alloy-eips", "alloy-genesis", "alloy-primitives", "alloy-rpc-types-engine", @@ -8083,6 +8089,7 @@ dependencies = [ name = "reth-optimism-payload-builder" version = "1.0.7" dependencies = [ + "alloy-eips", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-engine", @@ -8124,6 +8131,7 @@ dependencies = [ name = "reth-optimism-rpc" version = "1.0.7" dependencies = [ + "alloy-eips", "alloy-primitives", "alloy-rpc-types", "alloy-rpc-types-eth", @@ -8410,6 +8418,7 @@ version = "1.0.7" dependencies = [ "alloy-consensus", "alloy-dyn-abi", + "alloy-eips", "alloy-genesis", "alloy-network", "alloy-primitives", @@ -8730,6 +8739,7 @@ dependencies = [ name = "reth-rpc-types-compat" version = "1.0.7" dependencies = [ + "alloy-eips", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", @@ -9516,9 +9526,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.17" +version = "2.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c947adb109a8afce5fc9c7bf951f87f146e9147b3a6a58413105628fb1d1e66" +checksum = "215b1103f73e23e9cb6883072c1fb26ae55c09d42054654955c739e5418a7c96" dependencies = [ "sdd", ] @@ -10880,9 +10890,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "uint" diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index e456871facb6..3d3cbd06f4d7 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -66,6 +66,7 @@ reth-engine-util.workspace = true reth-prune.workspace = true # crypto +alloy-eips.workspace = true alloy-rlp.workspace = true alloy-rpc-types = { workspace = true, features = ["engine"] } alloy-consensus.workspace = true diff --git a/bin/reth/src/commands/debug_cmd/build_block.rs b/bin/reth/src/commands/debug_cmd/build_block.rs index 6f97276839b1..a7f75c02a8f8 100644 --- a/bin/reth/src/commands/debug_cmd/build_block.rs +++ b/bin/reth/src/commands/debug_cmd/build_block.rs @@ -1,5 +1,6 @@ //! Command for debugging block building. use alloy_consensus::TxEip4844; +use alloy_eips::eip2718::Encodable2718; use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_rlp::Decodable; use alloy_rpc_types::engine::{BlobsBundleV1, PayloadAttributes}; @@ -200,7 +201,7 @@ impl> Command { encoded_length } - _ => transaction.length_without_header(), + _ => transaction.encode_2718_len(), }; debug!(target: "reth::cli", ?transaction, "Adding transaction to the pool"); diff --git a/crates/optimism/evm/Cargo.toml b/crates/optimism/evm/Cargo.toml index ae0248262338..c0be459167ff 100644 --- a/crates/optimism/evm/Cargo.toml +++ b/crates/optimism/evm/Cargo.toml @@ -38,6 +38,8 @@ thiserror.workspace = true tracing.workspace = true [dev-dependencies] +alloy-eips.workspace = true + reth-revm = { workspace = true, features = ["test-utils"] } reth-optimism-chainspec.workspace = true alloy-genesis.workspace = true diff --git a/crates/optimism/evm/src/l1.rs b/crates/optimism/evm/src/l1.rs index a54bf08ce525..18ccbed9518a 100644 --- a/crates/optimism/evm/src/l1.rs +++ b/crates/optimism/evm/src/l1.rs @@ -299,6 +299,7 @@ where #[cfg(test)] mod tests { + use alloy_eips::eip2718::Decodable2718; use reth_optimism_chainspec::OP_MAINNET; use reth_optimism_forks::OptimismHardforks; use reth_primitives::{BlockBody, TransactionSigned}; @@ -311,7 +312,7 @@ mod tests { use reth_primitives::{Header, TransactionSigned}; let bytes = Bytes::from_static(&hex!("7ef9015aa044bae9d41b8380d781187b426c6fe43df5fb2fb57bd4466ef6a701e1f01e015694deaddeaddeaddeaddeaddeaddeaddeaddead000194420000000000000000000000000000000000001580808408f0d18001b90104015d8eb900000000000000000000000000000000000000000000000000000000008057650000000000000000000000000000000000000000000000000000000063d96d10000000000000000000000000000000000000000000000000000000000009f35273d89754a1e0387b89520d989d3be9c37c1f32495a88faf1ea05c61121ab0d1900000000000000000000000000000000000000000000000000000000000000010000000000000000000000002d679b567db6187c0c8323fa982cfb88b74dbcc7000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240")); - let l1_info_tx = TransactionSigned::decode_enveloped(&mut bytes.as_ref()).unwrap(); + let l1_info_tx = TransactionSigned::decode_2718(&mut bytes.as_ref()).unwrap(); let mock_block = Block { header: Header::default(), body: BlockBody { transactions: vec![l1_info_tx], ..Default::default() }, @@ -339,7 +340,7 @@ mod tests { // https://optimistic.etherscan.io/getRawTx?tx=0x88501da5d5ca990347c2193be90a07037af1e3820bb40774c8154871c7669150 const TX: [u8; 251] = hex!("7ef8f8a0a539eb753df3b13b7e386e147d45822b67cb908c9ddc5618e3dbaa22ed00850b94deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e2000000558000c5fc50000000000000000000000006605a89f00000000012a10d90000000000000000000000000000000000000000000000000000000af39ac3270000000000000000000000000000000000000000000000000000000d5ea528d24e582fa68786f080069bdbfe06a43f8e67bfd31b8e4d8a8837ba41da9a82a54a0000000000000000000000006887246668a3b87f54deb3b94ba47a6f63f32985"); - let tx = TransactionSigned::decode_enveloped(&mut TX.as_slice()).unwrap(); + let tx = TransactionSigned::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 4239f4ad9ce7..fdfb9bf6cee0 100644 --- a/crates/optimism/node/Cargo.toml +++ b/crates/optimism/node/Cargo.toml @@ -43,6 +43,7 @@ reth-optimism-consensus.workspace = true reth-optimism-forks.workspace = true # ethereum +alloy-eips.workspace = true alloy-primitives.workspace = true op-alloy-rpc-types-engine.workspace = true alloy-rpc-types-engine.workspace = true diff --git a/crates/optimism/node/src/txpool.rs b/crates/optimism/node/src/txpool.rs index a27e0d4ecb75..3bd5d6fd38db 100644 --- a/crates/optimism/node/src/txpool.rs +++ b/crates/optimism/node/src/txpool.rs @@ -1,4 +1,5 @@ //! OP transaction pool types +use alloy_eips::eip2718::Encodable2718; use parking_lot::RwLock; use reth_chainspec::ChainSpec; use reth_optimism_evm::RethL1BlockInfo; @@ -139,7 +140,7 @@ where let l1_block_info = self.block_info.l1_block_info.read().clone(); let mut encoded = Vec::with_capacity(valid_tx.transaction().encoded_length()); - valid_tx.transaction().clone().into_consensus().encode_enveloped(&mut encoded); + valid_tx.transaction().clone().into_consensus().encode_2718(&mut encoded); let cost_addition = match l1_block_info.l1_tx_data_fee( &self.chain_spec(), @@ -229,6 +230,7 @@ pub struct OpL1BlockInfo { #[cfg(test)] mod tests { use crate::txpool::OpTransactionValidator; + use alloy_eips::eip2718::Encodable2718; use alloy_primitives::{TxKind, U256}; use reth::primitives::Signature; use reth_chainspec::MAINNET; @@ -266,7 +268,7 @@ mod tests { let signed_tx = TransactionSigned::from_transaction_and_signature(deposit_tx, signature); let signed_recovered = TransactionSignedEcRecovered::from_signed_transaction(signed_tx, signer); - let len = signed_recovered.length_without_header(); + let len = signed_recovered.encode_2718_len(); let pooled_tx = EthPooledTransaction::new(signed_recovered, len); let outcome = validator.validate_one(origin, pooled_tx); diff --git a/crates/optimism/payload/Cargo.toml b/crates/optimism/payload/Cargo.toml index e731b50c0767..117f63201a48 100644 --- a/crates/optimism/payload/Cargo.toml +++ b/crates/optimism/payload/Cargo.toml @@ -35,6 +35,7 @@ reth-optimism-forks.workspace = true # ethereum revm.workspace = true +alloy-eips.workspace = true alloy-primitives.workspace = true alloy-rlp.workspace = true op-alloy-rpc-types-engine.workspace = true diff --git a/crates/optimism/payload/src/payload.rs b/crates/optimism/payload/src/payload.rs index ac73f05390ff..cb3b939136f3 100644 --- a/crates/optimism/payload/src/payload.rs +++ b/crates/optimism/payload/src/payload.rs @@ -2,6 +2,7 @@ //! Optimism builder support +use alloy_eips::eip2718::Decodable2718; use alloy_primitives::{Address, B256, U256}; use alloy_rlp::Encodable; use alloy_rpc_types_engine::{ExecutionPayloadEnvelopeV2, ExecutionPayloadV1, PayloadId}; @@ -53,8 +54,15 @@ impl PayloadBuilderAttributes for OptimismPayloadBuilderAttributes { .unwrap_or_default() .into_iter() .map(|data| { - TransactionSigned::decode_enveloped(&mut data.as_ref()) - .map(|tx| WithEncoded::new(data, tx)) + let mut buf = data.as_ref(); + let tx = + TransactionSigned::decode_2718(&mut buf).map_err(alloy_rlp::Error::from)?; + + if !buf.is_empty() { + return Err(alloy_rlp::Error::UnexpectedLength); + } + + Ok(WithEncoded::new(data, tx)) }) .collect::>()?; diff --git a/crates/optimism/rpc/Cargo.toml b/crates/optimism/rpc/Cargo.toml index 46967fba262d..24c3eb02d247 100644 --- a/crates/optimism/rpc/Cargo.toml +++ b/crates/optimism/rpc/Cargo.toml @@ -34,6 +34,7 @@ reth-optimism-evm.workspace = true reth-optimism-forks.workspace = true # ethereum +alloy-eips.workspace = true alloy-primitives.workspace = true alloy-rpc-types-eth.workspace = true alloy-rpc-types.workspace = true diff --git a/crates/optimism/rpc/src/eth/receipt.rs b/crates/optimism/rpc/src/eth/receipt.rs index a953d3f3096f..76b03e845a09 100644 --- a/crates/optimism/rpc/src/eth/receipt.rs +++ b/crates/optimism/rpc/src/eth/receipt.rs @@ -1,5 +1,6 @@ //! Loads and formats OP receipt RPC response. +use alloy_eips::eip2718::Encodable2718; use alloy_rpc_types::{AnyReceiptEnvelope, Log, TransactionReceipt}; use op_alloy_consensus::{OpDepositReceipt, OpDepositReceiptWithBloom, OpReceiptEnvelope}; use op_alloy_rpc_types::{ @@ -119,7 +120,7 @@ impl OpReceiptFieldsBuilder { tx: &TransactionSigned, l1_block_info: revm::L1BlockInfo, ) -> Result { - let raw_tx = tx.envelope_encoded(); + let raw_tx = tx.encoded_2718(); let timestamp = self.l1_block_timestamp; self.l1_fee = Some( @@ -300,6 +301,7 @@ impl OpReceiptBuilder { #[cfg(test)] mod test { use alloy_primitives::hex; + use op_alloy_network::eip2718::Decodable2718; use reth_optimism_chainspec::OP_MAINNET; use reth_primitives::{Block, BlockBody}; @@ -341,14 +343,13 @@ mod test { #[test] fn op_receipt_fields_from_block_and_tx() { // rig - let tx_0 = TransactionSigned::decode_enveloped( + let tx_0 = TransactionSigned::decode_2718( &mut TX_SET_L1_BLOCK_OP_MAINNET_BLOCK_124665056.as_slice(), ) .unwrap(); - let tx_1 = - TransactionSigned::decode_enveloped(&mut TX_1_OP_MAINNET_BLOCK_124665056.as_slice()) - .unwrap(); + let tx_1 = TransactionSigned::decode_2718(&mut TX_1_OP_MAINNET_BLOCK_124665056.as_slice()) + .unwrap(); let block = Block { body: BlockBody { transactions: [tx_0, tx_1.clone()].to_vec(), ..Default::default() }, diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 76683d16315a..0960cbac15b0 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -5,6 +5,7 @@ use alloc::vec::Vec; pub use alloy_eips::eip1898::{ BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag, ForkBlock, RpcBlockHash, }; +use alloy_eips::eip2718::Encodable2718; use alloy_primitives::{Address, Bytes, Sealable, B256}; use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable}; use derive_more::{Deref, DerefMut}; @@ -463,9 +464,10 @@ impl SealedBlock { Ok(()) } - /// Returns a vector of transactions RLP encoded with [`TransactionSigned::encode_enveloped`]. + /// Returns a vector of transactions RLP encoded with + /// [`alloy_eips::eip2718::Encodable2718::encoded_2718`]. pub fn raw_transactions(&self) -> Vec { - self.body.transactions().map(|tx| tx.envelope_encoded()).collect() + self.body.transactions().map(|tx| tx.encoded_2718().into()).collect() } } diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index 2fe611db1d2d..a12a5d6be89f 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -5,7 +5,7 @@ use crate::{ Request, TransactionSigned, Withdrawal, }; use alloc::vec::Vec; -use alloy_eips::eip7685::Encodable7685; +use alloy_eips::{eip2718::Encodable2718, eip7685::Encodable7685}; use alloy_primitives::{keccak256, B256}; use reth_trie_common::root::{ordered_trie_root, ordered_trie_root_with_encoder}; @@ -16,7 +16,7 @@ pub fn calculate_transaction_root(transactions: &[T]) -> B256 where T: AsRef, { - ordered_trie_root_with_encoder(transactions, |tx: &T, buf| tx.as_ref().encode_inner(buf, false)) + ordered_trie_root_with_encoder(transactions, |tx: &T, buf| tx.as_ref().encode_2718(buf)) } /// Calculates the root hash of the withdrawals. diff --git a/crates/primitives/src/transaction/compat.rs b/crates/primitives/src/transaction/compat.rs index 133cfbce9d6d..81281186f64c 100644 --- a/crates/primitives/src/transaction/compat.rs +++ b/crates/primitives/src/transaction/compat.rs @@ -11,11 +11,7 @@ pub trait FillTxEnv { impl FillTxEnv for TransactionSigned { fn fill_tx_env(&self, tx_env: &mut TxEnv, sender: Address) { #[cfg(feature = "optimism")] - let envelope = { - let mut envelope = alloc::vec::Vec::with_capacity(self.length_without_header()); - self.encode_enveloped(&mut envelope); - envelope - }; + let envelope = alloy_eips::eip2718::Encodable2718::encoded_2718(self); tx_env.caller = sender; match self.as_ref() { diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index b43f088cbe56..4f5c36002ddd 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -5,12 +5,12 @@ use alloy_eips::eip7702::SignedAuthorization; use alloy_primitives::{keccak256, Address, TxKind, B256, U256}; use alloy_consensus::{SignableTransaction, TxEip1559, TxEip2930, TxEip4844, TxEip7702, TxLegacy}; -use alloy_eips::eip2930::AccessList; -use alloy_primitives::{Bytes, Parity, TxHash}; -use alloy_rlp::{ - Decodable, Encodable, Error as RlpError, Header, EMPTY_LIST_CODE, EMPTY_STRING_CODE, +use alloy_eips::{ + eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}, + eip2930::AccessList, }; -use bytes::Buf; +use alloy_primitives::{Bytes, TxHash}; +use alloy_rlp::{Decodable, Encodable, Error as RlpError, Header}; use core::mem; use derive_more::{AsRef, Deref}; use once_cell::sync::Lazy; @@ -730,6 +730,8 @@ impl reth_codecs::Compact for Transaction { // 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 { COMPACT_IDENTIFIER_LEGACY => { let (tx, buf) = TxLegacy::from_compact(buf, buf.len()); @@ -975,6 +977,8 @@ impl reth_codecs::Compact for TransactionSignedNoHash { } 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; @@ -1196,62 +1200,10 @@ impl TransactionSigned { } } - /// Returns the enveloped encoded transactions. - /// - /// See also [`TransactionSigned::encode_enveloped`] - pub fn envelope_encoded(&self) -> Bytes { - let mut buf = Vec::new(); - self.encode_enveloped(&mut buf); - buf.into() - } - - /// Encodes the transaction into the "raw" format (e.g. `eth_sendRawTransaction`). - /// This format is also referred to as "binary" encoding. - /// - /// For legacy transactions, it encodes the RLP of the transaction into the buffer: - /// `rlp(tx-data)` - /// For EIP-2718 typed it encodes the type of the transaction followed by the rlp of the - /// transaction: `tx-type || rlp(tx-data)` - pub fn encode_enveloped(&self, out: &mut dyn bytes::BufMut) { - self.encode_inner(out, false) - } - - /// Inner encoding function that is used for both rlp [`Encodable`] trait and for calculating - /// hash that for eip2718 does not require rlp header - pub(crate) fn encode_inner(&self, out: &mut dyn bytes::BufMut, with_header: bool) { - self.transaction.encode_with_signature(&self.signature, out, with_header); - } - - /// Output the length of the `encode_inner(out`, true). Note to assume that `with_header` is - /// only `true`. - pub(crate) fn payload_len_inner(&self) -> usize { - match &self.transaction { - Transaction::Legacy(legacy_tx) => legacy_tx.encoded_len_with_signature( - &with_eip155_parity(&self.signature, legacy_tx.chain_id), - ), - Transaction::Eip2930(access_list_tx) => { - access_list_tx.encoded_len_with_signature(&self.signature, true) - } - Transaction::Eip1559(dynamic_fee_tx) => { - dynamic_fee_tx.encoded_len_with_signature(&self.signature, true) - } - Transaction::Eip4844(blob_tx) => { - blob_tx.encoded_len_with_signature(&self.signature, true) - } - Transaction::Eip7702(set_code_tx) => { - set_code_tx.encoded_len_with_signature(&self.signature, true) - } - #[cfg(feature = "optimism")] - Transaction::Deposit(deposit_tx) => deposit_tx.encoded_len(true), - } - } - /// Calculate transaction hash, eip2728 transaction does not contain rlp header and start with /// tx type. pub fn recalculate_hash(&self) -> B256 { - let mut buf = Vec::new(); - self.encode_inner(&mut buf, false); - keccak256(&buf) + keccak256(self.encoded_2718()) } /// Create a new signed transaction from a transaction and its signature. @@ -1329,134 +1281,6 @@ impl TransactionSigned { let signed = Self { transaction: Transaction::Legacy(transaction), hash, signature }; Ok(signed) } - - /// Decodes an enveloped EIP-2718 typed transaction. - /// - /// This should _only_ be used internally in general transaction decoding methods, - /// which have already ensured that the input is a typed transaction with the following format: - /// `tx-type || rlp(tx-data)` - /// - /// Note that this format does not start with any RLP header, and instead starts with a single - /// byte indicating the transaction type. - /// - /// CAUTION: this expects that `data` is `tx-type || rlp(tx-data)` - pub fn decode_enveloped_typed_transaction(data: &mut &[u8]) -> alloy_rlp::Result { - // keep this around so we can use it to calculate the hash - let original_encoding_without_header = *data; - - let tx_type = *data.first().ok_or(RlpError::InputTooShort)?; - data.advance(1); - - // decode the list header for the rest of the transaction - let header = Header::decode(data)?; - if !header.list { - return Err(RlpError::Custom("typed tx fields must be encoded as a list")) - } - - let remaining_len = data.len(); - - // length of tx encoding = tx type byte (size = 1) + length of header + payload length - let tx_length = 1 + header.length() + header.payload_length; - - // decode common fields - let Ok(tx_type) = TxType::try_from(tx_type) else { - return Err(RlpError::Custom("unsupported typed transaction type")) - }; - - let transaction = match tx_type { - TxType::Eip2930 => Transaction::Eip2930(TxEip2930::decode_fields(data)?), - TxType::Eip1559 => Transaction::Eip1559(TxEip1559::decode_fields(data)?), - TxType::Eip4844 => Transaction::Eip4844(TxEip4844::decode_fields(data)?), - TxType::Eip7702 => Transaction::Eip7702(TxEip7702::decode_fields(data)?), - #[cfg(feature = "optimism")] - TxType::Deposit => Transaction::Deposit(TxDeposit::decode_fields(data)?), - TxType::Legacy => return Err(RlpError::Custom("unexpected legacy tx type")), - }; - - #[cfg(not(feature = "optimism"))] - let signature = Signature::decode_rlp_vrs(data)?; - - #[cfg(feature = "optimism")] - let signature = if tx_type == TxType::Deposit { - optimism_deposit_tx_signature() - } else { - Signature::decode_rlp_vrs(data)? - }; - - if !matches!(signature.v(), Parity::Parity(_)) { - return Err(alloy_rlp::Error::Custom("invalid parity for typed transaction")); - } - - let bytes_consumed = remaining_len - data.len(); - if bytes_consumed != header.payload_length { - return Err(RlpError::UnexpectedLength) - } - - let hash = keccak256(&original_encoding_without_header[..tx_length]); - let signed = Self { transaction, hash, signature }; - Ok(signed) - } - - /// Decodes the "raw" format of transaction (similar to `eth_sendRawTransaction`). - /// - /// This should be used for any RPC method that accepts a raw transaction. - /// Currently, this includes: - /// * `eth_sendRawTransaction`. - /// * All versions of `engine_newPayload`, in the `transactions` field. - /// - /// A raw transaction is either a legacy transaction or EIP-2718 typed transaction. - /// - /// For legacy transactions, the format is encoded as: `rlp(tx-data)`. This format will start - /// with a RLP list header. - /// - /// For EIP-2718 typed transactions, the format is encoded as the type of the transaction - /// followed by the rlp of the transaction: `type || rlp(tx-data)`. - /// - /// Both for legacy and EIP-2718 transactions, an error will be returned if there is an excess - /// of bytes in input data. - pub fn decode_enveloped(input_data: &mut &[u8]) -> alloy_rlp::Result { - if input_data.is_empty() { - return Err(RlpError::InputTooShort) - } - - // Check if the tx is a list - let output_data = if input_data[0] >= EMPTY_LIST_CODE { - // decode as legacy transaction - Self::decode_rlp_legacy_transaction(input_data)? - } else { - Self::decode_enveloped_typed_transaction(input_data)? - }; - - if !input_data.is_empty() { - return Err(RlpError::UnexpectedLength) - } - - Ok(output_data) - } - - /// Returns the length without an RLP header - this is used for eth/68 sizes. - pub fn length_without_header(&self) -> usize { - // method computes the payload len without a RLP header - match &self.transaction { - Transaction::Legacy(legacy_tx) => legacy_tx.encoded_len_with_signature( - &with_eip155_parity(&self.signature, legacy_tx.chain_id), - ), - Transaction::Eip2930(access_list_tx) => { - access_list_tx.encoded_len_with_signature(&self.signature, false) - } - Transaction::Eip1559(dynamic_fee_tx) => { - dynamic_fee_tx.encoded_len_with_signature(&self.signature, false) - } - Transaction::Eip4844(blob_tx) => { - blob_tx.encoded_len_with_signature(&self.signature, false) - } - Transaction::Eip7702(set_code_tx) => { - set_code_tx.encoded_len_with_signature(&self.signature, false) - } - #[cfg(feature = "optimism")] - Transaction::Deposit(deposit_tx) => deposit_tx.encoded_len(false), - } - } } impl From for TransactionSigned { @@ -1475,11 +1299,16 @@ impl Encodable for TransactionSigned { /// transaction: /// `rlp(tx-type || rlp(tx-data))` fn encode(&self, out: &mut dyn bytes::BufMut) { - self.encode_inner(out, true); + self.network_encode(out); } fn length(&self) -> usize { - self.payload_len_inner() + let mut payload_length = self.encode_2718_len(); + if !self.is_legacy() { + payload_length += Header { list: false, payload_length }.length(); + } + + payload_length } } @@ -1510,38 +1339,76 @@ impl Decodable for TransactionSigned { /// 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 { - if buf.is_empty() { - return Err(RlpError::InputTooShort) - } + Self::network_decode(buf).map_err(Into::into) + } +} - // decode header - let mut original_encoding = *buf; - let header = Header::decode(buf)?; +impl Encodable2718 for TransactionSigned { + fn type_flag(&self) -> Option { + match self.transaction.tx_type() { + TxType::Legacy => None, + tx_type => Some(tx_type as u8), + } + } - let remaining_len = buf.len(); + fn encode_2718_len(&self) -> usize { + match &self.transaction { + Transaction::Legacy(legacy_tx) => legacy_tx.encoded_len_with_signature( + &with_eip155_parity(&self.signature, legacy_tx.chain_id), + ), + Transaction::Eip2930(access_list_tx) => { + access_list_tx.encoded_len_with_signature(&self.signature, false) + } + Transaction::Eip1559(dynamic_fee_tx) => { + dynamic_fee_tx.encoded_len_with_signature(&self.signature, false) + } + Transaction::Eip4844(blob_tx) => { + blob_tx.encoded_len_with_signature(&self.signature, false) + } + Transaction::Eip7702(set_code_tx) => { + set_code_tx.encoded_len_with_signature(&self.signature, false) + } + #[cfg(feature = "optimism")] + Transaction::Deposit(deposit_tx) => deposit_tx.encoded_len(false), + } + } - // if the transaction is encoded as a string then it is a typed transaction - if header.list { - let tx = Self::decode_rlp_legacy_transaction(&mut original_encoding)?; + fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { + self.transaction.encode_with_signature(&self.signature, out, false) + } +} - // advance the buffer based on how far `decode_rlp_legacy_transaction` advanced the - // buffer - *buf = original_encoding; - Ok(tx) - } else { - let tx = Self::decode_enveloped_typed_transaction(buf)?; - - let bytes_consumed = remaining_len - buf.len(); - // because Header::decode works for single bytes (including the tx type), returning a - // string Header with payload_length of 1, we need to make sure this check is only - // performed for transactions with a string header - if bytes_consumed != header.payload_length && original_encoding[0] > EMPTY_STRING_CODE { - return Err(RlpError::UnexpectedLength) +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, hash) = TxEip2930::decode_signed_fields(buf)?.into_parts(); + Ok(Self { transaction: Transaction::Eip2930(tx), signature, hash }) } - - Ok(tx) + TxType::Eip1559 => { + let (tx, signature, hash) = TxEip1559::decode_signed_fields(buf)?.into_parts(); + Ok(Self { transaction: Transaction::Eip1559(tx), signature, hash }) + } + TxType::Eip7702 => { + let (tx, signature, hash) = TxEip7702::decode_signed_fields(buf)?.into_parts(); + Ok(Self { transaction: Transaction::Eip7702(tx), signature, hash }) + } + TxType::Eip4844 => { + let (tx, signature, hash) = TxEip4844::decode_signed_fields(buf)?.into_parts(); + Ok(Self { transaction: Transaction::Eip4844(tx), signature, hash }) + } + #[cfg(feature = "optimism")] + TxType::Deposit => Ok(Self::from_transaction_and_signature( + Transaction::Deposit(TxDeposit::decode(buf)?), + optimism_deposit_tx_signature(), + )), } } + + fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result { + Ok(Self::decode_rlp_legacy_transaction(buf)?) + } } #[cfg(any(test, feature = "arbitrary"))] @@ -1702,6 +1569,7 @@ mod tests { transaction::{signature::Signature, TxEip1559, TxKind, TxLegacy}, Transaction, TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, }; + use alloy_eips::eip2718::{Decodable2718, Encodable2718}; use alloy_primitives::{address, b256, bytes, hex, Address, Bytes, Parity, B256, U256}; use alloy_rlp::{Decodable, Encodable, Error as RlpError}; use reth_chainspec::MIN_TRANSACTION_GAS; @@ -1743,7 +1611,7 @@ mod tests { // random mainnet tx let tx_bytes = hex!("02f872018307910d808507204d2cb1827d0094388c818ca8b9251b393131c08a736a67ccb19297880320d04823e2701c80c001a0cf024f4815304df2867a1a74e9d2707b6abda0337d2d54a4438d453f4160f190a07ac0e6b3bc9395b5b9c8b9e6d77204a236577a5b18467b9175c01de4faa208d9"); - let decoded = TransactionSigned::decode_enveloped(&mut &tx_bytes[..]).unwrap(); + let decoded = TransactionSigned::decode_2718(&mut &tx_bytes[..]).unwrap(); assert_eq!( decoded.recover_signer(), Some(Address::from_str("0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5").unwrap()) @@ -1759,7 +1627,7 @@ mod tests { // https://sepolia.etherscan.io/getRawTx?tx=0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0 let raw_tx = alloy_primitives::hex::decode("0x03f9011d83aa36a7820fa28477359400852e90edd0008252089411e9ca82a3a762b4b5bd264d4173a242e7a770648080c08504a817c800f8a5a0012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921aa00152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4a0013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7a001148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1a0011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e654901a0c8de4cced43169f9aa3d36506363b2d2c44f6c49fc1fd91ea114c86f3757077ea01e11fdd0d1934eda0492606ee0bb80a7bf8f35cc5f86ec60fe5031ba48bfd544").unwrap(); - let decoded = TransactionSigned::decode_enveloped(&mut raw_tx.as_slice()).unwrap(); + let decoded = TransactionSigned::decode_2718(&mut raw_tx.as_slice()).unwrap(); assert_eq!(decoded.tx_type(), TxType::Eip4844); let from = decoded.recover_signer(); @@ -1933,7 +1801,7 @@ mod tests { let input = hex!("02f871018302a90f808504890aef60826b6c94ddf4c5025d1a5742cf12f74eec246d4432c295e487e09c3bbcc12b2b80c080a0f21a4eacd0bf8fea9c5105c543be5a1d8c796516875710fafafdf16d16d8ee23a001280915021bb446d1973501a67f93d2b38894a514b976e7b46dc2fe54598d76"); let decoded = TransactionSigned::decode(&mut &input[..]).unwrap(); - let encoded = decoded.envelope_encoded(); + let encoded = decoded.encoded_2718(); assert_eq!(encoded[..], input); } @@ -1941,9 +1809,9 @@ mod tests { fn test_envelop_decode() { // random tx: let input = bytes!("02f871018302a90f808504890aef60826b6c94ddf4c5025d1a5742cf12f74eec246d4432c295e487e09c3bbcc12b2b80c080a0f21a4eacd0bf8fea9c5105c543be5a1d8c796516875710fafafdf16d16d8ee23a001280915021bb446d1973501a67f93d2b38894a514b976e7b46dc2fe54598d76"); - let decoded = TransactionSigned::decode_enveloped(&mut input.as_ref()).unwrap(); + let decoded = TransactionSigned::decode_2718(&mut input.as_ref()).unwrap(); - let encoded = decoded.envelope_encoded(); + let encoded = decoded.encoded_2718(); assert_eq!(encoded, input); } @@ -2022,13 +1890,13 @@ mod tests { #[test] fn recover_enveloped() { let data = hex!("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8"); - let tx = TransactionSigned::decode_enveloped(&mut data.as_slice()).unwrap(); + let tx = TransactionSigned::decode_2718(&mut data.as_slice()).unwrap(); let sender = tx.recover_signer().unwrap(); assert_eq!(sender, address!("001e2b7dE757bA469a57bF6b23d982458a07eFcE")); assert_eq!(tx.to(), Some(address!("D9e1459A7A482635700cBc20BBAF52D495Ab9C96"))); assert_eq!(tx.input().as_ref(), hex!("1b55ba3a")); - let encoded = tx.envelope_encoded(); - assert_eq!(encoded.as_ref(), data.as_slice()); + let encoded = tx.encoded_2718(); + assert_eq!(encoded.as_ref(), data.to_vec()); } // @@ -2036,7 +1904,7 @@ mod tests { #[test] fn recover_pre_eip2() { let data = hex!("f8ea0c850ba43b7400832dc6c0942935aa0a2d2fbb791622c29eb1c117b65b7a908580b884590528a9000000000000000000000001878ace42092b7f1ae1f28d16c1272b1aa80ca4670000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000d02ab486cedc0000000000000000000000000000000000000000000000000000557fe293cabc08cf1ca05bfaf3fda0a56b49cc78b22125feb5ae6a99d2b4781f00507d8b02c173771c85a0b5da0dbe6c5bc53740d0071fc83eb17ba0f709e49e9ae7df60dee625ef51afc5"); - let tx = TransactionSigned::decode_enveloped(&mut data.as_slice()).unwrap(); + let tx = TransactionSigned::decode_2718(&mut data.as_slice()).unwrap(); let sender = tx.recover_signer(); assert!(sender.is_none()); let sender = tx.recover_signer_unchecked().unwrap(); @@ -2096,28 +1964,8 @@ mod tests { fn create_txs_disallowed_for_eip4844() { let data = [3, 208, 128, 128, 123, 128, 120, 128, 129, 129, 128, 192, 129, 129, 192, 128, 128, 9]; - let res = TransactionSigned::decode_enveloped(&mut &data[..]); + let res = TransactionSigned::decode_2718(&mut &data[..]); assert!(res.is_err()); } - - #[test] - fn decode_envelope_fails_on_trailing_bytes_legacy() { - let data = [201, 3, 56, 56, 128, 43, 36, 27, 128, 3, 192]; - - let result = TransactionSigned::decode_enveloped(&mut data.as_ref()); - - assert!(result.is_err()); - assert_eq!(result, Err(RlpError::UnexpectedLength)); - } - - #[test] - fn decode_envelope_fails_on_trailing_bytes_eip2718() { - let data = hex!("02f872018307910d808507204d2cb1827d0094388c818ca8b9251b393131c08a736a67ccb19297880320d04823e2701c80c001a0cf024f4815304df2867a1a74e9d2707b6abda0337d2d54a4438d453f4160f190a07ac0e6b3bc9395b5b9c8b9e6d77204a236577a5b18467b9175c01de4faa208d900"); - - let result = TransactionSigned::decode_enveloped(&mut data.as_ref()); - - assert!(result.is_err()); - assert_eq!(result, Err(RlpError::UnexpectedLength)); - } } diff --git a/crates/primitives/src/transaction/pooled.rs b/crates/primitives/src/transaction/pooled.rs index 04026839538d..cc2dc5766394 100644 --- a/crates/primitives/src/transaction/pooled.rs +++ b/crates/primitives/src/transaction/pooled.rs @@ -15,6 +15,7 @@ use alloy_consensus::{ transaction::{TxEip1559, TxEip2930, TxEip4844, TxLegacy}, SignableTransaction, TxEip4844WithSidecar, }; +use alloy_eips::eip2718::{Decodable2718, Eip2718Error}; use alloy_primitives::{Address, Bytes, TxHash, B256}; use alloy_rlp::{Decodable, Encodable, Error as RlpError, Header, EMPTY_LIST_CODE}; use bytes::Buf; @@ -222,6 +223,9 @@ impl PooledTransactionsElement { // decode the type byte, only decode BlobTransaction if it is a 4844 transaction let tx_type = *data.first().ok_or(RlpError::InputTooShort)?; + // First, we advance the buffer past the type byte + data.advance(1); + if tx_type == EIP4844_TX_TYPE_ID { // Recall that the blob transaction response `TransactionPayload` is encoded like // this: `rlp([tx_payload_body, blobs, commitments, proofs])` @@ -231,18 +235,17 @@ impl PooledTransactionsElement { // // This makes the full encoding: // `tx_type (0x03) || rlp([[chain_id, nonce, ...], blobs, commitments, proofs])` - // - // First, we advance the buffer past the type byte - data.advance(1); // Now, we decode the inner blob transaction: // `rlp([[chain_id, nonce, ...], blobs, commitments, proofs])` let blob_tx = BlobTransaction::decode_inner(data)?; Ok(Self::BlobTransaction(blob_tx)) } else { - // DO NOT advance the buffer for the type, since we want the enveloped decoding to - // decode it again and advance the buffer on its own. - let typed_tx = TransactionSigned::decode_enveloped_typed_transaction(data)?; + let typed_tx = + TransactionSigned::typed_decode(tx_type, data).map_err(|err| match err { + Eip2718Error::RlpError(err) => err, + _ => RlpError::Custom("failed to decode EIP-2718 transaction"), + })?; // because we checked the tx type, we can be sure that the transaction is not a // blob transaction or legacy @@ -337,7 +340,7 @@ impl PooledTransactionsElement { /// Returns the enveloped encoded transactions. /// - /// See also [`TransactionSigned::encode_enveloped`] + /// See also [`alloy_eips::eip2718::Encodable2718::encoded_2718`] pub fn envelope_encoded(&self) -> Bytes { let mut buf = Vec::new(); self.encode_enveloped(&mut buf); @@ -591,6 +594,9 @@ impl Decodable for PooledTransactionsElement { let tx_type = *buf.first().ok_or(RlpError::InputTooShort)?; let remaining_len = buf.len(); + // Aadvance the buffer past the type byte + buf.advance(1); + if tx_type == EIP4844_TX_TYPE_ID { // Recall that the blob transaction response `TransactionPayload` is encoded like // this: `rlp([tx_payload_body, blobs, commitments, proofs])` @@ -600,11 +606,8 @@ impl Decodable for PooledTransactionsElement { // // This makes the full encoding: // `tx_type (0x03) || rlp([[chain_id, nonce, ...], blobs, commitments, proofs])` - // - // First, we advance the buffer past the type byte - buf.advance(1); - // Now, we decode the inner blob transaction: + // Decode the inner blob transaction: // `rlp([[chain_id, nonce, ...], blobs, commitments, proofs])` let blob_tx = BlobTransaction::decode_inner(buf)?; @@ -616,9 +619,8 @@ impl Decodable for PooledTransactionsElement { Ok(Self::BlobTransaction(blob_tx)) } else { - // DO NOT advance the buffer for the type, since we want the enveloped decoding to - // decode it again and advance the buffer on its own. - let typed_tx = TransactionSigned::decode_enveloped_typed_transaction(buf)?; + let typed_tx = + TransactionSigned::typed_decode(tx_type, buf).map_err(RlpError::from)?; // check that the bytes consumed match the payload length let bytes_consumed = remaining_len - buf.len(); diff --git a/crates/primitives/src/transaction/signature.rs b/crates/primitives/src/transaction/signature.rs index e99fc92324ff..39c0f92fda88 100644 --- a/crates/primitives/src/transaction/signature.rs +++ b/crates/primitives/src/transaction/signature.rs @@ -120,6 +120,7 @@ mod tests { }, Signature, }; + use alloy_eips::eip2718::Decodable2718; use alloy_primitives::{hex, Address, Parity, B256, U256}; use std::str::FromStr; @@ -164,7 +165,7 @@ mod tests { // // Block number: 46170 let raw_tx = hex!("f86d8085746a52880082520894c93f2250589a6563f5359051c1ea25746549f0d889208686e75e903bc000801ba034b6fdc33ea520e8123cf5ac4a9ff476f639cab68980cd9366ccae7aef437ea0a0e517caa5f50e27ca0d1e9a92c503b4ccb039680c6d9d0c71203ed611ea4feb33"); - let tx = crate::transaction::TransactionSigned::decode_enveloped(&mut &raw_tx[..]).unwrap(); + let tx = crate::transaction::TransactionSigned::decode_2718(&mut &raw_tx[..]).unwrap(); let signature = tx.signature(); // make sure we know it's greater than SECP256K1N_HALF diff --git a/crates/rpc/rpc-eth-api/Cargo.toml b/crates/rpc/rpc-eth-api/Cargo.toml index 4dd61324c389..23dd46baecf1 100644 --- a/crates/rpc/rpc-eth-api/Cargo.toml +++ b/crates/rpc/rpc-eth-api/Cargo.toml @@ -32,6 +32,7 @@ reth-network-api.workspace = true reth-trie.workspace = true # ethereum +alloy-eips.workspace = true alloy-dyn-abi = { workspace = true, features = ["eip712"] } alloy-json-rpc.workspace = true alloy-network.workspace = true @@ -39,7 +40,6 @@ alloy-primitives.workspace = true alloy-rpc-types-eth.workspace = true alloy-rpc-types.workspace = true alloy-rpc-types-mev.workspace = true -alloy-eips.workspace = true # rpc jsonrpsee = { workspace = true, features = ["server", "macros"] } diff --git a/crates/rpc/rpc-eth-api/src/helpers/transaction.rs b/crates/rpc/rpc-eth-api/src/helpers/transaction.rs index d5602de9a4dd..d98cb69bfc30 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/transaction.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/transaction.rs @@ -2,6 +2,7 @@ //! network. use alloy_dyn_abi::TypedData; +use alloy_eips::eip2718::Encodable2718; use alloy_network::TransactionBuilder; use alloy_primitives::{Address, Bytes, TxHash, B256}; use alloy_rpc_types::{BlockNumberOrTag, TransactionInfo}; @@ -106,7 +107,7 @@ pub trait EthTransactions: LoadTransaction { Ok(LoadTransaction::provider(this) .transaction_by_hash(hash) .map_err(Self::Error::from_eth_err)? - .map(|tx| tx.envelope_encoded())) + .map(|tx| tx.encoded_2718().into())) }) .await } @@ -305,7 +306,7 @@ pub trait EthTransactions: LoadTransaction { async move { if let Some(block) = self.block_with_senders(block_id).await? { if let Some(tx) = block.transactions().nth(index) { - return Ok(Some(tx.envelope_encoded())) + return Ok(Some(tx.encoded_2718().into())) } } diff --git a/crates/rpc/rpc-types-compat/Cargo.toml b/crates/rpc/rpc-types-compat/Cargo.toml index 1213e33ba623..a9d82d95779c 100644 --- a/crates/rpc/rpc-types-compat/Cargo.toml +++ b/crates/rpc/rpc-types-compat/Cargo.toml @@ -17,6 +17,7 @@ reth-primitives.workspace = true reth-trie-common.workspace = true # ethereum +alloy-eips.workspace = true alloy-primitives.workspace = true alloy-rlp.workspace = true alloy-rpc-types.workspace = true diff --git a/crates/rpc/rpc-types-compat/src/engine/payload.rs b/crates/rpc/rpc-types-compat/src/engine/payload.rs index 10cba29fdec1..84943b60e208 100644 --- a/crates/rpc/rpc-types-compat/src/engine/payload.rs +++ b/crates/rpc/rpc-types-compat/src/engine/payload.rs @@ -1,6 +1,7 @@ //! Standalone Conversion Functions for Handling Different Versions of Execution Payloads in //! Ethereum's Engine +use alloy_eips::eip2718::{Decodable2718, Encodable2718}; use alloy_primitives::{B256, U256}; use alloy_rpc_types_engine::{ payload::{ExecutionPayloadBodyV1, ExecutionPayloadFieldV2, ExecutionPayloadInputV2}, @@ -26,7 +27,17 @@ pub fn try_payload_v1_to_block(payload: ExecutionPayloadV1) -> Result, _>>()?; let transactions_root = proofs::calculate_transaction_root(&transactions); @@ -360,7 +371,7 @@ pub fn validate_block_hash( pub fn convert_to_payload_body_v1(value: Block) -> ExecutionPayloadBodyV1 { let transactions = value.body.transactions.into_iter().map(|tx| { let mut out = Vec::new(); - tx.encode_enveloped(&mut out); + tx.encode_2718(&mut out); out.into() }); ExecutionPayloadBodyV1 { @@ -373,7 +384,7 @@ pub fn convert_to_payload_body_v1(value: Block) -> ExecutionPayloadBodyV1 { pub fn convert_to_payload_body_v2(value: Block) -> ExecutionPayloadBodyV2 { let transactions = value.body.transactions.into_iter().map(|tx| { let mut out = Vec::new(); - tx.encode_enveloped(&mut out); + tx.encode_2718(&mut out); out.into() }); diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 9593efd4b8a3..4665cd002ca4 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -39,6 +39,7 @@ reth-trie.workspace = true alloy-consensus.workspace = true alloy-signer.workspace = true alloy-signer-local.workspace = true +alloy-eips.workspace = true alloy-dyn-abi.workspace = true alloy-genesis.workspace = true alloy-network.workspace = true diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index a57ddba2ccb2..d7ee43720c19 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -1,3 +1,4 @@ +use alloy_eips::eip2718::Encodable2718; use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_rlp::{Decodable, Encodable}; use alloy_rpc_types::{ @@ -883,7 +884,7 @@ where .block_with_senders_by_id(block_id, TransactionVariant::NoHash) .to_rpc_result()? .unwrap_or_default(); - Ok(block.into_transactions_ecrecovered().map(|tx| tx.envelope_encoded()).collect()) + Ok(block.into_transactions_ecrecovered().map(|tx| tx.encoded_2718().into()).collect()) } /// Handler for `debug_getRawReceipts` diff --git a/crates/rpc/rpc/src/eth/helpers/signer.rs b/crates/rpc/rpc/src/eth/helpers/signer.rs index de36bbc0919d..b5109d09017a 100644 --- a/crates/rpc/rpc/src/eth/helpers/signer.rs +++ b/crates/rpc/rpc/src/eth/helpers/signer.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use crate::EthApi; use alloy_consensus::TxEnvelope; use alloy_dyn_abi::TypedData; +use alloy_eips::eip2718::Decodable2718; use alloy_network::{eip2718::Encodable2718, EthereumWallet, TransactionBuilder}; use alloy_primitives::{eip191_hash_message, Address, B256}; use alloy_rpc_types_eth::TransactionRequest; @@ -95,7 +96,7 @@ impl EthSigner for DevSigner { // decode transaction into signed transaction type let encoded = txn_envelope.encoded_2718(); - let txn_signed = TransactionSigned::decode_enveloped(&mut encoded.as_ref()) + let txn_signed = TransactionSigned::decode_2718(&mut encoded.as_ref()) .map_err(|_| SignError::InvalidTransactionRequest)?; Ok(txn_signed) diff --git a/crates/transaction-pool/src/test_utils/gen.rs b/crates/transaction-pool/src/test_utils/gen.rs index 0ee605631e8a..d51bf80270de 100644 --- a/crates/transaction-pool/src/test_utils/gen.rs +++ b/crates/transaction-pool/src/test_utils/gen.rs @@ -1,6 +1,6 @@ use crate::EthPooledTransaction; use alloy_consensus::{TxEip1559, TxEip4844, TxLegacy}; -use alloy_eips::eip2930::AccessList; +use alloy_eips::{eip2718::Encodable2718, eip2930::AccessList}; use alloy_primitives::{Address, Bytes, TxKind, B256, U256}; use rand::Rng; use reth_chainspec::MAINNET; @@ -106,7 +106,7 @@ impl TransactionGenerator { /// 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 encoded_length = tx.length_without_header(); + let encoded_length = tx.encode_2718_len(); EthPooledTransaction::new(tx, encoded_length) } } diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index 643a69ed6802..d4eabc73bbce 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -7,7 +7,7 @@ use crate::{ validate::ValidPoolTransaction, AllTransactionsEvents, }; -use alloy_eips::{eip2930::AccessList, eip4844::BlobAndProofV1}; +use alloy_eips::{eip2718::Encodable2718, eip2930::AccessList, eip4844::BlobAndProofV1}; use alloy_primitives::{Address, TxHash, TxKind, B256, U256}; use futures_util::{ready, Stream}; use reth_eth_wire_types::HandleMempoolData; @@ -1251,7 +1251,7 @@ impl TryFrom for EthPooledTransaction { } }; - let encoded_length = tx.length_without_header(); + let encoded_length = tx.encode_2718_len(); let transaction = Self::new(tx, encoded_length); Ok(transaction) }