From ec90956da11ee4eef602ca2ae49365f0fbc38a9c Mon Sep 17 00:00:00 2001 From: gilescope Date: Mon, 16 Jan 2023 10:26:37 +0000 Subject: [PATCH 1/2] break out moch runtimes to separate files --- .../asset-tx-payment/src/lib.rs | 2 + .../asset-tx-payment/src/mock.rs | 213 ++++++++++++++++++ .../asset-tx-payment/src/tests.rs | 197 +--------------- 3 files changed, 220 insertions(+), 192 deletions(-) create mode 100644 frame/transaction-payment/asset-tx-payment/src/mock.rs diff --git a/frame/transaction-payment/asset-tx-payment/src/lib.rs b/frame/transaction-payment/asset-tx-payment/src/lib.rs index 43cc1efa0858e..230b307317f8b 100644 --- a/frame/transaction-payment/asset-tx-payment/src/lib.rs +++ b/frame/transaction-payment/asset-tx-payment/src/lib.rs @@ -59,6 +59,8 @@ use sp_runtime::{ FixedPointOperand, }; +#[cfg(test)] +mod mock; #[cfg(test)] mod tests; diff --git a/frame/transaction-payment/asset-tx-payment/src/mock.rs b/frame/transaction-payment/asset-tx-payment/src/mock.rs new file mode 100644 index 0000000000000..ddb02f5a611ff --- /dev/null +++ b/frame/transaction-payment/asset-tx-payment/src/mock.rs @@ -0,0 +1,213 @@ +// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// 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 super::*; +use crate as pallet_asset_tx_payment; + +use codec; +use frame_support::{ + dispatch::DispatchClass, + pallet_prelude::*, + parameter_types, + traits::{AsEnsureOriginWithArg, ConstU32, ConstU64, ConstU8, FindAuthor}, + weights::{Weight, WeightToFee as WeightToFeeT}, + ConsensusEngineId, +}; +use frame_system as system; +use frame_system::EnsureRoot; +use pallet_transaction_payment::CurrencyAdapter; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, ConvertInto, IdentityLookup, SaturatedConversion}, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; +type Balance = u64; +type AccountId = u64; + +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + Assets: pallet_assets::{Pallet, Call, Storage, Event}, + Authorship: pallet_authorship::{Pallet, Call, Storage}, + AssetTxPayment: pallet_asset_tx_payment::{Pallet, Event}, + } +); + +parameter_types! { + pub(crate) static ExtrinsicBaseWeight: Weight = Weight::zero(); +} + +pub struct BlockWeights; +impl Get for BlockWeights { + fn get() -> frame_system::limits::BlockWeights { + frame_system::limits::BlockWeights::builder() + .base_block(Weight::zero()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); + }) + .for_class(DispatchClass::non_mandatory(), |weights| { + weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); + }) + .build_or_panic() + } +} + +parameter_types! { + pub static WeightToFee: u64 = 1; + pub static TransactionByteFee: u64 = 1; +} + +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 10; +} + +impl pallet_balances::Config for Runtime { + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<10>; + type AccountStore = System; + type MaxLocks = (); + type WeightInfo = (); + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; +} + +impl WeightToFeeT for WeightToFee { + type Balance = u64; + + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow())) + } +} + +impl WeightToFeeT for TransactionByteFee { + type Balance = u64; + + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) + } +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = CurrencyAdapter; + type WeightToFee = WeightToFee; + type LengthToFee = TransactionByteFee; + type FeeMultiplierUpdate = (); + type OperationalFeeMultiplier = ConstU8<5>; +} + +type AssetId = u32; + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetIdParameter = codec::Compact; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; + type AssetDeposit = ConstU64<2>; + type AssetAccountDeposit = ConstU64<2>; + type MetadataDepositBase = ConstU64<0>; + type MetadataDepositPerByte = ConstU64<0>; + type ApprovalDeposit = ConstU64<0>; + type StringLimit = ConstU32<20>; + type Freezer = (); + type Extra = (); + type CallbackHandle = (); + type WeightInfo = (); + type RemoveItemsLimit = ConstU32<1000>; + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = (); + } +} + +pub struct HardcodedAuthor; +pub(crate) const BLOCK_AUTHOR: AccountId = 1234; +impl FindAuthor for HardcodedAuthor { + fn find_author<'a, I>(_: I) -> Option + where + I: 'a + IntoIterator, + { + Some(BLOCK_AUTHOR) + } +} + +impl pallet_authorship::Config for Runtime { + type FindAuthor = HardcodedAuthor; + type UncleGenerations = (); + type FilterUncle = (); + type EventHandler = (); +} + +pub struct CreditToBlockAuthor; +impl HandleCredit for CreditToBlockAuthor { + fn handle_credit(credit: CreditOf) { + if let Some(author) = pallet_authorship::Pallet::::author() { + // What to do in case paying the author fails (e.g. because `fee < min_balance`) + // default: drop the result which will trigger the `OnDrop` of the imbalance. + let _ = >::resolve(&author, credit); + } + } +} + +impl Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Fungibles = Assets; + type OnChargeAssetTransaction = FungiblesAdapter< + pallet_assets::BalanceToAssetBalance, + CreditToBlockAuthor, + >; +} diff --git a/frame/transaction-payment/asset-tx-payment/src/tests.rs b/frame/transaction-payment/asset-tx-payment/src/tests.rs index b79b87a93c7a4..f76293f456497 100644 --- a/frame/transaction-payment/asset-tx-payment/src/tests.rs +++ b/frame/transaction-payment/asset-tx-payment/src/tests.rs @@ -14,209 +14,22 @@ // limitations under the License. use super::*; -use crate as pallet_asset_tx_payment; -use codec; use frame_support::{ assert_ok, - dispatch::{DispatchClass, DispatchInfo, PostDispatchInfo}, + dispatch::{DispatchInfo, PostDispatchInfo}, pallet_prelude::*, - parameter_types, - traits::{fungibles::Mutate, AsEnsureOriginWithArg, ConstU32, ConstU64, ConstU8, FindAuthor}, - weights::{Weight, WeightToFee as WeightToFeeT}, - ConsensusEngineId, + traits::fungibles::Mutate, + weights::Weight, }; use frame_system as system; -use frame_system::EnsureRoot; +use mock::{ExtrinsicBaseWeight, *}; use pallet_balances::Call as BalancesCall; -use pallet_transaction_payment::CurrencyAdapter; -use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, ConvertInto, IdentityLookup, SaturatedConversion, StaticLookup}, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; -type Balance = u64; -type AccountId = u64; - -frame_support::construct_runtime!( - pub enum Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, - Assets: pallet_assets::{Pallet, Call, Storage, Event}, - Authorship: pallet_authorship::{Pallet, Call, Storage}, - AssetTxPayment: pallet_asset_tx_payment::{Pallet, Event}, - } -); +use sp_runtime::traits::StaticLookup; const CALL: &::RuntimeCall = &RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); -parameter_types! { - static ExtrinsicBaseWeight: Weight = Weight::zero(); -} - -pub struct BlockWeights; -impl Get for BlockWeights { - fn get() -> frame_system::limits::BlockWeights { - frame_system::limits::BlockWeights::builder() - .base_block(Weight::zero()) - .for_class(DispatchClass::all(), |weights| { - weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); - }) - .for_class(DispatchClass::non_mandatory(), |weights| { - weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); - }) - .build_or_panic() - } -} - -parameter_types! { - pub static WeightToFee: u64 = 1; - pub static TransactionByteFee: u64 = 1; -} - -impl frame_system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; -} - -parameter_types! { - pub const ExistentialDeposit: u64 = 10; -} - -impl pallet_balances::Config for Runtime { - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU64<10>; - type AccountStore = System; - type MaxLocks = (); - type WeightInfo = (); - type MaxReserves = ConstU32<50>; - type ReserveIdentifier = [u8; 8]; -} - -impl WeightToFeeT for WeightToFee { - type Balance = u64; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow())) - } -} - -impl WeightToFeeT for TransactionByteFee { - type Balance = u64; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) - } -} - -impl pallet_transaction_payment::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = CurrencyAdapter; - type WeightToFee = WeightToFee; - type LengthToFee = TransactionByteFee; - type FeeMultiplierUpdate = (); - type OperationalFeeMultiplier = ConstU8<5>; -} - -type AssetId = u32; - -impl pallet_assets::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Balance = Balance; - type AssetId = AssetId; - type AssetIdParameter = codec::Compact; - type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = EnsureRoot; - type AssetDeposit = ConstU64<2>; - type AssetAccountDeposit = ConstU64<2>; - type MetadataDepositBase = ConstU64<0>; - type MetadataDepositPerByte = ConstU64<0>; - type ApprovalDeposit = ConstU64<0>; - type StringLimit = ConstU32<20>; - type Freezer = (); - type Extra = (); - type CallbackHandle = (); - type WeightInfo = (); - type RemoveItemsLimit = ConstU32<1000>; - pallet_assets::runtime_benchmarks_enabled! { - type BenchmarkHelper = (); - } -} - -pub struct HardcodedAuthor; -const BLOCK_AUTHOR: AccountId = 1234; -impl FindAuthor for HardcodedAuthor { - fn find_author<'a, I>(_: I) -> Option - where - I: 'a + IntoIterator, - { - Some(BLOCK_AUTHOR) - } -} - -impl pallet_authorship::Config for Runtime { - type FindAuthor = HardcodedAuthor; - type UncleGenerations = (); - type FilterUncle = (); - type EventHandler = (); -} - -pub struct CreditToBlockAuthor; -impl HandleCredit for CreditToBlockAuthor { - fn handle_credit(credit: CreditOf) { - if let Some(author) = pallet_authorship::Pallet::::author() { - // What to do in case paying the author fails (e.g. because `fee < min_balance`) - // default: drop the result which will trigger the `OnDrop` of the imbalance. - let _ = >::resolve(&author, credit); - } - } -} - -impl Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Fungibles = Assets; - type OnChargeAssetTransaction = FungiblesAdapter< - pallet_assets::BalanceToAssetBalance, - CreditToBlockAuthor, - >; -} - pub struct ExtBuilder { balance_factor: u64, base_weight: Weight, From f51c2a3566a3f98fd9cb3bcc7136b035f7dc0239 Mon Sep 17 00:00:00 2001 From: gilescope Date: Mon, 16 Jan 2023 10:42:42 +0000 Subject: [PATCH 2/2] tranaction-payment: break out tests & mock runtime to separate files --- frame/transaction-payment/src/lib.rs | 961 +------------------------ frame/transaction-payment/src/mock.rs | 162 +++++ frame/transaction-payment/src/tests.rs | 836 +++++++++++++++++++++ 3 files changed, 1003 insertions(+), 956 deletions(-) create mode 100644 frame/transaction-payment/src/mock.rs create mode 100644 frame/transaction-payment/src/tests.rs diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index ce747fa6bd85c..13adbf89c4270 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -70,6 +70,11 @@ use frame_support::{ weights::{Weight, WeightToFee}, }; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + mod payment; mod types; @@ -847,959 +852,3 @@ where Self::compute_actual_fee(len, &info, &post_info, Zero::zero()) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate as pallet_transaction_payment; - - use codec::Encode; - - use sp_core::H256; - use sp_runtime::{ - testing::{Header, TestXt}, - traits::{BlakeTwo256, IdentityLookup, One}, - transaction_validity::InvalidTransaction, - }; - - use frame_support::{ - assert_noop, assert_ok, - dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, - parameter_types, - traits::{ConstU32, ConstU64, Currency, GenesisBuild, Imbalance, OnUnbalanced}, - weights::{Weight, WeightToFee as WeightToFeeT}, - }; - use frame_system as system; - use pallet_balances::Call as BalancesCall; - - type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; - type Block = frame_system::mocking::MockBlock; - - frame_support::construct_runtime!( - pub enum Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, - } - ); - - const CALL: &::RuntimeCall = - &RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); - - parameter_types! { - static ExtrinsicBaseWeight: Weight = Weight::zero(); - } - - pub struct BlockWeights; - impl Get for BlockWeights { - fn get() -> frame_system::limits::BlockWeights { - frame_system::limits::BlockWeights::builder() - .base_block(Weight::zero()) - .for_class(DispatchClass::all(), |weights| { - weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); - }) - .for_class(DispatchClass::non_mandatory(), |weights| { - weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); - }) - .build_or_panic() - } - } - - parameter_types! { - pub static WeightToFee: u64 = 1; - pub static TransactionByteFee: u64 = 1; - pub static OperationalFeeMultiplier: u8 = 5; - } - - impl frame_system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; - } - - impl pallet_balances::Config for Runtime { - type Balance = u64; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU64<1>; - type AccountStore = System; - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); - } - - impl WeightToFeeT for WeightToFee { - type Balance = u64; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow())) - } - } - - impl WeightToFeeT for TransactionByteFee { - type Balance = u64; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) - } - } - - parameter_types! { - static TipUnbalancedAmount: u64 = 0; - static FeeUnbalancedAmount: u64 = 0; - } - - pub struct DealWithFees; - impl OnUnbalanced> for DealWithFees { - fn on_unbalanceds( - mut fees_then_tips: impl Iterator>, - ) { - if let Some(fees) = fees_then_tips.next() { - FeeUnbalancedAmount::mutate(|a| *a += fees.peek()); - if let Some(tips) = fees_then_tips.next() { - TipUnbalancedAmount::mutate(|a| *a += tips.peek()); - } - } - } - } - - impl Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = CurrencyAdapter; - type OperationalFeeMultiplier = OperationalFeeMultiplier; - type WeightToFee = WeightToFee; - type LengthToFee = TransactionByteFee; - type FeeMultiplierUpdate = (); - } - - pub struct ExtBuilder { - balance_factor: u64, - base_weight: Weight, - byte_fee: u64, - weight_to_fee: u64, - initial_multiplier: Option, - } - - impl Default for ExtBuilder { - fn default() -> Self { - Self { - balance_factor: 1, - base_weight: Weight::zero(), - byte_fee: 1, - weight_to_fee: 1, - initial_multiplier: None, - } - } - } - - impl ExtBuilder { - pub fn base_weight(mut self, base_weight: Weight) -> Self { - self.base_weight = base_weight; - self - } - pub fn byte_fee(mut self, byte_fee: u64) -> Self { - self.byte_fee = byte_fee; - self - } - pub fn weight_fee(mut self, weight_to_fee: u64) -> Self { - self.weight_to_fee = weight_to_fee; - self - } - pub fn balance_factor(mut self, factor: u64) -> Self { - self.balance_factor = factor; - self - } - pub fn with_initial_multiplier(mut self, multiplier: Multiplier) -> Self { - self.initial_multiplier = Some(multiplier); - self - } - fn set_constants(&self) { - ExtrinsicBaseWeight::mutate(|v| *v = self.base_weight); - TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.byte_fee); - WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); - } - pub fn build(self) -> sp_io::TestExternalities { - self.set_constants(); - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: if self.balance_factor > 0 { - vec![ - (1, 10 * self.balance_factor), - (2, 20 * self.balance_factor), - (3, 30 * self.balance_factor), - (4, 40 * self.balance_factor), - (5, 50 * self.balance_factor), - (6, 60 * self.balance_factor), - ] - } else { - vec![] - }, - } - .assimilate_storage(&mut t) - .unwrap(); - - if let Some(multiplier) = self.initial_multiplier { - let genesis = pallet::GenesisConfig { multiplier }; - GenesisBuild::::assimilate_storage(&genesis, &mut t).unwrap(); - } - - t.into() - } - } - - /// create a transaction info struct from weight. Handy to avoid building the whole struct. - pub fn info_from_weight(w: Weight) -> DispatchInfo { - // pays_fee: Pays::Yes -- class: DispatchClass::Normal - DispatchInfo { weight: w, ..Default::default() } - } - - fn post_info_from_weight(w: Weight) -> PostDispatchInfo { - PostDispatchInfo { actual_weight: Some(w), pays_fee: Default::default() } - } - - fn post_info_from_pays(p: Pays) -> PostDispatchInfo { - PostDispatchInfo { actual_weight: None, pays_fee: p } - } - - fn default_post_info() -> PostDispatchInfo { - PostDispatchInfo { actual_weight: None, pays_fee: Default::default() } - } - - #[test] - fn signed_extension_transaction_payment_work() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - let len = 10; - let pre = ChargeTransactionPayment::::from(0) - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(5)), len) - .unwrap(); - assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(5)), - &default_post_info(), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); - assert_eq!(FeeUnbalancedAmount::get(), 5 + 5 + 10); - assert_eq!(TipUnbalancedAmount::get(), 0); - - FeeUnbalancedAmount::mutate(|a| *a = 0); - - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) - .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 50 - 5); - assert_eq!(FeeUnbalancedAmount::get(), 5 + 10 + 50); - assert_eq!(TipUnbalancedAmount::get(), 5); - }); - } - - #[test] - fn signed_extension_transaction_payment_multiplied_refund_works() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - let len = 10; - >::put(Multiplier::saturating_from_rational(3, 2)); - - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) - .unwrap(); - // 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 150 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), - len, - &Ok(()) - )); - // 75 (3/2 of the returned 50 units of weight) is refunded - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 75 - 5); - }); - } - - #[test] - fn signed_extension_transaction_payment_is_bounded() { - ExtBuilder::default().balance_factor(1000).byte_fee(0).build().execute_with(|| { - // maximum weight possible - assert_ok!(ChargeTransactionPayment::::from(0).pre_dispatch( - &1, - CALL, - &info_from_weight(Weight::MAX), - 10 - )); - // fee will be proportional to what is the actual maximum weight in the runtime. - assert_eq!( - Balances::free_balance(&1), - (10000 - - ::BlockWeights::get().max_block.ref_time()) as u64 - ); - }); - } - - #[test] - fn signed_extension_allows_free_transactions() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .balance_factor(0) - .build() - .execute_with(|| { - // 1 ain't have a penny. - assert_eq!(Balances::free_balance(1), 0); - - let len = 100; - - // This is a completely free (and thus wholly insecure/DoS-ridden) transaction. - let operational_transaction = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::No, - }; - assert_ok!(ChargeTransactionPayment::::from(0).validate( - &1, - CALL, - &operational_transaction, - len - )); - - // like a InsecureFreeNormal - let free_transaction = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }; - assert_noop!( - ChargeTransactionPayment::::from(0).validate( - &1, - CALL, - &free_transaction, - len - ), - TransactionValidityError::Invalid(InvalidTransaction::Payment), - ); - }); - } - - #[test] - fn signed_ext_length_fee_is_also_updated_per_congestion() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(5)) - .balance_factor(10) - .build() - .execute_with(|| { - // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); - let len = 10; - - assert_ok!( - ChargeTransactionPayment::::from(10) // tipped - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(3)), len) - ); - assert_eq!( - Balances::free_balance(1), - 100 // original - - 10 // tip - - 5 // base - - 10 // len - - (3 * 3 / 2) // adjusted weight - ); - }) - } - - #[test] - fn query_info_and_fee_details_works() { - let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); - let origin = 111111; - let extra = (); - let xt = TestXt::new(call.clone(), Some((origin, extra))); - let info = xt.get_dispatch_info(); - let ext = xt.encode(); - let len = ext.len() as u32; - - let unsigned_xt = TestXt::<_, ()>::new(call, None); - let unsigned_xt_info = unsigned_xt.get_dispatch_info(); - - ExtBuilder::default() - .base_weight(Weight::from_ref_time(5)) - .weight_fee(2) - .build() - .execute_with(|| { - // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); - - assert_eq!( - TransactionPayment::query_info(xt.clone(), len), - RuntimeDispatchInfo { - weight: info.weight, - class: info.class, - partial_fee: 5 * 2 /* base * weight_fee */ - + len as u64 /* len * 1 */ - + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ - }, - ); - - assert_eq!( - TransactionPayment::query_info(unsigned_xt.clone(), len), - RuntimeDispatchInfo { - weight: unsigned_xt_info.weight, - class: unsigned_xt_info.class, - partial_fee: 0, - }, - ); - - assert_eq!( - TransactionPayment::query_fee_details(xt, len), - FeeDetails { - inclusion_fee: Some(InclusionFee { - base_fee: 5 * 2, - len_fee: len as u64, - adjusted_weight_fee: info - .weight - .min(BlockWeights::get().max_block) - .ref_time() as u64 * 2 * 3 / 2 - }), - tip: 0, - }, - ); - - assert_eq!( - TransactionPayment::query_fee_details(unsigned_xt, len), - FeeDetails { inclusion_fee: None, tip: 0 }, - ); - }); - } - - #[test] - fn query_call_info_and_fee_details_works() { - let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); - let info = call.get_dispatch_info(); - let encoded_call = call.encode(); - let len = encoded_call.len() as u32; - - ExtBuilder::default() - .base_weight(Weight::from_ref_time(5)) - .weight_fee(2) - .build() - .execute_with(|| { - // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); - - assert_eq!( - TransactionPayment::query_call_info(call.clone(), len), - RuntimeDispatchInfo { - weight: info.weight, - class: info.class, - partial_fee: 5 * 2 /* base * weight_fee */ - + len as u64 /* len * 1 */ - + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ - }, - ); - - assert_eq!( - TransactionPayment::query_call_fee_details(call, len), - FeeDetails { - inclusion_fee: Some(InclusionFee { - base_fee: 5 * 2, /* base * weight_fee */ - len_fee: len as u64, /* len * 1 */ - adjusted_weight_fee: info - .weight - .min(BlockWeights::get().max_block) - .ref_time() as u64 * 2 * 3 / 2 /* weight * weight_fee * multipler */ - }), - tip: 0, - }, - ); - }); - } - - #[test] - fn compute_fee_works_without_multiplier() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .byte_fee(10) - .balance_factor(0) - .build() - .execute_with(|| { - // Next fee multiplier is zero - assert_eq!(>::get(), Multiplier::one()); - - // Tip only, no fees works - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::No, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 10), 10); - // No tip, only base fee works - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); - // Tip + base fee works - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 69), 169); - // Len (byte fee) + base fee works - assert_eq!(Pallet::::compute_fee(42, &dispatch_info, 0), 520); - // Weight fee + base fee works - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(1000), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 1100); - }); - } - - #[test] - fn compute_fee_works_with_multiplier() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .byte_fee(10) - .balance_factor(0) - .build() - .execute_with(|| { - // Add a next fee multiplier. Fees will be x3/2. - >::put(Multiplier::saturating_from_rational(3, 2)); - // Base fee is unaffected by multiplier - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); - - // Everything works together :) - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(123), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - // 123 weight, 456 length, 100 base - assert_eq!( - Pallet::::compute_fee(456, &dispatch_info, 789), - 100 + (3 * 123 / 2) + 4560 + 789, - ); - }); - } - - #[test] - fn compute_fee_works_with_negative_multiplier() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .byte_fee(10) - .balance_factor(0) - .build() - .execute_with(|| { - // Add a next fee multiplier. All fees will be x1/2. - >::put(Multiplier::saturating_from_rational(1, 2)); - - // Base fee is unaffected by multiplier. - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); - - // Everything works together. - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(123), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - // 123 weight, 456 length, 100 base - assert_eq!( - Pallet::::compute_fee(456, &dispatch_info, 789), - 100 + (123 / 2) + 4560 + 789, - ); - }); - } - - #[test] - fn compute_fee_does_not_overflow() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .byte_fee(10) - .balance_factor(0) - .build() - .execute_with(|| { - // Overflow is handled - let dispatch_info = DispatchInfo { - weight: Weight::MAX, - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!( - Pallet::::compute_fee(u32::MAX, &dispatch_info, u64::MAX), - u64::MAX - ); - }); - } - - #[test] - fn refund_does_not_recreate_account() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - // So events are emitted - System::set_block_number(10); - let len = 10; - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) - .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - // kill the account between pre and post dispatch - assert_ok!(Balances::transfer(Some(2).into(), 3, Balances::free_balance(2))); - assert_eq!(Balances::free_balance(2), 0); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(2), 0); - // Transfer Event - System::assert_has_event(RuntimeEvent::Balances( - pallet_balances::Event::Transfer { from: 2, to: 3, amount: 80 }, - )); - // Killed Event - System::assert_has_event(RuntimeEvent::System(system::Event::KilledAccount { - account: 2, - })); - }); - } - - #[test] - fn actual_weight_higher_than_max_refunds_nothing() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - let len = 10; - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) - .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(101)), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - }); - } - - #[test] - fn zero_transfer_on_free_transaction() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - // So events are emitted - System::set_block_number(10); - let len = 10; - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(100), - pays_fee: Pays::No, - class: DispatchClass::Normal, - }; - let user = 69; - let pre = ChargeTransactionPayment::::from(0) - .pre_dispatch(&user, CALL, &dispatch_info, len) - .unwrap(); - assert_eq!(Balances::total_balance(&user), 0); - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &dispatch_info, - &default_post_info(), - len, - &Ok(()) - )); - assert_eq!(Balances::total_balance(&user), 0); - // TransactionFeePaid Event - System::assert_has_event(RuntimeEvent::TransactionPayment( - pallet_transaction_payment::Event::TransactionFeePaid { - who: user, - actual_fee: 0, - tip: 0, - }, - )); - }); - } - - #[test] - fn refund_consistent_with_actual_weight() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(7)) - .build() - .execute_with(|| { - let info = info_from_weight(Weight::from_ref_time(100)); - let post_info = post_info_from_weight(Weight::from_ref_time(33)); - let prev_balance = Balances::free_balance(2); - let len = 10; - let tip = 5; - - >::put(Multiplier::saturating_from_rational(5, 4)); - - let pre = ChargeTransactionPayment::::from(tip) - .pre_dispatch(&2, CALL, &info, len) - .unwrap(); - - ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info, - &post_info, - len, - &Ok(()), - ) - .unwrap(); - - let refund_based_fee = prev_balance - Balances::free_balance(2); - let actual_fee = - Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); - - // 33 weight, 10 length, 7 base, 5 tip - assert_eq!(actual_fee, 7 + 10 + (33 * 5 / 4) + 5); - assert_eq!(refund_based_fee, actual_fee); - }); - } - - #[test] - fn should_alter_operational_priority() { - let tip = 5; - let len = 10; - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let normal = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - - assert_eq!(priority, 60); - - let priority = ChargeTransactionPayment::(2 * tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - - assert_eq!(priority, 110); - }); - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let op = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; - assert_eq!(priority, 5810); - - let priority = ChargeTransactionPayment::(2 * tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; - assert_eq!(priority, 6110); - }); - } - - #[test] - fn no_tip_has_some_priority() { - let tip = 0; - let len = 10; - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let normal = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - - assert_eq!(priority, 10); - }); - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let op = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; - assert_eq!(priority, 5510); - }); - } - - #[test] - fn higher_tip_have_higher_priority() { - let get_priorities = |tip: u64| { - let mut priority1 = 0; - let mut priority2 = 0; - let len = 10; - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let normal = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }; - priority1 = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - }); - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let op = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - priority2 = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; - }); - - (priority1, priority2) - }; - - let mut prev_priorities = get_priorities(0); - - for tip in 1..3 { - let priorities = get_priorities(tip); - assert!(prev_priorities.0 < priorities.0); - assert!(prev_priorities.1 < priorities.1); - prev_priorities = priorities; - } - } - - #[test] - fn post_info_can_change_pays_fee() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(7)) - .build() - .execute_with(|| { - let info = info_from_weight(Weight::from_ref_time(100)); - let post_info = post_info_from_pays(Pays::No); - let prev_balance = Balances::free_balance(2); - let len = 10; - let tip = 5; - - >::put(Multiplier::saturating_from_rational(5, 4)); - - let pre = ChargeTransactionPayment::::from(tip) - .pre_dispatch(&2, CALL, &info, len) - .unwrap(); - - ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info, - &post_info, - len, - &Ok(()), - ) - .unwrap(); - - let refund_based_fee = prev_balance - Balances::free_balance(2); - let actual_fee = - Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); - - // Only 5 tip is paid - assert_eq!(actual_fee, 5); - assert_eq!(refund_based_fee, actual_fee); - }); - } - - #[test] - fn genesis_config_works() { - ExtBuilder::default() - .with_initial_multiplier(Multiplier::from_u32(100)) - .build() - .execute_with(|| { - assert_eq!( - >::get(), - Multiplier::saturating_from_integer(100) - ); - }); - } - - #[test] - fn genesis_default_works() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(>::get(), Multiplier::saturating_from_integer(1)); - }); - } -} diff --git a/frame/transaction-payment/src/mock.rs b/frame/transaction-payment/src/mock.rs new file mode 100644 index 0000000000000..e214458b3766e --- /dev/null +++ b/frame/transaction-payment/src/mock.rs @@ -0,0 +1,162 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// 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 super::*; +use crate as pallet_transaction_payment; + +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, +}; + +use frame_support::{ + dispatch::DispatchClass, + parameter_types, + traits::{ConstU32, ConstU64, Imbalance, OnUnbalanced}, + weights::{Weight, WeightToFee as WeightToFeeT}, +}; +use frame_system as system; +use pallet_balances::Call as BalancesCall; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + } +); + +pub(crate) const CALL: &::RuntimeCall = + &RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + +parameter_types! { + pub(crate) static ExtrinsicBaseWeight: Weight = Weight::zero(); +} + +pub struct BlockWeights; +impl Get for BlockWeights { + fn get() -> frame_system::limits::BlockWeights { + frame_system::limits::BlockWeights::builder() + .base_block(Weight::zero()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); + }) + .for_class(DispatchClass::non_mandatory(), |weights| { + weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); + }) + .build_or_panic() + } +} + +parameter_types! { + pub static WeightToFee: u64 = 1; + pub static TransactionByteFee: u64 = 1; + pub static OperationalFeeMultiplier: u8 = 5; +} + +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_balances::Config for Runtime { + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); +} + +impl WeightToFeeT for WeightToFee { + type Balance = u64; + + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow())) + } +} + +impl WeightToFeeT for TransactionByteFee { + type Balance = u64; + + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) + } +} + +parameter_types! { + pub(crate) static TipUnbalancedAmount: u64 = 0; + pub(crate) static FeeUnbalancedAmount: u64 = 0; +} + +pub struct DealWithFees; +impl OnUnbalanced> for DealWithFees { + fn on_unbalanceds( + mut fees_then_tips: impl Iterator>, + ) { + if let Some(fees) = fees_then_tips.next() { + FeeUnbalancedAmount::mutate(|a| *a += fees.peek()); + if let Some(tips) = fees_then_tips.next() { + TipUnbalancedAmount::mutate(|a| *a += tips.peek()); + } + } + } +} + +impl Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = CurrencyAdapter; + type OperationalFeeMultiplier = OperationalFeeMultiplier; + type WeightToFee = WeightToFee; + type LengthToFee = TransactionByteFee; + type FeeMultiplierUpdate = (); +} diff --git a/frame/transaction-payment/src/tests.rs b/frame/transaction-payment/src/tests.rs new file mode 100644 index 0000000000000..ee54c1130e699 --- /dev/null +++ b/frame/transaction-payment/src/tests.rs @@ -0,0 +1,836 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// 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 super::*; +use crate as pallet_transaction_payment; + +use codec::Encode; + +use sp_runtime::{testing::TestXt, traits::One, transaction_validity::InvalidTransaction}; + +use frame_support::{ + assert_noop, assert_ok, + dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, + traits::{Currency, GenesisBuild}, + weights::Weight, +}; +use frame_system as system; +use mock::*; +use pallet_balances::Call as BalancesCall; + +pub struct ExtBuilder { + balance_factor: u64, + base_weight: Weight, + byte_fee: u64, + weight_to_fee: u64, + initial_multiplier: Option, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { + balance_factor: 1, + base_weight: Weight::zero(), + byte_fee: 1, + weight_to_fee: 1, + initial_multiplier: None, + } + } +} + +impl ExtBuilder { + pub fn base_weight(mut self, base_weight: Weight) -> Self { + self.base_weight = base_weight; + self + } + pub fn byte_fee(mut self, byte_fee: u64) -> Self { + self.byte_fee = byte_fee; + self + } + pub fn weight_fee(mut self, weight_to_fee: u64) -> Self { + self.weight_to_fee = weight_to_fee; + self + } + pub fn balance_factor(mut self, factor: u64) -> Self { + self.balance_factor = factor; + self + } + pub fn with_initial_multiplier(mut self, multiplier: Multiplier) -> Self { + self.initial_multiplier = Some(multiplier); + self + } + fn set_constants(&self) { + ExtrinsicBaseWeight::mutate(|v| *v = self.base_weight); + TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.byte_fee); + WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); + } + pub fn build(self) -> sp_io::TestExternalities { + self.set_constants(); + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { + balances: if self.balance_factor > 0 { + vec![ + (1, 10 * self.balance_factor), + (2, 20 * self.balance_factor), + (3, 30 * self.balance_factor), + (4, 40 * self.balance_factor), + (5, 50 * self.balance_factor), + (6, 60 * self.balance_factor), + ] + } else { + vec![] + }, + } + .assimilate_storage(&mut t) + .unwrap(); + + if let Some(multiplier) = self.initial_multiplier { + let genesis = pallet::GenesisConfig { multiplier }; + GenesisBuild::::assimilate_storage(&genesis, &mut t).unwrap(); + } + + t.into() + } +} + +/// create a transaction info struct from weight. Handy to avoid building the whole struct. +pub fn info_from_weight(w: Weight) -> DispatchInfo { + // pays_fee: Pays::Yes -- class: DispatchClass::Normal + DispatchInfo { weight: w, ..Default::default() } +} + +fn post_info_from_weight(w: Weight) -> PostDispatchInfo { + PostDispatchInfo { actual_weight: Some(w), pays_fee: Default::default() } +} + +fn post_info_from_pays(p: Pays) -> PostDispatchInfo { + PostDispatchInfo { actual_weight: None, pays_fee: p } +} + +fn default_post_info() -> PostDispatchInfo { + PostDispatchInfo { actual_weight: None, pays_fee: Default::default() } +} + +#[test] +fn signed_extension_transaction_payment_work() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + let len = 10; + let pre = ChargeTransactionPayment::::from(0) + .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(5)), len) + .unwrap(); + assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(5)), + &default_post_info(), + len, + &Ok(()) + )); + assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); + assert_eq!(FeeUnbalancedAmount::get(), 5 + 5 + 10); + assert_eq!(TipUnbalancedAmount::get(), 0); + + FeeUnbalancedAmount::mutate(|a| *a = 0); + + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .unwrap(); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(100)), + &post_info_from_weight(Weight::from_ref_time(50)), + len, + &Ok(()) + )); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 50 - 5); + assert_eq!(FeeUnbalancedAmount::get(), 5 + 10 + 50); + assert_eq!(TipUnbalancedAmount::get(), 5); + }); +} + +#[test] +fn signed_extension_transaction_payment_multiplied_refund_works() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + let len = 10; + >::put(Multiplier::saturating_from_rational(3, 2)); + + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .unwrap(); + // 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 150 - 5); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(100)), + &post_info_from_weight(Weight::from_ref_time(50)), + len, + &Ok(()) + )); + // 75 (3/2 of the returned 50 units of weight) is refunded + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 75 - 5); + }); +} + +#[test] +fn signed_extension_transaction_payment_is_bounded() { + ExtBuilder::default().balance_factor(1000).byte_fee(0).build().execute_with(|| { + // maximum weight possible + assert_ok!(ChargeTransactionPayment::::from(0).pre_dispatch( + &1, + CALL, + &info_from_weight(Weight::MAX), + 10 + )); + // fee will be proportional to what is the actual maximum weight in the runtime. + assert_eq!( + Balances::free_balance(&1), + (10000 - ::BlockWeights::get().max_block.ref_time()) + as u64 + ); + }); +} + +#[test] +fn signed_extension_allows_free_transactions() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .balance_factor(0) + .build() + .execute_with(|| { + // 1 ain't have a penny. + assert_eq!(Balances::free_balance(1), 0); + + let len = 100; + + // This is a completely free (and thus wholly insecure/DoS-ridden) transaction. + let operational_transaction = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::No, + }; + assert_ok!(ChargeTransactionPayment::::from(0).validate( + &1, + CALL, + &operational_transaction, + len + )); + + // like a InsecureFreeNormal + let free_transaction = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + assert_noop!( + ChargeTransactionPayment::::from(0).validate( + &1, + CALL, + &free_transaction, + len + ), + TransactionValidityError::Invalid(InvalidTransaction::Payment), + ); + }); +} + +#[test] +fn signed_ext_length_fee_is_also_updated_per_congestion() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(5)) + .balance_factor(10) + .build() + .execute_with(|| { + // all fees should be x1.5 + >::put(Multiplier::saturating_from_rational(3, 2)); + let len = 10; + + assert_ok!(ChargeTransactionPayment::::from(10) // tipped + .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(3)), len)); + assert_eq!( + Balances::free_balance(1), + 100 // original + - 10 // tip + - 5 // base + - 10 // len + - (3 * 3 / 2) // adjusted weight + ); + }) +} + +#[test] +fn query_info_and_fee_details_works() { + let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + let origin = 111111; + let extra = (); + let xt = TestXt::new(call.clone(), Some((origin, extra))); + let info = xt.get_dispatch_info(); + let ext = xt.encode(); + let len = ext.len() as u32; + + let unsigned_xt = TestXt::<_, ()>::new(call, None); + let unsigned_xt_info = unsigned_xt.get_dispatch_info(); + + ExtBuilder::default() + .base_weight(Weight::from_ref_time(5)) + .weight_fee(2) + .build() + .execute_with(|| { + // all fees should be x1.5 + >::put(Multiplier::saturating_from_rational(3, 2)); + + assert_eq!( + TransactionPayment::query_info(xt.clone(), len), + RuntimeDispatchInfo { + weight: info.weight, + class: info.class, + partial_fee: 5 * 2 /* base * weight_fee */ + + len as u64 /* len * 1 */ + + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ + }, + ); + + assert_eq!( + TransactionPayment::query_info(unsigned_xt.clone(), len), + RuntimeDispatchInfo { + weight: unsigned_xt_info.weight, + class: unsigned_xt_info.class, + partial_fee: 0, + }, + ); + + assert_eq!( + TransactionPayment::query_fee_details(xt, len), + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: 5 * 2, + len_fee: len as u64, + adjusted_weight_fee: info + .weight + .min(BlockWeights::get().max_block) + .ref_time() as u64 * 2 * 3 / 2 + }), + tip: 0, + }, + ); + + assert_eq!( + TransactionPayment::query_fee_details(unsigned_xt, len), + FeeDetails { inclusion_fee: None, tip: 0 }, + ); + }); +} + +#[test] +fn query_call_info_and_fee_details_works() { + let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + let info = call.get_dispatch_info(); + let encoded_call = call.encode(); + let len = encoded_call.len() as u32; + + ExtBuilder::default() + .base_weight(Weight::from_ref_time(5)) + .weight_fee(2) + .build() + .execute_with(|| { + // all fees should be x1.5 + >::put(Multiplier::saturating_from_rational(3, 2)); + + assert_eq!( + TransactionPayment::query_call_info(call.clone(), len), + RuntimeDispatchInfo { + weight: info.weight, + class: info.class, + partial_fee: 5 * 2 /* base * weight_fee */ + + len as u64 /* len * 1 */ + + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ + }, + ); + + assert_eq!( + TransactionPayment::query_call_fee_details(call, len), + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: 5 * 2, /* base * weight_fee */ + len_fee: len as u64, /* len * 1 */ + adjusted_weight_fee: info + .weight + .min(BlockWeights::get().max_block) + .ref_time() as u64 * 2 * 3 / 2 /* weight * weight_fee * multipler */ + }), + tip: 0, + }, + ); + }); +} + +#[test] +fn compute_fee_works_without_multiplier() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .byte_fee(10) + .balance_factor(0) + .build() + .execute_with(|| { + // Next fee multiplier is zero + assert_eq!(>::get(), Multiplier::one()); + + // Tip only, no fees works + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::No, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 10), 10); + // No tip, only base fee works + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); + // Tip + base fee works + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 69), 169); + // Len (byte fee) + base fee works + assert_eq!(Pallet::::compute_fee(42, &dispatch_info, 0), 520); + // Weight fee + base fee works + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(1000), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 1100); + }); +} + +#[test] +fn compute_fee_works_with_multiplier() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .byte_fee(10) + .balance_factor(0) + .build() + .execute_with(|| { + // Add a next fee multiplier. Fees will be x3/2. + >::put(Multiplier::saturating_from_rational(3, 2)); + // Base fee is unaffected by multiplier + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); + + // Everything works together :) + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(123), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + // 123 weight, 456 length, 100 base + assert_eq!( + Pallet::::compute_fee(456, &dispatch_info, 789), + 100 + (3 * 123 / 2) + 4560 + 789, + ); + }); +} + +#[test] +fn compute_fee_works_with_negative_multiplier() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .byte_fee(10) + .balance_factor(0) + .build() + .execute_with(|| { + // Add a next fee multiplier. All fees will be x1/2. + >::put(Multiplier::saturating_from_rational(1, 2)); + + // Base fee is unaffected by multiplier. + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); + + // Everything works together. + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(123), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + // 123 weight, 456 length, 100 base + assert_eq!( + Pallet::::compute_fee(456, &dispatch_info, 789), + 100 + (123 / 2) + 4560 + 789, + ); + }); +} + +#[test] +fn compute_fee_does_not_overflow() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .byte_fee(10) + .balance_factor(0) + .build() + .execute_with(|| { + // Overflow is handled + let dispatch_info = DispatchInfo { + weight: Weight::MAX, + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!( + Pallet::::compute_fee(u32::MAX, &dispatch_info, u64::MAX), + u64::MAX + ); + }); +} + +#[test] +fn refund_does_not_recreate_account() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + // So events are emitted + System::set_block_number(10); + let len = 10; + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .unwrap(); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + + // kill the account between pre and post dispatch + assert_ok!(Balances::transfer(Some(2).into(), 3, Balances::free_balance(2))); + assert_eq!(Balances::free_balance(2), 0); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(100)), + &post_info_from_weight(Weight::from_ref_time(50)), + len, + &Ok(()) + )); + assert_eq!(Balances::free_balance(2), 0); + // Transfer Event + System::assert_has_event(RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: 2, + to: 3, + amount: 80, + })); + // Killed Event + System::assert_has_event(RuntimeEvent::System(system::Event::KilledAccount { + account: 2, + })); + }); +} + +#[test] +fn actual_weight_higher_than_max_refunds_nothing() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + let len = 10; + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .unwrap(); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(100)), + &post_info_from_weight(Weight::from_ref_time(101)), + len, + &Ok(()) + )); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + }); +} + +#[test] +fn zero_transfer_on_free_transaction() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + // So events are emitted + System::set_block_number(10); + let len = 10; + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(100), + pays_fee: Pays::No, + class: DispatchClass::Normal, + }; + let user = 69; + let pre = ChargeTransactionPayment::::from(0) + .pre_dispatch(&user, CALL, &dispatch_info, len) + .unwrap(); + assert_eq!(Balances::total_balance(&user), 0); + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &dispatch_info, + &default_post_info(), + len, + &Ok(()) + )); + assert_eq!(Balances::total_balance(&user), 0); + // TransactionFeePaid Event + System::assert_has_event(RuntimeEvent::TransactionPayment( + pallet_transaction_payment::Event::TransactionFeePaid { + who: user, + actual_fee: 0, + tip: 0, + }, + )); + }); +} + +#[test] +fn refund_consistent_with_actual_weight() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(7)) + .build() + .execute_with(|| { + let info = info_from_weight(Weight::from_ref_time(100)); + let post_info = post_info_from_weight(Weight::from_ref_time(33)); + let prev_balance = Balances::free_balance(2); + let len = 10; + let tip = 5; + + >::put(Multiplier::saturating_from_rational(5, 4)); + + let pre = ChargeTransactionPayment::::from(tip) + .pre_dispatch(&2, CALL, &info, len) + .unwrap(); + + ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info, + &post_info, + len, + &Ok(()), + ) + .unwrap(); + + let refund_based_fee = prev_balance - Balances::free_balance(2); + let actual_fee = + Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); + + // 33 weight, 10 length, 7 base, 5 tip + assert_eq!(actual_fee, 7 + 10 + (33 * 5 / 4) + 5); + assert_eq!(refund_based_fee, actual_fee); + }); +} + +#[test] +fn should_alter_operational_priority() { + let tip = 5; + let len = 10; + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let normal = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &normal, len) + .unwrap() + .priority; + + assert_eq!(priority, 60); + + let priority = ChargeTransactionPayment::(2 * tip) + .validate(&2, CALL, &normal, len) + .unwrap() + .priority; + + assert_eq!(priority, 110); + }); + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let op = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &op, len) + .unwrap() + .priority; + assert_eq!(priority, 5810); + + let priority = ChargeTransactionPayment::(2 * tip) + .validate(&2, CALL, &op, len) + .unwrap() + .priority; + assert_eq!(priority, 6110); + }); +} + +#[test] +fn no_tip_has_some_priority() { + let tip = 0; + let len = 10; + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let normal = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &normal, len) + .unwrap() + .priority; + + assert_eq!(priority, 10); + }); + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let op = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &op, len) + .unwrap() + .priority; + assert_eq!(priority, 5510); + }); +} + +#[test] +fn higher_tip_have_higher_priority() { + let get_priorities = |tip: u64| { + let mut priority1 = 0; + let mut priority2 = 0; + let len = 10; + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let normal = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + priority1 = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &normal, len) + .unwrap() + .priority; + }); + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let op = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + priority2 = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &op, len) + .unwrap() + .priority; + }); + + (priority1, priority2) + }; + + let mut prev_priorities = get_priorities(0); + + for tip in 1..3 { + let priorities = get_priorities(tip); + assert!(prev_priorities.0 < priorities.0); + assert!(prev_priorities.1 < priorities.1); + prev_priorities = priorities; + } +} + +#[test] +fn post_info_can_change_pays_fee() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(7)) + .build() + .execute_with(|| { + let info = info_from_weight(Weight::from_ref_time(100)); + let post_info = post_info_from_pays(Pays::No); + let prev_balance = Balances::free_balance(2); + let len = 10; + let tip = 5; + + >::put(Multiplier::saturating_from_rational(5, 4)); + + let pre = ChargeTransactionPayment::::from(tip) + .pre_dispatch(&2, CALL, &info, len) + .unwrap(); + + ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info, + &post_info, + len, + &Ok(()), + ) + .unwrap(); + + let refund_based_fee = prev_balance - Balances::free_balance(2); + let actual_fee = + Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); + + // Only 5 tip is paid + assert_eq!(actual_fee, 5); + assert_eq!(refund_based_fee, actual_fee); + }); +} + +#[test] +fn genesis_config_works() { + ExtBuilder::default() + .with_initial_multiplier(Multiplier::from_u32(100)) + .build() + .execute_with(|| { + assert_eq!( + >::get(), + Multiplier::saturating_from_integer(100) + ); + }); +} + +#[test] +fn genesis_default_works() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(>::get(), Multiplier::saturating_from_integer(1)); + }); +}