referrerpolicy=no-referrer-when-downgrade

substrate_test_runtime_transaction_pool/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Test utils for the transaction pool together with the test runtime.
19//!
20//! See [`TestApi`] for more information.
21
22use async_trait::async_trait;
23use codec::Encode;
24use parking_lot::RwLock;
25use sc_transaction_pool::{ChainApi, ValidateTransactionPriority};
26use sp_blockchain::{CachedHeaderMetadata, HashAndNumber, TreeRoute};
27use sp_runtime::{
28	generic::{self, BlockId},
29	traits::{
30		BlakeTwo256, Block as BlockT, Hash as HashT, Header as _, NumberFor, TrailingZeroInput,
31	},
32	transaction_validity::{
33		InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError,
34		ValidTransaction,
35	},
36};
37use std::{
38	collections::{BTreeMap, HashMap, HashSet},
39	sync::Arc,
40};
41use substrate_test_runtime_client::{
42	runtime::{
43		AccountId, Block, BlockNumber, Extrinsic, ExtrinsicBuilder, Hash, Header, Nonce, Transfer,
44		TransferData,
45	},
46	Sr25519Keyring::{self, *},
47};
48
49/// Error type used by [`TestApi`].
50#[derive(Debug, thiserror::Error)]
51#[error(transparent)]
52pub struct Error(#[from] pub sc_transaction_pool_api::error::Error);
53
54impl sc_transaction_pool_api::error::IntoPoolError for Error {
55	fn into_pool_error(self) -> Result<sc_transaction_pool_api::error::Error, Self> {
56		Ok(self.0)
57	}
58}
59
60pub enum IsBestBlock {
61	Yes,
62	No,
63}
64
65impl IsBestBlock {
66	pub fn is_best(&self) -> bool {
67		matches!(self, Self::Yes)
68	}
69}
70
71impl From<bool> for IsBestBlock {
72	fn from(is_best: bool) -> Self {
73		if is_best {
74			Self::Yes
75		} else {
76			Self::No
77		}
78	}
79}
80
81#[derive(Default)]
82pub struct ChainState {
83	pub block_by_number: BTreeMap<BlockNumber, Vec<(Block, IsBestBlock)>>,
84	pub block_by_hash: HashMap<Hash, Block>,
85	pub nonces: HashMap<Hash, HashMap<AccountId, u64>>,
86	pub invalid_hashes: HashSet<Hash>,
87	pub priorities: HashMap<Hash, u64>,
88	pub valid_till_blocks: HashMap<Hash, u64>,
89}
90
91/// Test Api for transaction pool.
92pub struct TestApi {
93	valid_modifier: RwLock<Box<dyn Fn(&mut ValidTransaction) + Send + Sync>>,
94	chain: RwLock<ChainState>,
95	validation_requests: RwLock<Vec<Extrinsic>>,
96	enable_stale_check: bool,
97}
98
99impl TestApi {
100	/// Test Api with Alice nonce set initially.
101	pub fn with_alice_nonce(nonce: u64) -> Self {
102		let api = Self::empty();
103		assert_eq!(api.chain.read().block_by_hash.len(), 1);
104		assert_eq!(api.chain.read().nonces.len(), 1);
105
106		api.chain
107			.write()
108			.nonces
109			.values_mut()
110			.nth(0)
111			.map(|h| h.insert(Alice.into(), nonce));
112
113		api
114	}
115
116	/// Default Test Api
117	pub fn empty() -> Self {
118		let api = TestApi {
119			valid_modifier: RwLock::new(Box::new(|_| {})),
120			chain: Default::default(),
121			validation_requests: RwLock::new(Default::default()),
122			enable_stale_check: false,
123		};
124
125		// Push genesis block
126		api.push_block(0, Vec::new(), true);
127
128		let hash0 = *api.chain.read().block_by_hash.keys().nth(0).unwrap();
129		api.chain.write().nonces.insert(hash0, Default::default());
130
131		api
132	}
133
134	pub fn enable_stale_check(mut self) -> Self {
135		self.enable_stale_check = true;
136		self
137	}
138
139	/// Set hook on modify valid result of transaction.
140	pub fn set_valid_modifier(&self, modifier: Box<dyn Fn(&mut ValidTransaction) + Send + Sync>) {
141		*self.valid_modifier.write() = modifier;
142	}
143
144	/// Push block under given number.
145	pub fn push_block(
146		&self,
147		block_number: BlockNumber,
148		xts: Vec<Extrinsic>,
149		is_best_block: bool,
150	) -> Header {
151		let parent_hash = {
152			let chain = self.chain.read();
153			block_number
154				.checked_sub(1)
155				.and_then(|num| {
156					chain.block_by_number.get(&num).map(|blocks| blocks[0].0.header.hash())
157				})
158				.unwrap_or_default()
159		};
160
161		self.push_block_with_parent(parent_hash, xts, is_best_block)
162	}
163
164	/// Push a block using the given `parent`.
165	///
166	/// Panics if `parent` does not exists.
167	pub fn push_block_with_parent(
168		&self,
169		parent: Hash,
170		xts: Vec<Extrinsic>,
171		is_best_block: bool,
172	) -> Header {
173		// `Hash::default()` is the genesis parent hash
174		let block_number = if parent == Hash::default() {
175			0
176		} else {
177			*self
178				.chain
179				.read()
180				.block_by_hash
181				.get(&parent)
182				.expect("`parent` exists")
183				.header()
184				.number() + 1
185		};
186
187		let header = Header {
188			number: block_number,
189			digest: Default::default(),
190			extrinsics_root: Hash::random(),
191			parent_hash: parent,
192			state_root: Default::default(),
193		};
194
195		self.add_block(Block::new(header.clone(), xts), is_best_block);
196
197		header
198	}
199
200	/// Add a block to the internal state.
201	pub fn add_block(&self, block: Block, is_best_block: bool) {
202		let hash = block.header.hash();
203		let block_number = block.header.number();
204
205		let mut chain = self.chain.write();
206		chain.block_by_hash.insert(hash, block.clone());
207
208		if *block_number > 0 {
209			// copy nonces to new block
210			let prev_nonces = chain
211				.nonces
212				.get(block.header.parent_hash())
213				.expect("there shall be nonces for parent block")
214				.clone();
215			chain.nonces.insert(hash, prev_nonces);
216		}
217
218		log::info!(
219			"add_block: {:?} {:?} {:?} nonces:{:#?}",
220			block_number,
221			hash,
222			block.header.parent_hash(),
223			chain.nonces
224		);
225
226		if is_best_block {
227			chain
228				.block_by_number
229				.entry(*block_number)
230				.or_default()
231				.iter_mut()
232				.for_each(|x| {
233					x.1 = IsBestBlock::No;
234				});
235		}
236
237		chain
238			.block_by_number
239			.entry(*block_number)
240			.or_default()
241			.push((block, is_best_block.into()));
242	}
243
244	fn hash_and_length_inner(ex: &Extrinsic) -> (Hash, usize) {
245		let encoded = ex.encode();
246		(BlakeTwo256::hash(&encoded), encoded.len())
247	}
248
249	/// Mark some transaction is invalid.
250	///
251	/// Next time transaction pool will try to validate this
252	/// extrinsic, api will return invalid result.
253	pub fn add_invalid(&self, xts: &Extrinsic) {
254		self.chain.write().invalid_hashes.insert(Self::hash_and_length_inner(xts).0);
255	}
256
257	/// Remove a transaction that was previously declared as invalid via `[Self::add_invalid]`.
258	///
259	/// Next time transaction pool will try to validate this
260	/// extrinsic, api will succeed.
261	pub fn remove_invalid(&self, xts: &Extrinsic) {
262		self.chain.write().invalid_hashes.remove(&Self::hash_and_length_inner(xts).0);
263	}
264
265	/// Set a transaction priority.
266	pub fn set_priority(&self, xts: &Extrinsic, priority: u64) {
267		self.chain
268			.write()
269			.priorities
270			.insert(Self::hash_and_length_inner(xts).0, priority);
271	}
272
273	/// Set a transaction mortality (block at which it will expire).
274	pub fn set_valid_till(&self, xts: &Extrinsic, valid_till: u64) {
275		self.chain
276			.write()
277			.valid_till_blocks
278			.insert(Self::hash_and_length_inner(xts).0, valid_till);
279	}
280
281	/// Query validation requests received.
282	pub fn validation_requests(&self) -> Vec<Extrinsic> {
283		self.validation_requests.read().clone()
284	}
285
286	/// get a reference to the chain state
287	pub fn chain(&self) -> &RwLock<ChainState> {
288		&self.chain
289	}
290
291	/// Set nonce in the inner state for given block.
292	pub fn set_nonce(&self, at: Hash, account: AccountId, nonce: u64) {
293		let mut chain = self.chain.write();
294		chain.nonces.entry(at).and_modify(|h| {
295			h.insert(account, nonce);
296		});
297
298		log::debug!("set_nonce: {:?} nonces:{:#?}", at, chain.nonces);
299	}
300
301	/// Increment nonce in the inner state for given block.
302	pub fn increment_nonce_at_block(&self, at: Hash, account: AccountId) {
303		let mut chain = self.chain.write();
304		chain.nonces.entry(at).and_modify(|h| {
305			h.entry(account).and_modify(|n| *n += 1).or_insert(1);
306		});
307
308		log::debug!("increment_nonce_at_block: {:?} nonces:{:#?}", at, chain.nonces);
309	}
310
311	/// Increment nonce in the inner state.
312	pub fn increment_nonce(&self, account: AccountId) {
313		let mut chain = self.chain.write();
314		// if no particular block was given, then update nonce everywhere
315		chain.nonces.values_mut().for_each(|h| {
316			h.entry(account).and_modify(|n| *n += 1).or_insert(1);
317		})
318	}
319
320	/// Calculate a tree route between the two given blocks.
321	pub fn tree_route(
322		&self,
323		from: Hash,
324		to: Hash,
325	) -> Result<sp_blockchain::TreeRoute<Block>, Error> {
326		sp_blockchain::tree_route(self, from, to)
327	}
328
329	/// Helper function for mapping block number to hash. Use if mapping shall not fail.
330	pub fn expect_hash_from_number(&self, n: BlockNumber) -> Hash {
331		self.block_id_to_hash(&BlockId::Number(n)).unwrap().unwrap()
332	}
333
334	/// Helper function for getting genesis hash
335	pub fn genesis_hash(&self) -> Hash {
336		self.expect_hash_from_number(0)
337	}
338
339	pub fn expect_hash_and_number(&self, n: BlockNumber) -> HashAndNumber<Block> {
340		HashAndNumber { hash: self.expect_hash_from_number(n), number: n }
341	}
342}
343
344trait TagFrom {
345	fn tag_from(&self) -> u8;
346}
347
348impl TagFrom for AccountId {
349	fn tag_from(&self) -> u8 {
350		let f = Sr25519Keyring::iter().enumerate().find(|k| AccountId::from(k.1) == *self);
351		u8::try_from(f.unwrap().0).unwrap()
352	}
353}
354
355#[async_trait]
356impl ChainApi for TestApi {
357	type Block = Block;
358	type Error = Error;
359
360	async fn validate_transaction(
361		&self,
362		at: <Self::Block as BlockT>::Hash,
363		source: TransactionSource,
364		uxt: Arc<<Self::Block as BlockT>::Extrinsic>,
365		_: ValidateTransactionPriority,
366	) -> Result<TransactionValidity, Error> {
367		self.validate_transaction_blocking(at, source, uxt)
368	}
369
370	fn validate_transaction_blocking(
371		&self,
372		at: <Self::Block as BlockT>::Hash,
373		_source: TransactionSource,
374		uxt: Arc<<Self::Block as BlockT>::Extrinsic>,
375	) -> Result<TransactionValidity, Error> {
376		let uxt = (*uxt).clone();
377		self.validation_requests.write().push(uxt.clone());
378		let block_number;
379
380		match self.block_id_to_number(&BlockId::Hash(at)) {
381			Ok(Some(number)) => {
382				let found_best = self
383					.chain
384					.read()
385					.block_by_number
386					.get(&number)
387					.map(|blocks| blocks.iter().any(|b| b.1.is_best()))
388					.unwrap_or(false);
389				block_number = Some(number);
390
391				// If there is no best block, we don't know based on which block we should validate
392				// the transaction. (This is not required for this test function, but in real
393				// environment it would fail because of this).
394				if !found_best {
395					return Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(1))))
396				}
397			},
398			Ok(None) =>
399				return Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(2)))),
400			Err(e) => return Err(e),
401		}
402
403		let (requires, provides) = if let Ok(transfer) = TransferData::try_from(&uxt) {
404			let chain_nonce = self
405				.chain
406				.read()
407				.nonces
408				.get(&at)
409				.expect("nonces must be there for every block")
410				.get(&transfer.from)
411				.cloned()
412				.unwrap_or(0);
413			let requires = if chain_nonce == transfer.nonce {
414				vec![]
415			} else {
416				if self.enable_stale_check {
417					vec![vec![transfer.from.tag_from(), (transfer.nonce - 1) as u8]]
418				} else {
419					vec![vec![(transfer.nonce - 1) as u8]]
420				}
421			};
422			let provides = if self.enable_stale_check {
423				vec![vec![transfer.from.tag_from(), transfer.nonce as u8]]
424			} else {
425				vec![vec![transfer.nonce as u8]]
426			};
427
428			log::info!(
429				"test_api::validate_transaction: h:{:?} n:{:?} cn:{:?} tn:{:?} r:{:?} p:{:?}",
430				at,
431				block_number,
432				chain_nonce,
433				transfer.nonce,
434				requires,
435				provides,
436			);
437
438			if self.enable_stale_check && transfer.nonce < chain_nonce {
439				log::info!("test_api::validate_transaction: invalid_transaction(stale)....");
440				return Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)))
441			}
442
443			(requires, provides)
444		} else {
445			(Vec::new(), vec![uxt.encode()])
446		};
447
448		if self.chain.read().invalid_hashes.contains(&self.hash_and_length(&uxt).0) {
449			log::info!("test_api::validate_transaction: invalid_transaction....");
450			return Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(0))))
451		}
452
453		let priority = self.chain.read().priorities.get(&self.hash_and_length(&uxt).0).cloned();
454		let longevity = self
455			.chain
456			.read()
457			.valid_till_blocks
458			.get(&self.hash_and_length(&uxt).0)
459			.cloned()
460			.map(|valid_till| valid_till.saturating_sub(block_number.unwrap()))
461			.unwrap_or(64);
462
463		if longevity == 0 {
464			return Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::BadProof)))
465		}
466
467		let mut validity = ValidTransaction {
468			priority: priority.unwrap_or(1),
469			requires,
470			provides,
471			longevity,
472			propagate: true,
473		};
474
475		(self.valid_modifier.read())(&mut validity);
476
477		Ok(Ok(validity))
478	}
479
480	fn block_id_to_number(
481		&self,
482		at: &BlockId<Self::Block>,
483	) -> Result<Option<NumberFor<Self::Block>>, Error> {
484		Ok(match at {
485			generic::BlockId::Hash(x) =>
486				self.chain.read().block_by_hash.get(x).map(|b| *b.header.number()),
487			generic::BlockId::Number(num) => Some(*num),
488		})
489	}
490
491	fn block_id_to_hash(
492		&self,
493		at: &BlockId<Self::Block>,
494	) -> Result<Option<<Self::Block as BlockT>::Hash>, Error> {
495		Ok(match at {
496			generic::BlockId::Hash(x) => Some(*x),
497			generic::BlockId::Number(num) =>
498				self.chain.read().block_by_number.get(num).and_then(|blocks| {
499					blocks.iter().find(|b| b.1.is_best()).map(|b| b.0.header().hash())
500				}),
501		})
502	}
503
504	fn hash_and_length(&self, ex: &<Self::Block as BlockT>::Extrinsic) -> (Hash, usize) {
505		Self::hash_and_length_inner(ex)
506	}
507
508	async fn block_body(
509		&self,
510		hash: <Self::Block as BlockT>::Hash,
511	) -> Result<Option<Vec<Extrinsic>>, Error> {
512		Ok(self.chain.read().block_by_hash.get(&hash).map(|b| b.extrinsics().to_vec()))
513	}
514
515	fn block_header(
516		&self,
517		hash: <Self::Block as BlockT>::Hash,
518	) -> Result<Option<<Self::Block as BlockT>::Header>, Self::Error> {
519		Ok(self.chain.read().block_by_hash.get(&hash).map(|b| b.header().clone()))
520	}
521
522	fn tree_route(
523		&self,
524		from: <Self::Block as BlockT>::Hash,
525		to: <Self::Block as BlockT>::Hash,
526	) -> Result<TreeRoute<Self::Block>, Self::Error> {
527		sp_blockchain::tree_route::<Block, TestApi>(self, from, to).map_err(Into::into)
528	}
529}
530
531impl sp_blockchain::HeaderMetadata<Block> for TestApi {
532	type Error = Error;
533
534	fn header_metadata(&self, hash: Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
535		let chain = self.chain.read();
536		let block = chain.block_by_hash.get(&hash).expect("Hash exists");
537
538		Ok(block.header().into())
539	}
540
541	fn insert_header_metadata(&self, _: Hash, _: CachedHeaderMetadata<Block>) {
542		unimplemented!("Not implemented for tests")
543	}
544
545	fn remove_header_metadata(&self, _: Hash) {
546		unimplemented!("Not implemented for tests")
547	}
548}
549
550/// Generate transfer extrinsic with a given nonce.
551///
552/// Part of the test api.
553pub fn uxt(who: Sr25519Keyring, nonce: Nonce) -> Extrinsic {
554	let dummy = codec::Decode::decode(&mut TrailingZeroInput::zeroes()).unwrap();
555	let transfer = Transfer { from: who.into(), to: dummy, nonce, amount: 1 };
556	ExtrinsicBuilder::new_transfer(transfer).build()
557}