feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,805 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! The overarching mock crate for all EPMB pallets.
|
||||
|
||||
mod signed;
|
||||
mod staking;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
self as multi_block,
|
||||
signed::{self as signed_pallet, HoldReason},
|
||||
unsigned::{
|
||||
self as unsigned_pallet,
|
||||
miner::{MinerConfig, OffchainMinerError, OffchainWorkerMiner},
|
||||
},
|
||||
verifier::{self as verifier_pallet, AsynchronousVerifier, Status},
|
||||
};
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use pezframe_election_provider_support::{
|
||||
bounds::{ElectionBounds, ElectionBoundsBuilder},
|
||||
InstantElectionProvider, NposSolution, SequentialPhragmen,
|
||||
};
|
||||
pub use pezframe_support::{assert_noop, assert_ok};
|
||||
use pezframe_support::{
|
||||
derive_impl, ord_parameter_types, parameter_types,
|
||||
traits::{fungible::InspectHold, Hooks},
|
||||
weights::{constants, Weight},
|
||||
};
|
||||
use pezframe_system::EnsureRoot;
|
||||
use parking_lot::RwLock;
|
||||
pub use signed::*;
|
||||
use pezsp_core::{
|
||||
offchain::{
|
||||
testing::{PoolState, TestOffchainExt, TestTransactionPoolExt},
|
||||
OffchainDbExt, OffchainWorkerExt, TransactionPoolExt,
|
||||
},
|
||||
ConstBool,
|
||||
};
|
||||
use pezsp_npos_elections::EvaluateSupport;
|
||||
use pezsp_runtime::{
|
||||
bounded_vec,
|
||||
traits::{BlakeTwo256, IdentityLookup},
|
||||
BuildStorage, PerU16, Perbill,
|
||||
};
|
||||
pub use staking::*;
|
||||
use std::{sync::Arc, vec};
|
||||
|
||||
pub type Extrinsic = pezsp_runtime::testing::TestXt<RuntimeCall, ()>;
|
||||
|
||||
pub type Balance = u64;
|
||||
pub type AccountId = u64;
|
||||
pub type BlockNumber = u64;
|
||||
pub type VoterIndex = u32;
|
||||
pub type TargetIndex = u16;
|
||||
|
||||
pezframe_support::construct_runtime!(
|
||||
pub enum Runtime {
|
||||
System: pezframe_system,
|
||||
Balances: pezpallet_balances,
|
||||
MultiBlock: multi_block,
|
||||
SignedPallet: signed_pallet,
|
||||
VerifierPallet: verifier_pallet,
|
||||
UnsignedPallet: unsigned_pallet,
|
||||
}
|
||||
);
|
||||
|
||||
pezframe_election_provider_support::generate_solution_type!(
|
||||
pub struct TestNposSolution::<
|
||||
VoterIndex = VoterIndex,
|
||||
TargetIndex = TargetIndex,
|
||||
Accuracy = PerU16,
|
||||
MaxVoters = ConstU32::<2_000>
|
||||
>(16)
|
||||
);
|
||||
|
||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
|
||||
impl pezframe_system::Config for Runtime {
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = AccountId;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type BlockLength = ();
|
||||
type BlockWeights = BlockWeights;
|
||||
type AccountData = pezpallet_balances::AccountData<Balance>;
|
||||
type Block = pezframe_system::mocking::MockBlock<Self>;
|
||||
}
|
||||
|
||||
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: Balance = 1;
|
||||
pub BlockWeights: pezframe_system::limits::BlockWeights = pezframe_system::limits::BlockWeights
|
||||
::with_sensible_defaults(
|
||||
Weight::from_parts(2u64 * constants::WEIGHT_REF_TIME_PER_SECOND, u64::MAX),
|
||||
NORMAL_DISPATCH_RATIO,
|
||||
);
|
||||
}
|
||||
|
||||
#[derive_impl(pezpallet_balances::config_preludes::TestDefaultConfig)]
|
||||
impl pezpallet_balances::Config for Runtime {
|
||||
type Balance = Balance;
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type AccountStore = System;
|
||||
type MaxLocks = ();
|
||||
type MaxReserves = ();
|
||||
type ReserveIdentifier = [u8; 8];
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FallbackModes {
|
||||
Continue,
|
||||
Emergency,
|
||||
Onchain,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AreWeDoneModes {
|
||||
Proceed,
|
||||
BackToSigned,
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
// The block at which we emit the start signal. This is used in `roll_next`, which is used all
|
||||
// across tests. The number comes across as a bit weird, but this is mainly due to backwards
|
||||
// compatibility with olds tests, when we used to have pull based election prediction.
|
||||
pub static ElectionStart: BlockNumber = 11;
|
||||
|
||||
|
||||
pub static Pages: PageIndex = 3;
|
||||
pub static UnsignedPhase: BlockNumber = 5;
|
||||
pub static SignedPhase: BlockNumber = 5;
|
||||
pub static SignedValidationPhase: BlockNumber = 6;
|
||||
|
||||
pub static FallbackMode: FallbackModes = FallbackModes::Emergency;
|
||||
pub static MinerTxPriority: u64 = 100;
|
||||
pub static OffchainRepeat: BlockNumber = 5;
|
||||
pub static OffchainStorage: bool = true;
|
||||
pub static MinerMaxLength: u32 = 256;
|
||||
pub static MinerPages: u32 = 1;
|
||||
pub static MaxVotesPerVoter: u32 = <TestNposSolution as NposSolution>::LIMIT as u32;
|
||||
|
||||
// by default we stick to 3 pages to host our 12 voters.
|
||||
pub static VoterSnapshotPerBlock: VoterIndex = 4;
|
||||
// and 4 targets, whom we fetch all.
|
||||
pub static TargetSnapshotPerBlock: TargetIndex = 4;
|
||||
|
||||
// we have 12 voters in the default setting, this should be enough to make sure they are not
|
||||
// trimmed accidentally in any test.
|
||||
#[derive(Encode, Decode, PartialEq, Eq, Debug, scale_info::TypeInfo, MaxEncodedLen)]
|
||||
pub static MaxBackersPerWinner: u32 = 12;
|
||||
pub static MaxBackersPerWinnerFinal: u32 = 12;
|
||||
// we have 4 targets in total and we desire `Desired` thereof, no single page can represent more
|
||||
// than the min of these two.
|
||||
#[derive(Encode, Decode, PartialEq, Eq, Debug, scale_info::TypeInfo, MaxEncodedLen)]
|
||||
pub static MaxWinnersPerPage: u32 = (staking::Targets::get().len() as u32).min(staking::DesiredTargets::get());
|
||||
pub static AreWeDone: AreWeDoneModes = AreWeDoneModes::Proceed;
|
||||
}
|
||||
|
||||
ord_parameter_types! {
|
||||
pub const Manager: AccountId = 7;
|
||||
}
|
||||
|
||||
impl Get<Phase<Runtime>> for AreWeDone {
|
||||
fn get() -> Phase<Runtime> {
|
||||
match <Self as Get<AreWeDoneModes>>::get() {
|
||||
AreWeDoneModes::BackToSigned => RevertToSignedIfNotQueuedOf::<Runtime>::get(),
|
||||
AreWeDoneModes::Proceed => ProceedRegardlessOf::<Runtime>::get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::verifier::Config for Runtime {
|
||||
type MaxBackersPerWinnerFinal = MaxBackersPerWinnerFinal;
|
||||
type MaxBackersPerWinner = MaxBackersPerWinner;
|
||||
type MaxWinnersPerPage = MaxWinnersPerPage;
|
||||
type SolutionDataProvider = signed::DualSignedPhase;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
impl crate::unsigned::Config for Runtime {
|
||||
type MinerPages = MinerPages;
|
||||
type OffchainRepeat = OffchainRepeat;
|
||||
type OffchainStorage = OffchainStorage;
|
||||
type MinerTxPriority = MinerTxPriority;
|
||||
type OffchainSolver = SequentialPhragmen<Self::AccountId, Perbill>;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
impl MinerConfig for Runtime {
|
||||
type AccountId = AccountId;
|
||||
type Hash = <Runtime as pezframe_system::Config>::Hash;
|
||||
type MaxLength = MinerMaxLength;
|
||||
type Pages = Pages;
|
||||
type MaxVotesPerVoter = MaxVotesPerVoter;
|
||||
type Solution = TestNposSolution;
|
||||
type Solver = SequentialPhragmen<AccountId, Perbill>;
|
||||
type TargetSnapshotPerBlock = TargetSnapshotPerBlock;
|
||||
type VoterSnapshotPerBlock = VoterSnapshotPerBlock;
|
||||
type MaxBackersPerWinner = MaxBackersPerWinner;
|
||||
type MaxBackersPerWinnerFinal = MaxBackersPerWinnerFinal;
|
||||
type MaxWinnersPerPage = MaxWinnersPerPage;
|
||||
}
|
||||
|
||||
impl crate::Config for Runtime {
|
||||
type SignedPhase = SignedPhase;
|
||||
type SignedValidationPhase = SignedValidationPhase;
|
||||
type UnsignedPhase = UnsignedPhase;
|
||||
type DataProvider = staking::MockStaking;
|
||||
type Fallback = MockFallback;
|
||||
type TargetSnapshotPerBlock = TargetSnapshotPerBlock;
|
||||
type VoterSnapshotPerBlock = VoterSnapshotPerBlock;
|
||||
type MinerConfig = Self;
|
||||
type WeightInfo = ();
|
||||
type Verifier = VerifierPallet;
|
||||
type AdminOrigin = EnsureRoot<AccountId>;
|
||||
type ManagerOrigin = pezframe_system::EnsureSignedBy<Manager, AccountId>;
|
||||
type Pages = Pages;
|
||||
type AreWeDone = AreWeDone;
|
||||
type OnRoundRotation = CleanRound<Self>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub static OnChainElectionBounds: ElectionBounds = ElectionBoundsBuilder::default().build();
|
||||
}
|
||||
|
||||
impl onchain::Config for Runtime {
|
||||
type DataProvider = staking::MockStaking;
|
||||
type MaxBackersPerWinner = MaxBackersPerWinner;
|
||||
type MaxWinnersPerPage = MaxWinnersPerPage;
|
||||
type Sort = ConstBool<true>;
|
||||
type Solver = SequentialPhragmen<AccountId, pezsp_runtime::PerU16, ()>;
|
||||
type System = Runtime;
|
||||
type WeightInfo = ();
|
||||
type Bounds = OnChainElectionBounds;
|
||||
}
|
||||
|
||||
pub struct MockFallback;
|
||||
impl ElectionProvider for MockFallback {
|
||||
type AccountId = AccountId;
|
||||
type BlockNumber = u64;
|
||||
type Error = String;
|
||||
type DataProvider = staking::MockStaking;
|
||||
type Pages = ConstU32<1>;
|
||||
type MaxWinnersPerPage = MaxWinnersPerPage;
|
||||
type MaxBackersPerWinner = MaxBackersPerWinner;
|
||||
type MaxBackersPerWinnerFinal = MaxBackersPerWinnerFinal;
|
||||
|
||||
fn elect(_remaining: PageIndex) -> Result<BoundedSupportsOf<Self>, Self::Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn duration() -> Self::BlockNumber {
|
||||
0
|
||||
}
|
||||
|
||||
fn start() -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn status() -> Result<bool, ()> {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl InstantElectionProvider for MockFallback {
|
||||
fn instant_elect(
|
||||
voters: Vec<VoterOf<Runtime>>,
|
||||
targets: Vec<Self::AccountId>,
|
||||
desired_targets: u32,
|
||||
) -> Result<BoundedSupportsOf<Self>, Self::Error> {
|
||||
match FallbackMode::get() {
|
||||
FallbackModes::Continue =>
|
||||
crate::Continue::<Runtime>::instant_elect(voters, targets, desired_targets)
|
||||
.map_err(|x| x.to_string()),
|
||||
FallbackModes::Emergency => crate::InitiateEmergencyPhase::<Runtime>::instant_elect(
|
||||
voters,
|
||||
targets,
|
||||
desired_targets,
|
||||
)
|
||||
.map_err(|x| x.to_string()),
|
||||
FallbackModes::Onchain => onchain::OnChainExecution::<Runtime>::instant_elect(
|
||||
voters,
|
||||
targets,
|
||||
desired_targets,
|
||||
)
|
||||
.map_err(|e| format!("onchain fallback failed: {:?}", e)),
|
||||
}
|
||||
}
|
||||
fn bother() -> bool {
|
||||
matches!(FallbackMode::get(), FallbackModes::Onchain)
|
||||
}
|
||||
}
|
||||
|
||||
impl<LocalCall> pezframe_system::offchain::CreateTransactionBase<LocalCall> for Runtime
|
||||
where
|
||||
RuntimeCall: From<LocalCall>,
|
||||
{
|
||||
type RuntimeCall = RuntimeCall;
|
||||
type Extrinsic = Extrinsic;
|
||||
}
|
||||
|
||||
impl<LocalCall> pezframe_system::offchain::CreateBare<LocalCall> for Runtime
|
||||
where
|
||||
RuntimeCall: From<LocalCall>,
|
||||
{
|
||||
fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic {
|
||||
Extrinsic::new_bare(call)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExtBuilder {}
|
||||
|
||||
impl ExtBuilder {
|
||||
pub fn full() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn verifier() -> Self {
|
||||
SignedPhase::set(0);
|
||||
SignedValidationPhase::set(0);
|
||||
signed::SignedPhaseSwitch::set(signed::SignedSwitch::Mock);
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn unsigned() -> Self {
|
||||
SignedPhase::set(0);
|
||||
SignedValidationPhase::set(0);
|
||||
signed::SignedPhaseSwitch::set(signed::SignedSwitch::Mock);
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn signed() -> Self {
|
||||
UnsignedPhase::set(0);
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtBuilder {
|
||||
pub(crate) fn max_backers_per_winner(self, c: u32) -> Self {
|
||||
MaxBackersPerWinner::set(c);
|
||||
self
|
||||
}
|
||||
pub(crate) fn max_backers_per_winner_final(self, c: u32) -> Self {
|
||||
MaxBackersPerWinnerFinal::set(c);
|
||||
self
|
||||
}
|
||||
pub(crate) fn offchain_storage(self, s: bool) -> Self {
|
||||
OffchainStorage::set(s);
|
||||
self
|
||||
}
|
||||
pub(crate) fn miner_tx_priority(self, p: u64) -> Self {
|
||||
MinerTxPriority::set(p);
|
||||
self
|
||||
}
|
||||
pub(crate) fn election_start(self, at: BlockNumber) -> Self {
|
||||
ElectionStart::set(at);
|
||||
self
|
||||
}
|
||||
pub(crate) fn pages(self, pages: PageIndex) -> Self {
|
||||
Pages::set(pages);
|
||||
self
|
||||
}
|
||||
pub(crate) fn voter_per_page(self, count: u32) -> Self {
|
||||
VoterSnapshotPerBlock::set(count);
|
||||
self
|
||||
}
|
||||
pub(crate) fn miner_max_length(self, len: u32) -> Self {
|
||||
MinerMaxLength::set(len);
|
||||
self
|
||||
}
|
||||
pub(crate) fn desired_targets(self, t: u32) -> Self {
|
||||
staking::DesiredTargets::set(t);
|
||||
self
|
||||
}
|
||||
pub(crate) fn signed_phase(self, d: BlockNumber, v: BlockNumber) -> Self {
|
||||
SignedPhase::set(d);
|
||||
SignedValidationPhase::set(v);
|
||||
self
|
||||
}
|
||||
pub(crate) fn unsigned_phase(self, d: BlockNumber) -> Self {
|
||||
UnsignedPhase::set(d);
|
||||
self
|
||||
}
|
||||
pub(crate) fn signed_validation_phase(self, d: BlockNumber) -> Self {
|
||||
SignedValidationPhase::set(d);
|
||||
self
|
||||
}
|
||||
pub(crate) fn miner_pages(self, p: u32) -> Self {
|
||||
MinerPages::set(p);
|
||||
self
|
||||
}
|
||||
pub(crate) fn max_signed_submissions(self, s: u32) -> Self {
|
||||
SignedMaxSubmissions::set(s);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn max_winners_per_page(self, w: u32) -> Self {
|
||||
MaxWinnersPerPage::set(w);
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn add_voter(self, who: AccountId, stake: Balance, targets: Vec<AccountId>) -> Self {
|
||||
staking::VOTERS.with(|v| v.borrow_mut().push((who, stake, targets.try_into().unwrap())));
|
||||
self
|
||||
}
|
||||
pub(crate) fn fallback_mode(self, mode: FallbackModes) -> Self {
|
||||
FallbackMode::set(mode);
|
||||
self
|
||||
}
|
||||
pub(crate) fn are_we_done(self, mode: AreWeDoneModes) -> Self {
|
||||
AreWeDone::set(mode);
|
||||
self
|
||||
}
|
||||
pub(crate) fn build_unchecked(self) -> pezsp_io::TestExternalities {
|
||||
pezsp_tracing::try_init_simple();
|
||||
let mut storage =
|
||||
pezframe_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
|
||||
|
||||
let _ = pezpallet_balances::GenesisConfig::<Runtime> {
|
||||
balances: vec![
|
||||
// bunch of account for submitting stuff only.
|
||||
(91, 100),
|
||||
(92, 100),
|
||||
(93, 100),
|
||||
(94, 100),
|
||||
(95, 100),
|
||||
(96, 100),
|
||||
(97, 100),
|
||||
(98, 100),
|
||||
(99, 100),
|
||||
(999, 100),
|
||||
(9999, 100),
|
||||
],
|
||||
..Default::default()
|
||||
}
|
||||
.assimilate_storage(&mut storage);
|
||||
|
||||
pezsp_io::TestExternalities::from(storage)
|
||||
}
|
||||
|
||||
/// Warning: this does not execute the post-sanity-checks.
|
||||
pub(crate) fn build_offchainify(self) -> (pezsp_io::TestExternalities, Arc<RwLock<PoolState>>) {
|
||||
let mut ext = self.build_unchecked();
|
||||
let (offchain, _offchain_state) = TestOffchainExt::new();
|
||||
let (pool, pool_state) = TestTransactionPoolExt::new();
|
||||
|
||||
ext.register_extension(OffchainDbExt::new(offchain.clone()));
|
||||
ext.register_extension(OffchainWorkerExt::new(offchain));
|
||||
ext.register_extension(TransactionPoolExt::new(pool));
|
||||
|
||||
(ext, pool_state)
|
||||
}
|
||||
|
||||
/// Build the externalities, and execute the given s`test` closure with it.
|
||||
pub(crate) fn build_and_execute(self, test: impl FnOnce() -> ()) {
|
||||
let mut ext = self.build_unchecked();
|
||||
ext.execute_with_sanity_checks(test);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExecuteWithSanityChecks {
|
||||
fn execute_with_sanity_checks(&mut self, test: impl FnOnce() -> ());
|
||||
}
|
||||
|
||||
impl ExecuteWithSanityChecks for pezsp_io::TestExternalities {
|
||||
fn execute_with_sanity_checks(&mut self, test: impl FnOnce() -> ()) {
|
||||
self.execute_with(all_pallets_integrity_test);
|
||||
self.execute_with(test);
|
||||
self.execute_with(all_pallets_sanity_checks);
|
||||
}
|
||||
}
|
||||
|
||||
fn all_pallets_integrity_test() {
|
||||
// ensure that all pallets are sane.
|
||||
VerifierPallet::integrity_test();
|
||||
UnsignedPallet::integrity_test();
|
||||
MultiBlock::integrity_test();
|
||||
SignedPallet::integrity_test();
|
||||
}
|
||||
|
||||
fn all_pallets_sanity_checks() {
|
||||
let now = System::block_number();
|
||||
let _ = VerifierPallet::do_try_state(now).unwrap();
|
||||
let _ = UnsignedPallet::do_try_state(now).unwrap();
|
||||
let _ = MultiBlock::do_try_state(now).unwrap();
|
||||
let _ = SignedPallet::do_try_state(now).unwrap();
|
||||
}
|
||||
|
||||
/// Fully verify a solution.
|
||||
///
|
||||
/// This will progress the blocks until the verifier pallet is done verifying it.
|
||||
///
|
||||
/// The solution must have already been loaded via `load_and_start_verification`.
|
||||
///
|
||||
/// Return the final supports, which is the outcome. If this succeeds, then the valid variant of the
|
||||
/// `QueuedSolution` form `verifier` is ready to be read.
|
||||
pub fn roll_to_full_verification() -> Vec<BoundedSupportsOf<MultiBlock>> {
|
||||
// we must be ready to verify.
|
||||
assert_eq!(VerifierPallet::status(), Status::Ongoing(Pages::get() - 1));
|
||||
|
||||
while matches!(VerifierPallet::status(), Status::Ongoing(_)) {
|
||||
roll_to(System::block_number() + 1);
|
||||
}
|
||||
|
||||
(MultiBlock::lsp()..=MultiBlock::msp())
|
||||
.map(|p| VerifierPallet::get_queued_solution_page(p).unwrap_or_default())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// Generate a single page of `TestNposSolution` from the give supports.
|
||||
///
|
||||
/// All of the voters in this support must live in a single page of the snapshot, noted by
|
||||
/// `snapshot_page`.
|
||||
pub fn solution_from_supports(
|
||||
supports: pezsp_npos_elections::Supports<AccountId>,
|
||||
snapshot_page: PageIndex,
|
||||
) -> TestNposSolution {
|
||||
let staked = pezsp_npos_elections::supports_to_staked_assignment(supports);
|
||||
let assignments = pezsp_npos_elections::assignment_staked_to_ratio_normalized(staked).unwrap();
|
||||
|
||||
let voters = crate::Snapshot::<Runtime>::voters(snapshot_page).unwrap();
|
||||
let targets = crate::Snapshot::<Runtime>::targets().unwrap();
|
||||
let voter_index = helpers::voter_index_fn_linear::<Runtime>(&voters);
|
||||
let target_index = helpers::target_index_fn_linear::<Runtime>(&targets);
|
||||
|
||||
TestNposSolution::from_assignment(&assignments, &voter_index, &target_index).unwrap()
|
||||
}
|
||||
|
||||
/// Generate a raw paged solution from the given vector of supports.
|
||||
///
|
||||
/// Given vector must be aligned with the snapshot, at most need to be 'pagified' which we do
|
||||
/// internally.
|
||||
pub fn raw_paged_from_supports(
|
||||
paged_supports: Vec<pezsp_npos_elections::Supports<AccountId>>,
|
||||
round: u32,
|
||||
) -> PagedRawSolution<Runtime> {
|
||||
let score = {
|
||||
let flattened = paged_supports.iter().cloned().flatten().collect::<Vec<_>>();
|
||||
flattened.evaluate()
|
||||
};
|
||||
|
||||
let solution_pages = paged_supports
|
||||
.pagify(Pages::get())
|
||||
.map(|(page_index, page_support)| solution_from_supports(page_support.to_vec(), page_index))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let solution_pages = solution_pages.try_into().unwrap();
|
||||
PagedRawSolution { solution_pages, score, round }
|
||||
}
|
||||
|
||||
/// ensure that the snapshot fully exists.
|
||||
///
|
||||
/// NOTE: this should not be used that often, because we check snapshot in sanity checks, which are
|
||||
/// called ALL THE TIME.
|
||||
pub fn assert_full_snapshot() {
|
||||
assert_ok!(Snapshot::<Runtime>::ensure_snapshot(true, Pages::get()));
|
||||
}
|
||||
|
||||
/// ensure that the no snapshot exists.
|
||||
///
|
||||
/// NOTE: this should not be used that often, because we check snapshot in sanity checks, which are
|
||||
/// called ALL THE TIME.
|
||||
pub fn assert_none_snapshot() {
|
||||
assert_ok!(Snapshot::<Runtime>::ensure_snapshot(false, Pages::get()));
|
||||
}
|
||||
|
||||
/// Simple wrapper for mining a new solution. Just more handy in case the interface of mine solution
|
||||
/// changes.
|
||||
///
|
||||
/// For testing, we never want to do reduce.
|
||||
pub fn mine_full_solution() -> Result<PagedRawSolution<Runtime>, OffchainMinerError<Runtime>> {
|
||||
OffchainWorkerMiner::<Runtime>::mine_solution(Pages::get(), false)
|
||||
}
|
||||
|
||||
/// Same as [`mine_full_solution`] but with custom pages.
|
||||
pub fn mine_solution(
|
||||
pages: PageIndex,
|
||||
) -> Result<PagedRawSolution<Runtime>, OffchainMinerError<Runtime>> {
|
||||
OffchainWorkerMiner::<Runtime>::mine_solution(pages, false)
|
||||
}
|
||||
|
||||
/// Assert that `count` voters exist across `pages` number of pages.
|
||||
pub fn ensure_voters(pages: PageIndex, count: usize) {
|
||||
assert_eq!(crate::Snapshot::<Runtime>::voter_pages(), pages);
|
||||
assert_eq!(crate::Snapshot::<Runtime>::voters_iter_flattened().count(), count);
|
||||
}
|
||||
|
||||
/// Assert that `count` targets exist across `pages` number of pages.
|
||||
pub fn ensure_targets(pages: PageIndex, count: usize) {
|
||||
assert_eq!(crate::Snapshot::<Runtime>::target_pages(), pages);
|
||||
assert_eq!(crate::Snapshot::<Runtime>::targets().unwrap().len(), count);
|
||||
}
|
||||
|
||||
/// get the events of the multi-block pallet.
|
||||
pub fn multi_block_events() -> Vec<crate::Event<Runtime>> {
|
||||
System::events()
|
||||
.into_iter()
|
||||
.map(|r| r.event)
|
||||
.filter_map(|e| if let RuntimeEvent::MultiBlock(inner) = e { Some(inner) } else { None })
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
static MultiBlockEvents: u32 = 0;
|
||||
static VerifierEvents: u32 = 0;
|
||||
}
|
||||
|
||||
pub fn multi_block_events_since_last_call() -> Vec<crate::Event<Runtime>> {
|
||||
let events = multi_block_events();
|
||||
let already_seen = MultiBlockEvents::get();
|
||||
MultiBlockEvents::set(events.len() as u32);
|
||||
events.into_iter().skip(already_seen as usize).collect()
|
||||
}
|
||||
|
||||
/// get the events of the verifier pallet.
|
||||
pub fn verifier_events() -> Vec<crate::verifier::Event<Runtime>> {
|
||||
System::events()
|
||||
.into_iter()
|
||||
.map(|r| r.event)
|
||||
.filter_map(
|
||||
|e| if let RuntimeEvent::VerifierPallet(inner) = e { Some(inner) } else { None },
|
||||
)
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// get the events of the verifier pallet since last call.
|
||||
pub fn verifier_events_since_last_call() -> Vec<crate::verifier::Event<Runtime>> {
|
||||
let events = verifier_events();
|
||||
let already_seen = VerifierEvents::get();
|
||||
VerifierEvents::set(events.len() as u32);
|
||||
events.into_iter().skip(already_seen as usize).collect()
|
||||
}
|
||||
|
||||
/// proceed block number to `n`.
|
||||
pub fn roll_to(n: BlockNumber) {
|
||||
crate::Pallet::<Runtime>::roll_to(
|
||||
n,
|
||||
matches!(SignedPhaseSwitch::get(), SignedSwitch::Real),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
/// proceed block number to whenever the snapshot is fully created (`Phase::Snapshot(0)`).
|
||||
pub fn roll_to_snapshot_created() {
|
||||
while !matches!(MultiBlock::current_phase(), Phase::Snapshot(0)) {
|
||||
roll_next()
|
||||
}
|
||||
roll_next();
|
||||
assert_full_snapshot();
|
||||
}
|
||||
|
||||
/// proceed block number to whenever the unsigned phase is open (`Phase::Unsigned(_)`).
|
||||
pub fn roll_to_unsigned_open() {
|
||||
while !matches!(MultiBlock::current_phase(), Phase::Unsigned(_)) {
|
||||
roll_next()
|
||||
}
|
||||
}
|
||||
|
||||
/// proceed block number to whenever the unsigned phase is about to close (`Phase::Unsigned(_)`).
|
||||
pub fn roll_to_last_unsigned() {
|
||||
while !matches!(MultiBlock::current_phase(), Phase::Unsigned(0)) {
|
||||
roll_next()
|
||||
}
|
||||
}
|
||||
|
||||
/// proceed block number to whenever the signed phase is open (`Phase::Signed(_)`).
|
||||
pub fn roll_to_signed_open() {
|
||||
while !matches!(MultiBlock::current_phase(), Phase::Signed(_)) {
|
||||
roll_next();
|
||||
}
|
||||
}
|
||||
|
||||
/// proceed block number to whenever the signed validation phase is open
|
||||
/// (`Phase::SignedValidation(_)`).
|
||||
pub fn roll_to_signed_validation_open() {
|
||||
while !matches!(MultiBlock::current_phase(), Phase::SignedValidation(_)) {
|
||||
roll_next()
|
||||
}
|
||||
}
|
||||
|
||||
/// proceed block number until we reach the done phase (`Phase::Done`).
|
||||
pub fn roll_to_done() {
|
||||
while !MultiBlock::current_phase().is_done() {
|
||||
roll_next()
|
||||
}
|
||||
}
|
||||
|
||||
/// Proceed one block.
|
||||
pub fn roll_next() {
|
||||
let now = System::block_number();
|
||||
roll_to(now + 1);
|
||||
}
|
||||
|
||||
/// Proceed one block, and execute offchain workers as well.
|
||||
pub fn roll_next_with_ocw(maybe_pool: Option<Arc<RwLock<PoolState>>>) {
|
||||
roll_to_with_ocw(System::block_number() + 1, maybe_pool)
|
||||
}
|
||||
|
||||
pub fn roll_to_unsigned_open_with_ocw(maybe_pool: Option<Arc<RwLock<PoolState>>>) {
|
||||
while !matches!(MultiBlock::current_phase(), Phase::Unsigned(_)) {
|
||||
roll_next_with_ocw(maybe_pool.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// proceed block number to `n`, while running all offchain workers as well.
|
||||
pub fn roll_to_with_ocw(n: BlockNumber, maybe_pool: Option<Arc<RwLock<PoolState>>>) {
|
||||
use pezsp_runtime::traits::Dispatchable;
|
||||
let now = System::block_number();
|
||||
for i in now + 1..=n {
|
||||
// check the offchain transaction pool, and if anything's there, submit it.
|
||||
if let Some(ref pool) = maybe_pool {
|
||||
pool.read()
|
||||
.transactions
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|uxt| <Extrinsic as codec::Decode>::decode(&mut &*uxt).unwrap())
|
||||
.for_each(|xt| {
|
||||
xt.function.dispatch(pezframe_system::RawOrigin::None.into()).unwrap();
|
||||
});
|
||||
pool.try_write().unwrap().transactions.clear();
|
||||
}
|
||||
|
||||
System::set_block_number(i);
|
||||
|
||||
MultiBlock::on_initialize(i);
|
||||
VerifierPallet::on_initialize(i);
|
||||
UnsignedPallet::on_initialize(i);
|
||||
if matches!(SignedPhaseSwitch::get(), SignedSwitch::Real) {
|
||||
SignedPallet::on_initialize(i);
|
||||
}
|
||||
|
||||
MultiBlock::offchain_worker(i);
|
||||
VerifierPallet::offchain_worker(i);
|
||||
UnsignedPallet::offchain_worker(i);
|
||||
if matches!(SignedPhaseSwitch::get(), SignedSwitch::Real) {
|
||||
SignedPallet::offchain_worker(i);
|
||||
}
|
||||
|
||||
// invariants must hold at the end of each block.
|
||||
all_pallets_sanity_checks()
|
||||
}
|
||||
}
|
||||
|
||||
/// An invalid solution with any score.
|
||||
pub fn fake_solution(score: ElectionScore) -> PagedRawSolution<Runtime> {
|
||||
PagedRawSolution {
|
||||
score,
|
||||
solution_pages: bounded_vec![Default::default()],
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// A real solution that's valid, but has a really bad score.
|
||||
///
|
||||
/// This is different from `solution_from_supports` in that it does not require the snapshot to
|
||||
/// exist.
|
||||
pub fn raw_paged_solution_low_score() -> PagedRawSolution<Runtime> {
|
||||
PagedRawSolution {
|
||||
solution_pages: vec![TestNposSolution {
|
||||
// 2 targets, both voting for themselves
|
||||
votes1: vec![(0, 0), (1, 2)],
|
||||
..Default::default()
|
||||
}]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
round: 0,
|
||||
score: ElectionScore { minimal_stake: 10, sum_stake: 20, sum_stake_squared: 200 },
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the free and held balance of `who`.
|
||||
pub fn balances(who: AccountId) -> (Balance, Balance) {
|
||||
(
|
||||
Balances::free_balance(who),
|
||||
Balances::balance_on_hold(&HoldReason::SignedSubmission.into(), &who),
|
||||
)
|
||||
}
|
||||
|
||||
/// Election bounds based on just the given count.
|
||||
pub fn bound_by_count(count: Option<u32>) -> DataProviderBounds {
|
||||
DataProviderBounds { count: count.map(|x| x.into()), size: None }
|
||||
}
|
||||
|
||||
pub fn emergency_solution() -> (BoundedSupportsOf<MultiBlock>, ElectionScore) {
|
||||
let supports = onchain::OnChainExecution::<Runtime>::elect(0).unwrap();
|
||||
let score = supports.evaluate();
|
||||
(supports, score)
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{Balance, Balances, Pages, Runtime, RuntimeEvent, SignedPallet, System};
|
||||
use crate::{
|
||||
mock::*,
|
||||
signed::{self as signed_pallet, Event as SignedEvent, Submissions},
|
||||
unsigned::miner::MinerConfig,
|
||||
verifier::{self, AsynchronousVerifier, SolutionDataProvider, VerificationResult, Verifier},
|
||||
Event, PadSolutionPages, PagedRawSolution, Pagify, Phase, SolutionOf,
|
||||
};
|
||||
use pezframe_election_provider_support::PageIndex;
|
||||
use pezframe_support::{
|
||||
assert_ok, dispatch::PostDispatchInfo, parameter_types, traits::EstimateCallFee,
|
||||
};
|
||||
use pezsp_npos_elections::ElectionScore;
|
||||
use pezsp_runtime::{traits::Zero, Perbill};
|
||||
|
||||
parameter_types! {
|
||||
pub static MockSignedNextSolution: Option<Vec<SolutionOf<Runtime>>> = None;
|
||||
pub static MockSignedNextScore: ElectionScore = Default::default();
|
||||
pub static MockSignedResults: Vec<VerificationResult> = Default::default();
|
||||
}
|
||||
|
||||
/// A simple implementation of the signed phase that can be controller by some static variables
|
||||
/// directly.
|
||||
///
|
||||
/// Useful for when you don't care too much about the signed phase.
|
||||
#[allow(dead_code)]
|
||||
pub struct MockSignedPhase;
|
||||
impl SolutionDataProvider for MockSignedPhase {
|
||||
type Solution = <Runtime as MinerConfig>::Solution;
|
||||
fn get_page(page: PageIndex) -> Self::Solution {
|
||||
MockSignedNextSolution::get()
|
||||
.and_then(|i| i.get(page as usize).cloned())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn get_score() -> ElectionScore {
|
||||
MockSignedNextScore::get()
|
||||
}
|
||||
|
||||
fn report_result(result: verifier::VerificationResult) {
|
||||
MOCK_SIGNED_RESULTS.with(|r| r.borrow_mut().push(result));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FixedCallFee;
|
||||
impl EstimateCallFee<signed_pallet::Call<Runtime>, Balance> for FixedCallFee {
|
||||
fn estimate_call_fee(_: &signed_pallet::Call<Runtime>, _: PostDispatchInfo) -> Balance {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub static SignedDepositBase: Balance = 5;
|
||||
pub static SignedDepositPerPage: Balance = 1;
|
||||
pub static InvulnerableDeposit: Balance = 7;
|
||||
pub static SignedMaxSubmissions: u32 = 3;
|
||||
pub static SignedRewardBase: Balance = 3;
|
||||
pub static SignedPhaseSwitch: SignedSwitch = SignedSwitch::Real;
|
||||
pub static BailoutGraceRatio: Perbill = Perbill::from_percent(20);
|
||||
pub static EjectGraceRatio: Perbill = Perbill::from_percent(20);
|
||||
}
|
||||
|
||||
impl crate::signed::Config for Runtime {
|
||||
type Currency = Balances;
|
||||
type DepositBase = SignedDepositBase;
|
||||
type DepositPerPage = SignedDepositPerPage;
|
||||
type InvulnerableDeposit = InvulnerableDeposit;
|
||||
type EstimateCallFee = FixedCallFee;
|
||||
type MaxSubmissions = SignedMaxSubmissions;
|
||||
type RewardBase = SignedRewardBase;
|
||||
type BailoutGraceRatio = BailoutGraceRatio;
|
||||
type EjectGraceRatio = EjectGraceRatio;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
/// Control which signed phase is being used.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum SignedSwitch {
|
||||
Mock,
|
||||
Real,
|
||||
}
|
||||
|
||||
pub struct DualSignedPhase;
|
||||
impl SolutionDataProvider for DualSignedPhase {
|
||||
type Solution = <Runtime as MinerConfig>::Solution;
|
||||
fn get_page(page: PageIndex) -> Self::Solution {
|
||||
match SignedPhaseSwitch::get() {
|
||||
SignedSwitch::Mock => MockSignedNextSolution::get()
|
||||
.and_then(|i| i.get(page as usize).cloned())
|
||||
.unwrap_or_default(),
|
||||
SignedSwitch::Real => SignedPallet::get_page(page),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_score() -> ElectionScore {
|
||||
match SignedPhaseSwitch::get() {
|
||||
SignedSwitch::Mock => MockSignedNextScore::get(),
|
||||
SignedSwitch::Real => SignedPallet::get_score(),
|
||||
}
|
||||
}
|
||||
|
||||
fn report_result(result: verifier::VerificationResult) {
|
||||
match SignedPhaseSwitch::get() {
|
||||
SignedSwitch::Mock => MOCK_SIGNED_RESULTS.with(|r| r.borrow_mut().push(result)),
|
||||
SignedSwitch::Real => SignedPallet::report_result(result),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
static SignedEventsIndex: u32 = 0;
|
||||
}
|
||||
|
||||
pub fn signed_events_since_last_call() -> Vec<crate::signed::Event<Runtime>> {
|
||||
let events = signed_events();
|
||||
let already_seen = SignedEventsIndex::get();
|
||||
SignedEventsIndex::set(events.len() as u32);
|
||||
events.into_iter().skip(already_seen as usize).collect()
|
||||
}
|
||||
|
||||
/// get the events of the verifier pallet.
|
||||
pub fn signed_events() -> Vec<crate::signed::Event<Runtime>> {
|
||||
System::events()
|
||||
.into_iter()
|
||||
.map(|r| r.event)
|
||||
.filter_map(|e| if let RuntimeEvent::SignedPallet(inner) = e { Some(inner) } else { None })
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// Load a signed solution into its pallet.
|
||||
pub fn load_signed_for_verification(who: AccountId, paged: PagedRawSolution<Runtime>) {
|
||||
let initial_balance = Balances::free_balance(&who);
|
||||
assert_eq!(balances(who), (initial_balance, 0));
|
||||
|
||||
assert_ok!(SignedPallet::register(RuntimeOrigin::signed(who), paged.score));
|
||||
|
||||
assert_eq!(
|
||||
balances(who),
|
||||
(initial_balance - SignedDepositBase::get(), SignedDepositBase::get())
|
||||
);
|
||||
|
||||
for (page_index, solution_page) in paged.solution_pages.pagify(Pages::get()) {
|
||||
assert_ok!(SignedPallet::submit_page(
|
||||
RuntimeOrigin::signed(who),
|
||||
page_index,
|
||||
Some(Box::new(solution_page.clone()))
|
||||
));
|
||||
}
|
||||
|
||||
let mut events = signed_events();
|
||||
for _ in 0..Pages::get() {
|
||||
let event = events.pop().unwrap();
|
||||
assert!(matches!(event, SignedEvent::Stored(_, x, _) if x == who))
|
||||
}
|
||||
assert!(matches!(events.pop().unwrap(), SignedEvent::Registered(_, x, _) if x == who));
|
||||
|
||||
let full_deposit =
|
||||
SignedDepositBase::get() + (Pages::get() as Balance) * SignedDepositPerPage::get();
|
||||
assert_eq!(balances(who), (initial_balance - full_deposit, full_deposit));
|
||||
}
|
||||
|
||||
/// Same as [`load_signed_for_verification`], but also goes forward to the beginning of the signed
|
||||
/// verification phase.
|
||||
pub fn load_signed_for_verification_and_start(
|
||||
who: AccountId,
|
||||
paged: PagedRawSolution<Runtime>,
|
||||
_round: u32,
|
||||
) {
|
||||
load_signed_for_verification(who, paged);
|
||||
|
||||
// now the solution should start being verified.
|
||||
roll_to_signed_validation_open();
|
||||
assert_eq!(
|
||||
multi_block_events(),
|
||||
vec![
|
||||
Event::PhaseTransitioned { from: Phase::Off, to: Phase::Snapshot(3) },
|
||||
Event::PhaseTransitioned {
|
||||
from: Phase::Snapshot(0),
|
||||
to: Phase::Signed(SignedPhase::get() - 1)
|
||||
},
|
||||
Event::PhaseTransitioned {
|
||||
from: Phase::Signed(0),
|
||||
to: Phase::SignedValidation(SignedValidationPhase::get())
|
||||
}
|
||||
]
|
||||
);
|
||||
assert_eq!(verifier_events(), vec![]);
|
||||
}
|
||||
|
||||
/// Same as [`load_signed_for_verification_and_start`], but also goes forward enough blocks for the
|
||||
/// solution to be verified, assuming it is all correct.
|
||||
///
|
||||
/// In other words, it goes [`Pages`] blocks forward.
|
||||
pub fn load_signed_for_verification_and_start_and_roll_to_verified(
|
||||
who: AccountId,
|
||||
paged: PagedRawSolution<Runtime>,
|
||||
_round: u32,
|
||||
) {
|
||||
load_signed_for_verification(who, paged.clone());
|
||||
|
||||
// now the solution should start being verified.
|
||||
roll_to_signed_validation_open();
|
||||
assert_eq!(
|
||||
multi_block_events(),
|
||||
vec![
|
||||
Event::PhaseTransitioned { from: Phase::Off, to: Phase::Snapshot(Pages::get()) },
|
||||
Event::PhaseTransitioned {
|
||||
from: Phase::Snapshot(0),
|
||||
to: Phase::Signed(SignedPhase::get() - 1)
|
||||
},
|
||||
Event::PhaseTransitioned {
|
||||
from: Phase::Signed(0),
|
||||
to: Phase::SignedValidation(SignedValidationPhase::get())
|
||||
}
|
||||
]
|
||||
);
|
||||
assert_eq!(verifier_events(), vec![]);
|
||||
|
||||
// there is no queued solution prior to the last page of the solution getting verified
|
||||
assert_eq!(<Runtime as crate::Config>::Verifier::queued_score(), None);
|
||||
|
||||
// roll to the block it is finalized.
|
||||
for _ in 0..Pages::get() + 1 {
|
||||
roll_next();
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
verifier_events(),
|
||||
vec![
|
||||
// NOTE: these are hardcoded for 3 page.
|
||||
verifier::Event::Verified(2, 2),
|
||||
verifier::Event::Verified(1, 2),
|
||||
verifier::Event::Verified(0, 2),
|
||||
verifier::Event::Queued(paged.score, None),
|
||||
]
|
||||
);
|
||||
|
||||
// there is now a queued solution.
|
||||
assert_eq!(<Runtime as crate::Config>::Verifier::queued_score(), Some(paged.score));
|
||||
}
|
||||
|
||||
/// Load a full raw paged solution for verification.
|
||||
///
|
||||
/// More or less the equivalent of `load_signed_for_verification_and_start`, but when
|
||||
/// `SignedSwitch::Mock` is set.
|
||||
pub fn load_mock_signed_and_start(raw_paged: PagedRawSolution<Runtime>) {
|
||||
assert_eq!(
|
||||
SignedPhaseSwitch::get(),
|
||||
SignedSwitch::Mock,
|
||||
"you should not use this if mock phase is not being mocked"
|
||||
);
|
||||
MockSignedNextSolution::set(Some(raw_paged.solution_pages.pad_solution_pages(Pages::get())));
|
||||
MockSignedNextScore::set(raw_paged.score);
|
||||
|
||||
// Let's gooooo!
|
||||
assert_ok!(<VerifierPallet as AsynchronousVerifier>::start());
|
||||
}
|
||||
|
||||
/// Ensure that no submission data exists in `round` for `who`.
|
||||
pub fn assert_no_data_for(round: u32, who: AccountId) {
|
||||
assert!(!Submissions::<Runtime>::leaderboard(round).into_iter().any(|(x, _)| x == who));
|
||||
assert!(Submissions::<Runtime>::metadata_of(round, who).is_none());
|
||||
assert!(Submissions::<Runtime>::pages_of(round, who).count().is_zero());
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{AccountId, MaxVotesPerVoter, Runtime};
|
||||
use crate::VoterOf;
|
||||
use pezframe_election_provider_support::{
|
||||
data_provider, DataProviderBounds, ElectionDataProvider, PageIndex, VoteWeight,
|
||||
};
|
||||
use pezframe_support::pezpallet_prelude::*;
|
||||
use pezsp_core::bounded_vec;
|
||||
use pezsp_std::prelude::*;
|
||||
|
||||
pub type T = Runtime;
|
||||
|
||||
pezframe_support::parameter_types! {
|
||||
pub static Targets: Vec<AccountId> = vec![10, 20, 30, 40];
|
||||
pub static Voters: Vec<VoterOf<Runtime>> = vec![
|
||||
// page 2:
|
||||
(1, 10, bounded_vec![10, 20]),
|
||||
(2, 10, bounded_vec![30, 40]),
|
||||
(3, 10, bounded_vec![40]),
|
||||
(4, 10, bounded_vec![10, 20, 40]),
|
||||
// page 1:
|
||||
(5, 10, bounded_vec![10, 30, 40]),
|
||||
(6, 10, bounded_vec![20, 30, 40]),
|
||||
(7, 10, bounded_vec![20, 30]),
|
||||
(8, 10, bounded_vec![10]),
|
||||
// page 0: (self-votes)
|
||||
(10, 10, bounded_vec![10]),
|
||||
(20, 20, bounded_vec![20]),
|
||||
(30, 30, bounded_vec![30]),
|
||||
(40, 40, bounded_vec![40]),
|
||||
];
|
||||
pub static DesiredTargets: u32 = 2;
|
||||
pub static EpochLength: u64 = 30;
|
||||
|
||||
pub static LastIteratedVoterIndex: Option<usize> = None;
|
||||
}
|
||||
|
||||
pub struct MockStaking;
|
||||
impl ElectionDataProvider for MockStaking {
|
||||
type AccountId = AccountId;
|
||||
type BlockNumber = u64;
|
||||
type MaxVotesPerVoter = MaxVotesPerVoter;
|
||||
|
||||
fn electable_targets(
|
||||
bounds: DataProviderBounds,
|
||||
remaining: PageIndex,
|
||||
) -> data_provider::Result<Vec<AccountId>> {
|
||||
let targets = Targets::get();
|
||||
|
||||
if remaining != 0 {
|
||||
crate::log!(
|
||||
warn,
|
||||
"requesting targets for non-zero page, we will return the same page in any case"
|
||||
);
|
||||
}
|
||||
if bounds.slice_exhausted(&targets) {
|
||||
return Err("Targets too big");
|
||||
}
|
||||
|
||||
Ok(targets)
|
||||
}
|
||||
|
||||
fn electing_voters(
|
||||
bounds: DataProviderBounds,
|
||||
remaining: PageIndex,
|
||||
) -> data_provider::Result<
|
||||
Vec<(AccountId, VoteWeight, BoundedVec<AccountId, Self::MaxVotesPerVoter>)>,
|
||||
> {
|
||||
let mut voters = Voters::get();
|
||||
|
||||
// jump to the first non-iterated, if this is a follow up.
|
||||
if let Some(index) = LastIteratedVoterIndex::get() {
|
||||
voters = voters.iter().skip(index).cloned().collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
// take as many as you can.
|
||||
if let Some(max_len) = bounds.count.map(|c| c.0 as usize) {
|
||||
voters.truncate(max_len)
|
||||
}
|
||||
|
||||
if voters.is_empty() {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
if remaining > 0 {
|
||||
let last = voters.last().cloned().unwrap();
|
||||
LastIteratedVoterIndex::set(Some(
|
||||
Voters::get().iter().position(|v| v == &last).map(|i| i + 1).unwrap(),
|
||||
));
|
||||
} else {
|
||||
LastIteratedVoterIndex::set(None)
|
||||
}
|
||||
|
||||
Ok(voters)
|
||||
}
|
||||
|
||||
fn desired_targets() -> data_provider::Result<u32> {
|
||||
Ok(DesiredTargets::get())
|
||||
}
|
||||
|
||||
fn next_election_prediction(_: u64) -> u64 {
|
||||
unreachable!("not used in this pallet")
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn put_snapshot(
|
||||
voters: Vec<(AccountId, VoteWeight, BoundedVec<AccountId, MaxVotesPerVoter>)>,
|
||||
targets: Vec<AccountId>,
|
||||
_target_stake: Option<VoteWeight>,
|
||||
) {
|
||||
Targets::set(targets);
|
||||
Voters::set(voters);
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn clear() {
|
||||
Targets::set(vec![]);
|
||||
Voters::set(vec![]);
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn fetch_page(page: PageIndex) {
|
||||
use pezframe_election_provider_support::ElectionProvider;
|
||||
super::MultiBlock::elect(page).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn add_voter(
|
||||
voter: AccountId,
|
||||
weight: VoteWeight,
|
||||
targets: BoundedVec<AccountId, MaxVotesPerVoter>,
|
||||
) {
|
||||
let mut current = Voters::get();
|
||||
current.push((voter, weight, targets));
|
||||
Voters::set(current);
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn add_target(target: AccountId) {
|
||||
use super::ExistentialDeposit;
|
||||
|
||||
let mut current = Targets::get();
|
||||
current.push(target);
|
||||
Targets::set(current);
|
||||
|
||||
// to be on-par with staking, we add a self vote as well. the stake is really not that
|
||||
// important.
|
||||
let mut current = Voters::get();
|
||||
current.push((target, ExistentialDeposit::get() as u64, vec![target].try_into().unwrap()));
|
||||
Voters::set(current);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mock::{bound_by_count, ExtBuilder};
|
||||
|
||||
#[test]
|
||||
fn targets() {
|
||||
ExtBuilder::full().build_and_execute(|| {
|
||||
assert_eq!(Targets::get().len(), 4);
|
||||
|
||||
// any non-zero page returns page zero.
|
||||
assert_eq!(MockStaking::electable_targets(bound_by_count(None), 2).unwrap().len(), 4);
|
||||
assert_eq!(MockStaking::electable_targets(bound_by_count(None), 1).unwrap().len(), 4);
|
||||
|
||||
// 0 is also fine.
|
||||
assert_eq!(MockStaking::electable_targets(bound_by_count(None), 0).unwrap().len(), 4);
|
||||
|
||||
// fetch less targets is error, because targets cannot be sorted (both by MockStaking,
|
||||
// and the real staking).
|
||||
assert!(MockStaking::electable_targets(bound_by_count(Some(2)), 0).is_err());
|
||||
|
||||
// more targets is fine.
|
||||
assert!(MockStaking::electable_targets(bound_by_count(Some(4)), 0).is_ok());
|
||||
assert!(MockStaking::electable_targets(bound_by_count(Some(5)), 0).is_ok());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_page_votes() {
|
||||
ExtBuilder::full().build_and_execute(|| {
|
||||
assert_eq!(MockStaking::electing_voters(bound_by_count(None), 0).unwrap().len(), 12);
|
||||
assert!(LastIteratedVoterIndex::get().is_none());
|
||||
|
||||
assert_eq!(
|
||||
MockStaking::electing_voters(bound_by_count(Some(4)), 0)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|(x, _, _)| x)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![1, 2, 3, 4],
|
||||
);
|
||||
assert!(LastIteratedVoterIndex::get().is_none());
|
||||
|
||||
assert_eq!(
|
||||
MockStaking::electing_voters(bound_by_count(Some(4)), 2)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|(x, _, _)| x)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![1, 2, 3, 4],
|
||||
);
|
||||
assert_eq!(LastIteratedVoterIndex::get().unwrap(), 4);
|
||||
|
||||
assert_eq!(
|
||||
MockStaking::electing_voters(bound_by_count(Some(4)), 1)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|(x, _, _)| x)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![5, 6, 7, 8],
|
||||
);
|
||||
assert_eq!(LastIteratedVoterIndex::get().unwrap(), 8);
|
||||
|
||||
assert_eq!(
|
||||
MockStaking::electing_voters(bound_by_count(Some(4)), 0)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|(x, _, _)| x)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![10, 20, 30, 40],
|
||||
);
|
||||
assert!(LastIteratedVoterIndex::get().is_none());
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user