use std::{
collections::HashSet,
time::Duration,
};
use fuel_core_types::{
fuel_tx::{
input::{
coin::{
CoinPredicate,
CoinSigned,
},
message::{
MessageCoinPredicate,
MessageCoinSigned,
MessageDataPredicate,
MessageDataSigned,
},
},
Address,
ContractId,
Input,
UtxoId,
},
fuel_types::Nonce,
services::txpool::PoolTransaction,
};
use crate::error::BlacklistedError;
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct BlackList {
/// Blacklisted addresses.
pub owners: HashSet
,
/// Blacklisted UTXO ids.
pub coins: HashSet,
/// Blacklisted messages by `Nonce`.
pub messages: HashSet,
/// Blacklisted contracts.
pub contracts: HashSet,
}
impl BlackList {
/// Create a new 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(),
}
}
/// Check if the transaction has blacklisted inputs.
pub fn check_blacklisting(
&self,
tx: &PoolTransaction,
) -> Result<(), BlacklistedError> {
for input in tx.inputs() {
match input {
Input::CoinSigned(CoinSigned { utxo_id, owner, .. })
| Input::CoinPredicate(CoinPredicate { utxo_id, owner, .. }) => {
if self.coins.contains(utxo_id) {
return Err(BlacklistedError::BlacklistedUTXO(*utxo_id));
}
if self.owners.contains(owner) {
return Err(BlacklistedError::BlacklistedOwner(*owner));
}
}
Input::Contract(contract) => {
if self.contracts.contains(&contract.contract_id) {
return Err(BlacklistedError::BlacklistedContract(
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.messages.contains(nonce) {
return Err(BlacklistedError::BlacklistedMessage(*nonce));
}
if self.owners.contains(sender) {
return Err(BlacklistedError::BlacklistedOwner(*sender));
}
if self.owners.contains(recipient) {
return Err(BlacklistedError::BlacklistedOwner(*recipient));
}
}
}
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct Config {
/// Enable UTXO validation (will check if UTXO exists in the database and has correct data).
pub utxo_validation: bool,
/// Maximum of subscriptions to listen to updates of a transaction.
pub max_tx_update_subscriptions: usize,
/// Maximum transactions per dependencies chain.
pub max_txs_chain_count: usize,
/// Pool limits
pub pool_limits: PoolLimits,
/// Service channel limits
pub service_channel_limits: ServiceChannelLimits,
/// Interval for checking the time to live of transactions.
pub ttl_check_interval: Duration,
/// Maximum transaction time to live.
pub max_txs_ttl: Duration,
/// Heavy async processing configuration.
pub heavy_work: HeavyWorkConfig,
/// Blacklist. Transactions with blacklisted inputs will not be accepted.
pub black_list: BlackList,
}
#[derive(Clone, Debug)]
pub struct PoolLimits {
/// Maximum number of transactions in the pool.
pub max_txs: usize,
/// Maximum number of gas in the pool.
pub max_gas: u64,
/// Maximum number of bytes in the pool.
pub max_bytes_size: usize,
}
#[derive(Clone, Debug)]
pub struct ServiceChannelLimits {
/// Maximum number of pending requests waiting in the write pool channel.
pub max_pending_write_pool_requests: usize,
/// Maximum number of pending requests waiting in the read pool channel.
pub max_pending_read_pool_requests: usize,
}
#[derive(Clone, Debug)]
pub struct HeavyWorkConfig {
/// Maximum of threads for managing verifications/insertions.
pub number_threads_to_verify_transactions: usize,
/// Maximum of tasks in the heavy async processing queue.
pub size_of_verification_queue: usize,
/// Maximum number of threads for managing the p2p synchronisation
pub number_threads_p2p_sync: usize,
/// Maximum number of tasks in the p2p sync queue
pub size_of_p2p_sync_queue: usize,
}
#[cfg(feature = "test-helpers")]
impl Default for Config {
fn default() -> Self {
Self {
utxo_validation: true,
max_tx_update_subscriptions: 1000,
max_txs_chain_count: 50,
ttl_check_interval: Duration::from_secs(60),
max_txs_ttl: Duration::from_secs(60 * 10),
black_list: BlackList::default(),
pool_limits: PoolLimits {
max_txs: 10000,
max_gas: 100_000_000_000,
max_bytes_size: 1_000_000_000,
},
heavy_work: HeavyWorkConfig {
// It is important for tests to have only one thread for verification
// because some of them rely on the ordering of insertion.
number_threads_to_verify_transactions: 0,
size_of_verification_queue: 100,
number_threads_p2p_sync: 0,
size_of_p2p_sync_queue: 100,
},
service_channel_limits: ServiceChannelLimits {
max_pending_write_pool_requests: 1000,
max_pending_read_pool_requests: 1000,
},
}
}
}