Decouple Staking and Election - Part 2 Unsigned Phase (#7909)

* Base features and traits.

* pallet and unsigned phase

* Undo bad formattings.

* some formatting cleanup.

* Small self-cleanup.

* Make it all build

* self-review

* Some doc tests.

* Some changes from other PR

* Fix session test

* Update Cargo.lock

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

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* Some review comments

* Rename + make encode/decode

* Do an assert as well, just in case.

* Fix build

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

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* Las comment

* fix staking fuzzer.

* cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_election_provider_multi_phase --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/election-provider-multi-phase/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Add one last layer of feasibility check as well.

* Last fixes to benchmarks

* Some more docs.

* cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_election_provider_multi_phase --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/election-provider-multi-phase/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_election_provider_multi_phase --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/election-provider-multi-phase/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Some nits

* cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_staking --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/staking/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Fix doc

* Mkae ci green

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
Co-authored-by: Parity Benchmarking Bot <admin@parity.io>
This commit is contained in:
Kian Paimani
2021-02-23 14:46:17 +00:00
committed by GitHub
parent ba659f9440
commit 7205eea40d
34 changed files with 4092 additions and 231 deletions
@@ -0,0 +1,381 @@
// This file is part of Substrate.
// Copyright (C) 2021 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::*;
use crate as multi_phase;
pub use frame_support::{assert_noop, assert_ok};
use frame_support::{
parameter_types,
traits::{Hooks},
weights::Weight,
};
use parking_lot::RwLock;
use sp_core::{
offchain::{
testing::{PoolState, TestOffchainExt, TestTransactionPoolExt},
OffchainExt, TransactionPoolExt,
},
H256,
};
use sp_election_providers::ElectionDataProvider;
use sp_npos_elections::{
assignment_ratio_to_staked_normalized, seq_phragmen, to_supports, to_without_backing,
CompactSolution, ElectionResult, EvaluateSupport,
};
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup},
PerU16,
};
use std::sync::Arc;
pub type Block = sp_runtime::generic::Block<Header, UncheckedExtrinsic>;
pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic<AccountId, Call, (), ()>;
frame_support::construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system::{Module, Call, Event<T>, Config},
Balances: pallet_balances::{Module, Call, Event<T>, Config<T>},
MultiPhase: multi_phase::{Module, Call, Event<T>},
}
);
pub(crate) type Balance = u64;
pub(crate) type AccountId = u64;
sp_npos_elections::generate_solution_type!(
#[compact]
pub struct TestCompact::<u32, u16, PerU16>(16)
);
/// All events of this pallet.
pub(crate) fn multi_phase_events() -> Vec<super::Event<Runtime>> {
System::events()
.into_iter()
.map(|r| r.event)
.filter_map(|e| if let Event::multi_phase(inner) = e { Some(inner) } else { None })
.collect::<Vec<_>>()
}
/// To from `now` to block `n`.
pub fn roll_to(n: u64) {
let now = System::block_number();
for i in now + 1..=n {
System::set_block_number(i);
MultiPhase::on_initialize(i);
}
}
pub fn roll_to_with_ocw(n: u64) {
let now = System::block_number();
for i in now + 1..=n {
System::set_block_number(i);
MultiPhase::on_initialize(i);
MultiPhase::offchain_worker(i);
}
}
/// Spit out a verifiable raw solution.
///
/// This is a good example of what an offchain miner would do.
pub fn raw_solution() -> RawSolution<CompactOf<Runtime>> {
let RoundSnapshot { voters, targets } = MultiPhase::snapshot().unwrap();
let desired_targets = MultiPhase::desired_targets().unwrap();
// closures
let cache = helpers::generate_voter_cache::<Runtime>(&voters);
let voter_index = helpers::voter_index_fn_linear::<Runtime>(&voters);
let target_index = helpers::target_index_fn_linear::<Runtime>(&targets);
let stake_of = helpers::stake_of_fn::<Runtime>(&voters, &cache);
let ElectionResult { winners, assignments } = seq_phragmen::<_, CompactAccuracyOf<Runtime>>(
desired_targets as usize,
targets.clone(),
voters.clone(),
None,
)
.unwrap();
let winners = to_without_backing(winners);
let score = {
let staked = assignment_ratio_to_staked_normalized(assignments.clone(), &stake_of).unwrap();
to_supports(&winners, &staked).unwrap().evaluate()
};
let compact =
<CompactOf<Runtime>>::from_assignment(assignments, &voter_index, &target_index).unwrap();
let round = MultiPhase::round();
RawSolution { compact, score, round }
}
pub fn witness() -> SolutionOrSnapshotSize {
MultiPhase::snapshot()
.map(|snap| SolutionOrSnapshotSize {
voters: snap.voters.len() as u32,
targets: snap.targets.len() as u32,
})
.unwrap_or_default()
}
impl frame_system::Config for Runtime {
type SS58Prefix = ();
type BaseCallFilter = ();
type Origin = Origin;
type Index = u64;
type BlockNumber = u64;
type Call = Call;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = Event;
type BlockHashCount = ();
type DbWeight = ();
type BlockLength = ();
type BlockWeights = BlockWeights;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<u64>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
}
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights
::with_sensible_defaults(2 * frame_support::weights::constants::WEIGHT_PER_SECOND, NORMAL_DISPATCH_RATIO);
}
impl pallet_balances::Config for Runtime {
type Balance = Balance;
type Event = Event;
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type MaxLocks = ();
type WeightInfo = ();
}
parameter_types! {
pub static Targets: Vec<AccountId> = vec![10, 20, 30, 40];
pub static Voters: Vec<(AccountId, VoteWeight, Vec<AccountId>)> = vec![
(1, 10, vec![10, 20]),
(2, 10, vec![30, 40]),
(3, 10, vec![40]),
(4, 10, vec![10, 20, 30, 40]),
// self votes.
(10, 10, vec![10]),
(20, 20, vec![20]),
(30, 30, vec![30]),
(40, 40, vec![40]),
];
pub static Fallback: FallbackStrategy = FallbackStrategy::OnChain;
pub static DesiredTargets: u32 = 2;
pub static SignedPhase: u64 = 10;
pub static UnsignedPhase: u64 = 5;
pub static MaxSignedSubmissions: u32 = 5;
pub static MinerMaxIterations: u32 = 5;
pub static MinerTxPriority: u64 = 100;
pub static SolutionImprovementThreshold: Perbill = Perbill::zero();
pub static MinerMaxWeight: Weight = BlockWeights::get().max_block;
pub static MockWeightInfo: bool = false;
pub static EpochLength: u64 = 30;
}
// Hopefully this won't be too much of a hassle to maintain.
pub struct DualMockWeightInfo;
impl multi_phase::weights::WeightInfo for DualMockWeightInfo {
fn on_initialize_nothing() -> Weight {
if MockWeightInfo::get() {
Zero::zero()
} else {
<() as multi_phase::weights::WeightInfo>::on_initialize_nothing()
}
}
fn on_initialize_open_signed() -> Weight {
if MockWeightInfo::get() {
Zero::zero()
} else {
<() as multi_phase::weights::WeightInfo>::on_initialize_open_signed()
}
}
fn on_initialize_open_unsigned_with_snapshot() -> Weight {
if MockWeightInfo::get() {
Zero::zero()
} else {
<() as multi_phase::weights::WeightInfo>::on_initialize_open_unsigned_with_snapshot()
}
}
fn on_initialize_open_unsigned_without_snapshot() -> Weight {
if MockWeightInfo::get() {
Zero::zero()
} else {
<() as multi_phase::weights::WeightInfo>::on_initialize_open_unsigned_without_snapshot()
}
}
fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight {
if MockWeightInfo::get() {
// 10 base
// 5 per edge.
(10 as Weight).saturating_add((5 as Weight).saturating_mul(a as Weight))
} else {
<() as multi_phase::weights::WeightInfo>::submit_unsigned(v, t, a, d)
}
}
fn feasibility_check(v: u32, t: u32, a: u32, d: u32) -> Weight {
if MockWeightInfo::get() {
// 10 base
// 5 per edge.
(10 as Weight).saturating_add((5 as Weight).saturating_mul(a as Weight))
} else {
<() as multi_phase::weights::WeightInfo>::feasibility_check(v, t, a, d)
}
}
}
impl crate::Config for Runtime {
type Event = Event;
type Currency = Balances;
type SignedPhase = SignedPhase;
type UnsignedPhase = UnsignedPhase;
type SolutionImprovementThreshold = SolutionImprovementThreshold;
type MinerMaxIterations = MinerMaxIterations;
type MinerMaxWeight = MinerMaxWeight;
type MinerTxPriority = MinerTxPriority;
type DataProvider = StakingMock;
type WeightInfo = DualMockWeightInfo;
type BenchmarkingConfig = ();
type OnChainAccuracy = Perbill;
type Fallback = Fallback;
type CompactSolution = TestCompact;
}
impl<LocalCall> frame_system::offchain::SendTransactionTypes<LocalCall> for Runtime
where
Call: From<LocalCall>,
{
type OverarchingCall = Call;
type Extrinsic = Extrinsic;
}
pub type Extrinsic = sp_runtime::testing::TestXt<Call, ()>;
#[derive(Default)]
pub struct ExtBuilder {}
pub struct StakingMock;
impl ElectionDataProvider<AccountId, u64> for StakingMock {
fn targets() -> Vec<AccountId> {
Targets::get()
}
fn voters() -> Vec<(AccountId, VoteWeight, Vec<AccountId>)> {
Voters::get()
}
fn desired_targets() -> u32 {
DesiredTargets::get()
}
fn next_election_prediction(now: u64) -> u64 {
now + EpochLength::get() - now % EpochLength::get()
}
}
impl ExtBuilder {
pub fn miner_tx_priority(self, p: u64) -> Self {
<MinerTxPriority>::set(p);
self
}
pub fn solution_improvement_threshold(self, p: Perbill) -> Self {
<SolutionImprovementThreshold>::set(p);
self
}
pub fn phases(self, signed: u64, unsigned: u64) -> Self {
<SignedPhase>::set(signed);
<UnsignedPhase>::set(unsigned);
self
}
pub fn fallabck(self, fallback: FallbackStrategy) -> Self {
<Fallback>::set(fallback);
self
}
pub fn miner_weight(self, weight: Weight) -> Self {
<MinerMaxWeight>::set(weight);
self
}
pub fn mock_weight_info(self, mock: bool) -> Self {
<MockWeightInfo>::set(mock);
self
}
pub fn desired_targets(self, t: u32) -> Self {
<DesiredTargets>::set(t);
self
}
pub fn add_voter(self, who: AccountId, stake: Balance, targets: Vec<AccountId>) -> Self {
VOTERS.with(|v| v.borrow_mut().push((who, stake, targets)));
self
}
pub fn build(self) -> sp_io::TestExternalities {
sp_tracing::try_init_simple();
let mut storage =
frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
let _ = pallet_balances::GenesisConfig::<Runtime> {
balances: vec![
// bunch of account for submitting stuff only.
(99, 100),
(999, 100),
(9999, 100),
],
}
.assimilate_storage(&mut storage);
sp_io::TestExternalities::from(storage)
}
pub fn build_offchainify(
self,
iters: u32,
) -> (sp_io::TestExternalities, Arc<RwLock<PoolState>>) {
let mut ext = self.build();
let (offchain, offchain_state) = TestOffchainExt::new();
let (pool, pool_state) = TestTransactionPoolExt::new();
let mut seed = [0_u8; 32];
seed[0..4].copy_from_slice(&iters.to_le_bytes());
offchain_state.write().seed = seed;
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(TransactionPoolExt::new(pool));
(ext, pool_state)
}
pub fn build_and_execute(self, test: impl FnOnce() -> ()) {
self.build().execute_with(test)
}
}