1use crate::{common::tracing_log_xt::log_xt_trace, LOG_TARGET};
20use async_trait::async_trait;
21use futures::channel::mpsc::Receiver;
22use indexmap::IndexMap;
23use sc_transaction_pool_api::error;
24use sp_blockchain::{HashAndNumber, TreeRoute};
25use sp_runtime::{
26 generic::BlockId,
27 traits::{self, Block as BlockT, SaturatedConversion},
28 transaction_validity::{
29 TransactionSource, TransactionTag as Tag, TransactionValidity, TransactionValidityError,
30 },
31};
32use std::{
33 collections::HashMap,
34 sync::Arc,
35 time::{Duration, Instant},
36};
37use tracing::{debug, instrument, trace, Level};
38
39use super::{
40 base_pool as base,
41 validated_pool::{IsValidator, ValidatedPool, ValidatedTransaction},
42 EventHandler, ValidatedPoolSubmitOutcome,
43};
44
45pub type EventStream<H> = Receiver<H>;
47
48pub type BlockHash<A> = <<A as ChainApi>::Block as traits::Block>::Hash;
50pub type ExtrinsicHash<A> = <<A as ChainApi>::Block as traits::Block>::Hash;
52pub type ExtrinsicFor<A> = Arc<<<A as ChainApi>::Block as traits::Block>::Extrinsic>;
54pub type RawExtrinsicFor<A> = <<A as ChainApi>::Block as traits::Block>::Extrinsic;
56pub type NumberFor<A> = traits::NumberFor<<A as ChainApi>::Block>;
58pub type TransactionFor<A> = Arc<base::Transaction<ExtrinsicHash<A>, ExtrinsicFor<A>>>;
60pub type ValidatedTransactionFor<A> =
62 ValidatedTransaction<ExtrinsicHash<A>, ExtrinsicFor<A>, <A as ChainApi>::Error>;
63
64#[derive(PartialEq, Copy, Clone)]
66pub enum ValidateTransactionPriority {
67 Submitted,
71 Maintained,
75}
76
77#[async_trait]
79pub trait ChainApi: Send + Sync {
80 type Block: BlockT;
82 type Error: From<error::Error> + error::IntoPoolError + error::IntoMetricsLabel;
84
85 async fn validate_transaction(
87 &self,
88 at: <Self::Block as BlockT>::Hash,
89 source: TransactionSource,
90 uxt: ExtrinsicFor<Self>,
91 validation_priority: ValidateTransactionPriority,
92 ) -> Result<TransactionValidity, Self::Error>;
93
94 fn validate_transaction_blocking(
99 &self,
100 at: <Self::Block as BlockT>::Hash,
101 source: TransactionSource,
102 uxt: ExtrinsicFor<Self>,
103 ) -> Result<TransactionValidity, Self::Error>;
104
105 fn block_id_to_number(
107 &self,
108 at: &BlockId<Self::Block>,
109 ) -> Result<Option<NumberFor<Self>>, Self::Error>;
110
111 fn block_id_to_hash(
113 &self,
114 at: &BlockId<Self::Block>,
115 ) -> Result<Option<<Self::Block as BlockT>::Hash>, Self::Error>;
116
117 fn hash_and_length(&self, uxt: &RawExtrinsicFor<Self>) -> (ExtrinsicHash<Self>, usize);
119
120 async fn block_body(
122 &self,
123 at: <Self::Block as BlockT>::Hash,
124 ) -> Result<Option<Vec<<Self::Block as traits::Block>::Extrinsic>>, Self::Error>;
125
126 fn block_header(
128 &self,
129 at: <Self::Block as BlockT>::Hash,
130 ) -> Result<Option<<Self::Block as BlockT>::Header>, Self::Error>;
131
132 fn tree_route(
134 &self,
135 from: <Self::Block as BlockT>::Hash,
136 to: <Self::Block as BlockT>::Hash,
137 ) -> Result<TreeRoute<Self::Block>, Self::Error>;
138
139 fn resolve_block_number(
141 &self,
142 at: <Self::Block as BlockT>::Hash,
143 ) -> Result<NumberFor<Self>, Self::Error> {
144 self.block_id_to_number(&BlockId::Hash(at)).and_then(|number| {
145 number.ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)).into())
146 })
147 }
148}
149
150#[derive(Debug, Clone)]
152pub struct Options {
153 pub ready: base::Limit,
155 pub future: base::Limit,
157 pub reject_future_transactions: bool,
159 pub ban_time: Duration,
161}
162
163impl Default for Options {
164 fn default() -> Self {
165 Self {
166 ready: base::Limit { count: 8192, total_bytes: 20 * 1024 * 1024 },
167 future: base::Limit { count: 512, total_bytes: 1 * 1024 * 1024 },
168 reject_future_transactions: false,
169 ban_time: Duration::from_secs(60 * 30),
170 }
171 }
172}
173
174impl Options {
175 pub fn total_count(&self) -> usize {
177 self.ready.count + self.future.count
178 }
179}
180
181#[derive(Copy, Clone)]
184pub(crate) enum CheckBannedBeforeVerify {
185 Yes,
186 No,
187}
188
189pub struct Pool<B: ChainApi, L: EventHandler<B>> {
191 validated_pool: Arc<ValidatedPool<B, L>>,
192}
193
194impl<B: ChainApi, L: EventHandler<B>> Pool<B, L> {
195 pub fn new_with_staticly_sized_rotator(
197 options: Options,
198 is_validator: IsValidator,
199 api: Arc<B>,
200 ) -> Self {
201 Self {
202 validated_pool: Arc::new(ValidatedPool::new_with_staticly_sized_rotator(
203 options,
204 is_validator,
205 api,
206 )),
207 }
208 }
209
210 pub fn new(options: Options, is_validator: IsValidator, api: Arc<B>) -> Self {
212 Self { validated_pool: Arc::new(ValidatedPool::new(options, is_validator, api)) }
213 }
214
215 pub fn new_with_event_handler(
217 options: Options,
218 is_validator: IsValidator,
219 api: Arc<B>,
220 event_handler: L,
221 ) -> Self {
222 Self {
223 validated_pool: Arc::new(ValidatedPool::new_with_event_handler(
224 options,
225 is_validator,
226 api,
227 event_handler,
228 )),
229 }
230 }
231
232 #[instrument(level = Level::TRACE, skip_all, target="txpool", name = "pool::submit_at")]
234 pub async fn submit_at(
235 &self,
236 at: &HashAndNumber<B::Block>,
237 xts: impl IntoIterator<Item = (base::TimedTransactionSource, ExtrinsicFor<B>)>,
238 validation_priority: ValidateTransactionPriority,
239 ) -> Vec<Result<ValidatedPoolSubmitOutcome<B>, B::Error>> {
240 let validated_transactions =
241 self.verify(at, xts, CheckBannedBeforeVerify::Yes, validation_priority).await;
242 self.validated_pool.submit(validated_transactions.into_values())
243 }
244
245 pub async fn resubmit_at(
249 &self,
250 at: &HashAndNumber<B::Block>,
251 xts: impl IntoIterator<Item = (base::TimedTransactionSource, ExtrinsicFor<B>)>,
252 validation_priority: ValidateTransactionPriority,
253 ) -> Vec<Result<ValidatedPoolSubmitOutcome<B>, B::Error>> {
254 let validated_transactions =
255 self.verify(at, xts, CheckBannedBeforeVerify::No, validation_priority).await;
256 self.validated_pool.submit(validated_transactions.into_values())
257 }
258
259 pub async fn submit_one(
261 &self,
262 at: &HashAndNumber<B::Block>,
263 source: base::TimedTransactionSource,
264 xt: ExtrinsicFor<B>,
265 ) -> Result<ValidatedPoolSubmitOutcome<B>, B::Error> {
266 let res = self
267 .submit_at(at, std::iter::once((source, xt)), ValidateTransactionPriority::Submitted)
268 .await
269 .pop();
270 res.expect("One extrinsic passed; one result returned; qed")
271 }
272
273 pub async fn submit_and_watch(
275 &self,
276 at: &HashAndNumber<B::Block>,
277 source: base::TimedTransactionSource,
278 xt: ExtrinsicFor<B>,
279 ) -> Result<ValidatedPoolSubmitOutcome<B>, B::Error> {
280 let (_, tx) = self
281 .verify_one(
282 at.hash,
283 at.number,
284 source,
285 xt,
286 CheckBannedBeforeVerify::Yes,
287 ValidateTransactionPriority::Submitted,
288 )
289 .await;
290 self.validated_pool.submit_and_watch(tx)
291 }
292
293 pub fn resubmit(
295 &self,
296 revalidated_transactions: IndexMap<ExtrinsicHash<B>, ValidatedTransactionFor<B>>,
297 ) {
298 let now = Instant::now();
299 self.validated_pool.resubmit(revalidated_transactions);
300 trace!(
301 target: LOG_TARGET,
302 duration = ?now.elapsed(),
303 status = ?self.validated_pool.status(),
304 "Resubmitted transaction."
305 );
306 }
307
308 pub fn prune_known(&self, at: &HashAndNumber<B::Block>, hashes: &[ExtrinsicHash<B>]) {
314 let in_pool_tags =
316 self.validated_pool.extrinsics_tags(hashes).into_iter().flatten().flatten();
317
318 let prune_status = self.validated_pool.prune_tags(in_pool_tags);
320 let pruned_transactions =
321 hashes.iter().cloned().chain(prune_status.pruned.iter().map(|tx| tx.hash));
322 self.validated_pool.fire_pruned(at, pruned_transactions);
323 }
324
325 pub async fn prune(
332 &self,
333 at: &HashAndNumber<B::Block>,
334 parent: <B::Block as BlockT>::Hash,
335 extrinsics: &[RawExtrinsicFor<B>],
336 known_provides_tags: Option<Arc<HashMap<ExtrinsicHash<B>, Vec<Tag>>>>,
337 ) {
338 debug!(
339 target: LOG_TARGET,
340 ?at,
341 extrinsics_count = extrinsics.len(),
342 "Starting pruning of block."
343 );
344 let in_pool_hashes =
346 extrinsics.iter().map(|extrinsic| self.hash_of(extrinsic)).collect::<Vec<_>>();
347 let in_pool_tags = self.validated_pool.extrinsics_tags(&in_pool_hashes);
348 let mut unknown_txs_count = 0usize;
350 let mut reused_txs_count = 0usize;
351 let tags = in_pool_hashes.iter().zip(in_pool_tags).map(|(tx_hash, tags)| {
352 tags.or_else(|| {
353 unknown_txs_count += 1;
354 known_provides_tags.as_ref().and_then(|inner| {
355 inner.get(&tx_hash).map(|found_tags| {
356 reused_txs_count += 1;
357 found_tags.clone()
358 })
359 })
360 })
361 });
362
363 let all = extrinsics.iter().zip(tags);
366 let mut validated_counter: usize = 0;
367 let mut future_tags = Vec::new();
368 let now = Instant::now();
369 for (extrinsic, in_pool_tags) in all {
370 match in_pool_tags {
371 Some(tags) => future_tags.extend(tags),
374 None => {
377 if !self.validated_pool.status().is_empty() {
379 validated_counter = validated_counter + 1;
380 let validity = self
381 .validated_pool
382 .api()
383 .validate_transaction(
384 parent,
385 TransactionSource::InBlock,
386 Arc::from(extrinsic.clone()),
387 ValidateTransactionPriority::Maintained,
388 )
389 .await;
390
391 trace!(
392 target: LOG_TARGET,
393 tx_hash = ?self.validated_pool.api().hash_and_length(&extrinsic.clone()).0,
394 ?validity,
395 "prune::revalidated"
396 );
397 if let Ok(Ok(validity)) = validity {
398 future_tags.extend(validity.provides);
399 }
400 } else {
401 trace!(
402 target: LOG_TARGET,
403 ?at,
404 "txpool is empty, skipping validation for block",
405 );
406 }
407 },
408 }
409 }
410
411 let known_provides_tags_len = known_provides_tags.map(|inner| inner.len()).unwrap_or(0);
412 debug!(
413 target: LOG_TARGET,
414 validated_counter,
415 known_provides_tags_len,
416 unknown_txs_count,
417 reused_txs_count,
418 duration = ?now.elapsed(),
419 "prune"
420 );
421 self.prune_tags(at, future_tags, in_pool_hashes).await
422 }
423
424 pub async fn prune_tags(
446 &self,
447 at: &HashAndNumber<B::Block>,
448 tags: impl IntoIterator<Item = Tag>,
449 known_imported_hashes: impl IntoIterator<Item = ExtrinsicHash<B>> + Clone,
450 ) {
451 let now = Instant::now();
452 trace!(target: LOG_TARGET, ?at, "Pruning tags.");
453 let prune_status = self.validated_pool.prune_tags(tags);
455
456 self.validated_pool.ban(
460 &Instant::now(),
461 known_imported_hashes.clone().into_iter(),
462 crate::graph::rotator::BanReason::Validation,
463 );
464
465 let pruned_transactions =
468 prune_status.pruned.into_iter().map(|tx| (tx.source.clone(), tx.data.clone()));
469
470 let reverified_transactions = self
471 .verify(
472 at,
473 pruned_transactions,
474 CheckBannedBeforeVerify::Yes,
475 ValidateTransactionPriority::Maintained,
476 )
477 .await;
478
479 let pruned_hashes = reverified_transactions.keys().map(Clone::clone).collect::<Vec<_>>();
480 debug!(
481 target: LOG_TARGET,
482 ?at,
483 reverified_transactions = reverified_transactions.len(),
484 duration = ?now.elapsed(),
485 "Pruned. Resubmitting transactions."
486 );
487 log_xt_trace!(data: tuple, target: LOG_TARGET, &reverified_transactions, "Resubmitting transaction: {:?}");
488
489 self.validated_pool.resubmit_pruned(
491 &at,
492 known_imported_hashes,
493 pruned_hashes,
494 reverified_transactions.into_values().collect(),
495 )
496 }
497
498 pub fn hash_of(&self, xt: &RawExtrinsicFor<B>) -> ExtrinsicHash<B> {
500 self.validated_pool.api().hash_and_length(xt).0
501 }
502
503 #[instrument(level = Level::TRACE, skip_all, target = "txpool",name = "pool::verify")]
505 async fn verify(
506 &self,
507 at: &HashAndNumber<B::Block>,
508 xts: impl IntoIterator<Item = (base::TimedTransactionSource, ExtrinsicFor<B>)>,
509 check: CheckBannedBeforeVerify,
510 validation_priority: ValidateTransactionPriority,
511 ) -> IndexMap<ExtrinsicHash<B>, ValidatedTransactionFor<B>> {
512 let HashAndNumber { number, hash } = *at;
513
514 let res = futures::future::join_all(xts.into_iter().map(|(source, xt)| {
515 self.verify_one(hash, number, source, xt, check, validation_priority)
516 }))
517 .await
518 .into_iter()
519 .collect::<IndexMap<_, _>>();
520
521 res
522 }
523
524 #[instrument(level = Level::TRACE, skip_all, target = "txpool",name = "pool::verify_one")]
526 pub(crate) async fn verify_one(
527 &self,
528 block_hash: <B::Block as BlockT>::Hash,
529 block_number: NumberFor<B>,
530 source: base::TimedTransactionSource,
531 xt: ExtrinsicFor<B>,
532 check: CheckBannedBeforeVerify,
533 validation_priority: ValidateTransactionPriority,
534 ) -> (ExtrinsicHash<B>, ValidatedTransactionFor<B>) {
535 let (hash, bytes) = self.validated_pool.api().hash_and_length(&xt);
536
537 let ignore_banned = matches!(check, CheckBannedBeforeVerify::No);
538 if let Err(err) = self.validated_pool.check_is_known(&hash, ignore_banned) {
539 return (hash, ValidatedTransaction::Invalid(hash, err));
540 }
541
542 let validation_result = self
543 .validated_pool
544 .api()
545 .validate_transaction(
546 block_hash,
547 source.clone().into(),
548 xt.clone(),
549 validation_priority,
550 )
551 .await;
552
553 let status = match validation_result {
554 Ok(status) => status,
555 Err(e) => return (hash, ValidatedTransaction::Invalid(hash, e)),
556 };
557
558 let validity = match status {
559 Ok(validity) => {
560 if validity.provides.is_empty() {
561 ValidatedTransaction::Invalid(hash, error::Error::NoTagsProvided.into())
562 } else {
563 ValidatedTransaction::valid_at(
564 block_number.saturated_into::<u64>(),
565 hash,
566 source,
567 xt,
568 bytes,
569 validity,
570 )
571 }
572 },
573 Err(TransactionValidityError::Invalid(e)) => {
574 ValidatedTransaction::Invalid(hash, error::Error::InvalidTransaction(e).into())
575 },
576 Err(TransactionValidityError::Unknown(e)) => {
577 ValidatedTransaction::Unknown(hash, error::Error::UnknownTransaction(e).into())
578 },
579 };
580
581 (hash, validity)
582 }
583
584 pub fn validated_pool(&self) -> &ValidatedPool<B, L> {
586 &self.validated_pool
587 }
588
589 pub fn clear_recently_pruned(&mut self) {
591 self.validated_pool.pool.write().clear_recently_pruned();
592 }
593}
594
595impl<B: ChainApi, L: EventHandler<B>> Pool<B, L> {
596 pub fn deep_clone_with_event_handler(&self, event_handler: L) -> Self {
600 let other: ValidatedPool<B, L> =
601 self.validated_pool().deep_clone_with_event_handler(event_handler);
602 Self { validated_pool: Arc::from(other) }
603 }
604}
605
606#[cfg(test)]
607mod tests {
608 use super::{super::base_pool::Limit, *};
609 use crate::common::tests::{pool, uxt, TestApi, INVALID_NONCE};
610 use assert_matches::assert_matches;
611 use base::TimedTransactionSource;
612 use codec::Encode;
613 use futures::executor::block_on;
614 use parking_lot::Mutex;
615 use sc_transaction_pool_api::TransactionStatus;
616 use sp_runtime::transaction_validity::TransactionSource;
617 use std::{collections::HashMap, time::Instant};
618 use substrate_test_runtime::{AccountId, ExtrinsicBuilder, Transfer, H256};
619 use substrate_test_runtime_client::Sr25519Keyring::{Alice, Bob};
620
621 const SOURCE: TimedTransactionSource =
622 TimedTransactionSource { source: TransactionSource::External, timestamp: None };
623
624 type Pool<Api> = super::Pool<Api, ()>;
625
626 #[test]
627 fn should_validate_and_import_transaction() {
628 let (pool, api) = pool();
630
631 let hash = block_on(
633 pool.submit_one(
634 &api.expect_hash_and_number(0),
635 SOURCE,
636 uxt(Transfer {
637 from: Alice.into(),
638 to: AccountId::from_h256(H256::from_low_u64_be(2)),
639 amount: 5,
640 nonce: 0,
641 })
642 .into(),
643 ),
644 )
645 .map(|outcome| outcome.hash())
646 .unwrap();
647
648 assert_eq!(pool.validated_pool().ready().map(|v| v.hash).collect::<Vec<_>>(), vec![hash]);
650 }
651
652 #[test]
653 fn submit_at_preserves_order() {
654 sp_tracing::try_init_simple();
655 let (pool, api) = pool();
657
658 let txs = (0..10)
659 .map(|i| {
660 uxt(Transfer {
661 from: Alice.into(),
662 to: AccountId::from_h256(H256::from_low_u64_be(i)),
663 amount: 5,
664 nonce: i,
665 })
666 .into()
667 })
668 .collect::<Vec<_>>();
669
670 let initial_hashes = txs.iter().map(|t| api.hash_and_length(t).0).collect::<Vec<_>>();
671
672 let txs = txs.into_iter().map(|x| (SOURCE, Arc::from(x))).collect::<Vec<_>>();
674 let hashes = block_on(pool.submit_at(
675 &api.expect_hash_and_number(0),
676 txs,
677 ValidateTransactionPriority::Submitted,
678 ))
679 .into_iter()
680 .map(|r| r.map(|o| o.hash()))
681 .collect::<Vec<_>>();
682 debug!(hashes = ?hashes, "-->");
683
684 hashes.into_iter().zip(initial_hashes.into_iter()).for_each(
686 |(result_hash, initial_hash)| {
687 assert_eq!(result_hash.unwrap(), initial_hash);
688 },
689 );
690 }
691
692 #[test]
693 fn should_reject_if_temporarily_banned() {
694 let (pool, api) = pool();
696 let uxt = uxt(Transfer {
697 from: Alice.into(),
698 to: AccountId::from_h256(H256::from_low_u64_be(2)),
699 amount: 5,
700 nonce: 0,
701 });
702
703 pool.validated_pool.ban(
705 &Instant::now(),
706 vec![pool.hash_of(&uxt)],
707 crate::graph::rotator::BanReason::Validation,
708 );
709 let res = block_on(pool.submit_one(&api.expect_hash_and_number(0), SOURCE, uxt.into()))
710 .map(|o| o.hash());
711 assert_eq!(pool.validated_pool().status().ready, 0);
712 assert_eq!(pool.validated_pool().status().future, 0);
713
714 assert_matches!(res.unwrap_err(), error::Error::TemporarilyBanned);
716 }
717
718 #[test]
719 fn should_reject_unactionable_transactions() {
720 let api = Arc::new(TestApi::default());
722 let pool = Pool::new_with_staticly_sized_rotator(
723 Default::default(),
724 false.into(),
726 api.clone(),
727 );
728
729 let uxt = ExtrinsicBuilder::new_include_data(vec![42]).build();
731
732 let res = block_on(pool.submit_one(&api.expect_hash_and_number(0), SOURCE, uxt.into()))
734 .map(|o| o.hash());
735
736 assert_matches!(res.unwrap_err(), error::Error::Unactionable);
738 }
739
740 #[test]
741 fn should_notify_about_pool_events() {
742 let (stream, hash0, hash1) = {
743 let (pool, api) = pool();
745 let han_of_block0 = api.expect_hash_and_number(0);
746 let stream = pool.validated_pool().import_notification_stream();
747
748 let hash0 = block_on(
750 pool.submit_one(
751 &han_of_block0,
752 SOURCE,
753 uxt(Transfer {
754 from: Alice.into(),
755 to: AccountId::from_h256(H256::from_low_u64_be(2)),
756 amount: 5,
757 nonce: 0,
758 })
759 .into(),
760 ),
761 )
762 .unwrap()
763 .hash();
764 let hash1 = block_on(
765 pool.submit_one(
766 &han_of_block0,
767 SOURCE,
768 uxt(Transfer {
769 from: Alice.into(),
770 to: AccountId::from_h256(H256::from_low_u64_be(2)),
771 amount: 5,
772 nonce: 1,
773 })
774 .into(),
775 ),
776 )
777 .unwrap()
778 .hash();
779 let _hash = block_on(
781 pool.submit_one(
782 &han_of_block0,
783 SOURCE,
784 uxt(Transfer {
785 from: Alice.into(),
786 to: AccountId::from_h256(H256::from_low_u64_be(2)),
787 amount: 5,
788 nonce: 3,
789 })
790 .into(),
791 ),
792 )
793 .unwrap()
794 .hash();
795
796 assert_eq!(pool.validated_pool().status().ready, 2);
797 assert_eq!(pool.validated_pool().status().future, 1);
798
799 (stream, hash0, hash1)
800 };
801
802 let mut it = futures::executor::block_on_stream(stream);
804 assert_eq!(it.next(), Some(hash0));
805 assert_eq!(it.next(), Some(hash1));
806 assert_eq!(it.next(), None);
807 }
808
809 #[test]
810 fn should_clear_stale_transactions() {
811 let (pool, api) = pool();
813 let han_of_block0 = api.expect_hash_and_number(0);
814 let hash1 = block_on(
815 pool.submit_one(
816 &han_of_block0,
817 SOURCE,
818 uxt(Transfer {
819 from: Alice.into(),
820 to: AccountId::from_h256(H256::from_low_u64_be(2)),
821 amount: 5,
822 nonce: 0,
823 })
824 .into(),
825 ),
826 )
827 .unwrap()
828 .hash();
829 let hash2 = block_on(
830 pool.submit_one(
831 &han_of_block0,
832 SOURCE,
833 uxt(Transfer {
834 from: Alice.into(),
835 to: AccountId::from_h256(H256::from_low_u64_be(2)),
836 amount: 5,
837 nonce: 1,
838 })
839 .into(),
840 ),
841 )
842 .unwrap()
843 .hash();
844 let hash3 = block_on(
845 pool.submit_one(
846 &han_of_block0,
847 SOURCE,
848 uxt(Transfer {
849 from: Alice.into(),
850 to: AccountId::from_h256(H256::from_low_u64_be(2)),
851 amount: 5,
852 nonce: 3,
853 })
854 .into(),
855 ),
856 )
857 .unwrap()
858 .hash();
859
860 pool.validated_pool.clear_stale(&api.expect_hash_and_number(5));
862
863 assert_eq!(pool.validated_pool().ready().count(), 0);
865 assert_eq!(pool.validated_pool().status().future, 0);
866 assert_eq!(pool.validated_pool().status().ready, 0);
867 assert!(pool.validated_pool.is_banned(&hash1));
869 assert!(pool.validated_pool.is_banned(&hash2));
870 assert!(pool.validated_pool.is_banned(&hash3));
871 }
872
873 #[test]
874 fn should_ban_mined_transactions() {
875 let (pool, api) = pool();
877 let hash1 = block_on(
878 pool.submit_one(
879 &api.expect_hash_and_number(0),
880 SOURCE,
881 uxt(Transfer {
882 from: Alice.into(),
883 to: AccountId::from_h256(H256::from_low_u64_be(2)),
884 amount: 5,
885 nonce: 0,
886 })
887 .into(),
888 ),
889 )
890 .unwrap()
891 .hash();
892
893 block_on(pool.prune_tags(&api.expect_hash_and_number(1), vec![vec![0]], vec![hash1]));
895
896 assert!(pool.validated_pool.is_banned(&hash1));
898 }
899
900 #[test]
901 fn should_limit_futures() {
902 sp_tracing::try_init_simple();
903
904 let xt = uxt(Transfer {
905 from: Alice.into(),
906 to: AccountId::from_h256(H256::from_low_u64_be(2)),
907 amount: 5,
908 nonce: 1,
909 });
910
911 let limit = Limit { count: 100, total_bytes: xt.encoded_size() };
913
914 let options = Options { ready: limit.clone(), future: limit.clone(), ..Default::default() };
915
916 let api = Arc::new(TestApi::default());
917 let pool = Pool::new_with_staticly_sized_rotator(options, true.into(), api.clone());
918
919 let hash1 = block_on(pool.submit_one(&api.expect_hash_and_number(0), SOURCE, xt.into()))
920 .unwrap()
921 .hash();
922 assert_eq!(pool.validated_pool().status().future, 1);
923
924 let hash2 = block_on(
926 pool.submit_one(
927 &api.expect_hash_and_number(0),
928 SOURCE,
929 uxt(Transfer {
930 from: Bob.into(),
931 to: AccountId::from_h256(H256::from_low_u64_be(2)),
932 amount: 5,
933 nonce: 10,
934 })
935 .into(),
936 ),
937 )
938 .unwrap()
939 .hash();
940
941 assert_eq!(pool.validated_pool().status().future, 1);
943 assert!(pool.validated_pool.is_banned(&hash1));
944 assert!(!pool.validated_pool.is_banned(&hash2));
945 }
946
947 #[test]
948 fn should_error_if_reject_immediately() {
949 let limit = Limit { count: 100, total_bytes: 10 };
951
952 let options = Options { ready: limit.clone(), future: limit.clone(), ..Default::default() };
953
954 let api = Arc::new(TestApi::default());
955 let pool = Pool::new_with_staticly_sized_rotator(options, true.into(), api.clone());
956
957 block_on(
959 pool.submit_one(
960 &api.expect_hash_and_number(0),
961 SOURCE,
962 uxt(Transfer {
963 from: Alice.into(),
964 to: AccountId::from_h256(H256::from_low_u64_be(2)),
965 amount: 5,
966 nonce: 1,
967 })
968 .into(),
969 ),
970 )
971 .map(|o| o.hash())
972 .unwrap_err();
973
974 assert_eq!(pool.validated_pool().status().ready, 0);
976 assert_eq!(pool.validated_pool().status().future, 0);
977 }
978
979 #[test]
980 fn should_reject_transactions_with_no_provides() {
981 let (pool, api) = pool();
983
984 let err = block_on(
986 pool.submit_one(
987 &api.expect_hash_and_number(0),
988 SOURCE,
989 uxt(Transfer {
990 from: Alice.into(),
991 to: AccountId::from_h256(H256::from_low_u64_be(2)),
992 amount: 5,
993 nonce: INVALID_NONCE,
994 })
995 .into(),
996 ),
997 )
998 .map(|o| o.hash())
999 .unwrap_err();
1000
1001 assert_eq!(pool.validated_pool().status().ready, 0);
1003 assert_eq!(pool.validated_pool().status().future, 0);
1004 assert_matches!(err, error::Error::NoTagsProvided);
1005 }
1006
1007 mod listener {
1008 use super::*;
1009
1010 #[test]
1011 fn should_trigger_ready_and_in_block_when_pruning_via_hash() {
1012 let (pool, api) = pool();
1014 let watcher = block_on(
1015 pool.submit_and_watch(
1016 &api.expect_hash_and_number(0),
1017 SOURCE,
1018 uxt(Transfer {
1019 from: Alice.into(),
1020 to: AccountId::from_h256(H256::from_low_u64_be(2)),
1021 amount: 5,
1022 nonce: 0,
1023 })
1024 .into(),
1025 ),
1026 )
1027 .unwrap()
1028 .expect_watcher();
1029 assert_eq!(pool.validated_pool().status().ready, 1);
1030 assert_eq!(pool.validated_pool().status().future, 0);
1031
1032 let han_of_block2 = api.expect_hash_and_number(2);
1033
1034 block_on(pool.prune_tags(&han_of_block2, vec![vec![0u8]], vec![*watcher.hash()]));
1036 assert_eq!(pool.validated_pool().status().ready, 0);
1037 assert_eq!(pool.validated_pool().status().future, 0);
1038
1039 let mut stream = futures::executor::block_on_stream(watcher.into_stream());
1041 assert_eq!(stream.next(), Some(TransactionStatus::Ready));
1042 assert_eq!(
1043 stream.next(),
1044 Some(TransactionStatus::InBlock((han_of_block2.hash.into(), 0))),
1045 );
1046 }
1047
1048 #[test]
1049 fn should_trigger_future_and_ready_after_promoted() {
1050 let (pool, api) = pool();
1052 let han_of_block0 = api.expect_hash_and_number(0);
1053
1054 let watcher = block_on(
1055 pool.submit_and_watch(
1056 &han_of_block0,
1057 SOURCE,
1058 uxt(Transfer {
1059 from: Alice.into(),
1060 to: AccountId::from_h256(H256::from_low_u64_be(2)),
1061 amount: 5,
1062 nonce: 1,
1063 })
1064 .into(),
1065 ),
1066 )
1067 .unwrap()
1068 .expect_watcher();
1069 assert_eq!(pool.validated_pool().status().ready, 0);
1070 assert_eq!(pool.validated_pool().status().future, 1);
1071
1072 block_on(
1074 pool.submit_one(
1075 &han_of_block0,
1076 SOURCE,
1077 uxt(Transfer {
1078 from: Alice.into(),
1079 to: AccountId::from_h256(H256::from_low_u64_be(2)),
1080 amount: 5,
1081 nonce: 0,
1082 })
1083 .into(),
1084 ),
1085 )
1086 .unwrap();
1087 assert_eq!(pool.validated_pool().status().ready, 2);
1088
1089 let mut stream = futures::executor::block_on_stream(watcher.into_stream());
1091 assert_eq!(stream.next(), Some(TransactionStatus::Future));
1092 assert_eq!(stream.next(), Some(TransactionStatus::Ready));
1093 }
1094
1095 #[test]
1096 fn should_trigger_invalid_and_ban() {
1097 let (pool, api) = pool();
1099 let uxt = uxt(Transfer {
1100 from: Alice.into(),
1101 to: AccountId::from_h256(H256::from_low_u64_be(2)),
1102 amount: 5,
1103 nonce: 0,
1104 });
1105 let watcher =
1106 block_on(pool.submit_and_watch(&api.expect_hash_and_number(0), SOURCE, uxt.into()))
1107 .unwrap()
1108 .expect_watcher();
1109 assert_eq!(pool.validated_pool().status().ready, 1);
1110
1111 pool.validated_pool.remove_invalid(&[*watcher.hash()]);
1113
1114 let mut stream = futures::executor::block_on_stream(watcher.into_stream());
1116 assert_eq!(stream.next(), Some(TransactionStatus::Ready));
1117 assert_eq!(stream.next(), Some(TransactionStatus::Invalid));
1118 assert_eq!(stream.next(), None);
1119 }
1120
1121 #[test]
1122 fn should_trigger_broadcasted() {
1123 let (pool, api) = pool();
1125 let uxt = uxt(Transfer {
1126 from: Alice.into(),
1127 to: AccountId::from_h256(H256::from_low_u64_be(2)),
1128 amount: 5,
1129 nonce: 0,
1130 });
1131 let watcher =
1132 block_on(pool.submit_and_watch(&api.expect_hash_and_number(0), SOURCE, uxt.into()))
1133 .unwrap()
1134 .expect_watcher();
1135 assert_eq!(pool.validated_pool().status().ready, 1);
1136
1137 let mut map = HashMap::new();
1139 let peers = vec!["a".into(), "b".into(), "c".into()];
1140 map.insert(*watcher.hash(), peers.clone());
1141 pool.validated_pool().on_broadcasted(map);
1142
1143 let mut stream = futures::executor::block_on_stream(watcher.into_stream());
1145 assert_eq!(stream.next(), Some(TransactionStatus::Ready));
1146 assert_eq!(stream.next(), Some(TransactionStatus::Broadcast(peers)));
1147 }
1148
1149 #[test]
1150 fn should_trigger_dropped_older() {
1151 let limit = Limit { count: 1, total_bytes: 1000 };
1153 let options =
1154 Options { ready: limit.clone(), future: limit.clone(), ..Default::default() };
1155
1156 let api = Arc::new(TestApi::default());
1157 let pool = Pool::new_with_staticly_sized_rotator(options, true.into(), api.clone());
1158
1159 let xt = uxt(Transfer {
1160 from: Alice.into(),
1161 to: AccountId::from_h256(H256::from_low_u64_be(2)),
1162 amount: 5,
1163 nonce: 0,
1164 });
1165 let watcher =
1166 block_on(pool.submit_and_watch(&api.expect_hash_and_number(0), SOURCE, xt.into()))
1167 .unwrap()
1168 .expect_watcher();
1169 assert_eq!(pool.validated_pool().status().ready, 1);
1170
1171 let xt = uxt(Transfer {
1173 from: Bob.into(),
1174 to: AccountId::from_h256(H256::from_low_u64_be(1)),
1175 amount: 4,
1176 nonce: 1,
1177 });
1178 block_on(pool.submit_one(&api.expect_hash_and_number(1), SOURCE, xt.into())).unwrap();
1179 assert_eq!(pool.validated_pool().status().ready, 1);
1180
1181 let mut stream = futures::executor::block_on_stream(watcher.into_stream());
1183 assert_eq!(stream.next(), Some(TransactionStatus::Ready));
1184 assert_eq!(stream.next(), Some(TransactionStatus::Dropped));
1185 }
1186
1187 #[test]
1188 fn should_trigger_dropped_lower_priority() {
1189 {
1190 let limit = Limit { count: 1, total_bytes: 1000 };
1192 let options =
1193 Options { ready: limit.clone(), future: limit.clone(), ..Default::default() };
1194
1195 let api = Arc::new(TestApi::default());
1196 let pool = Pool::new_with_staticly_sized_rotator(options, true.into(), api.clone());
1197
1198 let xt = ExtrinsicBuilder::new_include_data(Vec::new()).build();
1201 block_on(pool.submit_one(&api.expect_hash_and_number(0), SOURCE, xt.into()))
1202 .unwrap();
1203 assert_eq!(pool.validated_pool().status().ready, 1);
1204
1205 let xt = uxt(Transfer {
1209 from: Bob.into(),
1210 to: AccountId::from_h256(H256::from_low_u64_be(1)),
1211 amount: 4,
1212 nonce: 1,
1213 });
1214 let result =
1215 block_on(pool.submit_one(&api.expect_hash_and_number(1), SOURCE, xt.into()));
1216 assert!(matches!(
1217 result,
1218 Err(sc_transaction_pool_api::error::Error::ImmediatelyDropped)
1219 ));
1220 }
1221 {
1222 let limit = Limit { count: 2, total_bytes: 1000 };
1224 let options =
1225 Options { ready: limit.clone(), future: limit.clone(), ..Default::default() };
1226
1227 let api = Arc::new(TestApi::default());
1228 let pool = Pool::new_with_staticly_sized_rotator(options, true.into(), api.clone());
1229
1230 let han_of_block0 = api.expect_hash_and_number(0);
1231
1232 let xt = ExtrinsicBuilder::new_include_data(Vec::new()).build();
1235 block_on(pool.submit_and_watch(&han_of_block0, SOURCE, xt.into()))
1236 .unwrap()
1237 .expect_watcher();
1238 assert_eq!(pool.validated_pool().status().ready, 1);
1239
1240 let xt = uxt(Transfer {
1243 from: Alice.into(),
1244 to: AccountId::from_h256(H256::from_low_u64_be(2)),
1245 amount: 5,
1246 nonce: 0,
1247 });
1248 let watcher = block_on(pool.submit_and_watch(&han_of_block0, SOURCE, xt.into()))
1249 .unwrap()
1250 .expect_watcher();
1251 assert_eq!(pool.validated_pool().status().ready, 2);
1252
1253 let xt = ExtrinsicBuilder::new_indexed_call(Vec::new()).build();
1257 block_on(pool.submit_one(&api.expect_hash_and_number(1), SOURCE, xt.into()))
1258 .unwrap();
1259 assert_eq!(pool.validated_pool().status().ready, 2);
1260
1261 let mut stream = futures::executor::block_on_stream(watcher.into_stream());
1263 assert_eq!(stream.next(), Some(TransactionStatus::Ready));
1264 assert_eq!(stream.next(), Some(TransactionStatus::Dropped));
1265 }
1266 }
1267
1268 #[test]
1269 fn should_handle_pruning_in_the_middle_of_import() {
1270 let (ready, is_ready) = std::sync::mpsc::sync_channel(0);
1272 let (tx, rx) = std::sync::mpsc::sync_channel(1);
1273 let mut api = TestApi::default();
1274 api.delay = Arc::new(Mutex::new(rx.into()));
1275 let api = Arc::new(api);
1276 let pool = Arc::new(Pool::new_with_staticly_sized_rotator(
1277 Default::default(),
1278 true.into(),
1279 api.clone(),
1280 ));
1281
1282 let han_of_block0 = api.expect_hash_and_number(0);
1283
1284 let xt = uxt(Transfer {
1286 from: Alice.into(),
1287 to: AccountId::from_h256(H256::from_low_u64_be(2)),
1288 amount: 5,
1289 nonce: 1,
1290 });
1291
1292 let pool2 = pool.clone();
1294 std::thread::spawn({
1295 let hash_of_block0 = han_of_block0.clone();
1296 move || {
1297 block_on(pool2.submit_one(&hash_of_block0, SOURCE, xt.into())).unwrap();
1298 ready.send(()).unwrap();
1299 }
1300 });
1301
1302 let xt = uxt(Transfer {
1305 from: Alice.into(),
1306 to: AccountId::from_h256(H256::from_low_u64_be(2)),
1307 amount: 4,
1308 nonce: 0,
1309 });
1310 let provides = vec![0_u8];
1312 block_on(pool.submit_one(&han_of_block0, SOURCE, xt.into())).unwrap();
1313 assert_eq!(pool.validated_pool().status().ready, 1);
1314
1315 block_on(pool.prune_tags(&api.expect_hash_and_number(1), vec![provides], vec![]));
1318 assert_eq!(pool.validated_pool().status().ready, 0);
1319
1320 tx.send(()).unwrap();
1324
1325 is_ready.recv().unwrap(); assert_eq!(pool.validated_pool().status().ready, 1);
1328 assert_eq!(pool.validated_pool().status().future, 0);
1329 }
1330 }
1331}