From e4676f7469956503e8bf85999f769ff21d8e19af Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Tue, 22 Oct 2024 18:07:10 +0200 Subject: [PATCH 01/11] mempool: modify maintain_transaction_pool to prune txs --- Makefile | 3 + src/main.rs | 8 +- src/pool/mempool.rs | 46 ++++++- src/providers/eth_provider/database/filter.rs | 2 +- tests/tests/mempool.rs | 128 +++++++++++++++++- 5 files changed, 174 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 13375173a..7d40c002d 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,9 @@ test-ci: load-env test-target: load-env cargo test --tests --all-features $(TARGET) -- --nocapture +test-target1: load-env + cargo test --package kakarot-rpc --test entry --all-features -- tests::mempool::test_maintain_mempool --exact --show-output + benchmark: cd benchmarks && bun i && bun run benchmark diff --git a/src/main.rs b/src/main.rs index dedf9397f..7bde9dc00 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -use std::{env::var, str::FromStr, sync::Arc}; - use dotenvy::dotenv; use eyre::Result; use kakarot_rpc::{ @@ -16,6 +14,7 @@ use starknet::{ core::types::Felt, providers::{jsonrpc::HttpTransport, JsonRpcClient}, }; +use std::{env::var, str::FromStr, sync::Arc, time::Duration}; use tracing_opentelemetry::MetricsLayer; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer}; @@ -57,8 +56,11 @@ async fn main() -> Result<()> { var("RELAYERS_ADDRESSES")?.split(',').filter_map(|addr| Felt::from_str(addr).ok()).collect::>(); AccountManager::from_addresses(addresses, Arc::clone(ð_client)).await?.start(); + // Transactions should be pruned after 5 minutes in the mempool + let prune_duration = Duration::from_secs(300); + // Start the maintenance of the mempool - maintain_transaction_pool(Arc::clone(ð_client)); + maintain_transaction_pool(Arc::clone(ð_client), prune_duration); // Setup the RPC module let kakarot_rpc_module = KakarotRpcModuleBuilder::new(eth_client).rpc_module()?; diff --git a/src/pool/mempool.rs b/src/pool/mempool.rs index de5ad2818..ff037511f 100644 --- a/src/pool/mempool.rs +++ b/src/pool/mempool.rs @@ -24,7 +24,7 @@ use starknet::{ providers::{jsonrpc::HttpTransport, JsonRpcClient}, }; use std::{collections::HashMap, sync::Arc, time::Duration}; -use tokio::sync::Mutex; +use tokio::{sync::Mutex, time::Instant}; use tracing::instrument; /// A type alias for the Kakarot Transaction Validator. @@ -268,13 +268,27 @@ where /// Maintains the transaction pool by periodically polling the database in order to /// fetch the latest block and mark the block's transactions as mined by the node. -pub fn maintain_transaction_pool(eth_client: Arc>) +pub fn maintain_transaction_pool(eth_client: Arc>, prune_duration: Duration) where SP: starknet::providers::Provider + Send + Sync + Clone + 'static, { tokio::spawn(async move { let mut block_number = 0u64; + + // Mapping to store the transactions in the mempool with a timestamp to potentially prune them + let mut mempool_transactions = HashMap::new(); + loop { + // Adding the transactions to the mempool mapping with a timestamp + for tx in eth_client + .mempool() + .queued_transactions() + .into_iter() + .chain(eth_client.mempool().pending_transactions()) + { + mempool_transactions.entry(*tx.hash()).or_insert_with(Instant::now); + } + // Fetch the latest block number let Ok(current_block_number) = eth_client.eth_provider().block_number().await else { tracing::error!(target: "maintain_transaction_pool", "failed to fetch current block number"); @@ -305,7 +319,7 @@ where chain_spec.base_fee_params_at_timestamp(latest_header.timestamp + 12), ) .unwrap_or_default(), - pending_blob_fee: latest_header.next_block_blob_fee(), + pending_blob_fee: None, }; eth_client.mempool().set_block_info(info); @@ -324,7 +338,31 @@ where } let sealed_block = latest_block.seal(hash); - let mined_transactions = sealed_block.body.transactions.iter().map(|tx| tx.hash).collect(); + let mut mined_transactions: Vec<_> = + sealed_block.body.transactions.iter().map(|tx| tx.hash).collect(); + + // Prune mined transactions from the mempool mapping + for tx_hash in &mined_transactions { + mempool_transactions.remove(tx_hash); + } + + // Prune transactions that have been in the mempool for more than 5 minutes + let now = Instant::now(); + + for (tx_hash, timestamp) in &mempool_transactions.clone() { + // - If the transaction has been in the mempool for more than 5 minutes + // - And the transaction is in the mempool right now + if now.duration_since(*timestamp) > prune_duration && eth_client.mempool().contains(tx_hash) + { + tracing::warn!("Transaction {} in mempool for more than 5 minutes. Pruning.", tx_hash); + + // Add the transaction to the mined transactions so that it can be pruned + mined_transactions.push(*tx_hash); + + // Remove the transaction from the mempool mapping + mempool_transactions.remove(tx_hash); + } + } // Canonical update let update = CanonicalStateUpdate { diff --git a/src/providers/eth_provider/database/filter.rs b/src/providers/eth_provider/database/filter.rs index c1826d884..467682ae2 100644 --- a/src/providers/eth_provider/database/filter.rs +++ b/src/providers/eth_provider/database/filter.rs @@ -256,7 +256,7 @@ impl EthDatabaseFilterBuilder { } } -pub(crate) fn format_hex(value: impl LowerHex, width: usize) -> String { +pub fn format_hex(value: impl LowerHex, width: usize) -> String { // Add 2 to the width to account for the 0x prefix. let s = format!("{:#0width$x}", value, width = width + 2); // `s.len() < width` can happen because of the LowerHex implementation diff --git a/tests/tests/mempool.rs b/tests/tests/mempool.rs index 1c85aadc8..613b7d2db 100644 --- a/tests/tests/mempool.rs +++ b/tests/tests/mempool.rs @@ -1,19 +1,35 @@ #![allow(clippy::used_underscore_binding)] #![cfg(feature = "testing")] -use alloy_consensus::TxEip1559; +use alloy_consensus::{TxEip1559, EMPTY_ROOT_HASH}; use alloy_eips::eip2718::Encodable2718; -use alloy_primitives::{Address, TxKind, U256}; +use alloy_primitives::{Address, TxKind, B64, U256}; +use alloy_rpc_types::Header; use kakarot_rpc::{ - providers::eth_provider::{error::SignatureError, ChainProvider}, + pool::mempool::maintain_transaction_pool, + providers::eth_provider::{ + constant::U64_HEX_STRING_LEN, + database::{ + filter::{self, format_hex, EthDatabaseFilterBuilder}, + types::header::StoredHeader, + }, + error::SignatureError, + ChainProvider, + }, test_utils::{ eoa::Eoa, - fixtures::{katana_empty, setup}, + fixtures::{katana, katana_empty, setup}, katana::Katana, }, }; +use mongodb::{ + bson::doc, + options::{UpdateModifications, UpdateOptions}, +}; use reth_primitives::{sign_message, Transaction, TransactionSigned, TransactionSignedEcRecovered}; -use reth_transaction_pool::{EthPooledTransaction, TransactionOrigin, TransactionPool}; +use reth_transaction_pool::{EthPooledTransaction, PoolTransaction, TransactionOrigin, TransactionPool}; +use revm_primitives::B256; use rstest::*; +use std::{sync::Arc, time::Duration}; #[rstest] #[awt] @@ -351,3 +367,105 @@ pub async fn create_sample_transactions( } Ok(transactions) } + +#[rstest] +#[awt] +#[tokio::test(flavor = "multi_thread")] +async fn test_maintain_mempool(#[future] katana: Katana, _setup: ()) { + let eth_client = Arc::new(katana.eth_client()); + + // Create two sample transactions at once + let transactions = create_sample_transactions(&katana, 2).await.expect("Failed to create sample transactions"); + + // Extract and ensure we have two valid transactions from the transaction list. + let ((transaction1, _), (transaction2, _)) = ( + transactions.first().expect("Expected at least one transaction").clone(), + transactions.get(1).expect("Expected at least two transactions").clone(), + ); + + // Add transactions to the mempool + eth_client.mempool().add_transaction(TransactionOrigin::Private, transaction1.clone()).await.unwrap(); + eth_client.mempool().add_transaction(TransactionOrigin::Private, transaction2.clone()).await.unwrap(); + + // Start maintaining the transaction pool + // + // This task will periodically prune the mempool based on the given prune_duration. + // For testing purposes, we set the prune_duration to 1 second. + let prune_duration = Duration::from_secs(1); + let eth_client_clone = Arc::clone(ð_client); + let maintain_task = tokio::spawn(async move { + maintain_transaction_pool(eth_client_clone, prune_duration); + }); + + // Initialize the block number based on the current blockchain state from katana. + let mut last_block_number = katana.block_number(); + + // Loop to simulate new blocks being added to the blockchain every 100 milliseconds. + for _ in 0..10 { + // Sleep for 100 milliseconds to simulate the passage of time between blocks. + tokio::time::sleep(Duration::from_millis(100)).await; + + // Increment the block number to simulate the blockchain progressing. + last_block_number += 1; + + // Format the block number in both padded and unpadded hexadecimal formats. + let unpadded_block_number = format_hex(last_block_number, 0); + let padded_block_number = format_hex(last_block_number, U64_HEX_STRING_LEN); + + // Get the block header collection from the database. + let header_collection = eth_client.eth_provider().database().collection::(); + + // Build a filter for updating the header based on the new block number. + let filter = EthDatabaseFilterBuilder::::default().with_block_number(last_block_number).build(); + + // Insert a new header for the new block number in the database. + eth_client + .eth_provider() + .database() + .update_one( + StoredHeader { + header: Header { + hash: B256::random(), + total_difficulty: Some(U256::default()), + mix_hash: Some(B256::default()), + nonce: Some(B64::default()), + withdrawals_root: Some(EMPTY_ROOT_HASH), + base_fee_per_gas: Some(0), + blob_gas_used: Some(0), + excess_blob_gas: Some(0), + number: last_block_number, + ..Default::default() + }, + }, + filter, + true, + ) + .await + .expect("Failed to update header in database"); + + // Update the header collection with the padded block number in the database. + header_collection + .update_one( + doc! {"header.number": unpadded_block_number}, + UpdateModifications::Document(doc! {"$set": {"header.number": padded_block_number}}), + ) + .with_options(UpdateOptions::builder().upsert(true).build()) + .await + .expect("Failed to update block number"); + + // Check if both transactions are still in the mempool. + // We expect them to still be in the mempool until 1 second has elapsed. + assert!(eth_client.mempool().contains(transaction1.hash()), "Transaction 1 should still be in the mempool"); + assert!(eth_client.mempool().contains(transaction2.hash()), "Transaction 2 should still be in the mempool"); + } + + // Sleep for some additional time to allow the pruning to occur. + tokio::time::sleep(Duration::from_millis(200)).await; + + // Verify that both transactions have been pruned from the mempool after the pruning duration. + assert!(!eth_client.mempool().contains(transaction1.hash()), "Transaction 1 should be pruned after 1 second"); + assert!(!eth_client.mempool().contains(transaction2.hash()), "Transaction 2 should be pruned after 1 second"); + + // Ensure the background task is stopped gracefully. + maintain_task.abort(); +} From 87c1a7ca173113fdcf943a2acd94020abd22b7f4 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Tue, 22 Oct 2024 18:10:41 +0200 Subject: [PATCH 02/11] clean up --- Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Makefile b/Makefile index 7d40c002d..13375173a 100644 --- a/Makefile +++ b/Makefile @@ -61,9 +61,6 @@ test-ci: load-env test-target: load-env cargo test --tests --all-features $(TARGET) -- --nocapture -test-target1: load-env - cargo test --package kakarot-rpc --test entry --all-features -- tests::mempool::test_maintain_mempool --exact --show-output - benchmark: cd benchmarks && bun i && bun run benchmark From c35ce2e593eb487b205be037e2b0fb20ab2825b0 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Thu, 24 Oct 2024 11:23:08 +0200 Subject: [PATCH 03/11] update pruning time --- tests/tests/mempool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests/mempool.rs b/tests/tests/mempool.rs index 613b7d2db..e8a167f18 100644 --- a/tests/tests/mempool.rs +++ b/tests/tests/mempool.rs @@ -460,7 +460,7 @@ async fn test_maintain_mempool(#[future] katana: Katana, _setup: ()) { } // Sleep for some additional time to allow the pruning to occur. - tokio::time::sleep(Duration::from_millis(200)).await; + tokio::time::sleep(Duration::from_secs(1)).await; // Verify that both transactions have been pruned from the mempool after the pruning duration. assert!(!eth_client.mempool().contains(transaction1.hash()), "Transaction 1 should be pruned after 1 second"); From c9ca10759fd9bc5a35adeff69aa885ec3f883983 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Sun, 27 Oct 2024 17:30:34 +0100 Subject: [PATCH 04/11] fix comment --- tests/tests/mempool.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/tests/mempool.rs b/tests/tests/mempool.rs index e8a167f18..a2610c9bf 100644 --- a/tests/tests/mempool.rs +++ b/tests/tests/mempool.rs @@ -390,8 +390,8 @@ async fn test_maintain_mempool(#[future] katana: Katana, _setup: ()) { // Start maintaining the transaction pool // // This task will periodically prune the mempool based on the given prune_duration. - // For testing purposes, we set the prune_duration to 1 second. - let prune_duration = Duration::from_secs(1); + // For testing purposes, we set the prune_duration to 100 milliseconds. + let prune_duration = Duration::from_millis(100); let eth_client_clone = Arc::clone(ð_client); let maintain_task = tokio::spawn(async move { maintain_transaction_pool(eth_client_clone, prune_duration); @@ -402,8 +402,8 @@ async fn test_maintain_mempool(#[future] katana: Katana, _setup: ()) { // Loop to simulate new blocks being added to the blockchain every 100 milliseconds. for _ in 0..10 { - // Sleep for 100 milliseconds to simulate the passage of time between blocks. - tokio::time::sleep(Duration::from_millis(100)).await; + // Sleep for 10 milliseconds to simulate the passage of time between blocks. + tokio::time::sleep(Duration::from_millis(10)).await; // Increment the block number to simulate the blockchain progressing. last_block_number += 1; @@ -460,7 +460,7 @@ async fn test_maintain_mempool(#[future] katana: Katana, _setup: ()) { } // Sleep for some additional time to allow the pruning to occur. - tokio::time::sleep(Duration::from_secs(1)).await; + tokio::time::sleep(Duration::from_millis(100)).await; // Verify that both transactions have been pruned from the mempool after the pruning duration. assert!(!eth_client.mempool().contains(transaction1.hash()), "Transaction 1 should be pruned after 1 second"); From d813d774551cea4223644d729b03ed99d04ae059 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Sun, 27 Oct 2024 17:31:51 +0100 Subject: [PATCH 05/11] fix comment --- tests/tests/mempool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests/mempool.rs b/tests/tests/mempool.rs index a2610c9bf..6ebabf40b 100644 --- a/tests/tests/mempool.rs +++ b/tests/tests/mempool.rs @@ -401,7 +401,7 @@ async fn test_maintain_mempool(#[future] katana: Katana, _setup: ()) { let mut last_block_number = katana.block_number(); // Loop to simulate new blocks being added to the blockchain every 100 milliseconds. - for _ in 0..10 { + for _ in 0..9 { // Sleep for 10 milliseconds to simulate the passage of time between blocks. tokio::time::sleep(Duration::from_millis(10)).await; From b94eb81fcd49a8e1d5fc9e7a79f8502394ec7b7b Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Sun, 27 Oct 2024 18:32:23 +0100 Subject: [PATCH 06/11] fix prune time check --- tests/tests/mempool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests/mempool.rs b/tests/tests/mempool.rs index 6ebabf40b..6468ddab0 100644 --- a/tests/tests/mempool.rs +++ b/tests/tests/mempool.rs @@ -460,7 +460,7 @@ async fn test_maintain_mempool(#[future] katana: Katana, _setup: ()) { } // Sleep for some additional time to allow the pruning to occur. - tokio::time::sleep(Duration::from_millis(100)).await; + tokio::time::sleep(Duration::from_millis(500)).await; // Verify that both transactions have been pruned from the mempool after the pruning duration. assert!(!eth_client.mempool().contains(transaction1.hash()), "Transaction 1 should be pruned after 1 second"); From 30a83061e6cd779454118b5ebe2d13a1aeed921b Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Mon, 28 Oct 2024 15:29:48 +0100 Subject: [PATCH 07/11] fix comments --- src/main.rs | 12 ++++++------ src/pool/constants.rs | 5 +++++ src/pool/mod.rs | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7bde9dc00..b7c3b6fde 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,10 @@ use kakarot_rpc::{ client::EthClient, constants::{KAKAROT_RPC_CONFIG, RPC_CONFIG}, eth_rpc::{rpc::KakarotRpcModuleBuilder, run_server}, - pool::mempool::{maintain_transaction_pool, AccountManager}, + pool::{ + constants::PRUNE_DURATION, + mempool::{maintain_transaction_pool, AccountManager}, + }, providers::eth_provider::database::Database, }; use mongodb::options::{DatabaseOptions, ReadConcern, WriteConcern}; @@ -14,7 +17,7 @@ use starknet::{ core::types::Felt, providers::{jsonrpc::HttpTransport, JsonRpcClient}, }; -use std::{env::var, str::FromStr, sync::Arc, time::Duration}; +use std::{env::var, str::FromStr, sync::Arc}; use tracing_opentelemetry::MetricsLayer; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer}; @@ -56,11 +59,8 @@ async fn main() -> Result<()> { var("RELAYERS_ADDRESSES")?.split(',').filter_map(|addr| Felt::from_str(addr).ok()).collect::>(); AccountManager::from_addresses(addresses, Arc::clone(ð_client)).await?.start(); - // Transactions should be pruned after 5 minutes in the mempool - let prune_duration = Duration::from_secs(300); - // Start the maintenance of the mempool - maintain_transaction_pool(Arc::clone(ð_client), prune_duration); + maintain_transaction_pool(Arc::clone(ð_client), PRUNE_DURATION); // Setup the RPC module let kakarot_rpc_module = KakarotRpcModuleBuilder::new(eth_client).rpc_module()?; diff --git a/src/pool/constants.rs b/src/pool/constants.rs index 752ce8384..9435f6a84 100644 --- a/src/pool/constants.rs +++ b/src/pool/constants.rs @@ -1 +1,6 @@ +use std::time::Duration; + pub(super) static ONE_TENTH_ETH: u64 = 10u64.pow(17); + +// Transactions should be pruned after 5 minutes in the mempool +pub const PRUNE_DURATION: Duration = Duration::from_secs(300); diff --git a/src/pool/mod.rs b/src/pool/mod.rs index 252056409..a9ec7c4ce 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -1,3 +1,3 @@ -mod constants; +pub mod constants; pub mod mempool; pub mod validate; From d0b81692c78add5b53d79ce83d6b4c997a99f6e3 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Mon, 28 Oct 2024 15:32:21 +0100 Subject: [PATCH 08/11] fix conflicts --- src/main.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 587817604..f5342c21a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,10 @@ use kakarot_rpc::{ client::EthClient, constants::{KAKAROT_RPC_CONFIG, RPC_CONFIG}, eth_rpc::{rpc::KakarotRpcModuleBuilder, run_server}, - pool::mempool::{maintain_transaction_pool, AccountManager}, + pool::{ + constants::PRUNE_DURATION, + mempool::{maintain_transaction_pool, AccountManager}, + }, providers::eth_provider::{ database::Database, starknet::kakarot_core::{core::KakarotCoreReader, KAKAROT_ADDRESS}, From 6afc92d3e117812561e9c80504ac6eedaadfc2d6 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Mon, 28 Oct 2024 15:34:58 +0100 Subject: [PATCH 09/11] fix --- src/pool/mempool.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pool/mempool.rs b/src/pool/mempool.rs index 51daad365..4d7b72145 100644 --- a/src/pool/mempool.rs +++ b/src/pool/mempool.rs @@ -363,18 +363,18 @@ where // Prune transactions that have been in the mempool for more than 5 minutes let now = Instant::now(); - for (tx_hash, timestamp) in &mempool_transactions.clone() { + for (tx_hash, timestamp) in mempool_transactions.clone() { // - If the transaction has been in the mempool for more than 5 minutes // - And the transaction is in the mempool right now - if now.duration_since(*timestamp) > prune_duration && eth_client.mempool().contains(tx_hash) + if now.duration_since(timestamp) > prune_duration && eth_client.mempool().contains(&tx_hash) { tracing::warn!("Transaction {} in mempool for more than 5 minutes. Pruning.", tx_hash); // Add the transaction to the mined transactions so that it can be pruned - mined_transactions.push(*tx_hash); + mined_transactions.push(tx_hash); // Remove the transaction from the mempool mapping - mempool_transactions.remove(tx_hash); + mempool_transactions.remove(&tx_hash); } } From 6c1a3cffffe322542aa5b91ccceedb33c3075c52 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Mon, 28 Oct 2024 15:40:08 +0100 Subject: [PATCH 10/11] fix --- src/pool/mempool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pool/mempool.rs b/src/pool/mempool.rs index 4d7b72145..bdb1d6d12 100644 --- a/src/pool/mempool.rs +++ b/src/pool/mempool.rs @@ -368,7 +368,7 @@ where // - And the transaction is in the mempool right now if now.duration_since(timestamp) > prune_duration && eth_client.mempool().contains(&tx_hash) { - tracing::warn!("Transaction {} in mempool for more than 5 minutes. Pruning.", tx_hash); + tracing::warn!(target: "maintain_transaction_pool", tx_hash, "pruning"); // Add the transaction to the mined transactions so that it can be pruned mined_transactions.push(tx_hash); From c8f53c075379ed0d8a38fe9fba1ab3fbc54ef669 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Mon, 28 Oct 2024 16:31:51 +0100 Subject: [PATCH 11/11] fix --- src/pool/mempool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pool/mempool.rs b/src/pool/mempool.rs index bdb1d6d12..468fe7d0a 100644 --- a/src/pool/mempool.rs +++ b/src/pool/mempool.rs @@ -368,7 +368,7 @@ where // - And the transaction is in the mempool right now if now.duration_since(timestamp) > prune_duration && eth_client.mempool().contains(&tx_hash) { - tracing::warn!(target: "maintain_transaction_pool", tx_hash, "pruning"); + tracing::warn!(target: "maintain_transaction_pool", ?tx_hash, "pruning"); // Add the transaction to the mined transactions so that it can be pruned mined_transactions.push(tx_hash);