// This file is part of Bizinikiwi. // Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . //! Testing related primitives for internal usage in this crate. use crate::{ graph::{BlockHash, ChainApi, ExtrinsicFor, NumberFor, RawExtrinsicFor}, ValidateTransactionPriority, }; use async_trait::async_trait; use bizinikiwi_test_runtime::{ bizinikiwi_test_pallet::pezpallet::Call as PalletCall, BalancesCall, Block, BlockNumber, Extrinsic, ExtrinsicBuilder, Hashing, RuntimeCall, Transfer, TransferData, H256, }; use codec::Encode; use parking_lot::Mutex; use pezsc_transaction_pool_api::error; use pezsp_blockchain::{HashAndNumber, TreeRoute}; use pezsp_runtime::{ generic::BlockId, traits::{Block as BlockT, Hash}, transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction, }, }; use std::{collections::HashSet, sync::Arc}; type Pool = crate::graph::Pool; pub(crate) const INVALID_NONCE: u64 = 254; /// Test api that implements [`ChainApi`]. #[derive(Clone, Debug, Default)] pub(crate) struct TestApi { pub delay: Arc>>>, pub invalidate: Arc>>, pub clear_requirements: Arc>>, pub add_requirements: Arc>>, pub validation_requests: Arc>>, } impl TestApi { /// Query validation requests received. pub fn validation_requests(&self) -> Vec { self.validation_requests.lock().clone() } /// Helper function for mapping block number to hash. Use if mapping shall not fail. pub fn expect_hash_from_number(&self, n: BlockNumber) -> H256 { self.block_id_to_hash(&BlockId::Number(n)).unwrap().unwrap() } pub fn expect_hash_and_number(&self, n: BlockNumber) -> HashAndNumber { HashAndNumber { hash: self.expect_hash_from_number(n), number: n } } } #[async_trait] impl ChainApi for TestApi { type Block = Block; type Error = error::Error; /// Verify extrinsic at given block. async fn validate_transaction( &self, at: ::Hash, _source: TransactionSource, uxt: ExtrinsicFor, _: ValidateTransactionPriority, ) -> Result { let uxt = (*uxt).clone(); self.validation_requests.lock().push(uxt.clone()); let hash = self.hash_and_length(&uxt).0; let block_number = self.block_id_to_number(&BlockId::Hash(at)).unwrap().unwrap(); let res = match uxt { Extrinsic { function: RuntimeCall::Balances(BalancesCall::transfer_allow_death { .. }), .. } => { let TransferData { nonce, .. } = (&uxt).try_into().unwrap(); // This is used to control the test flow. if nonce > 0 { let opt = self.delay.lock().take(); if let Some(delay) = opt { if delay.recv().is_err() { println!("Error waiting for delay!"); } } } if self.invalidate.lock().contains(&hash) { InvalidTransaction::Custom(0).into() } else if nonce < block_number { InvalidTransaction::Stale.into() } else { let mut transaction = ValidTransaction { priority: 4, requires: if nonce > block_number { vec![vec![nonce as u8 - 1]] } else { vec![] }, provides: if nonce == INVALID_NONCE { vec![] } else { vec![vec![nonce as u8]] }, longevity: 3, propagate: true, }; if self.clear_requirements.lock().contains(&hash) { transaction.requires.clear(); } if self.add_requirements.lock().contains(&hash) { transaction.requires.push(vec![128]); } Ok(transaction) } }, Extrinsic { function: RuntimeCall::BizinikiwiTest(PalletCall::include_data { .. }), .. } => Ok(ValidTransaction { priority: 9001, requires: vec![], provides: vec![vec![42]], longevity: 9001, propagate: false, }), Extrinsic { function: RuntimeCall::BizinikiwiTest(PalletCall::indexed_call { .. }), .. } => Ok(ValidTransaction { priority: 9001, requires: vec![], provides: vec![vec![43]], longevity: 9001, propagate: false, }), _ => unimplemented!(), }; Ok(res) } fn validate_transaction_blocking( &self, _at: ::Hash, _source: TransactionSource, _uxt: Arc<::Extrinsic>, ) -> Result { unimplemented!(); } /// Returns a block number given the block id. fn block_id_to_number( &self, at: &BlockId, ) -> Result>, Self::Error> { Ok(match at { BlockId::Number(num) => Some(*num), BlockId::Hash(hash) if *hash == H256::from_low_u64_be(hash.to_low_u64_be()) => { Some(hash.to_low_u64_be()) }, BlockId::Hash(_) => None, }) } /// Returns a block hash given the block id. fn block_id_to_hash( &self, at: &BlockId, ) -> Result::Hash>, Self::Error> { Ok(match at { BlockId::Number(num) => Some(H256::from_low_u64_be(*num)).into(), BlockId::Hash(hash) => Some(*hash), }) } /// Hash the extrinsic. fn hash_and_length(&self, uxt: &RawExtrinsicFor) -> (BlockHash, usize) { let encoded = uxt.encode(); let len = encoded.len(); (Hashing::hash(&encoded), len) } async fn block_body( &self, _id: ::Hash, ) -> Result::Extrinsic>>, Self::Error> { Ok(None) } fn block_header( &self, _: ::Hash, ) -> Result::Header>, Self::Error> { Ok(None) } fn tree_route( &self, _from: ::Hash, _to: ::Hash, ) -> Result, Self::Error> { unimplemented!() } } pub(crate) fn uxt(transfer: Transfer) -> Extrinsic { ExtrinsicBuilder::new_transfer(transfer).build() } pub(crate) fn pool() -> (Pool, Arc) { let api = Arc::new(TestApi::default()); (Pool::new_with_staticly_sized_rotator(Default::default(), true.into(), api.clone()), api) }