// This file is part of Bizinikiwi. // Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use codec::Encode; use pezframe_support::{ derive_impl, dispatch::GetDispatchInfo, pezpallet_prelude::{TransactionSource, Weight}, }; use pezsp_runtime::{ testing::UintAuthorityId, traits::{Applyable, Checkable}, transaction_validity::{ InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, }, BuildStorage, DispatchError, }; // test for instance #[pezframe_support::pezpallet] pub mod pallet1 { use pezframe_support::pezpallet_prelude::*; use pezframe_system::pezpallet_prelude::*; pub const CALL_1_AUTH_WEIGHT: Weight = Weight::from_all(1); pub const CALL_1_WEIGHT: Weight = Weight::from_all(2); pub const CALL_2_AUTH_WEIGHT: Weight = Weight::from_all(3); pub const CALL_2_WEIGHT: Weight = Weight::from_all(5); pub const CALL_2_REFUND: Weight = Weight::from_all(4); pub const CALL_3_AUTH_WEIGHT: Weight = Weight::from_all(6); pub const CALL_3_WEIGHT: Weight = Weight::from_all(7); pub const CALL_3_REFUND: Weight = Weight::from_all(6); pub const CALL_4_AUTH_WEIGHT: Weight = Weight::from_all(10); pub const CALL_4_WEIGHT: Weight = Weight::from_all(11); #[pezpallet::pezpallet] pub struct Pezpallet(_); pub trait WeightInfo { fn call1() -> Weight; fn call2() -> Weight; fn authorize_call2() -> Weight; fn call3() -> Weight; fn authorize_call3() -> Weight; } impl WeightInfo for () { fn call1() -> Weight { CALL_1_WEIGHT } fn call2() -> Weight { CALL_2_WEIGHT } fn authorize_call2() -> Weight { CALL_2_AUTH_WEIGHT } fn call3() -> Weight { CALL_3_WEIGHT } fn authorize_call3() -> Weight { CALL_3_AUTH_WEIGHT } } #[pezpallet::config] pub trait Config: pezframe_system::Config { type SomeGeneric: Parameter; type WeightInfo: WeightInfo; } #[pezpallet::call(weight = T::WeightInfo)] impl, I: 'static> Pezpallet { #[pezpallet::authorize(|_source| Ok((ValidTransaction::default(), Weight::zero())))] #[pezpallet::weight_of_authorize(CALL_1_AUTH_WEIGHT)] #[pezpallet::call_index(0)] pub fn call1(origin: OriginFor) -> DispatchResult { ensure_authorized(origin)?; Ok(()) } #[pezpallet::authorize(|_source, a, b, c, d, e, f, authorize_refund| if *a { let valid = ValidTransaction { priority: *b, requires: vec![c.encode()], provides: vec![d.encode()], longevity: *e, propagate: *f, }; Ok((valid, *authorize_refund)) } else { Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) } )] #[pezpallet::call_index(1)] pub fn call2( origin: OriginFor, a: bool, b: u64, c: u8, d: u8, e: u64, f: bool, authorize_refund: Weight, ) -> DispatchResultWithPostInfo { ensure_authorized(origin)?; let _ = (a, b, c, d, e, f, authorize_refund); Ok(Some(CALL_2_REFUND).into()) } #[pezpallet::authorize(Self::authorize_call3)] #[pezpallet::call_index(2)] pub fn call3( origin: OriginFor, valid: bool, _some_gen: T::SomeGeneric, ) -> DispatchResultWithPostInfo { ensure_authorized(origin)?; let _ = valid; Err(pezsp_runtime::DispatchErrorWithPostInfo { post_info: Some(CALL_3_REFUND).into(), error: DispatchError::Other("Call3 failed"), }) } #[cfg(feature = "frame-feature-testing")] #[pezpallet::call_index(3)] #[pezpallet::authorize(|_source| Ok((ValidTransaction::default(), Weight::zero())))] #[pezpallet::weight_of_authorize(CALL_4_AUTH_WEIGHT)] #[pezpallet::weight(CALL_4_WEIGHT)] pub fn call4(origin: OriginFor) -> DispatchResult { ensure_authorized(origin)?; Ok(()) } } impl, I: 'static> Pezpallet { fn authorize_call3( _source: TransactionSource, valid: &bool, _some_gen: &T::SomeGeneric, ) -> TransactionValidityWithRefund { if *valid { Ok(Default::default()) } else { Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) } } } } // test for dev mode. #[pezframe_support::pezpallet(dev_mode)] pub mod pallet2 { use pezframe_support::pezpallet_prelude::*; use pezframe_system::pezpallet_prelude::*; #[pezpallet::pezpallet] pub struct Pezpallet(_); pub trait SomeTrait {} #[pezpallet::config] pub trait Config: pezframe_system::Config {} #[pezpallet::call] impl Pezpallet { #[pezpallet::authorize(|_source| Ok(Default::default()))] pub fn call1(origin: OriginFor) -> DispatchResult { ensure_authorized(origin)?; Ok(()) } } } // test for no pezpallet info. #[pezframe_support::pezpallet] pub mod pallet3 { use pezframe_support::pezpallet_prelude::*; use pezframe_system::pezpallet_prelude::*; pub const CALL_1_AUTH_WEIGHT: Weight = Weight::from_all(1); pub const CALL_1_WEIGHT: Weight = Weight::from_all(1); #[pezpallet::pezpallet] pub struct Pezpallet(_); #[pezpallet::config] pub trait Config: crate::pallet1::Config + pezframe_system::Config {} #[pezpallet::call] impl Pezpallet { #[pezpallet::authorize(|_source| Ok(Default::default()))] #[pezpallet::weight_of_authorize(CALL_1_AUTH_WEIGHT)] #[pezpallet::weight(CALL_1_WEIGHT)] #[pezpallet::call_index(0)] pub fn call1(origin: OriginFor) -> DispatchResult { ensure_authorized(origin)?; Ok(()) } } } // test for pezpallet with no authorized call #[pezframe_support::pezpallet(dev_mode)] pub mod pallet4 { use pezframe_support::pezpallet_prelude::*; use pezframe_system::pezpallet_prelude::*; #[pezpallet::pezpallet] pub struct Pezpallet(_); #[pezpallet::config] pub trait Config: pezframe_system::Config {} #[pezpallet::call] impl Pezpallet { #[pezpallet::call_index(0)] pub fn call1(origin: OriginFor) -> DispatchResult { ensure_authorized(origin)?; Ok(()) } } } #[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)] impl pezframe_system::Config for Runtime { type Block = Block; } impl pallet2::SomeTrait for RuntimeOrigin {} impl pallet1::Config for Runtime { type SomeGeneric = u32; type WeightInfo = (); } impl pallet1::Config for Runtime { type SomeGeneric = u32; type WeightInfo = (); } #[cfg(feature = "frame-feature-testing")] impl pallet1::Config for Runtime { type SomeGeneric = u32; type WeightInfo = (); } impl pallet2::Config for Runtime {} impl pallet3::Config for Runtime {} impl pallet4::Config for Runtime {} pub type TransactionExtension = pezframe_system::AuthorizeCall; pub type Header = pezsp_runtime::generic::Header; pub type Block = pezsp_runtime::generic::Block; pub type UncheckedExtrinsic = pezsp_runtime::generic::UncheckedExtrinsic< u64, RuntimeCall, UintAuthorityId, TransactionExtension, >; pezframe_support::construct_runtime!( pub enum Runtime { System: pezframe_system, Pallet1: pallet1, Pallet1Instance2: pallet1::, Pallet2: pallet2, Pallet3: pallet3, #[cfg(feature = "frame-feature-testing")] Pallet1Instance3: pallet1::, Pallet4: pallet4, } ); pub fn new_test_ext() -> pezsp_io::TestExternalities { let t = RuntimeGenesisConfig { ..Default::default() }.build_storage().unwrap(); t.into() } #[test] fn valid_call_weight_test() { // Tests for valid successful calls and assert their weight struct Test { call: RuntimeCall, dispatch_success: bool, call_weight: Weight, ext_weight: Weight, actual_weight: Weight, } let call_2_auth_weight_refund = Weight::from_all(2); let tests = vec![ Test { call: RuntimeCall::Pallet1(pallet1::Call::call1 {}), dispatch_success: true, call_weight: pallet1::CALL_1_WEIGHT, ext_weight: pallet1::CALL_1_AUTH_WEIGHT, actual_weight: pallet1::CALL_1_WEIGHT + pallet1::CALL_1_AUTH_WEIGHT, }, Test { call: RuntimeCall::Pallet1(pallet1::Call::call2 { a: true, b: 1, c: 2, d: 3, e: 4, f: true, authorize_refund: Weight::zero(), }), dispatch_success: true, call_weight: pallet1::CALL_2_WEIGHT, ext_weight: pallet1::CALL_2_AUTH_WEIGHT, actual_weight: pallet1::CALL_2_REFUND + pallet1::CALL_2_AUTH_WEIGHT, }, Test { call: RuntimeCall::Pallet1(pallet1::Call::call3 { valid: true, some_gen: 1 }), dispatch_success: false, call_weight: pallet1::CALL_3_WEIGHT, ext_weight: pallet1::CALL_3_AUTH_WEIGHT, actual_weight: pallet1::CALL_3_REFUND + pallet1::CALL_3_AUTH_WEIGHT, }, Test { call: RuntimeCall::Pallet1Instance2(pallet1::Call::call1 {}), dispatch_success: true, call_weight: pallet1::CALL_1_WEIGHT, ext_weight: pallet1::CALL_1_AUTH_WEIGHT, actual_weight: pallet1::CALL_1_WEIGHT + pallet1::CALL_1_AUTH_WEIGHT, }, Test { call: RuntimeCall::Pallet1Instance2(pallet1::Call::call2 { a: true, b: 1, c: 2, d: 3, e: 4, f: true, authorize_refund: call_2_auth_weight_refund, }), dispatch_success: true, call_weight: pallet1::CALL_2_WEIGHT, ext_weight: pallet1::CALL_2_AUTH_WEIGHT, actual_weight: pallet1::CALL_2_REFUND + pallet1::CALL_2_AUTH_WEIGHT - call_2_auth_weight_refund, }, Test { call: RuntimeCall::Pallet1Instance2(pallet1::Call::call3 { valid: true, some_gen: 1 }), dispatch_success: false, call_weight: pallet1::CALL_3_WEIGHT, ext_weight: pallet1::CALL_3_AUTH_WEIGHT, actual_weight: pallet1::CALL_3_REFUND + pallet1::CALL_3_AUTH_WEIGHT, }, Test { call: RuntimeCall::Pallet2(pallet2::Call::call1 {}), dispatch_success: true, call_weight: Weight::zero(), ext_weight: Weight::zero(), actual_weight: Weight::zero(), }, Test { call: RuntimeCall::Pallet3(pallet3::Call::call1 {}), dispatch_success: true, call_weight: pallet3::CALL_1_WEIGHT, ext_weight: pallet3::CALL_1_AUTH_WEIGHT, actual_weight: pallet3::CALL_1_WEIGHT + pallet3::CALL_1_AUTH_WEIGHT, }, #[cfg(feature = "frame-feature-testing")] Test { call: RuntimeCall::Pallet1Instance3(pallet1::Call::call4 {}), dispatch_success: true, call_weight: pallet1::CALL_4_WEIGHT, ext_weight: pallet1::CALL_4_AUTH_WEIGHT, actual_weight: pallet1::CALL_4_WEIGHT + pallet1::CALL_4_AUTH_WEIGHT, }, ]; for (index, test) in tests.into_iter().enumerate() { let Test { call, dispatch_success, call_weight, ext_weight, actual_weight } = test; println!("Running test {}", index); new_test_ext().execute_with(|| { let tx_ext = pezframe_system::AuthorizeCall::::new(); let tx = UncheckedExtrinsic::new_transaction(call, tx_ext); let info = tx.get_dispatch_info(); let len = tx.using_encoded(|e| e.len()); let checked = Checkable::check(tx, &pezframe_system::ChainContext::::default()) .expect("Transaction is general so signature is good"); checked .validate::(TransactionSource::External, &info, len) .expect("call1 is always valid"); let dispatch_result = checked.apply::(&info, len).expect("Transaction is valid"); assert_eq!(dispatch_result.is_ok(), dispatch_success); let post_info = dispatch_result.unwrap_or_else(|e| e.post_info); assert_eq!(info.call_weight, call_weight); assert_eq!(info.extension_weight, ext_weight); assert_eq!(post_info.actual_weight, Some(actual_weight)); }); } } #[test] fn call_validity() { struct Test { call: RuntimeCall, validate_res: TransactionValidity, } let tests = vec![ Test { call: RuntimeCall::Pallet1(pallet1::Call::call1 {}), validate_res: Ok(Default::default()), }, Test { call: RuntimeCall::Pallet1(pallet1::Call::call2 { a: true, b: 1, c: 2, d: 3, e: 4, f: true, authorize_refund: Weight::zero(), }), validate_res: Ok(ValidTransaction { priority: 1, requires: vec![2u8.encode()], provides: vec![3u8.encode()], longevity: 4, propagate: true, }), }, Test { call: RuntimeCall::Pallet1(pallet1::Call::call2 { a: false, b: 1, c: 2, d: 3, e: 4, f: true, authorize_refund: Weight::zero(), }), validate_res: Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), }, Test { call: RuntimeCall::Pallet1(pallet1::Call::call3 { valid: true, some_gen: 1 }), validate_res: Ok(Default::default()), }, Test { call: RuntimeCall::Pallet1(pallet1::Call::call3 { valid: false, some_gen: 1 }), validate_res: Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), }, ]; for (index, test) in tests.into_iter().enumerate() { let Test { call, validate_res } = test; println!("Running test {}", index); new_test_ext().execute_with(|| { let tx_ext = pezframe_system::AuthorizeCall::::new(); let tx = UncheckedExtrinsic::new_transaction(call, tx_ext); let info = tx.get_dispatch_info(); let len = tx.using_encoded(|e| e.len()); let checked = Checkable::check(tx, &pezframe_system::ChainContext::::default()) .expect("Transaction is general so signature is good"); let res = checked.validate::(TransactionSource::External, &info, len); assert_eq!(res, validate_res); }); } } #[test] fn signed_is_valid_but_dispatch_error() { new_test_ext().execute_with(|| { let call = RuntimeCall::Pallet1(pallet1::Call::call1 {}); let tx_ext = pezframe_system::AuthorizeCall::::new(); let tx = UncheckedExtrinsic::new_signed(call, 1u64, 1.into(), tx_ext); let info = tx.get_dispatch_info(); let len = tx.using_encoded(|e| e.len()); let checked = Checkable::check(tx, &pezframe_system::ChainContext::::default()) .expect("Signature is good"); checked .validate::(TransactionSource::External, &info, len) .expect("origin is signed, transaction is valid"); let dispatch_err = checked .apply::(&info, len) .expect("origin is signed, transaction is valid") .expect_err("origin is wrong for the dispatched call"); assert_eq!(dispatch_err.error, DispatchError::BadOrigin); }); } #[test] fn call_without_authorization() { use pezframe_support::traits::Authorize; new_test_ext().execute_with(|| { let call = RuntimeCall::Pallet4(pallet4::Call::call1 {}); // tests for trait implementation assert_eq!(call.weight_of_authorize(), Weight::zero()); assert_eq!(call.authorize(TransactionSource::External), None); assert_eq!(call.authorize(TransactionSource::InBlock), None); assert_eq!(call.authorize(TransactionSource::Local), None); // tests for transaction extension implementation let tx_ext = pezframe_system::AuthorizeCall::::new(); let tx = UncheckedExtrinsic::new_transaction(call, tx_ext); let info = tx.get_dispatch_info(); let len = tx.using_encoded(|e| e.len()); let checked = Checkable::check(tx, &pezframe_system::ChainContext::::default()) .expect("Transaction is general so signature is good"); let err = checked .validate::(TransactionSource::External, &info, len) .expect_err("Call is not authorized, transaction is invalid"); assert_eq!(err, TransactionValidityError::Invalid(InvalidTransaction::UnknownOrigin)); let err = checked .apply::(&info, len) .expect_err("Call is not authorized, transaction is invalid"); assert_eq!(err, TransactionValidityError::Invalid(InvalidTransaction::UnknownOrigin)); }); }