// 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 frame_support::traits::{OnUnbalanced, Currency}; use crate::{Balances, Authorship, NegativeImbalance}; pub struct Author; impl OnUnbalanced for Author { fn on_nonzero_unbalanced(amount: NegativeImbalance) { Balances::resolve_creating(&Authorship::author(), amount); } } #[cfg(test)] mod multiplier_tests { use sp_runtime::{assert_eq_error_rate, FixedPointNumber, traits::Convert}; use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment}; use crate::{ constants::{currency::*, time::*}, TransactionPayment, Runtime, TargetBlockFullness, AdjustmentVariable, System, MinimumMultiplier, RuntimeBlockWeights as BlockWeights, }; use frame_support::weights::{Weight, WeightToFeePolynomial, DispatchClass}; 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_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(w: Weight, assertions: F) where F: Fn() -> () { let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::default().build_storage::().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 = ::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, 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); }) }); } }