Skip to content

Commit

Permalink
Merge branch 'master' into feat/cast-send-bump-gas-price-stuck-txs
Browse files Browse the repository at this point in the history
  • Loading branch information
leovct authored Nov 4, 2024
2 parents ec2c43a + d2ed15d commit 5ff1422
Show file tree
Hide file tree
Showing 57 changed files with 2,643 additions and 488 deletions.
362 changes: 183 additions & 179 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

## Reporting a Vulnerability

Contact georgios at paradigm.xyz.
Contact [security@ithaca.xyz](mailto:security@ithaca.xyz).
24 changes: 10 additions & 14 deletions crates/anvil/core/src/eth/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::{
transaction::{TransactionInfo, TypedReceipt},
trie,
};
use alloy_consensus::Header;
use alloy_consensus::{Header, EMPTY_OMMER_ROOT_HASH};
use alloy_eips::eip2718::Encodable2718;
use alloy_primitives::{Address, Bloom, Bytes, B256, B64, U256};
use alloy_rlp::{RlpDecodable, RlpEncodable};
Expand Down Expand Up @@ -34,27 +34,19 @@ impl Block {
///
/// Note: if the `impersonate-tx` feature is enabled this will also accept
/// `MaybeImpersonatedTransaction`.
pub fn new<T>(
partial_header: PartialHeader,
transactions: impl IntoIterator<Item = T>,
ommers: Vec<Header>,
) -> Self
pub fn new<T>(partial_header: PartialHeader, transactions: impl IntoIterator<Item = T>) -> Self
where
T: Into<Transaction>,
{
let transactions: Vec<_> = transactions.into_iter().map(Into::into).collect();
let mut encoded_ommers: Vec<u8> = Vec::new();
alloy_rlp::encode_list(&ommers, &mut encoded_ommers);
let ommers_hash =
B256::from_slice(alloy_primitives::utils::keccak256(encoded_ommers).as_slice());
let transactions_root =
trie::ordered_trie_root(transactions.iter().map(|r| r.encoded_2718()));

Self {
header: Header {
parent_hash: partial_header.parent_hash,
beneficiary: partial_header.beneficiary,
ommers_hash,
ommers_hash: EMPTY_OMMER_ROOT_HASH,
state_root: partial_header.state_root,
transactions_root,
receipts_root: partial_header.receipts_root,
Expand All @@ -66,16 +58,16 @@ impl Block {
timestamp: partial_header.timestamp,
extra_data: partial_header.extra_data,
mix_hash: partial_header.mix_hash,
withdrawals_root: None,
withdrawals_root: partial_header.withdrawals_root,
blob_gas_used: partial_header.blob_gas_used,
excess_blob_gas: partial_header.excess_blob_gas,
parent_beacon_block_root: partial_header.parent_beacon_block_root,
nonce: partial_header.nonce,
base_fee_per_gas: partial_header.base_fee,
requests_hash: None,
requests_hash: partial_header.requests_hash,
},
transactions,
ommers,
ommers: vec![],
}
}
}
Expand All @@ -100,6 +92,8 @@ pub struct PartialHeader {
pub parent_beacon_block_root: Option<B256>,
pub nonce: B64,
pub base_fee: Option<u64>,
pub withdrawals_root: Option<B256>,
pub requests_hash: Option<B256>,
}

impl From<Header> for PartialHeader {
Expand All @@ -122,6 +116,8 @@ impl From<Header> for PartialHeader {
blob_gas_used: value.blob_gas_used,
excess_blob_gas: value.excess_blob_gas,
parent_beacon_block_root: value.parent_beacon_block_root,
requests_hash: value.requests_hash,
withdrawals_root: value.withdrawals_root,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/anvil/src/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub enum AnvilSubcommand {

fn main() {
if let Err(err) = run() {
let _ = foundry_common::Shell::get().error(&err);
let _ = foundry_common::sh_err!("{err:?}");
std::process::exit(1);
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/anvil/src/eth/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1697,12 +1697,12 @@ impl EthApi {

// mine all the blocks
for _ in 0..blocks.to::<u64>() {
self.mine_one().await;

// If we have an interval, jump forwards in time to the "next" timestamp
if let Some(interval) = interval {
self.backend.time().increase_time(interval);
}

self.mine_one().await;
}

Ok(())
Expand Down
13 changes: 8 additions & 5 deletions crates/anvil/src/eth/backend/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use crate::{
mem::inspector::Inspector,
PrecompileFactory,
};
use alloy_consensus::{Header, Receipt, ReceiptWithBloom};
use alloy_eips::eip2718::Encodable2718;
use alloy_consensus::{constants::EMPTY_WITHDRAWALS, Receipt, ReceiptWithBloom};
use alloy_eips::{eip2718::Encodable2718, eip7685::EMPTY_REQUESTS_HASH};
use alloy_primitives::{Bloom, BloomInput, Log, B256};
use anvil_core::eth::{
block::{Block, BlockInfo, PartialHeader},
Expand Down Expand Up @@ -134,7 +134,9 @@ impl<DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'_, DB, V> {
None
};

let is_shanghai = self.cfg_env.handler_cfg.spec_id >= SpecId::SHANGHAI;
let is_cancun = self.cfg_env.handler_cfg.spec_id >= SpecId::CANCUN;
let is_prague = self.cfg_env.handler_cfg.spec_id >= SpecId::PRAGUE;
let excess_blob_gas = if is_cancun { self.block_env.get_blob_excess_gas() } else { None };
let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None };

Expand Down Expand Up @@ -208,7 +210,6 @@ impl<DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'_, DB, V> {
transactions.push(transaction.pending_transaction.transaction.clone());
}

let ommers: Vec<Header> = Vec::new();
let receipts_root =
trie::ordered_trie_root(receipts.iter().map(Encodable2718::encoded_2718));

Expand All @@ -227,12 +228,14 @@ impl<DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'_, DB, V> {
mix_hash: Default::default(),
nonce: Default::default(),
base_fee,
parent_beacon_block_root: Default::default(),
parent_beacon_block_root: is_cancun.then_some(Default::default()),
blob_gas_used: cumulative_blob_gas_used,
excess_blob_gas,
withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
};

let block = Block::new(partial_header, transactions.clone(), ommers);
let block = Block::new(partial_header, transactions.clone());
let block = BlockInfo { block, transactions: transaction_infos, receipts };
ExecutedTransactions { block, included, invalid }
}
Expand Down
42 changes: 26 additions & 16 deletions crates/anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,10 @@ impl Backend {
trace!(target: "backend", "using forked blockchain at {}", fork.block_number());
Blockchain::forked(fork.block_number(), fork.block_hash(), fork.total_difficulty())
} else {
let env = env.read();
Blockchain::new(
&env.read(),
&env,
env.handler_cfg.spec_id,
fees.is_eip1559().then(|| fees.base_fee()),
genesis.timestamp,
)
Expand Down Expand Up @@ -907,19 +909,27 @@ impl Backend {

// Set the current best block number.
// Defaults to block number for compatibility with existing state files.
let fork_num_and_hash = self.get_fork().map(|f| (f.block_number(), f.block_hash()));

let best_number = state.best_block_number.unwrap_or(block.number.to::<U64>());
self.blockchain.storage.write().best_number = best_number;

// Set the current best block hash;
let best_hash =
self.blockchain.storage.read().hash(best_number.into()).ok_or_else(|| {
BlockchainError::RpcError(RpcError::internal_error_with(format!(
"Best hash not found for best number {best_number}",
)))
})?;

self.blockchain.storage.write().best_hash = best_hash;
if let Some((number, hash)) = fork_num_and_hash {
// If loading state file on a fork, set best number to the fork block number.
// Ref: https://github.com/foundry-rs/foundry/pull/9215#issue-2618681838
self.blockchain.storage.write().best_number = U64::from(number);
self.blockchain.storage.write().best_hash = hash;
} else {
let best_number = state.best_block_number.unwrap_or(block.number.to::<U64>());
self.blockchain.storage.write().best_number = best_number;

// Set the current best block hash;
let best_hash =
self.blockchain.storage.read().hash(best_number.into()).ok_or_else(|| {
BlockchainError::RpcError(RpcError::internal_error_with(format!(
"Best hash not found for best number {best_number}",
)))
})?;

self.blockchain.storage.write().best_hash = best_hash;
}
}

if !self.db.write().await.load_state(state.clone())? {
Expand Down Expand Up @@ -1880,7 +1890,7 @@ impl Backend {
mix_hash,
nonce,
base_fee_per_gas,
withdrawals_root: _,
withdrawals_root,
blob_gas_used,
excess_blob_gas,
parent_beacon_block_root,
Expand All @@ -1906,7 +1916,7 @@ impl Backend {
mix_hash: Some(mix_hash),
nonce: Some(nonce),
base_fee_per_gas,
withdrawals_root: None,
withdrawals_root,
blob_gas_used,
excess_blob_gas,
parent_beacon_block_root,
Expand All @@ -1917,7 +1927,7 @@ impl Backend {
transactions.into_iter().map(|tx| tx.hash()).collect(),
),
uncles: vec![],
withdrawals: None,
withdrawals: withdrawals_root.map(|_| Default::default()),
};

let mut block = WithOtherFields::new(block);
Expand Down
30 changes: 21 additions & 9 deletions crates/anvil/src/eth/backend/mem/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use crate::eth::{
error::BlockchainError,
pool::transactions::PoolTransaction,
};
use alloy_consensus::constants::EMPTY_WITHDRAWALS;
use alloy_eips::eip7685::EMPTY_REQUESTS_HASH;
use alloy_primitives::{
map::{B256HashMap, HashMap},
Bytes, B256, U256, U64,
Expand Down Expand Up @@ -38,6 +40,7 @@ use foundry_evm::{
},
};
use parking_lot::RwLock;
use revm::primitives::SpecId;
use std::{collections::VecDeque, fmt, sync::Arc, time::Duration};
// use yansi::Paint;

Expand Down Expand Up @@ -262,7 +265,11 @@ pub struct BlockchainStorage {

impl BlockchainStorage {
/// Creates a new storage with a genesis block
pub fn new(env: &Env, base_fee: Option<u64>, timestamp: u64) -> Self {
pub fn new(env: &Env, spec_id: SpecId, base_fee: Option<u64>, timestamp: u64) -> Self {
let is_shanghai = spec_id >= SpecId::SHANGHAI;
let is_cancun = spec_id >= SpecId::CANCUN;
let is_prague = spec_id >= SpecId::PRAGUE;

// create a dummy genesis block
let partial_header = PartialHeader {
timestamp,
Expand All @@ -272,9 +279,13 @@ impl BlockchainStorage {
difficulty: env.block.difficulty,
blob_gas_used: env.block.blob_excess_gas_and_price.as_ref().map(|_| 0),
excess_blob_gas: env.block.get_blob_excess_gas(),

parent_beacon_block_root: is_cancun.then_some(Default::default()),
withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
..Default::default()
};
let block = Block::new::<MaybeImpersonatedTransaction>(partial_header, vec![], vec![]);
let block = Block::new::<MaybeImpersonatedTransaction>(partial_header, vec![]);
let genesis_hash = block.header.hash_slow();
let best_hash = genesis_hash;
let best_number: U64 = U64::from(0u64);
Expand Down Expand Up @@ -423,8 +434,12 @@ pub struct Blockchain {

impl Blockchain {
/// Creates a new storage with a genesis block
pub fn new(env: &Env, base_fee: Option<u64>, timestamp: u64) -> Self {
Self { storage: Arc::new(RwLock::new(BlockchainStorage::new(env, base_fee, timestamp))) }
pub fn new(env: &Env, spec_id: SpecId, base_fee: Option<u64>, timestamp: u64) -> Self {
Self {
storage: Arc::new(RwLock::new(BlockchainStorage::new(
env, spec_id, base_fee, timestamp,
))),
}
}

pub fn forked(block_number: u64, block_hash: B256, total_difficulty: U256) -> Self {
Expand Down Expand Up @@ -693,11 +708,8 @@ mod tests {
let bytes_first = &mut &hex::decode("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18").unwrap()[..];
let tx: MaybeImpersonatedTransaction =
TypedTransaction::decode(&mut &bytes_first[..]).unwrap().into();
let block = Block::new::<MaybeImpersonatedTransaction>(
partial_header.clone(),
vec![tx.clone()],
vec![],
);
let block =
Block::new::<MaybeImpersonatedTransaction>(partial_header.clone(), vec![tx.clone()]);
let block_hash = block.header.hash_slow();
dump_storage.blocks.insert(block_hash, block);

Expand Down
7 changes: 2 additions & 5 deletions crates/anvil/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ use crate::{
use alloy_primitives::{Address, U256};
use alloy_signer_local::PrivateKeySigner;
use eth::backend::fork::ClientFork;
use foundry_common::{
provider::{ProviderBuilder, RetryProvider},
shell,
};
use foundry_common::provider::{ProviderBuilder, RetryProvider};
use foundry_evm::revm;
use futures::{FutureExt, TryFutureExt};
use parking_lot::Mutex;
Expand Down Expand Up @@ -131,7 +128,7 @@ pub async fn spawn(config: NodeConfig) -> (EthApi, NodeHandle) {
/// ```
pub async fn try_spawn(mut config: NodeConfig) -> io::Result<(EthApi, NodeHandle)> {
let logger = if config.enable_tracing { init_tracing() } else { Default::default() };
logger.set_enabled(!shell::is_quiet());
logger.set_enabled(!config.silent);

let backend = Arc::new(config.setup().await);

Expand Down
29 changes: 28 additions & 1 deletion crates/anvil/tests/it/anvil.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
//! tests for anvil specific logic

use alloy_consensus::EMPTY_ROOT_HASH;
use alloy_eips::BlockNumberOrTag;
use alloy_primitives::Address;
use alloy_provider::Provider;
use anvil::{spawn, NodeConfig};
use anvil::{spawn, EthereumHardfork, NodeConfig};

#[tokio::test(flavor = "multi_thread")]
async fn test_can_change_mining_mode() {
Expand Down Expand Up @@ -88,3 +89,29 @@ async fn test_can_handle_large_timestamp() {
let block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap();
assert_eq!(block.header.timestamp, num);
}

#[tokio::test(flavor = "multi_thread")]
async fn test_shanghai_fields() {
let (api, _handle) =
spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Shanghai.into()))).await;
api.mine_one().await;

let block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap();
assert_eq!(block.header.withdrawals_root, Some(EMPTY_ROOT_HASH));
assert_eq!(block.withdrawals, Some(Default::default()));
assert!(block.header.blob_gas_used.is_none());
assert!(block.header.excess_blob_gas.is_none());
}

#[tokio::test(flavor = "multi_thread")]
async fn test_cancun_fields() {
let (api, _handle) =
spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into()))).await;
api.mine_one().await;

let block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap();
assert_eq!(block.header.withdrawals_root, Some(EMPTY_ROOT_HASH));
assert_eq!(block.withdrawals, Some(Default::default()));
assert!(block.header.blob_gas_used.is_some());
assert!(block.header.excess_blob_gas.is_some());
}
18 changes: 18 additions & 0 deletions crates/anvil/tests/it/anvil_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,3 +1009,21 @@ async fn test_mine_blks_with_same_timestamp() {
// timestamps should be equal
assert_eq!(blks, vec![init_timestamp; 4]);
}

// <https://github.com/foundry-rs/foundry/issues/8962>
#[tokio::test(flavor = "multi_thread")]
async fn test_mine_first_block_with_interval() {
let (api, _) = spawn(NodeConfig::test()).await;

let init_block = api.block_by_number(0.into()).await.unwrap().unwrap();
let init_timestamp = init_block.header.timestamp;

// Mine 2 blocks with interval of 60.
let _ = api.anvil_mine(Some(U256::from(2)), Some(U256::from(60))).await;

let first_block = api.block_by_number(1.into()).await.unwrap().unwrap();
assert_eq!(first_block.header.timestamp, init_timestamp + 60);

let second_block = api.block_by_number(2.into()).await.unwrap().unwrap();
assert_eq!(second_block.header.timestamp, init_timestamp + 120);
}
Loading

0 comments on commit 5ff1422

Please sign in to comment.