sc_transaction_pool/graph/
base_pool.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! A basic version of the dependency graph.
20//!
21//! For a more full-featured pool, have a look at the `pool` module.
22
23use 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/// Successful import result.
44#[derive(Debug, PartialEq, Eq)]
45pub enum Imported<Hash, Ex> {
46	/// Transaction was successfully imported to Ready queue.
47	Ready {
48		/// Hash of transaction that was successfully imported.
49		hash: Hash,
50		/// Transactions that got promoted from the Future queue.
51		promoted: Vec<Hash>,
52		/// Transactions that failed to be promoted from the Future queue and are now discarded.
53		failed: Vec<Hash>,
54		/// Transactions removed from the Ready pool (replaced).
55		removed: Vec<Arc<Transaction<Hash, Ex>>>,
56	},
57	/// Transaction was successfully imported to Future queue.
58	Future {
59		/// Hash of transaction that was successfully imported.
60		hash: Hash,
61	},
62}
63
64impl<Hash, Ex> Imported<Hash, Ex> {
65	/// Returns the hash of imported transaction.
66	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/// Status of pruning the queue.
76#[derive(Debug)]
77pub struct PruneStatus<Hash, Ex> {
78	/// A list of imports that satisfying the tag triggered.
79	pub promoted: Vec<Imported<Hash, Ex>>,
80	/// A list of transactions that failed to be promoted and now are discarded.
81	pub failed: Vec<Hash>,
82	/// A list of transactions that got pruned from the ready queue.
83	pub pruned: Vec<Arc<Transaction<Hash, Ex>>>,
84}
85
86/// Immutable transaction
87#[derive(PartialEq, Eq, Clone)]
88pub struct Transaction<Hash, Extrinsic> {
89	/// Raw extrinsic representing that transaction.
90	pub data: Extrinsic,
91	/// Number of bytes encoding of the transaction requires.
92	pub bytes: usize,
93	/// Transaction hash (unique)
94	pub hash: Hash,
95	/// Transaction priority (higher = better)
96	pub priority: Priority,
97	/// At which block the transaction becomes invalid?
98	pub valid_till: Longevity,
99	/// Tags required by the transaction.
100	pub requires: Vec<Tag>,
101	/// Tags that this transaction provides.
102	pub provides: Vec<Tag>,
103	/// Should that transaction be propagated.
104	pub propagate: bool,
105	/// Source of that transaction.
106	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	/// Explicit transaction clone.
150	///
151	/// Transaction should be cloned only if absolutely necessary && we want
152	/// every reason to be commented. That's why we `Transaction` is not `Clone`,
153	/// but there's explicit `duplicate` method.
154	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
197/// Store last pruned tags for given number of invocations.
198const RECENTLY_PRUNED_TAGS: usize = 2;
199
200/// Transaction pool.
201///
202/// Builds a dependency graph for all transactions in the pool and returns
203/// the ones that are currently ready to be executed.
204///
205/// General note:
206/// If function returns some transactions it usually means that importing them
207/// as-is for the second time will fail or produce unwanted results.
208/// Most likely it is required to revalidate them and recompute set of
209/// required tags.
210#[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	/// Store recently pruned tags (for last two invocations).
216	///
217	/// This is used to make sure we don't accidentally put
218	/// transactions to future in case they were just stuck in verification.
219	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	/// Create new pool given reject_future_transactions flag.
231	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	/// Temporary enables future transactions, runs closure and then restores
242	/// `reject_future_transactions` flag back to previous value.
243	///
244	/// The closure accepts the mutable reference to the pool and original value
245	/// of the `reject_future_transactions` flag.
246	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	/// Returns if the transaction for the given hash is already imported.
258	pub fn is_imported(&self, tx_hash: &Hash) -> bool {
259		self.future.contains(tx_hash) || self.ready.contains(tx_hash)
260	}
261
262	/// Imports transaction to the pool.
263	///
264	/// The pool consists of two parts: Future and Ready.
265	/// The former contains transactions that require some tags that are not yet provided by
266	/// other transactions in the pool.
267	/// The latter contains transactions that have all the requirements satisfied and are
268	/// ready to be included in the block.
269	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 all tags are not satisfied import to future.
284		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	/// Imports transaction to ready queue.
298	///
299	/// NOTE the transaction has to have all requirements satisfied.
300	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		// take first transaction from the list
313		while let Some(tx) = to_import.pop() {
314			// find transactions in Future that it unlocks
315			to_import.append(&mut self.future.satisfy_tags(&tx.transaction.provides));
316
317			// import this transaction
318			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					// The transactions were removed from the ready pool. We might attempt to
325					// re-import them.
326					removed.append(&mut replaced);
327				},
328				// transaction failed to be imported.
329				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		// An edge case when importing transaction caused
341		// some future transactions to be imported and that
342		// future transactions pushed out current transaction.
343		// This means that there is a cycle and the transactions should
344		// be moved back to future, since we can't resolve it.
345		if removed.iter().any(|tx| tx.hash == hash) {
346			// We still need to remove all transactions that we promoted
347			// since they depend on each other and will never get to the best iterator.
348			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	/// Returns an iterator over ready transactions in the pool.
358	pub fn ready(&self) -> BestIterator<Hash, Ex> {
359		self.ready.get()
360	}
361
362	/// Returns an iterator over future transactions in the pool.
363	pub fn futures(&self) -> impl Iterator<Item = &Transaction<Hash, Ex>> {
364		self.future.all()
365	}
366
367	/// Returns pool transactions given list of hashes.
368	///
369	/// Includes both ready and future pool. For every hash in the `hashes`
370	/// iterator an `Option` is produced (so the resulting `Vec` always have the same length).
371	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	/// Returns pool transaction by hash.
379	pub fn ready_by_hash(&self, hash: &Hash) -> Option<Arc<Transaction<Hash, Ex>>> {
380		self.ready.by_hash(hash)
381	}
382
383	/// Makes sure that the transactions in the queues stay within provided limits.
384	///
385	/// Removes and returns worst transactions from the queues and all transactions that depend on
386	/// them. Technically the worst transaction should be evaluated by computing the entire pending
387	/// set. We use a simplified approach to remove transactions with the lowest priority first or
388	/// those that occupy the pool for the longest time in case priority is the same.
389	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			// find the worst transaction
398			let worst = self.ready.fold::<TransactionRef<Hash, Ex>, _>(|worst, current| {
399				let transaction = &current.transaction;
400				worst
401					.map(|worst| {
402						// Here we don't use `TransactionRef`'s ordering implementation because
403						// while it prefers priority like need here, it also prefers older
404						// transactions for inclusion purposes and limit enforcement needs to prefer
405						// newer transactions instead and drop the older ones.
406						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			// find the worst transaction
429			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	/// Removes all transactions represented by the hashes and all other transactions
446	/// that depend on them.
447	///
448	/// Returns a list of actually removed transactions.
449	/// NOTE some transactions might still be valid, but were just removed because
450	/// they were part of a chain, you may attempt to re-import them later.
451	/// NOTE If you want to remove ready transactions that were already used
452	/// and you don't want them to be stored in the pool use `prune_tags` method.
453	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	/// Removes and returns all transactions from the future queue.
460	pub fn clear_future(&mut self) -> Vec<Arc<Transaction<Hash, Ex>>> {
461		self.future.clear()
462	}
463
464	/// Prunes transactions that provide given list of tags.
465	///
466	/// This will cause all transactions that provide these tags to be removed from the pool,
467	/// but unlike `remove_subtree`, dependent transactions are not touched.
468	/// Additional transactions from future queue might be promoted to ready if you satisfy tags
469	/// that the pool didn't previously know about.
470	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			// make sure to promote any future transactions that could be unlocked
479			to_import.append(&mut self.future.satisfy_tags(std::iter::once(&tag)));
480			// and actually prune transactions in ready queue
481			pruned.append(&mut self.ready.prune_tags(tag.clone()));
482			// store the tags for next submission
483			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	/// Get pool status.
506	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/// Queue limits
517#[derive(Debug, Clone)]
518pub struct Limit {
519	/// Maximal number of transactions in the queue.
520	pub count: usize,
521	/// Maximal size of encodings of all transactions in the queue.
522	pub total_bytes: usize,
523}
524
525impl Limit {
526	/// Returns true if any of the provided values exceeds the limit.
527	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		// given
557		let mut pool = pool();
558
559		// when
560		pool.import(Transaction { data: vec![1u8], provides: vec![vec![1]], ..DEFAULT_TX.clone() })
561			.unwrap();
562
563		// then
564		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		// given
571		let mut pool = pool();
572
573		// when
574		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		// then
580		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		// given
587		let mut pool = pool();
588
589		// when
590		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		// then
608		assert_eq!(pool.ready().count(), 2);
609		assert_eq!(pool.ready.len(), 2);
610	}
611
612	#[test]
613	fn should_promote_a_subgraph() {
614		// given
615		let mut pool = pool();
616
617		// when
618		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		// then
661		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		// given
683		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		// when
703		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		// then
713		{
714			let mut it = pool.ready().into_iter().map(|tx| tx.data[0]);
715			assert_eq!(it.next(), None);
716		}
717		// all transactions occupy the Future queue - it's fine
718		assert_eq!(pool.future.len(), 3);
719
720		// let's close the cycle with one additional transaction
721		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		// given
745		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		// when
765		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		// then
775		{
776			let mut it = pool.ready().into_iter().map(|tx| tx.data[0]);
777			assert_eq!(it.next(), None);
778		}
779		// all transactions occupy the Future queue - it's fine
780		assert_eq!(pool.future.len(), 3);
781
782		// let's close the cycle with one additional transaction
783		let err = pool
784			.import(Transaction {
785				data: vec![4u8],
786				hash: 4,
787				priority: 1u64, // lower priority than Tx(2)
788				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		// given
805		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		// future
844		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		// when
856		pool.remove_subtree(&[6, 1]);
857
858		// then
859		assert_eq!(pool.ready().count(), 1);
860		assert_eq!(pool.future.len(), 0);
861	}
862
863	#[test]
864	fn should_prune_ready_transactions() {
865		// given
866		let mut pool = pool();
867		// future (waiting for 0)
868		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		// ready
877		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		// when
909		let result = pool.prune_tags(vec![vec![0], vec![2]]);
910
911		// then
912		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		// given
978		let mut pool = pool();
979
980		// when
981		pool.reject_future_transactions = true;
982
983		// then
984		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		// given
1000		let mut pool = pool();
1001
1002		// when
1003		pool.import(Transaction {
1004			data: vec![5u8],
1005			hash: 5,
1006			requires: vec![vec![0]],
1007			..DEFAULT_TX.clone()
1008		})
1009		.unwrap();
1010
1011		// then
1012		assert_eq!(pool.future.len(), 1);
1013
1014		// and then when
1015		assert_eq!(pool.clear_future().len(), 1);
1016
1017		// then
1018		assert_eq!(pool.future.len(), 0);
1019	}
1020
1021	#[test]
1022	fn should_accept_future_transactions_when_explicitly_asked_to() {
1023		// given
1024		let mut pool = pool();
1025		pool.reject_future_transactions = true;
1026
1027		// when
1028		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		// then
1041		assert_eq!(flag_value, true);
1042		assert_eq!(pool.reject_future_transactions, true);
1043		assert_eq!(pool.future.len(), 1);
1044	}
1045}