mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 11:07:56 +00:00
21fbc00d04
This PR is a follow up to #1661 - [x] rename the `simple` module to `legacy` - [x] fix benchmarks to disregard the number of additional fields - [x] change the storage deposits to charge per encoded byte of the identity information instance, removing the need for `fn additional(&self) -> usize` in `IdentityInformationProvider` - [x] ~add an extrinsic to rejig deposits to account for the change above~ - [ ] ~ensure through proper configuration that the new byte-based deposit is always lower than whatever is reserved now~ - [x] remove `IdentityFields` from the `set_fields` extrinsic signature, as per [this discussion](https://github.com/paritytech/polkadot-sdk/pull/1661#discussion_r1371703403) > ensure through proper configuration that the new byte-based deposit is always lower than whatever is reserved now Not sure this is needed anymore. If the new deposits are higher than what is currently on chain and users don't have enough funds to reserve what is needed, the extrinisc fails and they're basically grandfathered and frozen until they add more funds and/or make a change to their identity. This behavior seems fine to me. Original idea [here](https://github.com/paritytech/polkadot-sdk/pull/1661#issuecomment-1779606319). > add an extrinsic to rejig deposits to account for the change above This was initially implemented but now removed from this PR in favor of the implementation detailed [here](https://github.com/paritytech/polkadot-sdk/pull/2088). --------- Signed-off-by: georgepisaltu <george.pisaltu@parity.io> Co-authored-by: joepetrowski <joe@parity.io>
491 lines
14 KiB
Rust
491 lines
14 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) 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::{
|
|
pallet_prelude::*,
|
|
traits::{
|
|
fungibles::{Balanced, Credit},
|
|
Currency, OnUnbalanced,
|
|
},
|
|
};
|
|
use pallet_alliance::{IdentityVerifier, ProposalIndex, ProposalProvider};
|
|
use pallet_asset_tx_payment::HandleCredit;
|
|
use pallet_identity::legacy::IdentityField;
|
|
use sp_std::prelude::*;
|
|
|
|
use crate::{
|
|
AccountId, AllianceMotion, Assets, Authorship, Balances, Hash, NegativeImbalance, Runtime,
|
|
RuntimeCall,
|
|
};
|
|
|
|
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: Credit<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_required_identities(who: &AccountId) -> bool {
|
|
crate::Identity::has_identity(who, (IdentityField::Display | IdentityField::Web).bits())
|
|
}
|
|
|
|
fn has_good_judgement(who: &AccountId) -> bool {
|
|
use pallet_identity::Judgement;
|
|
crate::Identity::identity(who)
|
|
.map(|registration| registration.judgements)
|
|
.map_or(false, |judgements| {
|
|
judgements
|
|
.iter()
|
|
.any(|(_, j)| matches!(j, Judgement::KnownGood | Judgement::Reasonable))
|
|
})
|
|
}
|
|
|
|
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 frame_support::{
|
|
dispatch::DispatchClass,
|
|
weights::{Weight, WeightToFee},
|
|
};
|
|
use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment};
|
|
use sp_runtime::{
|
|
assert_eq_error_rate,
|
|
traits::{Convert, One, Zero},
|
|
BuildStorage, FixedPointNumber,
|
|
};
|
|
|
|
use crate::{
|
|
constants::{currency::*, time::*},
|
|
AdjustmentVariable, MaximumMultiplier, MinimumMultiplier, Runtime,
|
|
RuntimeBlockWeights as BlockWeights, System, TargetBlockFullness, TransactionPayment,
|
|
};
|
|
|
|
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);
|
|
|
|
let max_normal = max_normal();
|
|
let target_weight = target();
|
|
let normalized_weight_dimensions = (
|
|
block_weight.ref_time() as f64 / max_normal.ref_time() as f64,
|
|
block_weight.proof_size() as f64 / max_normal.proof_size() as f64,
|
|
);
|
|
|
|
let (normal, max, target) =
|
|
if normalized_weight_dimensions.0 < normalized_weight_dimensions.1 {
|
|
(block_weight.proof_size(), max_normal.proof_size(), target_weight.proof_size())
|
|
} else {
|
|
(block_weight.ref_time(), max_normal.ref_time(), target_weight.ref_time())
|
|
};
|
|
|
|
// maximum tx weight
|
|
let m = max as f64;
|
|
// block weight always truncated to max weight
|
|
let block_weight = (normal 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::<Runtime>::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![
|
|
(Weight::zero(), fm),
|
|
(Weight::from_parts(100, 0), fm),
|
|
(Weight::from_parts(1000, 0), 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 block ref time 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());
|
|
});
|
|
|
|
// the block proof size is 1/100th bigger than target.
|
|
run_with_system_weight(target().set_proof_size((target().proof_size() / 100) * 101), || {
|
|
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_parts(100, 0);
|
|
|
|
// 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_time = Weight::from_parts(1024, 0);
|
|
let kb_size = Weight::from_parts(0, 1024);
|
|
let mb_time = 1024u64 * kb_time;
|
|
let max_fm = Multiplier::saturating_from_integer(i128::MAX);
|
|
|
|
// check that for all values it can compute, correctly.
|
|
vec![
|
|
Weight::zero(),
|
|
// testcases ignoring proof size part of the weight.
|
|
Weight::from_parts(1, 0),
|
|
Weight::from_parts(10, 0),
|
|
Weight::from_parts(1000, 0),
|
|
kb_time,
|
|
10u64 * kb_time,
|
|
100u64 * kb_time,
|
|
mb_time,
|
|
10u64 * mb_time,
|
|
Weight::from_parts(2147483647, 0),
|
|
Weight::from_parts(4294967295, 0),
|
|
// testcases ignoring ref time part of the weight.
|
|
Weight::from_parts(0, 100000000000),
|
|
1000000u64 * kb_size,
|
|
1000000000u64 * kb_size,
|
|
Weight::from_parts(0, 18014398509481983),
|
|
Weight::from_parts(0, 9223372036854775807),
|
|
// test cases with both parts of the weight.
|
|
BlockWeights::get().max_block / 1024,
|
|
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_parts(100, 0),
|
|
t + Weight::from_parts(0, t.proof_size() * 2),
|
|
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);
|
|
})
|
|
});
|
|
}
|
|
}
|