{
imports_external_transactions: bool,
pool: Arc,
client: Arc,
executor: TaskExecutor,
}
/// Get transactions for propagation.
///
/// Function extracted to simplify the test and prevent creating `ServiceFactory`.
fn transactions_to_propagate(pool: &TransactionPool)
-> Vec<(H, B::Extrinsic)>
where
PoolApi: ChainApi,
B: BlockT,
H: std::hash::Hash + Eq + sr_primitives::traits::Member + sr_primitives::traits::MaybeSerialize,
E: txpool::error::IntoPoolError + From,
{
pool.ready()
.filter(|t| t.is_propagateable())
.map(|t| {
let hash = t.hash.clone();
let ex: B::Extrinsic = t.data.clone();
(hash, ex)
})
.collect()
}
impl network::TransactionPool for
TransactionPoolAdapter>
where
C: network::ClientHandle + Send + Sync,
PoolApi: 'static + ChainApi,
B: BlockT,
H: std::hash::Hash + Eq + sr_primitives::traits::Member + sr_primitives::traits::MaybeSerialize,
E: txpool::error::IntoPoolError + From,
{
fn transactions(&self) -> Vec<(H, ::Extrinsic)> {
transactions_to_propagate(&self.pool)
}
fn hash_of(&self, transaction: &B::Extrinsic) -> H {
self.pool.hash_of(transaction)
}
fn import(&self, report_handle: ReportHandle, who: PeerId, reputation_change: i32, transaction: B::Extrinsic) {
if !self.imports_external_transactions {
debug!("Transaction rejected");
return;
}
let encoded = transaction.encode();
match Decode::decode(&mut &encoded[..]) {
Ok(uxt) => {
let best_block_id = BlockId::hash(self.client.info().chain.best_hash);
let import_future = self.pool.submit_one(&best_block_id, uxt);
let import_future = import_future
.then(move |import_result| {
match import_result {
Ok(_) => report_handle.report_peer(who, reputation_change),
Err(e) => match e.into_pool_error() {
Ok(txpool::error::Error::AlreadyImported(_)) => (),
Ok(e) => debug!("Error adding transaction to the pool: {:?}", e),
Err(e) => debug!("Error converting pool error: {:?}", e),
}
}
ready(Ok(()))
})
.compat();
if let Err(e) = self.executor.execute(Box::new(import_future)) {
warn!("Error scheduling extrinsic import: {:?}", e);
}
}
Err(e) => debug!("Error decoding transaction {}", e),
}
}
fn on_broadcasted(&self, propagations: HashMap>) {
self.pool.on_broadcasted(propagations)
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures03::executor::block_on;
use consensus_common::SelectChain;
use sr_primitives::traits::BlindCheckable;
use substrate_test_runtime_client::{prelude::*, runtime::{Extrinsic, Transfer}};
#[test]
fn should_not_propagate_transactions_that_are_marked_as_such() {
// given
let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain();
let client = Arc::new(client);
let pool = Arc::new(TransactionPool::new(
Default::default(),
transaction_pool::FullChainApi::new(client.clone())
));
let best = longest_chain.best_chain().unwrap();
let transaction = Transfer {
amount: 5,
nonce: 0,
from: AccountKeyring::Alice.into(),
to: Default::default(),
}.into_signed_tx();
block_on(pool.submit_one(&BlockId::hash(best.hash()), transaction.clone())).unwrap();
block_on(pool.submit_one(&BlockId::hash(best.hash()), Extrinsic::IncludeData(vec![1]))).unwrap();
assert_eq!(pool.status().ready, 2);
// when
let transactions = transactions_to_propagate(&pool);
// then
assert_eq!(transactions.len(), 1);
assert!(transactions[0].1.clone().check().is_ok());
// this should not panic
let _ = transactions[0].1.transfer();
}
}