From 0ec305f8e17cebb402e157dc82eeb83c6b6156b0 Mon Sep 17 00:00:00 2001 From: Wodann Date: Wed, 13 Mar 2024 03:47:48 -0500 Subject: [PATCH] fix: remove empty pending and future transaction lists (#4987) --- .changeset/thick-cycles-attend.md | 5 ++ crates/edr_evm/src/mempool.rs | 12 ++++- crates/edr_provider/tests/issue_325.rs | 65 ++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 .changeset/thick-cycles-attend.md create mode 100644 crates/edr_provider/tests/issue_325.rs diff --git a/.changeset/thick-cycles-attend.md b/.changeset/thick-cycles-attend.md new file mode 100644 index 0000000000..05c6f97782 --- /dev/null +++ b/.changeset/thick-cycles-attend.md @@ -0,0 +1,5 @@ +--- +"@nomicfoundation/edr": patch +--- + +Fixed a bug in `hardhat_dropTransaction` where empty queues persisted and caused panics diff --git a/crates/edr_evm/src/mempool.rs b/crates/edr_evm/src/mempool.rs index 73a755f0da..6581640519 100644 --- a/crates/edr_evm/src/mempool.rs +++ b/crates/edr_evm/src/mempool.rs @@ -335,6 +335,10 @@ impl MemPool { let mut invalidated_transactions = pending_transactions.split_off(idx + 1); let removed = pending_transactions.remove(idx); + if pending_transactions.is_empty() { + self.pending_transactions.remove(caller); + } + self.future_transactions .entry(*caller) .and_modify(|transactions| { @@ -352,7 +356,13 @@ impl MemPool { .enumerate() .find(|(_, transaction)| *transaction.hash() == *hash) { - return Some(future_transactions.remove(idx)); + let removed = future_transactions.remove(idx); + + if future_transactions.is_empty() { + self.future_transactions.remove(caller); + } + + return Some(removed); } } } diff --git a/crates/edr_provider/tests/issue_325.rs b/crates/edr_provider/tests/issue_325.rs new file mode 100644 index 0000000000..6e1fe0ccd5 --- /dev/null +++ b/crates/edr_provider/tests/issue_325.rs @@ -0,0 +1,65 @@ +use edr_eth::{ + remote::PreEip1898BlockSpec, transaction::EthTransactionRequest, AccountInfo, Address, SpecId, + B256, +}; +use edr_evm::KECCAK_EMPTY; +use edr_provider::{ + test_utils::{create_test_config_with_fork, one_ether}, + MethodInvocation, MiningConfig, NoopLogger, Provider, ProviderRequest, +}; +use tokio::runtime; + +#[tokio::test(flavor = "multi_thread")] +async fn issue_325() -> anyhow::Result<()> { + let logger = Box::new(NoopLogger); + let subscriber = Box::new(|_event| {}); + + let mut config = create_test_config_with_fork(None); + config.hardfork = SpecId::CANCUN; + config.mining = MiningConfig { + auto_mine: false, + ..MiningConfig::default() + }; + + let impersonated_account = Address::random(); + config.genesis_accounts.insert( + impersonated_account, + AccountInfo { + balance: one_ether(), + nonce: 0, + code: None, + code_hash: KECCAK_EMPTY, + }, + ); + + let provider = Provider::new(runtime::Handle::current(), logger, subscriber, config)?; + + provider.handle_request(ProviderRequest::Single( + MethodInvocation::ImpersonateAccount(impersonated_account.into()), + ))?; + + let result = provider.handle_request(ProviderRequest::Single( + MethodInvocation::SendTransaction(EthTransactionRequest { + from: impersonated_account, + to: Some(Address::random()), + ..EthTransactionRequest::default() + }), + ))?; + + let transaction_hash: B256 = serde_json::from_value(result.result)?; + + let result = provider.handle_request(ProviderRequest::Single( + MethodInvocation::DropTransaction(transaction_hash), + ))?; + + let dropped: bool = serde_json::from_value(result.result)?; + + assert!(dropped); + + provider.handle_request(ProviderRequest::Single(MethodInvocation::GetBlockByNumber( + PreEip1898BlockSpec::pending(), + false, + )))?; + + Ok(()) +}