1use std::{cmp::Ordering, collections::HashSet, fmt, hash, sync::Arc};
24
25use crate::LOG_TARGET;
26use log::{debug, trace, warn};
27use sc_transaction_pool_api::{error, InPoolTransaction, PoolStatus};
28use serde::Serialize;
29use sp_core::hexdisplay::HexDisplay;
30use sp_runtime::{
31 traits::Member,
32 transaction_validity::{
33 TransactionLongevity as Longevity, TransactionPriority as Priority,
34 TransactionSource as Source, TransactionTag as Tag,
35 },
36};
37
38use super::{
39 future::{FutureTransactions, WaitingTransaction},
40 ready::{BestIterator, ReadyTransactions, TransactionRef},
41};
42
43#[derive(Debug, PartialEq, Eq)]
45pub enum Imported<Hash, Ex> {
46 Ready {
48 hash: Hash,
50 promoted: Vec<Hash>,
52 failed: Vec<Hash>,
54 removed: Vec<Arc<Transaction<Hash, Ex>>>,
56 },
57 Future {
59 hash: Hash,
61 },
62}
63
64impl<Hash, Ex> Imported<Hash, Ex> {
65 pub fn hash(&self) -> &Hash {
67 use self::Imported::*;
68 match *self {
69 Ready { ref hash, .. } => hash,
70 Future { ref hash, .. } => hash,
71 }
72 }
73}
74
75#[derive(Debug)]
77pub struct PruneStatus<Hash, Ex> {
78 pub promoted: Vec<Imported<Hash, Ex>>,
80 pub failed: Vec<Hash>,
82 pub pruned: Vec<Arc<Transaction<Hash, Ex>>>,
84}
85
86#[derive(PartialEq, Eq, Clone)]
88pub struct Transaction<Hash, Extrinsic> {
89 pub data: Extrinsic,
91 pub bytes: usize,
93 pub hash: Hash,
95 pub priority: Priority,
97 pub valid_till: Longevity,
99 pub requires: Vec<Tag>,
101 pub provides: Vec<Tag>,
103 pub propagate: bool,
105 pub source: Source,
107}
108
109impl<Hash, Extrinsic> AsRef<Extrinsic> for Transaction<Hash, Extrinsic> {
110 fn as_ref(&self) -> &Extrinsic {
111 &self.data
112 }
113}
114
115impl<Hash, Extrinsic> InPoolTransaction for Transaction<Hash, Extrinsic> {
116 type Transaction = Extrinsic;
117 type Hash = Hash;
118
119 fn data(&self) -> &Extrinsic {
120 &self.data
121 }
122
123 fn hash(&self) -> &Hash {
124 &self.hash
125 }
126
127 fn priority(&self) -> &Priority {
128 &self.priority
129 }
130
131 fn longevity(&self) -> &Longevity {
132 &self.valid_till
133 }
134
135 fn requires(&self) -> &[Tag] {
136 &self.requires
137 }
138
139 fn provides(&self) -> &[Tag] {
140 &self.provides
141 }
142
143 fn is_propagable(&self) -> bool {
144 self.propagate
145 }
146}
147
148impl<Hash: Clone, Extrinsic: Clone> Transaction<Hash, Extrinsic> {
149 pub fn duplicate(&self) -> Self {
155 Self {
156 data: self.data.clone(),
157 bytes: self.bytes,
158 hash: self.hash.clone(),
159 priority: self.priority,
160 source: self.source,
161 valid_till: self.valid_till,
162 requires: self.requires.clone(),
163 provides: self.provides.clone(),
164 propagate: self.propagate,
165 }
166 }
167}
168
169impl<Hash, Extrinsic> fmt::Debug for Transaction<Hash, Extrinsic>
170where
171 Hash: fmt::Debug,
172 Extrinsic: fmt::Debug,
173{
174 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
175 let join_tags = |tags: &[Tag]| {
176 tags.iter()
177 .map(|tag| HexDisplay::from(tag).to_string())
178 .collect::<Vec<_>>()
179 .join(", ")
180 };
181
182 write!(fmt, "Transaction {{ ")?;
183 write!(fmt, "hash: {:?}, ", &self.hash)?;
184 write!(fmt, "priority: {:?}, ", &self.priority)?;
185 write!(fmt, "valid_till: {:?}, ", &self.valid_till)?;
186 write!(fmt, "bytes: {:?}, ", &self.bytes)?;
187 write!(fmt, "propagate: {:?}, ", &self.propagate)?;
188 write!(fmt, "source: {:?}, ", &self.source)?;
189 write!(fmt, "requires: [{}], ", join_tags(&self.requires))?;
190 write!(fmt, "provides: [{}], ", join_tags(&self.provides))?;
191 write!(fmt, "data: {:?}", &self.data)?;
192 write!(fmt, "}}")?;
193 Ok(())
194 }
195}
196
197const RECENTLY_PRUNED_TAGS: usize = 2;
199
200#[derive(Debug)]
211pub struct BasePool<Hash: hash::Hash + Eq, Ex> {
212 reject_future_transactions: bool,
213 future: FutureTransactions<Hash, Ex>,
214 ready: ReadyTransactions<Hash, Ex>,
215 recently_pruned: [HashSet<Tag>; RECENTLY_PRUNED_TAGS],
220 recently_pruned_index: usize,
221}
222
223impl<Hash: hash::Hash + Member + Serialize, Ex: std::fmt::Debug> Default for BasePool<Hash, Ex> {
224 fn default() -> Self {
225 Self::new(false)
226 }
227}
228
229impl<Hash: hash::Hash + Member + Serialize, Ex: std::fmt::Debug> BasePool<Hash, Ex> {
230 pub fn new(reject_future_transactions: bool) -> Self {
232 Self {
233 reject_future_transactions,
234 future: Default::default(),
235 ready: Default::default(),
236 recently_pruned: Default::default(),
237 recently_pruned_index: 0,
238 }
239 }
240
241 pub(crate) fn with_futures_enabled<T>(
247 &mut self,
248 closure: impl FnOnce(&mut Self, bool) -> T,
249 ) -> T {
250 let previous = self.reject_future_transactions;
251 self.reject_future_transactions = false;
252 let return_value = closure(self, previous);
253 self.reject_future_transactions = previous;
254 return_value
255 }
256
257 pub fn is_imported(&self, tx_hash: &Hash) -> bool {
259 self.future.contains(tx_hash) || self.ready.contains(tx_hash)
260 }
261
262 pub fn import(&mut self, tx: Transaction<Hash, Ex>) -> error::Result<Imported<Hash, Ex>> {
270 if self.is_imported(&tx.hash) {
271 return Err(error::Error::AlreadyImported(Box::new(tx.hash)))
272 }
273
274 let tx = WaitingTransaction::new(tx, self.ready.provided_tags(), &self.recently_pruned);
275 trace!(target: LOG_TARGET, "[{:?}] {:?}", tx.transaction.hash, tx);
276 debug!(
277 target: LOG_TARGET,
278 "[{:?}] Importing to {}",
279 tx.transaction.hash,
280 if tx.is_ready() { "ready" } else { "future" }
281 );
282
283 if !tx.is_ready() {
285 if self.reject_future_transactions {
286 return Err(error::Error::RejectedFutureTransaction)
287 }
288
289 let hash = tx.transaction.hash.clone();
290 self.future.import(tx);
291 return Ok(Imported::Future { hash })
292 }
293
294 self.import_to_ready(tx)
295 }
296
297 fn import_to_ready(
301 &mut self,
302 tx: WaitingTransaction<Hash, Ex>,
303 ) -> error::Result<Imported<Hash, Ex>> {
304 let hash = tx.transaction.hash.clone();
305 let mut promoted = vec![];
306 let mut failed = vec![];
307 let mut removed = vec![];
308
309 let mut first = true;
310 let mut to_import = vec![tx];
311
312 while let Some(tx) = to_import.pop() {
314 to_import.append(&mut self.future.satisfy_tags(&tx.transaction.provides));
316
317 let current_hash = tx.transaction.hash.clone();
319 match self.ready.import(tx) {
320 Ok(mut replaced) => {
321 if !first {
322 promoted.push(current_hash);
323 }
324 removed.append(&mut replaced);
327 },
328 Err(e) =>
330 if first {
331 debug!(target: LOG_TARGET, "[{:?}] Error importing: {:?}", current_hash, e);
332 return Err(e)
333 } else {
334 failed.push(current_hash);
335 },
336 }
337 first = false;
338 }
339
340 if removed.iter().any(|tx| tx.hash == hash) {
346 self.ready.remove_subtree(&promoted);
349
350 debug!(target: LOG_TARGET, "[{:?}] Cycle detected, bailing.", hash);
351 return Err(error::Error::CycleDetected)
352 }
353
354 Ok(Imported::Ready { hash, promoted, failed, removed })
355 }
356
357 pub fn ready(&self) -> BestIterator<Hash, Ex> {
359 self.ready.get()
360 }
361
362 pub fn futures(&self) -> impl Iterator<Item = &Transaction<Hash, Ex>> {
364 self.future.all()
365 }
366
367 pub fn by_hashes(&self, hashes: &[Hash]) -> Vec<Option<Arc<Transaction<Hash, Ex>>>> {
372 let ready = self.ready.by_hashes(hashes);
373 let future = self.future.by_hashes(hashes);
374
375 ready.into_iter().zip(future).map(|(a, b)| a.or(b)).collect()
376 }
377
378 pub fn ready_by_hash(&self, hash: &Hash) -> Option<Arc<Transaction<Hash, Ex>>> {
380 self.ready.by_hash(hash)
381 }
382
383 pub fn enforce_limits(
390 &mut self,
391 ready: &Limit,
392 future: &Limit,
393 ) -> Vec<Arc<Transaction<Hash, Ex>>> {
394 let mut removed = vec![];
395
396 while ready.is_exceeded(self.ready.len(), self.ready.bytes()) {
397 let worst = self.ready.fold::<TransactionRef<Hash, Ex>, _>(|worst, current| {
399 let transaction = ¤t.transaction;
400 worst
401 .map(|worst| {
402 match worst.transaction.priority.cmp(&transaction.transaction.priority) {
407 Ordering::Less => worst,
408 Ordering::Equal =>
409 if worst.insertion_id > transaction.insertion_id {
410 transaction.clone()
411 } else {
412 worst
413 },
414 Ordering::Greater => transaction.clone(),
415 }
416 })
417 .or_else(|| Some(transaction.clone()))
418 });
419
420 if let Some(worst) = worst {
421 removed.append(&mut self.remove_subtree(&[worst.transaction.hash.clone()]))
422 } else {
423 break
424 }
425 }
426
427 while future.is_exceeded(self.future.len(), self.future.bytes()) {
428 let worst = self.future.fold(|worst, current| match worst {
430 None => Some(current.clone()),
431 Some(ref tx) if tx.imported_at > current.imported_at => Some(current.clone()),
432 other => other,
433 });
434
435 if let Some(worst) = worst {
436 removed.append(&mut self.remove_subtree(&[worst.transaction.hash.clone()]))
437 } else {
438 break
439 }
440 }
441
442 removed
443 }
444
445 pub fn remove_subtree(&mut self, hashes: &[Hash]) -> Vec<Arc<Transaction<Hash, Ex>>> {
454 let mut removed = self.ready.remove_subtree(hashes);
455 removed.extend(self.future.remove(hashes));
456 removed
457 }
458
459 pub fn clear_future(&mut self) -> Vec<Arc<Transaction<Hash, Ex>>> {
461 self.future.clear()
462 }
463
464 pub fn prune_tags(&mut self, tags: impl IntoIterator<Item = Tag>) -> PruneStatus<Hash, Ex> {
471 let mut to_import = vec![];
472 let mut pruned = vec![];
473 let recently_pruned = &mut self.recently_pruned[self.recently_pruned_index];
474 self.recently_pruned_index = (self.recently_pruned_index + 1) % RECENTLY_PRUNED_TAGS;
475 recently_pruned.clear();
476
477 for tag in tags {
478 to_import.append(&mut self.future.satisfy_tags(std::iter::once(&tag)));
480 pruned.append(&mut self.ready.prune_tags(tag.clone()));
482 recently_pruned.insert(tag);
484 }
485
486 let mut promoted = vec![];
487 let mut failed = vec![];
488 for tx in to_import {
489 let hash = tx.transaction.hash.clone();
490 match self.import_to_ready(tx) {
491 Ok(res) => promoted.push(res),
492 Err(e) => {
493 warn!(
494 target: LOG_TARGET,
495 "[{:?}] Failed to promote during pruning: {:?}", hash, e,
496 );
497 failed.push(hash)
498 },
499 }
500 }
501
502 PruneStatus { pruned, failed, promoted }
503 }
504
505 pub fn status(&self) -> PoolStatus {
507 PoolStatus {
508 ready: self.ready.len(),
509 ready_bytes: self.ready.bytes(),
510 future: self.future.len(),
511 future_bytes: self.future.bytes(),
512 }
513 }
514}
515
516#[derive(Debug, Clone)]
518pub struct Limit {
519 pub count: usize,
521 pub total_bytes: usize,
523}
524
525impl Limit {
526 pub fn is_exceeded(&self, count: usize, bytes: usize) -> bool {
528 self.count < count || self.total_bytes < bytes
529 }
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535
536 type Hash = u64;
537
538 fn pool() -> BasePool<Hash, Vec<u8>> {
539 BasePool::default()
540 }
541
542 const DEFAULT_TX: Transaction<Hash, Vec<u8>> = Transaction {
543 data: vec![],
544 bytes: 1,
545 hash: 1u64,
546 priority: 5u64,
547 valid_till: 64u64,
548 requires: vec![],
549 provides: vec![],
550 propagate: true,
551 source: Source::External,
552 };
553
554 #[test]
555 fn should_import_transaction_to_ready() {
556 let mut pool = pool();
558
559 pool.import(Transaction { data: vec![1u8], provides: vec![vec![1]], ..DEFAULT_TX.clone() })
561 .unwrap();
562
563 assert_eq!(pool.ready().count(), 1);
565 assert_eq!(pool.ready.len(), 1);
566 }
567
568 #[test]
569 fn should_not_import_same_transaction_twice() {
570 let mut pool = pool();
572
573 pool.import(Transaction { data: vec![1u8], provides: vec![vec![1]], ..DEFAULT_TX.clone() })
575 .unwrap();
576 pool.import(Transaction { data: vec![1u8], provides: vec![vec![1]], ..DEFAULT_TX.clone() })
577 .unwrap_err();
578
579 assert_eq!(pool.ready().count(), 1);
581 assert_eq!(pool.ready.len(), 1);
582 }
583
584 #[test]
585 fn should_import_transaction_to_future_and_promote_it_later() {
586 let mut pool = pool();
588
589 pool.import(Transaction {
591 data: vec![1u8],
592 requires: vec![vec![0]],
593 provides: vec![vec![1]],
594 ..DEFAULT_TX.clone()
595 })
596 .unwrap();
597 assert_eq!(pool.ready().count(), 0);
598 assert_eq!(pool.ready.len(), 0);
599 pool.import(Transaction {
600 data: vec![2u8],
601 hash: 2,
602 provides: vec![vec![0]],
603 ..DEFAULT_TX.clone()
604 })
605 .unwrap();
606
607 assert_eq!(pool.ready().count(), 2);
609 assert_eq!(pool.ready.len(), 2);
610 }
611
612 #[test]
613 fn should_promote_a_subgraph() {
614 let mut pool = pool();
616
617 pool.import(Transaction {
619 data: vec![1u8],
620 requires: vec![vec![0]],
621 provides: vec![vec![1]],
622 ..DEFAULT_TX.clone()
623 })
624 .unwrap();
625 pool.import(Transaction {
626 data: vec![3u8],
627 hash: 3,
628 requires: vec![vec![2]],
629 ..DEFAULT_TX.clone()
630 })
631 .unwrap();
632 pool.import(Transaction {
633 data: vec![2u8],
634 hash: 2,
635 requires: vec![vec![1]],
636 provides: vec![vec![3], vec![2]],
637 ..DEFAULT_TX.clone()
638 })
639 .unwrap();
640 pool.import(Transaction {
641 data: vec![4u8],
642 hash: 4,
643 priority: 1_000u64,
644 requires: vec![vec![3], vec![4]],
645 ..DEFAULT_TX.clone()
646 })
647 .unwrap();
648 assert_eq!(pool.ready().count(), 0);
649 assert_eq!(pool.ready.len(), 0);
650
651 let res = pool
652 .import(Transaction {
653 data: vec![5u8],
654 hash: 5,
655 provides: vec![vec![0], vec![4]],
656 ..DEFAULT_TX.clone()
657 })
658 .unwrap();
659
660 let mut it = pool.ready().into_iter().map(|tx| tx.data[0]);
662
663 assert_eq!(it.next(), Some(5));
664 assert_eq!(it.next(), Some(1));
665 assert_eq!(it.next(), Some(2));
666 assert_eq!(it.next(), Some(4));
667 assert_eq!(it.next(), Some(3));
668 assert_eq!(it.next(), None);
669 assert_eq!(
670 res,
671 Imported::Ready {
672 hash: 5,
673 promoted: vec![1, 2, 3, 4],
674 failed: vec![],
675 removed: vec![],
676 }
677 );
678 }
679
680 #[test]
681 fn should_handle_a_cycle() {
682 let mut pool = pool();
684 pool.import(Transaction {
685 data: vec![1u8],
686 requires: vec![vec![0]],
687 provides: vec![vec![1]],
688 ..DEFAULT_TX.clone()
689 })
690 .unwrap();
691 pool.import(Transaction {
692 data: vec![3u8],
693 hash: 3,
694 requires: vec![vec![1]],
695 provides: vec![vec![2]],
696 ..DEFAULT_TX.clone()
697 })
698 .unwrap();
699 assert_eq!(pool.ready().count(), 0);
700 assert_eq!(pool.ready.len(), 0);
701
702 pool.import(Transaction {
704 data: vec![2u8],
705 hash: 2,
706 requires: vec![vec![2]],
707 provides: vec![vec![0]],
708 ..DEFAULT_TX.clone()
709 })
710 .unwrap();
711
712 {
714 let mut it = pool.ready().into_iter().map(|tx| tx.data[0]);
715 assert_eq!(it.next(), None);
716 }
717 assert_eq!(pool.future.len(), 3);
719
720 let res = pool
722 .import(Transaction {
723 data: vec![4u8],
724 hash: 4,
725 priority: 50u64,
726 provides: vec![vec![0]],
727 ..DEFAULT_TX.clone()
728 })
729 .unwrap();
730 let mut it = pool.ready().into_iter().map(|tx| tx.data[0]);
731 assert_eq!(it.next(), Some(4));
732 assert_eq!(it.next(), Some(1));
733 assert_eq!(it.next(), Some(3));
734 assert_eq!(it.next(), None);
735 assert_eq!(
736 res,
737 Imported::Ready { hash: 4, promoted: vec![1, 3], failed: vec![2], removed: vec![] }
738 );
739 assert_eq!(pool.future.len(), 0);
740 }
741
742 #[test]
743 fn should_handle_a_cycle_with_low_priority() {
744 let mut pool = pool();
746 pool.import(Transaction {
747 data: vec![1u8],
748 requires: vec![vec![0]],
749 provides: vec![vec![1]],
750 ..DEFAULT_TX.clone()
751 })
752 .unwrap();
753 pool.import(Transaction {
754 data: vec![3u8],
755 hash: 3,
756 requires: vec![vec![1]],
757 provides: vec![vec![2]],
758 ..DEFAULT_TX.clone()
759 })
760 .unwrap();
761 assert_eq!(pool.ready().count(), 0);
762 assert_eq!(pool.ready.len(), 0);
763
764 pool.import(Transaction {
766 data: vec![2u8],
767 hash: 2,
768 requires: vec![vec![2]],
769 provides: vec![vec![0]],
770 ..DEFAULT_TX.clone()
771 })
772 .unwrap();
773
774 {
776 let mut it = pool.ready().into_iter().map(|tx| tx.data[0]);
777 assert_eq!(it.next(), None);
778 }
779 assert_eq!(pool.future.len(), 3);
781
782 let err = pool
784 .import(Transaction {
785 data: vec![4u8],
786 hash: 4,
787 priority: 1u64, provides: vec![vec![0]],
789 ..DEFAULT_TX.clone()
790 })
791 .unwrap_err();
792 let mut it = pool.ready().into_iter().map(|tx| tx.data[0]);
793 assert_eq!(it.next(), None);
794 assert_eq!(pool.ready.len(), 0);
795 assert_eq!(pool.future.len(), 0);
796 if let error::Error::CycleDetected = err {
797 } else {
798 assert!(false, "Invalid error kind: {:?}", err);
799 }
800 }
801
802 #[test]
803 fn should_remove_invalid_transactions() {
804 let mut pool = pool();
806 pool.import(Transaction {
807 data: vec![5u8],
808 hash: 5,
809 provides: vec![vec![0], vec![4]],
810 ..DEFAULT_TX.clone()
811 })
812 .unwrap();
813 pool.import(Transaction {
814 data: vec![1u8],
815 requires: vec![vec![0]],
816 provides: vec![vec![1]],
817 ..DEFAULT_TX.clone()
818 })
819 .unwrap();
820 pool.import(Transaction {
821 data: vec![3u8],
822 hash: 3,
823 requires: vec![vec![2]],
824 ..DEFAULT_TX.clone()
825 })
826 .unwrap();
827 pool.import(Transaction {
828 data: vec![2u8],
829 hash: 2,
830 requires: vec![vec![1]],
831 provides: vec![vec![3], vec![2]],
832 ..DEFAULT_TX.clone()
833 })
834 .unwrap();
835 pool.import(Transaction {
836 data: vec![4u8],
837 hash: 4,
838 priority: 1_000u64,
839 requires: vec![vec![3], vec![4]],
840 ..DEFAULT_TX.clone()
841 })
842 .unwrap();
843 pool.import(Transaction {
845 data: vec![6u8],
846 hash: 6,
847 priority: 1_000u64,
848 requires: vec![vec![11]],
849 ..DEFAULT_TX.clone()
850 })
851 .unwrap();
852 assert_eq!(pool.ready().count(), 5);
853 assert_eq!(pool.future.len(), 1);
854
855 pool.remove_subtree(&[6, 1]);
857
858 assert_eq!(pool.ready().count(), 1);
860 assert_eq!(pool.future.len(), 0);
861 }
862
863 #[test]
864 fn should_prune_ready_transactions() {
865 let mut pool = pool();
867 pool.import(Transaction {
869 data: vec![5u8],
870 hash: 5,
871 requires: vec![vec![0]],
872 provides: vec![vec![100]],
873 ..DEFAULT_TX.clone()
874 })
875 .unwrap();
876 pool.import(Transaction { data: vec![1u8], provides: vec![vec![1]], ..DEFAULT_TX.clone() })
878 .unwrap();
879 pool.import(Transaction {
880 data: vec![2u8],
881 hash: 2,
882 requires: vec![vec![2]],
883 provides: vec![vec![3]],
884 ..DEFAULT_TX.clone()
885 })
886 .unwrap();
887 pool.import(Transaction {
888 data: vec![3u8],
889 hash: 3,
890 requires: vec![vec![1]],
891 provides: vec![vec![2]],
892 ..DEFAULT_TX.clone()
893 })
894 .unwrap();
895 pool.import(Transaction {
896 data: vec![4u8],
897 hash: 4,
898 priority: 1_000u64,
899 requires: vec![vec![3], vec![2]],
900 provides: vec![vec![4]],
901 ..DEFAULT_TX.clone()
902 })
903 .unwrap();
904
905 assert_eq!(pool.ready().count(), 4);
906 assert_eq!(pool.future.len(), 1);
907
908 let result = pool.prune_tags(vec![vec![0], vec![2]]);
910
911 assert_eq!(result.pruned.len(), 2);
913 assert_eq!(result.failed.len(), 0);
914 assert_eq!(
915 result.promoted[0],
916 Imported::Ready { hash: 5, promoted: vec![], failed: vec![], removed: vec![] }
917 );
918 assert_eq!(result.promoted.len(), 1);
919 assert_eq!(pool.future.len(), 0);
920 assert_eq!(pool.ready.len(), 3);
921 assert_eq!(pool.ready().count(), 3);
922 }
923
924 #[test]
925 fn transaction_debug() {
926 assert_eq!(
927 format!(
928 "{:?}",
929 Transaction {
930 data: vec![4u8],
931 hash: 4,
932 priority: 1_000u64,
933 requires: vec![vec![3], vec![2]],
934 provides: vec![vec![4]],
935 ..DEFAULT_TX.clone()
936 }
937 ),
938 "Transaction { \
939hash: 4, priority: 1000, valid_till: 64, bytes: 1, propagate: true, \
940source: TransactionSource::External, requires: [03, 02], provides: [04], data: [4]}"
941 .to_owned()
942 );
943 }
944
945 #[test]
946 fn transaction_propagation() {
947 assert_eq!(
948 Transaction {
949 data: vec![4u8],
950 hash: 4,
951 priority: 1_000u64,
952 requires: vec![vec![3], vec![2]],
953 provides: vec![vec![4]],
954 ..DEFAULT_TX.clone()
955 }
956 .is_propagable(),
957 true
958 );
959
960 assert_eq!(
961 Transaction {
962 data: vec![4u8],
963 hash: 4,
964 priority: 1_000u64,
965 requires: vec![vec![3], vec![2]],
966 provides: vec![vec![4]],
967 propagate: false,
968 ..DEFAULT_TX.clone()
969 }
970 .is_propagable(),
971 false
972 );
973 }
974
975 #[test]
976 fn should_reject_future_transactions() {
977 let mut pool = pool();
979
980 pool.reject_future_transactions = true;
982
983 let err = pool.import(Transaction {
985 data: vec![5u8],
986 hash: 5,
987 requires: vec![vec![0]],
988 ..DEFAULT_TX.clone()
989 });
990
991 if let Err(error::Error::RejectedFutureTransaction) = err {
992 } else {
993 assert!(false, "Invalid error kind: {:?}", err);
994 }
995 }
996
997 #[test]
998 fn should_clear_future_queue() {
999 let mut pool = pool();
1001
1002 pool.import(Transaction {
1004 data: vec![5u8],
1005 hash: 5,
1006 requires: vec![vec![0]],
1007 ..DEFAULT_TX.clone()
1008 })
1009 .unwrap();
1010
1011 assert_eq!(pool.future.len(), 1);
1013
1014 assert_eq!(pool.clear_future().len(), 1);
1016
1017 assert_eq!(pool.future.len(), 0);
1019 }
1020
1021 #[test]
1022 fn should_accept_future_transactions_when_explicitly_asked_to() {
1023 let mut pool = pool();
1025 pool.reject_future_transactions = true;
1026
1027 let flag_value = pool.with_futures_enabled(|pool, flag| {
1029 pool.import(Transaction {
1030 data: vec![5u8],
1031 hash: 5,
1032 requires: vec![vec![0]],
1033 ..DEFAULT_TX.clone()
1034 })
1035 .unwrap();
1036
1037 flag
1038 });
1039
1040 assert_eq!(flag_value, true);
1042 assert_eq!(pool.reject_future_transactions, true);
1043 assert_eq!(pool.future.len(), 1);
1044 }
1045}