mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 04:41:03 +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:
@@ -18,7 +18,7 @@
|
||||
//! Two phase election pallet benchmarking.
|
||||
|
||||
use super::*;
|
||||
use crate::{Pallet as MultiPhase, unsigned::IndexAssignmentOf};
|
||||
use crate::{unsigned::IndexAssignmentOf, Pallet as MultiPhase};
|
||||
use frame_benchmarking::{account, impl_benchmark_test_suite};
|
||||
use frame_support::{assert_ok, traits::Hooks};
|
||||
use frame_system::RawOrigin;
|
||||
@@ -53,8 +53,9 @@ fn solution_with_size<T: Config>(
|
||||
let stake: VoteWeight = ed.max(One::one()).saturating_mul(100);
|
||||
|
||||
// first generates random targets.
|
||||
let targets: Vec<T::AccountId> =
|
||||
(0..size.targets).map(|i| frame_benchmarking::account("Targets", i, SEED)).collect();
|
||||
let targets: Vec<T::AccountId> = (0..size.targets)
|
||||
.map(|i| frame_benchmarking::account("Targets", i, SEED))
|
||||
.collect();
|
||||
|
||||
let mut rng = SmallRng::seed_from_u64(SEED.into());
|
||||
|
||||
@@ -80,8 +81,11 @@ fn solution_with_size<T: Config>(
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// rest of the voters. They can only vote for non-winners.
|
||||
let non_winners =
|
||||
targets.iter().filter(|t| !winners.contains(t)).cloned().collect::<Vec<T::AccountId>>();
|
||||
let non_winners = targets
|
||||
.iter()
|
||||
.filter(|t| !winners.contains(t))
|
||||
.cloned()
|
||||
.collect::<Vec<T::AccountId>>();
|
||||
let rest_voters = (active_voters_count..size.voters)
|
||||
.map(|i| {
|
||||
let votes = (&non_winners)
|
||||
@@ -147,14 +151,22 @@ fn set_up_data_provider<T: Config>(v: u32, t: u32) {
|
||||
// number of votes in snapshot.
|
||||
|
||||
T::DataProvider::clear();
|
||||
log!(info, "setting up with voters = {} [degree = {}], targets = {}", v, T::DataProvider::MAXIMUM_VOTES_PER_VOTER, t);
|
||||
log!(
|
||||
info,
|
||||
"setting up with voters = {} [degree = {}], targets = {}",
|
||||
v,
|
||||
T::DataProvider::MAXIMUM_VOTES_PER_VOTER,
|
||||
t
|
||||
);
|
||||
|
||||
// fill targets.
|
||||
let mut targets = (0..t).map(|i| {
|
||||
let target = frame_benchmarking::account::<T::AccountId>("Target", i, SEED);
|
||||
T::DataProvider::add_target(target.clone());
|
||||
target
|
||||
}).collect::<Vec<_>>();
|
||||
let mut targets = (0..t)
|
||||
.map(|i| {
|
||||
let target = frame_benchmarking::account::<T::AccountId>("Target", i, SEED);
|
||||
T::DataProvider::add_target(target.clone());
|
||||
target
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
// we should always have enough voters to fill.
|
||||
assert!(targets.len() > T::DataProvider::MAXIMUM_VOTES_PER_VOTER as usize);
|
||||
targets.truncate(T::DataProvider::MAXIMUM_VOTES_PER_VOTER as usize);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
//! Some helper functions/macros for this crate.
|
||||
|
||||
use super::{Config, VoteWeight, CompactVoterIndexOf, CompactTargetIndexOf};
|
||||
use super::{CompactTargetIndexOf, CompactVoterIndexOf, Config, VoteWeight};
|
||||
use sp_std::{collections::btree_map::BTreeMap, convert::TryInto, prelude::*};
|
||||
|
||||
#[macro_export]
|
||||
@@ -58,7 +58,9 @@ pub fn voter_index_fn<T: Config>(
|
||||
cache: &BTreeMap<T::AccountId, usize>,
|
||||
) -> impl Fn(&T::AccountId) -> Option<CompactVoterIndexOf<T>> + '_ {
|
||||
move |who| {
|
||||
cache.get(who).and_then(|i| <usize as TryInto<CompactVoterIndexOf<T>>>::try_into(*i).ok())
|
||||
cache
|
||||
.get(who)
|
||||
.and_then(|i| <usize as TryInto<CompactVoterIndexOf<T>>>::try_into(*i).ok())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +72,9 @@ pub fn voter_index_fn_owned<T: Config>(
|
||||
cache: BTreeMap<T::AccountId, usize>,
|
||||
) -> impl Fn(&T::AccountId) -> Option<CompactVoterIndexOf<T>> {
|
||||
move |who| {
|
||||
cache.get(who).and_then(|i| <usize as TryInto<CompactVoterIndexOf<T>>>::try_into(*i).ok())
|
||||
cache
|
||||
.get(who)
|
||||
.and_then(|i| <usize as TryInto<CompactVoterIndexOf<T>>>::try_into(*i).ok())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +177,11 @@ pub fn stake_of_fn_linear<T: Config>(
|
||||
snapshot: &Vec<(T::AccountId, VoteWeight, Vec<T::AccountId>)>,
|
||||
) -> impl Fn(&T::AccountId) -> VoteWeight + '_ {
|
||||
move |who| {
|
||||
snapshot.iter().find(|(x, _, _)| x == who).map(|(_, x, _)| *x).unwrap_or_default()
|
||||
snapshot
|
||||
.iter()
|
||||
.find(|(x, _, _)| x == who)
|
||||
.map(|(_, x, _)| *x)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
//!
|
||||
//! ### Signed Phase
|
||||
//!
|
||||
//! In the signed phase, solutions (of type [`RawSolution`]) are submitted and queued on chain. A
|
||||
//! In the signed phase, solutions (of type [`RawSolution`]) are submitted and queued on chain. A
|
||||
//! deposit is reserved, based on the size of the solution, for the cost of keeping this solution
|
||||
//! on-chain for a number of blocks, and the potential weight of the solution upon being checked. A
|
||||
//! maximum of `pallet::Config::MaxSignedSubmissions` solutions are stored. The queue is always
|
||||
@@ -228,34 +228,31 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use frame_election_provider_support::{onchain, ElectionDataProvider, ElectionProvider};
|
||||
use frame_support::{
|
||||
dispatch::DispatchResultWithPostInfo,
|
||||
ensure,
|
||||
traits::{Currency, Get, ReservableCurrency, OnUnbalanced},
|
||||
traits::{Currency, Get, OnUnbalanced, ReservableCurrency},
|
||||
weights::Weight,
|
||||
};
|
||||
use frame_system::{ensure_none, offchain::SendTransactionTypes};
|
||||
use frame_election_provider_support::{ElectionDataProvider, ElectionProvider, onchain};
|
||||
use sp_arithmetic::{
|
||||
traits::{CheckedAdd, Zero},
|
||||
UpperOf,
|
||||
};
|
||||
use sp_npos_elections::{
|
||||
assignment_ratio_to_staked_normalized, CompactSolution, ElectionScore,
|
||||
EvaluateSupport, PerThing128, Supports, VoteWeight,
|
||||
assignment_ratio_to_staked_normalized, CompactSolution, ElectionScore, EvaluateSupport,
|
||||
PerThing128, Supports, VoteWeight,
|
||||
};
|
||||
use sp_runtime::{
|
||||
traits::Bounded,
|
||||
transaction_validity::{
|
||||
InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
|
||||
TransactionValidityError, ValidTransaction,
|
||||
},
|
||||
DispatchError, PerThing, Perbill, RuntimeDebug, SaturatedConversion,
|
||||
traits::Bounded,
|
||||
};
|
||||
use sp_std::{
|
||||
convert::TryInto,
|
||||
prelude::*,
|
||||
};
|
||||
use sp_arithmetic::{
|
||||
UpperOf,
|
||||
traits::{Zero, CheckedAdd},
|
||||
};
|
||||
use sp_std::{convert::TryInto, prelude::*};
|
||||
|
||||
#[cfg(any(feature = "runtime-benchmarks", test))]
|
||||
mod benchmarking;
|
||||
@@ -562,7 +559,9 @@ pub mod pallet {
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config + SendTransactionTypes<Call<Self>> {
|
||||
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event> + TryInto<Event<Self>>;
|
||||
type Event: From<Event<Self>>
|
||||
+ IsType<<Self as frame_system::Config>::Event>
|
||||
+ TryInto<Event<Self>>;
|
||||
|
||||
/// Currency type.
|
||||
type Currency: ReservableCurrency<Self::AccountId> + Currency<Self::AccountId>;
|
||||
@@ -701,21 +700,22 @@ pub mod pallet {
|
||||
Ok(snap_weight) => {
|
||||
log!(info, "Starting signed phase round {}.", Self::round());
|
||||
T::WeightInfo::on_initialize_open_signed().saturating_add(snap_weight)
|
||||
}
|
||||
},
|
||||
Err(why) => {
|
||||
// Not much we can do about this at this point.
|
||||
log!(warn, "failed to open signed phase due to {:?}", why);
|
||||
T::WeightInfo::on_initialize_nothing()
|
||||
// NOTE: ^^ The trait specifies that this is a noop in terms of weight
|
||||
// in case of error.
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
Phase::Signed | Phase::Off
|
||||
if remaining <= unsigned_deadline && remaining > Zero::zero() =>
|
||||
{
|
||||
// our needs vary according to whether or not the unsigned phase follows a signed phase
|
||||
let (need_snapshot, enabled, signed_weight) = if current_phase == Phase::Signed {
|
||||
let (need_snapshot, enabled, signed_weight) = if current_phase == Phase::Signed
|
||||
{
|
||||
// there was previously a signed phase: close the signed phase, no need for snapshot.
|
||||
//
|
||||
// Notes:
|
||||
@@ -744,14 +744,14 @@ pub mod pallet {
|
||||
};
|
||||
|
||||
base_weight.saturating_add(snap_weight).saturating_add(signed_weight)
|
||||
}
|
||||
},
|
||||
Err(why) => {
|
||||
// Not much we can do about this at this point.
|
||||
log!(warn, "failed to open unsigned phase due to {:?}", why);
|
||||
T::WeightInfo::on_initialize_nothing()
|
||||
// NOTE: ^^ The trait specifies that this is a noop in terms of weight
|
||||
// in case of error.
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => T::WeightInfo::on_initialize_nothing(),
|
||||
@@ -759,15 +759,16 @@ pub mod pallet {
|
||||
}
|
||||
|
||||
fn offchain_worker(now: T::BlockNumber) {
|
||||
use sp_runtime::offchain::storage_lock::{StorageLock, BlockAndTime};
|
||||
use sp_runtime::offchain::storage_lock::{BlockAndTime, StorageLock};
|
||||
|
||||
// Create a lock with the maximum deadline of number of blocks in the unsigned phase.
|
||||
// This should only come useful in an **abrupt** termination of execution, otherwise the
|
||||
// guard will be dropped upon successful execution.
|
||||
let mut lock = StorageLock::<BlockAndTime<frame_system::Pallet::<T>>>::with_block_deadline(
|
||||
unsigned::OFFCHAIN_LOCK,
|
||||
T::UnsignedPhase::get().saturated_into(),
|
||||
);
|
||||
let mut lock =
|
||||
StorageLock::<BlockAndTime<frame_system::Pallet<T>>>::with_block_deadline(
|
||||
unsigned::OFFCHAIN_LOCK,
|
||||
T::UnsignedPhase::get().saturated_into(),
|
||||
);
|
||||
|
||||
match lock.try_lock() {
|
||||
Ok(_guard) => {
|
||||
@@ -775,7 +776,7 @@ pub mod pallet {
|
||||
},
|
||||
Err(deadline) => {
|
||||
log!(debug, "offchain worker lock not released, deadline is {:?}", deadline);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -857,8 +858,7 @@ pub mod pallet {
|
||||
witness: SolutionOrSnapshotSize,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
ensure_none(origin)?;
|
||||
let error_message =
|
||||
"Invalid unsigned submission must produce invalid block and \
|
||||
let error_message = "Invalid unsigned submission must produce invalid block and \
|
||||
deprive validator from their authoring reward.";
|
||||
|
||||
// Check score being an improvement, phase, and desired targets.
|
||||
@@ -921,11 +921,8 @@ pub mod pallet {
|
||||
// Note: we don't `rotate_round` at this point; the next call to
|
||||
// `ElectionProvider::elect` will succeed and take care of that.
|
||||
|
||||
let solution = ReadySolution {
|
||||
supports,
|
||||
score: [0, 0, 0],
|
||||
compute: ElectionCompute::Emergency,
|
||||
};
|
||||
let solution =
|
||||
ReadySolution { supports, score: [0, 0, 0], compute: ElectionCompute::Emergency };
|
||||
|
||||
<QueuedSolution<T>>::put(solution);
|
||||
Ok(())
|
||||
@@ -954,7 +951,8 @@ pub mod pallet {
|
||||
|
||||
// ensure witness data is correct.
|
||||
ensure!(
|
||||
num_signed_submissions >= <SignedSubmissions<T>>::decode_len().unwrap_or_default() as u32,
|
||||
num_signed_submissions >=
|
||||
<SignedSubmissions<T>>::decode_len().unwrap_or_default() as u32,
|
||||
Error::<T>::SignedInvalidWitness,
|
||||
);
|
||||
|
||||
@@ -989,8 +987,7 @@ pub mod pallet {
|
||||
};
|
||||
|
||||
// collect deposit. Thereafter, the function cannot fail.
|
||||
T::Currency::reserve(&who, deposit)
|
||||
.map_err(|_| Error::<T>::SignedCannotPayDeposit)?;
|
||||
T::Currency::reserve(&who, deposit).map_err(|_| Error::<T>::SignedCannotPayDeposit)?;
|
||||
|
||||
let ejected_a_solution = maybe_removed.is_some();
|
||||
// if we had to remove the weakest solution, unreserve its deposit
|
||||
@@ -1068,10 +1065,8 @@ pub mod pallet {
|
||||
if let Call::submit_unsigned(solution, _) = call {
|
||||
// Discard solution not coming from the local OCW.
|
||||
match source {
|
||||
TransactionSource::Local | TransactionSource::InBlock => { /* allowed */ }
|
||||
_ => {
|
||||
return InvalidTransaction::Call.into();
|
||||
}
|
||||
TransactionSource::Local | TransactionSource::InBlock => { /* allowed */ },
|
||||
_ => return InvalidTransaction::Call.into(),
|
||||
}
|
||||
|
||||
let _ = Self::unsigned_pre_dispatch_checks(solution)
|
||||
@@ -1084,9 +1079,8 @@ pub mod pallet {
|
||||
ValidTransaction::with_tag_prefix("OffchainElection")
|
||||
// The higher the score[0], the better a solution is.
|
||||
.priority(
|
||||
T::MinerTxPriority::get().saturating_add(
|
||||
solution.score[0].saturated_into()
|
||||
),
|
||||
T::MinerTxPriority::get()
|
||||
.saturating_add(solution.score[0].saturated_into()),
|
||||
)
|
||||
// Used to deduplicate unsigned solutions: each validator should produce one
|
||||
// solution per round at most, and solutions are not propagate.
|
||||
@@ -1219,20 +1213,18 @@ impl<T: Config> Pallet<T> {
|
||||
match current_phase {
|
||||
Phase::Unsigned((true, opened)) if opened == now => {
|
||||
// Mine a new solution, cache it, and attempt to submit it
|
||||
let initial_output = Self::ensure_offchain_repeat_frequency(now).and_then(|_| {
|
||||
Self::mine_check_save_submit()
|
||||
});
|
||||
let initial_output = Self::ensure_offchain_repeat_frequency(now)
|
||||
.and_then(|_| Self::mine_check_save_submit());
|
||||
log!(debug, "initial offchain thread output: {:?}", initial_output);
|
||||
}
|
||||
},
|
||||
Phase::Unsigned((true, opened)) if opened < now => {
|
||||
// Try and resubmit the cached solution, and recompute ONLY if it is not
|
||||
// feasible.
|
||||
let resubmit_output = Self::ensure_offchain_repeat_frequency(now).and_then(|_| {
|
||||
Self::restore_or_compute_then_maybe_submit()
|
||||
});
|
||||
let resubmit_output = Self::ensure_offchain_repeat_frequency(now)
|
||||
.and_then(|_| Self::restore_or_compute_then_maybe_submit());
|
||||
log!(debug, "resubmit offchain thread output: {:?}", resubmit_output);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
// After election finalization, clear OCW solution storage.
|
||||
@@ -1242,9 +1234,7 @@ impl<T: Config> Pallet<T> {
|
||||
let local_event = <T as Config>::Event::from(event_record.event);
|
||||
local_event.try_into().ok()
|
||||
})
|
||||
.any(|event| {
|
||||
matches!(event, Event::ElectionFinalized(_))
|
||||
})
|
||||
.any(|event| matches!(event, Event::ElectionFinalized(_)))
|
||||
{
|
||||
unsigned::kill_ocw_solution::<T>();
|
||||
}
|
||||
@@ -1308,14 +1298,12 @@ impl<T: Config> Pallet<T> {
|
||||
// Defensive-only.
|
||||
if targets.len() > target_limit || voters.len() > voter_limit {
|
||||
debug_assert!(false, "Snapshot limit has not been respected.");
|
||||
return Err(ElectionError::DataProvider("Snapshot too big for submission."));
|
||||
return Err(ElectionError::DataProvider("Snapshot too big for submission."))
|
||||
}
|
||||
|
||||
// Only write snapshot if all existed.
|
||||
let metadata = SolutionOrSnapshotSize {
|
||||
voters: voters.len() as u32,
|
||||
targets: targets.len() as u32,
|
||||
};
|
||||
let metadata =
|
||||
SolutionOrSnapshotSize { voters: voters.len() as u32, targets: targets.len() as u32 };
|
||||
log!(debug, "creating a snapshot with metadata {:?}", metadata);
|
||||
|
||||
<SnapshotMetadata<T>>::put(metadata);
|
||||
@@ -1335,7 +1323,10 @@ impl<T: Config> Pallet<T> {
|
||||
debug_assert!(buffer.len() == size && size == buffer.capacity());
|
||||
|
||||
sp_io::storage::set(&<Snapshot<T>>::hashed_key(), &buffer);
|
||||
Ok(w1.saturating_add(w2).saturating_add(w3).saturating_add(T::DbWeight::get().writes(3)))
|
||||
Ok(w1
|
||||
.saturating_add(w2)
|
||||
.saturating_add(w3)
|
||||
.saturating_add(T::DbWeight::get().writes(3)))
|
||||
}
|
||||
|
||||
/// Kill everything created by [`Pallet::create_snapshot`].
|
||||
@@ -1369,9 +1360,9 @@ impl<T: Config> Pallet<T> {
|
||||
// Ensure that the solution's score can pass absolute min-score.
|
||||
let submitted_score = solution.score.clone();
|
||||
ensure!(
|
||||
Self::minimum_untrusted_score().map_or(true, |min_score|
|
||||
Self::minimum_untrusted_score().map_or(true, |min_score| {
|
||||
sp_npos_elections::is_score_better(submitted_score, min_score, Perbill::zero())
|
||||
),
|
||||
}),
|
||||
FeasibilityError::UntrustedScoreTooLow
|
||||
);
|
||||
|
||||
@@ -1418,7 +1409,7 @@ impl<T: Config> Pallet<T> {
|
||||
|
||||
// Check that all of the targets are valid based on the snapshot.
|
||||
if assignment.distribution.iter().any(|(d, _)| !targets.contains(d)) {
|
||||
return Err(FeasibilityError::InvalidVote);
|
||||
return Err(FeasibilityError::InvalidVote)
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
@@ -1494,8 +1485,13 @@ impl<T: Config> Pallet<T> {
|
||||
.fold(Zero::zero(), |acc, next| acc + next.voters.len() as u32);
|
||||
Ok((
|
||||
supports,
|
||||
T::WeightInfo::elect_queued(metadata.voters, metadata.targets, active_voters, desired),
|
||||
compute
|
||||
T::WeightInfo::elect_queued(
|
||||
metadata.voters,
|
||||
metadata.targets,
|
||||
active_voters,
|
||||
desired,
|
||||
),
|
||||
compute,
|
||||
))
|
||||
},
|
||||
)
|
||||
@@ -1526,12 +1522,12 @@ impl<T: Config> ElectionProvider<T::AccountId, T::BlockNumber> for Pallet<T> {
|
||||
// All went okay, put sign to be Off, clean snapshot, etc.
|
||||
Self::rotate_round();
|
||||
Ok((supports, weight))
|
||||
}
|
||||
},
|
||||
Err(why) => {
|
||||
log!(error, "Entering emergency mode: {:?}", why);
|
||||
<CurrentPhase<T>>::put(Phase::Emergency);
|
||||
Err(why)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1553,11 +1549,9 @@ mod feasibility_check {
|
||||
//! that is invalid, but gets through the system as valid.
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
mock::{
|
||||
MultiPhase, Runtime, roll_to, TargetIndex, raw_solution, EpochLength, UnsignedPhase,
|
||||
SignedPhase, VoterIndex, ExtBuilder,
|
||||
},
|
||||
use crate::mock::{
|
||||
raw_solution, roll_to, EpochLength, ExtBuilder, MultiPhase, Runtime, SignedPhase,
|
||||
TargetIndex, UnsignedPhase, VoterIndex,
|
||||
};
|
||||
use frame_support::assert_noop;
|
||||
|
||||
@@ -1728,11 +1722,11 @@ mod feasibility_check {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
Phase,
|
||||
mock::{
|
||||
ExtBuilder, MultiPhase, Runtime, roll_to, MockWeightInfo, AccountId, TargetIndex,
|
||||
Targets, multi_phase_events, System, SignedMaxSubmissions,
|
||||
multi_phase_events, roll_to, AccountId, ExtBuilder, MockWeightInfo, MultiPhase,
|
||||
Runtime, SignedMaxSubmissions, System, TargetIndex, Targets,
|
||||
},
|
||||
Phase,
|
||||
};
|
||||
use frame_election_provider_support::ElectionProvider;
|
||||
use frame_support::{assert_noop, assert_ok};
|
||||
@@ -2002,7 +1996,6 @@ mod tests {
|
||||
roll_to(15);
|
||||
assert_eq!(MultiPhase::current_phase(), Phase::Signed);
|
||||
|
||||
|
||||
let (solution, _) = MultiPhase::mine_solution(2).unwrap();
|
||||
// Default solution has a score of [50, 100, 5000].
|
||||
assert_eq!(solution.score, [50, 100, 5000]);
|
||||
@@ -2012,10 +2005,7 @@ mod tests {
|
||||
|
||||
<MinimumUntrustedScore<Runtime>>::put([51, 0, 0]);
|
||||
assert_noop!(
|
||||
MultiPhase::feasibility_check(
|
||||
solution,
|
||||
ElectionCompute::Signed
|
||||
),
|
||||
MultiPhase::feasibility_check(solution, ElectionCompute::Signed),
|
||||
FeasibilityError::UntrustedScoreTooLow,
|
||||
);
|
||||
})
|
||||
@@ -2039,9 +2029,9 @@ mod tests {
|
||||
};
|
||||
|
||||
let mut active = 1;
|
||||
while weight_with(active)
|
||||
<= <Runtime as frame_system::Config>::BlockWeights::get().max_block
|
||||
|| active == all_voters
|
||||
while weight_with(active) <=
|
||||
<Runtime as frame_system::Config>::BlockWeights::get().max_block ||
|
||||
active == all_voters
|
||||
{
|
||||
active += 1;
|
||||
}
|
||||
|
||||
@@ -17,13 +17,10 @@
|
||||
|
||||
use super::*;
|
||||
use crate as multi_phase;
|
||||
use multi_phase::unsigned::{IndexAssignmentOf, Voter};
|
||||
use frame_election_provider_support::{data_provider, ElectionDataProvider};
|
||||
pub use frame_support::{assert_noop, assert_ok};
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
traits::{Hooks},
|
||||
weights::Weight,
|
||||
};
|
||||
use frame_support::{parameter_types, traits::Hooks, weights::Weight};
|
||||
use multi_phase::unsigned::{IndexAssignmentOf, Voter};
|
||||
use parking_lot::RwLock;
|
||||
use sp_core::{
|
||||
offchain::{
|
||||
@@ -32,7 +29,6 @@ use sp_core::{
|
||||
},
|
||||
H256,
|
||||
};
|
||||
use frame_election_provider_support::{ElectionDataProvider, data_provider};
|
||||
use sp_npos_elections::{
|
||||
assignment_ratio_to_staked_normalized, seq_phragmen, to_supports, to_without_backing,
|
||||
CompactSolution, ElectionResult, EvaluateSupport,
|
||||
@@ -405,7 +401,7 @@ impl ElectionDataProvider<AccountId, u64> for StakingMock {
|
||||
let targets = Targets::get();
|
||||
|
||||
if maybe_max_len.map_or(false, |max_len| targets.len() > max_len) {
|
||||
return Err("Targets too big");
|
||||
return Err("Targets too big")
|
||||
}
|
||||
|
||||
Ok((targets, 0))
|
||||
@@ -416,7 +412,7 @@ impl ElectionDataProvider<AccountId, u64> for StakingMock {
|
||||
) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec<AccountId>)>, Weight)> {
|
||||
let voters = Voters::get();
|
||||
if maybe_max_len.map_or(false, |max_len| voters.len() > max_len) {
|
||||
return Err("Voters too big");
|
||||
return Err("Voters too big")
|
||||
}
|
||||
|
||||
Ok((voters, 0))
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
//! The signed phase implementation.
|
||||
|
||||
use crate::{
|
||||
CompactOf, Config, ElectionCompute, Pallet, RawSolution, ReadySolution, SolutionOrSnapshotSize,
|
||||
Weight, WeightInfo, QueuedSolution, SignedSubmissionsMap, SignedSubmissionIndices,
|
||||
SignedSubmissionNextIndex,
|
||||
CompactOf, Config, ElectionCompute, Pallet, QueuedSolution, RawSolution, ReadySolution,
|
||||
SignedSubmissionIndices, SignedSubmissionNextIndex, SignedSubmissionsMap,
|
||||
SolutionOrSnapshotSize, Weight, WeightInfo,
|
||||
};
|
||||
use codec::{Encode, Decode, HasCompact};
|
||||
use codec::{Decode, Encode, HasCompact};
|
||||
use frame_support::{
|
||||
storage::bounded_btree_map::BoundedBTreeMap,
|
||||
traits::{Currency, Get, OnUnbalanced, ReservableCurrency},
|
||||
@@ -31,8 +31,8 @@ use frame_support::{
|
||||
use sp_arithmetic::traits::SaturatedConversion;
|
||||
use sp_npos_elections::{is_score_better, CompactSolution, ElectionScore};
|
||||
use sp_runtime::{
|
||||
RuntimeDebug,
|
||||
traits::{Saturating, Zero},
|
||||
RuntimeDebug,
|
||||
};
|
||||
use sp_std::{
|
||||
cmp::Ordering,
|
||||
@@ -131,24 +131,30 @@ impl<T: Config> SignedSubmissions<T> {
|
||||
deletion_overlay: BTreeSet::new(),
|
||||
};
|
||||
// validate that the stored state is sane
|
||||
debug_assert!(submissions.indices.values().copied().max().map_or(
|
||||
true,
|
||||
|max_idx| submissions.next_idx > max_idx,
|
||||
));
|
||||
debug_assert!(submissions
|
||||
.indices
|
||||
.values()
|
||||
.copied()
|
||||
.max()
|
||||
.map_or(true, |max_idx| submissions.next_idx > max_idx,));
|
||||
submissions
|
||||
}
|
||||
|
||||
/// Put the signed submissions back into storage.
|
||||
pub fn put(mut self) {
|
||||
// validate that we're going to write only sane things to storage
|
||||
debug_assert!(self.insertion_overlay.keys().copied().max().map_or(
|
||||
true,
|
||||
|max_idx| self.next_idx > max_idx,
|
||||
));
|
||||
debug_assert!(self.indices.values().copied().max().map_or(
|
||||
true,
|
||||
|max_idx| self.next_idx > max_idx,
|
||||
));
|
||||
debug_assert!(self
|
||||
.insertion_overlay
|
||||
.keys()
|
||||
.copied()
|
||||
.max()
|
||||
.map_or(true, |max_idx| self.next_idx > max_idx,));
|
||||
debug_assert!(self
|
||||
.indices
|
||||
.values()
|
||||
.copied()
|
||||
.max()
|
||||
.map_or(true, |max_idx| self.next_idx > max_idx,));
|
||||
|
||||
SignedSubmissionIndices::<T>::put(self.indices);
|
||||
SignedSubmissionNextIndex::<T>::put(self.next_idx);
|
||||
@@ -203,10 +209,12 @@ impl<T: Config> SignedSubmissions<T> {
|
||||
}
|
||||
|
||||
self.insertion_overlay.remove(&remove_idx).or_else(|| {
|
||||
(!self.deletion_overlay.contains(&remove_idx)).then(|| {
|
||||
self.deletion_overlay.insert(remove_idx);
|
||||
SignedSubmissionsMap::<T>::try_get(remove_idx).ok()
|
||||
}).flatten()
|
||||
(!self.deletion_overlay.contains(&remove_idx))
|
||||
.then(|| {
|
||||
self.deletion_overlay.insert(remove_idx);
|
||||
SignedSubmissionsMap::<T>::try_get(remove_idx).ok()
|
||||
})
|
||||
.flatten()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -256,10 +264,7 @@ impl<T: Config> SignedSubmissions<T> {
|
||||
///
|
||||
/// In the event that the new submission is not better than the current weakest according
|
||||
/// to `is_score_better`, we do not change anything.
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
submission: SignedSubmissionOf<T>,
|
||||
) -> InsertResult<T> {
|
||||
pub fn insert(&mut self, submission: SignedSubmissionOf<T>) -> InsertResult<T> {
|
||||
// verify the expectation that we never reuse an index
|
||||
debug_assert!(!self.indices.values().any(|&idx| idx == self.next_idx));
|
||||
|
||||
@@ -271,12 +276,12 @@ impl<T: Config> SignedSubmissions<T> {
|
||||
self.indices
|
||||
.try_insert(submission.solution.score, prev_idx)
|
||||
.expect("didn't change the map size; qed");
|
||||
return InsertResult::NotInserted;
|
||||
}
|
||||
return InsertResult::NotInserted
|
||||
},
|
||||
Ok(None) => {
|
||||
// successfully inserted into the set; no need to take out weakest member
|
||||
None
|
||||
}
|
||||
},
|
||||
Err((insert_score, insert_idx)) => {
|
||||
// could not insert into the set because it is full.
|
||||
// note that we short-circuit return here in case the iteration produces `None`.
|
||||
@@ -290,11 +295,11 @@ impl<T: Config> SignedSubmissions<T> {
|
||||
|
||||
// if we haven't improved on the weakest score, don't change anything.
|
||||
if !is_score_better(insert_score, weakest_score, threshold) {
|
||||
return InsertResult::NotInserted;
|
||||
return InsertResult::NotInserted
|
||||
}
|
||||
|
||||
self.swap_out_submission(weakest_score, Some((insert_score, insert_idx)))
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// we've taken out the weakest, so update the storage map and the next index
|
||||
@@ -349,17 +354,12 @@ impl<T: Config> Pallet<T> {
|
||||
let reward = T::SignedRewardBase::get();
|
||||
|
||||
while let Some(best) = all_submissions.pop_last() {
|
||||
let SignedSubmission { solution, who, deposit} = best;
|
||||
let SignedSubmission { solution, who, deposit } = best;
|
||||
let active_voters = solution.compact.voter_count() as u32;
|
||||
let feasibility_weight = {
|
||||
// defensive only: at the end of signed phase, snapshot will exits.
|
||||
let desired_targets = Self::desired_targets().unwrap_or_default();
|
||||
T::WeightInfo::feasibility_check(
|
||||
voters,
|
||||
targets,
|
||||
active_voters,
|
||||
desired_targets,
|
||||
)
|
||||
T::WeightInfo::feasibility_check(voters, targets, active_voters, desired_targets)
|
||||
};
|
||||
// the feasibility check itself has some weight
|
||||
weight = weight.saturating_add(feasibility_weight);
|
||||
@@ -375,13 +375,13 @@ impl<T: Config> Pallet<T> {
|
||||
|
||||
weight = weight
|
||||
.saturating_add(T::WeightInfo::finalize_signed_phase_accept_solution());
|
||||
break;
|
||||
}
|
||||
break
|
||||
},
|
||||
Err(_) => {
|
||||
Self::finalize_signed_phase_reject_solution(&who, deposit);
|
||||
weight = weight
|
||||
.saturating_add(T::WeightInfo::finalize_signed_phase_reject_solution());
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +398,12 @@ impl<T: Config> Pallet<T> {
|
||||
debug_assert!(!SignedSubmissionNextIndex::<T>::exists());
|
||||
debug_assert!(SignedSubmissionsMap::<T>::iter().next().is_none());
|
||||
|
||||
log!(debug, "closed signed phase, found solution? {}, discarded {}", found_solution, discarded);
|
||||
log!(
|
||||
debug,
|
||||
"closed signed phase, found solution? {}, discarded {}",
|
||||
found_solution,
|
||||
discarded
|
||||
);
|
||||
(found_solution, weight)
|
||||
}
|
||||
|
||||
@@ -469,9 +474,12 @@ impl<T: Config> Pallet<T> {
|
||||
let feasibility_weight = Self::feasibility_weight_of(solution, size);
|
||||
|
||||
let len_deposit = T::SignedDepositByte::get().saturating_mul(encoded_len);
|
||||
let weight_deposit = T::SignedDepositWeight::get().saturating_mul(feasibility_weight.saturated_into());
|
||||
let weight_deposit =
|
||||
T::SignedDepositWeight::get().saturating_mul(feasibility_weight.saturated_into());
|
||||
|
||||
T::SignedDepositBase::get().saturating_add(len_deposit).saturating_add(weight_deposit)
|
||||
T::SignedDepositBase::get()
|
||||
.saturating_add(len_deposit)
|
||||
.saturating_add(weight_deposit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,13 +487,13 @@ impl<T: Config> Pallet<T> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
Phase, Error,
|
||||
mock::{
|
||||
balances, ExtBuilder, MultiPhase, Origin, raw_solution, roll_to, Runtime,
|
||||
balances, raw_solution, roll_to, ExtBuilder, MultiPhase, Origin, Runtime,
|
||||
SignedMaxSubmissions, SignedMaxWeight,
|
||||
},
|
||||
Error, Phase,
|
||||
};
|
||||
use frame_support::{dispatch::DispatchResult, assert_noop, assert_storage_noop, assert_ok};
|
||||
use frame_support::{assert_noop, assert_ok, assert_storage_noop, dispatch::DispatchResult};
|
||||
|
||||
fn submit_with_witness(
|
||||
origin: Origin,
|
||||
@@ -626,7 +634,6 @@ mod tests {
|
||||
assert_ok!(submit_with_witness(Origin::signed(99), solution));
|
||||
}
|
||||
|
||||
|
||||
// weaker.
|
||||
let solution = RawSolution { score: [4, 0, 0], ..Default::default() };
|
||||
|
||||
@@ -810,33 +817,36 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn cannot_consume_too_much_future_weight() {
|
||||
ExtBuilder::default().signed_weight(40).mock_weight_info(true).build_and_execute(|| {
|
||||
roll_to(15);
|
||||
assert!(MultiPhase::current_phase().is_signed());
|
||||
ExtBuilder::default()
|
||||
.signed_weight(40)
|
||||
.mock_weight_info(true)
|
||||
.build_and_execute(|| {
|
||||
roll_to(15);
|
||||
assert!(MultiPhase::current_phase().is_signed());
|
||||
|
||||
let (solution, witness) = MultiPhase::mine_solution(2).unwrap();
|
||||
let solution_weight = <Runtime as Config>::WeightInfo::feasibility_check(
|
||||
witness.voters,
|
||||
witness.targets,
|
||||
solution.compact.voter_count() as u32,
|
||||
solution.compact.unique_targets().len() as u32,
|
||||
);
|
||||
// default solution will have 5 edges (5 * 5 + 10)
|
||||
assert_eq!(solution_weight, 35);
|
||||
assert_eq!(solution.compact.voter_count(), 5);
|
||||
assert_eq!(<Runtime as Config>::SignedMaxWeight::get(), 40);
|
||||
let (solution, witness) = MultiPhase::mine_solution(2).unwrap();
|
||||
let solution_weight = <Runtime as Config>::WeightInfo::feasibility_check(
|
||||
witness.voters,
|
||||
witness.targets,
|
||||
solution.compact.voter_count() as u32,
|
||||
solution.compact.unique_targets().len() as u32,
|
||||
);
|
||||
// default solution will have 5 edges (5 * 5 + 10)
|
||||
assert_eq!(solution_weight, 35);
|
||||
assert_eq!(solution.compact.voter_count(), 5);
|
||||
assert_eq!(<Runtime as Config>::SignedMaxWeight::get(), 40);
|
||||
|
||||
assert_ok!(submit_with_witness(Origin::signed(99), solution.clone()));
|
||||
assert_ok!(submit_with_witness(Origin::signed(99), solution.clone()));
|
||||
|
||||
<SignedMaxWeight>::set(30);
|
||||
<SignedMaxWeight>::set(30);
|
||||
|
||||
// note: resubmitting the same solution is technically okay as long as the queue has
|
||||
// space.
|
||||
assert_noop!(
|
||||
submit_with_witness(Origin::signed(99), solution),
|
||||
Error::<Runtime>::SignedTooMuchWeight,
|
||||
);
|
||||
})
|
||||
// note: resubmitting the same solution is technically okay as long as the queue has
|
||||
// space.
|
||||
assert_noop!(
|
||||
submit_with_witness(Origin::signed(99), solution),
|
||||
Error::<Runtime>::SignedTooMuchWeight,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -21,19 +21,18 @@ use crate::{
|
||||
helpers, Call, CompactAccuracyOf, CompactOf, Config, ElectionCompute, Error, FeasibilityError,
|
||||
Pallet, RawSolution, ReadySolution, RoundSnapshot, SolutionOrSnapshotSize, Weight, WeightInfo,
|
||||
};
|
||||
use codec::{Encode, Decode};
|
||||
use codec::{Decode, Encode};
|
||||
use frame_support::{dispatch::DispatchResult, ensure, traits::Get};
|
||||
use frame_system::offchain::SubmitTransaction;
|
||||
use sp_arithmetic::Perbill;
|
||||
use sp_npos_elections::{
|
||||
CompactSolution, ElectionResult, assignment_ratio_to_staked_normalized,
|
||||
assignment_staked_to_ratio_normalized, is_score_better, seq_phragmen,
|
||||
assignment_ratio_to_staked_normalized, assignment_staked_to_ratio_normalized, is_score_better,
|
||||
seq_phragmen, CompactSolution, ElectionResult,
|
||||
};
|
||||
use sp_runtime::{
|
||||
DispatchError,
|
||||
SaturatedConversion,
|
||||
offchain::storage::{MutateStorageError, StorageValueRef},
|
||||
traits::TrailingZeroInput,
|
||||
DispatchError, SaturatedConversion,
|
||||
};
|
||||
use sp_std::{cmp::Ordering, convert::TryFrom, vec::Vec};
|
||||
|
||||
@@ -54,10 +53,8 @@ pub type Voter<T> = (
|
||||
);
|
||||
|
||||
/// The relative distribution of a voter's stake among the winning targets.
|
||||
pub type Assignment<T> = sp_npos_elections::Assignment<
|
||||
<T as frame_system::Config>::AccountId,
|
||||
CompactAccuracyOf<T>,
|
||||
>;
|
||||
pub type Assignment<T> =
|
||||
sp_npos_elections::Assignment<<T as frame_system::Config>::AccountId, CompactAccuracyOf<T>>;
|
||||
|
||||
/// The [`IndexAssignment`][sp_npos_elections::IndexAssignment] type specialized for a particular
|
||||
/// runtime `T`.
|
||||
@@ -105,7 +102,8 @@ fn save_solution<T: Config>(call: &Call<T>) -> Result<(), MinerError> {
|
||||
let storage = StorageValueRef::persistent(&OFFCHAIN_CACHED_CALL);
|
||||
match storage.mutate::<_, (), _>(|_| Ok(call.clone())) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(MutateStorageError::ConcurrentModification(_)) => Err(MinerError::FailedToStoreSolution),
|
||||
Err(MutateStorageError::ConcurrentModification(_)) =>
|
||||
Err(MinerError::FailedToStoreSolution),
|
||||
Err(MutateStorageError::ValueFunctionFailed(_)) => {
|
||||
// this branch should be unreachable according to the definition of
|
||||
// `StorageValueRef::mutate`: that function should only ever `Err` if the closure we
|
||||
@@ -151,44 +149,45 @@ impl<T: Config> Pallet<T> {
|
||||
/// Attempt to restore a solution from cache. Otherwise, compute it fresh. Either way, submit
|
||||
/// if our call's score is greater than that of the cached solution.
|
||||
pub fn restore_or_compute_then_maybe_submit() -> Result<(), MinerError> {
|
||||
log!(debug,"miner attempting to restore or compute an unsigned solution.");
|
||||
log!(debug, "miner attempting to restore or compute an unsigned solution.");
|
||||
|
||||
let call = restore_solution::<T>()
|
||||
.and_then(|call| {
|
||||
// ensure the cached call is still current before submitting
|
||||
if let Call::submit_unsigned(solution, _) = &call {
|
||||
// prevent errors arising from state changes in a forkful chain
|
||||
Self::basic_checks(solution, "restored")?;
|
||||
Ok(call)
|
||||
} else {
|
||||
Err(MinerError::SolutionCallInvalid)
|
||||
}
|
||||
}).or_else::<MinerError, _>(|error| {
|
||||
log!(debug, "restoring solution failed due to {:?}", error);
|
||||
match error {
|
||||
MinerError::NoStoredSolution => {
|
||||
log!(trace, "mining a new solution.");
|
||||
// if not present or cache invalidated due to feasibility, regenerate.
|
||||
// note that failing `Feasibility` can only mean that the solution was
|
||||
// computed over a snapshot that has changed due to a fork.
|
||||
let call = Self::mine_checked_call()?;
|
||||
save_solution(&call)?;
|
||||
.and_then(|call| {
|
||||
// ensure the cached call is still current before submitting
|
||||
if let Call::submit_unsigned(solution, _) = &call {
|
||||
// prevent errors arising from state changes in a forkful chain
|
||||
Self::basic_checks(solution, "restored")?;
|
||||
Ok(call)
|
||||
} else {
|
||||
Err(MinerError::SolutionCallInvalid)
|
||||
}
|
||||
MinerError::Feasibility(_) => {
|
||||
log!(trace, "wiping infeasible solution.");
|
||||
// kill the infeasible solution, hopefully in the next runs (whenever they
|
||||
// may be) we mine a new one.
|
||||
kill_ocw_solution::<T>();
|
||||
clear_offchain_repeat_frequency();
|
||||
Err(error)
|
||||
},
|
||||
_ => {
|
||||
// nothing to do. Return the error as-is.
|
||||
Err(error)
|
||||
})
|
||||
.or_else::<MinerError, _>(|error| {
|
||||
log!(debug, "restoring solution failed due to {:?}", error);
|
||||
match error {
|
||||
MinerError::NoStoredSolution => {
|
||||
log!(trace, "mining a new solution.");
|
||||
// if not present or cache invalidated due to feasibility, regenerate.
|
||||
// note that failing `Feasibility` can only mean that the solution was
|
||||
// computed over a snapshot that has changed due to a fork.
|
||||
let call = Self::mine_checked_call()?;
|
||||
save_solution(&call)?;
|
||||
Ok(call)
|
||||
},
|
||||
MinerError::Feasibility(_) => {
|
||||
log!(trace, "wiping infeasible solution.");
|
||||
// kill the infeasible solution, hopefully in the next runs (whenever they
|
||||
// may be) we mine a new one.
|
||||
kill_ocw_solution::<T>();
|
||||
clear_offchain_repeat_frequency();
|
||||
Err(error)
|
||||
},
|
||||
_ => {
|
||||
// nothing to do. Return the error as-is.
|
||||
Err(error)
|
||||
},
|
||||
}
|
||||
}
|
||||
})?;
|
||||
})?;
|
||||
|
||||
Self::submit_call(call)
|
||||
}
|
||||
@@ -240,10 +239,12 @@ impl<T: Config> Pallet<T> {
|
||||
MinerError::PreDispatchChecksFailed(err)
|
||||
})?;
|
||||
|
||||
Self::feasibility_check(raw_solution.clone(), ElectionCompute::Unsigned).map_err(|err| {
|
||||
log!(debug, "feasibility check failed for {} solution: {:?}", solution_type, err);
|
||||
err
|
||||
})?;
|
||||
Self::feasibility_check(raw_solution.clone(), ElectionCompute::Unsigned).map_err(
|
||||
|err| {
|
||||
log!(debug, "feasibility check failed for {} solution: {:?}", solution_type, err);
|
||||
err
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -347,11 +348,7 @@ impl<T: Config> Pallet<T> {
|
||||
// converting to `Compact`.
|
||||
let mut index_assignments = sorted_assignments
|
||||
.into_iter()
|
||||
.map(|assignment| IndexAssignmentOf::<T>::new(
|
||||
&assignment,
|
||||
&voter_index,
|
||||
&target_index,
|
||||
))
|
||||
.map(|assignment| IndexAssignmentOf::<T>::new(&assignment, &voter_index, &target_index))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// trim assignments list for weight and length.
|
||||
@@ -390,10 +387,10 @@ impl<T: Config> Pallet<T> {
|
||||
max @ _ => {
|
||||
let seed = sp_io::offchain::random_seed();
|
||||
let random = <u32>::decode(&mut TrailingZeroInput::new(seed.as_ref()))
|
||||
.expect("input is padded with zeroes; qed")
|
||||
% max.saturating_add(1);
|
||||
.expect("input is padded with zeroes; qed") %
|
||||
max.saturating_add(1);
|
||||
random as usize
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,18 +415,16 @@ impl<T: Config> Pallet<T> {
|
||||
max_weight: Weight,
|
||||
assignments: &mut Vec<IndexAssignmentOf<T>>,
|
||||
) {
|
||||
let maximum_allowed_voters = Self::maximum_voter_for_weight::<T::WeightInfo>(
|
||||
desired_targets,
|
||||
size,
|
||||
max_weight,
|
||||
);
|
||||
let removing: usize = assignments.len().saturating_sub(
|
||||
maximum_allowed_voters.saturated_into(),
|
||||
);
|
||||
let maximum_allowed_voters =
|
||||
Self::maximum_voter_for_weight::<T::WeightInfo>(desired_targets, size, max_weight);
|
||||
let removing: usize =
|
||||
assignments.len().saturating_sub(maximum_allowed_voters.saturated_into());
|
||||
log!(
|
||||
debug,
|
||||
"from {} assignments, truncating to {} for weight, removing {}",
|
||||
assignments.len(), maximum_allowed_voters, removing,
|
||||
assignments.len(),
|
||||
maximum_allowed_voters,
|
||||
removing,
|
||||
);
|
||||
assignments.truncate(maximum_allowed_voters as usize);
|
||||
}
|
||||
@@ -461,7 +456,7 @@ impl<T: Config> Pallet<T> {
|
||||
|
||||
// not much we can do if assignments are already empty.
|
||||
if high == low {
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
while high - low > 1 {
|
||||
@@ -472,22 +467,21 @@ impl<T: Config> Pallet<T> {
|
||||
high = test;
|
||||
}
|
||||
}
|
||||
let maximum_allowed_voters =
|
||||
if low < assignments.len() &&
|
||||
encoded_size_of(&assignments[..low + 1])? <= max_allowed_length
|
||||
{
|
||||
low + 1
|
||||
} else {
|
||||
low
|
||||
};
|
||||
let maximum_allowed_voters = if low < assignments.len() &&
|
||||
encoded_size_of(&assignments[..low + 1])? <= max_allowed_length
|
||||
{
|
||||
low + 1
|
||||
} else {
|
||||
low
|
||||
};
|
||||
|
||||
// ensure our post-conditions are correct
|
||||
debug_assert!(
|
||||
encoded_size_of(&assignments[..maximum_allowed_voters]).unwrap() <= max_allowed_length
|
||||
);
|
||||
debug_assert!(if maximum_allowed_voters < assignments.len() {
|
||||
encoded_size_of(&assignments[..maximum_allowed_voters + 1]).unwrap()
|
||||
> max_allowed_length
|
||||
encoded_size_of(&assignments[..maximum_allowed_voters + 1]).unwrap() >
|
||||
max_allowed_length
|
||||
} else {
|
||||
true
|
||||
});
|
||||
@@ -517,7 +511,7 @@ impl<T: Config> Pallet<T> {
|
||||
max_weight: Weight,
|
||||
) -> u32 {
|
||||
if size.voters < 1 {
|
||||
return size.voters;
|
||||
return size.voters
|
||||
}
|
||||
|
||||
let max_voters = size.voters.max(1);
|
||||
@@ -536,7 +530,7 @@ impl<T: Config> Pallet<T> {
|
||||
Some(voters) if voters < max_voters => Ok(voters),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
},
|
||||
Ordering::Greater => voters.checked_sub(step).ok_or(()),
|
||||
Ordering::Equal => Ok(voters),
|
||||
}
|
||||
@@ -551,11 +545,9 @@ impl<T: Config> Pallet<T> {
|
||||
// proceed with the binary search
|
||||
Ok(next) if next != voters => {
|
||||
voters = next;
|
||||
}
|
||||
},
|
||||
// we are out of bounds, break out of the loop.
|
||||
Err(()) => {
|
||||
break;
|
||||
}
|
||||
Err(()) => break,
|
||||
// we found the right value - early exit the function.
|
||||
Ok(next) => return next,
|
||||
}
|
||||
@@ -599,17 +591,16 @@ impl<T: Config> Pallet<T> {
|
||||
|maybe_head: Result<Option<T::BlockNumber>, _>| {
|
||||
match maybe_head {
|
||||
Ok(Some(head)) if now < head => Err("fork."),
|
||||
Ok(Some(head)) if now >= head && now <= head + threshold => {
|
||||
Err("recently executed.")
|
||||
}
|
||||
Ok(Some(head)) if now >= head && now <= head + threshold =>
|
||||
Err("recently executed."),
|
||||
Ok(Some(head)) if now > head + threshold => {
|
||||
// we can run again now. Write the new head.
|
||||
Ok(now)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// value doesn't exists. Probably this node just booted up. Write, and run
|
||||
Ok(now)
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -632,9 +623,7 @@ impl<T: Config> Pallet<T> {
|
||||
///
|
||||
/// NOTE: Ideally, these tests should move more and more outside of this and more to the miner's
|
||||
/// code, so that we do less and less storage reads here.
|
||||
pub fn unsigned_pre_dispatch_checks(
|
||||
solution: &RawSolution<CompactOf<T>>,
|
||||
) -> DispatchResult {
|
||||
pub fn unsigned_pre_dispatch_checks(solution: &RawSolution<CompactOf<T>>) -> DispatchResult {
|
||||
// ensure solution is timely. Don't panic yet. This is a cheap check.
|
||||
ensure!(Self::current_phase().is_unsigned_open(), Error::<T>::PreDispatchEarlySubmission);
|
||||
|
||||
@@ -643,8 +632,8 @@ impl<T: Config> Pallet<T> {
|
||||
|
||||
// ensure correct number of winners.
|
||||
ensure!(
|
||||
Self::desired_targets().unwrap_or_default()
|
||||
== solution.compact.unique_targets().len() as u32,
|
||||
Self::desired_targets().unwrap_or_default() ==
|
||||
solution.compact.unique_targets().len() as u32,
|
||||
Error::<T>::PreDispatchWrongWinnerCount,
|
||||
);
|
||||
|
||||
@@ -761,19 +750,22 @@ mod max_weight {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
mock::{
|
||||
roll_to, roll_to_with_ocw, trim_helpers, witness, BlockNumber, Call as OuterCall,
|
||||
ExtBuilder, Extrinsic, MinerMaxWeight, MultiPhase, Origin, Runtime, System,
|
||||
TestCompact, TrimHelpers, UnsignedPhase,
|
||||
},
|
||||
CurrentPhase, InvalidTransaction, Phase, QueuedSolution, TransactionSource,
|
||||
TransactionValidityError,
|
||||
mock::{
|
||||
Call as OuterCall, ExtBuilder, Extrinsic, MinerMaxWeight, MultiPhase, Origin, Runtime,
|
||||
TestCompact, TrimHelpers, roll_to, roll_to_with_ocw, trim_helpers, witness,
|
||||
UnsignedPhase, BlockNumber, System,
|
||||
},
|
||||
};
|
||||
use frame_benchmarking::Zero;
|
||||
use frame_support::{assert_noop, assert_ok, dispatch::Dispatchable, traits::OffchainWorker};
|
||||
use sp_npos_elections::IndexAssignment;
|
||||
use sp_runtime::offchain::storage_lock::{StorageLock, BlockAndTime};
|
||||
use sp_runtime::{traits::ValidateUnsigned, PerU16};
|
||||
use sp_runtime::{
|
||||
offchain::storage_lock::{BlockAndTime, StorageLock},
|
||||
traits::ValidateUnsigned,
|
||||
PerU16,
|
||||
};
|
||||
|
||||
type Assignment = crate::unsigned::Assignment<Runtime>;
|
||||
|
||||
@@ -786,8 +778,11 @@ mod tests {
|
||||
// initial
|
||||
assert_eq!(MultiPhase::current_phase(), Phase::Off);
|
||||
assert!(matches!(
|
||||
<MultiPhase as ValidateUnsigned>::validate_unsigned(TransactionSource::Local, &call)
|
||||
.unwrap_err(),
|
||||
<MultiPhase as ValidateUnsigned>::validate_unsigned(
|
||||
TransactionSource::Local,
|
||||
&call
|
||||
)
|
||||
.unwrap_err(),
|
||||
TransactionValidityError::Invalid(InvalidTransaction::Custom(0))
|
||||
));
|
||||
assert!(matches!(
|
||||
@@ -799,8 +794,11 @@ mod tests {
|
||||
roll_to(15);
|
||||
assert_eq!(MultiPhase::current_phase(), Phase::Signed);
|
||||
assert!(matches!(
|
||||
<MultiPhase as ValidateUnsigned>::validate_unsigned(TransactionSource::Local, &call)
|
||||
.unwrap_err(),
|
||||
<MultiPhase as ValidateUnsigned>::validate_unsigned(
|
||||
TransactionSource::Local,
|
||||
&call
|
||||
)
|
||||
.unwrap_err(),
|
||||
TransactionValidityError::Invalid(InvalidTransaction::Custom(0))
|
||||
));
|
||||
assert!(matches!(
|
||||
@@ -823,8 +821,11 @@ mod tests {
|
||||
<CurrentPhase<Runtime>>::put(Phase::Unsigned((false, 25)));
|
||||
assert!(MultiPhase::current_phase().is_unsigned());
|
||||
assert!(matches!(
|
||||
<MultiPhase as ValidateUnsigned>::validate_unsigned(TransactionSource::Local, &call)
|
||||
.unwrap_err(),
|
||||
<MultiPhase as ValidateUnsigned>::validate_unsigned(
|
||||
TransactionSource::Local,
|
||||
&call
|
||||
)
|
||||
.unwrap_err(),
|
||||
TransactionValidityError::Invalid(InvalidTransaction::Custom(0))
|
||||
));
|
||||
assert!(matches!(
|
||||
@@ -895,23 +896,27 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn priority_is_set() {
|
||||
ExtBuilder::default().miner_tx_priority(20).desired_targets(0).build_and_execute(|| {
|
||||
roll_to(25);
|
||||
assert!(MultiPhase::current_phase().is_unsigned());
|
||||
ExtBuilder::default()
|
||||
.miner_tx_priority(20)
|
||||
.desired_targets(0)
|
||||
.build_and_execute(|| {
|
||||
roll_to(25);
|
||||
assert!(MultiPhase::current_phase().is_unsigned());
|
||||
|
||||
let solution = RawSolution::<TestCompact> { score: [5, 0, 0], ..Default::default() };
|
||||
let call = Call::submit_unsigned(solution.clone(), witness());
|
||||
let solution =
|
||||
RawSolution::<TestCompact> { score: [5, 0, 0], ..Default::default() };
|
||||
let call = Call::submit_unsigned(solution.clone(), witness());
|
||||
|
||||
assert_eq!(
|
||||
<MultiPhase as ValidateUnsigned>::validate_unsigned(
|
||||
TransactionSource::Local,
|
||||
&call
|
||||
)
|
||||
.unwrap()
|
||||
.priority,
|
||||
25
|
||||
);
|
||||
})
|
||||
assert_eq!(
|
||||
<MultiPhase as ValidateUnsigned>::validate_unsigned(
|
||||
TransactionSource::Local,
|
||||
&call
|
||||
)
|
||||
.unwrap()
|
||||
.priority,
|
||||
25
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -974,35 +979,38 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn miner_trims_weight() {
|
||||
ExtBuilder::default().miner_weight(100).mock_weight_info(true).build_and_execute(|| {
|
||||
roll_to(25);
|
||||
assert!(MultiPhase::current_phase().is_unsigned());
|
||||
ExtBuilder::default()
|
||||
.miner_weight(100)
|
||||
.mock_weight_info(true)
|
||||
.build_and_execute(|| {
|
||||
roll_to(25);
|
||||
assert!(MultiPhase::current_phase().is_unsigned());
|
||||
|
||||
let (solution, witness) = MultiPhase::mine_solution(2).unwrap();
|
||||
let solution_weight = <Runtime as Config>::WeightInfo::submit_unsigned(
|
||||
witness.voters,
|
||||
witness.targets,
|
||||
solution.compact.voter_count() as u32,
|
||||
solution.compact.unique_targets().len() as u32,
|
||||
);
|
||||
// default solution will have 5 edges (5 * 5 + 10)
|
||||
assert_eq!(solution_weight, 35);
|
||||
assert_eq!(solution.compact.voter_count(), 5);
|
||||
let (solution, witness) = MultiPhase::mine_solution(2).unwrap();
|
||||
let solution_weight = <Runtime as Config>::WeightInfo::submit_unsigned(
|
||||
witness.voters,
|
||||
witness.targets,
|
||||
solution.compact.voter_count() as u32,
|
||||
solution.compact.unique_targets().len() as u32,
|
||||
);
|
||||
// default solution will have 5 edges (5 * 5 + 10)
|
||||
assert_eq!(solution_weight, 35);
|
||||
assert_eq!(solution.compact.voter_count(), 5);
|
||||
|
||||
// now reduce the max weight
|
||||
<MinerMaxWeight>::set(25);
|
||||
// now reduce the max weight
|
||||
<MinerMaxWeight>::set(25);
|
||||
|
||||
let (solution, witness) = MultiPhase::mine_solution(2).unwrap();
|
||||
let solution_weight = <Runtime as Config>::WeightInfo::submit_unsigned(
|
||||
witness.voters,
|
||||
witness.targets,
|
||||
solution.compact.voter_count() as u32,
|
||||
solution.compact.unique_targets().len() as u32,
|
||||
);
|
||||
// default solution will have 5 edges (5 * 5 + 10)
|
||||
assert_eq!(solution_weight, 25);
|
||||
assert_eq!(solution.compact.voter_count(), 3);
|
||||
})
|
||||
let (solution, witness) = MultiPhase::mine_solution(2).unwrap();
|
||||
let solution_weight = <Runtime as Config>::WeightInfo::submit_unsigned(
|
||||
witness.voters,
|
||||
witness.targets,
|
||||
solution.compact.voter_count() as u32,
|
||||
solution.compact.unique_targets().len() as u32,
|
||||
);
|
||||
// default solution will have 5 edges (5 * 5 + 10)
|
||||
assert_eq!(solution_weight, 25);
|
||||
assert_eq!(solution.compact.voter_count(), 3);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1014,7 +1022,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
MultiPhase::mine_check_save_submit().unwrap_err(),
|
||||
MinerError::PreDispatchChecksFailed(DispatchError::Module{
|
||||
MinerError::PreDispatchChecksFailed(DispatchError::Module {
|
||||
index: 2,
|
||||
error: 1,
|
||||
message: Some("PreDispatchWrongWinnerCount"),
|
||||
@@ -1360,15 +1368,14 @@ mod tests {
|
||||
};
|
||||
|
||||
// Custom(7) maps to PreDispatchChecksFailed
|
||||
let pre_dispatch_check_error = TransactionValidityError::Invalid(
|
||||
InvalidTransaction::Custom(7),
|
||||
);
|
||||
let pre_dispatch_check_error =
|
||||
TransactionValidityError::Invalid(InvalidTransaction::Custom(7));
|
||||
assert_eq!(
|
||||
<MultiPhase as ValidateUnsigned>::validate_unsigned(
|
||||
TransactionSource::Local,
|
||||
&call,
|
||||
)
|
||||
.unwrap_err(),
|
||||
.unwrap_err(),
|
||||
pre_dispatch_check_error,
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -1384,21 +1391,14 @@ mod tests {
|
||||
roll_to(25);
|
||||
|
||||
// given
|
||||
let TrimHelpers {
|
||||
mut assignments,
|
||||
encoded_size_of,
|
||||
..
|
||||
} = trim_helpers();
|
||||
let TrimHelpers { mut assignments, encoded_size_of, .. } = trim_helpers();
|
||||
let compact = CompactOf::<Runtime>::try_from(assignments.as_slice()).unwrap();
|
||||
let encoded_len = compact.encoded_size() as u32;
|
||||
let compact_clone = compact.clone();
|
||||
|
||||
// when
|
||||
MultiPhase::trim_assignments_length(
|
||||
encoded_len,
|
||||
&mut assignments,
|
||||
encoded_size_of,
|
||||
).unwrap();
|
||||
MultiPhase::trim_assignments_length(encoded_len, &mut assignments, encoded_size_of)
|
||||
.unwrap();
|
||||
|
||||
// then
|
||||
let compact = CompactOf::<Runtime>::try_from(assignments.as_slice()).unwrap();
|
||||
@@ -1412,11 +1412,7 @@ mod tests {
|
||||
roll_to(25);
|
||||
|
||||
// given
|
||||
let TrimHelpers {
|
||||
mut assignments,
|
||||
encoded_size_of,
|
||||
..
|
||||
} = trim_helpers();
|
||||
let TrimHelpers { mut assignments, encoded_size_of, .. } = trim_helpers();
|
||||
let compact = CompactOf::<Runtime>::try_from(assignments.as_slice()).unwrap();
|
||||
let encoded_len = compact.encoded_size();
|
||||
let compact_clone = compact.clone();
|
||||
@@ -1426,7 +1422,8 @@ mod tests {
|
||||
encoded_len as u32 - 1,
|
||||
&mut assignments,
|
||||
encoded_size_of,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// then
|
||||
let compact = CompactOf::<Runtime>::try_from(assignments.as_slice()).unwrap();
|
||||
@@ -1441,33 +1438,26 @@ mod tests {
|
||||
roll_to(25);
|
||||
|
||||
// given
|
||||
let TrimHelpers {
|
||||
voters,
|
||||
mut assignments,
|
||||
encoded_size_of,
|
||||
voter_index,
|
||||
} = trim_helpers();
|
||||
let TrimHelpers { voters, mut assignments, encoded_size_of, voter_index } =
|
||||
trim_helpers();
|
||||
let compact = CompactOf::<Runtime>::try_from(assignments.as_slice()).unwrap();
|
||||
let encoded_len = compact.encoded_size() as u32;
|
||||
let count = assignments.len();
|
||||
let min_stake_voter = voters.iter()
|
||||
let min_stake_voter = voters
|
||||
.iter()
|
||||
.map(|(id, weight, _)| (weight, id))
|
||||
.min()
|
||||
.and_then(|(_, id)| voter_index(id))
|
||||
.unwrap();
|
||||
|
||||
// when
|
||||
MultiPhase::trim_assignments_length(
|
||||
encoded_len - 1,
|
||||
&mut assignments,
|
||||
encoded_size_of,
|
||||
).unwrap();
|
||||
MultiPhase::trim_assignments_length(encoded_len - 1, &mut assignments, encoded_size_of)
|
||||
.unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(assignments.len(), count - 1, "we must have removed exactly one assignment");
|
||||
assert!(
|
||||
assignments.iter()
|
||||
.all(|IndexAssignment{ who, ..}| *who != min_stake_voter),
|
||||
assignments.iter().all(|IndexAssignment { who, .. }| *who != min_stake_voter),
|
||||
"min_stake_voter must no longer be in the set of voters",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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