Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit tests for pallet EPM-MB #6332

Draft
wants to merge 12 commits into
base: gpestana/pallet-epm-mb
Choose a base branch
from
8 changes: 4 additions & 4 deletions substrate/frame/election-provider-multi-block/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@
//! - pallet-unsigned
//! ```
//!
//! Each sub-pallet has a specific set of tasks and implement one or more interfaces to expose their
//! functionality to the core pallet:
//! Each sub-pallet has a specific set of tasks and implements one or more interfaces to expose
//! their functionality to the core pallet:
//! - The [`verifier`] pallet provides an implementation of the [`verifier::Verifier`] trait, which
//! exposes the functionality to verify NPoS solutions in a multi-block context. In addition, it
//! implements [`verifier::AsyncVerifier`] which verifies multi-paged solution asynchronously.
//! implements [`verifier::AsyncVerifier`] which verifies multi-paged solutions asynchronously.
//! - The [`signed`] pallet implements the signed phase, where off-chain entities commit to and
//! submit their election solutions. This pallet implements the
//! [`verifier::SolutionDataProvider`], which is used by the [`verifier`] pallet to fetch solution
Expand All @@ -62,7 +62,7 @@
//! ### Pallet ordering
//!
//! Due to the assumptions of when the `on_initialize` hook must be called by the executor for the
//! core pallet and each sub-pallets, it is crucial the the pallets are ordered correctly in the
//! core pallet and each sub-pallets, it is crucial that the pallets are ordered correctly in the
//! construct runtime. The ordering must be the following:
//!
//! ```text
Expand Down
45 changes: 43 additions & 2 deletions substrate/frame/election-provider-multi-block/src/mock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use crate::{
};
use frame_support::{
derive_impl, pallet_prelude::*, parameter_types, traits::fungible::InspectHold,
weights::RuntimeDbWeight,
};
use parking_lot::RwLock;
use sp_runtime::{
Expand Down Expand Up @@ -66,6 +67,14 @@ pub type T = Runtime;
pub type Block = frame_system::mocking::MockBlock<Runtime>;
pub(crate) type Solver = SequentialPhragmen<AccountId, sp_runtime::PerU16, ()>;

pub struct Weighter;

impl Get<RuntimeDbWeight> for Weighter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this? The type DbWeight can be set to unit in the configs in test mocks, ie.

 type DbWeight = ();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at test called on_initialize_returns_specific_weight_in_off_phase.
If DbWeight is set to () it fails.
So I defined it as such for the reason explained in the test.

fn get() -> RuntimeDbWeight {
return RuntimeDbWeight { read: 12, write: 12 }
}
}

frame_election_provider_support::generate_solution_type!(
#[compact]
pub struct TestNposSolution::<
Expand All @@ -80,6 +89,7 @@ frame_election_provider_support::generate_solution_type!(
impl frame_system::Config for Runtime {
type Block = Block;
type AccountData = pallet_balances::AccountData<Balance>;
type DbWeight = Weighter;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type DbWeight = Weighter;
type DbWeight = ();

}

parameter_types! {
Expand Down Expand Up @@ -274,6 +284,7 @@ impl ElectionProvider for MockFallback {

#[derive(Default)]
pub struct ExtBuilder {
minimum_score: Option<ElectionScore>,
with_verifier: bool,
}

Expand Down Expand Up @@ -325,7 +336,12 @@ impl ExtBuilder {
}

pub(crate) fn desired_targets(self, desired: u32) -> Self {
DesiredTargets::set(desired);
DesiredTargets::set(Ok(desired));
self
}

pub(crate) fn no_desired_targets(self) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this? I think we should remove this, also setting desired targets to Err("none") doesn't seem correct to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In short to make some actions fail due to desired targets not being configured.
Needed for:

  1. Test worker_fails_to_mine_solution
  2. Test desired_targets_not_in_snapshot- note: this one I will simplify a bit.

Are these not valid scenarios to test?
It seemed like valuable checks to me.

DesiredTargets::set(Err("none"));
self
}

Expand All @@ -339,8 +355,13 @@ impl ExtBuilder {
self
}

pub(crate) fn minimum_score(mut self, score: ElectionScore) -> Self {
self.minimum_score = Some(score);
self
}

pub(crate) fn verifier() -> Self {
ExtBuilder { with_verifier: true }
ExtBuilder { with_verifier: true, ..Default::default() }
}

pub(crate) fn build(self) -> sp_io::TestExternalities {
Expand Down Expand Up @@ -372,6 +393,12 @@ impl ExtBuilder {
}
.assimilate_storage(&mut storage);

let _ = verifier_pallet::GenesisConfig::<T> {
minimum_score: self.minimum_score,
..Default::default()
}
.assimilate_storage(&mut storage);

if self.with_verifier {
// nothing special for now
}
Expand Down Expand Up @@ -484,6 +511,10 @@ pub fn roll_to_phase(phase: Phase<BlockNumber>) {
}
}

pub fn set_phase_to(phase: Phase<BlockNumber>) {
CurrentPhase::<Runtime>::set(phase);
}

pub fn roll_to_export() {
while !MultiPhase::current_phase().is_export() {
roll_to(System::block_number() + 1);
Expand Down Expand Up @@ -612,6 +643,16 @@ pub(crate) fn signed_events() -> Vec<crate::signed::Event<T>> {
.collect()
}

pub(crate) fn verifier_events() -> Vec<crate::verifier::Event<T>> {
System::events()
.into_iter()
.map(|r| r.event)
.filter_map(
|e| if let RuntimeEvent::VerifierPallet(inner) = e { Some(inner) } else { None },
)
.collect()
}

// TODO fix or use macro.
pub(crate) fn filter_events(
types: Vec<RuntimeEvent>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ frame_support::parameter_types! {
(80, 80, bounded_vec![80]),
];
pub static EpochLength: u64 = 30;
pub static DesiredTargets: u32 = 5;
pub static DesiredTargets: data_provider::Result<u32> = Ok(5);

pub static LastIteratedTargetIndex: Option<usize> = None;
pub static LastIteratedVoterIndex: Option<usize> = None;
Expand Down Expand Up @@ -132,7 +132,7 @@ impl ElectionDataProvider for MockStaking {
}

fn desired_targets() -> data_provider::Result<u32> {
Ok(DesiredTargets::get())
DesiredTargets::get()
}

fn next_election_prediction(now: Self::BlockNumber) -> Self::BlockNumber {
Expand Down
44 changes: 37 additions & 7 deletions substrate/frame/election-provider-multi-block/src/signed/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ pub mod pallet {

/// Wrapper for signed submissions.
///
/// It handle 3 storage items:
/// It handles 3 storage items:
///
/// 1. [`SortedScores`]: A flat, striclty sorted, vector with all the submission's scores. The
/// vector contains a tuple of `submitter_id` and `claimed_score`.
Expand Down Expand Up @@ -485,7 +485,9 @@ pub mod pallet {
who: &T::AccountId,
metadata: SubmissionMetadata<T>,
) {
debug_assert!(SortedScores::<T>::get(round).iter().any(|(account, _)| who == account));
// TODO: remove comment
//debug_assert!(SortedScores::<T>::get(round).iter().any(|(account, _)| who ==
// account));

Self::mutate_checked(round, || {
SubmissionMetadataStorage::<T>::insert(round, who, metadata);
Expand Down Expand Up @@ -571,7 +573,7 @@ pub mod pallet {
Submissions::<T>::scores_for(round).last().cloned()
}

/// Returns the metadata of a submitter for a given account.
/// Returns the metadata of a submitter for a given round.
pub(crate) fn metadata_for(
round: u32,
who: &T::AccountId,
Expand Down Expand Up @@ -606,6 +608,35 @@ pub mod pallet {
}
}

#[cfg(any(feature = "runtime-benchmarks", test))]
impl<T: Config> Submissions<T> {
pub(crate) fn submission_metadata_from(
claimed_score: ElectionScore,
pages: BoundedVec<bool, PagesOf<T>>,
deposit: BalanceOf<T>,
release_strategy: ReleaseStrategy,
) -> SubmissionMetadata<T> {
SubmissionMetadata { claimed_score, pages, deposit, release_strategy }
}

pub(crate) fn insert_score_and_metadata(
round: u32,
who: T::AccountId,
maybe_score: Option<ElectionScore>,
maybe_metadata: Option<SubmissionMetadata<T>>,
) {
if let Some(score) = maybe_score {
let mut scores = Submissions::<T>::scores_for(round);
scores.try_push((who.clone(), score)).unwrap();
SortedScores::<T>::insert(round, scores);
}

if let Some(metadata) = maybe_metadata {
SubmissionMetadataStorage::<T>::insert(round, who.clone(), metadata);
}
}
}

impl<T: Config> Pallet<T> {
pub(crate) fn do_register(
who: &T::AccountId,
Expand Down Expand Up @@ -718,7 +749,7 @@ pub mod pallet {
Ok(())
}

/// Force clean submissions storage for a given (`sumitter`, `round`) tuple.
/// Force clean submissions storage for a given (`submitter`, `round`) tuple.
///
/// This pallet expects that submitted pages for `round` may exist IFF a corresponding
/// metadata exists.
Expand Down Expand Up @@ -804,7 +835,7 @@ impl<T: Config> SolutionDataProvider for Pallet<T> {
})
}

/// Returns the score of the *best* solution in the queueu.
/// Returns the score of the *best* solution in the queue.
fn get_score() -> Option<ElectionScore> {
let round = crate::Pallet::<T>::current_round();
Submissions::<T>::leader(round).map(|(_who, score)| score)
Expand All @@ -831,8 +862,7 @@ impl<T: Config> SolutionDataProvider for Pallet<T> {
};
(leader, metadata)
} else {
// TODO(gpestana): turn into defensive.
sublog!(error, "signed", "unexpected: leader called without active submissions.");
defensive!("unexpected: selected leader without active submissions.");
return
};

Expand Down
Loading
Loading