mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-07 00:18:03 +00:00
970c5f94f2
* Initial skeleton for offchain phragmen * Basic compact encoding decoding for results * add compact files * Bring back Self::ensure_storage_upgraded(); * Make staking use compact stuff. * First seemingly working version of reduce, full of todos * Everything phragmen related works again. * Signing made easier, still issues. * Signing from offchain compile fine 😎 * make compact work with staked asssignment * Evaluation basics are in place. * Move reduce into crate. Document stuff * move reduce into no_std * Add files * Remove other std deps. Runtime compiles * Seemingly it is al stable; cycle implemented but not integrated. * Add fuzzing code. * Cleanup reduce a bit more. * a metric ton of tests for staking; wip 🔨 * Implement a lot more of the tests. * wip getting the unsigned stuff to work * A bit gleanup for unsigned debug * Clean and finalize compact code. * Document reduce. * Still problems with signing * We officaly duct taped the transaction submission stuff. 🤓 * Deadlock with keys again * Runtime builds * Unsigned test works 🙌 * Some cleanups * Make all the tests compile and stuff * Minor cleanup * fix more merge stuff * Most tests work again. * a very nasty bug in reduce * Fix all integrations * Fix more todos * Revamp everything and everything * Remove bogus test * Some review grumbles. * Some fixes * Fix doc test * loop for submission * Fix cli, keyring etc. * some cleanup * Fix staking tests again * fix per-things; bring patches from benchmarking * better score prediction * Add fuzzer, more patches. * Some fixes * More docs * Remove unused generics * Remove max-nominator footgun * Better fuzzer * Disable it ❌ * Bump. * Another round of self-review * Refactor a lot * More major fixes in perThing * Add new fuzz file * Update lock * fix fuzzing code. * Fix nominator retain test * Add slashing check * Update frame/staking/src/tests.rs Co-Authored-By: Joshy Orndorff <JoshOrndorff@users.noreply.github.com> * Some formatting nits * Review comments. * Fix cargo file * Almost all tests work again * Update frame/staking/src/tests.rs Co-Authored-By: thiolliere <gui.thiolliere@gmail.com> * Fix review comments * More review stuff * Some nits * Fix new staking / session / babe relation * Update primitives/phragmen/src/lib.rs Co-Authored-By: thiolliere <gui.thiolliere@gmail.com> * Update primitives/phragmen/src/lib.rs Co-Authored-By: thiolliere <gui.thiolliere@gmail.com> * Update primitives/phragmen/compact/src/lib.rs Co-Authored-By: thiolliere <gui.thiolliere@gmail.com> * Some doc updates to slashing * Fix derive * Remove imports * Remove unimplemented tests * nits * Remove dbg * Better fuzzing params * Remove unused pref map * Deferred Slashing/Offence for offchain Phragmen (#5151) * Some boilerplate * Add test * One more test * Review comments * Fix build * review comments * fix more * fix build * Some cleanups and self-reviews * More minor self reviews * Final nits * Some merge fixes. * opt comment * Fix build * Fix build again. * Update frame/staking/fuzz/fuzz_targets/submit_solution.rs Co-Authored-By: Gavin Wood <gavin@parity.io> * Update frame/staking/src/slashing.rs Co-Authored-By: Gavin Wood <gavin@parity.io> * Update frame/staking/src/offchain_election.rs Co-Authored-By: Gavin Wood <gavin@parity.io> * Fix review comments * fix test * === 🔑 Revamp without staking key. * final round of changes. * Fix cargo-deny * Update frame/staking/src/lib.rs Co-Authored-By: Gavin Wood <gavin@parity.io> Co-authored-by: Joshy Orndorff <JoshOrndorff@users.noreply.github.com> Co-authored-by: thiolliere <gui.thiolliere@gmail.com> Co-authored-by: Gavin Wood <gavin@parity.io>
671 lines
20 KiB
Rust
671 lines
20 KiB
Rust
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
|
// This file is part of Substrate.
|
|
|
|
// Substrate 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.
|
|
|
|
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//! # Transaction Payment Module
|
|
//!
|
|
//! This module provides the basic logic needed to pay the absolute minimum amount needed for a
|
|
//! transaction to be included. This includes:
|
|
//! - _weight fee_: A fee proportional to amount of weight a transaction consumes.
|
|
//! - _length fee_: A fee proportional to the encoded length of the transaction.
|
|
//! - _tip_: An optional tip. Tip increases the priority of the transaction, giving it a higher
|
|
//! chance to be included by the transaction queue.
|
|
//!
|
|
//! Additionally, this module allows one to configure:
|
|
//! - The mapping between one unit of weight to one unit of fee via [`WeightToFee`].
|
|
//! - A means of updating the fee for the next block, via defining a multiplier, based on the
|
|
//! final state of the chain at the end of the previous block. This can be configured via
|
|
//! [`FeeMultiplierUpdate`]
|
|
|
|
#![cfg_attr(not(feature = "std"), no_std)]
|
|
|
|
use sp_std::prelude::*;
|
|
use codec::{Encode, Decode};
|
|
use frame_support::{
|
|
decl_storage, decl_module,
|
|
traits::{Currency, Get, OnUnbalanced, ExistenceRequirement, WithdrawReason, Imbalance},
|
|
weights::{Weight, DispatchInfo, GetDispatchInfo},
|
|
};
|
|
use sp_runtime::{
|
|
Fixed64,
|
|
transaction_validity::{
|
|
TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError,
|
|
TransactionValidity,
|
|
},
|
|
traits::{Zero, Saturating, SignedExtension, SaturatedConversion, Convert},
|
|
};
|
|
use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
|
|
|
|
type Multiplier = Fixed64;
|
|
type BalanceOf<T> =
|
|
<<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
|
|
type NegativeImbalanceOf<T> =
|
|
<<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::NegativeImbalance;
|
|
|
|
pub trait Trait: frame_system::Trait {
|
|
/// The currency type in which fees will be paid.
|
|
type Currency: Currency<Self::AccountId> + Send + Sync;
|
|
|
|
/// Handler for the unbalanced reduction when taking transaction fees. This is either one or
|
|
/// two separate imbalances, the first is the transaction fee paid, the second is the tip paid,
|
|
/// if any.
|
|
type OnTransactionPayment: OnUnbalanced<NegativeImbalanceOf<Self>>;
|
|
|
|
/// The fee to be paid for making a transaction; the base.
|
|
type TransactionBaseFee: Get<BalanceOf<Self>>;
|
|
|
|
/// The fee to be paid for making a transaction; the per-byte portion.
|
|
type TransactionByteFee: Get<BalanceOf<Self>>;
|
|
|
|
/// Convert a weight value into a deductible fee based on the currency type.
|
|
type WeightToFee: Convert<Weight, BalanceOf<Self>>;
|
|
|
|
/// Update the multiplier of the next block, based on the previous block's weight.
|
|
type FeeMultiplierUpdate: Convert<Multiplier, Multiplier>;
|
|
}
|
|
|
|
decl_storage! {
|
|
trait Store for Module<T: Trait> as TransactionPayment {
|
|
pub NextFeeMultiplier get(fn next_fee_multiplier): Multiplier = Multiplier::from_parts(0);
|
|
}
|
|
}
|
|
|
|
decl_module! {
|
|
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
|
/// The fee to be paid for making a transaction; the base.
|
|
const TransactionBaseFee: BalanceOf<T> = T::TransactionBaseFee::get();
|
|
|
|
/// The fee to be paid for making a transaction; the per-byte portion.
|
|
const TransactionByteFee: BalanceOf<T> = T::TransactionByteFee::get();
|
|
|
|
fn on_finalize() {
|
|
NextFeeMultiplier::mutate(|fm| {
|
|
*fm = T::FeeMultiplierUpdate::convert(*fm)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Trait> Module<T> {
|
|
/// Query the data that we know about the fee of a given `call`.
|
|
///
|
|
/// As this module is not and cannot be aware of the internals of a signed extension, it only
|
|
/// interprets them as some encoded value and takes their length into account.
|
|
///
|
|
/// All dispatchables must be annotated with weight and will have some fee info. This function
|
|
/// always returns.
|
|
pub fn query_info<Extrinsic: GetDispatchInfo>(
|
|
unchecked_extrinsic: Extrinsic,
|
|
len: u32,
|
|
) -> RuntimeDispatchInfo<BalanceOf<T>>
|
|
where
|
|
T: Send + Sync,
|
|
BalanceOf<T>: Send + Sync,
|
|
{
|
|
// NOTE: we can actually make it understand `ChargeTransactionPayment`, but would be some
|
|
// hassle for sure. We have to make it aware of the index of `ChargeTransactionPayment` in
|
|
// `Extra`. Alternatively, we could actually execute the tx's per-dispatch and record the
|
|
// balance of the sender before and after the pipeline.. but this is way too much hassle for
|
|
// a very very little potential gain in the future.
|
|
let dispatch_info = <Extrinsic as GetDispatchInfo>::get_dispatch_info(&unchecked_extrinsic);
|
|
|
|
let partial_fee =
|
|
<ChargeTransactionPayment<T>>::compute_fee(len, dispatch_info, 0u32.into());
|
|
let DispatchInfo { weight, class, .. } = dispatch_info;
|
|
|
|
RuntimeDispatchInfo { weight, class, partial_fee }
|
|
}
|
|
}
|
|
|
|
/// Require the transactor pay for themselves and maybe include a tip to gain additional priority
|
|
/// in the queue.
|
|
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
|
|
pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(#[codec(compact)] BalanceOf<T>);
|
|
|
|
impl<T: Trait + Send + Sync> ChargeTransactionPayment<T> {
|
|
/// utility constructor. Used only in client/factory code.
|
|
pub fn from(fee: BalanceOf<T>) -> Self {
|
|
Self(fee)
|
|
}
|
|
|
|
/// Compute the final fee value for a particular transaction.
|
|
///
|
|
/// The final fee is composed of:
|
|
/// - _base_fee_: This is the minimum amount a user pays for a transaction.
|
|
/// - _len_fee_: This is the amount paid merely to pay for size of the transaction.
|
|
/// - _weight_fee_: This amount is computed based on the weight of the transaction. Unlike
|
|
/// size-fee, this is not input dependent and reflects the _complexity_ of the execution
|
|
/// and the time it consumes.
|
|
/// - _targeted_fee_adjustment_: This is a multiplier that can tune the final fee based on
|
|
/// the congestion of the network.
|
|
/// - (optional) _tip_: if included in the transaction, it will be added on top. Only signed
|
|
/// transactions can have a tip.
|
|
///
|
|
/// final_fee = base_fee + targeted_fee_adjustment(len_fee + weight_fee) + tip;
|
|
pub fn compute_fee(
|
|
len: u32,
|
|
info: <Self as SignedExtension>::DispatchInfo,
|
|
tip: BalanceOf<T>,
|
|
) -> BalanceOf<T>
|
|
where
|
|
BalanceOf<T>: Sync + Send,
|
|
{
|
|
if info.pays_fee {
|
|
let len = <BalanceOf<T>>::from(len);
|
|
let per_byte = T::TransactionByteFee::get();
|
|
let len_fee = per_byte.saturating_mul(len);
|
|
|
|
let weight_fee = {
|
|
// cap the weight to the maximum defined in runtime, otherwise it will be the
|
|
// `Bounded` maximum of its data type, which is not desired.
|
|
let capped_weight = info.weight
|
|
.min(<T as frame_system::Trait>::MaximumBlockWeight::get());
|
|
T::WeightToFee::convert(capped_weight)
|
|
};
|
|
|
|
// the adjustable part of the fee
|
|
let adjustable_fee = len_fee.saturating_add(weight_fee);
|
|
let targeted_fee_adjustment = NextFeeMultiplier::get();
|
|
// adjusted_fee = adjustable_fee + (adjustable_fee * targeted_fee_adjustment)
|
|
let adjusted_fee = targeted_fee_adjustment.saturated_multiply_accumulate(adjustable_fee);
|
|
|
|
let base_fee = T::TransactionBaseFee::get();
|
|
base_fee.saturating_add(adjusted_fee).saturating_add(tip)
|
|
} else {
|
|
tip
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Trait + Send + Sync> sp_std::fmt::Debug for ChargeTransactionPayment<T> {
|
|
#[cfg(feature = "std")]
|
|
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
|
write!(f, "ChargeTransactionPayment<{:?}>", self.0)
|
|
}
|
|
#[cfg(not(feature = "std"))]
|
|
fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<T: Trait + Send + Sync> SignedExtension for ChargeTransactionPayment<T>
|
|
where BalanceOf<T>: Send + Sync
|
|
{
|
|
const IDENTIFIER: &'static str = "ChargeTransactionPayment";
|
|
type AccountId = T::AccountId;
|
|
type Call = T::Call;
|
|
type AdditionalSigned = ();
|
|
type DispatchInfo = DispatchInfo;
|
|
type Pre = ();
|
|
fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) }
|
|
|
|
fn validate(
|
|
&self,
|
|
who: &Self::AccountId,
|
|
_call: &Self::Call,
|
|
info: Self::DispatchInfo,
|
|
len: usize,
|
|
) -> TransactionValidity {
|
|
// pay any fees.
|
|
let tip = self.0;
|
|
let fee = Self::compute_fee(len as u32, info, tip);
|
|
// Only mess with balances if fee is not zero.
|
|
if !fee.is_zero() {
|
|
let imbalance = match T::Currency::withdraw(
|
|
who,
|
|
fee,
|
|
if tip.is_zero() {
|
|
WithdrawReason::TransactionPayment.into()
|
|
} else {
|
|
WithdrawReason::TransactionPayment | WithdrawReason::Tip
|
|
},
|
|
ExistenceRequirement::KeepAlive,
|
|
) {
|
|
Ok(imbalance) => imbalance,
|
|
Err(_) => return InvalidTransaction::Payment.into(),
|
|
};
|
|
let imbalances = imbalance.split(tip);
|
|
T::OnTransactionPayment::on_unbalanceds(Some(imbalances.0).into_iter()
|
|
.chain(Some(imbalances.1)));
|
|
}
|
|
|
|
let mut r = ValidTransaction::default();
|
|
// NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which
|
|
// will be a bit more than setting the priority to tip. For now, this is enough.
|
|
r.priority = fee.saturated_into::<TransactionPriority>();
|
|
Ok(r)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use codec::Encode;
|
|
use frame_support::{
|
|
impl_outer_dispatch, impl_outer_origin, parameter_types,
|
|
weights::{DispatchClass, DispatchInfo, GetDispatchInfo, Weight},
|
|
};
|
|
use pallet_balances::Call as BalancesCall;
|
|
use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
|
|
use sp_core::H256;
|
|
use sp_runtime::{
|
|
testing::{Header, TestXt},
|
|
traits::{BlakeTwo256, IdentityLookup},
|
|
Perbill,
|
|
};
|
|
use std::cell::RefCell;
|
|
|
|
const CALL: &<Runtime as frame_system::Trait>::Call =
|
|
&Call::Balances(BalancesCall::transfer(2, 69));
|
|
|
|
impl_outer_dispatch! {
|
|
pub enum Call for Runtime where origin: Origin {
|
|
pallet_balances::Balances,
|
|
frame_system::System,
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
pub struct Runtime;
|
|
|
|
use frame_system as system;
|
|
impl_outer_origin!{
|
|
pub enum Origin for Runtime {}
|
|
}
|
|
|
|
parameter_types! {
|
|
pub const BlockHashCount: u64 = 250;
|
|
pub const MaximumBlockWeight: Weight = 1024;
|
|
pub const MaximumBlockLength: u32 = 2 * 1024;
|
|
pub const AvailableBlockRatio: Perbill = Perbill::one();
|
|
}
|
|
|
|
impl frame_system::Trait for Runtime {
|
|
type Origin = Origin;
|
|
type Index = u64;
|
|
type BlockNumber = u64;
|
|
type Call = Call;
|
|
type Hash = H256;
|
|
type Hashing = BlakeTwo256;
|
|
type AccountId = u64;
|
|
type Lookup = IdentityLookup<Self::AccountId>;
|
|
type Header = Header;
|
|
type Event = ();
|
|
type BlockHashCount = BlockHashCount;
|
|
type MaximumBlockWeight = MaximumBlockWeight;
|
|
type MaximumBlockLength = MaximumBlockLength;
|
|
type AvailableBlockRatio = AvailableBlockRatio;
|
|
type Version = ();
|
|
type ModuleToIndex = ();
|
|
type AccountData = pallet_balances::AccountData<u64>;
|
|
type OnNewAccount = ();
|
|
type OnKilledAccount = ();
|
|
}
|
|
|
|
parameter_types! {
|
|
pub const ExistentialDeposit: u64 = 1;
|
|
}
|
|
|
|
impl pallet_balances::Trait for Runtime {
|
|
type Balance = u64;
|
|
type Event = ();
|
|
type DustRemoval = ();
|
|
type ExistentialDeposit = ExistentialDeposit;
|
|
type AccountStore = System;
|
|
}
|
|
thread_local! {
|
|
static TRANSACTION_BASE_FEE: RefCell<u64> = RefCell::new(0);
|
|
static TRANSACTION_BYTE_FEE: RefCell<u64> = RefCell::new(1);
|
|
static WEIGHT_TO_FEE: RefCell<u64> = RefCell::new(1);
|
|
}
|
|
|
|
pub struct TransactionBaseFee;
|
|
impl Get<u64> for TransactionBaseFee {
|
|
fn get() -> u64 { TRANSACTION_BASE_FEE.with(|v| *v.borrow()) }
|
|
}
|
|
|
|
pub struct TransactionByteFee;
|
|
impl Get<u64> for TransactionByteFee {
|
|
fn get() -> u64 { TRANSACTION_BYTE_FEE.with(|v| *v.borrow()) }
|
|
}
|
|
|
|
pub struct WeightToFee(u64);
|
|
impl Convert<Weight, u64> for WeightToFee {
|
|
fn convert(t: Weight) -> u64 {
|
|
WEIGHT_TO_FEE.with(|v| *v.borrow() * (t as u64))
|
|
}
|
|
}
|
|
|
|
impl Trait for Runtime {
|
|
type Currency = pallet_balances::Module<Runtime>;
|
|
type OnTransactionPayment = ();
|
|
type TransactionBaseFee = TransactionBaseFee;
|
|
type TransactionByteFee = TransactionByteFee;
|
|
type WeightToFee = WeightToFee;
|
|
type FeeMultiplierUpdate = ();
|
|
}
|
|
|
|
type Balances = pallet_balances::Module<Runtime>;
|
|
type System = frame_system::Module<Runtime>;
|
|
type TransactionPayment = Module<Runtime>;
|
|
|
|
pub struct ExtBuilder {
|
|
balance_factor: u64,
|
|
base_fee: u64,
|
|
byte_fee: u64,
|
|
weight_to_fee: u64
|
|
}
|
|
|
|
impl Default for ExtBuilder {
|
|
fn default() -> Self {
|
|
Self {
|
|
balance_factor: 1,
|
|
base_fee: 0,
|
|
byte_fee: 1,
|
|
weight_to_fee: 1,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ExtBuilder {
|
|
pub fn base_fee(mut self, base_fee: u64) -> Self {
|
|
self.base_fee = base_fee;
|
|
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
|
|
}
|
|
fn set_constants(&self) {
|
|
TRANSACTION_BASE_FEE.with(|v| *v.borrow_mut() = self.base_fee);
|
|
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::<Runtime>().unwrap();
|
|
pallet_balances::GenesisConfig::<Runtime> {
|
|
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();
|
|
t.into()
|
|
}
|
|
}
|
|
|
|
/// create a transaction info struct from weight. Handy to avoid building the whole struct.
|
|
pub fn info_from_weight(w: Weight) -> DispatchInfo {
|
|
DispatchInfo { weight: w, pays_fee: true, ..Default::default() }
|
|
}
|
|
|
|
#[test]
|
|
fn signed_extension_transaction_payment_work() {
|
|
ExtBuilder::default()
|
|
.balance_factor(10)
|
|
.base_fee(5)
|
|
.build()
|
|
.execute_with(||
|
|
{
|
|
let len = 10;
|
|
assert!(
|
|
ChargeTransactionPayment::<Runtime>::from(0)
|
|
.pre_dispatch(&1, CALL, info_from_weight(5), len)
|
|
.is_ok()
|
|
);
|
|
assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10);
|
|
|
|
assert!(
|
|
ChargeTransactionPayment::<Runtime>::from(5 /* tipped */)
|
|
.pre_dispatch(&2, CALL, info_from_weight(3), len)
|
|
.is_ok()
|
|
);
|
|
assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 3 - 5);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn signed_extension_transaction_payment_is_bounded() {
|
|
ExtBuilder::default()
|
|
.balance_factor(1000)
|
|
.byte_fee(0)
|
|
.build()
|
|
.execute_with(||
|
|
{
|
|
// maximum weight possible
|
|
assert!(
|
|
ChargeTransactionPayment::<Runtime>::from(0)
|
|
.pre_dispatch(&1, CALL, info_from_weight(Weight::max_value()), 10)
|
|
.is_ok()
|
|
);
|
|
// fee will be proportional to what is the actual maximum weight in the runtime.
|
|
assert_eq!(
|
|
Balances::free_balance(&1),
|
|
(10000 - <Runtime as frame_system::Trait>::MaximumBlockWeight::get()) as u64
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn signed_extension_allows_free_transactions() {
|
|
ExtBuilder::default()
|
|
.base_fee(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: 0,
|
|
class: DispatchClass::Operational,
|
|
pays_fee: false,
|
|
};
|
|
assert!(
|
|
ChargeTransactionPayment::<Runtime>::from(0)
|
|
.validate(&1, CALL, operational_transaction , len)
|
|
.is_ok()
|
|
);
|
|
|
|
// like a InsecureFreeNormal
|
|
let free_transaction = DispatchInfo {
|
|
weight: 0,
|
|
class: DispatchClass::Normal,
|
|
pays_fee: true,
|
|
};
|
|
assert!(
|
|
ChargeTransactionPayment::<Runtime>::from(0)
|
|
.validate(&1, CALL, free_transaction , len)
|
|
.is_err()
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn signed_ext_length_fee_is_also_updated_per_congestion() {
|
|
ExtBuilder::default()
|
|
.base_fee(5)
|
|
.balance_factor(10)
|
|
.build()
|
|
.execute_with(||
|
|
{
|
|
// all fees should be x1.5
|
|
NextFeeMultiplier::put(Fixed64::from_rational(1, 2));
|
|
let len = 10;
|
|
|
|
assert!(
|
|
ChargeTransactionPayment::<Runtime>::from(10) // tipped
|
|
.pre_dispatch(&1, CALL, info_from_weight(3), len)
|
|
.is_ok()
|
|
);
|
|
assert_eq!(Balances::free_balance(1), 100 - 10 - 5 - (10 + 3) * 3 / 2);
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn query_info_works() {
|
|
let call = Call::Balances(BalancesCall::transfer(2, 69));
|
|
let origin = 111111;
|
|
let extra = ();
|
|
let xt = TestXt::new(call, Some((origin, extra)));
|
|
let info = xt.get_dispatch_info();
|
|
let ext = xt.encode();
|
|
let len = ext.len() as u32;
|
|
ExtBuilder::default()
|
|
.base_fee(5)
|
|
.weight_fee(2)
|
|
.build()
|
|
.execute_with(||
|
|
{
|
|
// all fees should be x1.5
|
|
NextFeeMultiplier::put(Fixed64::from_rational(1, 2));
|
|
|
|
assert_eq!(
|
|
TransactionPayment::query_info(xt, len),
|
|
RuntimeDispatchInfo {
|
|
weight: info.weight,
|
|
class: info.class,
|
|
partial_fee:
|
|
5 /* base */
|
|
+ (
|
|
len as u64 /* len * 1 */
|
|
+ info.weight.min(MaximumBlockWeight::get()) as u64 * 2 /* weight * weight_to_fee */
|
|
) * 3 / 2
|
|
},
|
|
);
|
|
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn compute_fee_works_without_multiplier() {
|
|
ExtBuilder::default()
|
|
.base_fee(100)
|
|
.byte_fee(10)
|
|
.balance_factor(0)
|
|
.build()
|
|
.execute_with(||
|
|
{
|
|
// Next fee multiplier is zero
|
|
assert_eq!(NextFeeMultiplier::get(), Fixed64::from_natural(0));
|
|
|
|
// Tip only, no fees works
|
|
let dispatch_info = DispatchInfo {
|
|
weight: 0,
|
|
class: DispatchClass::Operational,
|
|
pays_fee: false,
|
|
};
|
|
assert_eq!(ChargeTransactionPayment::<Runtime>::compute_fee(0, dispatch_info, 10), 10);
|
|
// No tip, only base fee works
|
|
let dispatch_info = DispatchInfo {
|
|
weight: 0,
|
|
class: DispatchClass::Operational,
|
|
pays_fee: true,
|
|
};
|
|
assert_eq!(ChargeTransactionPayment::<Runtime>::compute_fee(0, dispatch_info, 0), 100);
|
|
// Tip + base fee works
|
|
assert_eq!(ChargeTransactionPayment::<Runtime>::compute_fee(0, dispatch_info, 69), 169);
|
|
// Len (byte fee) + base fee works
|
|
assert_eq!(ChargeTransactionPayment::<Runtime>::compute_fee(42, dispatch_info, 0), 520);
|
|
// Weight fee + base fee works
|
|
let dispatch_info = DispatchInfo {
|
|
weight: 1000,
|
|
class: DispatchClass::Operational,
|
|
pays_fee: true,
|
|
};
|
|
assert_eq!(ChargeTransactionPayment::<Runtime>::compute_fee(0, dispatch_info, 0), 1100);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn compute_fee_works_with_multiplier() {
|
|
ExtBuilder::default()
|
|
.base_fee(100)
|
|
.byte_fee(10)
|
|
.balance_factor(0)
|
|
.build()
|
|
.execute_with(||
|
|
{
|
|
// Add a next fee multiplier
|
|
NextFeeMultiplier::put(Fixed64::from_rational(1, 2)); // = 1/2 = .5
|
|
// Base fee is unaffected by multiplier
|
|
let dispatch_info = DispatchInfo {
|
|
weight: 0,
|
|
class: DispatchClass::Operational,
|
|
pays_fee: true,
|
|
};
|
|
assert_eq!(ChargeTransactionPayment::<Runtime>::compute_fee(0, dispatch_info, 0), 100);
|
|
|
|
// Everything works together :)
|
|
let dispatch_info = DispatchInfo {
|
|
weight: 123,
|
|
class: DispatchClass::Operational,
|
|
pays_fee: true,
|
|
};
|
|
// 123 weight, 456 length, 100 base
|
|
// adjustable fee = (123 * 1) + (456 * 10) = 4683
|
|
// adjusted fee = (4683 * .5) + 4683 = 7024.5 -> 7024
|
|
// final fee = 100 + 7024 + 789 tip = 7913
|
|
assert_eq!(ChargeTransactionPayment::<Runtime>::compute_fee(456, dispatch_info, 789), 7913);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn compute_fee_does_not_overflow() {
|
|
ExtBuilder::default()
|
|
.base_fee(100)
|
|
.byte_fee(10)
|
|
.balance_factor(0)
|
|
.build()
|
|
.execute_with(||
|
|
{
|
|
// Overflow is handled
|
|
let dispatch_info = DispatchInfo {
|
|
weight: <u32>::max_value(),
|
|
class: DispatchClass::Operational,
|
|
pays_fee: true,
|
|
};
|
|
assert_eq!(
|
|
ChargeTransactionPayment::<Runtime>::compute_fee(
|
|
<u32>::max_value(),
|
|
dispatch_info,
|
|
<u64>::max_value()
|
|
),
|
|
<u64>::max_value()
|
|
);
|
|
});
|
|
}
|
|
}
|