mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 18:07:58 +00:00
bcea0d7e7f
* allow fellows to abdicate voting rights * rename founders to founding fellows, give equal power * Drop FoundingFellow role and veto call (#12762) * drop FoundingFellow role * drop veto call * Storage migration to remove founder role (#12766) * storage migration to remove founder role * skip migration if no members * truncate the final fellows set if overflows * change log - action order * MemberAbdicated -> FellowAbdicated Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>
454 lines
12 KiB
Rust
454 lines
12 KiB
Rust
// 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.
|
|
|
|
//! Some configurable implementations as associated type for the substrate runtime.
|
|
|
|
use crate::{
|
|
AccountId, AllianceMotion, Assets, Authorship, Balances, Hash, NegativeImbalance, Runtime,
|
|
RuntimeCall,
|
|
};
|
|
use frame_support::{
|
|
pallet_prelude::*,
|
|
traits::{
|
|
fungibles::{Balanced, CreditOf},
|
|
Currency, OnUnbalanced,
|
|
},
|
|
};
|
|
use pallet_alliance::{IdentityVerifier, ProposalIndex, ProposalProvider};
|
|
use pallet_asset_tx_payment::HandleCredit;
|
|
use sp_std::prelude::*;
|
|
|
|
pub struct Author;
|
|
impl OnUnbalanced<NegativeImbalance> for Author {
|
|
fn on_nonzero_unbalanced(amount: NegativeImbalance) {
|
|
if let Some(author) = Authorship::author() {
|
|
Balances::resolve_creating(&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>) {
|
|
if let Some(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct AllianceIdentityVerifier;
|
|
impl IdentityVerifier<AccountId> for AllianceIdentityVerifier {
|
|
fn has_identity(who: &AccountId, fields: u64) -> bool {
|
|
crate::Identity::has_identity(who, fields)
|
|
}
|
|
|
|
fn has_good_judgement(who: &AccountId) -> bool {
|
|
use pallet_identity::Judgement;
|
|
if let Some(judgements) =
|
|
crate::Identity::identity(who).map(|registration| registration.judgements)
|
|
{
|
|
judgements
|
|
.iter()
|
|
.any(|(_, j)| matches!(j, Judgement::KnownGood | Judgement::Reasonable))
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
fn super_account_id(who: &AccountId) -> Option<AccountId> {
|
|
crate::Identity::super_of(who).map(|parent| parent.0)
|
|
}
|
|
}
|
|
|
|
pub struct AllianceProposalProvider;
|
|
impl ProposalProvider<AccountId, Hash, RuntimeCall> for AllianceProposalProvider {
|
|
fn propose_proposal(
|
|
who: AccountId,
|
|
threshold: u32,
|
|
proposal: Box<RuntimeCall>,
|
|
length_bound: u32,
|
|
) -> Result<(u32, u32), DispatchError> {
|
|
AllianceMotion::do_propose_proposed(who, threshold, proposal, length_bound)
|
|
}
|
|
|
|
fn vote_proposal(
|
|
who: AccountId,
|
|
proposal: Hash,
|
|
index: ProposalIndex,
|
|
approve: bool,
|
|
) -> Result<bool, DispatchError> {
|
|
AllianceMotion::do_vote(who, proposal, index, approve)
|
|
}
|
|
|
|
fn close_proposal(
|
|
proposal_hash: Hash,
|
|
proposal_index: ProposalIndex,
|
|
proposal_weight_bound: Weight,
|
|
length_bound: u32,
|
|
) -> DispatchResultWithPostInfo {
|
|
AllianceMotion::do_close(proposal_hash, proposal_index, proposal_weight_bound, length_bound)
|
|
}
|
|
|
|
fn proposal_of(proposal_hash: Hash) -> Option<RuntimeCall> {
|
|
AllianceMotion::proposal_of(proposal_hash)
|
|
}
|
|
}
|
|
|
|
#[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, MaximumMultiplier, MinimumMultiplier, Runtime,
|
|
RuntimeBlockWeights as BlockWeights, System, TargetBlockFullness, TransactionPayment,
|
|
};
|
|
use frame_support::{
|
|
dispatch::DispatchClass,
|
|
weights::{Weight, WeightToFee},
|
|
};
|
|
|
|
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,
|
|
MaximumMultiplier,
|
|
>::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().ref_time() as f64;
|
|
// block weight always truncated to max weight
|
|
let block_weight = (block_weight.ref_time() as f64).min(m);
|
|
let v: f64 = AdjustmentVariable::get().to_float();
|
|
|
|
// Ideal saturation in terms of weight
|
|
let ss = target().ref_time() 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![
|
|
(Weight::zero(), fm),
|
|
(Weight::from_ref_time(100), fm),
|
|
(Weight::from_ref_time(1000), fm),
|
|
(target(), fm),
|
|
(max_normal() / 2, fm),
|
|
(max_normal(), fm),
|
|
];
|
|
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().set_ref_time(target().ref_time() * 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(Weight::zero(), || {
|
|
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(Weight::zero(), || {
|
|
// 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() -
|
|
Weight::from_ref_time(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::weight_to_fee(
|
|
&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 = Weight::from_ref_time(1024);
|
|
let mb = 1024u64 * kb;
|
|
let max_fm = Multiplier::saturating_from_integer(i128::MAX);
|
|
|
|
// check that for all values it can compute, correctly.
|
|
vec![
|
|
Weight::zero(),
|
|
Weight::from_ref_time(1),
|
|
Weight::from_ref_time(10),
|
|
Weight::from_ref_time(1000),
|
|
kb,
|
|
10u64 * kb,
|
|
100u64 * kb,
|
|
mb,
|
|
10u64 * mb,
|
|
Weight::from_ref_time(2147483647),
|
|
Weight::from_ref_time(4294967295),
|
|
BlockWeights::get().max_block / 2,
|
|
BlockWeights::get().max_block,
|
|
Weight::MAX / 2,
|
|
Weight::MAX,
|
|
]
|
|
.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 + Weight::from_ref_time(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);
|
|
})
|
|
});
|
|
}
|
|
}
|