@@ -7,10 +7,15 @@ use crate::{
77 ReviveFilter , SubstrateU256 , convert_to_generic_transaction,
88 } ,
99 signer:: DevSigner ,
10+ txpool_helpers:: {
11+ TxpoolTransactionInfo , extract_sender, extract_tx_info, extract_tx_summary,
12+ transaction_matches_eth_hash,
13+ } ,
1014 } ,
1115 logging:: LoggingManager ,
1216 macros:: node_info,
1317 substrate_node:: {
18+ host:: recover_maybe_impersonated_address,
1419 impersonation:: ImpersonationManager ,
1520 in_mem_rpc:: InMemoryRpcClient ,
1621 mining_engine:: MiningEngine ,
@@ -30,13 +35,13 @@ use alloy_primitives::{Address, B256, U64, U256};
3035use alloy_rpc_types:: {
3136 Filter , TransactionRequest ,
3237 anvil:: { Metadata as AnvilMetadata , MineOptions , NodeEnvironment , NodeInfo } ,
33- txpool:: TxpoolStatus ,
38+ txpool:: { TxpoolContent , TxpoolInspect , TxpoolStatus } ,
3439} ;
3540use alloy_serde:: WithOtherFields ;
3641use alloy_trie:: { EMPTY_ROOT_HASH , KECCAK_EMPTY , TrieAccount } ;
3742use anvil_core:: eth:: { EthRequest , Params as MineParams } ;
3843use anvil_rpc:: response:: ResponseResult ;
39- use codec:: { Decode , DecodeLimit , Encode } ;
44+ use codec:: { Decode , Encode } ;
4045use futures:: { StreamExt , channel:: mpsc} ;
4146use indexmap:: IndexMap ;
4247use pallet_revive_eth_rpc:: {
@@ -65,7 +70,7 @@ use polkadot_sdk::{
6570use revm:: primitives:: hardfork:: SpecId ;
6671use sqlx:: sqlite:: SqlitePoolOptions ;
6772use std:: { collections:: HashSet , sync:: Arc , time:: Duration } ;
68- use substrate_runtime:: { Balance , RuntimeCall , UncheckedExtrinsic } ;
73+ use substrate_runtime:: Balance ;
6974use subxt:: {
7075 Metadata as SubxtMetadata , OnlineClient , backend:: rpc:: RpcClient ,
7176 client:: RuntimeVersion as SubxtRuntimeVersion , config:: substrate:: H256 ,
@@ -75,7 +80,6 @@ use subxt_signer::eth::Keypair;
7580use tokio:: try_join;
7681
7782pub const CLIENT_VERSION : & str = concat ! ( "anvil-polkadot/v" , env!( "CARGO_PKG_VERSION" ) ) ;
78- const MAX_EXTRINSIC_DEPTH : u32 = 256 ;
7983
8084pub struct ApiServer {
8185 eth_rpc_client : EthRpcClient ,
@@ -339,18 +343,18 @@ impl ApiServer {
339343 self . get_account_info ( addr, block) . await . to_rpc_result ( )
340344 }
341345 //------- Transaction Pool ---------
342- EthRequest :: TxPoolStatus ( _) => {
343- node_info ! ( "txpool_status" ) ;
344- self . txpool_status ( ) . await . to_rpc_result ( )
345- }
346+ EthRequest :: TxPoolStatus ( _) => self . txpool_status ( ) . await . to_rpc_result ( ) ,
347+ EthRequest :: TxPoolInspect ( _) => self . txpool_inspect ( ) . await . to_rpc_result ( ) ,
348+ EthRequest :: TxPoolContent ( _) => self . txpool_content ( ) . await . to_rpc_result ( ) ,
346349 EthRequest :: DropAllTransactions ( ) => {
347- node_info ! ( "anvil_dropAllTransactions" ) ;
348350 self . anvil_drop_all_transactions ( ) . await . to_rpc_result ( )
349351 }
350352 EthRequest :: DropTransaction ( eth_hash) => {
351- node_info ! ( "anvil_dropTransaction" ) ;
352353 self . anvil_drop_transaction ( eth_hash) . await . to_rpc_result ( )
353354 }
355+ EthRequest :: RemovePoolTransactions ( address) => {
356+ self . anvil_remove_pool_transactions ( address) . await . to_rpc_result ( )
357+ }
354358 // --- Metadata ---
355359 EthRequest :: NodeInfo ( _) => self . anvil_node_info ( ) . await . to_rpc_result ( ) ,
356360 EthRequest :: AnvilMetadata ( _) => self . anvil_metadata ( ) . await . to_rpc_result ( ) ,
@@ -1247,12 +1251,58 @@ impl ApiServer {
12471251
12481252 /// Returns transaction pool status
12491253 async fn txpool_status ( & self ) -> Result < TxpoolStatus > {
1254+ node_info ! ( "txpool_status" ) ;
12501255 let pool_status = self . tx_pool . status ( ) ;
12511256 Ok ( TxpoolStatus { pending : pool_status. ready as u64 , queued : pool_status. future as u64 } )
12521257 }
12531258
1259+ /// Returns a summary of all transactions in the pool
1260+ async fn txpool_inspect ( & self ) -> Result < TxpoolInspect > {
1261+ node_info ! ( "txpool_inspect" ) ;
1262+ let mut inspect = TxpoolInspect :: default ( ) ;
1263+
1264+ for tx in self . tx_pool . ready ( ) {
1265+ if let Some ( ( sender, nonce, summary) ) = extract_tx_summary ( tx. data ( ) ) {
1266+ let entry = inspect. pending . entry ( sender) . or_default ( ) ;
1267+ entry. insert ( nonce. to_string ( ) , summary) ;
1268+ }
1269+ }
1270+
1271+ for tx in self . tx_pool . futures ( ) {
1272+ if let Some ( ( sender, nonce, summary) ) = extract_tx_summary ( tx. data ( ) ) {
1273+ let entry = inspect. queued . entry ( sender) . or_default ( ) ;
1274+ entry. insert ( nonce. to_string ( ) , summary) ;
1275+ }
1276+ }
1277+
1278+ Ok ( inspect)
1279+ }
1280+
1281+ /// Returns full transaction details for all transactions in the pool
1282+ async fn txpool_content ( & self ) -> Result < TxpoolContent < TxpoolTransactionInfo > > {
1283+ node_info ! ( "txpool_content" ) ;
1284+ let mut content = TxpoolContent :: default ( ) ;
1285+
1286+ for tx in self . tx_pool . ready ( ) {
1287+ if let Some ( ( sender, nonce, tx_info) ) = extract_tx_info ( tx. data ( ) ) {
1288+ let entry = content. pending . entry ( sender) . or_default ( ) ;
1289+ entry. insert ( nonce. to_string ( ) , tx_info) ;
1290+ }
1291+ }
1292+
1293+ for tx in self . tx_pool . futures ( ) {
1294+ if let Some ( ( sender, nonce, tx_info) ) = extract_tx_info ( tx. data ( ) ) {
1295+ let entry = content. queued . entry ( sender) . or_default ( ) ;
1296+ entry. insert ( nonce. to_string ( ) , tx_info) ;
1297+ }
1298+ }
1299+
1300+ Ok ( content)
1301+ }
1302+
12541303 /// Drop all transactions from pool
12551304 async fn anvil_drop_all_transactions ( & self ) -> Result < ( ) > {
1305+ node_info ! ( "anvil_dropAllTransactions" ) ;
12561306 let ready_txs = self . tx_pool . ready ( ) ;
12571307 let future_txs = self . tx_pool . futures ( ) ;
12581308
@@ -1273,7 +1323,7 @@ impl ApiServer {
12731323
12741324 /// Drop a specific transaction from the pool by its ETH hash
12751325 async fn anvil_drop_transaction ( & self , eth_hash : B256 ) -> Result < Option < B256 > > {
1276- // Search in ready transactions
1326+ node_info ! ( "anvil_dropTransaction" ) ;
12771327 for tx in self . tx_pool . ready ( ) {
12781328 if transaction_matches_eth_hash ( tx. data ( ) , eth_hash) {
12791329 let mut invalid_txs = IndexMap :: new ( ) ;
@@ -1283,7 +1333,6 @@ impl ApiServer {
12831333 }
12841334 }
12851335
1286- // Search in future transactions
12871336 for tx in self . tx_pool . futures ( ) {
12881337 if transaction_matches_eth_hash ( tx. data ( ) , eth_hash) {
12891338 let mut invalid_txs = IndexMap :: new ( ) ;
@@ -1296,30 +1345,34 @@ impl ApiServer {
12961345 // Transaction not found
12971346 Ok ( None )
12981347 }
1299- }
13001348
1301- /// Helper function to check if transaction matches ETH hash
1302- fn transaction_matches_eth_hash (
1303- tx_data : & Arc < polkadot_sdk:: sp_runtime:: OpaqueExtrinsic > ,
1304- target_eth_hash : B256 ,
1305- ) -> bool {
1306- let encoded = tx_data. encode ( ) ;
1307- let Ok ( ext) =
1308- UncheckedExtrinsic :: decode_all_with_depth_limit ( MAX_EXTRINSIC_DEPTH , & mut & encoded[ ..] )
1309- else {
1310- return false ;
1311- } ;
1349+ /// Remove all transactions from a specific sender address
1350+ async fn anvil_remove_pool_transactions ( & self , address : Address ) -> Result < ( ) > {
1351+ node_info ! ( "anvil_removePoolTransactions" ) ;
1352+ let mut invalid_txs = IndexMap :: new ( ) ;
13121353
1313- let polkadot_sdk:: sp_runtime:: generic:: UncheckedExtrinsic {
1314- function : RuntimeCall :: Revive ( polkadot_sdk:: pallet_revive:: Call :: eth_transact { payload } ) ,
1315- ..
1316- } = ext. 0
1317- else {
1318- return false ;
1319- } ;
1354+ for tx in self . tx_pool . ready ( ) {
1355+ if let Some ( sender) = extract_sender ( tx. data ( ) ) {
1356+ if sender == address {
1357+ invalid_txs. insert ( * tx. hash ( ) , None ) ;
1358+ }
1359+ }
1360+ }
1361+
1362+ for tx in self . tx_pool . futures ( ) {
1363+ if let Some ( sender) = extract_sender ( tx. data ( ) ) {
1364+ if sender == address {
1365+ invalid_txs. insert ( * tx. hash ( ) , None ) ;
1366+ }
1367+ }
1368+ }
13201369
1321- let tx_eth_hash = keccak_256 ( & payload) ;
1322- B256 :: from_slice ( & tx_eth_hash) == target_eth_hash
1370+ if !invalid_txs. is_empty ( ) {
1371+ self . tx_pool . report_invalid ( None , invalid_txs) . await ;
1372+ }
1373+
1374+ Ok ( ( ) )
1375+ }
13231376}
13241377
13251378fn new_contract_info ( address : & Address , code_hash : H256 , nonce : Nonce ) -> ContractInfo {
@@ -1437,16 +1490,7 @@ async fn create_revive_rpc_client(
14371490 let receipt_extractor = ReceiptExtractor :: new_with_custom_address_recovery (
14381491 api. clone ( ) ,
14391492 None ,
1440- Arc :: new ( |signed_tx : & TransactionSigned | {
1441- let sig = signed_tx. raw_signature ( ) ?;
1442- if sig[ ..12 ] == [ 0 ; 12 ] && sig[ 32 ..64 ] == [ 0 ; 32 ] {
1443- let mut res = [ 0 ; 20 ] ;
1444- res. copy_from_slice ( & sig[ 12 ..32 ] ) ;
1445- Ok ( H160 :: from ( res) )
1446- } else {
1447- signed_tx. recover_eth_address ( )
1448- }
1449- } ) ,
1493+ Arc :: new ( recover_maybe_impersonated_address) ,
14501494 )
14511495 . await
14521496 . map_err ( |err| Error :: ReviveRpc ( EthRpcError :: ClientError ( err) ) ) ?;
0 commit comments