Skip to content

Commit b36226e

Browse files
authored
Merge branch 'foundry-rs:master' into master
2 parents 375b486 + 10c0ab1 commit b36226e

File tree

5 files changed

+77
-68
lines changed

5 files changed

+77
-68
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/anvil/core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ alloy-rlp.workspace = true
3030
alloy-eips.workspace = true
3131
alloy-eip5792.workspace = true
3232
alloy-consensus = { workspace = true, features = ["k256", "kzg"] }
33+
alloy-network.workspace = true
3334
alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] }
3435
serde.workspace = true
3536
serde_json.workspace = true

crates/anvil/core/src/eth/block.rs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,25 @@ use super::transaction::TransactionInfo;
22
use alloy_consensus::{
33
BlockBody, EMPTY_OMMER_ROOT_HASH, Header, proofs::calculate_transaction_root,
44
};
5-
use foundry_primitives::FoundryReceiptEnvelope;
5+
use alloy_eips::eip2718::Encodable2718;
6+
use alloy_network::Network;
7+
use foundry_primitives::FoundryNetwork;
8+
use std::fmt::Debug;
69

7-
// Type alias to optionally support impersonated transactions
8-
type Transaction = crate::eth::transaction::MaybeImpersonatedTransaction;
10+
use crate::eth::transaction::{ImpersonatedTransaction, MaybeImpersonatedTransaction};
911

1012
/// Type alias for Ethereum Block with Anvil's transaction type
11-
pub type Block = alloy_consensus::Block<Transaction>;
13+
pub type Block = alloy_consensus::Block<MaybeImpersonatedTransaction>;
1214

13-
/// Container type that gathers all block data
15+
/// Anvil's concrete block info type.
16+
pub type BlockInfo = TypedBlockInfo<FoundryNetwork>;
17+
18+
/// Container type that gathers all block data, generic over a [`Network`].
1419
#[derive(Clone, Debug)]
15-
pub struct BlockInfo {
16-
pub block: Block,
20+
pub struct TypedBlockInfo<N: Network> {
21+
pub block: alloy_consensus::Block<ImpersonatedTransaction<N::TxEnvelope>>,
1722
pub transactions: Vec<TransactionInfo>,
18-
pub receipts: Vec<FoundryReceiptEnvelope>,
23+
pub receipts: Vec<N::ReceiptEnvelope>,
1924
}
2025

2126
/// Helper function to create a new block with Header and Anvil transactions
@@ -24,7 +29,7 @@ pub struct BlockInfo {
2429
/// `MaybeImpersonatedTransaction`.
2530
pub fn create_block<T>(mut header: Header, transactions: impl IntoIterator<Item = T>) -> Block
2631
where
27-
T: Into<Transaction>,
32+
T: Into<MaybeImpersonatedTransaction>,
2833
{
2934
let transactions: Vec<_> = transactions.into_iter().map(Into::into).collect();
3035
let transactions_root = calculate_transaction_root(&transactions);
@@ -36,6 +41,24 @@ where
3641
Block::new(header, body)
3742
}
3843

44+
/// Generic helper function to create a block with any transaction type that supports encoding.
45+
pub fn create_typed_block<T>(
46+
mut header: Header,
47+
transactions: impl IntoIterator<Item = T>,
48+
) -> alloy_consensus::Block<T>
49+
where
50+
T: Encodable2718,
51+
{
52+
let transactions: Vec<_> = transactions.into_iter().collect();
53+
let transactions_root = calculate_transaction_root(&transactions);
54+
55+
header.transactions_root = transactions_root;
56+
header.ommers_hash = EMPTY_OMMER_ROOT_HASH;
57+
58+
let body = BlockBody { transactions, ommers: Vec::new(), withdrawals: None };
59+
alloy_consensus::Block::new(header, body)
60+
}
61+
3962
#[cfg(test)]
4063
mod tests {
4164
use alloy_primitives::{

crates/anvil/core/src/eth/transaction/mod.rs

Lines changed: 32 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,58 @@
11
//! Transaction related types
2-
use alloy_consensus::{
3-
Signed, Transaction, Typed2718, crypto::RecoveryError, transaction::Recovered,
4-
};
2+
use alloy_consensus::{Transaction, Typed2718, crypto::RecoveryError};
53

64
use alloy_eips::eip2718::Encodable2718;
75
use alloy_primitives::{Address, B256, Bytes, TxHash};
86
use alloy_rlp::{Decodable, Encodable};
9-
use alloy_rpc_types::Transaction as RpcTransaction;
107
use bytes::BufMut;
118
use foundry_evm::traces::CallTraceNode;
129
use foundry_primitives::FoundryTxEnvelope;
1310
use revm::interpreter::InstructionResult;
1411
use serde::{Deserialize, Serialize};
1512
use std::ops::Deref;
1613

17-
/// A wrapper for [FoundryTxEnvelope] that allows impersonating accounts.
14+
/// Anvil's concrete impersonated transaction type.
15+
pub type MaybeImpersonatedTransaction = ImpersonatedTransaction<FoundryTxEnvelope>;
16+
17+
/// A wrapper for a transaction envelope that allows impersonating accounts.
1818
///
1919
/// This is a helper that carries the `impersonated` sender so that the right hash
20-
/// [FoundryTxEnvelope::impersonated_hash] can be created.
20+
/// can be created.
2121
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
22-
pub struct MaybeImpersonatedTransaction {
23-
transaction: FoundryTxEnvelope,
22+
pub struct ImpersonatedTransaction<T> {
23+
transaction: T,
2424
impersonated_sender: Option<Address>,
2525
}
2626

27-
impl Typed2718 for MaybeImpersonatedTransaction {
27+
impl<T: Typed2718> Typed2718 for ImpersonatedTransaction<T> {
2828
fn ty(&self) -> u8 {
2929
self.transaction.ty()
3030
}
3131
}
3232

33-
impl MaybeImpersonatedTransaction {
33+
impl<T> ImpersonatedTransaction<T> {
3434
/// Creates a new wrapper for the given transaction
35-
pub fn new(transaction: FoundryTxEnvelope) -> Self {
35+
pub fn new(transaction: T) -> Self {
3636
Self { transaction, impersonated_sender: None }
3737
}
3838

3939
/// Creates a new impersonated transaction wrapper using the given sender
40-
pub fn impersonated(transaction: FoundryTxEnvelope, impersonated_sender: Address) -> Self {
40+
pub fn impersonated(transaction: T, impersonated_sender: Address) -> Self {
4141
Self { transaction, impersonated_sender: Some(impersonated_sender) }
4242
}
4343

44+
/// Returns whether the transaction is impersonated
45+
pub fn is_impersonated(&self) -> bool {
46+
self.impersonated_sender.is_some()
47+
}
48+
49+
/// Returns the inner transaction.
50+
pub fn into_inner(self) -> T {
51+
self.transaction
52+
}
53+
}
54+
55+
impl ImpersonatedTransaction<FoundryTxEnvelope> {
4456
/// Recovers the Ethereum address which was used to sign the transaction.
4557
pub fn recover(&self) -> Result<Address, RecoveryError> {
4658
if let Some(sender) = self.impersonated_sender {
@@ -49,41 +61,16 @@ impl MaybeImpersonatedTransaction {
4961
self.transaction.recover()
5062
}
5163

52-
/// Returns whether the transaction is impersonated
53-
pub fn is_impersonated(&self) -> bool {
54-
self.impersonated_sender.is_some()
55-
}
56-
5764
/// Returns the hash of the transaction
5865
pub fn hash(&self) -> B256 {
5966
if let Some(sender) = self.impersonated_sender {
6067
return self.transaction.impersonated_hash(sender);
6168
}
6269
self.transaction.hash()
6370
}
64-
65-
/// Converts the transaction into an [`RpcTransaction`]
66-
pub fn into_rpc_transaction(self) -> RpcTransaction {
67-
let hash = self.hash();
68-
let from = self.recover().unwrap_or_default();
69-
let envelope = self.transaction.try_into_eth().expect("cant build deposit transactions");
70-
71-
// NOTE: we must update the hash because the tx can be impersonated, this requires forcing
72-
// the hash
73-
let (typed_tx, signature, _) = Into::<Signed<_>>::into(envelope).into_parts();
74-
let inner_envelope = Signed::new_unchecked(typed_tx, signature, hash).into();
75-
76-
RpcTransaction {
77-
block_hash: None,
78-
block_number: None,
79-
transaction_index: None,
80-
effective_gas_price: None,
81-
inner: Recovered::new_unchecked(inner_envelope, from),
82-
}
83-
}
8471
}
8572

86-
impl Encodable2718 for MaybeImpersonatedTransaction {
73+
impl<T: Encodable2718> Encodable2718 for ImpersonatedTransaction<T> {
8774
fn encode_2718_len(&self) -> usize {
8875
self.transaction.encode_2718_len()
8976
}
@@ -93,7 +80,7 @@ impl Encodable2718 for MaybeImpersonatedTransaction {
9380
}
9481
}
9582

96-
impl Encodable for MaybeImpersonatedTransaction {
83+
impl<T: Encodable> Encodable for ImpersonatedTransaction<T> {
9784
fn encode(&self, out: &mut dyn bytes::BufMut) {
9885
self.transaction.encode(out)
9986
}
@@ -111,32 +98,26 @@ impl From<FoundryTxEnvelope> for MaybeImpersonatedTransaction {
11198
}
11299
}
113100

114-
impl Decodable for MaybeImpersonatedTransaction {
101+
impl<T: Decodable> Decodable for ImpersonatedTransaction<T> {
115102
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
116-
FoundryTxEnvelope::decode(buf).map(Self::new)
103+
T::decode(buf).map(Self::new)
117104
}
118105
}
119106

120-
impl AsRef<FoundryTxEnvelope> for MaybeImpersonatedTransaction {
121-
fn as_ref(&self) -> &FoundryTxEnvelope {
107+
impl<T> AsRef<T> for ImpersonatedTransaction<T> {
108+
fn as_ref(&self) -> &T {
122109
&self.transaction
123110
}
124111
}
125112

126-
impl Deref for MaybeImpersonatedTransaction {
127-
type Target = FoundryTxEnvelope;
113+
impl<T> Deref for ImpersonatedTransaction<T> {
114+
type Target = T;
128115

129116
fn deref(&self) -> &Self::Target {
130117
&self.transaction
131118
}
132119
}
133120

134-
impl From<MaybeImpersonatedTransaction> for RpcTransaction {
135-
fn from(value: MaybeImpersonatedTransaction) -> Self {
136-
value.into_rpc_transaction()
137-
}
138-
}
139-
140121
/// Queued transaction
141122
#[derive(Clone, Debug, PartialEq, Eq)]
142123
pub struct PendingTransaction {

crates/anvil/src/eth/backend/mem/mod.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3862,20 +3862,23 @@ pub fn transaction_build(
38623862
}
38633863
}
38643864

3865-
let transaction = eth_transaction.into_rpc_transaction();
3866-
let effective_gas_price = transaction.effective_gas_price(base_fee);
3867-
3868-
let envelope = transaction.inner;
3869-
let from = envelope.signer();
3865+
let from = eth_transaction.recover().unwrap_or_default();
3866+
let effective_gas_price = eth_transaction.effective_gas_price(base_fee);
38703867

38713868
// if a specific hash was provided we update the transaction's hash
38723869
// This is important for impersonated transactions since they all use the
38733870
// `BYPASS_SIGNATURE` which would result in different hashes
38743871
// Note: for impersonated transactions this only concerns pending transactions because
3875-
// there's // no `info` yet.
3876-
let hash = tx_hash.unwrap_or(*envelope.tx_hash());
3872+
// there's no `info` yet.
3873+
let hash = tx_hash.unwrap_or_else(|| eth_transaction.hash());
3874+
3875+
// TODO: this panics for non-standard tx types (e.g. Tempo) that aren't handled above
3876+
// (pre-existing issue from the original `into_rpc_transaction`).
3877+
let eth_envelope = FoundryTxEnvelope::from(eth_transaction)
3878+
.try_into_eth()
3879+
.expect("deposit transactions are handled above");
38773880

3878-
let envelope = match envelope.into_inner() {
3881+
let envelope = match eth_envelope {
38793882
TxEnvelope::Legacy(signed_tx) => {
38803883
let (t, sig, _) = signed_tx.into_parts();
38813884
let new_signed = Signed::new_unchecked(t, sig, hash);

0 commit comments

Comments
 (0)