From 659375d7766fe1f64fb31fbe0b476fc85819bc2e Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:16:25 -0700 Subject: [PATCH] eip7702: use alloy `TxEip7702` (#10617) --- Cargo.lock | 104 +++--- Cargo.toml | 52 +-- crates/primitives/src/transaction/compat.rs | 2 +- crates/primitives/src/transaction/eip7702.rs | 312 +----------------- crates/primitives/src/transaction/mod.rs | 36 +- crates/primitives/src/transaction/pooled.rs | 22 +- .../codecs/src/alloy/transaction/eip7702.rs | 71 ++++ .../codecs/src/alloy/transaction/mod.rs | 7 +- 8 files changed, 195 insertions(+), 411 deletions(-) create mode 100644 crates/storage/codecs/src/alloy/transaction/eip7702.rs diff --git a/Cargo.lock b/Cargo.lock index c9368795c..60ec4502b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,9 +117,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7198a527b4c4762cb88d54bcaeb0428f4298b72552c9c8ec4af614b4a4990c59" +checksum = "4177d135789e282e925092be8939d421b701c6d92c0a16679faa659d9166289d" dependencies = [ "alloy-eips", "alloy-primitives 0.8.0", @@ -177,9 +177,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "159eab0e4e15b88571f55673af37314f4b8f17630dc1b393c3d70f2128a1d494" +checksum = "499ee14d296a133d142efd215eb36bf96124829fe91cf8f5d4e5ccdd381eae00" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -196,9 +196,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "210f4b358d724f85df8adaec753c583defb58169ad3cad3d48c80d1a25a6ff0e" +checksum = "4b85dfc693e4a1193f0372a8f789df12ab51fcbe7be0733baa04939a86dd813b" dependencies = [ "alloy-primitives 0.8.0", "alloy-serde", @@ -219,9 +219,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7733446dd531f8eb877331fea02f6c40bdbb47444a17dc3464bf75319cc073a" +checksum = "4207166c79cfdf7f3bed24bbc84f5c7c5d4db1970f8c82e3fcc76257f16d2166" dependencies = [ "alloy-primitives 0.8.0", "alloy-sol-types", @@ -233,9 +233,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b80851d1697fc4fa2827998e3ee010a3d1fc59c7d25e87070840169fcf465832" +checksum = "dfbe2802d5b8c632f18d68c352073378f02a3407c1b6a4487194e7d21ab0f002" dependencies = [ "alloy-consensus", "alloy-eips", @@ -265,9 +265,9 @@ dependencies = [ [[package]] name = "alloy-node-bindings" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2657dae91ae61ed6cdd4c58b7e09330de934eea4e14d2f54f72f2a6720b23437" +checksum = "c847311cc7386684ef38ab404069d795bee07da945f63d884265436870a17276" dependencies = [ "alloy-genesis", "alloy-primitives 0.8.0", @@ -329,9 +329,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d2a195caa6707f5ce13905794865765afc6d9ea92c3a56e3a973c168d703bc" +checksum = "1376948df782ffee83a54cac4b2aba14134edd997229a3db97da0a606586eb5c" dependencies = [ "alloy-chains", "alloy-consensus", @@ -367,9 +367,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c59e13200322138fe4279b4676b0d78c4f55502de127f5a448495d3ddfaa43" +checksum = "fa73f976e7b6341f3f8a404241cf04f883d40212cd4f2633c66d99de472e262c" dependencies = [ "alloy-json-rpc", "alloy-primitives 0.8.0", @@ -408,9 +408,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed31cdba2b23d71c555505b06674f8e7459496abfd7f4875d268434ef5a99ee6" +checksum = "02378418a429f8a14a0ad8ffaa15b2d25ff34914fc4a1e366513c6a3800e03b3" dependencies = [ "alloy-json-rpc", "alloy-primitives 0.8.0", @@ -432,9 +432,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d758f65aa648491c6358335c578de45cd7de6fdf2877c3cef61f2c9bebea21" +checksum = "d9ae4c4fbd37d9996f501fbc7176405aab97ae3a5772789be06ef0e7c4dad6dd" dependencies = [ "alloy-rpc-types-engine", "alloy-rpc-types-eth", @@ -445,9 +445,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-admin" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e41c33bbddaec71ca1bd7a4df38f95f408ef4fa3b3c29a7e9cc8d0e43be5fbe" +checksum = "594b7cb723759c7b438c95a3bbd2e391760c03ee857443070758aaf2593ae84e" dependencies = [ "alloy-genesis", "alloy-primitives 0.8.0", @@ -457,9 +457,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa5ee4ffe3e687a6372dd02e998f4f65e512ffdfe0d2c248db822649814c36cd" +checksum = "140b079c6fda14d9586432bf988b46ac0e04871ca313c9e00aa85cc808105e8a" dependencies = [ "alloy-primitives 0.8.0", "alloy-serde", @@ -468,9 +468,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-beacon" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3173bf0239a59d3616f4f4ab1682de25dd30b13fb8f52bf7ee7503729354f3c4" +checksum = "abbd9b6764423821bd6874477791ca68cfd0e946958d611319b57b006edf0113" dependencies = [ "alloy-eips", "alloy-primitives 0.8.0", @@ -482,9 +482,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99137a6d8d559138ed5a64f0e270ef095abc84d36b52727621d3ba845ed5d5d1" +checksum = "dd56b9d938e458b8d4498b276bd3aaf0e8f0b58e86fe04fef2c71fed4b4c96de" dependencies = [ "alloy-primitives 0.8.0", "serde", @@ -492,9 +492,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e800d959606fa19b36b31d7c24d68ef75b970c654b7aa581dce23de82db0a5" +checksum = "d79cadb52e32d40afa04847647eb50a332559d7870e66e46a0c32c33bf1c801d" dependencies = [ "alloy-consensus", "alloy-eips", @@ -511,9 +511,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ba05d6ee4db0d89113294a614137940f79abfc2c40a9a3bee2995660358776" +checksum = "15bb3506ab1cf415d4752778c93e102050399fb8de97b7da405a5bf3e31f5f3b" dependencies = [ "alloy-consensus", "alloy-eips", @@ -532,9 +532,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-mev" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a0b28949d1077826684b5912fe9ab1c752a863af0419b1ba9abff19006d61b1" +checksum = "19e8cb848b66617f7d58b576bfc416854c4e9ae8d35e14f5077c0c779048f280" dependencies = [ "alloy-eips", "alloy-primitives 0.8.0", @@ -545,9 +545,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd2af822ed58f2b6dd7cfccf88bf69f42c9a8cbf4663316227646a8a3e5a591f" +checksum = "16cca915e0aab3b2657b4f9efe02eb88e5483905fb6d244749652aae14e5f92e" dependencies = [ "alloy-primitives 0.8.0", "alloy-rpc-types-eth", @@ -559,9 +559,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a8fbdf39e93a9b213df39541be51671e93e6e8b142c3602ddb4ff6219a1bc85" +checksum = "68eede4bd722bb872222efbbfbccc8f9b86e597143934b8ce556d3e0487bb662" dependencies = [ "alloy-primitives 0.8.0", "alloy-rpc-types-eth", @@ -571,9 +571,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd260ede54f0b53761fdd04133acc10ae70427f66a69aa9590529bbd066cd58" +checksum = "ae417978015f573b4a8c02af17f88558fb22e3fccd12e8a910cf6a2ff331cfcb" dependencies = [ "alloy-primitives 0.8.0", "arbitrary", @@ -583,9 +583,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5193ee6b370b89db154d7dc40c6a8e6ce11213865baaf2b418a9f2006be762" +checksum = "b750c9b61ac0646f8f4a61231c2732a337b2c829866fc9a191b96b7eedf80ffe" dependencies = [ "alloy-primitives 0.8.0", "async-trait", @@ -597,9 +597,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6b19bbb231c7f941af07f363d4c74d356dfcdfcd7dfa85a41a504ae856a6d5" +checksum = "7c640f9343e8f741f837c345c5ea30239ba77938b3691b884c736834853bd16c" dependencies = [ "alloy-consensus", "alloy-network", @@ -685,9 +685,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "454220c714857cf68af87d788d1f0638ad8766268b94f6a49fed96cbc2ab382c" +checksum = "2799749ca692ae145f54968778877afd7c95e788488f176cfdfcf2a8abeb2062" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -704,9 +704,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377f2353d7fea03a2dba6b9ffbb7d610402c040dd5700d1fae8b9ec2673eed9b" +checksum = "bc10c4dd932f66e0db6cc5735241e0c17a6a18564b430bbc1839f7db18587a93" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -719,9 +719,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8653c47dcc30326fb09a34140e8800fa21987fc52453de6cfcdd5c7b8b6e9886" +checksum = "38f39f88798c3282914079a3eda3ea8b9fbf21e383a0ce85958b4f1c170d222f" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -738,9 +738,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d43ba8e9a3a7fef626d5fd93cc87ff2d6d2c81acfb866f068b3dce31dda060" +checksum = "65e732028930aa17b7edd464a9711365417635e984028fcc7176393ccea22c00" dependencies = [ "alloy-pubsub", "alloy-transport", diff --git a/Cargo.toml b/Cargo.toml index 2f1cb72c0..050f71a23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -415,38 +415,38 @@ alloy-rlp = "0.3.4" alloy-sol-types = "0.8.0" alloy-trie = { version = "0.5", default-features = false } -alloy-consensus = { version = "0.3.0", default-features = false } -alloy-eips = { version = "0.3.0", default-features = false } -alloy-genesis = { version = "0.3.0", default-features = false } -alloy-json-rpc = { version = "0.3.0", default-features = false } -alloy-network = { version = "0.3.0", default-features = false } -alloy-node-bindings = { version = "0.3.0", default-features = false } -alloy-provider = { version = "0.3.0", features = [ +alloy-consensus = { version = "0.3.1", default-features = false } +alloy-eips = { version = "0.3.1", default-features = false } +alloy-genesis = { version = "0.3.1", default-features = false } +alloy-json-rpc = { version = "0.3.1", default-features = false } +alloy-network = { version = "0.3.1", default-features = false } +alloy-node-bindings = { version = "0.3.1", default-features = false } +alloy-provider = { version = "0.3.1", features = [ "reqwest", ], default-features = false } -alloy-pubsub = { version = "0.3.0", default-features = false } -alloy-rpc-client = { version = "0.3.0", default-features = false } -alloy-rpc-types = { version = "0.3.0", features = [ +alloy-pubsub = { version = "0.3.1", default-features = false } +alloy-rpc-client = { version = "0.3.1", default-features = false } +alloy-rpc-types = { version = "0.3.1", features = [ "eth", ], default-features = false } -alloy-rpc-types-admin = { version = "0.3.0", default-features = false } -alloy-rpc-types-anvil = { version = "0.3.0", default-features = false } -alloy-rpc-types-beacon = { version = "0.3.0", default-features = false } -alloy-rpc-types-debug = { version = "0.3.0", default-features = false } -alloy-rpc-types-engine = { version = "0.3.0", default-features = false } -alloy-rpc-types-eth = { version = "0.3.0", default-features = false } -alloy-rpc-types-mev = { version = "0.3.0", default-features = false } -alloy-rpc-types-trace = { version = "0.3.0", default-features = false } -alloy-rpc-types-txpool = { version = "0.3.0", default-features = false } -alloy-serde = { version = "0.3.0", default-features = false } -alloy-signer = { version = "0.3.0", default-features = false } -alloy-signer-local = { version = "0.3.0", default-features = false } -alloy-transport = { version = "0.3.0" } -alloy-transport-http = { version = "0.3.0", features = [ +alloy-rpc-types-admin = { version = "0.3.1", default-features = false } +alloy-rpc-types-anvil = { version = "0.3.1", default-features = false } +alloy-rpc-types-beacon = { version = "0.3.1", default-features = false } +alloy-rpc-types-debug = { version = "0.3.1", default-features = false } +alloy-rpc-types-engine = { version = "0.3.1", default-features = false } +alloy-rpc-types-eth = { version = "0.3.1", default-features = false } +alloy-rpc-types-mev = { version = "0.3.1", default-features = false } +alloy-rpc-types-trace = { version = "0.3.1", default-features = false } +alloy-rpc-types-txpool = { version = "0.3.1", default-features = false } +alloy-serde = { version = "0.3.1", default-features = false } +alloy-signer = { version = "0.3.1", default-features = false } +alloy-signer-local = { version = "0.3.1", default-features = false } +alloy-transport = { version = "0.3.1" } +alloy-transport-http = { version = "0.3.1", features = [ "reqwest-rustls-tls", ], default-features = false } -alloy-transport-ipc = { version = "0.3.0", default-features = false } -alloy-transport-ws = { version = "0.3.0", default-features = false } +alloy-transport-ipc = { version = "0.3.1", default-features = false } +alloy-transport-ws = { version = "0.3.1", default-features = false } # op op-alloy-rpc-types = "0.2.5" diff --git a/crates/primitives/src/transaction/compat.rs b/crates/primitives/src/transaction/compat.rs index dfe0083e4..60c32b0da 100644 --- a/crates/primitives/src/transaction/compat.rs +++ b/crates/primitives/src/transaction/compat.rs @@ -78,7 +78,7 @@ impl FillTxEnv for TransactionSigned { tx_env.authorization_list = None; } Transaction::Eip7702(tx) => { - tx_env.gas_limit = tx.gas_limit; + tx_env.gas_limit = tx.gas_limit as u64; 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; diff --git a/crates/primitives/src/transaction/eip7702.rs b/crates/primitives/src/transaction/eip7702.rs index 0445a1eac..8ebcf086b 100644 --- a/crates/primitives/src/transaction/eip7702.rs +++ b/crates/primitives/src/transaction/eip7702.rs @@ -1,311 +1 @@ -use super::access_list::AccessList; -use crate::{ - eip7702::SignedAuthorization, keccak256, Bytes, ChainId, Signature, TxKind, TxType, B256, U256, -}; -use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; -use core::mem; - -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; -use serde::{Deserialize, Serialize}; - -#[cfg(any(test, feature = "reth-codec"))] -use reth_codecs::Compact; - -/// [EIP-7702 Set Code Transaction](https://eips.ethereum.org/EIPS/eip-7702) -/// -/// Set EOA account code for one transaction -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -#[cfg_attr(any(test, feature = "reth-codec"), derive(Compact))] -#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))] -pub struct TxEip7702 { - /// Added as EIP-155: Simple replay attack protection - pub chain_id: ChainId, - /// A scalar value equal to the number of transactions sent by the sender; formally Tn. - pub nonce: u64, - /// A scalar value equal to the number of - /// Wei to be paid per unit of gas for all computation - /// costs incurred as a result of the execution of this transaction; formally Tp. - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - pub gas_limit: u64, - /// A scalar value equal to the maximum - /// amount of gas that should be used in executing - /// this transaction. This is paid up-front, before any - /// computation is done and may not be increased - /// later; formally Tg. - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - /// - /// This is also known as `GasFeeCap` - pub max_fee_per_gas: u128, - /// Max Priority fee that transaction is paying - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - /// - /// This is also known as `GasTipCap` - pub max_priority_fee_per_gas: u128, - /// The 160-bit address of the message call’s recipient or, for a contract creation - /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. - pub to: TxKind, - /// A scalar value equal to the number of Wei to - /// be transferred to the message call’s recipient or, - /// in the case of contract creation, as an endowment - /// to the newly created account; formally Tv. - pub value: U256, - /// The accessList specifies a list of addresses and storage keys; - /// these addresses and storage keys are added into the `accessed_addresses` - /// and `accessed_storage_keys` global sets (introduced in EIP-2929). - /// A gas cost is charged, though at a discount relative to the cost of - /// accessing outside the list. - pub access_list: AccessList, - /// Authorizations are used to temporarily set the code of its signer to - /// the code referenced by `address`. These also include a `chain_id` (which - /// can be set to zero and not evaluated) as well as an optional `nonce`. - pub authorization_list: Vec, - /// Input has two uses depending if the transaction `to` field is [`TxKind::Create`] or - /// [`TxKind::Call`]. - /// - /// Input as init code, or if `to` is [`TxKind::Create`]: An unlimited size byte array - /// specifying the EVM-code for the account initialisation procedure `CREATE` - /// - /// Input as data, or if `to` is [`TxKind::Call`]: An unlimited size byte array specifying the - /// input data of the message call, formally Td. - pub input: Bytes, -} - -impl TxEip7702 { - /// Returns the effective gas price for the given `base_fee`. - pub const fn effective_gas_price(&self, base_fee: Option) -> u128 { - match base_fee { - None => self.max_fee_per_gas, - Some(base_fee) => { - // if the tip is greater than the max priority fee per gas, set it to the max - // priority fee per gas + base fee - let tip = self.max_fee_per_gas.saturating_sub(base_fee as u128); - if tip > self.max_priority_fee_per_gas { - self.max_priority_fee_per_gas + base_fee as u128 - } else { - // otherwise return the max fee per gas - self.max_fee_per_gas - } - } - } - } - - /// Calculates a heuristic for the in-memory size of the [`TxEip7702`] transaction. - #[inline] - pub fn size(&self) -> usize { - mem::size_of::() + // chain_id - mem::size_of::() + // nonce - mem::size_of::() + // gas_price - mem::size_of::() + // gas_limit - self.to.size() + // to - mem::size_of::() + // value - self.access_list.size() + // access_list - mem::size_of::() - * self.authorization_list.capacity() + // authorization_list - self.input.len() // input - } - - /// Decodes the inner [`TxEip7702`] fields from RLP bytes. - /// - /// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following - /// RLP fields in the following order: - /// - /// - `chain_id` - /// - `nonce` - /// - `gas_price` - /// - `gas_limit` - /// - `to` - /// - `value` - /// - `data` (`input`) - /// - `access_list` - /// - `authorization_list` - pub(crate) fn decode_inner(buf: &mut &[u8]) -> alloy_rlp::Result { - Ok(Self { - chain_id: Decodable::decode(buf)?, - nonce: Decodable::decode(buf)?, - max_priority_fee_per_gas: Decodable::decode(buf)?, - max_fee_per_gas: Decodable::decode(buf)?, - gas_limit: Decodable::decode(buf)?, - to: Decodable::decode(buf)?, - value: Decodable::decode(buf)?, - input: Decodable::decode(buf)?, - access_list: Decodable::decode(buf)?, - authorization_list: Decodable::decode(buf)?, - }) - } - - /// Outputs the length of the transaction's fields, without a RLP header. - pub(crate) fn fields_len(&self) -> usize { - self.chain_id.length() + - self.nonce.length() + - self.max_priority_fee_per_gas.length() + - self.max_fee_per_gas.length() + - self.gas_limit.length() + - self.to.length() + - self.value.length() + - self.input.0.length() + - self.access_list.length() + - self.authorization_list.length() - } - - /// Encodes only the transaction's fields into the desired buffer, without a RLP header. - pub(crate) fn encode_fields(&self, out: &mut dyn bytes::BufMut) { - self.chain_id.encode(out); - self.nonce.encode(out); - self.max_priority_fee_per_gas.encode(out); - self.max_fee_per_gas.encode(out); - self.gas_limit.encode(out); - self.to.encode(out); - self.value.encode(out); - self.input.0.encode(out); - self.access_list.encode(out); - self.authorization_list.encode(out); - } - - /// Inner encoding function that is used for both rlp [`Encodable`] trait and for calculating - /// hash that for eip2718 does not require rlp header - /// - /// This encodes the transaction as: - /// `rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, - /// value, data, access_list, authorization_list, signature_y_parity, signature_r, - /// signature_s])` - pub(crate) fn encode_with_signature( - &self, - signature: &Signature, - out: &mut dyn bytes::BufMut, - with_header: bool, - ) { - let payload_length = self.fields_len() + signature.payload_len(); - if with_header { - Header { - list: false, - payload_length: 1 + length_of_length(payload_length) + payload_length, - } - .encode(out); - } - out.put_u8(self.tx_type() as u8); - let header = Header { list: true, payload_length }; - header.encode(out); - self.encode_fields(out); - signature.encode(out); - } - - /// Output the length of the RLP signed transaction encoding, _without_ a RLP string header. - pub(crate) fn payload_len_with_signature_without_header(&self, signature: &Signature) -> usize { - let payload_length = self.fields_len() + signature.payload_len(); - // 'transaction type byte length' + 'header length' + 'payload length' - 1 + length_of_length(payload_length) + payload_length - } - - /// Output the length of the RLP signed transaction encoding. This encodes with a RLP header. - pub(crate) fn payload_len_with_signature(&self, signature: &Signature) -> usize { - let len = self.payload_len_with_signature_without_header(signature); - length_of_length(len) + len - } - - /// Get transaction type - pub(crate) const fn tx_type(&self) -> TxType { - TxType::Eip7702 - } - - /// Encodes the EIP-7702 transaction in RLP for signing. - /// - /// This encodes the transaction as: - /// `tx_type || rlp(chain_id, nonce, gas_price, gas_limit, to, value, input, access_list, - /// authorization_list)` - /// - /// Note that there is no rlp header before the transaction type byte. - pub(crate) fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) { - out.put_u8(self.tx_type() as u8); - Header { list: true, payload_length: self.fields_len() }.encode(out); - self.encode_fields(out); - } - - /// Outputs the length of the signature RLP encoding for the transaction. - pub(crate) fn payload_len_for_signature(&self) -> usize { - let payload_length = self.fields_len(); - // 'transaction type byte length' + 'header length' + 'payload length' - 1 + length_of_length(payload_length) + payload_length - } - - /// Outputs the signature hash of the transaction by first encoding without a signature, then - /// hashing. - pub(crate) fn signature_hash(&self) -> B256 { - let mut buf = Vec::with_capacity(self.payload_len_for_signature()); - self.encode_for_signing(&mut buf); - keccak256(&buf) - } -} - -#[cfg(test)] -mod tests { - use super::TxEip7702; - use crate::{ - transaction::{signature::Signature, TxKind}, - Address, Bytes, Transaction, TransactionSigned, U256, - }; - use alloy_rlp::{Decodable, Encodable}; - - #[test] - fn test_decode_create() { - // tests that a contract creation tx encodes and decodes properly - let request = Transaction::Eip7702(TxEip7702 { - chain_id: 1u64, - nonce: 0, - max_fee_per_gas: 0x4a817c800, - max_priority_fee_per_gas: 0x3b9aca00, - gas_limit: 2, - to: TxKind::Create, - value: U256::ZERO, - input: Bytes::from(vec![1, 2]), - access_list: Default::default(), - authorization_list: Default::default(), - }); - let signature = Signature { odd_y_parity: true, r: U256::default(), s: U256::default() }; - let tx = TransactionSigned::from_transaction_and_signature(request, signature); - - let mut encoded = Vec::new(); - tx.encode(&mut encoded); - assert_eq!(encoded.len(), tx.length()); - - let decoded = TransactionSigned::decode(&mut &*encoded).unwrap(); - assert_eq!(decoded, tx); - } - - #[test] - fn test_decode_call() { - let request = Transaction::Eip7702(TxEip7702 { - chain_id: 1u64, - nonce: 0, - max_fee_per_gas: 0x4a817c800, - max_priority_fee_per_gas: 0x3b9aca00, - gas_limit: 2, - to: Address::default().into(), - value: U256::ZERO, - input: Bytes::from(vec![1, 2]), - access_list: Default::default(), - authorization_list: Default::default(), - }); - - let signature = Signature { odd_y_parity: true, r: U256::default(), s: U256::default() }; - - let tx = TransactionSigned::from_transaction_and_signature(request, signature); - - let mut encoded = Vec::new(); - tx.encode(&mut encoded); - assert_eq!(encoded.len(), tx.length()); - - let decoded = TransactionSigned::decode(&mut &*encoded).unwrap(); - assert_eq!(decoded, tx); - } -} +pub use alloy_consensus::transaction::TxEip7702; diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index b580258f3..a176f14bd 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -165,7 +165,11 @@ impl<'a> arbitrary::Arbitrary<'a> for Transaction { Self::Eip4844(tx) } - TxType::Eip7702 => Self::Eip7702(TxEip7702::arbitrary(u)?), + TxType::Eip7702 => { + let mut tx = TxEip7702::arbitrary(u)?; + tx.gas_limit = (tx.gas_limit as u64).into(); + Self::Eip7702(tx) + } #[cfg(feature = "optimism")] TxType::Deposit => Self::Deposit(TxDeposit::arbitrary(u)?), }) @@ -244,7 +248,7 @@ impl Transaction { Self::Eip2930(_) => TxType::Eip2930, Self::Eip1559(_) => TxType::Eip1559, Self::Eip4844(_) => TxType::Eip4844, - Self::Eip7702(set_code_tx) => set_code_tx.tx_type(), + Self::Eip7702(_) => TxType::Eip7702, #[cfg(feature = "optimism")] Self::Deposit(deposit_tx) => deposit_tx.tx_type(), } @@ -308,7 +312,7 @@ impl Transaction { Self::Legacy(TxLegacy { gas_limit, .. }) => *gas_limit as u64, Self::Eip1559(TxEip1559 { gas_limit, .. }) => *gas_limit as u64, Self::Eip4844(TxEip4844 { gas_limit, .. }) => *gas_limit as u64, - Self::Eip7702(TxEip7702 { gas_limit, .. }) => *gas_limit, + Self::Eip7702(TxEip7702 { gas_limit, .. }) => *gas_limit as u64, Self::Eip2930(TxEip2930 { gas_limit, .. }) => *gas_limit as u64, #[cfg(feature = "optimism")] Self::Deposit(TxDeposit { gas_limit, .. }) => *gas_limit, @@ -546,9 +550,11 @@ impl Transaction { out, with_header, ), - Self::Eip7702(set_code_tx) => { - set_code_tx.encode_with_signature(signature, out, with_header) - } + Self::Eip7702(set_code_tx) => set_code_tx.encode_with_signature( + &signature.as_signature_with_boolean_parity(), + out, + with_header, + ), #[cfg(feature = "optimism")] Self::Deposit(deposit_tx) => deposit_tx.encode(out, with_header), } @@ -561,7 +567,7 @@ impl Transaction { Self::Eip2930(tx) => tx.gas_limit = gas_limit.into(), Self::Eip1559(tx) => tx.gas_limit = gas_limit.into(), Self::Eip4844(tx) => tx.gas_limit = gas_limit.into(), - Self::Eip7702(tx) => tx.gas_limit = gas_limit, + Self::Eip7702(tx) => tx.gas_limit = gas_limit.into(), #[cfg(feature = "optimism")] Self::Deposit(tx) => tx.gas_limit = gas_limit, } @@ -1232,9 +1238,10 @@ impl TransactionSigned { &self.signature.as_signature_with_boolean_parity(), true, ), - Transaction::Eip7702(set_code_tx) => { - set_code_tx.payload_len_with_signature(&self.signature) - } + Transaction::Eip7702(set_code_tx) => set_code_tx.encoded_len_with_signature( + &self.signature.as_signature_with_boolean_parity(), + true, + ), #[cfg(feature = "optimism")] Transaction::Deposit(deposit_tx) => deposit_tx.payload_len(), } @@ -1361,7 +1368,7 @@ impl TransactionSigned { 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_inner(data)?), + TxType::Eip7702 => Transaction::Eip7702(TxEip7702::decode_fields(data)?), #[cfg(feature = "optimism")] TxType::Deposit => Transaction::Deposit(TxDeposit::decode_inner(data)?), TxType::Legacy => return Err(RlpError::Custom("unexpected legacy tx type")), @@ -1443,9 +1450,10 @@ impl TransactionSigned { &self.signature.as_signature_with_boolean_parity(), false, ), - Transaction::Eip7702(set_code_tx) => { - set_code_tx.payload_len_with_signature_without_header(&self.signature) - } + Transaction::Eip7702(set_code_tx) => set_code_tx.encoded_len_with_signature( + &self.signature.as_signature_with_boolean_parity(), + false, + ), #[cfg(feature = "optimism")] Transaction::Deposit(deposit_tx) => deposit_tx.payload_len_without_header(), } diff --git a/crates/primitives/src/transaction/pooled.rs b/crates/primitives/src/transaction/pooled.rs index 5e0efcbff..1ef05577a 100644 --- a/crates/primitives/src/transaction/pooled.rs +++ b/crates/primitives/src/transaction/pooled.rs @@ -326,7 +326,10 @@ impl PooledTransactionsElement { } Self::Eip7702 { transaction, signature, .. } => { // method computes the payload len without a RLP header - transaction.payload_len_with_signature_without_header(signature) + transaction.encoded_len_with_signature( + &signature.as_signature_with_boolean_parity(), + false, + ) } Self::BlobTransaction(blob_tx) => { // the encoding does not use a header, so we set `with_header` to false @@ -375,9 +378,11 @@ impl PooledTransactionsElement { out, false, ), - Self::Eip7702 { transaction, signature, .. } => { - transaction.encode_with_signature(signature, out, false) - } + Self::Eip7702 { transaction, signature, .. } => transaction.encode_with_signature( + &signature.as_signature_with_boolean_parity(), + out, + false, + ), Self::BlobTransaction(blob_tx) => { // The inner encoding is used with `with_header` set to true, making the final // encoding: @@ -520,7 +525,11 @@ impl Encodable for PooledTransactionsElement { } Self::Eip7702 { transaction, signature, .. } => { // encodes with string header - transaction.encode_with_signature(signature, out, true) + transaction.encode_with_signature( + &signature.as_signature_with_boolean_parity(), + out, + true, + ) } Self::BlobTransaction(blob_tx) => { // The inner encoding is used with `with_header` set to true, making the final @@ -551,7 +560,8 @@ impl Encodable for PooledTransactionsElement { } Self::Eip7702 { transaction, signature, .. } => { // method computes the payload len with a RLP header - transaction.payload_len_with_signature(signature) + transaction + .encoded_len_with_signature(&signature.as_signature_with_boolean_parity(), true) } Self::BlobTransaction(blob_tx) => { // the encoding uses a header, so we set `with_header` to true diff --git a/crates/storage/codecs/src/alloy/transaction/eip7702.rs b/crates/storage/codecs/src/alloy/transaction/eip7702.rs new file mode 100644 index 000000000..69f65d522 --- /dev/null +++ b/crates/storage/codecs/src/alloy/transaction/eip7702.rs @@ -0,0 +1,71 @@ +use crate::Compact; +use alloy_consensus::transaction::TxEip7702 as AlloyTxEip7702; +use alloy_eips::{eip2930::AccessList, eip7702::SignedAuthorization}; +use alloy_primitives::{Bytes, ChainId, TxKind, U256}; +use reth_codecs_derive::add_arbitrary_tests; +use serde::{Deserialize, Serialize}; + +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +/// [EIP-7702 Set Code Transaction](https://eips.ethereum.org/EIPS/eip-7702) +/// +/// This is a helper type to use derive on it instead of manually managing `bitfield`. +/// +/// By deriving `Compact` here, any future changes or enhancements to the `Compact` derive +/// will automatically apply to this type. +/// +/// Notice: Make sure this struct is 1:1 with [`alloy_consensus::transaction::TxEip7702`] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize, Compact)] +#[cfg_attr(test, derive(arbitrary::Arbitrary))] +#[add_arbitrary_tests(compact)] +pub(crate) struct TxEip7702 { + chain_id: ChainId, + nonce: u64, + gas_limit: u64, + max_fee_per_gas: u128, + max_priority_fee_per_gas: u128, + to: TxKind, + value: U256, + access_list: AccessList, + authorization_list: Vec, + input: Bytes, +} + +impl Compact for AlloyTxEip7702 { + fn to_compact(&self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + let tx = TxEip7702 { + chain_id: self.chain_id, + nonce: self.nonce, + max_fee_per_gas: self.max_fee_per_gas, + max_priority_fee_per_gas: self.max_priority_fee_per_gas, + gas_limit: self.gas_limit as u64, + to: self.to, + value: self.value, + input: self.input.clone(), + access_list: self.access_list.clone(), + authorization_list: self.authorization_list.clone(), + }; + tx.to_compact(buf) + } + + fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { + let (tx, _) = TxEip7702::from_compact(buf, len); + let alloy_tx = Self { + chain_id: tx.chain_id, + nonce: tx.nonce, + max_fee_per_gas: tx.max_fee_per_gas, + max_priority_fee_per_gas: tx.max_priority_fee_per_gas, + gas_limit: tx.gas_limit as u128, + to: tx.to, + value: tx.value, + input: tx.input, + access_list: tx.access_list, + authorization_list: tx.authorization_list, + }; + (alloy_tx, buf) + } +} diff --git a/crates/storage/codecs/src/alloy/transaction/mod.rs b/crates/storage/codecs/src/alloy/transaction/mod.rs index 65ac7be57..138ffa6c8 100644 --- a/crates/storage/codecs/src/alloy/transaction/mod.rs +++ b/crates/storage/codecs/src/alloy/transaction/mod.rs @@ -1,6 +1,7 @@ mod eip1559; mod eip2930; mod eip4844; +mod eip7702; mod legacy; #[cfg(test)] @@ -12,7 +13,10 @@ mod tests { // this check is to ensure we do not inadvertently add too many fields to a struct which would // expand the flags field and break backwards compatibility - use super::{eip1559::TxEip1559, eip2930::TxEip2930, eip4844::TxEip4844, legacy::TxLegacy}; + use super::{ + eip1559::TxEip1559, eip2930::TxEip2930, eip4844::TxEip4844, eip7702::TxEip7702, + legacy::TxLegacy, + }; #[test] fn test_ensure_backwards_compatibility() { @@ -20,5 +24,6 @@ mod tests { assert_eq!(TxLegacy::bitflag_encoded_bytes(), 3); assert_eq!(TxEip1559::bitflag_encoded_bytes(), 4); assert_eq!(TxEip2930::bitflag_encoded_bytes(), 3); + assert_eq!(TxEip7702::bitflag_encoded_bytes(), 4); } }