mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 11:07:56 +00:00
439af98e55
* Moving `pallet-asset-tx-payment` from cumulus * move pallet-asset-tx-payment into transaction payment directory * cargo +nightly fmt * Adding `pallet-asset-tx-payment` to node runtime I had to change the Balance type to u128. Also harmonised that pallet's version * Updating cargo.lock after merge * forgot this * Adding tx-payment signature * Missed one more * `transaction-payment` replaced in`SignedExtension` by `asset-tx-payment` and not added * Fixing benches * add test to verify that we don't charge on post-dispatch if we didn't on pre-dispatch * add (failing) test for asset tx payment of unsigned extrinsics * fix test by removing debug_assert * cargo +nightly fmt * typo in `Cargo.lock` * Object defined twice in lock file * cargo update * remove todo * Apply formatting suggestions from code review Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Refactoring `post_dispatch` of `asset-tx-payment` to reuse `post_dispatch` of `transaction-payment` if the fee asset is native Removing unneeded imports. * Removing redundant `TODO` * Reverting an accidental bump of `impl-serde` from `0.3.1` to `0.3.2` * Revert unneeded changes to `cargo.lock` * Update frame/transaction-payment/asset-tx-payment/src/payment.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Fixing cargo fmt Reverting changes which broke cargo fmt Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> Co-authored-by: Alexander Popiak <alexander.popiak@gmail.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
378 lines
10 KiB
Rust
378 lines
10 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) 2019-2021 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.
|
|
|
|
//! Some configurable implementations as associated type for the substrate runtime.
|
|
|
|
use crate::{AccountId, Assets, Authorship, Balances, NegativeImbalance, Runtime};
|
|
use frame_support::traits::{
|
|
fungibles::{Balanced, CreditOf},
|
|
Currency, OnUnbalanced,
|
|
};
|
|
use pallet_asset_tx_payment::HandleCredit;
|
|
|
|
pub struct Author;
|
|
impl OnUnbalanced<NegativeImbalance> for Author {
|
|
fn on_nonzero_unbalanced(amount: NegativeImbalance) {
|
|
Balances::resolve_creating(&Authorship::author(), amount);
|
|
}
|
|
}
|
|
|
|
/// A `HandleCredit` implementation that naively transfers the fees to the block author.
|
|
/// Will drop and burn the assets in case the transfer fails.
|
|
pub struct CreditToBlockAuthor;
|
|
impl HandleCredit<AccountId, Assets> for CreditToBlockAuthor {
|
|
fn handle_credit(credit: CreditOf<AccountId, Assets>) {
|
|
let author = pallet_authorship::Pallet::<Runtime>::author();
|
|
// Drop the result which will trigger the `OnDrop` of the imbalance in case of error.
|
|
let _ = Assets::resolve(&author, credit);
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod multiplier_tests {
|
|
use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment};
|
|
use sp_runtime::{
|
|
assert_eq_error_rate,
|
|
traits::{Convert, One, Zero},
|
|
FixedPointNumber,
|
|
};
|
|
|
|
use crate::{
|
|
constants::{currency::*, time::*},
|
|
AdjustmentVariable, MinimumMultiplier, Runtime, RuntimeBlockWeights as BlockWeights,
|
|
System, TargetBlockFullness, TransactionPayment,
|
|
};
|
|
use frame_support::weights::{DispatchClass, Weight, WeightToFeePolynomial};
|
|
|
|
fn max_normal() -> Weight {
|
|
BlockWeights::get()
|
|
.get(DispatchClass::Normal)
|
|
.max_total
|
|
.unwrap_or_else(|| BlockWeights::get().max_block)
|
|
}
|
|
|
|
fn min_multiplier() -> Multiplier {
|
|
MinimumMultiplier::get()
|
|
}
|
|
|
|
fn target() -> Weight {
|
|
TargetBlockFullness::get() * max_normal()
|
|
}
|
|
|
|
// update based on runtime impl.
|
|
fn runtime_multiplier_update(fm: Multiplier) -> Multiplier {
|
|
TargetedFeeAdjustment::<
|
|
Runtime,
|
|
TargetBlockFullness,
|
|
AdjustmentVariable,
|
|
MinimumMultiplier,
|
|
>::convert(fm)
|
|
}
|
|
|
|
// update based on reference impl.
|
|
fn truth_value_update(block_weight: Weight, previous: Multiplier) -> Multiplier {
|
|
let accuracy = Multiplier::accuracy() as f64;
|
|
let previous_float = previous.into_inner() as f64 / accuracy;
|
|
// bump if it is zero.
|
|
let previous_float = previous_float.max(min_multiplier().into_inner() as f64 / accuracy);
|
|
|
|
// maximum tx weight
|
|
let m = max_normal() as f64;
|
|
// block weight always truncated to max weight
|
|
let block_weight = (block_weight as f64).min(m);
|
|
let v: f64 = AdjustmentVariable::get().to_float();
|
|
|
|
// Ideal saturation in terms of weight
|
|
let ss = target() as f64;
|
|
// Current saturation in terms of weight
|
|
let s = block_weight;
|
|
|
|
let t1 = v * (s / m - ss / m);
|
|
let t2 = v.powi(2) * (s / m - ss / m).powi(2) / 2.0;
|
|
let next_float = previous_float * (1.0 + t1 + t2);
|
|
Multiplier::from_float(next_float)
|
|
}
|
|
|
|
fn run_with_system_weight<F>(w: Weight, assertions: F)
|
|
where
|
|
F: Fn() -> (),
|
|
{
|
|
let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::default()
|
|
.build_storage::<Runtime>()
|
|
.unwrap()
|
|
.into();
|
|
t.execute_with(|| {
|
|
System::set_block_consumed_resources(w, 0);
|
|
assertions()
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn truth_value_update_poc_works() {
|
|
let fm = Multiplier::saturating_from_rational(1, 2);
|
|
let test_set = vec![
|
|
(0, fm.clone()),
|
|
(100, fm.clone()),
|
|
(1000, fm.clone()),
|
|
(target(), fm.clone()),
|
|
(max_normal() / 2, fm.clone()),
|
|
(max_normal(), fm.clone()),
|
|
];
|
|
test_set.into_iter().for_each(|(w, fm)| {
|
|
run_with_system_weight(w, || {
|
|
assert_eq_error_rate!(
|
|
truth_value_update(w, fm),
|
|
runtime_multiplier_update(fm),
|
|
// Error is only 1 in 100^18
|
|
Multiplier::from_inner(100),
|
|
);
|
|
})
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn multiplier_can_grow_from_zero() {
|
|
// if the min is too small, then this will not change, and we are doomed forever.
|
|
// the weight is 1/100th bigger than target.
|
|
run_with_system_weight(target() * 101 / 100, || {
|
|
let next = runtime_multiplier_update(min_multiplier());
|
|
assert!(next > min_multiplier(), "{:?} !>= {:?}", next, min_multiplier());
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn multiplier_cannot_go_below_limit() {
|
|
// will not go any further below even if block is empty.
|
|
run_with_system_weight(0, || {
|
|
let next = runtime_multiplier_update(min_multiplier());
|
|
assert_eq!(next, min_multiplier());
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn time_to_reach_zero() {
|
|
// blocks per 24h in substrate-node: 28,800 (k)
|
|
// s* = 0.1875
|
|
// The bound from the research in an empty chain is:
|
|
// v <~ (p / k(0 - s*))
|
|
// p > v * k * -0.1875
|
|
// to get p == -1 we'd need
|
|
// -1 > 0.00001 * k * -0.1875
|
|
// 1 < 0.00001 * k * 0.1875
|
|
// 10^9 / 1875 < k
|
|
// k > 533_333 ~ 18,5 days.
|
|
run_with_system_weight(0, || {
|
|
// start from 1, the default.
|
|
let mut fm = Multiplier::one();
|
|
let mut iterations: u64 = 0;
|
|
loop {
|
|
let next = runtime_multiplier_update(fm);
|
|
fm = next;
|
|
if fm == min_multiplier() {
|
|
break
|
|
}
|
|
iterations += 1;
|
|
}
|
|
assert!(iterations > 533_333);
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn min_change_per_day() {
|
|
run_with_system_weight(max_normal(), || {
|
|
let mut fm = Multiplier::one();
|
|
// See the example in the doc of `TargetedFeeAdjustment`. are at least 0.234, hence
|
|
// `fm > 1.234`.
|
|
for _ in 0..DAYS {
|
|
let next = runtime_multiplier_update(fm);
|
|
fm = next;
|
|
}
|
|
assert!(fm > Multiplier::saturating_from_rational(1234, 1000));
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn congested_chain_simulation() {
|
|
// `cargo test congested_chain_simulation -- --nocapture` to get some insight.
|
|
|
|
// almost full. The entire quota of normal transactions is taken.
|
|
let block_weight = BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap() - 100;
|
|
|
|
// Default substrate weight.
|
|
let tx_weight = frame_support::weights::constants::ExtrinsicBaseWeight::get();
|
|
|
|
run_with_system_weight(block_weight, || {
|
|
// initial value configured on module
|
|
let mut fm = Multiplier::one();
|
|
assert_eq!(fm, TransactionPayment::next_fee_multiplier());
|
|
|
|
let mut iterations: u64 = 0;
|
|
loop {
|
|
let next = runtime_multiplier_update(fm);
|
|
// if no change, panic. This should never happen in this case.
|
|
if fm == next {
|
|
panic!("The fee should ever increase");
|
|
}
|
|
fm = next;
|
|
iterations += 1;
|
|
let fee =
|
|
<Runtime as pallet_transaction_payment::Config>::WeightToFee::calc(&tx_weight);
|
|
let adjusted_fee = fm.saturating_mul_acc_int(fee);
|
|
println!(
|
|
"iteration {}, new fm = {:?}. Fee at this point is: {} units / {} millicents, \
|
|
{} cents, {} dollars",
|
|
iterations,
|
|
fm,
|
|
adjusted_fee,
|
|
adjusted_fee / MILLICENTS,
|
|
adjusted_fee / CENTS,
|
|
adjusted_fee / DOLLARS,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn stateless_weight_mul() {
|
|
let fm = Multiplier::saturating_from_rational(1, 2);
|
|
run_with_system_weight(target() / 4, || {
|
|
let next = runtime_multiplier_update(fm);
|
|
assert_eq_error_rate!(
|
|
next,
|
|
truth_value_update(target() / 4, fm),
|
|
Multiplier::from_inner(100),
|
|
);
|
|
|
|
// Light block. Multiplier is reduced a little.
|
|
assert!(next < fm);
|
|
});
|
|
|
|
run_with_system_weight(target() / 2, || {
|
|
let next = runtime_multiplier_update(fm);
|
|
assert_eq_error_rate!(
|
|
next,
|
|
truth_value_update(target() / 2, fm),
|
|
Multiplier::from_inner(100),
|
|
);
|
|
// Light block. Multiplier is reduced a little.
|
|
assert!(next < fm);
|
|
});
|
|
run_with_system_weight(target(), || {
|
|
let next = runtime_multiplier_update(fm);
|
|
assert_eq_error_rate!(
|
|
next,
|
|
truth_value_update(target(), fm),
|
|
Multiplier::from_inner(100),
|
|
);
|
|
// ideal. No changes.
|
|
assert_eq!(next, fm)
|
|
});
|
|
run_with_system_weight(target() * 2, || {
|
|
// More than ideal. Fee is increased.
|
|
let next = runtime_multiplier_update(fm);
|
|
assert_eq_error_rate!(
|
|
next,
|
|
truth_value_update(target() * 2, fm),
|
|
Multiplier::from_inner(100),
|
|
);
|
|
|
|
// Heavy block. Fee is increased a little.
|
|
assert!(next > fm);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn weight_mul_grow_on_big_block() {
|
|
run_with_system_weight(target() * 2, || {
|
|
let mut original = Multiplier::zero();
|
|
let mut next = Multiplier::default();
|
|
|
|
(0..1_000).for_each(|_| {
|
|
next = runtime_multiplier_update(original);
|
|
assert_eq_error_rate!(
|
|
next,
|
|
truth_value_update(target() * 2, original),
|
|
Multiplier::from_inner(100),
|
|
);
|
|
// must always increase
|
|
assert!(next > original, "{:?} !>= {:?}", next, original);
|
|
original = next;
|
|
});
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn weight_mul_decrease_on_small_block() {
|
|
run_with_system_weight(target() / 2, || {
|
|
let mut original = Multiplier::saturating_from_rational(1, 2);
|
|
let mut next;
|
|
|
|
for _ in 0..100 {
|
|
// decreases
|
|
next = runtime_multiplier_update(original);
|
|
assert!(next < original, "{:?} !<= {:?}", next, original);
|
|
original = next;
|
|
}
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn weight_to_fee_should_not_overflow_on_large_weights() {
|
|
let kb = 1024 as Weight;
|
|
let mb = kb * kb;
|
|
let max_fm = Multiplier::saturating_from_integer(i128::MAX);
|
|
|
|
// check that for all values it can compute, correctly.
|
|
vec![
|
|
0,
|
|
1,
|
|
10,
|
|
1000,
|
|
kb,
|
|
10 * kb,
|
|
100 * kb,
|
|
mb,
|
|
10 * mb,
|
|
2147483647,
|
|
4294967295,
|
|
BlockWeights::get().max_block / 2,
|
|
BlockWeights::get().max_block,
|
|
Weight::max_value() / 2,
|
|
Weight::max_value(),
|
|
]
|
|
.into_iter()
|
|
.for_each(|i| {
|
|
run_with_system_weight(i, || {
|
|
let next = runtime_multiplier_update(Multiplier::one());
|
|
let truth = truth_value_update(i, Multiplier::one());
|
|
assert_eq_error_rate!(truth, next, Multiplier::from_inner(50_000_000));
|
|
});
|
|
});
|
|
|
|
// Some values that are all above the target and will cause an increase.
|
|
let t = target();
|
|
vec![t + 100, t * 2, t * 4].into_iter().for_each(|i| {
|
|
run_with_system_weight(i, || {
|
|
let fm = runtime_multiplier_update(max_fm);
|
|
// won't grow. The convert saturates everything.
|
|
assert_eq!(fm, max_fm);
|
|
})
|
|
});
|
|
}
|
|
}
|