mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 02:21:04 +00:00
Create trait for NPoS election algorithms (#9664)
* build the template, hand it over to zeke now. * Tests working * save wip * Some updates * Some cleanup * mo cleanin * Link to issue * Apply suggestions from code review Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Bound accuracy for prepare_election_result * Use npos_election::Error for phragmms * save * Apply suggestions from code review * Simplify test to use Balancing::set * Cargo.lock after build * Revert "Cargo.lock after build" This reverts commit 7d726c8efa687c09e4f377196b106eb9e9760487. * Try reduce cargo.lock diff * Update bin/node/runtime/src/lib.rs * Comment * Apply suggestions from code review * Set balancing directly * Document som pub items * Update frame/election-provider-multi-phase/src/unsigned.rs * Apply suggestions from code review Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Improve some comments * Revert accidental change to random file * tiney * revert Co-authored-by: kianenigma <kian@parity.io> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
@@ -485,13 +485,13 @@ pub struct SolutionOrSnapshotSize {
|
||||
/// Internal errors of the pallet.
|
||||
///
|
||||
/// Note that this is different from [`pallet::Error`].
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[derive(frame_support::DebugNoBound, frame_support::PartialEqNoBound)]
|
||||
#[cfg_attr(feature = "runtime-benchmarks", derive(strum_macros::IntoStaticStr))]
|
||||
pub enum ElectionError {
|
||||
pub enum ElectionError<T: Config> {
|
||||
/// An error happened in the feasibility check sub-system.
|
||||
Feasibility(FeasibilityError),
|
||||
/// An error in the miner (offchain) sub-system.
|
||||
Miner(unsigned::MinerError),
|
||||
Miner(unsigned::MinerError<T>),
|
||||
/// An error in the on-chain fallback.
|
||||
OnChainFallback(onchain::Error),
|
||||
/// An error happened in the data provider.
|
||||
@@ -500,20 +500,20 @@ pub enum ElectionError {
|
||||
NoFallbackConfigured,
|
||||
}
|
||||
|
||||
impl From<onchain::Error> for ElectionError {
|
||||
impl<T: Config> From<onchain::Error> for ElectionError<T> {
|
||||
fn from(e: onchain::Error) -> Self {
|
||||
ElectionError::OnChainFallback(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FeasibilityError> for ElectionError {
|
||||
impl<T: Config> From<FeasibilityError> for ElectionError<T> {
|
||||
fn from(e: FeasibilityError) -> Self {
|
||||
ElectionError::Feasibility(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<unsigned::MinerError> for ElectionError {
|
||||
fn from(e: unsigned::MinerError) -> Self {
|
||||
impl<T: Config> From<unsigned::MinerError<T>> for ElectionError<T> {
|
||||
fn from(e: unsigned::MinerError<T>) -> Self {
|
||||
ElectionError::Miner(e)
|
||||
}
|
||||
}
|
||||
@@ -555,6 +555,7 @@ pub use pallet::*;
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
use frame_election_provider_support::NposSolver;
|
||||
use frame_support::{pallet_prelude::*, traits::EstimateCallFee};
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
@@ -592,10 +593,6 @@ pub mod pallet {
|
||||
/// The priority of the unsigned transaction submitted in the unsigned-phase
|
||||
#[pallet::constant]
|
||||
type MinerTxPriority: Get<TransactionPriority>;
|
||||
/// Maximum number of iteration of balancing that will be executed in the embedded miner of
|
||||
/// the pallet.
|
||||
#[pallet::constant]
|
||||
type MinerMaxIterations: Get<u32>;
|
||||
|
||||
/// Maximum weight that the miner should consume.
|
||||
///
|
||||
@@ -668,6 +665,9 @@ pub mod pallet {
|
||||
/// Configuration for the fallback
|
||||
type Fallback: Get<FallbackStrategy>;
|
||||
|
||||
/// OCW election solution miner algorithm implementation.
|
||||
type Solver: NposSolver<AccountId = Self::AccountId>;
|
||||
|
||||
/// Origin that can control this pallet. Note that any action taken by this origin (such)
|
||||
/// as providing an emergency solution is not checked. Thus, it must be a trusted origin.
|
||||
type ForceOrigin: EnsureOrigin<Self::Origin>;
|
||||
@@ -1298,7 +1298,7 @@ impl<T: Config> Pallet<T> {
|
||||
///
|
||||
/// Extracted for easier weight calculation.
|
||||
fn create_snapshot_external(
|
||||
) -> Result<(Vec<T::AccountId>, Vec<crate::unsigned::Voter<T>>, u32), ElectionError> {
|
||||
) -> Result<(Vec<T::AccountId>, Vec<crate::unsigned::Voter<T>>, u32), ElectionError<T>> {
|
||||
let target_limit = <SolutionTargetIndexOf<T>>::max_value().saturated_into::<usize>();
|
||||
let voter_limit = <SolutionVoterIndexOf<T>>::max_value().saturated_into::<usize>();
|
||||
|
||||
@@ -1328,7 +1328,7 @@ impl<T: Config> Pallet<T> {
|
||||
///
|
||||
/// This is a *self-weighing* function, it will register its own extra weight as
|
||||
/// [`DispatchClass::Mandatory`] with the system pallet.
|
||||
pub fn create_snapshot() -> Result<(), ElectionError> {
|
||||
pub fn create_snapshot() -> Result<(), ElectionError<T>> {
|
||||
// this is self-weighing itself..
|
||||
let (targets, voters, desired_targets) = Self::create_snapshot_external()?;
|
||||
|
||||
@@ -1471,7 +1471,7 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
|
||||
/// On-chain fallback of election.
|
||||
fn onchain_fallback() -> Result<Supports<T::AccountId>, ElectionError> {
|
||||
fn onchain_fallback() -> Result<Supports<T::AccountId>, ElectionError<T>> {
|
||||
<onchain::OnChainSequentialPhragmen<OnChainConfig<T>> as ElectionProvider<
|
||||
T::AccountId,
|
||||
T::BlockNumber,
|
||||
@@ -1479,7 +1479,7 @@ impl<T: Config> Pallet<T> {
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn do_elect() -> Result<Supports<T::AccountId>, ElectionError> {
|
||||
fn do_elect() -> Result<Supports<T::AccountId>, ElectionError<T>> {
|
||||
// We have to unconditionally try finalizing the signed phase here. There are only two
|
||||
// possibilities:
|
||||
//
|
||||
@@ -1530,7 +1530,7 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
|
||||
impl<T: Config> ElectionProvider<T::AccountId, T::BlockNumber> for Pallet<T> {
|
||||
type Error = ElectionError;
|
||||
type Error = ElectionError<T>;
|
||||
type DataProvider = T::DataProvider;
|
||||
|
||||
fn elect() -> Result<Supports<T::AccountId>, Self::Error> {
|
||||
@@ -2013,7 +2013,10 @@ mod tests {
|
||||
roll_to(15);
|
||||
assert_eq!(MultiPhase::current_phase(), Phase::Signed);
|
||||
|
||||
let (solution, _) = MultiPhase::mine_solution(2).unwrap();
|
||||
// set the solution balancing to get the desired score.
|
||||
crate::mock::Balancing::set(Some((2, 0)));
|
||||
|
||||
let (solution, _) = MultiPhase::mine_solution::<<Runtime as Config>::Solver>().unwrap();
|
||||
// Default solution has a score of [50, 100, 5000].
|
||||
assert_eq!(solution.score, [50, 100, 5000]);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
use super::*;
|
||||
use crate as multi_phase;
|
||||
use frame_election_provider_support::{data_provider, ElectionDataProvider};
|
||||
use frame_election_provider_support::{data_provider, ElectionDataProvider, SequentialPhragmen};
|
||||
pub use frame_support::{assert_noop, assert_ok};
|
||||
use frame_support::{parameter_types, traits::Hooks, weights::Weight};
|
||||
use multi_phase::unsigned::{IndexAssignmentOf, Voter};
|
||||
@@ -31,7 +31,7 @@ use sp_core::{
|
||||
};
|
||||
use sp_npos_elections::{
|
||||
assignment_ratio_to_staked_normalized, seq_phragmen, to_supports, to_without_backing,
|
||||
ElectionResult, EvaluateSupport, NposSolution,
|
||||
ElectionResult, EvaluateSupport, ExtendedBalance, NposSolution,
|
||||
};
|
||||
use sp_runtime::{
|
||||
testing::Header,
|
||||
@@ -262,7 +262,6 @@ parameter_types! {
|
||||
pub static SignedDepositWeight: Balance = 0;
|
||||
pub static SignedRewardBase: Balance = 7;
|
||||
pub static SignedMaxWeight: Weight = BlockWeights::get().max_block;
|
||||
pub static MinerMaxIterations: u32 = 5;
|
||||
pub static MinerTxPriority: u64 = 100;
|
||||
pub static SolutionImprovementThreshold: Perbill = Perbill::zero();
|
||||
pub static OffchainRepeat: BlockNumber = 5;
|
||||
@@ -352,6 +351,10 @@ impl multi_phase::weights::WeightInfo for DualMockWeightInfo {
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub static Balancing: Option<(usize, ExtendedBalance)> = Some((0, 0));
|
||||
}
|
||||
|
||||
impl crate::Config for Runtime {
|
||||
type Event = Event;
|
||||
type Currency = Balances;
|
||||
@@ -360,7 +363,6 @@ impl crate::Config for Runtime {
|
||||
type UnsignedPhase = UnsignedPhase;
|
||||
type SolutionImprovementThreshold = SolutionImprovementThreshold;
|
||||
type OffchainRepeat = OffchainRepeat;
|
||||
type MinerMaxIterations = MinerMaxIterations;
|
||||
type MinerMaxWeight = MinerMaxWeight;
|
||||
type MinerMaxLength = MinerMaxLength;
|
||||
type MinerTxPriority = MinerTxPriority;
|
||||
@@ -379,6 +381,7 @@ impl crate::Config for Runtime {
|
||||
type Fallback = Fallback;
|
||||
type ForceOrigin = frame_system::EnsureRoot<AccountId>;
|
||||
type Solution = TestNposSolution;
|
||||
type Solver = SequentialPhragmen<AccountId, SolutionAccuracyOf<Runtime>, Balancing>;
|
||||
}
|
||||
|
||||
impl<LocalCall> frame_system::offchain::SendTransactionTypes<LocalCall> for Runtime
|
||||
|
||||
@@ -836,7 +836,8 @@ mod tests {
|
||||
roll_to(15);
|
||||
assert!(MultiPhase::current_phase().is_signed());
|
||||
|
||||
let (raw, witness) = MultiPhase::mine_solution(2).unwrap();
|
||||
let (raw, witness) =
|
||||
MultiPhase::mine_solution::<<Runtime as Config>::Solver>().unwrap();
|
||||
let solution_weight = <Runtime as Config>::WeightInfo::feasibility_check(
|
||||
witness.voters,
|
||||
witness.targets,
|
||||
|
||||
@@ -22,17 +22,17 @@ use crate::{
|
||||
ReadySolution, RoundSnapshot, SolutionAccuracyOf, SolutionOf, SolutionOrSnapshotSize, Weight,
|
||||
WeightInfo,
|
||||
};
|
||||
use codec::{Decode, Encode};
|
||||
use codec::Encode;
|
||||
use frame_election_provider_support::{NposSolver, PerThing128};
|
||||
use frame_support::{dispatch::DispatchResult, ensure, traits::Get};
|
||||
use frame_system::offchain::SubmitTransaction;
|
||||
use sp_arithmetic::Perbill;
|
||||
use sp_npos_elections::{
|
||||
assignment_ratio_to_staked_normalized, assignment_staked_to_ratio_normalized, is_score_better,
|
||||
seq_phragmen, ElectionResult, NposSolution,
|
||||
ElectionResult, NposSolution,
|
||||
};
|
||||
use sp_runtime::{
|
||||
offchain::storage::{MutateStorageError, StorageValueRef},
|
||||
traits::TrailingZeroInput,
|
||||
DispatchError, SaturatedConversion,
|
||||
};
|
||||
use sp_std::{boxed::Box, cmp::Ordering, convert::TryFrom, vec::Vec};
|
||||
@@ -61,8 +61,11 @@ pub type Assignment<T> =
|
||||
/// runtime `T`.
|
||||
pub type IndexAssignmentOf<T> = sp_npos_elections::IndexAssignmentOf<SolutionOf<T>>;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum MinerError {
|
||||
/// Error type of the pallet's [`crate::Config::Solver`].
|
||||
pub type SolverErrorOf<T> = <<T as Config>::Solver as NposSolver>::Error;
|
||||
/// Error type for operations related to the OCW npos solution miner.
|
||||
#[derive(frame_support::DebugNoBound, frame_support::PartialEqNoBound)]
|
||||
pub enum MinerError<T: Config> {
|
||||
/// An internal error in the NPoS elections crate.
|
||||
NposElections(sp_npos_elections::Error),
|
||||
/// Snapshot data was unavailable unexpectedly.
|
||||
@@ -83,22 +86,24 @@ pub enum MinerError {
|
||||
FailedToStoreSolution,
|
||||
/// There are no more voters to remove to trim the solution.
|
||||
NoMoreVoters,
|
||||
/// An error from the solver.
|
||||
Solver(SolverErrorOf<T>),
|
||||
}
|
||||
|
||||
impl From<sp_npos_elections::Error> for MinerError {
|
||||
impl<T: Config> From<sp_npos_elections::Error> for MinerError<T> {
|
||||
fn from(e: sp_npos_elections::Error) -> Self {
|
||||
MinerError::NposElections(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FeasibilityError> for MinerError {
|
||||
impl<T: Config> From<FeasibilityError> for MinerError<T> {
|
||||
fn from(e: FeasibilityError) -> Self {
|
||||
MinerError::Feasibility(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Save a given call into OCW storage.
|
||||
fn save_solution<T: Config>(call: &Call<T>) -> Result<(), MinerError> {
|
||||
fn save_solution<T: Config>(call: &Call<T>) -> Result<(), MinerError<T>> {
|
||||
log!(debug, "saving a call to the offchain storage.");
|
||||
let storage = StorageValueRef::persistent(&OFFCHAIN_CACHED_CALL);
|
||||
match storage.mutate::<_, (), _>(|_| Ok(call.clone())) {
|
||||
@@ -116,7 +121,7 @@ fn save_solution<T: Config>(call: &Call<T>) -> Result<(), MinerError> {
|
||||
}
|
||||
|
||||
/// Get a saved solution from OCW storage if it exists.
|
||||
fn restore_solution<T: Config>() -> Result<Call<T>, MinerError> {
|
||||
fn restore_solution<T: Config>() -> Result<Call<T>, MinerError<T>> {
|
||||
StorageValueRef::persistent(&OFFCHAIN_CACHED_CALL)
|
||||
.get()
|
||||
.ok()
|
||||
@@ -149,7 +154,7 @@ fn ocw_solution_exists<T: Config>() -> bool {
|
||||
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> {
|
||||
pub fn restore_or_compute_then_maybe_submit() -> Result<(), MinerError<T>> {
|
||||
log!(debug, "miner attempting to restore or compute an unsigned solution.");
|
||||
|
||||
let call = restore_solution::<T>()
|
||||
@@ -163,7 +168,7 @@ impl<T: Config> Pallet<T> {
|
||||
Err(MinerError::SolutionCallInvalid)
|
||||
}
|
||||
})
|
||||
.or_else::<MinerError, _>(|error| {
|
||||
.or_else::<MinerError<T>, _>(|error| {
|
||||
log!(debug, "restoring solution failed due to {:?}", error);
|
||||
match error {
|
||||
MinerError::NoStoredSolution => {
|
||||
@@ -194,7 +199,7 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
|
||||
/// Mine a new solution, cache it, and submit it back to the chain as an unsigned transaction.
|
||||
pub fn mine_check_save_submit() -> Result<(), MinerError> {
|
||||
pub fn mine_check_save_submit() -> Result<(), MinerError<T>> {
|
||||
log!(debug, "miner attempting to compute an unsigned solution.");
|
||||
|
||||
let call = Self::mine_checked_call()?;
|
||||
@@ -203,10 +208,9 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
|
||||
/// Mine a new solution as a call. Performs all checks.
|
||||
pub fn mine_checked_call() -> Result<Call<T>, MinerError> {
|
||||
let iters = Self::get_balancing_iters();
|
||||
pub fn mine_checked_call() -> Result<Call<T>, MinerError<T>> {
|
||||
// get the solution, with a load of checks to ensure if submitted, IT IS ABSOLUTELY VALID.
|
||||
let (raw_solution, witness) = Self::mine_and_check(iters)?;
|
||||
let (raw_solution, witness) = Self::mine_and_check()?;
|
||||
|
||||
let score = raw_solution.score.clone();
|
||||
let call: Call<T> = Call::submit_unsigned(Box::new(raw_solution), witness).into();
|
||||
@@ -221,7 +225,7 @@ impl<T: Config> Pallet<T> {
|
||||
Ok(call)
|
||||
}
|
||||
|
||||
fn submit_call(call: Call<T>) -> Result<(), MinerError> {
|
||||
fn submit_call(call: Call<T>) -> Result<(), MinerError<T>> {
|
||||
log!(debug, "miner submitting a solution as an unsigned transaction");
|
||||
|
||||
SubmitTransaction::<T, Call<T>>::submit_unsigned_transaction(call.into())
|
||||
@@ -234,7 +238,7 @@ impl<T: Config> Pallet<T> {
|
||||
pub fn basic_checks(
|
||||
raw_solution: &RawSolution<SolutionOf<T>>,
|
||||
solution_type: &str,
|
||||
) -> Result<(), MinerError> {
|
||||
) -> Result<(), MinerError<T>> {
|
||||
Self::unsigned_pre_dispatch_checks(raw_solution).map_err(|err| {
|
||||
log!(debug, "pre-dispatch checks failed for {} solution: {:?}", solution_type, err);
|
||||
MinerError::PreDispatchChecksFailed(err)
|
||||
@@ -257,38 +261,37 @@ impl<T: Config> Pallet<T> {
|
||||
/// If you want a checked solution and submit it at the same time, use
|
||||
/// [`Pallet::mine_check_save_submit`].
|
||||
pub fn mine_and_check(
|
||||
iters: usize,
|
||||
) -> Result<(RawSolution<SolutionOf<T>>, SolutionOrSnapshotSize), MinerError> {
|
||||
let (raw_solution, witness) = Self::mine_solution(iters)?;
|
||||
) -> Result<(RawSolution<SolutionOf<T>>, SolutionOrSnapshotSize), MinerError<T>> {
|
||||
let (raw_solution, witness) = Self::mine_solution::<T::Solver>()?;
|
||||
Self::basic_checks(&raw_solution, "mined")?;
|
||||
Ok((raw_solution, witness))
|
||||
}
|
||||
|
||||
/// Mine a new npos solution.
|
||||
pub fn mine_solution(
|
||||
iters: usize,
|
||||
) -> Result<(RawSolution<SolutionOf<T>>, SolutionOrSnapshotSize), MinerError> {
|
||||
///
|
||||
/// The Npos Solver type, `S`, must have the same AccountId and Error type as the
|
||||
/// [`crate::Config::Solver`] in order to create a unified return type.
|
||||
pub fn mine_solution<S>(
|
||||
) -> Result<(RawSolution<SolutionOf<T>>, SolutionOrSnapshotSize), MinerError<T>>
|
||||
where
|
||||
S: NposSolver<AccountId = T::AccountId, Error = SolverErrorOf<T>>,
|
||||
{
|
||||
let RoundSnapshot { voters, targets } =
|
||||
Self::snapshot().ok_or(MinerError::SnapshotUnAvailable)?;
|
||||
let desired_targets = Self::desired_targets().ok_or(MinerError::SnapshotUnAvailable)?;
|
||||
|
||||
seq_phragmen::<_, SolutionAccuracyOf<T>>(
|
||||
desired_targets as usize,
|
||||
targets,
|
||||
voters,
|
||||
Some((iters, 0)),
|
||||
)
|
||||
.map_err(Into::into)
|
||||
.and_then(Self::prepare_election_result)
|
||||
S::solve(desired_targets as usize, targets, voters)
|
||||
.map_err(|e| MinerError::Solver::<T>(e))
|
||||
.and_then(|e| Self::prepare_election_result::<S::Accuracy>(e))
|
||||
}
|
||||
|
||||
/// Convert a raw solution from [`sp_npos_elections::ElectionResult`] to [`RawSolution`], which
|
||||
/// is ready to be submitted to the chain.
|
||||
///
|
||||
/// Will always reduce the solution as well.
|
||||
pub fn prepare_election_result(
|
||||
election_result: ElectionResult<T::AccountId, SolutionAccuracyOf<T>>,
|
||||
) -> Result<(RawSolution<SolutionOf<T>>, SolutionOrSnapshotSize), MinerError> {
|
||||
pub fn prepare_election_result<Accuracy: PerThing128>(
|
||||
election_result: ElectionResult<T::AccountId, Accuracy>,
|
||||
) -> Result<(RawSolution<SolutionOf<T>>, SolutionOrSnapshotSize), MinerError<T>> {
|
||||
// NOTE: This code path is generally not optimized as it is run offchain. Could use some at
|
||||
// some point though.
|
||||
|
||||
@@ -378,23 +381,6 @@ impl<T: Config> Pallet<T> {
|
||||
Ok((RawSolution { solution, score, round }, size))
|
||||
}
|
||||
|
||||
/// Get a random number of iterations to run the balancing in the OCW.
|
||||
///
|
||||
/// Uses the offchain seed to generate a random number, maxed with
|
||||
/// [`Config::MinerMaxIterations`].
|
||||
pub fn get_balancing_iters() -> usize {
|
||||
match T::MinerMaxIterations::get() {
|
||||
0 => 0,
|
||||
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);
|
||||
random as usize
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Greedily reduce the size of the solution to fit into the block w.r.t. weight.
|
||||
///
|
||||
/// The weight of the solution is foremost a function of the number of voters (i.e.
|
||||
@@ -448,7 +434,7 @@ impl<T: Config> Pallet<T> {
|
||||
max_allowed_length: u32,
|
||||
assignments: &mut Vec<IndexAssignmentOf<T>>,
|
||||
encoded_size_of: impl Fn(&[IndexAssignmentOf<T>]) -> Result<usize, sp_npos_elections::Error>,
|
||||
) -> Result<(), MinerError> {
|
||||
) -> Result<(), MinerError<T>> {
|
||||
// Perform a binary search for the max subset of which can fit into the allowed
|
||||
// length. Having discovered that, we can truncate efficiently.
|
||||
let max_allowed_length: usize = max_allowed_length.saturated_into();
|
||||
@@ -584,7 +570,7 @@ impl<T: Config> Pallet<T> {
|
||||
///
|
||||
/// Returns `Ok(())` if offchain worker limit is respected, `Err(reason)` otherwise. If `Ok()`
|
||||
/// is returned, `now` is written in storage and will be used in further calls as the baseline.
|
||||
pub fn ensure_offchain_repeat_frequency(now: T::BlockNumber) -> Result<(), MinerError> {
|
||||
pub fn ensure_offchain_repeat_frequency(now: T::BlockNumber) -> Result<(), MinerError<T>> {
|
||||
let threshold = T::OffchainRepeat::get();
|
||||
let last_block = StorageValueRef::persistent(&OFFCHAIN_LAST_BLOCK);
|
||||
|
||||
@@ -761,6 +747,7 @@ mod tests {
|
||||
CurrentPhase, InvalidTransaction, Phase, QueuedSolution, TransactionSource,
|
||||
TransactionValidityError,
|
||||
};
|
||||
use codec::Decode;
|
||||
use frame_benchmarking::Zero;
|
||||
use frame_support::{assert_noop, assert_ok, dispatch::Dispatchable, traits::OffchainWorker};
|
||||
use sp_npos_elections::IndexAssignment;
|
||||
@@ -975,7 +962,8 @@ mod tests {
|
||||
assert_eq!(MultiPhase::desired_targets().unwrap(), 2);
|
||||
|
||||
// mine seq_phragmen solution with 2 iters.
|
||||
let (solution, witness) = MultiPhase::mine_solution(2).unwrap();
|
||||
let (solution, witness) =
|
||||
MultiPhase::mine_solution::<<Runtime as Config>::Solver>().unwrap();
|
||||
|
||||
// ensure this solution is valid.
|
||||
assert!(MultiPhase::queued_solution().is_none());
|
||||
@@ -993,7 +981,8 @@ mod tests {
|
||||
roll_to(25);
|
||||
assert!(MultiPhase::current_phase().is_unsigned());
|
||||
|
||||
let (raw, witness) = MultiPhase::mine_solution(2).unwrap();
|
||||
let (raw, witness) =
|
||||
MultiPhase::mine_solution::<<Runtime as Config>::Solver>().unwrap();
|
||||
let solution_weight = <Runtime as Config>::WeightInfo::submit_unsigned(
|
||||
witness.voters,
|
||||
witness.targets,
|
||||
@@ -1007,7 +996,8 @@ mod tests {
|
||||
// now reduce the max weight
|
||||
<MinerMaxWeight>::set(25);
|
||||
|
||||
let (raw, witness) = MultiPhase::mine_solution(2).unwrap();
|
||||
let (raw, witness) =
|
||||
MultiPhase::mine_solution::<<Runtime as Config>::Solver>().unwrap();
|
||||
let solution_weight = <Runtime as Config>::WeightInfo::submit_unsigned(
|
||||
witness.voters,
|
||||
witness.targets,
|
||||
@@ -1359,7 +1349,7 @@ mod tests {
|
||||
// OCW must have submitted now
|
||||
|
||||
let encoded = pool.read().transactions[0].clone();
|
||||
let extrinsic: Extrinsic = Decode::decode(&mut &*encoded).unwrap();
|
||||
let extrinsic: Extrinsic = codec::Decode::decode(&mut &*encoded).unwrap();
|
||||
let call = extrinsic.call;
|
||||
assert!(matches!(call, OuterCall::MultiPhase(Call::submit_unsigned(..))));
|
||||
})
|
||||
@@ -1534,14 +1524,14 @@ mod tests {
|
||||
roll_to(25);
|
||||
|
||||
// how long would the default solution be?
|
||||
let solution = MultiPhase::mine_solution(0).unwrap();
|
||||
let solution = MultiPhase::mine_solution::<<Runtime as Config>::Solver>().unwrap();
|
||||
let max_length = <Runtime as Config>::MinerMaxLength::get();
|
||||
let solution_size = solution.0.solution.encoded_size();
|
||||
assert!(solution_size <= max_length as usize);
|
||||
|
||||
// now set the max size to less than the actual size and regenerate
|
||||
<Runtime as Config>::MinerMaxLength::set(solution_size as u32 - 1);
|
||||
let solution = MultiPhase::mine_solution(0).unwrap();
|
||||
let solution = MultiPhase::mine_solution::<<Runtime as Config>::Solver>().unwrap();
|
||||
let max_length = <Runtime as Config>::MinerMaxLength::get();
|
||||
let solution_size = solution.0.solution.encoded_size();
|
||||
assert!(solution_size <= max_length as usize);
|
||||
|
||||
Reference in New Issue
Block a user