mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 08:51:09 +00:00
Run cargo fmt on the whole code base (#9394)
* Run cargo fmt on the whole code base * Second run * Add CI check * Fix compilation * More unnecessary braces * Handle weights * Use --all * Use correct attributes... * Fix UI tests * AHHHHHHHHH * 🤦 * Docs * Fix compilation * 🤷 * Please stop * 🤦 x 2 * More * make rustfmt.toml consistent with polkadot Co-authored-by: André Silva <andrerfosilva@gmail.com>
This commit is contained in:
@@ -21,11 +21,11 @@ use super::*;
|
||||
use crate::Pallet as Staking;
|
||||
use testing_utils::*;
|
||||
|
||||
use sp_runtime::traits::One;
|
||||
use frame_system::RawOrigin;
|
||||
pub use frame_benchmarking::{
|
||||
benchmarks, account, whitelisted_caller, whitelist_account, impl_benchmark_test_suite,
|
||||
account, benchmarks, impl_benchmark_test_suite, whitelist_account, whitelisted_caller,
|
||||
};
|
||||
use frame_system::RawOrigin;
|
||||
use sp_runtime::traits::One;
|
||||
|
||||
const SEED: u32 = 0;
|
||||
const MAX_SPANS: u32 = 100;
|
||||
@@ -36,13 +36,15 @@ const MAX_SLASHES: u32 = 1000;
|
||||
// Add slashing spans to a user account. Not relevant for actual use, only to benchmark
|
||||
// read and write operations.
|
||||
fn add_slashing_spans<T: Config>(who: &T::AccountId, spans: u32) {
|
||||
if spans == 0 { return }
|
||||
if spans == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// For the first slashing span, we initialize
|
||||
let mut slashing_spans = crate::slashing::SlashingSpans::new(0);
|
||||
SpanSlash::<T>::insert((who, 0), crate::slashing::SpanRecord::default());
|
||||
|
||||
for i in 1 .. spans {
|
||||
for i in 1..spans {
|
||||
assert!(slashing_spans.end_span(i));
|
||||
SpanSlash::<T>::insert((who, i), crate::slashing::SpanRecord::default());
|
||||
}
|
||||
@@ -56,7 +58,7 @@ pub fn create_validator_with_nominators<T: Config>(
|
||||
n: u32,
|
||||
upper_bound: u32,
|
||||
dead: bool,
|
||||
destination: RewardDestination<T::AccountId>
|
||||
destination: RewardDestination<T::AccountId>,
|
||||
) -> Result<(T::AccountId, Vec<(T::AccountId, T::AccountId)>), &'static str> {
|
||||
// Clean up any existing state.
|
||||
clear_validators_and_nominators::<T>();
|
||||
@@ -64,10 +66,8 @@ pub fn create_validator_with_nominators<T: Config>(
|
||||
let mut points_individual = Vec::new();
|
||||
|
||||
let (v_stash, v_controller) = create_stash_controller::<T>(0, 100, destination.clone())?;
|
||||
let validator_prefs = ValidatorPrefs {
|
||||
commission: Perbill::from_percent(50),
|
||||
.. Default::default()
|
||||
};
|
||||
let validator_prefs =
|
||||
ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() };
|
||||
Staking::<T>::validate(RawOrigin::Signed(v_controller).into(), validator_prefs)?;
|
||||
let stash_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(v_stash.clone());
|
||||
|
||||
@@ -77,14 +77,17 @@ pub fn create_validator_with_nominators<T: Config>(
|
||||
let mut nominators = Vec::new();
|
||||
|
||||
// Give the validator n nominators, but keep total users in the system the same.
|
||||
for i in 0 .. upper_bound {
|
||||
for i in 0..upper_bound {
|
||||
let (n_stash, n_controller) = if !dead {
|
||||
create_stash_controller::<T>(u32::MAX - i, 100, destination.clone())?
|
||||
} else {
|
||||
create_stash_and_dead_controller::<T>(u32::MAX - i, 100, destination.clone())?
|
||||
};
|
||||
if i < n {
|
||||
Staking::<T>::nominate(RawOrigin::Signed(n_controller.clone()).into(), vec![stash_lookup.clone()])?;
|
||||
Staking::<T>::nominate(
|
||||
RawOrigin::Signed(n_controller.clone()).into(),
|
||||
vec![stash_lookup.clone()],
|
||||
)?;
|
||||
nominators.push((n_stash, n_controller));
|
||||
}
|
||||
}
|
||||
@@ -639,7 +642,7 @@ benchmarks! {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mock::{ExtBuilder, Test, Balances, Staking, Origin};
|
||||
use crate::mock::{Balances, ExtBuilder, Origin, Staking, Test};
|
||||
use frame_support::assert_ok;
|
||||
|
||||
#[test]
|
||||
@@ -654,7 +657,8 @@ mod tests {
|
||||
<Test as Config>::MAX_NOMINATIONS as usize,
|
||||
false,
|
||||
None,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let count_validators = Validators::<Test>::iter().count();
|
||||
let count_nominators = Nominators::<Test>::iter().count();
|
||||
@@ -674,7 +678,8 @@ mod tests {
|
||||
<Test as Config>::MaxNominatorRewardedPerValidator::get() as u32,
|
||||
false,
|
||||
RewardDestination::Staked,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(nominators.len() as u32, n);
|
||||
|
||||
@@ -698,7 +703,8 @@ mod tests {
|
||||
<Test as Config>::MaxNominatorRewardedPerValidator::get() as u32,
|
||||
false,
|
||||
RewardDestination::Staked,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Add 20 slashing spans
|
||||
let num_of_slashing_spans = 20;
|
||||
@@ -706,14 +712,14 @@ mod tests {
|
||||
|
||||
let slashing_spans = SlashingSpans::<Test>::get(&validator_stash).unwrap();
|
||||
assert_eq!(slashing_spans.iter().count(), num_of_slashing_spans as usize);
|
||||
for i in 0 .. num_of_slashing_spans {
|
||||
for i in 0..num_of_slashing_spans {
|
||||
assert!(SpanSlash::<Test>::contains_key((&validator_stash, i)));
|
||||
}
|
||||
|
||||
// Test everything is cleaned up
|
||||
assert_ok!(Staking::kill_stash(&validator_stash, num_of_slashing_spans));
|
||||
assert!(SlashingSpans::<Test>::get(&validator_stash).is_none());
|
||||
for i in 0 .. num_of_slashing_spans {
|
||||
for i in 0..num_of_slashing_spans {
|
||||
assert!(!SpanSlash::<Test>::contains_key((&validator_stash, i)));
|
||||
}
|
||||
});
|
||||
@@ -726,13 +732,17 @@ mod tests {
|
||||
let n = 100;
|
||||
|
||||
let selected_benchmark = SelectedBenchmark::payout_all;
|
||||
let c = vec![(frame_benchmarking::BenchmarkParameter::v, v), (frame_benchmarking::BenchmarkParameter::n, n)];
|
||||
let c = vec![
|
||||
(frame_benchmarking::BenchmarkParameter::v, v),
|
||||
(frame_benchmarking::BenchmarkParameter::n, n),
|
||||
];
|
||||
let closure_to_benchmark =
|
||||
<SelectedBenchmark as frame_benchmarking::BenchmarkingSetup<Test>>::instance(
|
||||
&selected_benchmark,
|
||||
&c,
|
||||
true
|
||||
).unwrap();
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_ok!(closure_to_benchmark());
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
//! The staking rate in NPoS is the total amount of tokens staked by nominators and validators,
|
||||
//! divided by the total token supply.
|
||||
|
||||
use sp_runtime::{Perbill, traits::AtLeast32BitUnsigned, curve::PiecewiseLinear};
|
||||
use sp_runtime::{curve::PiecewiseLinear, traits::AtLeast32BitUnsigned, Perbill};
|
||||
|
||||
/// The total payout to all validators (and their nominators) per era and maximum payout.
|
||||
///
|
||||
@@ -33,16 +33,18 @@ pub fn compute_total_payout<N>(
|
||||
yearly_inflation: &PiecewiseLinear<'static>,
|
||||
npos_token_staked: N,
|
||||
total_tokens: N,
|
||||
era_duration: u64
|
||||
) -> (N, N) where N: AtLeast32BitUnsigned + Clone {
|
||||
era_duration: u64,
|
||||
) -> (N, N)
|
||||
where
|
||||
N: AtLeast32BitUnsigned + Clone,
|
||||
{
|
||||
// Milliseconds per year for the Julian year (365.25 days).
|
||||
const MILLISECONDS_PER_YEAR: u64 = 1000 * 3600 * 24 * 36525 / 100;
|
||||
|
||||
let portion = Perbill::from_rational(era_duration as u64, MILLISECONDS_PER_YEAR);
|
||||
let payout = portion * yearly_inflation.calculate_for_fraction_times_denominator(
|
||||
npos_token_staked,
|
||||
total_tokens.clone(),
|
||||
);
|
||||
let payout = portion *
|
||||
yearly_inflation
|
||||
.calculate_for_fraction_times_denominator(npos_token_staked, total_tokens.clone());
|
||||
let maximum = portion * (yearly_inflation.maximum * total_tokens);
|
||||
(payout, maximum)
|
||||
}
|
||||
@@ -70,7 +72,7 @@ mod test {
|
||||
// not 10_000 due to rounding error.
|
||||
assert_eq!(super::compute_total_payout(&I_NPOS, 0, 100_000u64, YEAR).1, 9_993);
|
||||
|
||||
//super::I_NPOS.calculate_for_fraction_times_denominator(25, 100)
|
||||
// super::I_NPOS.calculate_for_fraction_times_denominator(25, 100)
|
||||
assert_eq!(super::compute_total_payout(&I_NPOS, 0, 100_000u64, YEAR).0, 2_498);
|
||||
assert_eq!(super::compute_total_payout(&I_NPOS, 5_000, 100_000u64, YEAR).0, 3_248);
|
||||
assert_eq!(super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, YEAR).0, 6_246);
|
||||
@@ -98,7 +100,8 @@ mod test {
|
||||
2_500_000_000_000_000_000_000_000_000u128,
|
||||
5_000_000_000_000_000_000_000_000_000u128,
|
||||
HOUR
|
||||
).0,
|
||||
)
|
||||
.0,
|
||||
57_038_500_000_000_000_000_000
|
||||
);
|
||||
}
|
||||
|
||||
+304
-285
File diff suppressed because it is too large
Load Diff
@@ -17,8 +17,9 @@
|
||||
|
||||
//! Test utilities
|
||||
|
||||
use crate::*;
|
||||
use crate as staking;
|
||||
use crate::*;
|
||||
use frame_election_provider_support::onchain;
|
||||
use frame_support::{
|
||||
assert_ok, parameter_types,
|
||||
traits::{Currency, FindAuthor, Get, OnInitialize, OneSessionHandler},
|
||||
@@ -33,7 +34,6 @@ use sp_runtime::{
|
||||
};
|
||||
use sp_staking::offence::{OffenceDetails, OnOffenceHandler};
|
||||
use std::{cell::RefCell, collections::HashSet};
|
||||
use frame_election_provider_support::onchain;
|
||||
|
||||
pub const INIT_TIMESTAMP: u64 = 30_000;
|
||||
pub const BLOCK_TIME: u64 = 1000;
|
||||
@@ -54,16 +54,19 @@ impl OneSessionHandler<AccountId> for OtherSessionHandler {
|
||||
type Key = UintAuthorityId;
|
||||
|
||||
fn on_genesis_session<'a, I: 'a>(_: I)
|
||||
where I: Iterator<Item=(&'a AccountId, Self::Key)>, AccountId: 'a {}
|
||||
where
|
||||
I: Iterator<Item = (&'a AccountId, Self::Key)>,
|
||||
AccountId: 'a,
|
||||
{
|
||||
}
|
||||
|
||||
fn on_new_session<'a, I: 'a>(_: bool, validators: I, _: I,)
|
||||
where I: Iterator<Item=(&'a AccountId, Self::Key)>, AccountId: 'a
|
||||
fn on_new_session<'a, I: 'a>(_: bool, validators: I, _: I)
|
||||
where
|
||||
I: Iterator<Item = (&'a AccountId, Self::Key)>,
|
||||
AccountId: 'a,
|
||||
{
|
||||
SESSION.with(|x| {
|
||||
*x.borrow_mut() = (
|
||||
validators.map(|x| x.0.clone()).collect(),
|
||||
HashSet::new(),
|
||||
)
|
||||
*x.borrow_mut() = (validators.map(|x| x.0.clone()).collect(), HashSet::new())
|
||||
});
|
||||
}
|
||||
|
||||
@@ -107,7 +110,8 @@ frame_support::construct_runtime!(
|
||||
pub struct Author11;
|
||||
impl FindAuthor<AccountId> for Author11 {
|
||||
fn find_author<'a, I>(_digests: I) -> Option<AccountId>
|
||||
where I: 'a + IntoIterator<Item = (frame_support::ConsensusEngineId, &'a [u8])>,
|
||||
where
|
||||
I: 'a + IntoIterator<Item = (frame_support::ConsensusEngineId, &'a [u8])>,
|
||||
{
|
||||
Some(11)
|
||||
}
|
||||
@@ -376,21 +380,14 @@ impl ExtBuilder {
|
||||
}
|
||||
fn build(self) -> sp_io::TestExternalities {
|
||||
sp_tracing::try_init_simple();
|
||||
let mut storage = frame_system::GenesisConfig::default()
|
||||
.build_storage::<Test>()
|
||||
.unwrap();
|
||||
let balance_factor = if ExistentialDeposit::get() > 1 {
|
||||
256
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let mut storage = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||
let balance_factor = if ExistentialDeposit::get() > 1 { 256 } else { 1 };
|
||||
|
||||
let num_validators = self.num_validators.unwrap_or(self.validator_count);
|
||||
// Check that the number of validators is sensible.
|
||||
assert!(num_validators <= 8);
|
||||
let validators = (0..num_validators)
|
||||
.map(|x| ((x + 1) * 10 + 1) as AccountId)
|
||||
.collect::<Vec<_>>();
|
||||
let validators =
|
||||
(0..num_validators).map(|x| ((x + 1) * 10 + 1) as AccountId).collect::<Vec<_>>();
|
||||
|
||||
let _ = pallet_balances::GenesisConfig::<Test> {
|
||||
balances: vec![
|
||||
@@ -419,7 +416,8 @@ impl ExtBuilder {
|
||||
// This allows us to have a total_payout different from 0.
|
||||
(999, 1_000_000_000_000),
|
||||
],
|
||||
}.assimilate_storage(&mut storage);
|
||||
}
|
||||
.assimilate_storage(&mut storage);
|
||||
|
||||
let mut stakers = vec![];
|
||||
if self.has_stakers {
|
||||
@@ -438,11 +436,11 @@ impl ExtBuilder {
|
||||
(31, 30, stake_31, StakerStatus::<AccountId>::Validator),
|
||||
(41, 40, balance_factor * 1000, status_41),
|
||||
// nominator
|
||||
(101, 100, balance_factor * 500, StakerStatus::<AccountId>::Nominator(nominated))
|
||||
(101, 100, balance_factor * 500, StakerStatus::<AccountId>::Nominator(nominated)),
|
||||
];
|
||||
}
|
||||
let _ = staking::GenesisConfig::<Test>{
|
||||
stakers: stakers,
|
||||
let _ = staking::GenesisConfig::<Test> {
|
||||
stakers,
|
||||
validator_count: self.validator_count,
|
||||
minimum_validator_count: self.minimum_validator_count,
|
||||
invulnerables: self.invulnerables,
|
||||
@@ -454,12 +452,12 @@ impl ExtBuilder {
|
||||
.assimilate_storage(&mut storage);
|
||||
|
||||
let _ = pallet_session::GenesisConfig::<Test> {
|
||||
keys: validators.iter().map(|x| (
|
||||
*x,
|
||||
*x,
|
||||
SessionKeys { other: UintAuthorityId(*x as u64) }
|
||||
)).collect(),
|
||||
}.assimilate_storage(&mut storage);
|
||||
keys: validators
|
||||
.iter()
|
||||
.map(|x| (*x, *x, SessionKeys { other: UintAuthorityId(*x as u64) }))
|
||||
.collect(),
|
||||
}
|
||||
.assimilate_storage(&mut storage);
|
||||
|
||||
let mut ext = sp_io::TestExternalities::from(storage);
|
||||
ext.execute_with(|| {
|
||||
@@ -524,42 +522,46 @@ fn check_nominators() {
|
||||
// in if the nomination was submitted before the current era.
|
||||
let era = active_era();
|
||||
<Nominators<Test>>::iter()
|
||||
.filter_map(|(nominator, nomination)|
|
||||
if nomination.submitted_in > era {
|
||||
Some(nominator)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.filter_map(
|
||||
|(nominator, nomination)| {
|
||||
if nomination.submitted_in > era {
|
||||
Some(nominator)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.for_each(|nominator| {
|
||||
// must be bonded.
|
||||
assert_is_stash(nominator);
|
||||
let mut sum = 0;
|
||||
Session::validators()
|
||||
.iter()
|
||||
.map(|v| Staking::eras_stakers(era, v))
|
||||
.for_each(|e| {
|
||||
let individual = e.others.iter().filter(|e| e.who == nominator).collect::<Vec<_>>();
|
||||
let len = individual.len();
|
||||
match len {
|
||||
0 => { /* not supporting this validator at all. */ },
|
||||
1 => sum += individual[0].value,
|
||||
_ => panic!("nominator cannot back a validator more than once."),
|
||||
};
|
||||
});
|
||||
// must be bonded.
|
||||
assert_is_stash(nominator);
|
||||
let mut sum = 0;
|
||||
Session::validators()
|
||||
.iter()
|
||||
.map(|v| Staking::eras_stakers(era, v))
|
||||
.for_each(|e| {
|
||||
let individual =
|
||||
e.others.iter().filter(|e| e.who == nominator).collect::<Vec<_>>();
|
||||
let len = individual.len();
|
||||
match len {
|
||||
0 => { /* not supporting this validator at all. */ },
|
||||
1 => sum += individual[0].value,
|
||||
_ => panic!("nominator cannot back a validator more than once."),
|
||||
};
|
||||
});
|
||||
|
||||
let nominator_stake = Staking::slashable_balance_of(&nominator);
|
||||
// a nominator cannot over-spend.
|
||||
assert!(
|
||||
nominator_stake >= sum,
|
||||
"failed: Nominator({}) stake({}) >= sum divided({})",
|
||||
nominator,
|
||||
nominator_stake,
|
||||
sum,
|
||||
);
|
||||
let nominator_stake = Staking::slashable_balance_of(&nominator);
|
||||
// a nominator cannot over-spend.
|
||||
assert!(
|
||||
nominator_stake >= sum,
|
||||
"failed: Nominator({}) stake({}) >= sum divided({})",
|
||||
nominator,
|
||||
nominator_stake,
|
||||
sum,
|
||||
);
|
||||
|
||||
let diff = nominator_stake - sum;
|
||||
assert!(diff < 100);
|
||||
});
|
||||
let diff = nominator_stake - sum;
|
||||
assert!(diff < 100);
|
||||
});
|
||||
}
|
||||
|
||||
fn assert_is_stash(acc: AccountId) {
|
||||
@@ -569,10 +571,7 @@ fn assert_is_stash(acc: AccountId) {
|
||||
fn assert_ledger_consistent(ctrl: AccountId) {
|
||||
// ensures ledger.total == ledger.active + sum(ledger.unlocking).
|
||||
let ledger = Staking::ledger(ctrl).expect("Not a controller.");
|
||||
let real_total: Balance = ledger
|
||||
.unlocking
|
||||
.iter()
|
||||
.fold(ledger.active, |a, c| a + c.value);
|
||||
let real_total: Balance = ledger.unlocking.iter().fold(ledger.active, |a, c| a + c.value);
|
||||
assert_eq!(real_total, ledger.total);
|
||||
assert!(
|
||||
ledger.active >= Balances::minimum_balance() || ledger.active == 0,
|
||||
@@ -594,16 +593,8 @@ pub(crate) fn current_era() -> EraIndex {
|
||||
pub(crate) fn bond_validator(stash: AccountId, ctrl: AccountId, val: Balance) {
|
||||
let _ = Balances::make_free_balance_be(&stash, val);
|
||||
let _ = Balances::make_free_balance_be(&ctrl, val);
|
||||
assert_ok!(Staking::bond(
|
||||
Origin::signed(stash),
|
||||
ctrl,
|
||||
val,
|
||||
RewardDestination::Controller,
|
||||
));
|
||||
assert_ok!(Staking::validate(
|
||||
Origin::signed(ctrl),
|
||||
ValidatorPrefs::default()
|
||||
));
|
||||
assert_ok!(Staking::bond(Origin::signed(stash), ctrl, val, RewardDestination::Controller,));
|
||||
assert_ok!(Staking::validate(Origin::signed(ctrl), ValidatorPrefs::default()));
|
||||
}
|
||||
|
||||
pub(crate) fn bond_nominator(
|
||||
@@ -614,12 +605,7 @@ pub(crate) fn bond_nominator(
|
||||
) {
|
||||
let _ = Balances::make_free_balance_be(&stash, val);
|
||||
let _ = Balances::make_free_balance_be(&ctrl, val);
|
||||
assert_ok!(Staking::bond(
|
||||
Origin::signed(stash),
|
||||
ctrl,
|
||||
val,
|
||||
RewardDestination::Controller,
|
||||
));
|
||||
assert_ok!(Staking::bond(Origin::signed(stash), ctrl, val, RewardDestination::Controller,));
|
||||
assert_ok!(Staking::nominate(Origin::signed(ctrl), target));
|
||||
}
|
||||
|
||||
@@ -715,9 +701,7 @@ pub(crate) fn reward_time_per_era() -> u64 {
|
||||
}
|
||||
|
||||
pub(crate) fn reward_all_elected() {
|
||||
let rewards = <Test as Config>::SessionInterface::validators()
|
||||
.into_iter()
|
||||
.map(|v| (v, 1));
|
||||
let rewards = <Test as Config>::SessionInterface::validators().into_iter().map(|v| (v, 1));
|
||||
|
||||
<Pallet<Test>>::reward_by_ids(rewards)
|
||||
}
|
||||
@@ -741,26 +725,28 @@ pub(crate) fn on_offence_in_era(
|
||||
for &(bonded_era, start_session) in bonded_eras.iter() {
|
||||
if bonded_era == era {
|
||||
let _ = Staking::on_offence(offenders, slash_fraction, start_session);
|
||||
return;
|
||||
return
|
||||
} else if bonded_era > era {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if Staking::active_era().unwrap().index == era {
|
||||
let _ =
|
||||
Staking::on_offence(
|
||||
offenders,
|
||||
slash_fraction,
|
||||
Staking::eras_start_session_index(era).unwrap()
|
||||
);
|
||||
let _ = Staking::on_offence(
|
||||
offenders,
|
||||
slash_fraction,
|
||||
Staking::eras_start_session_index(era).unwrap(),
|
||||
);
|
||||
} else {
|
||||
panic!("cannot slash in era {}", era);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn on_offence_now(
|
||||
offenders: &[OffenceDetails<AccountId, pallet_session::historical::IdentificationTuple<Test>>],
|
||||
offenders: &[OffenceDetails<
|
||||
AccountId,
|
||||
pallet_session::historical::IdentificationTuple<Test>,
|
||||
>],
|
||||
slash_fraction: &[Perbill],
|
||||
) {
|
||||
let now = Staking::active_era().unwrap().index;
|
||||
@@ -769,29 +755,26 @@ pub(crate) fn on_offence_now(
|
||||
|
||||
pub(crate) fn add_slash(who: &AccountId) {
|
||||
on_offence_now(
|
||||
&[
|
||||
OffenceDetails {
|
||||
offender: (who.clone(), Staking::eras_stakers(active_era(), who.clone())),
|
||||
reporters: vec![],
|
||||
},
|
||||
],
|
||||
&[OffenceDetails {
|
||||
offender: (who.clone(), Staking::eras_stakers(active_era(), who.clone())),
|
||||
reporters: vec![],
|
||||
}],
|
||||
&[Perbill::from_percent(10)],
|
||||
);
|
||||
}
|
||||
|
||||
/// Make all validator and nominator request their payment
|
||||
pub(crate) fn make_all_reward_payment(era: EraIndex) {
|
||||
let validators_with_reward =
|
||||
ErasRewardPoints::<Test>::get(era).individual.keys().cloned().collect::<Vec<_>>();
|
||||
let validators_with_reward = ErasRewardPoints::<Test>::get(era)
|
||||
.individual
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// reward validators
|
||||
for validator_controller in validators_with_reward.iter().filter_map(Staking::bonded) {
|
||||
let ledger = <Ledger<Test>>::get(&validator_controller).unwrap();
|
||||
assert_ok!(Staking::payout_stakers(
|
||||
Origin::signed(1337),
|
||||
ledger.stash,
|
||||
era
|
||||
));
|
||||
assert_ok!(Staking::payout_stakers(Origin::signed(1337), ledger.stash, era));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -816,13 +799,11 @@ macro_rules! assert_session_era {
|
||||
}
|
||||
|
||||
pub(crate) fn staking_events() -> Vec<staking::Event<Test>> {
|
||||
System::events().into_iter().map(|r| r.event).filter_map(|e| {
|
||||
if let Event::Staking(inner) = e {
|
||||
Some(inner)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect()
|
||||
System::events()
|
||||
.into_iter()
|
||||
.map(|r| r.event)
|
||||
.filter_map(|e| if let Event::Staking(inner) = e { Some(inner) } else { None })
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn balances(who: &AccountId) -> (Balance, Balance) {
|
||||
|
||||
@@ -50,16 +50,19 @@
|
||||
//! Based on research at <https://w3f-research.readthedocs.io/en/latest/polkadot/slashing/npos.html>
|
||||
|
||||
use super::{
|
||||
EraIndex, Config, Pallet, Store, BalanceOf, Exposure, Perbill, SessionInterface,
|
||||
NegativeImbalanceOf, UnappliedSlash, Error,
|
||||
BalanceOf, Config, EraIndex, Error, Exposure, NegativeImbalanceOf, Pallet, Perbill,
|
||||
SessionInterface, Store, UnappliedSlash,
|
||||
};
|
||||
use sp_runtime::{traits::{Zero, Saturating}, RuntimeDebug, DispatchResult};
|
||||
use codec::{Decode, Encode};
|
||||
use frame_support::{
|
||||
ensure,
|
||||
traits::{Currency, OnUnbalanced, Imbalance},
|
||||
traits::{Currency, Imbalance, OnUnbalanced},
|
||||
};
|
||||
use sp_runtime::{
|
||||
traits::{Saturating, Zero},
|
||||
DispatchResult, RuntimeDebug,
|
||||
};
|
||||
use sp_std::vec::Vec;
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
/// The proportion of the slashing reward to be paid out on the first slashing detection.
|
||||
/// This is f_1 in the paper.
|
||||
@@ -118,7 +121,9 @@ impl SlashingSpans {
|
||||
// that internal state is unchanged.
|
||||
pub(crate) fn end_span(&mut self, now: EraIndex) -> bool {
|
||||
let next_start = now + 1;
|
||||
if next_start <= self.last_start { return false }
|
||||
if next_start <= self.last_start {
|
||||
return false
|
||||
}
|
||||
|
||||
let last_length = next_start - self.last_start;
|
||||
self.prior.insert(0, last_length);
|
||||
@@ -153,7 +158,8 @@ impl SlashingSpans {
|
||||
// If this returns `Some`, then it includes a range start..end of all the span
|
||||
// indices which were pruned.
|
||||
fn prune(&mut self, window_start: EraIndex) -> Option<(SpanIndex, SpanIndex)> {
|
||||
let old_idx = self.iter()
|
||||
let old_idx = self
|
||||
.iter()
|
||||
.skip(1) // skip ongoing span.
|
||||
.position(|span| span.length.map_or(false, |len| span.start + len <= window_start));
|
||||
|
||||
@@ -163,7 +169,7 @@ impl SlashingSpans {
|
||||
self.prior.truncate(o);
|
||||
let new_earliest = self.span_index - self.prior.len() as SpanIndex;
|
||||
Some((earliest_span_index, new_earliest))
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
@@ -214,18 +220,11 @@ pub(crate) struct SlashParams<'a, T: 'a + Config> {
|
||||
///
|
||||
/// The pending slash record returned does not have initialized reporters. Those have
|
||||
/// to be set at a higher level, if any.
|
||||
pub(crate) fn compute_slash<T: Config>(params: SlashParams<T>)
|
||||
-> Option<UnappliedSlash<T::AccountId, BalanceOf<T>>>
|
||||
{
|
||||
let SlashParams {
|
||||
stash,
|
||||
slash,
|
||||
exposure,
|
||||
slash_era,
|
||||
window_start,
|
||||
now,
|
||||
reward_proportion,
|
||||
} = params.clone();
|
||||
pub(crate) fn compute_slash<T: Config>(
|
||||
params: SlashParams<T>,
|
||||
) -> Option<UnappliedSlash<T::AccountId, BalanceOf<T>>> {
|
||||
let SlashParams { stash, slash, exposure, slash_era, window_start, now, reward_proportion } =
|
||||
params.clone();
|
||||
|
||||
let mut reward_payout = Zero::zero();
|
||||
let mut val_slashed = Zero::zero();
|
||||
@@ -236,22 +235,17 @@ pub(crate) fn compute_slash<T: Config>(params: SlashParams<T>)
|
||||
// kick out the validator even if they won't be slashed,
|
||||
// as long as the misbehavior is from their most recent slashing span.
|
||||
kick_out_if_recent::<T>(params);
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
|
||||
let (prior_slash_p, _era_slash) = <Pallet<T> as Store>::ValidatorSlashInEra::get(
|
||||
&slash_era,
|
||||
stash,
|
||||
).unwrap_or((Perbill::zero(), Zero::zero()));
|
||||
let (prior_slash_p, _era_slash) =
|
||||
<Pallet<T> as Store>::ValidatorSlashInEra::get(&slash_era, stash)
|
||||
.unwrap_or((Perbill::zero(), Zero::zero()));
|
||||
|
||||
// compare slash proportions rather than slash values to avoid issues due to rounding
|
||||
// error.
|
||||
if slash.deconstruct() > prior_slash_p.deconstruct() {
|
||||
<Pallet<T> as Store>::ValidatorSlashInEra::insert(
|
||||
&slash_era,
|
||||
stash,
|
||||
&(slash, own_slash),
|
||||
);
|
||||
<Pallet<T> as Store>::ValidatorSlashInEra::insert(&slash_era, stash, &(slash, own_slash));
|
||||
} else {
|
||||
// we slash based on the max in era - this new event is not the max,
|
||||
// so neither the validator or any nominators will need an update.
|
||||
@@ -260,7 +254,7 @@ pub(crate) fn compute_slash<T: Config>(params: SlashParams<T>)
|
||||
// pays out some reward even if the latest report is not max-in-era.
|
||||
// we opt to avoid the nominator lookups and edits and leave more rewards
|
||||
// for more drastic misbehavior.
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
|
||||
// apply slash to validator.
|
||||
@@ -273,10 +267,7 @@ pub(crate) fn compute_slash<T: Config>(params: SlashParams<T>)
|
||||
reward_proportion,
|
||||
);
|
||||
|
||||
let target_span = spans.compare_and_update_span_slash(
|
||||
slash_era,
|
||||
own_slash,
|
||||
);
|
||||
let target_span = spans.compare_and_update_span_slash(slash_era, own_slash);
|
||||
|
||||
if target_span == Some(spans.span_index()) {
|
||||
// misbehavior occurred within the current slashing span - take appropriate
|
||||
@@ -309,9 +300,7 @@ pub(crate) fn compute_slash<T: Config>(params: SlashParams<T>)
|
||||
|
||||
// doesn't apply any slash, but kicks out the validator if the misbehavior is from
|
||||
// the most recent slashing span.
|
||||
fn kick_out_if_recent<T: Config>(
|
||||
params: SlashParams<T>,
|
||||
) {
|
||||
fn kick_out_if_recent<T: Config>(params: SlashParams<T>) {
|
||||
// these are not updated by era-span or end-span.
|
||||
let mut reward_payout = Zero::zero();
|
||||
let mut val_slashed = Zero::zero();
|
||||
@@ -343,15 +332,8 @@ fn slash_nominators<T: Config>(
|
||||
prior_slash_p: Perbill,
|
||||
nominators_slashed: &mut Vec<(T::AccountId, BalanceOf<T>)>,
|
||||
) -> BalanceOf<T> {
|
||||
let SlashParams {
|
||||
stash: _,
|
||||
slash,
|
||||
exposure,
|
||||
slash_era,
|
||||
window_start,
|
||||
now,
|
||||
reward_proportion,
|
||||
} = params;
|
||||
let SlashParams { stash: _, slash, exposure, slash_era, window_start, now, reward_proportion } =
|
||||
params;
|
||||
|
||||
let mut reward_payout = Zero::zero();
|
||||
|
||||
@@ -367,18 +349,12 @@ fn slash_nominators<T: Config>(
|
||||
let own_slash_by_validator = slash * nominator.value;
|
||||
let own_slash_difference = own_slash_by_validator.saturating_sub(own_slash_prior);
|
||||
|
||||
let mut era_slash = <Pallet<T> as Store>::NominatorSlashInEra::get(
|
||||
&slash_era,
|
||||
stash,
|
||||
).unwrap_or_else(|| Zero::zero());
|
||||
let mut era_slash = <Pallet<T> as Store>::NominatorSlashInEra::get(&slash_era, stash)
|
||||
.unwrap_or_else(|| Zero::zero());
|
||||
|
||||
era_slash += own_slash_difference;
|
||||
|
||||
<Pallet<T> as Store>::NominatorSlashInEra::insert(
|
||||
&slash_era,
|
||||
stash,
|
||||
&era_slash,
|
||||
);
|
||||
<Pallet<T> as Store>::NominatorSlashInEra::insert(&slash_era, stash, &era_slash);
|
||||
|
||||
era_slash
|
||||
};
|
||||
@@ -393,10 +369,7 @@ fn slash_nominators<T: Config>(
|
||||
reward_proportion,
|
||||
);
|
||||
|
||||
let target_span = spans.compare_and_update_span_slash(
|
||||
slash_era,
|
||||
era_slash,
|
||||
);
|
||||
let target_span = spans.compare_and_update_span_slash(slash_era, era_slash);
|
||||
|
||||
if target_span == Some(spans.span_index()) {
|
||||
// End the span, but don't chill the nominator. its nomination
|
||||
@@ -497,8 +470,8 @@ impl<'a, T: 'a + Config> InspectingSpans<'a, T> {
|
||||
span_record.slashed = slash;
|
||||
|
||||
// compute reward.
|
||||
let reward = REWARD_F1
|
||||
* (self.reward_proportion * slash).saturating_sub(span_record.paid_out);
|
||||
let reward =
|
||||
REWARD_F1 * (self.reward_proportion * slash).saturating_sub(span_record.paid_out);
|
||||
|
||||
self.add_slash(difference, slash_era);
|
||||
changed = true;
|
||||
@@ -529,7 +502,9 @@ impl<'a, T: 'a + Config> InspectingSpans<'a, T> {
|
||||
impl<'a, T: 'a + Config> Drop for InspectingSpans<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
// only update on disk if we slashed this account.
|
||||
if !self.dirty { return }
|
||||
if !self.dirty {
|
||||
return
|
||||
}
|
||||
|
||||
if let Some((start, end)) = self.spans.prune(self.window_start) {
|
||||
for span_index in start..end {
|
||||
@@ -557,7 +532,10 @@ pub(crate) fn clear_stash_metadata<T: Config>(
|
||||
Some(s) => s,
|
||||
};
|
||||
|
||||
ensure!(num_slashing_spans as usize >= spans.iter().count(), Error::<T>::IncorrectSlashingSpans);
|
||||
ensure!(
|
||||
num_slashing_spans as usize >= spans.iter().count(),
|
||||
Error::<T>::IncorrectSlashingSpans
|
||||
);
|
||||
|
||||
<Pallet<T> as Store>::SlashingSpans::remove(stash);
|
||||
|
||||
@@ -606,9 +584,7 @@ pub fn do_slash<T: Config>(
|
||||
<Pallet<T>>::update_ledger(&controller, &ledger);
|
||||
|
||||
// trigger the event
|
||||
<Pallet<T>>::deposit_event(
|
||||
super::Event::<T>::Slash(stash.clone(), value)
|
||||
);
|
||||
<Pallet<T>>::deposit_event(super::Event::<T>::Slash(stash.clone(), value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,18 +601,12 @@ pub(crate) fn apply_slash<T: Config>(unapplied_slash: UnappliedSlash<T::AccountI
|
||||
);
|
||||
|
||||
for &(ref nominator, nominator_slash) in &unapplied_slash.others {
|
||||
do_slash::<T>(
|
||||
&nominator,
|
||||
nominator_slash,
|
||||
&mut reward_payout,
|
||||
&mut slashed_imbalance,
|
||||
);
|
||||
do_slash::<T>(&nominator, nominator_slash, &mut reward_payout, &mut slashed_imbalance);
|
||||
}
|
||||
|
||||
pay_reporters::<T>(reward_payout, slashed_imbalance, &unapplied_slash.reporters);
|
||||
}
|
||||
|
||||
|
||||
/// Apply a reward payout to some reporters, paying the rewards out of the slashed imbalance.
|
||||
fn pay_reporters<T: Config>(
|
||||
reward_payout: BalanceOf<T>,
|
||||
@@ -774,17 +744,13 @@ mod tests {
|
||||
assert_eq!(spans.prune(1000), Some((8, 10)));
|
||||
assert_eq!(
|
||||
spans.iter().collect::<Vec<_>>(),
|
||||
vec![
|
||||
SlashingSpan { index: 10, start: 1000, length: None },
|
||||
],
|
||||
vec![SlashingSpan { index: 10, start: 1000, length: None },],
|
||||
);
|
||||
|
||||
assert_eq!(spans.prune(2000), None);
|
||||
assert_eq!(
|
||||
spans.iter().collect::<Vec<_>>(),
|
||||
vec![
|
||||
SlashingSpan { index: 10, start: 2000, length: None },
|
||||
],
|
||||
vec![SlashingSpan { index: 10, start: 2000, length: None },],
|
||||
);
|
||||
|
||||
// now all in one shot.
|
||||
@@ -797,9 +763,7 @@ mod tests {
|
||||
assert_eq!(spans.prune(2000), Some((6, 10)));
|
||||
assert_eq!(
|
||||
spans.iter().collect::<Vec<_>>(),
|
||||
vec![
|
||||
SlashingSpan { index: 10, start: 2000, length: None },
|
||||
],
|
||||
vec![SlashingSpan { index: 10, start: 2000, length: None },],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,14 @@
|
||||
//! Testing utils for staking. Provides some common functions to setup staking state, such as
|
||||
//! bonding validators, nominators, and generating different types of solutions.
|
||||
|
||||
use crate::*;
|
||||
use crate::Pallet as Staking;
|
||||
use crate::{Pallet as Staking, *};
|
||||
use frame_benchmarking::account;
|
||||
use frame_system::RawOrigin;
|
||||
use rand_chacha::{
|
||||
rand_core::{RngCore, SeedableRng},
|
||||
ChaChaRng,
|
||||
};
|
||||
use sp_io::hashing::blake2_256;
|
||||
use rand_chacha::{rand_core::{RngCore, SeedableRng}, ChaChaRng};
|
||||
|
||||
const SEED: u32 = 0;
|
||||
|
||||
@@ -54,14 +56,18 @@ pub fn create_stash_controller<T: Config>(
|
||||
n: u32,
|
||||
balance_factor: u32,
|
||||
destination: RewardDestination<T::AccountId>,
|
||||
)
|
||||
-> Result<(T::AccountId, T::AccountId), &'static str>
|
||||
{
|
||||
) -> Result<(T::AccountId, T::AccountId), &'static str> {
|
||||
let stash = create_funded_user::<T>("stash", n, balance_factor);
|
||||
let controller = create_funded_user::<T>("controller", n, balance_factor);
|
||||
let controller_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(controller.clone());
|
||||
let controller_lookup: <T::Lookup as StaticLookup>::Source =
|
||||
T::Lookup::unlookup(controller.clone());
|
||||
let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into();
|
||||
Staking::<T>::bond(RawOrigin::Signed(stash.clone()).into(), controller_lookup, amount, destination)?;
|
||||
Staking::<T>::bond(
|
||||
RawOrigin::Signed(stash.clone()).into(),
|
||||
controller_lookup,
|
||||
amount,
|
||||
destination,
|
||||
)?;
|
||||
return Ok((stash, controller))
|
||||
}
|
||||
|
||||
@@ -71,15 +77,19 @@ pub fn create_stash_and_dead_controller<T: Config>(
|
||||
n: u32,
|
||||
balance_factor: u32,
|
||||
destination: RewardDestination<T::AccountId>,
|
||||
)
|
||||
-> Result<(T::AccountId, T::AccountId), &'static str>
|
||||
{
|
||||
) -> Result<(T::AccountId, T::AccountId), &'static str> {
|
||||
let stash = create_funded_user::<T>("stash", n, balance_factor);
|
||||
// controller has no funds
|
||||
let controller = create_funded_user::<T>("controller", n, 0);
|
||||
let controller_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(controller.clone());
|
||||
let controller_lookup: <T::Lookup as StaticLookup>::Source =
|
||||
T::Lookup::unlookup(controller.clone());
|
||||
let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into();
|
||||
Staking::<T>::bond(RawOrigin::Signed(stash.clone()).into(), controller_lookup, amount, destination)?;
|
||||
Staking::<T>::bond(
|
||||
RawOrigin::Signed(stash.clone()).into(),
|
||||
controller_lookup,
|
||||
amount,
|
||||
destination,
|
||||
)?;
|
||||
return Ok((stash, controller))
|
||||
}
|
||||
|
||||
@@ -89,12 +99,11 @@ pub fn create_validators<T: Config>(
|
||||
balance_factor: u32,
|
||||
) -> Result<Vec<<T::Lookup as StaticLookup>::Source>, &'static str> {
|
||||
let mut validators: Vec<<T::Lookup as StaticLookup>::Source> = Vec::with_capacity(max as usize);
|
||||
for i in 0 .. max {
|
||||
let (stash, controller) = create_stash_controller::<T>(i, balance_factor, RewardDestination::Staked)?;
|
||||
let validator_prefs = ValidatorPrefs {
|
||||
commission: Perbill::from_percent(50),
|
||||
.. Default::default()
|
||||
};
|
||||
for i in 0..max {
|
||||
let (stash, controller) =
|
||||
create_stash_controller::<T>(i, balance_factor, RewardDestination::Staked)?;
|
||||
let validator_prefs =
|
||||
ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() };
|
||||
Staking::<T>::validate(RawOrigin::Signed(controller).into(), validator_prefs)?;
|
||||
let stash_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(stash);
|
||||
validators.push(stash_lookup);
|
||||
@@ -126,20 +135,20 @@ pub fn create_validators_with_nominators_for_era<T: Config>(
|
||||
) -> Result<Vec<<T::Lookup as StaticLookup>::Source>, &'static str> {
|
||||
clear_validators_and_nominators::<T>();
|
||||
|
||||
let mut validators_stash: Vec<<T::Lookup as StaticLookup>::Source>
|
||||
= Vec::with_capacity(validators as usize);
|
||||
let mut validators_stash: Vec<<T::Lookup as StaticLookup>::Source> =
|
||||
Vec::with_capacity(validators as usize);
|
||||
let mut rng = ChaChaRng::from_seed(SEED.using_encoded(blake2_256));
|
||||
|
||||
// Create validators
|
||||
for i in 0 .. validators {
|
||||
for i in 0..validators {
|
||||
let balance_factor = if randomize_stake { rng.next_u32() % 255 + 10 } else { 100u32 };
|
||||
let (v_stash, v_controller) = create_stash_controller::<T>(i, balance_factor, RewardDestination::Staked)?;
|
||||
let validator_prefs = ValidatorPrefs {
|
||||
commission: Perbill::from_percent(50),
|
||||
.. Default::default()
|
||||
};
|
||||
let (v_stash, v_controller) =
|
||||
create_stash_controller::<T>(i, balance_factor, RewardDestination::Staked)?;
|
||||
let validator_prefs =
|
||||
ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() };
|
||||
Staking::<T>::validate(RawOrigin::Signed(v_controller.clone()).into(), validator_prefs)?;
|
||||
let stash_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(v_stash.clone());
|
||||
let stash_lookup: <T::Lookup as StaticLookup>::Source =
|
||||
T::Lookup::unlookup(v_stash.clone());
|
||||
validators_stash.push(stash_lookup.clone());
|
||||
}
|
||||
|
||||
@@ -147,25 +156,25 @@ pub fn create_validators_with_nominators_for_era<T: Config>(
|
||||
let validator_chosen = validators_stash[0..to_nominate].to_vec();
|
||||
|
||||
// Create nominators
|
||||
for j in 0 .. nominators {
|
||||
for j in 0..nominators {
|
||||
let balance_factor = if randomize_stake { rng.next_u32() % 255 + 10 } else { 100u32 };
|
||||
let (_n_stash, n_controller) = create_stash_controller::<T>(
|
||||
u32::MAX - j,
|
||||
balance_factor,
|
||||
RewardDestination::Staked,
|
||||
)?;
|
||||
let (_n_stash, n_controller) =
|
||||
create_stash_controller::<T>(u32::MAX - j, balance_factor, RewardDestination::Staked)?;
|
||||
|
||||
// Have them randomly validate
|
||||
let mut available_validators = validator_chosen.clone();
|
||||
let mut selected_validators: Vec<<T::Lookup as StaticLookup>::Source> =
|
||||
Vec::with_capacity(edge_per_nominator);
|
||||
|
||||
for _ in 0 .. validators.min(edge_per_nominator as u32) {
|
||||
for _ in 0..validators.min(edge_per_nominator as u32) {
|
||||
let selected = rng.next_u32() as usize % available_validators.len();
|
||||
let validator = available_validators.remove(selected);
|
||||
selected_validators.push(validator);
|
||||
}
|
||||
Staking::<T>::nominate(RawOrigin::Signed(n_controller.clone()).into(), selected_validators)?;
|
||||
Staking::<T>::nominate(
|
||||
RawOrigin::Signed(n_controller.clone()).into(),
|
||||
selected_validators,
|
||||
)?;
|
||||
}
|
||||
|
||||
ValidatorCount::<T>::put(validators);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@
|
||||
// --template=./.maintain/frame-weight-template.hbs
|
||||
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user