diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index dc758a52aad7..1084945de5af 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -7,7 +7,9 @@ use alloy_consensus::{ TxEnvelope, TxLegacy, TxReceipt, TxType, }; use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Encodable2718}; -use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, TxKind, B256, U256, U64}; +use alloy_primitives::{ + Address, Bloom, Bytes, Log, Parity, Signature, TxHash, TxKind, B256, U256, U64, +}; use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; use alloy_rpc_types::{ request::TransactionRequest, trace::otterscan::OtsReceipt, AccessList, AnyTransactionReceipt, @@ -25,13 +27,6 @@ use std::ops::{Deref, Mul}; pub mod optimism; -/// The signature used to bypass signing via the `eth_sendUnsignedTransaction` cheat RPC -#[cfg(feature = "impersonated-tx")] -pub fn impersonated_signature() -> Signature { - Signature::from_scalars_and_parity(B256::with_last_byte(1), B256::with_last_byte(1), false) - .unwrap() -} - /// Converts a [TransactionRequest] into a [TypedTransactionRequest]. /// Should be removed once the call builder abstraction for providers is in place. pub fn transaction_request_to_typed( @@ -203,16 +198,23 @@ impl MaybeImpersonatedTransaction { self.transaction.recover() } + /// Returns whether the transaction is impersonated + /// + /// Note: this is feature gated so it does not conflict with the `Deref`ed + /// [TypedTransaction::hash] function by default. + #[cfg(feature = "impersonated-tx")] + pub fn is_impersonated(&self) -> bool { + self.impersonated_sender.is_some() + } + /// Returns the hash of the transaction /// /// Note: this is feature gated so it does not conflict with the `Deref`ed /// [TypedTransaction::hash] function by default. #[cfg(feature = "impersonated-tx")] pub fn hash(&self) -> B256 { - if self.transaction.is_impersonated() { - if let Some(sender) = self.impersonated_sender { - return self.transaction.impersonated_hash(sender); - } + if let Some(sender) = self.impersonated_sender { + return self.transaction.impersonated_hash(sender) } self.transaction.hash() } @@ -288,7 +290,7 @@ pub fn to_alloy_transaction_with_hash_and_sender( signature: Some(RpcSignature { r: t.signature().r(), s: t.signature().s(), - v: U256::from(t.signature().v().y_parity_byte()), + v: U256::from(t.signature().v().to_u64()), y_parity: None, }), access_list: None, @@ -315,7 +317,7 @@ pub fn to_alloy_transaction_with_hash_and_sender( signature: Some(RpcSignature { r: t.signature().r(), s: t.signature().s(), - v: U256::from(t.signature().v().y_parity_byte()), + v: U256::from(t.signature().v().to_u64()), y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), }), access_list: Some(t.tx().access_list.clone()), @@ -342,7 +344,7 @@ pub fn to_alloy_transaction_with_hash_and_sender( signature: Some(RpcSignature { r: t.signature().r(), s: t.signature().s(), - v: U256::from(t.signature().v().y_parity_byte()), + v: U256::from(t.signature().v().to_u64()), y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), }), access_list: Some(t.tx().access_list.clone()), @@ -369,7 +371,7 @@ pub fn to_alloy_transaction_with_hash_and_sender( signature: Some(RpcSignature { r: t.signature().r(), s: t.signature().s(), - v: U256::from(t.signature().v().y_parity_byte()), + v: U256::from(t.signature().v().to_u64()), y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), }), access_list: Some(t.tx().tx().access_list.clone()), @@ -832,12 +834,6 @@ impl TypedTransaction { } } - /// Returns true if the transaction was impersonated (using the impersonate Signature) - #[cfg(feature = "impersonated-tx")] - pub fn is_impersonated(&self) -> bool { - self.signature() == impersonated_signature() - } - /// Returns the hash if the transaction is impersonated (using a fake signature) /// /// This appends the `address` before hashing it @@ -886,7 +882,7 @@ impl TypedTransaction { Self::Deposit(_) => Signature::from_scalars_and_parity( B256::with_last_byte(1), B256::with_last_byte(1), - false, + Parity::Parity(false), ) .unwrap(), } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 346fb27bd1dd..150e54b068b9 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -35,7 +35,7 @@ use alloy_consensus::{transaction::eip4844::TxEip4844Variant, TxEnvelope}; use alloy_dyn_abi::TypedData; use alloy_eips::eip2718::Encodable2718; use alloy_network::eip2718::Decodable2718; -use alloy_primitives::{Address, Bytes, TxHash, TxKind, B256, B64, U256, U64}; +use alloy_primitives::{Address, Bytes, Parity, TxHash, TxKind, B256, B64, U256, U64}; use alloy_rpc_types::{ anvil::{ ForkedNetwork, Forking, Metadata, MineOptions, NodeEnvironment, NodeForkConfig, NodeInfo, @@ -52,6 +52,7 @@ use alloy_rpc_types::{ Transaction, }; use alloy_serde::WithOtherFields; +use alloy_signer::Signature; use alloy_transport::TransportErrorKind; use anvil_core::{ eth::{ @@ -448,7 +449,7 @@ impl EthApi { alloy_primitives::Signature::from_scalars_and_parity( B256::with_last_byte(1), B256::with_last_byte(1), - false, + Parity::Parity(false), ) .unwrap(); return build_typed_transaction(request, nil_signature) @@ -937,7 +938,7 @@ impl EthApi { // if the sender is currently impersonated we need to "bypass" signing let pending_transaction = if self.is_impersonated(from) { - let bypass_signature = self.backend.cheats().bypass_signature(); + let bypass_signature = self.impersonated_signature(&request); let transaction = sign::build_typed_transaction(request, bypass_signature)?; self.ensure_typed_transaction_supported(&transaction)?; trace!(target : "node", ?from, "eth_sendTransaction: impersonating"); @@ -2084,7 +2085,7 @@ impl EthApi { let request = self.build_typed_tx_request(request, nonce)?; - let bypass_signature = self.backend.cheats().bypass_signature(); + let bypass_signature = self.impersonated_signature(&request); let transaction = sign::build_typed_transaction(request, bypass_signature)?; self.ensure_typed_transaction_supported(&transaction)?; @@ -2581,6 +2582,28 @@ impl EthApi { self.backend.cheats().is_impersonated(addr) } + /// The signature used to bypass signing via the `eth_sendUnsignedTransaction` cheat RPC + fn impersonated_signature(&self, request: &TypedTransactionRequest) -> Signature { + match request { + // Only the legacy transaction type requires v to be in {27, 28}, thus + // requiring the use of Parity::NonEip155 + TypedTransactionRequest::Legacy(_) => Signature::from_scalars_and_parity( + B256::with_last_byte(1), + B256::with_last_byte(1), + Parity::NonEip155(false), + ), + TypedTransactionRequest::EIP2930(_) | + TypedTransactionRequest::EIP1559(_) | + TypedTransactionRequest::EIP4844(_) | + TypedTransactionRequest::Deposit(_) => Signature::from_scalars_and_parity( + B256::with_last_byte(1), + B256::with_last_byte(1), + Parity::Parity(false), + ), + } + .unwrap() + } + /// Returns the nonce of the `address` depending on the `block_number` async fn get_transaction_count( &self, diff --git a/crates/anvil/src/eth/backend/cheats.rs b/crates/anvil/src/eth/backend/cheats.rs index dbc58670f62f..5b498f963c84 100644 --- a/crates/anvil/src/eth/backend/cheats.rs +++ b/crates/anvil/src/eth/backend/cheats.rs @@ -1,7 +1,6 @@ //! Support for "cheat codes" / bypass functions -use alloy_primitives::{Address, Signature}; -use anvil_core::eth::transaction::impersonated_signature; +use alloy_primitives::Address; use parking_lot::RwLock; use std::{collections::HashSet, sync::Arc}; @@ -48,11 +47,6 @@ impl CheatsManager { } } - /// Returns the signature to use to bypass transaction signing - pub fn bypass_signature(&self) -> Signature { - self.state.read().bypass_signature - } - /// Sets the auto impersonation flag which if set to true will make the `is_impersonated` /// function always return true pub fn set_auto_impersonate_account(&self, enabled: bool) { @@ -67,22 +61,10 @@ impl CheatsManager { } /// Container type for all the state variables -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct CheatsState { /// All accounts that are currently impersonated pub impersonated_accounts: HashSet
, - /// The signature used for the `eth_sendUnsignedTransaction` cheat code - pub bypass_signature: Signature, /// If set to true will make the `is_impersonated` function always return true pub auto_impersonate_accounts: bool, } - -impl Default for CheatsState { - fn default() -> Self { - Self { - impersonated_accounts: Default::default(), - bypass_signature: impersonated_signature(), - auto_impersonate_accounts: false, - } - } -}