mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 19:17:58 +00:00
82bdf1a891
* Initial draft, has some todos left * remove ununsed import * Apply suggestions from code review * Some refactors with migration * Fix more test and cleanup * Fix for companion * Apply suggestions from code review Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> * Update bin/node/runtime/src/impls.rs * Fix weight * Add integrity test * length is not affected. Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>
367 lines
10 KiB
Rust
367 lines
10 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) 2019-2020 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 node_primitives::Balance;
|
|
use sp_runtime::traits::Convert;
|
|
use frame_support::traits::{OnUnbalanced, Currency};
|
|
use crate::{Balances, Authorship, NegativeImbalance};
|
|
|
|
pub struct Author;
|
|
impl OnUnbalanced<NegativeImbalance> for Author {
|
|
fn on_nonzero_unbalanced(amount: NegativeImbalance) {
|
|
Balances::resolve_creating(&Authorship::author(), amount);
|
|
}
|
|
}
|
|
|
|
/// Struct that handles the conversion of Balance -> `u64`. This is used for staking's election
|
|
/// calculation.
|
|
pub struct CurrencyToVoteHandler;
|
|
|
|
impl CurrencyToVoteHandler {
|
|
fn factor() -> Balance { (Balances::total_issuance() / u64::max_value() as Balance).max(1) }
|
|
}
|
|
|
|
impl Convert<Balance, u64> for CurrencyToVoteHandler {
|
|
fn convert(x: Balance) -> u64 { (x / Self::factor()) as u64 }
|
|
}
|
|
|
|
impl Convert<u128, Balance> for CurrencyToVoteHandler {
|
|
fn convert(x: u128) -> Balance { x * Self::factor() }
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod multiplier_tests {
|
|
use super::*;
|
|
use sp_runtime::{assert_eq_error_rate, FixedPointNumber};
|
|
use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment};
|
|
|
|
use crate::{
|
|
constants::{currency::*, time::*},
|
|
TransactionPayment, MaximumBlockWeight, AvailableBlockRatio, Runtime, TargetBlockFullness,
|
|
AdjustmentVariable, System, MinimumMultiplier,
|
|
};
|
|
use frame_support::weights::{Weight, WeightToFeePolynomial};
|
|
|
|
fn max() -> Weight {
|
|
AvailableBlockRatio::get() * MaximumBlockWeight::get()
|
|
}
|
|
|
|
fn min_multiplier() -> Multiplier {
|
|
MinimumMultiplier::get()
|
|
}
|
|
|
|
fn target() -> Weight {
|
|
TargetBlockFullness::get() * max()
|
|
}
|
|
|
|
// 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() as f64;
|
|
// block weight always truncated to max weight
|
|
let block_weight = (block_weight as f64).min(m);
|
|
let v: f64 = AdjustmentVariable::get().to_fraction();
|
|
|
|
// 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_fraction(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_limits(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() / 2, fm.clone()),
|
|
(max(), 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(), || {
|
|
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 = AvailableBlockRatio::get() * max() - 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::Trait>::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_value());
|
|
|
|
// check that for all values it can compute, correctly.
|
|
vec![
|
|
0,
|
|
1,
|
|
10,
|
|
1000,
|
|
kb,
|
|
10 * kb,
|
|
100 * kb,
|
|
mb,
|
|
10 * mb,
|
|
2147483647,
|
|
4294967295,
|
|
MaximumBlockWeight::get() / 2,
|
|
MaximumBlockWeight::get(),
|
|
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);
|
|
})
|
|
});
|
|
}
|
|
}
|