From f9ba3322a1a43576a7aa679f471233bf5c765858 Mon Sep 17 00:00:00 2001 From: Green Baneling Date: Sat, 9 Mar 2024 01:24:03 +0100 Subject: [PATCH 1/2] Added blacklisting transaction on the `TxPool` (#1743) Co-authored-by: Brandon Kite --- CHANGELOG.md | 39 +++++++ bin/fuel-core/src/cli/run.rs | 67 ++++++----- bin/fuel-core/src/cli/run/tx_pool.rs | 104 +++++++++++++++++ crates/services/txpool/src/config.rs | 62 +++++++++- crates/services/txpool/src/txpool.rs | 96 ++++++++++++++- crates/services/txpool/src/txpool/tests.rs | 129 ++++++++++++++++++++- 6 files changed, 458 insertions(+), 39 deletions(-) create mode 100644 bin/fuel-core/src/cli/run/tx_pool.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b718210182..3755ac178ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -212,6 +212,45 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - [#1573](https://github.com/FuelLabs/fuel-core/pull/1573): Remove nested p2p request/response encoding. Only breaks p2p networking compatibility with older fuel-core versions, but is otherwise fully internal. +## [Version 0.22.4] + +### Added + +- [#1743](https://github.com/FuelLabs/fuel-core/pull/1743): Added blacklisting of the transactions on the `TxPool` level. + ```shell + --tx-blacklist-addresses + The list of banned addresses ignored by the `TxPool` + + [env: TX_BLACKLIST_ADDRESSES=] + + --tx-blacklist-coins + The list of banned coins ignored by the `TxPool` + + [env: TX_BLACKLIST_COINS=] + + --tx-blacklist-messages + The list of banned messages ignored by the `TxPool` + + [env: TX_BLACKLIST_MESSAGES=] + + --tx-blacklist-contracts + The list of banned contracts ignored by the `TxPool` + + [env: TX_BLACKLIST_CONTRACTS=] + ``` + +## [Version 0.22.3] + +### Added + +- [#1732](https://github.com/FuelLabs/fuel-core/pull/1732): Added `Clone` bounds to most datatypes of `fuel-core-client`. + +## [Version 0.22.2] + +### Added + +- [#1729](https://github.com/FuelLabs/fuel-core/pull/1729): Exposed the `schema.sdl` file from `fuel-core-client`. The user can create his own queries by using this file. + ## [Version 0.22.1] ### Fixed diff --git a/bin/fuel-core/src/cli/run.rs b/bin/fuel-core/src/cli/run.rs index e1d5499c484..0b846b5f594 100644 --- a/bin/fuel-core/src/cli/run.rs +++ b/bin/fuel-core/src/cli/run.rs @@ -1,15 +1,15 @@ #![allow(unused_variables)] use crate::{ cli::{ - run::consensus::PoATriggerArgs, + run::{ + consensus::PoATriggerArgs, + tx_pool::TxPoolArgs, + }, DEFAULT_DB_PATH, }, FuelService, }; -use anyhow::{ - anyhow, - Context, -}; +use anyhow::Context; use clap::Parser; use fuel_core::{ chain_config::{ @@ -30,7 +30,10 @@ use fuel_core::{ ServiceTrait, VMConfig, }, - txpool::Config as TxPoolConfig, + txpool::{ + config::BlackList, + Config as TxPoolConfig, + }, types::{ blockchain::primitives::SecretKeyWrapper, fuel_tx::ContractId, @@ -73,6 +76,7 @@ mod consensus; mod profiling; #[cfg(feature = "relayer")] mod relayer; +mod tx_pool; /// Run the Fuel client node locally. #[derive(Debug, Clone, Parser)] @@ -155,7 +159,11 @@ pub struct Command { /// /// If not set, `consensus_key` is used as the provider of the `Address`. #[arg(long = "coinbase-recipient", env)] - pub coinbase_recipient: Option, + pub coinbase_recipient: Option, + + /// The cli arguments supported by the `TxPool`. + #[clap(flatten)] + pub tx_pool: TxPoolArgs, #[cfg_attr(feature = "relayer", clap(flatten))] #[cfg(feature = "relayer")] @@ -178,22 +186,6 @@ pub struct Command { #[clap(long = "verify-max-relayer-wait", default_value = "30s", env)] pub max_wait_time: humantime::Duration, - /// The max time to live of the transaction inside of the `TxPool`. - #[clap(long = "tx-pool-ttl", default_value = "5m", env)] - pub tx_pool_ttl: humantime::Duration, - - /// The max number of transactions that the `TxPool` can simultaneously store. - #[clap(long = "tx-max-number", default_value = "4064", env)] - pub tx_max_number: usize, - - /// The max depth of the dependent transactions that supported by the `TxPool`. - #[clap(long = "tx-max-depth", default_value = "10", env)] - pub tx_max_depth: usize, - - /// The maximum number of active subscriptions that supported by the `TxPool`. - #[clap(long = "tx-number-active-subscriptions", default_value = "4064", env)] - pub tx_number_active_subscriptions: usize, - /// The number of reserved peers to connect to before starting to sync. #[clap(long = "min-connected-reserved-peers", default_value = "0", env)] pub min_connected_reserved_peers: usize, @@ -241,10 +233,7 @@ impl Command { metrics, max_da_lag, max_wait_time, - tx_pool_ttl, - tx_max_number, - tx_max_depth, - tx_number_active_subscriptions, + tx_pool, min_connected_reserved_peers, time_until_synced, query_log_threshold_time, @@ -301,10 +290,7 @@ impl Command { }); let coinbase_recipient = if let Some(coinbase_recipient) = coinbase_recipient { - Some( - ContractId::from_str(coinbase_recipient.as_str()) - .map_err(|err| anyhow!(err))?, - ) + Some(coinbase_recipient) } else { tracing::warn!("The coinbase recipient `ContractId` is not set!"); None @@ -324,6 +310,24 @@ impl Command { let block_importer = fuel_core::service::config::fuel_core_importer::Config::new(&chain_conf); + let TxPoolArgs { + tx_pool_ttl, + tx_max_number, + tx_max_depth, + tx_number_active_subscriptions, + tx_blacklist_addresses, + tx_blacklist_coins, + tx_blacklist_messages, + tx_blacklist_contracts, + } = tx_pool; + + let blacklist = BlackList::new( + tx_blacklist_addresses, + tx_blacklist_coins, + tx_blacklist_messages, + tx_blacklist_contracts, + ); + let config = Config { addr, api_request_timeout: api_request_timeout.into(), @@ -345,6 +349,7 @@ impl Command { metrics, tx_pool_ttl.into(), tx_number_active_subscriptions, + blacklist, ), block_producer: ProducerConfig { utxo_validation, diff --git a/bin/fuel-core/src/cli/run/tx_pool.rs b/bin/fuel-core/src/cli/run/tx_pool.rs new file mode 100644 index 00000000000..6ad5faef6f5 --- /dev/null +++ b/bin/fuel-core/src/cli/run/tx_pool.rs @@ -0,0 +1,104 @@ +//! Clap configuration related to consensus parameters + +use fuel_core::txpool::types::ContractId; +use fuel_core_types::{ + fuel_tx::{ + Address, + UtxoId, + }, + fuel_types::Nonce, +}; + +#[derive(Debug, Clone, clap::Args)] +pub struct TxPoolArgs { + /// The max time to live of the transaction inside of the `TxPool`. + #[clap(long = "tx-pool-ttl", default_value = "5m", env)] + pub tx_pool_ttl: humantime::Duration, + + /// The max number of transactions that the `TxPool` can simultaneously store. + #[clap(long = "tx-max-number", default_value = "4064", env)] + pub tx_max_number: usize, + + /// The max depth of the dependent transactions that supported by the `TxPool`. + #[clap(long = "tx-max-depth", default_value = "10", env)] + pub tx_max_depth: usize, + + /// The maximum number of active subscriptions that supported by the `TxPool`. + #[clap(long = "tx-number-active-subscriptions", default_value = "4064", env)] + pub tx_number_active_subscriptions: usize, + + /// The list of banned addresses ignored by the `TxPool`. + #[clap(long = "tx-blacklist-addresses", value_delimiter = ',', env)] + pub tx_blacklist_addresses: Vec
, + + /// The list of banned coins ignored by the `TxPool`. + #[clap(long = "tx-blacklist-coins", value_delimiter = ',', env)] + pub tx_blacklist_coins: Vec, + + /// The list of banned messages ignored by the `TxPool`. + #[clap(long = "tx-blacklist-messages", value_delimiter = ',', env)] + pub tx_blacklist_messages: Vec, + + /// The list of banned contracts ignored by the `TxPool`. + #[clap(long = "tx-blacklist-contracts", value_delimiter = ',', env)] + pub tx_blacklist_contracts: Vec, +} + +#[cfg(test)] +mod tests { + use super::*; + use clap::Parser; + use fuel_core::txpool::config::BlackList; + use test_case::test_case; + + #[derive(Debug, Clone, Parser)] + pub struct Command { + #[clap(flatten)] + tx_pool: TxPoolArgs, + } + + fn blacklist( + a: Vec
, + c: Vec, + m: Vec, + ct: Vec, + ) -> BlackList { + BlackList::new(a, c, m, ct) + } + + #[test_case(&[""] => Ok(blacklist(vec![], vec![], vec![], vec![])); "defaults works")] + #[test_case(&["", "--tx-blacklist-addresses=\ + 0x0000000000000000000000000000000000000000000000000000000000000000,\ + 0101010101010101010101010101010101010101010101010101010101010101" + ] + => Ok(blacklist(vec![[0; 32].into(), [1; 32].into()], vec![], vec![], vec![])); "addresses works")] + #[test_case(&["", "--tx-blacklist-coins=\ + 0x000000000000000000000000000000000000000000000000000000000000000002,\ + 010101010101010101010101010101010101010101010101010101010101010103" + ] + => Ok(blacklist(vec![], vec![UtxoId::new([0; 32].into(), 2), UtxoId::new([1; 32].into(), 3)], vec![], vec![])); "coins works")] + #[test_case(&["", "--tx-blacklist-messages=\ + 0x0000000000000000000000000000000000000000000000000000000000000000,\ + 0101010101010101010101010101010101010101010101010101010101010101" + ] + => Ok(blacklist(vec![], vec![], vec![[0; 32].into(), [1; 32].into()], vec![])); "messages works")] + #[test_case(&["", "--tx-blacklist-contracts=\ + 0x0000000000000000000000000000000000000000000000000000000000000000,\ + 0101010101010101010101010101010101010101010101010101010101010101" + ] + => Ok(blacklist(vec![], vec![], vec![], vec![[0; 32].into(), [1; 32].into()])); "contracts works")] + fn parse(args: &[&str]) -> Result { + let command: Command = + Command::try_parse_from(args).map_err(|e| e.to_string())?; + let args = command.tx_pool; + + let blacklist = blacklist( + args.tx_blacklist_addresses, + args.tx_blacklist_coins, + args.tx_blacklist_messages, + args.tx_blacklist_contracts, + ); + + Ok(blacklist) + } +} diff --git a/crates/services/txpool/src/config.rs b/crates/services/txpool/src/config.rs index 78b82c5445c..6c6a46d0525 100644 --- a/crates/services/txpool/src/config.rs +++ b/crates/services/txpool/src/config.rs @@ -1,5 +1,60 @@ +use crate::types::ContractId; use fuel_core_chain_config::ChainConfig; -use std::time::Duration; +use fuel_core_types::{ + fuel_tx::{ + Address, + UtxoId, + }, + fuel_types::Nonce, +}; +use std::{ + collections::HashSet, + time::Duration, +}; + +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct BlackList { + /// Blacklisted addresses. + pub(crate) owners: HashSet
, + /// Blacklisted UTXO ids. + pub(crate) coins: HashSet, + /// Blacklisted messages by `Nonce`. + pub(crate) messages: HashSet, + /// Blacklisted contracts. + pub(crate) contracts: HashSet, +} + +impl BlackList { + pub fn new( + owners: Vec
, + utxo_ids: Vec, + messages: Vec, + contracts: Vec, + ) -> Self { + Self { + owners: owners.into_iter().collect(), + coins: utxo_ids.into_iter().collect(), + messages: messages.into_iter().collect(), + contracts: contracts.into_iter().collect(), + } + } + + pub fn contains_address(&self, address: &Address) -> bool { + self.owners.contains(address) + } + + pub fn contains_coin(&self, utxo_id: &UtxoId) -> bool { + self.coins.contains(utxo_id) + } + + pub fn contains_message(&self, nonce: &Nonce) -> bool { + self.messages.contains(nonce) + } + + pub fn contains_contract(&self, contract_id: &ContractId) -> bool { + self.contracts.contains(contract_id) + } +} #[derive(Debug, Clone)] pub struct Config { @@ -19,6 +74,8 @@ pub struct Config { pub transaction_ttl: Duration, /// The number of allowed active transaction status subscriptions. pub number_of_active_subscription: usize, + /// The blacklist used to validate transaction. + pub blacklist: BlackList, } impl Default for Config { @@ -40,6 +97,7 @@ impl Default for Config { metrics, transaction_ttl, number_of_active_subscription, + Default::default(), ) } } @@ -55,6 +113,7 @@ impl Config { metrics: bool, transaction_ttl: Duration, number_of_active_subscription: usize, + blacklist: BlackList, ) -> Self { // # Dev-note: If you add a new field, be sure that this field is propagated correctly // in all places where `new` is used. @@ -67,6 +126,7 @@ impl Config { metrics, transaction_ttl, number_of_active_subscription, + blacklist, } } } diff --git a/crates/services/txpool/src/txpool.rs b/crates/services/txpool/src/txpool.rs index 6929bf8ad01..a86a5e5ac9a 100644 --- a/crates/services/txpool/src/txpool.rs +++ b/crates/services/txpool/src/txpool.rs @@ -46,6 +46,11 @@ use std::{ }; use tokio_rayon::AsyncRayonHandle; +#[cfg(test)] +mod test_helpers; +#[cfg(test)] +mod tests; + #[derive(Debug, Clone)] pub struct TxPool { by_hash: HashMap, @@ -75,6 +80,11 @@ impl TxPool { &self.config } + #[cfg(test)] + pub fn config_mut(&mut self) -> &mut Config { + &mut self.config + } + pub fn txs(&self) -> &HashMap { &self.by_hash } @@ -236,6 +246,85 @@ impl TxPool { result } + + fn check_blacklisting(&self, tx: &PoolTransaction) -> anyhow::Result<()> { + for input in tx.inputs() { + match input { + Input::CoinSigned(CoinSigned { utxo_id, owner, .. }) + | Input::CoinPredicate(CoinPredicate { utxo_id, owner, .. }) => { + if self.config.blacklist.contains_coin(utxo_id) { + return Err(anyhow::anyhow!( + "The UTXO `{}` is blacklisted", + utxo_id + )) + } + if self.config.blacklist.contains_address(owner) { + return Err(anyhow::anyhow!( + "The owner `{}` is blacklisted", + owner + )) + } + } + Input::Contract(contract) => { + if self + .config + .blacklist + .contains_contract(&contract.contract_id) + { + return Err(anyhow::anyhow!( + "The contract `{}` is blacklisted", + contract.contract_id + )) + } + } + Input::MessageCoinSigned(MessageCoinSigned { + nonce, + sender, + recipient, + .. + }) + | Input::MessageCoinPredicate(MessageCoinPredicate { + nonce, + sender, + recipient, + .. + }) + | Input::MessageDataSigned(MessageDataSigned { + nonce, + sender, + recipient, + .. + }) + | Input::MessageDataPredicate(MessageDataPredicate { + nonce, + sender, + recipient, + .. + }) => { + if self.config.blacklist.contains_message(nonce) { + return Err(anyhow::anyhow!( + "The message `{}` is blacklisted", + nonce + )) + } + if self.config.blacklist.contains_address(sender) { + return Err(anyhow::anyhow!( + "The sender `{}` is blacklisted", + sender + )) + } + if self.config.blacklist.contains_address(recipient) { + return Err(anyhow::anyhow!( + "The recipient `{}` is blacklisted", + recipient + )) + } + } + } + } + + Ok(()) + } } impl TxPool @@ -267,6 +356,8 @@ where CheckedTransaction::Mint(_) => return Err(Error::MintIsDisallowed), }); + self.check_blacklisting(tx.as_ref())?; + if !tx.is_computed() { return Err(Error::NoMetadata) } @@ -469,8 +560,3 @@ impl ParallelExecutor for TokioWithRayon { futures::future::join_all(futures).await } } - -#[cfg(test)] -mod test_helpers; -#[cfg(test)] -mod tests; diff --git a/crates/services/txpool/src/txpool/tests.rs b/crates/services/txpool/src/txpool/tests.rs index f601aa9a6c4..75bf20e1ab9 100644 --- a/crates/services/txpool/src/txpool/tests.rs +++ b/crates/services/txpool/src/txpool/tests.rs @@ -37,6 +37,8 @@ use fuel_core_types::{ Checked, }, }; + +use fuel_core_types::fuel_tx::Finalizable; use std::{ cmp::Reverse, collections::HashMap, @@ -78,6 +80,126 @@ async fn insert_simple_tx_succeeds() { .expect("Transaction should be OK, got Err"); } +#[tokio::test] +async fn insert_simple_tx_with_blacklisted_utxo_id_fails() { + let mut rng = StdRng::seed_from_u64(0); + let db = MockDb::default(); + let mut txpool = TxPool::new(Default::default(), db.clone()); + + let (_, gas_coin) = setup_coin(&mut rng, Some(&txpool.database)); + let tx = TransactionBuilder::script(vec![], vec![]) + .script_gas_limit(GAS_LIMIT) + .add_input(gas_coin.clone()) + .finalize_as_transaction(); + let tx = check_unwrap_tx(tx, db.clone(), &txpool.config).await; + let utxo_id = *gas_coin.utxo_id().unwrap(); + + // Given + txpool.config_mut().blacklist.coins.insert(utxo_id); + + // When + let result = txpool.insert_inner(tx); + + // Then + assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains(format!("The UTXO `{}` is blacklisted", utxo_id).as_str())); +} + +#[tokio::test] +async fn insert_simple_tx_with_blacklisted_owner_fails() { + let mut rng = StdRng::seed_from_u64(0); + let db = MockDb::default(); + let mut txpool = TxPool::new(Default::default(), db.clone()); + + let (_, gas_coin) = setup_coin(&mut rng, Some(&txpool.database)); + let tx = TransactionBuilder::script(vec![], vec![]) + .script_gas_limit(GAS_LIMIT) + .add_input(gas_coin.clone()) + .finalize_as_transaction(); + let tx = check_unwrap_tx(tx, db.clone(), &txpool.config).await; + let owner = *gas_coin.input_owner().unwrap(); + + // Given + txpool.config_mut().blacklist.owners.insert(owner); + + // When + let result = txpool.insert_inner(tx); + + // Then + assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains(format!("The owner `{}` is blacklisted", owner).as_str())); +} + +#[tokio::test] +async fn insert_simple_tx_with_blacklisted_contract_fails() { + let mut rng = StdRng::seed_from_u64(0); + let db = MockDb::default(); + let mut txpool = TxPool::new(Default::default(), db.clone()); + let contract_id = Contract::EMPTY_CONTRACT_ID; + + let (_, gas_coin) = setup_coin(&mut rng, Some(&txpool.database)); + let tx = TransactionBuilder::script(vec![], vec![]) + .script_gas_limit(GAS_LIMIT) + .add_input(gas_coin.clone()) + .add_input(create_contract_input( + Default::default(), + Default::default(), + contract_id, + )) + .add_output(Output::contract(1, Default::default(), Default::default())) + .finalize_as_transaction(); + let tx = check_unwrap_tx(tx, db.clone(), &txpool.config).await; + + // Given + txpool.config_mut().blacklist.contracts.insert(contract_id); + + // When + let result = txpool.insert_inner(tx); + + // Then + assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains(format!("The contract `{}` is blacklisted", contract_id).as_str())); +} + +#[tokio::test] +async fn insert_simple_tx_with_blacklisted_message_fails() { + let (message, input) = create_message_predicate_from_message(5000, 0); + + let tx = TransactionBuilder::script(vec![], vec![]) + .script_gas_limit(GAS_LIMIT) + .add_input(input) + .finalize_as_transaction(); + + let nonce = message.nonce; + let db = MockDb::default(); + db.insert_message(message); + let mut txpool = TxPool::new(Default::default(), db.clone()); + + let tx = check_unwrap_tx(tx, db.clone(), &txpool.config).await; + + // Given + txpool.config_mut().blacklist.messages.insert(nonce); + + // When + let result = txpool.insert_inner(tx); + + // Then + assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains(format!("The message `{}` is blacklisted", nonce).as_str())); +} + #[tokio::test] async fn insert_simple_tx_dependency_chain_succeeds() { let mut context = TextContext::default(); @@ -225,8 +347,11 @@ async fn not_inserted_known_tx() { let context = TextContext::default().config(config); let mut txpool = context.build(); - let tx = Transaction::default_test_tx(); - let tx = check_unwrap_tx(tx, &txpool.config).await; + let tx = TransactionBuilder::script(vec![], vec![]) + .add_random_fee_input() + .finalize() + .into(); + let tx = check_unwrap_tx(tx, db.clone(), &txpool.config).await; txpool .insert_single(tx.clone()) From b9c0328497c4014ffb4289c00df0b85c3aa4eea4 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Mon, 11 Mar 2024 12:16:59 +0100 Subject: [PATCH 2/2] Fixed compilation errors --- crates/services/txpool/src/txpool.rs | 47 ++++++++++------------ crates/services/txpool/src/txpool/tests.rs | 47 ++++++++++------------ crates/types/src/services/txpool.rs | 9 +++++ 3 files changed, 53 insertions(+), 50 deletions(-) diff --git a/crates/services/txpool/src/txpool.rs b/crates/services/txpool/src/txpool.rs index a86a5e5ac9a..0995aa68e56 100644 --- a/crates/services/txpool/src/txpool.rs +++ b/crates/services/txpool/src/txpool.rs @@ -35,6 +35,21 @@ use fuel_core_types::{ use fuel_core_metrics::txpool_metrics::txpool_metrics; use fuel_core_storage::transactional::AtomicView; use fuel_core_types::{ + fuel_tx::{ + input::{ + coin::{ + CoinPredicate, + CoinSigned, + }, + message::{ + MessageCoinPredicate, + MessageCoinSigned, + MessageDataPredicate, + MessageDataSigned, + }, + }, + Input, + }, fuel_vm::checked_transaction::CheckPredicateParams, services::executor::TransactionExecutionStatus, }; @@ -247,22 +262,16 @@ impl TxPool { result } - fn check_blacklisting(&self, tx: &PoolTransaction) -> anyhow::Result<()> { + fn check_blacklisting(&self, tx: &PoolTransaction) -> Result<(), Error> { for input in tx.inputs() { match input { Input::CoinSigned(CoinSigned { utxo_id, owner, .. }) | Input::CoinPredicate(CoinPredicate { utxo_id, owner, .. }) => { if self.config.blacklist.contains_coin(utxo_id) { - return Err(anyhow::anyhow!( - "The UTXO `{}` is blacklisted", - utxo_id - )) + return Err(Error::BlacklistedUTXO(*utxo_id)) } if self.config.blacklist.contains_address(owner) { - return Err(anyhow::anyhow!( - "The owner `{}` is blacklisted", - owner - )) + return Err(Error::BlacklistedOwner(*owner)) } } Input::Contract(contract) => { @@ -271,10 +280,7 @@ impl TxPool { .blacklist .contains_contract(&contract.contract_id) { - return Err(anyhow::anyhow!( - "The contract `{}` is blacklisted", - contract.contract_id - )) + return Err(Error::BlacklistedContract(contract.contract_id)) } } Input::MessageCoinSigned(MessageCoinSigned { @@ -302,22 +308,13 @@ impl TxPool { .. }) => { if self.config.blacklist.contains_message(nonce) { - return Err(anyhow::anyhow!( - "The message `{}` is blacklisted", - nonce - )) + return Err(Error::BlacklistedMessage(*nonce)) } if self.config.blacklist.contains_address(sender) { - return Err(anyhow::anyhow!( - "The sender `{}` is blacklisted", - sender - )) + return Err(Error::BlacklistedOwner(*sender)) } if self.config.blacklist.contains_address(recipient) { - return Err(anyhow::anyhow!( - "The recipient `{}` is blacklisted", - recipient - )) + return Err(Error::BlacklistedOwner(*recipient)) } } } diff --git a/crates/services/txpool/src/txpool/tests.rs b/crates/services/txpool/src/txpool/tests.rs index 75bf20e1ab9..8a0cad8c87b 100644 --- a/crates/services/txpool/src/txpool/tests.rs +++ b/crates/services/txpool/src/txpool/tests.rs @@ -82,23 +82,22 @@ async fn insert_simple_tx_succeeds() { #[tokio::test] async fn insert_simple_tx_with_blacklisted_utxo_id_fails() { - let mut rng = StdRng::seed_from_u64(0); - let db = MockDb::default(); - let mut txpool = TxPool::new(Default::default(), db.clone()); + let mut context = TextContext::default(); - let (_, gas_coin) = setup_coin(&mut rng, Some(&txpool.database)); + let (_, gas_coin) = context.setup_coin(); let tx = TransactionBuilder::script(vec![], vec![]) .script_gas_limit(GAS_LIMIT) .add_input(gas_coin.clone()) .finalize_as_transaction(); - let tx = check_unwrap_tx(tx, db.clone(), &txpool.config).await; + let mut txpool = context.build(); + let tx = check_unwrap_tx(tx, &txpool.config).await; let utxo_id = *gas_coin.utxo_id().unwrap(); // Given txpool.config_mut().blacklist.coins.insert(utxo_id); // When - let result = txpool.insert_inner(tx); + let result = txpool.insert_single(tx); // Then assert!(result.is_err()); @@ -110,23 +109,22 @@ async fn insert_simple_tx_with_blacklisted_utxo_id_fails() { #[tokio::test] async fn insert_simple_tx_with_blacklisted_owner_fails() { - let mut rng = StdRng::seed_from_u64(0); - let db = MockDb::default(); - let mut txpool = TxPool::new(Default::default(), db.clone()); + let mut context = TextContext::default(); - let (_, gas_coin) = setup_coin(&mut rng, Some(&txpool.database)); + let (_, gas_coin) = context.setup_coin(); let tx = TransactionBuilder::script(vec![], vec![]) .script_gas_limit(GAS_LIMIT) .add_input(gas_coin.clone()) .finalize_as_transaction(); - let tx = check_unwrap_tx(tx, db.clone(), &txpool.config).await; + let mut txpool = context.build(); + let tx = check_unwrap_tx(tx, &txpool.config).await; let owner = *gas_coin.input_owner().unwrap(); // Given txpool.config_mut().blacklist.owners.insert(owner); // When - let result = txpool.insert_inner(tx); + let result = txpool.insert_single(tx); // Then assert!(result.is_err()); @@ -138,12 +136,10 @@ async fn insert_simple_tx_with_blacklisted_owner_fails() { #[tokio::test] async fn insert_simple_tx_with_blacklisted_contract_fails() { - let mut rng = StdRng::seed_from_u64(0); - let db = MockDb::default(); - let mut txpool = TxPool::new(Default::default(), db.clone()); + let mut context = TextContext::default(); let contract_id = Contract::EMPTY_CONTRACT_ID; - let (_, gas_coin) = setup_coin(&mut rng, Some(&txpool.database)); + let (_, gas_coin) = context.setup_coin(); let tx = TransactionBuilder::script(vec![], vec![]) .script_gas_limit(GAS_LIMIT) .add_input(gas_coin.clone()) @@ -154,13 +150,14 @@ async fn insert_simple_tx_with_blacklisted_contract_fails() { )) .add_output(Output::contract(1, Default::default(), Default::default())) .finalize_as_transaction(); - let tx = check_unwrap_tx(tx, db.clone(), &txpool.config).await; + let mut txpool = context.build(); + let tx = check_unwrap_tx(tx, &txpool.config).await; // Given txpool.config_mut().blacklist.contracts.insert(contract_id); // When - let result = txpool.insert_inner(tx); + let result = txpool.insert_single(tx); // Then assert!(result.is_err()); @@ -179,18 +176,18 @@ async fn insert_simple_tx_with_blacklisted_message_fails() { .add_input(input) .finalize_as_transaction(); - let nonce = message.nonce; - let db = MockDb::default(); - db.insert_message(message); - let mut txpool = TxPool::new(Default::default(), db.clone()); + let nonce = *message.nonce(); + let mut context = TextContext::default(); + context.database_mut().insert_message(message); + let mut txpool = context.build(); - let tx = check_unwrap_tx(tx, db.clone(), &txpool.config).await; + let tx = check_unwrap_tx(tx, &txpool.config).await; // Given txpool.config_mut().blacklist.messages.insert(nonce); // When - let result = txpool.insert_inner(tx); + let result = txpool.insert_single(tx); // Then assert!(result.is_err()); @@ -351,7 +348,7 @@ async fn not_inserted_known_tx() { .add_random_fee_input() .finalize() .into(); - let tx = check_unwrap_tx(tx, db.clone(), &txpool.config).await; + let tx = check_unwrap_tx(tx, &txpool.config).await; txpool .insert_single(tx.clone()) diff --git a/crates/types/src/services/txpool.rs b/crates/types/src/services/txpool.rs index 4e0f6b2f1ed..c4b39b53e75 100644 --- a/crates/types/src/services/txpool.rs +++ b/crates/types/src/services/txpool.rs @@ -22,6 +22,7 @@ use crate::{ UtxoId, }, fuel_types::{ + Address, ContractId, Nonce, }, @@ -311,6 +312,14 @@ pub enum Error { ConsensusValidity(CheckError), #[error("Mint transactions are disallowed from the txpool")] MintIsDisallowed, + #[error("The UTXO `{0}` is blacklisted")] + BlacklistedUTXO(UtxoId), + #[error("The owner `{0}` is blacklisted")] + BlacklistedOwner(Address), + #[error("The contract `{0}` is blacklisted")] + BlacklistedContract(ContractId), + #[error("The message `{0}` is blacklisted")] + BlacklistedMessage(Nonce), #[error("Database error: {0}")] Database(String), // TODO: We need it for now until channels are removed from TxPool.