New sub-trait to mock staking miner (#11350)

* new separate config trait for staking miner

* fix some docs and stuff

* relax trait bounds

* some cleanup

* Update frame/election-provider-multi-phase/src/unsigned.rs

* add comment

* self review and fix build

* fix docs

Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
This commit is contained in:
Kian Paimani
2022-05-11 18:45:59 +01:00
committed by GitHub
parent 479dc63af4
commit d06d20d65b
7 changed files with 479 additions and 465 deletions
@@ -103,7 +103,7 @@
//!
//! Validators will only submit solutions if the one that they have computed is sufficiently better
//! than the best queued one (see [`pallet::Config::BetterUnsignedThreshold`]) and will limit the
//! weight of the solution to [`pallet::Config::MinerMaxWeight`].
//! weight of the solution to [`MinerConfig::MaxWeight`].
//!
//! The unsigned phase can be made passive depending on how the previous signed phase went, by
//! setting the first inner value of [`Phase`] to `false`. For now, the signed phase is always
@@ -276,16 +276,18 @@ pub use signed::{
BalanceOf, NegativeImbalanceOf, PositiveImbalanceOf, SignedSubmission, SignedSubmissionOf,
SignedSubmissions, SubmissionIndicesOf,
};
pub use unsigned::{Miner, MinerConfig};
/// The solution type used by this crate.
pub type SolutionOf<T> = <T as Config>::Solution;
pub type SolutionOf<T> = <T as MinerConfig>::Solution;
/// The voter index. Derived from [`SolutionOf`].
pub type SolutionVoterIndexOf<T> = <SolutionOf<T> as NposSolution>::VoterIndex;
/// The target index. Derived from [`SolutionOf`].
pub type SolutionTargetIndexOf<T> = <SolutionOf<T> as NposSolution>::TargetIndex;
/// The accuracy of the election, when submitted from offchain. Derived from [`SolutionOf`].
pub type SolutionAccuracyOf<T> = <SolutionOf<T> as NposSolution>::Accuracy;
pub type SolutionAccuracyOf<T> =
<SolutionOf<<T as crate::Config>::MinerConfig> as NposSolution>::Accuracy;
/// The fallback election type.
pub type FallbackErrorOf<T> = <<T as crate::Config>::Fallback as ElectionProvider>::Error;
@@ -488,7 +490,7 @@ 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<T>),
Miner(unsigned::MinerError),
/// An error happened in the data provider.
DataProvider(&'static str),
/// An error nested in the fallback.
@@ -520,8 +522,8 @@ impl<T: Config> From<FeasibilityError> for ElectionError<T> {
}
}
impl<T: Config> From<unsigned::MinerError<T>> for ElectionError<T> {
fn from(e: unsigned::MinerError<T>) -> Self {
impl<T: Config> From<unsigned::MinerError> for ElectionError<T> {
fn from(e: unsigned::MinerError) -> Self {
ElectionError::Miner(e)
}
}
@@ -605,12 +607,14 @@ pub mod pallet {
#[pallet::constant]
type MinerTxPriority: Get<TransactionPriority>;
/// Maximum weight that the miner should consume.
/// Configurations of the embedded miner.
///
/// The miner will ensure that the total weight of the unsigned solution will not exceed
/// this value, based on [`WeightInfo::submit_unsigned`].
#[pallet::constant]
type MinerMaxWeight: Get<Weight>;
/// Any external software implementing this can use the [`unsigned::Miner`] type provided,
/// which can mine new solutions and trim them accordingly.
type MinerConfig: crate::unsigned::MinerConfig<
AccountId = Self::AccountId,
MaxVotesPerVoter = <Self::DataProvider as ElectionDataProvider>::MaxVotesPerVoter,
>;
/// Maximum number of signed submissions that can be queued.
///
@@ -624,7 +628,9 @@ pub mod pallet {
/// Maximum weight of a signed solution.
///
/// This should probably be similar to [`Config::MinerMaxWeight`].
/// If [`Config::MinerConfig`] is being implemented to submit signed solutions (outside of
/// this pallet), then [`MinerConfig::solution_weight`] is used to compare against
/// this value.
#[pallet::constant]
type SignedMaxWeight: Get<Weight>;
@@ -652,11 +658,11 @@ pub mod pallet {
/// are only over a single block, but once multi-block elections are introduced they will
/// take place over multiple blocks.
#[pallet::constant]
type MaxElectingVoters: Get<SolutionVoterIndexOf<Self>>;
type MaxElectingVoters: Get<SolutionVoterIndexOf<Self::MinerConfig>>;
/// The maximum number of electable targets to put in the snapshot.
#[pallet::constant]
type MaxElectableTargets: Get<SolutionTargetIndexOf<Self>>;
type MaxElectableTargets: Get<SolutionTargetIndexOf<Self::MinerConfig>>;
/// Handler for the slashed deposits.
type SlashHandler: OnUnbalanced<NegativeImbalanceOf<Self>>;
@@ -664,30 +670,12 @@ pub mod pallet {
/// Handler for the rewards.
type RewardHandler: OnUnbalanced<PositiveImbalanceOf<Self>>;
/// Maximum length (bytes) that the mined solution should consume.
///
/// The miner will ensure that the total length of the unsigned solution will not exceed
/// this value.
#[pallet::constant]
type MinerMaxLength: Get<u32>;
/// Something that will provide the election data.
type DataProvider: ElectionDataProvider<
AccountId = Self::AccountId,
BlockNumber = Self::BlockNumber,
>;
/// The solution type.
type Solution: codec::Codec
+ Default
+ PartialEq
+ Eq
+ Clone
+ sp_std::fmt::Debug
+ Ord
+ NposSolution
+ TypeInfo;
/// Configuration for the fallback.
type Fallback: InstantElectionProvider<
AccountId = Self::AccountId,
@@ -824,12 +812,12 @@ pub mod pallet {
use sp_std::mem::size_of;
// The index type of both voters and targets need to be smaller than that of usize (very
// unlikely to be the case, but anyhow)..
assert!(size_of::<SolutionVoterIndexOf<T>>() <= size_of::<usize>());
assert!(size_of::<SolutionTargetIndexOf<T>>() <= size_of::<usize>());
assert!(size_of::<SolutionVoterIndexOf<T::MinerConfig>>() <= size_of::<usize>());
assert!(size_of::<SolutionTargetIndexOf<T::MinerConfig>>() <= size_of::<usize>());
// ----------------------------
// Based on the requirements of [`sp_npos_elections::Assignment::try_normalize`].
let max_vote: usize = <SolutionOf<T> as NposSolution>::LIMIT;
let max_vote: usize = <SolutionOf<T::MinerConfig> as NposSolution>::LIMIT;
// 2. Maximum sum of [SolutionAccuracy; 16] must fit into `UpperOf<OffchainAccuracy>`.
let maximum_chain_accuracy: Vec<UpperOf<SolutionAccuracyOf<T>>> = (0..max_vote)
@@ -850,7 +838,7 @@ pub mod pallet {
// solution cannot represent any voters more than `LIMIT` anyhow.
assert_eq!(
<T::DataProvider as ElectionDataProvider>::MaxVotesPerVoter::get(),
<SolutionOf<T> as NposSolution>::LIMIT as u32,
<SolutionOf<T::MinerConfig> as NposSolution>::LIMIT as u32,
);
// While it won't cause any failures, setting `SignedMaxRefunds` gt
@@ -887,7 +875,7 @@ pub mod pallet {
))]
pub fn submit_unsigned(
origin: OriginFor<T>,
raw_solution: Box<RawSolution<SolutionOf<T>>>,
raw_solution: Box<RawSolution<SolutionOf<T::MinerConfig>>>,
witness: SolutionOrSnapshotSize,
) -> DispatchResultWithPostInfo {
ensure_none(origin)?;
@@ -976,7 +964,7 @@ pub mod pallet {
#[pallet::weight(T::WeightInfo::submit())]
pub fn submit(
origin: OriginFor<T>,
raw_solution: Box<RawSolution<SolutionOf<T>>>,
raw_solution: Box<RawSolution<SolutionOf<T::MinerConfig>>>,
) -> DispatchResult {
let who = ensure_signed(origin)?;
@@ -991,7 +979,7 @@ pub mod pallet {
let size = Self::snapshot_metadata().ok_or(Error::<T>::MissingSnapshotMetadata)?;
ensure!(
Self::feasibility_weight_of(&raw_solution, size) < T::SignedMaxWeight::get(),
Self::solution_weight_of(&raw_solution, size) < T::SignedMaxWeight::get(),
Error::<T>::SignedTooMuchWeight,
);
@@ -1429,7 +1417,7 @@ impl<T: Config> Pallet<T> {
/// Checks the feasibility of a solution.
pub fn feasibility_check(
raw_solution: RawSolution<SolutionOf<T>>,
raw_solution: RawSolution<SolutionOf<T::MinerConfig>>,
compute: ElectionCompute,
) -> Result<ReadySolution<T::AccountId>, FeasibilityError> {
let RawSolution { solution, score, round } = raw_solution;
@@ -1459,10 +1447,10 @@ impl<T: Config> Pallet<T> {
Self::snapshot().ok_or(FeasibilityError::SnapshotUnavailable)?;
// ----- Start building. First, we need some closures.
let cache = helpers::generate_voter_cache::<T>(&snapshot_voters);
let voter_at = helpers::voter_at_fn::<T>(&snapshot_voters);
let target_at = helpers::target_at_fn::<T>(&snapshot_targets);
let voter_index = helpers::voter_index_fn_usize::<T>(&cache);
let cache = helpers::generate_voter_cache::<T::MinerConfig>(&snapshot_voters);
let voter_at = helpers::voter_at_fn::<T::MinerConfig>(&snapshot_voters);
let target_at = helpers::target_at_fn::<T::MinerConfig>(&snapshot_targets);
let voter_index = helpers::voter_index_fn_usize::<T::MinerConfig>(&cache);
// Then convert solution -> assignment. This will fail if any of the indices are gibberish,
// namely any of the voters or targets.
@@ -1493,7 +1481,7 @@ impl<T: Config> Pallet<T> {
})?;
// ----- Start building support. First, we need one more closure.
let stake_of = helpers::stake_of_fn::<T>(&snapshot_voters, &cache);
let stake_of = helpers::stake_of_fn::<T::MinerConfig>(&snapshot_voters, &cache);
// This might fail if the normalization fails. Very unlikely. See `integrity_test`.
let staked_assignments = assignment_ratio_to_staked_normalized(assignments, stake_of)
@@ -1803,8 +1791,8 @@ mod tests {
use super::*;
use crate::{
mock::{
multi_phase_events, roll_to, AccountId, ExtBuilder, MockWeightInfo, MultiPhase,
Runtime, SignedMaxSubmissions, System, TargetIndex, Targets,
multi_phase_events, roll_to, AccountId, ExtBuilder, MockWeightInfo, MockedWeightInfo,
MultiPhase, Runtime, SignedMaxSubmissions, System, TargetIndex, Targets,
},
Phase,
};
@@ -2123,7 +2111,7 @@ mod tests {
// 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();
let (solution, _) = MultiPhase::mine_solution().unwrap();
// Default solution's score.
assert!(matches!(solution.score, ElectionScore { minimal_stake: 50, .. }));
@@ -2147,7 +2135,7 @@ mod tests {
#[test]
fn number_of_voters_allowed_2sec_block() {
// Just a rough estimate with the substrate weights.
assert!(!MockWeightInfo::get());
assert_eq!(MockWeightInfo::get(), MockedWeightInfo::Real);
let all_voters: u32 = 10_000;
let all_targets: u32 = 5_000;