From 5aab75287fe2a96d6d9fedf7c1db0df8dd7e4a5a Mon Sep 17 00:00:00 2001 From: Agost Biro <5764438+agostbiro@users.noreply.github.com> Date: Thu, 22 Feb 2024 16:40:10 +0100 Subject: [PATCH] fix: use custom address recovery for fake signature (#4890) --- crates/edr_eth/src/transaction.rs | 1 + .../{request => }/fake_signature.rs | 21 ++++++ crates/edr_eth/src/transaction/request.rs | 1 - .../edr_eth/src/transaction/request/eip155.rs | 7 +- .../src/transaction/request/eip1559.rs | 6 +- .../src/transaction/request/eip2930.rs | 6 +- .../src/transaction/request/eip4844.rs | 6 +- .../edr_eth/src/transaction/request/legacy.rs | 7 +- crates/edr_eth/src/transaction/signed.rs | 10 +++ .../edr_eth/src/transaction/signed/eip155.rs | 14 +++- .../edr_eth/src/transaction/signed/eip1559.rs | 14 +++- .../edr_eth/src/transaction/signed/eip2930.rs | 14 +++- .../edr_eth/src/transaction/signed/eip4844.rs | 12 +++- .../edr_eth/src/transaction/signed/legacy.rs | 13 +++- crates/edr_evm/src/transaction/executable.rs | 5 ++ .../src/requests/eth/transactions.rs | 64 +++++++++++++++++++ 16 files changed, 183 insertions(+), 18 deletions(-) rename crates/edr_eth/src/transaction/{request => }/fake_signature.rs (78%) diff --git a/crates/edr_eth/src/transaction.rs b/crates/edr_eth/src/transaction.rs index 66aab356ba..6294e30794 100644 --- a/crates/edr_eth/src/transaction.rs +++ b/crates/edr_eth/src/transaction.rs @@ -4,6 +4,7 @@ //! transaction related data +mod fake_signature; mod kind; mod request; mod signed; diff --git a/crates/edr_eth/src/transaction/request/fake_signature.rs b/crates/edr_eth/src/transaction/fake_signature.rs similarity index 78% rename from crates/edr_eth/src/transaction/request/fake_signature.rs rename to crates/edr_eth/src/transaction/fake_signature.rs index 0c0c369af9..2748d1c58f 100644 --- a/crates/edr_eth/src/transaction/request/fake_signature.rs +++ b/crates/edr_eth/src/transaction/fake_signature.rs @@ -26,6 +26,11 @@ pub(super) fn make_fake_signature(sender: &Address) -> Signature Signature { r, s, v } } +pub(super) fn recover_fake_signature(signature: &Signature) -> Address { + let address: [u8; 32] = signature.r.to_be_bytes(); + Address::from_slice(&address.as_slice()[12..]) +} + #[cfg(test)] pub(super) mod tests { macro_rules! test_fake_sign_properties { @@ -60,6 +65,22 @@ pub(super) mod tests { assert_ne!(hash_one, hash_two); } + + #[test] + fn recovers_fake_sender() { + let transaction_request = dummy_request(); + + // Fails to recover with signature error if tried to ecrocver a fake signature + let sender: Address = "0x67091a7dd65bf4f1e95af0a479fbc782b61c129a" + .parse() + .expect("valid address"); + + let signed_transaction = transaction_request.fake_sign(&sender); + + let recovered = signed_transaction.recover().expect("valid signature"); + + assert_eq!(recovered, sender); + } }; } diff --git a/crates/edr_eth/src/transaction/request.rs b/crates/edr_eth/src/transaction/request.rs index 67e25a6f3e..c558f76f05 100644 --- a/crates/edr_eth/src/transaction/request.rs +++ b/crates/edr_eth/src/transaction/request.rs @@ -2,7 +2,6 @@ mod eip155; mod eip1559; mod eip2930; mod eip4844; -mod fake_signature; mod legacy; use k256::SecretKey; diff --git a/crates/edr_eth/src/transaction/request/eip155.rs b/crates/edr_eth/src/transaction/request/eip155.rs index 4abeeaed23..1f0ebc30ef 100644 --- a/crates/edr_eth/src/transaction/request/eip155.rs +++ b/crates/edr_eth/src/transaction/request/eip155.rs @@ -7,8 +7,7 @@ use revm_primitives::keccak256; use crate::{ signature::{Signature, SignatureError}, transaction::{ - kind::TransactionKind, request::fake_signature::make_fake_signature, - signed::Eip155SignedTransaction, + fake_signature::make_fake_signature, kind::TransactionKind, signed::Eip155SignedTransaction, }, Address, Bytes, B256, U256, }; @@ -47,6 +46,7 @@ impl Eip155TransactionRequest { input: self.input, signature, hash: OnceLock::new(), + is_fake: false, }) } @@ -64,6 +64,7 @@ impl Eip155TransactionRequest { input: self.input, signature, hash: OnceLock::new(), + is_fake: true, } } @@ -132,7 +133,7 @@ mod tests { use std::str::FromStr; use super::*; - use crate::transaction::request::fake_signature::tests::test_fake_sign_properties; + use crate::transaction::fake_signature::tests::test_fake_sign_properties; fn dummy_request() -> Eip155TransactionRequest { let to = Address::from_str("0xc014ba5ec014ba5ec014ba5ec014ba5ec014ba5e").unwrap(); diff --git a/crates/edr_eth/src/transaction/request/eip1559.rs b/crates/edr_eth/src/transaction/request/eip1559.rs index 442ca05de6..8a6023c829 100644 --- a/crates/edr_eth/src/transaction/request/eip1559.rs +++ b/crates/edr_eth/src/transaction/request/eip1559.rs @@ -8,7 +8,7 @@ use crate::{ access_list::AccessListItem, signature::{Signature, SignatureError}, transaction::{ - kind::TransactionKind, request::fake_signature::make_fake_signature, + fake_signature::make_fake_signature, kind::TransactionKind, signed::Eip1559SignedTransaction, }, utils::envelop_bytes, @@ -56,6 +56,7 @@ impl Eip1559TransactionRequest { r: signature.r, s: signature.s, hash: OnceLock::new(), + is_fake: false, }) } @@ -76,6 +77,7 @@ impl Eip1559TransactionRequest { r: signature.r, s: signature.s, hash: OnceLock::new(), + is_fake: true, } } } @@ -101,7 +103,7 @@ pub(crate) mod tests { use std::str::FromStr; use super::*; - use crate::transaction::request::fake_signature::tests::test_fake_sign_properties; + use crate::transaction::fake_signature::tests::test_fake_sign_properties; fn dummy_request() -> Eip1559TransactionRequest { let to = Address::from_str("0xc014ba5ec014ba5ec014ba5ec014ba5ec014ba5e").unwrap(); diff --git a/crates/edr_eth/src/transaction/request/eip2930.rs b/crates/edr_eth/src/transaction/request/eip2930.rs index 27850de865..f2a27650e2 100644 --- a/crates/edr_eth/src/transaction/request/eip2930.rs +++ b/crates/edr_eth/src/transaction/request/eip2930.rs @@ -8,7 +8,7 @@ use crate::{ access_list::AccessListItem, signature::{Signature, SignatureError}, transaction::{ - kind::TransactionKind, request::fake_signature::make_fake_signature, + fake_signature::make_fake_signature, kind::TransactionKind, signed::Eip2930SignedTransaction, }, utils::envelop_bytes, @@ -55,6 +55,7 @@ impl Eip2930TransactionRequest { r: signature.r, s: signature.s, hash: OnceLock::new(), + is_fake: false, }) } @@ -75,6 +76,7 @@ impl Eip2930TransactionRequest { r: signature.r, s: signature.s, hash: OnceLock::new(), + is_fake: true, } } } @@ -99,7 +101,7 @@ mod tests { use std::str::FromStr; use super::*; - use crate::transaction::request::fake_signature::tests::test_fake_sign_properties; + use crate::transaction::fake_signature::tests::test_fake_sign_properties; fn dummy_request() -> Eip2930TransactionRequest { let to = Address::from_str("0xc014ba5ec014ba5ec014ba5ec014ba5ec014ba5e").unwrap(); diff --git a/crates/edr_eth/src/transaction/request/eip4844.rs b/crates/edr_eth/src/transaction/request/eip4844.rs index 86ab5cac8e..fe3ba4c9d1 100644 --- a/crates/edr_eth/src/transaction/request/eip4844.rs +++ b/crates/edr_eth/src/transaction/request/eip4844.rs @@ -7,7 +7,7 @@ use k256::SecretKey; use crate::{ access_list::AccessListItem, signature::{Signature, SignatureError}, - transaction::{request::fake_signature::make_fake_signature, Eip4844SignedTransaction}, + transaction::{fake_signature::make_fake_signature, Eip4844SignedTransaction}, utils::envelop_bytes, Address, Bytes, B256, U256, }; @@ -57,6 +57,7 @@ impl Eip4844TransactionRequest { r: signature.r, s: signature.s, hash: OnceLock::new(), + is_fake: false, }) } @@ -79,6 +80,7 @@ impl Eip4844TransactionRequest { r: signature.r, s: signature.s, hash: OnceLock::new(), + is_fake: true, } } } @@ -106,7 +108,7 @@ pub(crate) mod tests { use std::str::FromStr; use super::*; - use crate::transaction::request::fake_signature::tests::test_fake_sign_properties; + use crate::transaction::fake_signature::tests::test_fake_sign_properties; fn dummy_request() -> Eip4844TransactionRequest { // From https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/tx/test/eip4844.spec.ts#L68 diff --git a/crates/edr_eth/src/transaction/request/legacy.rs b/crates/edr_eth/src/transaction/request/legacy.rs index 9729deb297..4c69424e31 100644 --- a/crates/edr_eth/src/transaction/request/legacy.rs +++ b/crates/edr_eth/src/transaction/request/legacy.rs @@ -7,8 +7,7 @@ use k256::SecretKey; use crate::{ signature::{Signature, SignatureError}, transaction::{ - kind::TransactionKind, request::fake_signature::make_fake_signature, - signed::LegacySignedTransaction, + fake_signature::make_fake_signature, kind::TransactionKind, signed::LegacySignedTransaction, }, Address, Bytes, B256, U256, }; @@ -45,6 +44,7 @@ impl LegacyTransactionRequest { input: self.input, signature, hash: OnceLock::new(), + is_fake: false, }) } @@ -61,6 +61,7 @@ impl LegacyTransactionRequest { input: self.input, signature, hash: OnceLock::new(), + is_fake: true, } } } @@ -83,7 +84,7 @@ mod tests { use std::str::FromStr; use super::*; - use crate::transaction::request::fake_signature::tests::test_fake_sign_properties; + use crate::transaction::fake_signature::tests::test_fake_sign_properties; fn dummy_request() -> LegacyTransactionRequest { let to = Address::from_str("0xc014ba5ec014ba5ec014ba5ec014ba5ec014ba5e").unwrap(); diff --git a/crates/edr_eth/src/transaction/signed.rs b/crates/edr_eth/src/transaction/signed.rs index d17febabd5..e41aac72cd 100644 --- a/crates/edr_eth/src/transaction/signed.rs +++ b/crates/edr_eth/src/transaction/signed.rs @@ -438,6 +438,7 @@ mod tests { v: 1, }, hash: OnceLock::new(), + is_fake: false }), post_eip155 => SignedTransaction::PostEip155Legacy(Eip155SignedTransaction { nonce: 0, @@ -452,6 +453,7 @@ mod tests { v: 37, }, hash: OnceLock::new(), + is_fake: false }), eip2930 => SignedTransaction::Eip2930(Eip2930SignedTransaction { chain_id: 1, @@ -466,6 +468,7 @@ mod tests { s: U256::default(), access_list: vec![].into(), hash: OnceLock::new(), + is_fake: false }), eip1559 => SignedTransaction::Eip1559(Eip1559SignedTransaction { chain_id: 1, @@ -481,6 +484,7 @@ mod tests { r: U256::default(), s: U256::default(), hash: OnceLock::new(), + is_fake: false }), eip4844 => SignedTransaction::Eip4844(Eip4844SignedTransaction { chain_id: 1, @@ -498,6 +502,7 @@ mod tests { r: U256::default(), s: U256::default(), hash: OnceLock::new(), + is_fake: false }), } @@ -527,6 +532,7 @@ mod tests { .unwrap(), }, hash: OnceLock::new(), + is_fake: false, }); assert_eq!( expected, @@ -555,6 +561,7 @@ mod tests { .unwrap(), }, hash: OnceLock::new(), + is_fake: false, }); assert_eq!( expected, @@ -583,6 +590,7 @@ mod tests { .unwrap(), }, hash: OnceLock::new(), + is_fake: false, }); assert_eq!( expected, @@ -608,6 +616,7 @@ mod tests { s: U256::from_str("0x016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469") .unwrap(), hash: OnceLock::new(), + is_fake: false, }); assert_eq!( expected, @@ -636,6 +645,7 @@ mod tests { .unwrap(), }, hash: OnceLock::new(), + is_fake: false, }); assert_eq!( expected, diff --git a/crates/edr_eth/src/transaction/signed/eip155.rs b/crates/edr_eth/src/transaction/signed/eip155.rs index b78dc1f0ac..68a44ac3b4 100644 --- a/crates/edr_eth/src/transaction/signed/eip155.rs +++ b/crates/edr_eth/src/transaction/signed/eip155.rs @@ -6,7 +6,10 @@ use alloy_rlp::{RlpDecodable, RlpEncodable}; use super::LegacySignedTransaction; use crate::{ signature::{Signature, SignatureError}, - transaction::{kind::TransactionKind, request::Eip155TransactionRequest}, + transaction::{ + fake_signature::recover_fake_signature, kind::TransactionKind, + request::Eip155TransactionRequest, + }, Address, Bytes, B256, U256, }; @@ -28,6 +31,11 @@ pub struct Eip155SignedTransaction { #[rlp(skip)] #[cfg_attr(feature = "serde", serde(skip))] pub hash: OnceLock, + /// Whether the signed transaction is from an impersonated account. + #[rlp(default)] + #[rlp(skip)] + #[cfg_attr(feature = "serde", serde(skip))] + pub is_fake: bool, } impl Eip155SignedTransaction { @@ -37,6 +45,9 @@ impl Eip155SignedTransaction { /// Recovers the Ethereum address which was used to sign the transaction. pub fn recover(&self) -> Result { + if self.is_fake { + return Ok(recover_fake_signature(&self.signature)); + } self.signature .recover(Eip155TransactionRequest::from(self).hash()) } @@ -57,6 +68,7 @@ impl From for Eip155SignedTransaction { input: tx.input, signature: tx.signature, hash: tx.hash, + is_fake: tx.is_fake, } } } diff --git a/crates/edr_eth/src/transaction/signed/eip1559.rs b/crates/edr_eth/src/transaction/signed/eip1559.rs index e79c67b54e..cb6795f5cf 100644 --- a/crates/edr_eth/src/transaction/signed/eip1559.rs +++ b/crates/edr_eth/src/transaction/signed/eip1559.rs @@ -6,7 +6,10 @@ use alloy_rlp::{RlpDecodable, RlpEncodable}; use crate::{ access_list::AccessList, signature::{Signature, SignatureError}, - transaction::{kind::TransactionKind, request::Eip1559TransactionRequest}, + transaction::{ + fake_signature::recover_fake_signature, kind::TransactionKind, + request::Eip1559TransactionRequest, + }, utils::envelop_bytes, Address, Bytes, B256, U256, }; @@ -35,6 +38,11 @@ pub struct Eip1559SignedTransaction { #[rlp(skip)] #[cfg_attr(feature = "serde", serde(skip))] pub hash: OnceLock, + /// Whether the signature is from an impersonated account. + #[rlp(default)] + #[rlp(skip)] + #[cfg_attr(feature = "serde", serde(skip))] + pub is_fake: bool, } impl Eip1559SignedTransaction { @@ -55,6 +63,10 @@ impl Eip1559SignedTransaction { v: u64::from(self.odd_y_parity), }; + if self.is_fake { + return Ok(recover_fake_signature(&signature)); + } + signature.recover(Eip1559TransactionRequest::from(self).hash()) } } diff --git a/crates/edr_eth/src/transaction/signed/eip2930.rs b/crates/edr_eth/src/transaction/signed/eip2930.rs index a6d3c8a394..2d363d4e81 100644 --- a/crates/edr_eth/src/transaction/signed/eip2930.rs +++ b/crates/edr_eth/src/transaction/signed/eip2930.rs @@ -6,7 +6,10 @@ use alloy_rlp::{RlpDecodable, RlpEncodable}; use crate::{ access_list::AccessList, signature::{Signature, SignatureError}, - transaction::{kind::TransactionKind, request::Eip2930TransactionRequest}, + transaction::{ + fake_signature::recover_fake_signature, kind::TransactionKind, + request::Eip2930TransactionRequest, + }, utils::envelop_bytes, Address, Bytes, B256, U256, }; @@ -34,6 +37,11 @@ pub struct Eip2930SignedTransaction { #[rlp(skip)] #[cfg_attr(feature = "serde", serde(skip))] pub hash: OnceLock, + /// Whether the signed transaction is from an impersonated account. + #[rlp(default)] + #[rlp(skip)] + #[cfg_attr(feature = "serde", serde(skip))] + pub is_fake: bool, } impl Eip2930SignedTransaction { @@ -54,6 +62,10 @@ impl Eip2930SignedTransaction { v: u64::from(self.odd_y_parity), }; + if self.is_fake { + return Ok(recover_fake_signature(&signature)); + } + signature.recover(Eip2930TransactionRequest::from(self).hash()) } } diff --git a/crates/edr_eth/src/transaction/signed/eip4844.rs b/crates/edr_eth/src/transaction/signed/eip4844.rs index 8b165f97ab..357c1cdbe0 100644 --- a/crates/edr_eth/src/transaction/signed/eip4844.rs +++ b/crates/edr_eth/src/transaction/signed/eip4844.rs @@ -6,7 +6,7 @@ use alloy_rlp::{RlpDecodable, RlpEncodable}; use crate::{ access_list::AccessList, signature::{Signature, SignatureError}, - transaction::Eip4844TransactionRequest, + transaction::{fake_signature::recover_fake_signature, Eip4844TransactionRequest}, utils::envelop_bytes, Address, Bytes, B256, U256, }; @@ -37,6 +37,11 @@ pub struct Eip4844SignedTransaction { #[rlp(skip)] #[cfg_attr(feature = "serde", serde(skip))] pub hash: OnceLock, + /// Whether the signed transaction is from an impersonated account. + #[rlp(default)] + #[rlp(skip)] + #[cfg_attr(feature = "serde", serde(skip))] + pub is_fake: bool, } impl Eip4844SignedTransaction { @@ -61,6 +66,10 @@ impl Eip4844SignedTransaction { v: u64::from(self.odd_y_parity), }; + if self.is_fake { + return Ok(recover_fake_signature(&signature)); + } + signature.recover(Eip4844TransactionRequest::from(self).hash()) } } @@ -113,6 +122,7 @@ mod tests { .unwrap(), odd_y_parity: false, hash: OnceLock::new(), + is_fake: false, } } diff --git a/crates/edr_eth/src/transaction/signed/legacy.rs b/crates/edr_eth/src/transaction/signed/legacy.rs index 94bcf9377f..9e7b641350 100644 --- a/crates/edr_eth/src/transaction/signed/legacy.rs +++ b/crates/edr_eth/src/transaction/signed/legacy.rs @@ -5,7 +5,10 @@ use alloy_rlp::{RlpDecodable, RlpEncodable}; use crate::{ signature::{Signature, SignatureError}, - transaction::{kind::TransactionKind, request::LegacyTransactionRequest}, + transaction::{ + fake_signature::recover_fake_signature, kind::TransactionKind, + request::LegacyTransactionRequest, + }, Address, Bytes, B256, U256, }; @@ -27,6 +30,11 @@ pub struct LegacySignedTransaction { #[rlp(skip)] #[cfg_attr(feature = "serde", serde(skip))] pub hash: OnceLock, + /// Whether the signature is from an impersonated account. + #[rlp(default)] + #[rlp(skip)] + #[cfg_attr(feature = "serde", serde(skip))] + pub is_fake: bool, } impl LegacySignedTransaction { @@ -36,6 +44,9 @@ impl LegacySignedTransaction { /// Recovers the Ethereum address which was used to sign the transaction. pub fn recover(&self) -> Result { + if self.is_fake { + return Ok(recover_fake_signature(&self.signature)); + } self.signature .recover(LegacyTransactionRequest::from(self).hash()) } diff --git a/crates/edr_evm/src/transaction/executable.rs b/crates/edr_evm/src/transaction/executable.rs index c6ea0c2895..2ff51397eb 100644 --- a/crates/edr_evm/src/transaction/executable.rs +++ b/crates/edr_evm/src/transaction/executable.rs @@ -284,6 +284,7 @@ impl TryFrom for ExecutableTransaction { v: value.v, }, hash: OnceLock::from(value.hash), + is_fake: false, }) } else { SignedTransaction::PostEip155Legacy(Eip155SignedTransaction { @@ -299,6 +300,7 @@ impl TryFrom for ExecutableTransaction { v: value.v, }, hash: OnceLock::from(value.hash), + is_fake: false, }) } } @@ -320,6 +322,7 @@ impl TryFrom for ExecutableTransaction { r: value.r, s: value.s, hash: OnceLock::from(value.hash), + is_fake: false, }), Some(2) => SignedTransaction::Eip1559(Eip1559SignedTransaction { odd_y_parity: value.odd_y_parity(), @@ -344,6 +347,7 @@ impl TryFrom for ExecutableTransaction { r: value.r, s: value.s, hash: OnceLock::from(value.hash), + is_fake: false, }), Some(3) => SignedTransaction::Eip4844(Eip4844SignedTransaction { odd_y_parity: value.odd_y_parity(), @@ -376,6 +380,7 @@ impl TryFrom for ExecutableTransaction { r: value.r, s: value.s, hash: OnceLock::from(value.hash), + is_fake: false, }), Some(r#type) => { return Err(TransactionConversionError::UnsupportedType(r#type)); diff --git a/crates/edr_provider/src/requests/eth/transactions.rs b/crates/edr_provider/src/requests/eth/transactions.rs index f56e8c81ba..769766fb33 100644 --- a/crates/edr_provider/src/requests/eth/transactions.rs +++ b/crates/edr_provider/src/requests/eth/transactions.rs @@ -510,3 +510,67 @@ You can use them by running Hardhat Network with 'hardfork' {minimum_hardfork:?} validate_transaction_and_call_request(data.spec_id(), signed_transaction) } + +#[cfg(test)] +mod tests { + use anyhow::Context; + use edr_eth::{ + transaction::{Eip155TransactionRequest, TransactionKind, TransactionRequest}, + Address, Bytes, U256, + }; + use edr_evm::ExecutableTransaction; + + use super::*; + use crate::{ + data::{test_utils::ProviderTestFixture, SendTransactionResult}, + test_utils::one_ether, + }; + + #[test] + fn transaction_by_hash_for_impersonated_account() -> anyhow::Result<()> { + let mut fixture = ProviderTestFixture::new_local()?; + + let impersonated_account: Address = "0x20620fa0ad46516e915029c94e3c87c9cd7861ff".parse()?; + fixture + .provider_data + .impersonate_account(impersonated_account); + + fixture + .provider_data + .set_balance(impersonated_account, one_ether())?; + + let chain_id = fixture.provider_data.chain_id(); + + let request = TransactionRequest::Eip155(Eip155TransactionRequest { + kind: TransactionKind::Call(Address::ZERO), + gas_limit: 30_000, + gas_price: U256::from(42_000_000_000_u64), + value: U256::from(1), + input: Bytes::default(), + nonce: 0, + chain_id, + }) + .fake_sign(&impersonated_account); + let transaction = ExecutableTransaction::with_caller( + fixture.provider_data.spec_id(), + request, + impersonated_account, + )?; + + fixture.provider_data.set_auto_mining(true); + let SendTransactionResult { + transaction_hash, + transaction_result, + .. + } = fixture.provider_data.send_transaction(transaction)?; + assert!(transaction_result.is_some()); + + let rpc_transaction = + handle_get_transaction_by_hash(&fixture.provider_data, transaction_hash)? + .context("transaction not found")?; + assert_eq!(&rpc_transaction.from, &impersonated_account); + assert_eq!(&rpc_transaction.hash, &transaction_hash); + + Ok(()) + } +}