Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

anvil/eth: Use the raw v signature value instead of bool #7918

Merged
merged 4 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 19 additions & 23 deletions crates/anvil/core/src/eth/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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(
Expand Down Expand Up @@ -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()
}
Expand Down Expand Up @@ -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,
Expand All @@ -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()),
Expand All @@ -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()),
Expand All @@ -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()),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(),
}
Expand Down
31 changes: 27 additions & 4 deletions crates/anvil/src/eth/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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::{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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,
Expand Down
22 changes: 2 additions & 20 deletions crates/anvil/src/eth/backend/cheats.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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) {
Expand All @@ -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<Address>,
/// 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,
}
}
}
Loading