From d998b12e7f8edeccb8cf64c84f4c0474a6b2ea61 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 15 Jan 2021 13:19:35 +0000 Subject: [PATCH 01/57] Base features and traits. --- Cargo.lock | 11 + Cargo.toml | 1 + primitives/election-providers/Cargo.toml | 33 ++ primitives/election-providers/src/lib.rs | 239 +++++++++++++ primitives/election-providers/src/onchain.rs | 168 +++++++++ .../npos-elections/compact/src/assignment.rs | 54 +-- primitives/npos-elections/compact/src/lib.rs | 123 ++++--- .../fuzzer/src/phragmen_balancing.rs | 23 +- .../fuzzer/src/phragmms_balancing.rs | 23 +- .../npos-elections/fuzzer/src/reduce.rs | 9 +- primitives/npos-elections/src/helpers.rs | 23 +- primitives/npos-elections/src/lib.rs | 324 ++++++++++++------ primitives/npos-elections/src/mock.rs | 12 +- primitives/npos-elections/src/phragmen.rs | 46 ++- primitives/npos-elections/src/phragmms.rs | 7 +- primitives/npos-elections/src/tests.rs | 99 ++++-- 16 files changed, 908 insertions(+), 287 deletions(-) create mode 100644 primitives/election-providers/Cargo.toml create mode 100644 primitives/election-providers/src/lib.rs create mode 100644 primitives/election-providers/src/onchain.rs diff --git a/Cargo.lock b/Cargo.lock index c42127aead43c..05f4896e071ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8357,6 +8357,17 @@ dependencies = [ "syn", ] +[[package]] +name = "sp-election-providers" +version = "2.0.0" +dependencies = [ + "parity-scale-codec", + "sp-arithmetic", + "sp-npos-elections", + "sp-runtime", + "sp-std", +] + [[package]] name = "sp-externalities" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index 12e79490ef6b0..1754f896c8846 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -139,6 +139,7 @@ members = [ "primitives/database", "primitives/debug-derive", "primitives/externalities", + "primitives/election-providers", "primitives/finality-grandpa", "primitives/inherents", "primitives/io", diff --git a/primitives/election-providers/Cargo.toml b/primitives/election-providers/Cargo.toml new file mode 100644 index 0000000000000..65ca0e400958e --- /dev/null +++ b/primitives/election-providers/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "sp-election-providers" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "Apache-2.0" +homepage = "https://substrate.dev" +repository = "https://github.com/paritytech/substrate/" +description = "Primitive election providers" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } +sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } +sp-arithmetic = { version = "2.0.0-rc6", default-features = false, path = "../arithmetic" } +sp-npos-elections = { version = "2.0.0-rc6", default-features = false, path = "../npos-elections" } + +[dev-dependencies] +sp-npos-elections = { version = "2.0.0-rc6", path = "../npos-elections" } +sp-runtime = { version = "2.0.0-rc6", path = "../runtime" } + +[features] +default = ["std"] +runtime-benchmarks = [] +std = [ + "codec/std", + "sp-std/std", + "sp-npos-elections/std", + "sp-arithmetic/std", +] diff --git a/primitives/election-providers/src/lib.rs b/primitives/election-providers/src/lib.rs new file mode 100644 index 0000000000000..69261920be9a2 --- /dev/null +++ b/primitives/election-providers/src/lib.rs @@ -0,0 +1,239 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 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. + +//! Primitive traits for providing election functionality. +//! +//! This crate provides two traits that could interact to enable extensible election functionality +//! within FRAME pallets. +//! +//! Something that will provide the functionality of election will implement [`ElectionProvider`], +//! whilst needing an associated [`ElectionProvider::DataProvider`], which needs to be fulfilled by +//! an entity implementing [`ElectionDataProvider`]. Most often, *the data provider is* the receiver +//! of the election, resulting in a diagram as below: +//! +//! ```ignore +//! ElectionDataProvider +//! <------------------------------------------+ +//! | | +//! v | +//! +-----+----+ +------+---+ +//! | | | | +//! pallet-do-election | | | | pallet-needs-election +//! | | | | +//! | | | | +//! +-----+----+ +------+---+ +//! | ^ +//! | | +//! +------------------------------------------+ +//! ElectionProvider +//! ``` +//! +//! > It could also be possible that a third party pallet (C), provides the data of election to an +//! > election provider (B), which then passes the election result to another pallet (A). +//! +//! ## Election Types +//! +//! Typically, two types of elections exist: +//! +//! 1. **Stateless**: Election data is provided, and the election result is immediately ready. +//! 2. **Stateful**: Election data is is queried ahead of time, and the election result might be +//! ready some number of blocks in the future. +//! +//! To accommodate both type of elections in one trait, the traits lean toward **stateful +//! election**, as it is more general than the stateless. This is why [`ElectionProvider::elect`] +//! has no parameters. All value and type parameter must be provided by the [`ElectionDataProvider`] +//! trait, even if the election happens immediately. +//! +//! ## Election Data +//! +//! The data associated with an election, essentially what the [`ElectionDataProvider`] must convey +//! is as follows: +//! +//! 1. A list of voters, with their stake. +//! 2. A list of targets (i.e. _candidates_). +//! 3. A number of desired targets to be elected (i.e. _winners_) +//! +//! In addition to that, the [`ElectionDataProvider`] must also hint [`ElectionProvider`] at when +//! the next election might happen ([`ElectionDataProvider::next_election_prediction`]). A stateless +//! election provider would probably ignore this. A stateful election provider can use this to +//! prepare the election result in advance. +//! +//! Nonetheless, an [`ElectionProvider`] shan't rely on this and should preferably provide some +//! means of fallback election as well, in case the `elect` was called immaturely early. +//! +//! ## Example +//! +//! ```rust +//! # use sp_election_providers::*; +//! # use sp_npos_elections::{Support, Assignment}; +//! +//! type AccountId = u64; +//! type Balance = u64; +//! type BlockNumber = u32; +//! +//! mod data_provider { +//! use super::*; +//! +//! pub trait Config: Sized { +//! type ElectionProvider: ElectionProvider< +//! AccountId, +//! BlockNumber, +//! DataProvider = Module, +//! >; +//! } +//! +//! pub struct Module(std::marker::PhantomData); +//! +//! impl ElectionDataProvider for Module { +//! fn desired_targets() -> u32 { +//! 1 +//! } +//! fn voters() -> Vec<(AccountId, VoteWeight, Vec)> { +//! Default::default() +//! } +//! fn targets() -> Vec { +//! vec![10, 20, 30] +//! } +//! fn next_election_prediction(now: BlockNumber) -> BlockNumber { +//! 0 +//! } +//! } +//! } +//! +//! +//! mod generic_election_provider { +//! use super::*; +//! +//! pub struct GenericElectionProvider(std::marker::PhantomData); +//! +//! pub trait Config { +//! type DataProvider: ElectionDataProvider; +//! } +//! +//! impl ElectionProvider for GenericElectionProvider { +//! type Error = (); +//! type DataProvider = T::DataProvider; +//! +//! fn elect() -> Result, Self::Error> { +//! Self::DataProvider::targets() +//! .first() +//! .map(|winner| vec![(*winner, Support::default())]) +//! .ok_or(()) +//! } +//! } +//! } +//! +//! mod runtime { +//! use super::generic_election_provider; +//! use super::data_provider; +//! use super::AccountId; +//! +//! struct Runtime; +//! impl generic_election_provider::Config for Runtime { +//! type DataProvider = data_provider::Module; +//! } +//! +//! impl data_provider::Config for Runtime { +//! type ElectionProvider = generic_election_provider::GenericElectionProvider; +//! } +//! +//! } +//! +//! # fn main() {} +//! ``` + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod onchain; +use sp_std::prelude::*; + +/// Re-export some type as they are used in the interface. +pub use sp_arithmetic::PerThing; +pub use sp_npos_elections::{Assignment, ExtendedBalance, PerThing128, Supports, VoteWeight}; + +/// Something that can provide the data to an [`ElectionProvider`]. +pub trait ElectionDataProvider { + /// All possible targets for the election, i.e. the candidates. + fn targets() -> Vec; + + /// All possible voters for the election. + /// + /// Note that if a notion of self-vote exists, it should be represented here. + fn voters() -> Vec<(AccountId, VoteWeight, Vec)>; + + /// The number of targets to elect. + fn desired_targets() -> u32; + + /// Provide a best effort prediction about when the next election is about to happen. + /// + /// In essence, the implementor should predict with this function when it will trigger the + /// [`ElectionProvider::elect`]. + /// + /// This is only useful for stateful election providers. + fn next_election_prediction(now: BlockNumber) -> BlockNumber; + + /// Utility function only to be used in benchmarking scenarios, to be implemented optionally, + /// else a noop. + #[cfg(any(feature = "runtime-benchmarks", test))] + fn put_snapshot( + _voters: Vec<(AccountId, VoteWeight, Vec)>, + _targets: Vec, + ) { + } +} + +impl ElectionDataProvider for () { + fn targets() -> Vec { + Default::default() + } + fn voters() -> Vec<(AccountId, VoteWeight, Vec)> { + Default::default() + } + fn desired_targets() -> u32 { + Default::default() + } + fn next_election_prediction(now: BlockNumber) -> BlockNumber { + now + } +} + +/// Something that can compute the result of an election and pass it back to the caller. +/// +/// This trait only provides an interface to _request_ an election, i.e. +/// [`ElectionProvider::elect`]. That data required for the election need to be passed to the +/// implemented of this trait through [`ElectionProvider::DataProvider`]. +pub trait ElectionProvider { + /// The error type that is returned by the provider. + type Error: sp_std::fmt::Debug; + + /// The data provider of the election. + type DataProvider: ElectionDataProvider; + + /// Elect a new set of winners. + /// + /// The result is returned in a target major format, namely as vector of supports. + fn elect() -> Result, Self::Error>; +} + +impl ElectionProvider for () { + type Error = &'static str; + type DataProvider = (); + + fn elect() -> Result, Self::Error> { + Err("<() as ElectionProvider> cannot do anything.") + } +} diff --git a/primitives/election-providers/src/onchain.rs b/primitives/election-providers/src/onchain.rs new file mode 100644 index 0000000000000..5813d385969fa --- /dev/null +++ b/primitives/election-providers/src/onchain.rs @@ -0,0 +1,168 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 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. + +//! An implementation of [`ElectionProvider`] that does an on-chain sequential phragmen. + +use sp_arithmetic::InnerOf; +use crate::{ElectionDataProvider, ElectionProvider}; +use sp_npos_elections::{ + ElectionResult, ExtendedBalance, IdentifierT, PerThing128, Supports, VoteWeight, +}; +use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, prelude::*}; + +/// Errors of the on-chain election. +#[derive(Eq, PartialEq, Debug)] +pub enum Error { + /// An internal error in the NPoS elections crate. + NposElections(sp_npos_elections::Error), +} + +impl From for Error { + fn from(e: sp_npos_elections::Error) -> Self { + Error::NposElections(e) + } +} + +/// A simple on-chain implementation of the election provider trait. +/// +/// This will accept voting data on the fly and produce the results immediately. +/// +/// ### Warning +/// +/// This can be very expensive to run frequently on-chain. Use with care. +pub struct OnChainSequentialPhragmen(PhantomData); + +/// Configuration trait of [`OnChainSequentialPhragmen`]. +/// +/// Note that this is similar to a pallet traits, but [`OnChainSequentialPhragmen`] is not a pallet. +pub trait Config { + /// The account identifier type. + type AccountId: IdentifierT; + /// The block number type. + type BlockNumber; + /// The accuracy used to compute the election: + type Accuracy: PerThing128; + /// Something that provides the data for election. + type DataProvider: ElectionDataProvider; +} + +impl ElectionProvider for OnChainSequentialPhragmen +where + ExtendedBalance: From>, +{ + type Error = Error; + type DataProvider = T::DataProvider; + + fn elect() -> Result, Self::Error> { + let voters = Self::DataProvider::voters(); + let targets = Self::DataProvider::targets(); + let desired_targets = Self::DataProvider::desired_targets() as usize; + + let mut stake_map: BTreeMap = BTreeMap::new(); + + voters.iter().for_each(|(v, s, _)| { + stake_map.insert(v.clone(), *s); + }); + + let stake_of = Box::new(|w: &T::AccountId| -> VoteWeight { + stake_map.get(w).cloned().unwrap_or_default() + }); + + let ElectionResult { + winners, + assignments, + } = sp_npos_elections::seq_phragmen::<_, T::Accuracy>(desired_targets, targets, voters, None) + .map_err(Error::from)?; + + let staked = + sp_npos_elections::assignment_ratio_to_staked_normalized(assignments, &stake_of)?; + let winners = sp_npos_elections::to_without_backing(winners); + + sp_npos_elections::to_supports(&winners, &staked).map_err(Error::from) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_npos_elections::Support; + use sp_runtime::Perbill; + + type AccountId = u64; + type BlockNumber = u32; + + struct Runtime; + impl Config for Runtime { + type AccountId = AccountId; + type BlockNumber = BlockNumber; + type Accuracy = Perbill; + type DataProvider = mock_data_provider::DataProvider; + } + + type OnChainPhragmen = OnChainSequentialPhragmen; + + mod mock_data_provider { + use super::*; + + pub struct DataProvider; + + impl ElectionDataProvider for DataProvider { + fn voters() -> Vec<(AccountId, VoteWeight, Vec)> { + vec![ + (1, 10, vec![10, 20]), + (2, 20, vec![30, 20]), + (3, 30, vec![10, 30]), + ] + } + + fn targets() -> Vec { + vec![10, 20, 30] + } + + fn desired_targets() -> u32 { + 2 + } + + fn next_election_prediction(_: BlockNumber) -> BlockNumber { + 0 + } + } + } + + #[test] + fn onchain_seq_phragmen_works() { + assert_eq!( + OnChainPhragmen::elect().unwrap(), + vec![ + ( + 10, + Support { + total: 25, + voters: vec![(1, 10), (3, 15)] + } + ), + ( + 30, + Support { + total: 35, + voters: vec![(2, 20), (3, 15)] + } + ) + ] + ); + } +} diff --git a/primitives/npos-elections/compact/src/assignment.rs b/primitives/npos-elections/compact/src/assignment.rs index 4f527aa40a748..12f5ca2b41735 100644 --- a/primitives/npos-elections/compact/src/assignment.rs +++ b/primitives/npos-elections/compact/src/assignment.rs @@ -21,7 +21,7 @@ use crate::field_name_for; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -fn from_impl(count: usize) -> TokenStream2 { +pub(crate) fn from_impl(count: usize) -> TokenStream2 { let from_impl_single = { let name = field_name_for(1); quote!(1 => compact.#name.push( @@ -73,7 +73,7 @@ fn from_impl(count: usize) -> TokenStream2 { ) } -fn into_impl(count: usize, per_thing: syn::Type) -> TokenStream2 { +pub(crate) fn into_impl(count: usize, per_thing: syn::Type) -> TokenStream2 { let into_impl_single = { let name = field_name_for(1); quote!( @@ -153,53 +153,3 @@ fn into_impl(count: usize, per_thing: syn::Type) -> TokenStream2 { #into_impl_rest ) } - -pub(crate) fn assignment( - ident: syn::Ident, - voter_type: syn::Type, - target_type: syn::Type, - weight_type: syn::Type, - count: usize, -) -> TokenStream2 { - let from_impl = from_impl(count); - let into_impl = into_impl(count, weight_type.clone()); - - quote!( - use _npos::__OrInvalidIndex; - impl #ident { - pub fn from_assignment( - assignments: Vec<_npos::Assignment>, - index_of_voter: FV, - index_of_target: FT, - ) -> Result - where - A: _npos::IdentifierT, - for<'r> FV: Fn(&'r A) -> Option<#voter_type>, - for<'r> FT: Fn(&'r A) -> Option<#target_type>, - { - let mut compact: #ident = Default::default(); - - for _npos::Assignment { who, distribution } in assignments { - match distribution.len() { - 0 => continue, - #from_impl - _ => { - return Err(_npos::Error::CompactTargetOverflow); - } - } - }; - Ok(compact) - } - - pub fn into_assignment( - self, - voter_at: impl Fn(#voter_type) -> Option, - target_at: impl Fn(#target_type) -> Option, - ) -> Result>, _npos::Error> { - let mut assignments: Vec<_npos::Assignment> = Default::default(); - #into_impl - Ok(assignments) - } - } - ) -} diff --git a/primitives/npos-elections/compact/src/lib.rs b/primitives/npos-elections/compact/src/lib.rs index 32397652f9b93..c8008dba51d65 100644 --- a/primitives/npos-elections/compact/src/lib.rs +++ b/primitives/npos-elections/compact/src/lib.rs @@ -95,19 +95,11 @@ pub fn generate_solution_type(item: TokenStream) -> TokenStream { compact_encoding, ).unwrap_or_else(|e| e.to_compile_error()); - let assignment_impls = assignment::assignment( - ident.clone(), - voter_type.clone(), - target_type.clone(), - weight_type.clone(), - count, - ); - quote!( #imports #solution_struct - #assignment_impls - ).into() + ) + .into() } fn struct_def( @@ -125,29 +117,32 @@ fn struct_def( let singles = { let name = field_name_for(1); + // NOTE: we use the visibility of the struct for the fields as well.. could be made better. quote!( - #name: Vec<(#voter_type, #target_type)>, + #vis #name: Vec<(#voter_type, #target_type)>, ) }; let doubles = { let name = field_name_for(2); quote!( - #name: Vec<(#voter_type, (#target_type, #weight_type), #target_type)>, + #vis #name: Vec<(#voter_type, (#target_type, #weight_type), #target_type)>, ) }; - let rest = (3..=count).map(|c| { - let field_name = field_name_for(c); - let array_len = c - 1; - quote!( - #field_name: Vec<( - #voter_type, - [(#target_type, #weight_type); #array_len], - #target_type - )>, - ) - }).collect::(); + let rest = (3..=count) + .map(|c| { + let field_name = field_name_for(c); + let array_len = c - 1; + quote!( + #vis #field_name: Vec<( + #voter_type, + [(#target_type, #weight_type); #array_len], + #target_type + )>, + ) + }) + .collect::(); let len_impl = len_impl(count); let edge_count_impl = edge_count_impl(count); @@ -172,40 +167,39 @@ fn struct_def( quote!(#[derive(Default, PartialEq, Eq, Clone, Debug, _npos::codec::Encode, _npos::codec::Decode)]) }; + let from_impl = assignment::from_impl(count); + let into_impl = assignment::into_impl(count, weight_type.clone()); + Ok(quote! ( /// A struct to encode a election assignment in a compact way. #derives_and_maybe_compact_encoding #vis struct #ident { #singles #doubles #rest } - impl _npos::VotingLimit for #ident { + use _npos::__OrInvalidIndex; + impl _npos::CompactSolution for #ident { const LIMIT: usize = #count; - } + type Voter = #voter_type; + type Target = #target_type; + type Accuracy = #weight_type; - impl #ident { - /// Get the length of all the assignments that this type is encoding. This is basically - /// the same as the number of assignments, or the number of voters in total. - pub fn len(&self) -> usize { + fn voter_count(&self) -> usize { let mut all_len = 0usize; #len_impl all_len } - /// Get the total count of edges. - pub fn edge_count(&self) -> usize { + fn edge_count(&self) -> usize { let mut all_edges = 0usize; #edge_count_impl all_edges } - /// Get the number of unique targets in the whole struct. - /// - /// Once presented with a list of winners, this set and the set of winners must be - /// equal. - /// - /// The resulting indices are sorted. - pub fn unique_targets(&self) -> Vec<#target_type> { - let mut all_targets: Vec<#target_type> = Vec::with_capacity(self.average_edge_count()); - let mut maybe_insert_target = |t: #target_type| { + fn unique_targets(&self) -> Vec { + // NOTE: this implementation returns the targets sorted, but we don't use it yet per + // se, nor is the API enforcing it. + let mut all_targets: Vec = + Vec::with_capacity(self.average_edge_count()); + let mut maybe_insert_target = |t: Self::Target| { match all_targets.binary_search(&t) { Ok(_) => (), Err(pos) => all_targets.insert(pos, t) @@ -217,22 +211,44 @@ fn struct_def( all_targets } - /// Get the average edge count. - pub fn average_edge_count(&self) -> usize { - self.edge_count().checked_div(self.len()).unwrap_or(0) - } - - /// Remove a certain voter. - /// - /// This will only search until the first instance of `to_remove`, and return true. If - /// no instance is found (no-op), then it returns false. - /// - /// In other words, if this return true, exactly one element must have been removed from - /// `self.len()`. - pub fn remove_voter(&mut self, to_remove: #voter_type) -> bool { + fn remove_voter(&mut self, to_remove: Self::Voter) -> bool { #remove_voter_impl return false } + + fn from_assignment( + assignments: Vec<_npos::Assignment>, + index_of_voter: FV, + index_of_target: FT, + ) -> Result + where + A: _npos::IdentifierT, + for<'r> FV: Fn(&'r A) -> Option, + for<'r> FT: Fn(&'r A) -> Option, + { + let mut compact: #ident = Default::default(); + + for _npos::Assignment { who, distribution } in assignments { + match distribution.len() { + 0 => continue, + #from_impl + _ => { + return Err(_npos::Error::CompactTargetOverflow); + } + } + }; + Ok(compact) + } + + fn into_assignment( + self, + voter_at: impl Fn(Self::Voter) -> Option, + target_at: impl Fn(Self::Target) -> Option, + ) -> Result>, _npos::Error> { + let mut assignments: Vec<_npos::Assignment> = Default::default(); + #into_impl + Ok(assignments) + } } )) } @@ -347,7 +363,6 @@ fn imports() -> Result { } } } - struct SolutionDef { vis: syn::Visibility, ident: syn::Ident, diff --git a/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs b/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs index 024b721b222a7..2ba7e409568b3 100644 --- a/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs +++ b/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs @@ -22,8 +22,8 @@ mod common; use common::*; use honggfuzz::fuzz; use sp_npos_elections::{ - assignment_ratio_to_staked_normalized, build_support_map, to_without_backing, VoteWeight, - evaluate_support, is_score_better, seq_phragmen, + assignment_ratio_to_staked_normalized, is_score_better, seq_phragmen, to_supports, + to_without_backing, EvaluateSupport, VoteWeight, }; use sp_runtime::Perbill; use rand::{self, SeedableRng}; @@ -66,11 +66,16 @@ fn main() { }; let unbalanced_score = { - let staked = assignment_ratio_to_staked_normalized(unbalanced.assignments.clone(), &stake_of).unwrap(); + let staked = assignment_ratio_to_staked_normalized( + unbalanced.assignments.clone(), + &stake_of, + ) + .unwrap(); let winners = to_without_backing(unbalanced.winners.clone()); - let support = build_support_map(winners.as_ref(), staked.as_ref()).unwrap(); + let score = to_supports(winners.as_ref(), staked.as_ref()) + .unwrap() + .evaluate(); - let score = evaluate_support(&support); if score[0] == 0 { // such cases cannot be improved by balancing. return; @@ -87,11 +92,13 @@ fn main() { ).unwrap(); let balanced_score = { - let staked = assignment_ratio_to_staked_normalized(balanced.assignments.clone(), &stake_of).unwrap(); + let staked = assignment_ratio_to_staked_normalized( + balanced.assignments.clone(), + &stake_of, + ).unwrap(); let winners = to_without_backing(balanced.winners); - let support = build_support_map(winners.as_ref(), staked.as_ref()).unwrap(); + to_supports(winners.as_ref(), staked.as_ref()).unwrap().evaluate() - evaluate_support(&support) }; let enhance = is_score_better(balanced_score, unbalanced_score, Perbill::zero()); diff --git a/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs b/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs index 868aa67236f41..8ce7e7d415fa2 100644 --- a/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs +++ b/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs @@ -22,8 +22,8 @@ mod common; use common::*; use honggfuzz::fuzz; use sp_npos_elections::{ - assignment_ratio_to_staked_normalized, build_support_map, to_without_backing, VoteWeight, - evaluate_support, is_score_better, phragmms, + assignment_ratio_to_staked_normalized, is_score_better, phragmms, to_supports, + to_without_backing, EvaluateSupport, VoteWeight, }; use sp_runtime::Perbill; use rand::{self, SeedableRng}; @@ -66,11 +66,14 @@ fn main() { }; let unbalanced_score = { - let staked = assignment_ratio_to_staked_normalized(unbalanced.assignments.clone(), &stake_of).unwrap(); + let staked = assignment_ratio_to_staked_normalized( + unbalanced.assignments.clone(), + &stake_of, + ) + .unwrap(); let winners = to_without_backing(unbalanced.winners.clone()); - let support = build_support_map(winners.as_ref(), staked.as_ref()).unwrap(); + let score = to_supports(&winners, &staked).unwrap().evaluate(); - let score = evaluate_support(&support); if score[0] == 0 { // such cases cannot be improved by balancing. return; @@ -86,11 +89,13 @@ fn main() { ).unwrap(); let balanced_score = { - let staked = assignment_ratio_to_staked_normalized(balanced.assignments.clone(), &stake_of).unwrap(); + let staked = + assignment_ratio_to_staked_normalized(balanced.assignments.clone(), &stake_of) + .unwrap(); let winners = to_without_backing(balanced.winners); - let support = build_support_map(winners.as_ref(), staked.as_ref()).unwrap(); - - evaluate_support(&support) + to_supports(winners.as_ref(), staked.as_ref()) + .unwrap() + .evaluate() }; let enhance = is_score_better(balanced_score, unbalanced_score, Perbill::zero()); diff --git a/primitives/npos-elections/fuzzer/src/reduce.rs b/primitives/npos-elections/fuzzer/src/reduce.rs index 074c1546d49d8..4ee2468d9d140 100644 --- a/primitives/npos-elections/fuzzer/src/reduce.rs +++ b/primitives/npos-elections/fuzzer/src/reduce.rs @@ -34,8 +34,8 @@ use honggfuzz::fuzz; mod common; use common::to_range; -use sp_npos_elections::{StakedAssignment, ExtendedBalance, build_support_map, reduce}; -use rand::{self, Rng, SeedableRng, RngCore}; +use sp_npos_elections::{reduce, to_support_map, ExtendedBalance, StakedAssignment}; +use rand::{self, Rng, RngCore, SeedableRng}; type Balance = u128; type AccountId = u64; @@ -109,9 +109,8 @@ fn assert_assignments_equal( ass1: &Vec>, ass2: &Vec>, ) { - - let support_1 = build_support_map::(winners, ass1).unwrap(); - let support_2 = build_support_map::(winners, ass2).unwrap(); + let support_1 = to_support_map::(winners, ass1).unwrap(); + let support_2 = to_support_map::(winners, ass2).unwrap(); for (who, support) in support_1.iter() { assert_eq!(support.total, support_2.get(who).unwrap().total); diff --git a/primitives/npos-elections/src/helpers.rs b/primitives/npos-elections/src/helpers.rs index 6f4400b6748fd..3dbde0e03c386 100644 --- a/primitives/npos-elections/src/helpers.rs +++ b/primitives/npos-elections/src/helpers.rs @@ -18,21 +18,21 @@ //! Helper methods for npos-elections. use crate::{ - Assignment, ExtendedBalance, VoteWeight, IdentifierT, StakedAssignment, WithApprovalOf, Error, + Assignment, Error, ExtendedBalance, IdentifierT, PerThing128, StakedAssignment, VoteWeight, + WithApprovalOf, }; -use sp_arithmetic::{PerThing, InnerOf}; +use sp_arithmetic::{InnerOf, PerThing}; use sp_std::prelude::*; /// Converts a vector of ratio assignments into ones with absolute budget value. /// /// Note that this will NOT attempt at normalizing the result. -pub fn assignment_ratio_to_staked( +pub fn assignment_ratio_to_staked( ratios: Vec>, stake_of: FS, ) -> Vec> where for<'r> FS: Fn(&'r A) -> VoteWeight, - P: sp_std::ops::Mul, ExtendedBalance: From>, { ratios @@ -45,19 +45,22 @@ where } /// Same as [`assignment_ratio_to_staked`] and try and do normalization. -pub fn assignment_ratio_to_staked_normalized( +pub fn assignment_ratio_to_staked_normalized( ratio: Vec>, stake_of: FS, ) -> Result>, Error> where for<'r> FS: Fn(&'r A) -> VoteWeight, - P: sp_std::ops::Mul, ExtendedBalance: From>, { let mut staked = assignment_ratio_to_staked(ratio, &stake_of); - staked.iter_mut().map(|a| - a.try_normalize(stake_of(&a.who).into()).map_err(|err| Error::ArithmeticError(err)) - ).collect::>()?; + staked + .iter_mut() + .map(|a| { + a.try_normalize(stake_of(&a.who).into()) + .map_err(|err| Error::ArithmeticError(err)) + }) + .collect::>()?; Ok(staked) } @@ -74,7 +77,7 @@ where } /// Same as [`assignment_staked_to_ratio`] and try and do normalization. -pub fn assignment_staked_to_ratio_normalized( +pub fn assignment_staked_to_ratio_normalized( staked: Vec>, ) -> Result>, Error> where diff --git a/primitives/npos-elections/src/lib.rs b/primitives/npos-elections/src/lib.rs index 1e3c2707497c2..7966b66e383fc 100644 --- a/primitives/npos-elections/src/lib.rs +++ b/primitives/npos-elections/src/lib.rs @@ -21,8 +21,8 @@ //! - [`phragmms()`]: Implements a hybrid approach inspired by Phragmén which is executed faster but //! it can achieve a constant factor approximation of the maximin problem, similar to that of the //! MMS algorithm. -//! - [`balance`]: Implements the star balancing algorithm. This iterative process can push -//! a solution toward being more `balances`, which in turn can increase its score. +//! - [`balance`]: Implements the star balancing algorithm. This iterative process can push a +//! solution toward being more `balances`, which in turn can increase its score. //! //! ### Terminology //! @@ -57,7 +57,6 @@ //! //! // the combination of the two makes the election result. //! let election_result = ElectionResult { winners, assignments }; -//! //! ``` //! //! The `Assignment` field of the election result is voter-major, i.e. it is from the perspective of @@ -74,18 +73,24 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::{ - prelude::*, collections::btree_map::BTreeMap, fmt::Debug, cmp::Ordering, rc::Rc, cell::RefCell, -}; use sp_arithmetic::{ - PerThing, Rational128, ThresholdOrd, InnerOf, Normalizable, - traits::{Zero, Bounded}, + traits::{Bounded, UniqueSaturatedInto, Zero}, + InnerOf, Normalizable, PerThing, Rational128, ThresholdOrd, +}; +use sp_std::{ + cell::RefCell, + cmp::Ordering, + collections::btree_map::BTreeMap, + convert::{TryFrom, TryInto}, + fmt::Debug, + ops::Mul, + prelude::*, + rc::Rc, }; +use codec::{Decode, Encode}; #[cfg(feature = "std")] -use serde::{Serialize, Deserialize}; -#[cfg(feature = "std")] -use codec::{Encode, Decode}; +use serde::{Deserialize, Serialize}; #[cfg(test)] mod mock; @@ -125,20 +130,105 @@ impl __OrInvalidIndex for Option { } } -// re-export the compact solution type. -pub use sp_npos_elections_compact::generate_solution_type; - -/// A trait to limit the number of votes per voter. The generated compact type will implement this. -pub trait VotingLimit { +/// A common interface for all compact solutions. +/// +/// See [`sp-npos-elections-compact`] for more info. +pub trait CompactSolution: Sized { + /// The maximum number of votes that are allowed. const LIMIT: usize; + + /// The voter type. + type Voter: UniqueSaturatedInto + TryInto + TryFrom + Debug + Copy + Clone; + + /// The target type + type Target: UniqueSaturatedInto + TryInto + TryFrom + Debug + Copy + Clone; + + /// The weight/accuracy type of each vote. + type Accuracy: PerThing128; + + /// Build self from a `Vec>`. + fn from_assignment( + assignments: Vec>, + voter_index: FV, + target_index: FT, + ) -> Result + where + A: IdentifierT, + for<'r> FV: Fn(&'r A) -> Option, + for<'r> FT: Fn(&'r A) -> Option; + + /// Convert self into a `Vec>` + fn into_assignment( + self, + voter_at: impl Fn(Self::Voter) -> Option, + target_at: impl Fn(Self::Target) -> Option, + ) -> Result>, Error>; + + /// Get the length of all the voters that this type is encoding. + /// + /// This is basically the same as the number of assignments. + fn voter_count(&self) -> usize; + + /// Get the total count of edges. + /// + /// This is effectively in the range of {[`Self::voter_count`], [`Self::voter_count`] * + /// [`Self::LIMIT`]}. + fn edge_count(&self) -> usize; + + /// Get the number of unique targets in the whole struct. + /// + /// Once presented with a list of winners, this set and the set of winners must be + /// equal. + fn unique_targets(&self) -> Vec; + + /// Get the average edge count. + fn average_edge_count(&self) -> usize { + self.edge_count() + .checked_div(self.voter_count()) + .unwrap_or(0) + } + + /// Remove a certain voter. + /// + /// This will only search until the first instance of `to_remove`, and return true. If + /// no instance is found (no-op), then it returns false. + /// + /// In other words, if this return true, exactly one element must have been removed from + /// `self.len()`. + fn remove_voter(&mut self, to_remove: Self::Voter) -> bool; + + /// Compute the score of this compact solution type. + fn score( + self, + winners: &[A], + stake_of: FS, + voter_at: impl Fn(Self::Voter) -> Option, + target_at: impl Fn(Self::Target) -> Option, + ) -> Result + where + for<'r> FS: Fn(&'r A) -> VoteWeight, + A: IdentifierT, + ExtendedBalance: From>, + { + let ratio = self.into_assignment(voter_at, target_at)?; + let staked = helpers::assignment_ratio_to_staked_normalized(ratio, stake_of)?; + let supports = to_supports(winners, &staked)?; + Ok(supports.evaluate()) + } } +// re-export the compact solution type. +pub use sp_npos_elections_compact::generate_solution_type; + /// an aggregator trait for a generic type of a voter/target identifier. This usually maps to /// substrate's account id. pub trait IdentifierT: Clone + Eq + Default + Ord + Debug + codec::Codec {} - impl IdentifierT for T {} +/// Aggregator trait for a PerThing that can be multiplied by u128 (ExtendedBalance). +pub trait PerThing128: PerThing + Mul {} +impl> PerThing128 for T {} + /// The errors that might occur in the this crate and compact. #[derive(Debug, Eq, PartialEq)] pub enum Error { @@ -151,6 +241,8 @@ pub enum Error { CompactInvalidIndex, /// An error occurred in some arithmetic operation. ArithmeticError(&'static str), + /// The data provided to create support map was invalid. + InvalidSupportEdge, } /// A type which is used in the API of this crate as a numeric weight of a vote, most often the @@ -160,7 +252,8 @@ pub type VoteWeight = u64; /// A type in which performing operations on vote weights are safe. pub type ExtendedBalance = u128; -/// The score of an assignment. This can be computed from the support map via [`evaluate_support`]. +/// The score of an assignment. This can be computed from the support map via +/// [`EvaluateSupport::evaluate`]. pub type ElectionScore = [ExtendedBalance; 3]; /// A winner, with their respective approval stake. @@ -331,10 +424,7 @@ pub struct Assignment { pub distribution: Vec<(AccountId, P)>, } -impl Assignment -where - ExtendedBalance: From>, -{ +impl Assignment { /// Convert from a ratio assignment into one with absolute values aka. [`StakedAssignment`]. /// /// It needs `stake` which is the total budget of the voter. If `fill` is set to true, it @@ -344,11 +434,9 @@ where /// /// If an edge ratio is [`Bounded::min_value()`], it is dropped. This edge can never mean /// anything useful. - pub fn into_staked(self, stake: ExtendedBalance) -> StakedAssignment - where - P: sp_std::ops::Mul, - { - let distribution = self.distribution + pub fn into_staked(self, stake: ExtendedBalance) -> StakedAssignment { + let distribution = self + .distribution .into_iter() .filter_map(|(target, p)| { // if this ratio is zero, then skip it. @@ -408,11 +496,8 @@ pub struct StakedAssignment { impl StakedAssignment { /// Converts self into the normal [`Assignment`] type. /// - /// If `fill` is set to true, it _tries_ to ensure that all the potential rounding errors are - /// compensated and the distribution's sum is exactly equal to 100%, by adding or subtracting - /// the remainder from the last distribution. - /// - /// NOTE: it is quite critical that this attempt always works. The data type returned here will + /// NOTE: This will always round down, and thus the results might be less than a full 100% `P`. + /// Use a normalization post-processing to fix this. The data type returned here will /// potentially get used to create a compact type; a compact type requires sum of ratios to be /// less than 100% upon un-compacting. /// @@ -479,8 +564,8 @@ impl StakedAssignment { /// /// This, at the current version, resembles the `Exposure` defined in the Staking pallet, yet they /// do not necessarily have to be the same. -#[derive(Default, Debug)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Eq, PartialEq))] +#[derive(Default, Debug, Encode, Decode, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct Support { /// Total support. pub total: ExtendedBalance, @@ -488,51 +573,43 @@ pub struct Support { pub voters: Vec<(AccountId, ExtendedBalance)>, } -/// A linkage from a candidate and its [`Support`]. -pub type SupportMap = BTreeMap>; - -/// Build the support map from the given election result. It maps a flat structure like -/// -/// ```nocompile -/// assignments: vec![ -/// voter1, vec![(candidate1, w11), (candidate2, w12)], -/// voter2, vec![(candidate1, w21), (candidate2, w22)] -/// ] -/// ``` +/// A target-major representation of the the election outcome. /// -/// into a mapping of candidates and their respective support: +/// Essentially a flat variant of [`SupportMap`]. /// -/// ```nocompile -/// SupportMap { -/// candidate1: Support { -/// own:0, -/// total: w11 + w21, -/// others: vec![(candidate1, w11), (candidate2, w21)] -/// }, -/// candidate2: Support { -/// own:0, -/// total: w12 + w22, -/// others: vec![(candidate1, w12), (candidate2, w22)] -/// }, -/// } -/// ``` +/// The main advantage of this is that it is encodable. +pub type Supports = Vec<(A, Support)>; + +/// Linkage from a winner to their [`Support`]. /// -/// The second returned flag indicates the number of edges who didn't corresponded to an actual -/// winner from the given winner set. A value in this place larger than 0 indicates a potentially -/// faulty assignment. +/// This is more helpful than a normal [`Supports`] as it allows faster error checking. +pub type SupportMap = BTreeMap>; + +/// Helper trait to convert from a support map to a flat support vector. +pub trait FlattenSupportMap { + /// Flatten the support. + fn flatten(self) -> Supports; +} + +impl FlattenSupportMap for SupportMap { + fn flatten(self) -> Supports { + self.into_iter().collect::>() + } +} + +/// Build the support map from the winners and assignments. /// -/// `O(E)` where `E` is the total number of edges. -pub fn build_support_map( - winners: &[AccountId], - assignments: &[StakedAssignment], -) -> Result, AccountId> where - AccountId: IdentifierT, -{ +/// The list of winners is basically a redundancy for error checking only; It ensures that all the +/// targets pointed to by the [`Assignment`] are present in the `winners`. +pub fn to_support_map( + winners: &[A], + assignments: &[StakedAssignment], +) -> Result, Error> { // Initialize the support of each candidate. - let mut supports = >::new(); - winners - .iter() - .for_each(|e| { supports.insert(e.clone(), Default::default()); }); + let mut supports = >::new(); + winners.iter().for_each(|e| { + supports.insert(e.clone(), Default::default()); + }); // build support struct. for StakedAssignment { who, distribution } in assignments.iter() { @@ -541,37 +618,83 @@ pub fn build_support_map( support.total = support.total.saturating_add(*weight_extended); support.voters.push((who.clone(), *weight_extended)); } else { - return Err(c.clone()) + return Err(Error::InvalidSupportEdge) } } } Ok(supports) } -/// Evaluate a support map. The returned tuple contains: +/// Same as [`to_support_map`] except it calls `FlattenSupportMap` on top of the result to return a +/// flat vector. /// -/// - Minimum support. This value must be **maximized**. -/// - Sum of all supports. This value must be **maximized**. -/// - Sum of all supports squared. This value must be **minimized**. +/// Similar to [`to_support_map`], `winners` is used for error checking. +pub fn to_supports( + winners: &[A], + assignments: &[StakedAssignment], +) -> Result, Error> { + to_support_map(winners, assignments).map(FlattenSupportMap::flatten) +} + +/// Extension trait for evaluating a support map or vector. +pub trait EvaluateSupport { + /// Evaluate a support map. The returned tuple contains: + /// + /// - Minimum support. This value must be **maximized**. + /// - Sum of all supports. This value must be **maximized**. + /// - Sum of all supports squared. This value must be **minimized**. + fn evaluate(self) -> ElectionScore; +} + +/// A common wrapper trait for both (&A, &B) and &(A, B). /// -/// `O(E)` where `E` is the total number of edges. -pub fn evaluate_support( - support: &SupportMap, -) -> ElectionScore { - let mut min_support = ExtendedBalance::max_value(); - let mut sum: ExtendedBalance = Zero::zero(); - // NOTE: The third element might saturate but fine for now since this will run on-chain and need - // to be fast. - let mut sum_squared: ExtendedBalance = Zero::zero(); - for (_, support) in support.iter() { - sum = sum.saturating_add(support.total); - let squared = support.total.saturating_mul(support.total); - sum_squared = sum_squared.saturating_add(squared); - if support.total < min_support { - min_support = support.total; +/// This allows us to implemented something for both `Vec<_>` and `BTreeMap<_>`, such as +/// [`EvaluateSupport`]. +pub trait TupleRef { + fn extract(&self) -> (&K, &V); +} + +impl TupleRef for &(K, V) { + fn extract(&self) -> (&K, &V) { + (&self.0, &self.1) + } +} + +impl TupleRef for (K, V) { + fn extract(&self) -> (&K, &V) { + (&self.0, &self.1) + } +} + +impl TupleRef for (&K, &V) { + fn extract(&self) -> (&K, &V) { + (self.0, self.1) + } +} + +impl EvaluateSupport for C +where + C: IntoIterator, + I: TupleRef>, + A: IdentifierT, +{ + fn evaluate(self) -> ElectionScore { + let mut min_support = ExtendedBalance::max_value(); + let mut sum: ExtendedBalance = Zero::zero(); + // NOTE: The third element might saturate but fine for now since this will run on-chain and + // need to be fast. + let mut sum_squared: ExtendedBalance = Zero::zero(); + for item in self { + let (_, support) = item.extract(); + sum = sum.saturating_add(support.total); + let squared = support.total.saturating_mul(support.total); + sum_squared = sum_squared.saturating_add(squared); + if support.total < min_support { + min_support = support.total; + } } + [min_support, sum, sum_squared] } - [min_support, sum, sum_squared] } /// Compares two sets of election scores based on desirability and returns true if `this` is better @@ -582,14 +705,15 @@ pub fn evaluate_support( /// /// Note that the third component should be minimized. pub fn is_score_better(this: ElectionScore, that: ElectionScore, epsilon: P) -> bool - where ExtendedBalance: From> +where + ExtendedBalance: From>, { match this .iter() - .enumerate() - .map(|(i, e)| ( - e.ge(&that[i]), - e.tcmp(&that[i], epsilon.mul_ceil(that[i])), + .zip(that.iter()) + .map(|(thi, tha)| ( + thi.ge(&tha), + thi.tcmp(&tha, epsilon.mul_ceil(*tha)), )) .collect::>() .as_slice() diff --git a/primitives/npos-elections/src/mock.rs b/primitives/npos-elections/src/mock.rs index 410adcc3779e0..57b2204a72b48 100644 --- a/primitives/npos-elections/src/mock.rs +++ b/primitives/npos-elections/src/mock.rs @@ -19,10 +19,13 @@ #![cfg(test)] -use crate::{seq_phragmen, ElectionResult, Assignment, VoteWeight, ExtendedBalance}; -use sp_arithmetic::{PerThing, InnerOf, traits::{SaturatedConversion, Zero, One}}; -use sp_std::collections::btree_map::BTreeMap; +use crate::*; +use sp_arithmetic::{ + traits::{One, SaturatedConversion, Zero}, + InnerOf, PerThing, +}; use sp_runtime::assert_eq_error_rate; +use sp_std::collections::btree_map::BTreeMap; #[derive(Default, Debug)] pub(crate) struct _Candidate { @@ -313,14 +316,13 @@ pub fn check_assignments_sum(assignments: Vec( +pub(crate) fn run_and_compare( candidates: Vec, voters: Vec<(AccountId, Vec)>, stake_of: &Box VoteWeight>, to_elect: usize, ) where ExtendedBalance: From>, - Output: sp_std::ops::Mul, { // run fixed point code. let ElectionResult { winners, assignments } = seq_phragmen::<_, Output>( diff --git a/primitives/npos-elections/src/phragmen.rs b/primitives/npos-elections/src/phragmen.rs index 8f88c45ae6de8..ed45efe1b54f7 100644 --- a/primitives/npos-elections/src/phragmen.rs +++ b/primitives/npos-elections/src/phragmen.rs @@ -21,15 +21,15 @@ //! to the Maximin problem. use crate::{ - IdentifierT, VoteWeight, Voter, CandidatePtr, ExtendedBalance, setup_inputs, ElectionResult, + balancing, setup_inputs, CandidatePtr, ElectionResult, ExtendedBalance, IdentifierT, + PerThing128, VoteWeight, Voter, }; -use sp_std::prelude::*; use sp_arithmetic::{ - PerThing, InnerOf, Rational128, helpers_128bit::multiply_by_rational, - traits::{Zero, Bounded}, + traits::{Bounded, Zero}, + InnerOf, Rational128, }; -use crate::balancing; +use sp_std::prelude::*; /// The denominator used for loads. Since votes are collected as u64, the smallest ratio that we /// might collect is `1/approval_stake` where approval stake is the sum of votes. Hence, some number @@ -63,12 +63,15 @@ const DEN: ExtendedBalance = ExtendedBalance::max_value(); /// `expect` this to return `Ok`. /// /// This can only fail if the normalization fails. -pub fn seq_phragmen( +pub fn seq_phragmen( rounds: usize, initial_candidates: Vec, initial_voters: Vec<(AccountId, VoteWeight, Vec)>, balance: Option<(usize, ExtendedBalance)>, -) -> Result, &'static str> where ExtendedBalance: From> { +) -> Result, crate::Error> +where + ExtendedBalance: From>, +{ let (candidates, voters) = setup_inputs(initial_candidates, initial_voters); let (candidates, mut voters) = seq_phragmen_core::( @@ -93,13 +96,26 @@ pub fn seq_phragmen( // sort winners based on desirability. winners.sort_by_key(|c_ptr| c_ptr.borrow().round); - let mut assignments = voters.into_iter().filter_map(|v| v.into_assignment()).collect::>(); - let _ = assignments.iter_mut().map(|a| a.try_normalize()).collect::>()?; - let winners = winners.into_iter().map(|w_ptr| - (w_ptr.borrow().who.clone(), w_ptr.borrow().backed_stake) - ).collect(); + let mut assignments = voters + .into_iter() + .filter_map(|v| v.into_assignment()) + .collect::>(); + let _ = assignments + .iter_mut() + .map(|a| { + a.try_normalize() + .map_err(|e| crate::Error::ArithmeticError(e)) + }) + .collect::>()?; + let winners = winners + .into_iter() + .map(|w_ptr| (w_ptr.borrow().who.clone(), w_ptr.borrow().backed_stake)) + .collect(); - Ok(ElectionResult { winners, assignments }) + Ok(ElectionResult { + winners, + assignments, + }) } /// Core implementation of seq-phragmen. @@ -114,7 +130,7 @@ pub fn seq_phragmen_core( rounds: usize, candidates: Vec>, mut voters: Vec>, -) -> Result<(Vec>, Vec>), &'static str> { +) -> Result<(Vec>, Vec>), crate::Error> { // we have already checked that we have more candidates than minimum_candidate_count. let to_elect = rounds.min(candidates.len()); @@ -198,7 +214,7 @@ pub fn seq_phragmen_core( // edge of all candidates that eventually have a non-zero weight must be elected. debug_assert!(voter.edges.iter().all(|e| e.candidate.borrow().elected)); // inc budget to sum the budget. - voter.try_normalize_elected()?; + voter.try_normalize_elected().map_err(|e| crate::Error::ArithmeticError(e))?; } Ok((candidates, voters)) diff --git a/primitives/npos-elections/src/phragmms.rs b/primitives/npos-elections/src/phragmms.rs index b0f841e57f245..b37d3432f9d7e 100644 --- a/primitives/npos-elections/src/phragmms.rs +++ b/primitives/npos-elections/src/phragmms.rs @@ -23,7 +23,7 @@ use crate::{ IdentifierT, ElectionResult, ExtendedBalance, setup_inputs, VoteWeight, Voter, CandidatePtr, - balance, + balance, PerThing128, }; use sp_arithmetic::{PerThing, InnerOf, Rational128, traits::Bounded}; use sp_std::{prelude::*, rc::Rc}; @@ -41,13 +41,14 @@ use sp_std::{prelude::*, rc::Rc}; /// assignments, `assignment.distribution.map(|p| p.deconstruct()).sum()` fails to fit inside /// `UpperOf

`. A user of this crate may statically assert that this can never happen and safely /// `expect` this to return `Ok`. -pub fn phragmms( +pub fn phragmms( to_elect: usize, initial_candidates: Vec, initial_voters: Vec<(AccountId, VoteWeight, Vec)>, balancing_config: Option<(usize, ExtendedBalance)>, ) -> Result, &'static str> - where ExtendedBalance: From> +where + ExtendedBalance: From>, { let (candidates, mut voters) = setup_inputs(initial_candidates, initial_voters); diff --git a/primitives/npos-elections/src/tests.rs b/primitives/npos-elections/src/tests.rs index 1d26909911f33..7aac5aae1ddab 100644 --- a/primitives/npos-elections/src/tests.rs +++ b/primitives/npos-elections/src/tests.rs @@ -17,14 +17,13 @@ //! Tests for npos-elections. -use crate::mock::*; use crate::{ - seq_phragmen, balancing, build_support_map, is_score_better, helpers::*, - Support, StakedAssignment, Assignment, ElectionResult, ExtendedBalance, setup_inputs, - seq_phragmen_core, Voter, + balancing, helpers::*, is_score_better, mock::*, seq_phragmen, seq_phragmen_core, setup_inputs, + to_support_map, to_supports, Assignment, ElectionResult, ExtendedBalance, StakedAssignment, + Support, Voter, EvaluateSupport, }; +use sp_arithmetic::{PerU16, Perbill, Percent, Permill}; use substrate_test_utils::assert_eq_uvec; -use sp_arithmetic::{Perbill, Permill, Percent, PerU16}; #[test] fn float_phragmen_poc_works() { @@ -53,22 +52,44 @@ fn float_phragmen_poc_works() { assert_eq!( support_map.get(&2).unwrap(), - &_Support { own: 0.0, total: 25.0, others: vec![(10u64, 10.0), (30u64, 15.0)]} + &_Support { + own: 0.0, + total: 25.0, + others: vec![(10u64, 10.0), (30u64, 15.0)] + } ); assert_eq!( support_map.get(&3).unwrap(), - &_Support { own: 0.0, total: 35.0, others: vec![(20u64, 20.0), (30u64, 15.0)]} + &_Support { + own: 0.0, + total: 35.0, + others: vec![(20u64, 20.0), (30u64, 15.0)] + } ); - equalize_float(phragmen_result.assignments, &mut support_map, 0.0, 2, stake_of); + equalize_float( + phragmen_result.assignments, + &mut support_map, + 0.0, + 2, + stake_of, + ); assert_eq!( support_map.get(&2).unwrap(), - &_Support { own: 0.0, total: 30.0, others: vec![(10u64, 10.0), (30u64, 20.0)]} + &_Support { + own: 0.0, + total: 30.0, + others: vec![(10u64, 10.0), (30u64, 20.0)] + } ); assert_eq!( support_map.get(&3).unwrap(), - &_Support { own: 0.0, total: 30.0, others: vec![(20u64, 20.0), (30u64, 10.0)]} + &_Support { + own: 0.0, + total: 30.0, + others: vec![(20u64, 20.0), (30u64, 10.0)] + } ); } @@ -300,7 +321,7 @@ fn phragmen_poc_works() { let staked = assignment_ratio_to_staked(assignments, &stake_of); let winners = to_without_backing(winners); - let support_map = build_support_map::(&winners, &staked).unwrap(); + let support_map = to_support_map::(&winners, &staked).unwrap(); assert_eq_uvec!( staked, @@ -374,7 +395,7 @@ fn phragmen_poc_works_with_balancing() { let staked = assignment_ratio_to_staked(assignments, &stake_of); let winners = to_without_backing(winners); - let support_map = build_support_map::(&winners, &staked).unwrap(); + let support_map = to_support_map::(&winners, &staked).unwrap(); assert_eq_uvec!( staked, @@ -766,7 +787,7 @@ fn phragmen_self_votes_should_be_kept() { let staked_assignments = assignment_ratio_to_staked(result.assignments, &stake_of); let winners = to_without_backing(result.winners); - let supports = build_support_map::(&winners, &staked_assignments).unwrap(); + let supports = to_support_map::(&winners, &staked_assignments).unwrap(); assert_eq!(supports.get(&5u64), None); assert_eq!( @@ -839,6 +860,34 @@ fn duplicate_target_is_ignored_when_winner() { ); } +#[test] +fn support_map_and_vec_can_be_evaluated() { + let candidates = vec![1, 2, 3]; + let voters = vec![(10, vec![1, 2]), (20, vec![1, 3]), (30, vec![2, 3])]; + + let stake_of = create_stake_of(&[(10, 10), (20, 20), (30, 30)]); + let ElectionResult { + winners, + assignments, + } = seq_phragmen::<_, Perbill>( + 2, + candidates, + voters + .iter() + .map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())) + .collect::>(), + None, + ) + .unwrap(); + + let staked = assignment_ratio_to_staked(assignments, &stake_of); + let winners = to_without_backing(winners); + let support_map = to_support_map::(&winners, &staked).unwrap(); + let support_vec = to_supports(&winners, &staked).unwrap(); + + assert_eq!(support_map.evaluate(), support_vec.evaluate()); +} + mod assignment_convert_normalize { use super::*; #[test] @@ -1112,20 +1161,16 @@ mod score { } mod solution_type { - use codec::{Decode, Encode}; use super::AccountId; + use codec::{Decode, Encode}; // these need to come from the same dev-dependency `sp-npos-elections`, not from the crate. - use crate::{ - generate_solution_type, Assignment, - Error as PhragmenError, - }; - use sp_std::{convert::TryInto, fmt::Debug}; + use crate::{generate_solution_type, Assignment, CompactSolution, Error as PhragmenError}; use sp_arithmetic::Percent; + use sp_std::{convert::TryInto, fmt::Debug}; type TestAccuracy = Percent; generate_solution_type!(pub struct TestSolutionCompact::(16)); - #[allow(dead_code)] mod __private { // This is just to make sure that that the compact can be generated in a scope without any @@ -1136,7 +1181,6 @@ mod solution_type { #[compact] struct InnerTestSolutionCompact::(12) ); - } #[test] @@ -1190,7 +1234,7 @@ mod solution_type { compact, Decode::decode(&mut &encoded[..]).unwrap(), ); - assert_eq!(compact.len(), 4); + assert_eq!(compact.voter_count(), 4); assert_eq!(compact.edge_count(), 2 + 4); assert_eq!(compact.unique_targets(), vec![10, 11, 20, 40, 50, 51]); } @@ -1326,7 +1370,7 @@ mod solution_type { ).unwrap(); // basically number of assignments that it is encoding. - assert_eq!(compacted.len(), assignments.len()); + assert_eq!(compacted.voter_count(), assignments.len()); assert_eq!( compacted.edge_count(), assignments.iter().fold(0, |a, b| a + b.distribution.len()), @@ -1410,9 +1454,12 @@ mod solution_type { ..Default::default() }; - assert_eq!(compact.unique_targets(), vec![1, 2, 3, 4, 7, 8, 11, 12, 13, 66, 67]); + assert_eq!( + compact.unique_targets(), + vec![1, 2, 3, 4, 7, 8, 11, 12, 13, 66, 67] + ); assert_eq!(compact.edge_count(), 2 + (2 * 2) + 3 + 16); - assert_eq!(compact.len(), 6); + assert_eq!(compact.voter_count(), 6); // this one has some duplicates. let compact = TestSolutionCompact { @@ -1429,7 +1476,7 @@ mod solution_type { assert_eq!(compact.unique_targets(), vec![1, 3, 4, 7, 8, 11, 13]); assert_eq!(compact.edge_count(), 2 + (2 * 2) + 3); - assert_eq!(compact.len(), 5); + assert_eq!(compact.voter_count(), 5); } #[test] From 5aea9cc9681890eadc110028a49f9a2d65ab12aa Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 15 Jan 2021 15:17:56 +0000 Subject: [PATCH 02/57] pallet and unsigned phase --- Cargo.lock | 34 + Cargo.toml | 1 + bin/node/runtime/Cargo.toml | 5 + bin/node/runtime/src/constants.rs | 2 +- bin/node/runtime/src/lib.rs | 57 +- frame/babe/Cargo.toml | 1 + frame/babe/src/lib.rs | 12 +- frame/babe/src/mock.rs | 11 +- .../election-provider-multi-phase/Cargo.toml | 67 + .../src/benchmarking.rs | 283 ++++ .../src/helpers.rs | 199 +++ .../election-provider-multi-phase/src/lib.rs | 1495 +++++++++++++++++ .../election-provider-multi-phase/src/mock.rs | 386 +++++ .../src/unsigned.rs | 799 +++++++++ .../src/weights.rs | 147 ++ frame/grandpa/Cargo.toml | 1 + frame/grandpa/src/mock.rs | 9 + frame/offences/benchmarking/Cargo.toml | 2 + frame/offences/benchmarking/src/mock.rs | 10 +- frame/session/benchmarking/Cargo.toml | 2 + frame/session/benchmarking/src/mock.rs | 12 +- frame/session/src/lib.rs | 34 +- frame/staking/Cargo.toml | 5 + frame/staking/fuzzer/Cargo.toml | 1 + frame/staking/fuzzer/src/mock.rs | 18 +- frame/staking/src/lib.rs | 344 +++- frame/staking/src/mock.rs | 51 +- frame/staking/src/offchain_election.rs | 52 +- frame/staking/src/testing_utils.rs | 8 +- frame/staking/src/tests.rs | 98 +- frame/support/src/traits.rs | 65 +- 31 files changed, 4042 insertions(+), 169 deletions(-) create mode 100644 frame/election-provider-multi-phase/Cargo.toml create mode 100644 frame/election-provider-multi-phase/src/benchmarking.rs create mode 100644 frame/election-provider-multi-phase/src/helpers.rs create mode 100644 frame/election-provider-multi-phase/src/lib.rs create mode 100644 frame/election-provider-multi-phase/src/mock.rs create mode 100644 frame/election-provider-multi-phase/src/unsigned.rs create mode 100644 frame/election-provider-multi-phase/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index 05f4896e071ff..f02b4183a069f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3954,6 +3954,7 @@ dependencies = [ "pallet-contracts-primitives", "pallet-contracts-rpc-runtime-api", "pallet-democracy", + "pallet-election-provider-multi-phase", "pallet-elections-phragmen", "pallet-grandpa", "pallet-identity", @@ -3992,6 +3993,7 @@ dependencies = [ "sp-inherents", "sp-io", "sp-keyring", + "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-session", @@ -4385,6 +4387,7 @@ dependencies = [ "sp-consensus-babe", "sp-consensus-vrf", "sp-core", + "sp-election-providers", "sp-inherents", "sp-io", "sp-runtime", @@ -4545,6 +4548,32 @@ dependencies = [ "substrate-test-utils", ] +[[package]] +name = "pallet-election-provider-multi-phase" +version = "2.0.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal", + "pallet-balances", + "parity-scale-codec", + "parking_lot 0.11.1", + "paste 1.0.3", + "rand 0.7.3", + "serde", + "sp-arithmetic", + "sp-core", + "sp-election-providers", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-std", + "sp-tracing", + "static_assertions", + "substrate-test-utils", +] + [[package]] name = "pallet-elections" version = "2.0.1" @@ -4645,6 +4674,7 @@ dependencies = [ "serde", "sp-application-crypto", "sp-core", + "sp-election-providers", "sp-finality-grandpa", "sp-io", "sp-keyring", @@ -4834,6 +4864,7 @@ dependencies = [ "parity-scale-codec", "serde", "sp-core", + "sp-election-providers", "sp-io", "sp-runtime", "sp-staking", @@ -4955,6 +4986,7 @@ dependencies = [ "rand 0.7.3", "serde", "sp-core", + "sp-election-providers", "sp-io", "sp-runtime", "sp-session", @@ -4996,6 +5028,7 @@ dependencies = [ "serde", "sp-application-crypto", "sp-core", + "sp-election-providers", "sp-io", "sp-npos-elections", "sp-runtime", @@ -5022,6 +5055,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "sp-core", + "sp-election-providers", "sp-io", "sp-npos-elections", "sp-runtime", diff --git a/Cargo.toml b/Cargo.toml index 1754f896c8846..e3f04cd996e41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ members = [ "frame/contracts/rpc/runtime-api", "frame/democracy", "frame/elections", + "frame/election-provider-multi-phase", "frame/example", "frame/example-offchain-worker", "frame/example-parallel", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index d3cc0101e082b..e74600f9501f0 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -35,6 +35,7 @@ sp-keyring = { version = "2.0.0", optional = true, path = "../../../primitives/k sp-session = { version = "2.0.0", default-features = false, path = "../../../primitives/session" } sp-transaction-pool = { version = "2.0.0", default-features = false, path = "../../../primitives/transaction-pool" } sp-version = { version = "2.0.0", default-features = false, path = "../../../primitives/version" } +sp-npos-elections = { version = "2.0.0", default-features = false, path = "../../../primitives/npos-elections" } # frame dependencies frame-executive = { version = "2.0.0", default-features = false, path = "../../../frame/executive" } @@ -71,6 +72,7 @@ pallet-recovery = { version = "2.0.0", default-features = false, path = "../../. pallet-session = { version = "2.0.0", features = ["historical"], path = "../../../frame/session", default-features = false } pallet-session-benchmarking = { version = "2.0.0", path = "../../../frame/session/benchmarking", default-features = false, optional = true } pallet-staking = { version = "2.0.0", default-features = false, path = "../../../frame/staking" } +pallet-election-provider-multi-phase = { version = "2.0.0", default-features = false, path = "../../../frame/election-provider-multi-phase" } pallet-staking-reward-curve = { version = "2.0.0", default-features = false, path = "../../../frame/staking/reward-curve" } pallet-scheduler = { version = "2.0.0", default-features = false, path = "../../../frame/scheduler" } pallet-society = { version = "2.0.0", default-features = false, path = "../../../frame/society" } @@ -114,6 +116,7 @@ std = [ "pallet-im-online/std", "pallet-indices/std", "sp-inherents/std", + "sp-npos-elections/std", "pallet-lottery/std", "pallet-membership/std", "pallet-mmr/std", @@ -140,6 +143,7 @@ std = [ "frame-benchmarking/std", "frame-system-rpc-runtime-api/std", "frame-system/std", + "pallet-election-provider-multi-phase/std", "pallet-timestamp/std", "pallet-tips/std", "pallet-transaction-payment-rpc-runtime-api/std", @@ -156,6 +160,7 @@ runtime-benchmarks = [ "frame-benchmarking", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-election-provider-multi-phase/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-babe/runtime-benchmarks", diff --git a/bin/node/runtime/src/constants.rs b/bin/node/runtime/src/constants.rs index f447486c7ffc4..c549b1977d376 100644 --- a/bin/node/runtime/src/constants.rs +++ b/bin/node/runtime/src/constants.rs @@ -35,7 +35,7 @@ pub mod time { use node_primitives::{Moment, BlockNumber}; /// Since BABE is probabilistic this is the average expected block time that - /// we are targetting. Blocks will be produced at a minimum duration defined + /// we are targeting. Blocks will be produced at a minimum duration defined /// by `SLOT_DURATION`, but some slots will not be allocated to any /// authority and hence no block will be produced. We expect to have this /// block time on average following the defined slot duration and the value diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e88484e472958..bc3526af195f7 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -28,7 +28,8 @@ use frame_support::{ construct_runtime, parameter_types, debug, RuntimeDebug, weights::{ Weight, IdentityFee, - constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, DispatchClass, + constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, + DispatchClass, }, traits::{ Currency, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness, LockIdentifier, @@ -50,14 +51,14 @@ pub use node_primitives::{AccountId, Signature}; use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Index, Moment}; use sp_api::impl_runtime_apis; use sp_runtime::{ - Permill, Perbill, Perquintill, Percent, ApplyExtrinsicResult, - impl_opaque_keys, generic, create_runtime_str, ModuleId, FixedPointNumber, + Permill, Perbill, Perquintill, Percent, ApplyExtrinsicResult, impl_opaque_keys, generic, + create_runtime_str, ModuleId, FixedPointNumber, }; use sp_runtime::curve::PiecewiseLinear; use sp_runtime::transaction_validity::{TransactionValidity, TransactionSource, TransactionPriority}; use sp_runtime::traits::{ - self, BlakeTwo256, Block as BlockT, StaticLookup, SaturatedConversion, - ConvertInto, OpaqueKeys, NumberFor, + self, BlakeTwo256, Block as BlockT, StaticLookup, SaturatedConversion, ConvertInto, OpaqueKeys, + NumberFor, }; use sp_version::RuntimeVersion; #[cfg(any(feature = "std", test))] @@ -145,7 +146,7 @@ impl OnUnbalanced for DealWithFees { } } -/// We assume that ~10% of the block weight is consumed by `on_initalize` handlers. +/// We assume that ~10% of the block weight is consumed by `on_initialize` handlers. /// This is used to limit the maximal weight of a single extrinsic. const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); /// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used @@ -488,18 +489,56 @@ impl pallet_staking::Config for Runtime { type SessionInterface = Self; type RewardCurve = RewardCurve; type NextNewSession = Session; + type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type ElectionLookahead = ElectionLookahead; type Call = Call; type MaxIterations = MaxIterations; type MinSolutionScoreBump = MinSolutionScoreBump; - type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type UnsignedPriority = StakingUnsignedPriority; // The unsigned solution weight targeted by the OCW. We set it to the maximum possible value of // a single extrinsic. type OffchainSolutionWeightLimit = OffchainSolutionWeightLimit; + type ElectionProvider = ElectionProviderMultiPhase; type WeightInfo = pallet_staking::weights::SubstrateWeight; } +use pallet_election_provider_multi_phase::FallbackStrategy; +parameter_types! { + // phase durations + pub const SignedPhase: u32 = 100; + pub const UnsignedPhase: u32 = 100; + + // fallback: no need to do on-chain phragmen initially. + pub const Fallback: FallbackStrategy = FallbackStrategy::Nothing; + + pub SolutionImprovementThreshold: Perbill = Perbill::from_rational_approximation(1u32, 10_000); + + // miner configs + pub const TwoPhaseUnsignedPriority: TransactionPriority = StakingUnsignedPriority::get() - 1u64; + pub const MinerMaxIterations: u32 = 10; + pub MinerMaxWeight: Weight = RuntimeBlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic.expect("Normal extrinsics have a weight limit configured; qed") + .saturating_sub(BlockExecutionWeight::get()); +} + +impl pallet_election_provider_multi_phase::Config for Runtime { + type Event = Event; + type Currency = Balances; + type SignedPhase = SignedPhase; + type UnsignedPhase = UnsignedPhase; + type SolutionImprovementThreshold = MinSolutionScoreBump; + type MinerMaxIterations = MinerMaxIterations; + type MinerMaxWeight = MinerMaxWeight; + type UnsignedPriority = TwoPhaseUnsignedPriority; + type DataProvider = Staking; + type OnChainAccuracy = Perbill; + type CompactSolution = pallet_staking::CompactAssignments; + type Fallback = Fallback; + type WeightInfo = pallet_election_provider_multi_phase::weights::SubstrateWeight; + type BenchmarkingConfig = (); +} + parameter_types! { pub const LaunchPeriod: BlockNumber = 28 * 24 * 60 * MINUTES; pub const VotingPeriod: BlockNumber = 28 * 24 * 60 * MINUTES; @@ -546,7 +585,7 @@ impl pallet_democracy::Config for Runtime { >; type BlacklistOrigin = EnsureRoot; // Any single technical committee member may veto a coming council proposal, however they can - // only do it once and it lasts only for the cooloff period. + // only do it once and it lasts only for the cool-off period. type VetoOrigin = pallet_collective::EnsureMember; type CooloffPeriod = CooloffPeriod; type PreimageByteDeposit = PreimageByteDeposit; @@ -1006,6 +1045,7 @@ construct_runtime!( Indices: pallet_indices::{Module, Call, Storage, Config, Event}, Balances: pallet_balances::{Module, Call, Storage, Config, Event}, TransactionPayment: pallet_transaction_payment::{Module, Storage}, + ElectionProviderMultiPhase: pallet_election_provider_multi_phase::{Module, Call, Storage, Event, ValidateUnsigned}, Staking: pallet_staking::{Module, Call, Config, Storage, Event, ValidateUnsigned}, Session: pallet_session::{Module, Call, Storage, Event, Config}, Democracy: pallet_democracy::{Module, Call, Storage, Config, Event}, @@ -1337,6 +1377,7 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_treasury, Treasury); add_benchmark!(params, batches, pallet_utility, Utility); add_benchmark!(params, batches, pallet_vesting, Vesting); + add_benchmark!(params, batches, pallet_election_provider_multi_phase, ElectionProviderMultiPhase); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok(batches) diff --git a/frame/babe/Cargo.toml b/frame/babe/Cargo.toml index 13ac2e4034c9f..a5e33bbf8f338 100644 --- a/frame/babe/Cargo.toml +++ b/frame/babe/Cargo.toml @@ -39,6 +39,7 @@ pallet-offences = { version = "2.0.0", path = "../offences" } pallet-staking = { version = "2.0.0", path = "../staking" } pallet-staking-reward-curve = { version = "2.0.0", path = "../staking/reward-curve" } sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-election-providers = { version = "2.0.0", path = "../../primitives/election-providers" } [features] default = ["std"] diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index d604bfd57d1a3..31266d64f6323 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -415,12 +415,14 @@ impl Module { /// In other word, this is only accurate if no slots are missed. Given missed slots, the slot /// number will grow while the block number will not. Hence, the result can be interpreted as an /// upper bound. - // -------------- IMPORTANT NOTE -------------- + // + // ## IMPORTANT NOTE + // // This implementation is linked to how [`should_epoch_change`] is working. This might need to // be updated accordingly, if the underlying mechanics of slot and epochs change. // - // WEIGHT NOTE: This function is tied to the weight of `EstimateNextSessionRotation`. If you update - // this function, you must also update the corresponding weight. + // WEIGHT NOTE: This function is tied to the weight of `EstimateNextSessionRotation`. If you + // update this function, you must also update the corresponding weight. pub fn next_expected_epoch_change(now: T::BlockNumber) -> Option { let next_slot = Self::current_epoch_start().saturating_add(T::EpochDuration::get()); next_slot @@ -748,6 +750,10 @@ impl OnTimestampSet for Module { } impl frame_support::traits::EstimateNextSessionRotation for Module { + fn average_session_length() -> T::BlockNumber { + T::EpochDuration::get().saturated_into() + } + fn estimate_next_session_rotation(now: T::BlockNumber) -> Option { Self::next_expected_epoch_change(now) } diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 58e2af873fd91..a26f6f7f70c56 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -37,8 +37,9 @@ use sp_consensus_babe::{AuthorityId, AuthorityPair, SlotNumber}; use sp_consensus_vrf::schnorrkel::{VRFOutput, VRFProof}; use sp_staking::SessionIndex; use pallet_staking::EraIndex; +use sp_election_providers::onchain; -impl_outer_origin!{ +impl_outer_origin! { pub enum Origin for Test where system = frame_system {} } @@ -179,6 +180,13 @@ parameter_types! { pub const StakingUnsignedPriority: u64 = u64::max_value() / 2; } +impl onchain::Config for Test { + type AccountId = ::AccountId; + type BlockNumber = ::BlockNumber; + type Accuracy = Perbill; + type DataProvider = Staking; +} + impl pallet_staking::Config for Test { type RewardRemainder = (); type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; @@ -201,6 +209,7 @@ impl pallet_staking::Config for Test { type MaxIterations = (); type MinSolutionScoreBump = (); type OffchainSolutionWeightLimit = (); + type ElectionProvider = onchain::OnChainSequentialPhragmen; type WeightInfo = (); } diff --git a/frame/election-provider-multi-phase/Cargo.toml b/frame/election-provider-multi-phase/Cargo.toml new file mode 100644 index 0000000000000..c70e68cd36faa --- /dev/null +++ b/frame/election-provider-multi-phase/Cargo.toml @@ -0,0 +1,67 @@ +[package] +name = "pallet-election-provider-multi-phase" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "Apache-2.0" +homepage = "https://substrate.dev" +repository = "https://github.com/paritytech/substrate/" +description = "PALLET two phase election providers" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +static_assertions = "1.1.0" +serde = { version = "1.0.101", optional = true } +codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } + +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } + +sp-io ={ version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-npos-elections = { version = "2.0.0", default-features = false, path = "../../primitives/npos-elections" } +sp-arithmetic = { version = "2.0.0", default-features = false, path = "../../primitives/arithmetic" } +sp-election-providers = { version = "2.0.0", default-features = false, path = "../../primitives/election-providers" } + +# Optional imports for benchmarking +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } +rand = { version = "0.7.3", default-features = false, optional = true, features = ["alloc", "small_rng"] } + +[dev-dependencies] +sp-io = { version = "2.0.0", path = "../../primitives/io" } +hex-literal = "0.3.1" +pallet-balances = { version = "2.0.0", path = "../balances" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +paste = "1.0.3" +substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } +parking_lot = "0.11.0" +sp-tracing = { version = "2.0.0", path = "../../primitives/tracing" } +rand = { version = "0.7.3" } +frame-benchmarking = { path = "../benchmarking" } +sp-election-providers = { version = "2.0.0", features = ["runtime-benchmarks"], path = "../../primitives/election-providers" } + + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + + "frame-support/std", + "frame-system/std", + + "sp-io/std", + "sp-std/std", + "sp-runtime/std", + "sp-npos-elections/std", + "sp-arithmetic/std", + "sp-election-providers/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "rand", +] diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs new file mode 100644 index 0000000000000..a7a4eed852850 --- /dev/null +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -0,0 +1,283 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 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. + +//! Two phase election pallet benchmarking. + +use super::*; +use crate::Module as TwoPhase; + +pub use frame_benchmarking::{account, benchmarks, whitelist_account, whitelisted_caller}; +use frame_support::{assert_ok, traits::OnInitialize}; +use frame_system::RawOrigin; +use rand::{prelude::SliceRandom, rngs::SmallRng, SeedableRng}; +use sp_election_providers::Assignment; +use sp_npos_elections::ExtendedBalance; +use sp_runtime::InnerOf; +use sp_arithmetic::traits::One; +use sp_std::convert::TryInto; + +const SEED: u32 = 0; + +/// Creates a **valid** solution with exactly the given size. +/// +/// The snapshot is also created internally. +fn solution_with_size( + size: SolutionSize, + active_voters_count: u32, + desired_targets: u32, +) -> RawSolution> +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, + > as sp_std::convert::TryFrom>::Error: sp_std::fmt::Debug, +{ + assert!(size.targets >= desired_targets, "must have enough targets"); + assert!( + size.targets >= (>::LIMIT * 2) as u32, + "must have enough targets for unique votes." + ); + assert!(size.voters >= active_voters_count, "must have enough voters"); + assert!( + (>::LIMIT as u32) < desired_targets, + "must have enough winners to give them votes." + ); + + let ed: VoteWeight = T::Currency::minimum_balance().saturated_into::(); + let stake: VoteWeight = ed.max(One::one()).saturating_mul(100); + + // first generates random targets. + let targets: Vec = + (0..size.targets).map(|i| account("Targets", i, SEED)).collect(); + + let mut rng = SmallRng::seed_from_u64(999u64); + + // decide who are the winners. + let winners = targets + .as_slice() + .choose_multiple(&mut rng, desired_targets as usize) + .cloned() + .collect::>(); + + // first generate active voters who must vote for a subset of winners. + let active_voters = (0..active_voters_count) + .map(|i| { + // chose a random subset of winners. + let winner_votes = winners + .as_slice() + .choose_multiple(&mut rng, >::LIMIT) + .cloned() + .collect::>(); + let voter = account::("Voter", i, SEED); + (voter, stake, winner_votes) + }) + .collect::>(); + + // rest of the voters. They can only vote for non-winners. + let non_winners = + targets.iter().filter(|t| !winners.contains(t)).cloned().collect::>(); + let rest_voters = (active_voters_count..size.voters) + .map(|i| { + let votes = (&non_winners) + .choose_multiple(&mut rng, >::LIMIT) + .cloned() + .collect::>(); + let voter = account::("Voter", i, SEED); + (voter, stake, votes) + }) + .collect::>(); + + let mut all_voters = active_voters.clone(); + all_voters.extend(rest_voters); + all_voters.shuffle(&mut rng); + + assert_eq!(active_voters.len() as u32, active_voters_count); + assert_eq!(all_voters.len() as u32, size.voters); + assert_eq!(winners.len() as u32, desired_targets); + + >::put(RoundSnapshotMetadata { + voters_len: all_voters.len() as u32, + targets_len: targets.len() as u32, + }); + >::put(desired_targets); + >::put(RoundSnapshot { voters: all_voters.clone(), targets: targets.clone() }); + + // write the snapshot to staking or whoever is the data provider. + T::DataProvider::put_snapshot(all_voters.clone(), targets.clone()); + + let cache = helpers::generate_voter_cache::(&all_voters); + let stake_of = helpers::stake_of_fn::(&all_voters, &cache); + let voter_index = helpers::voter_index_fn::(&cache); + let target_index = helpers::target_index_fn_linear::(&targets); + let voter_at = helpers::voter_at_fn::(&all_voters); + let target_at = helpers::target_at_fn::(&targets); + + let assignments = active_voters + .iter() + .map(|(voter, _stake, votes)| { + let percent_per_edge: InnerOf> = + (100 / votes.len()).try_into().unwrap(); + Assignment { + who: voter.clone(), + distribution: votes + .iter() + .map(|t| (t.clone(), >::from_percent(percent_per_edge))) + .collect::>(), + } + }) + .collect::>(); + + let compact = + >::from_assignment(assignments, &voter_index, &target_index).unwrap(); + let score = compact.clone().score(&winners, stake_of, voter_at, target_at).unwrap(); + let round = >::round(); + RawSolution { compact, score, round } +} + +benchmarks! { + where_clause { + where ExtendedBalance: From>>, + > as sp_std::convert::TryFrom>::Error: sp_std::fmt::Debug, + ExtendedBalance: From>>, + } + + on_initialize_nothing { + assert!(>::current_phase().is_off()); + }: { + >::on_initialize(1u32.into()); + } verify { + assert!(>::current_phase().is_off()); + } + + on_initialize_open_signed { + // NOTE: this benchmark currently doesn't have any components because the length of a db + // read/write is not captured. Otherwise, it is quite influenced by how much data + // `T::ElectionDataProvider` is reading and passing on. + assert!(>::snapshot().is_none()); + assert!(>::current_phase().is_off()); + }: { + >::on_initialize_open_signed(); + } verify { + assert!(>::snapshot().is_some()); + assert!(>::current_phase().is_signed()); + } + + on_initialize_open_unsigned_with_snapshot { + assert!(>::snapshot().is_none()); + assert!(>::current_phase().is_off()); + }: { + >::on_initialize_open_unsigned(true, true, 1u32.into()); + } verify { + assert!(>::snapshot().is_some()); + assert!(>::current_phase().is_unsigned()); + } + + on_initialize_open_unsigned_without_snapshot { + // need to assume signed phase was open before + >::on_initialize_open_signed(); + assert!(>::snapshot().is_some()); + assert!(>::current_phase().is_signed()); + }: { + >::on_initialize_open_unsigned(false, true, 1u32.into()); + } verify { + assert!(>::snapshot().is_some()); + assert!(>::current_phase().is_unsigned()); + } + + #[extra] + create_snapshot { + assert!(>::snapshot().is_none()); + }: { + >::create_snapshot() + } verify { + assert!(>::snapshot().is_some()); + } + + submit_unsigned { + // number of votes in snapshot. + let v in (T::BenchmarkingConfig::VOTERS[0]) .. T::BenchmarkingConfig::VOTERS[1]; + // number of targets in snapshot. + let t in (T::BenchmarkingConfig::TARGETS[0]) .. T::BenchmarkingConfig::TARGETS[1]; + // number of assignments, i.e. compact.len(). This means the active nominators, thus must be + // a subset of `v` component. + let a in (T::BenchmarkingConfig::ACTIVE_VOTERS[0]) .. T::BenchmarkingConfig::ACTIVE_VOTERS[1]; + // number of desired targets. Must be a subset of `t` component. + let d in (T::BenchmarkingConfig::DESIRED_TARGETS[0]) .. T::BenchmarkingConfig::DESIRED_TARGETS[1]; + + let witness = SolutionSize { voters: v, targets: t }; + let raw_solution = solution_with_size::(witness, a, d); + + assert!(>::queued_solution().is_none()); + >::put(Phase::Unsigned((true, 1u32.into()))); + }: _(RawOrigin::None, raw_solution, witness) + verify { + assert!(>::queued_solution().is_some()); + } + + // This is checking a valid solution. The worse case is indeed a valid solution. + feasibility_check { + // number of votes in snapshot. + let v in (T::BenchmarkingConfig::VOTERS[0]) .. T::BenchmarkingConfig::VOTERS[1]; + // number of targets in snapshot. + let t in (T::BenchmarkingConfig::TARGETS[0]) .. T::BenchmarkingConfig::TARGETS[1]; + // number of assignments, i.e. compact.len(). This means the active nominators, thus must be + // a subset of `v` component. + let a in (T::BenchmarkingConfig::ACTIVE_VOTERS[0]) .. T::BenchmarkingConfig::ACTIVE_VOTERS[1]; + // number of desired targets. Must be a subset of `t` component. + let d in (T::BenchmarkingConfig::DESIRED_TARGETS[0]) .. T::BenchmarkingConfig::DESIRED_TARGETS[1]; + + let size = SolutionSize { voters: v, targets: t }; + let raw_solution = solution_with_size::(size, a, d); + + assert_eq!(raw_solution.compact.voter_count() as u32, a); + assert_eq!(raw_solution.compact.unique_targets().len() as u32, d); + }: { + assert_ok!(>::feasibility_check(raw_solution, ElectionCompute::Unsigned)); + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::mock::*; + + #[test] + fn test_benchmarks() { + ExtBuilder::default().build_and_execute(|| { + assert_ok!(test_benchmark_feasibility_check::()); + }); + + ExtBuilder::default().build_and_execute(|| { + assert_ok!(test_benchmark_submit_unsigned::()); + }); + + ExtBuilder::default().build_and_execute(|| { + assert_ok!(test_benchmark_on_initialize_open_unsigned_with_snapshot::()); + }); + + ExtBuilder::default().build_and_execute(|| { + assert_ok!(test_benchmark_on_initialize_open_unsigned_without_snapshot::()); + }); + + ExtBuilder::default().build_and_execute(|| { + assert_ok!(test_benchmark_on_initialize_nothing::()); + }); + + ExtBuilder::default().build_and_execute(|| { + assert_ok!(test_benchmark_create_snapshot::()); + }); + } +} diff --git a/frame/election-provider-multi-phase/src/helpers.rs b/frame/election-provider-multi-phase/src/helpers.rs new file mode 100644 index 0000000000000..da4a092653ae7 --- /dev/null +++ b/frame/election-provider-multi-phase/src/helpers.rs @@ -0,0 +1,199 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 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. + +//! Some helper functions/macros for this crate. + +use super::{ + Config, VoteWeight, CompactVoterIndexOf, CompactTargetIndexOf, CompactAccuracyOf, + OnChainAccuracyOf, ExtendedBalance, +}; +use sp_runtime::InnerOf; +use sp_std::{collections::btree_map::BTreeMap, convert::TryInto, boxed::Box, prelude::*}; + +#[macro_export] +macro_rules! log { + ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { + frame_support::debug::$level!( + target: $crate::LOG_TARGET, + concat!("🏦 ", $patter) $(, $values)* + ) + }; +} + +/// Generate a btree-map cache of the voters and their indices. +/// +/// This can be used to efficiently build index getter closures. +pub fn generate_voter_cache( + snapshot: &Vec<(T::AccountId, VoteWeight, Vec)>, +) -> BTreeMap +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + let mut cache: BTreeMap = BTreeMap::new(); + snapshot.iter().enumerate().for_each(|(i, (x, _, _))| { + let _existed = cache.insert(x.clone(), i); + // if a duplicate exists, we only consider the last one. Defensive only, should never + // happen. + debug_assert!(_existed.is_none()); + }); + + cache +} + +/// Create a function the returns the index a voter in the snapshot. +/// +/// The returning index type is the same as the one defined in [`T::CompactSolution::Voter`]. +/// +/// ## Warning +/// +/// The snapshot must be the same is the one used to create `cache`. +pub fn voter_index_fn( + cache: &BTreeMap, +) -> Box Option> + '_> +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + Box::new(move |who| { + cache.get(who).and_then(|i| >>::try_into(*i).ok()) + }) +} + +/// Same as [`voter_index_fn`], but the returning index is converted into usize, if possible. +/// +/// ## Warning +/// +/// The snapshot must be the same is the one used to create `cache`. +pub fn voter_index_fn_usize( + cache: &BTreeMap, +) -> Box Option + '_> +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + Box::new(move |who| cache.get(who).cloned()) +} + +/// A non-optimized, linear version of [`voter_index_fn`] that does not need a cache and does a +/// linear search. +/// +/// ## Warning +/// +/// Not meant to be used in production. +pub fn voter_index_fn_linear( + snapshot: &Vec<(T::AccountId, VoteWeight, Vec)>, +) -> Box Option> + '_> +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + Box::new(move |who| { + snapshot + .iter() + .position(|(x, _, _)| x == who) + .and_then(|i| >>::try_into(i).ok()) + }) +} + +/// Create a function the returns the index a targets in the snapshot. +/// +/// The returning index type is the same as the one defined in [`T::CompactSolution::Target`]. +pub fn target_index_fn_linear( + snapshot: &Vec, +) -> Box Option> + '_> +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + Box::new(move |who| { + snapshot + .iter() + .position(|x| x == who) + .and_then(|i| >>::try_into(i).ok()) + }) +} + +/// Create a function that can map a voter index ([`CompactVoterIndexOf`]) to the actual voter +/// account using a linearly indexible snapshot. +pub fn voter_at_fn( + snapshot: &Vec<(T::AccountId, VoteWeight, Vec)>, +) -> Box) -> Option + '_> +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + Box::new(move |i| { + as TryInto>::try_into(i) + .ok() + .and_then(|i| snapshot.get(i).map(|(x, _, _)| x).cloned()) + }) +} + +/// Create a function that can map a target index ([`CompactTargetIndexOf`]) to the actual target +/// account using a linearly indexible snapshot. +pub fn target_at_fn( + snapshot: &Vec, +) -> Box) -> Option + '_> +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + Box::new(move |i| { + as TryInto>::try_into(i) + .ok() + .and_then(|i| snapshot.get(i).cloned()) + }) +} + +/// Create a function to get the stake of a voter. +/// +/// This is not optimized and uses a linear search. +pub fn stake_of_fn_linear( + snapshot: &Vec<(T::AccountId, VoteWeight, Vec)>, +) -> Box VoteWeight + '_> +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + Box::new(move |who| { + snapshot.iter().find(|(x, _, _)| x == who).map(|(_, x, _)| *x).unwrap_or_default() + }) +} + +/// Create a function to get the stake of a voter. +/// +/// ## Warning +/// +/// The cache need must be derived from the same snapshot. Zero is returned if a voter is +/// non-existent. +pub fn stake_of_fn<'a, T: Config>( + snapshot: &'a Vec<(T::AccountId, VoteWeight, Vec)>, + cache: &'a BTreeMap, +) -> Box VoteWeight + 'a> +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + Box::new(move |who| { + if let Some(index) = cache.get(who) { + snapshot.get(*index).map(|(_, x, _)| x).cloned().unwrap_or_default() + } else { + 0 + } + }) +} diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs new file mode 100644 index 0000000000000..79112297f869f --- /dev/null +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -0,0 +1,1495 @@ +// 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. + +//! # Two phase, offchain election provider pallet. +//! +//! As the name suggests, this election-provider has two distinct phases (see [`Phase`]), signed and +//! unsigned. +//! +//! ## Phases +//! +//! The timeline of pallet is as follows. At each block, +//! [`sp_election_providers::ElectionDataProvider::next_election_prediction`] is used to estimate +//! the time remaining to the next call to [`sp_election_providers::ElectionProvider::elect`]. Based +//! on this, a phase is chosen. The timeline is as follows. +//! +//! ```ignore +//! elect() +//! + <--T::SignedPhase--> + <--T::UnsignedPhase--> + +//! +-------------------------------------------------------------------+ +//! Phase::Off + Phase::Signed + Phase::Unsigned + +//! ``` +//! +//! Note that the unsigned phase starts [`pallet::Config::UnsignedPhase`] blocks before the +//! `next_election_prediction`, but only ends when a call to [`ElectionProvider::elect`] happens. +//! +//! > Given this, it is rather important for the user of this pallet to ensure it always terminates +//! election via `elect` before requesting a new one. +//! +//! Each of the phases can be disabled by essentially setting their length to zero. If both phases +//! have length zero, then the pallet essentially runs only the on-chain backup. +//! +//! ### Signed Phase +//! +//! In the signed phase, solutions (of type [`RawSolution`]) are submitted and queued on chain. A +//! deposit is reserved, based on the size of the solution, for the cost of keeping this solution +//! on-chain for a number of blocks, and the potential weight of the solution upon being checked. A +//! maximum of [`pallet::Config::MaxSignedSubmissions`] solutions are stored. The queue is always +//! sorted based on score (worse to best). +//! +//! Upon arrival of a new solution: +//! +//! 1. If the queue is not full, it is stored in the appropriate sorted index. +//! 2. If the queue is full but the submitted solution is better than one of the queued ones, the +//! worse solution is discarded, the bond of the outgoing solution is returned, and the new +//! solution is stored in the correct index. +//! 3. If the queue is full and the solution is not an improvement compared to any of the queued +//! ones, it is instantly rejected and no additional bond is reserved. +//! +//! A signed solution cannot be reversed, taken back, updated, or retracted. In other words, the +//! origin can not bail out in any way, if their solution is queued. +//! +//! Upon the end of the signed phase, the solutions are examined from best to worse (i.e. `pop()`ed +//! until drained). Each solution undergoes an expensive [`Pallet::feasibility_check`], which +//! ensures the score claimed by this score was correct, and it is valid based on the election data +//! (i.e. votes and candidates). At each step, if the current best solution passes the feasibility +//! check, it is considered to be the best one. The sender of the origin is rewarded, and the rest +//! of the queued solutions get their deposit back and are discarded, without being checked. +//! +//! The following example covers all of the cases at the end of the signed phase: +//! +//! ```ignore +//! Queue +//! +-------------------------------+ +//! |Solution(score=20, valid=false)| +--> Slashed +//! +-------------------------------+ +//! |Solution(score=15, valid=true )| +--> Rewarded, Saved +//! +-------------------------------+ +//! |Solution(score=10, valid=true )| +--> Discarded +//! +-------------------------------+ +//! |Solution(score=05, valid=false)| +--> Discarded +//! +-------------------------------+ +//! | None | +//! +-------------------------------+ +//! ``` +//! +//! Note that both of the bottom solutions end up being discarded and get their deposit back, +//! despite one of them being *invalid*. +//! +//! ## Unsigned Phase +//! +//! The unsigned phase will always follow the signed phase, with the specified duration. In this +//! phase, only validator nodes can submit solutions. A validator node who has offchain workers +//! enabled will start to mine a solution in this phase and submits it back to the chain as an +//! unsigned transaction, thus the name _unsigned_ phase. This unsigned transaction can never be +//! valid if propagated, and it acts similar to an inherent. +//! +//! Validators will only submit solutions if the one that they have computed is sufficiently better +//! than the best queued one (see [`pallet::Config::SolutionImprovementThreshold`]) and will limit +//! the weigh of the solution to [`pallet::Config::MinerMaxWeight`]. +//! +//! ### Fallback +//! +//! If we reach the end of both phases (i.e. call to [`ElectionProvider::elect`] happens) and no +//! good solution is queued, then the fallback strategy [`pallet::Config::Fallback`] is used to +//! determine what needs to be done. The on-chain election is slow, and contains no balancing or +//! reduction post-processing. See [`onchain::OnChainSequentialPhragmen`]. The +//! [`FallbackStrategy::Nothing`] should probably only be used for testing, and returns an error. +//! +//! ## Feasible Solution (correct solution) +//! +//! All submissions must undergo a feasibility check. Signed solutions are checked on by one at the +//! end of the signed phase, and the unsigned solutions are checked on the spot. A feasible solution +//! is as follows: +//! +//! 0. **all** of the used indices must be correct. +//! 1. present *exactly* correct number of winners. +//! 2. any assignment is checked to match with [`RoundSnapshot::voters`]. +//! 3. the claimed score is valid, based on the fixed point arithmetic accuracy. +//! +//! ## Accuracy +//! +//! The accuracy of the election is configured via two trait parameters. namely, +//! [`OnChainAccuracyOf`] dictates the accuracy used to compute the on-chain fallback election and +//! [`CompactAccuracyOf`] is the accuracy that the submitted solutions must adhere to. +//! +//! Note that both accuracies are of great importance. The offchain solution should be as small as +//! possible, reducing solutions size/weight. The on-chain solution can use more space for accuracy, +//! but should still be fast to prevent massively large blocks in case of a fallback. +//! +//! ## Future Plans +//! +//! **Challenge Phase**. We plan adding a third phase to the pallet, called the challenge phase. +//! This is phase in which no further solutions are processed, and the current best solution might +//! be challenged by anyone (signed or unsigned). The main plan here is to enforce the solution to +//! be PJR. Checking PJR on-chain is quite expensive, yet proving that a solution is **not** PJR is +//! rather cheap. If a queued solution is challenged: +//! +//! 1. We must surely slash whoever submitted that solution (might be a challenge for unsigned +//! solutions). +//! 2. It is probably fine to fallback to the on-chain election, as we expect this to happen rarely. +//! +//! **Bailing out**. The functionality of bailing out of a queued solution is nice. A miner can +//! submit a solution as soon as they _think_ it is high probability feasible, and do the checks +//! afterwards, and remove their solution (for a small cost of probably just transaction fees, or a +//! portion of the bond). +//! +//! **Conditionally open unsigned phase**: Currently, the unsigned phase is always opened. This is +//! useful because an honest validation will run our OCW code, which should be good enough to trump +//! a mediocre or malicious signed submission (assuming in the absence of honest signed bots). If an +//! when the signed submissions are checked against an absolute measure (e.g. PJR), then we can only +//! open the unsigned phase in extreme conditions (i.e. "not good signed solution received") to +//! spare some work in the validators +//! +//! **Allow smaller solutions and build up**: For now we only allow solutions that are exactly +//! [`DesiredTargets`], no more, no less. Over time, we can change this to a [min, max] where any +//! solution within this range is acceptable, where bigger solutions are prioritized. +//! +//! **Recursive Fallback**: Currently, the fallback is a separate enum. A different and fancier way +//! of doing this would be to have the fallback be another +//! [`sp_election_providers::ElectionProvider`]. In this case, this pallet can even have the +//! on-chain election provider as fallback, or special _noop_ fallback that simply returns an error, +//! thus replicating [`FallbackStrategy::Nothing`]. +//! +//! **Score based on size**: We should always prioritize small solutions over bigger ones, if there +//! is a tie. Even more harsh should be to enforce the bound of the `reduce` algorithm. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode, HasCompact}; +use frame_support::{ + dispatch::DispatchResultWithPostInfo, + ensure, + traits::{Currency, Get, ReservableCurrency}, + weights::Weight, +}; +use frame_system::{ensure_none, ensure_signed, offchain::SendTransactionTypes}; +use sp_election_providers::{ElectionDataProvider, ElectionProvider, onchain}; +use sp_npos_elections::{ + assignment_ratio_to_staked_normalized, is_score_better, CompactSolution, ElectionScore, + EvaluateSupport, ExtendedBalance, PerThing128, Supports, VoteWeight, +}; +use sp_runtime::{ + transaction_validity::{ + InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, + TransactionValidityError, ValidTransaction, + }, + DispatchError, InnerOf, PerThing, Perbill, RuntimeDebug, SaturatedConversion, +}; +use sp_std::prelude::*; +use sp_arithmetic::{ + UpperOf, + traits::{Zero, CheckedAdd}, +}; + +#[cfg(any(feature = "runtime-benchmarks", test))] +mod benchmarking; +#[cfg(test)] +mod mock; +#[macro_use] +pub mod helpers; + +const LOG_TARGET: &'static str = "election-provider"; + +// for the helper macros +#[doc(hidden)] +pub use sp_runtime::traits::UniqueSaturatedInto; +#[doc(hidden)] +pub use sp_std; + +pub mod unsigned; +pub mod weights; + +use weights::WeightInfo; + +// pub mod signed; +// use signed::SignedSubmission; + +/// The compact solution type used by this crate. +pub type CompactOf = ::CompactSolution; + +/// The voter index. Derived from [`CompactOf`]. +pub type CompactVoterIndexOf = as CompactSolution>::Voter; +/// The target index. Derived from [`CompactOf`]. +pub type CompactTargetIndexOf = as CompactSolution>::Target; +/// The accuracy of the election, when submitted from offchain. Derived from [`CompactOf`]. +pub type CompactAccuracyOf = as CompactSolution>::Accuracy; +/// The accuracy of the election, when computed on-chain. Equal to [`Config::OnChainAccuracy`]. +pub type OnChainAccuracyOf = ::OnChainAccuracy; + +struct OnChainConfig(sp_std::marker::PhantomData) +where + ExtendedBalance: From>>, + ExtendedBalance: From>>; +impl onchain::Config for OnChainConfig +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + type AccountId = T::AccountId; + type BlockNumber = T::BlockNumber; + type Accuracy = T::OnChainAccuracy; + type DataProvider = T::DataProvider; +} + +/// Configuration for the benchmarks of the pallet. +pub trait BenchmarkingConfig { + /// Range of voters. + const VOTERS: [u32; 2]; + /// Range of targets. + const TARGETS: [u32; 2]; + /// Range of active voters. + const ACTIVE_VOTERS: [u32; 2]; + /// Range of desired targets. + const DESIRED_TARGETS: [u32; 2]; +} + +impl BenchmarkingConfig for () { + const VOTERS: [u32; 2] = [4000, 6000]; + const TARGETS: [u32; 2] = [1000, 1600]; + const ACTIVE_VOTERS: [u32; 2] = [1000, 3000]; + const DESIRED_TARGETS: [u32; 2] = [400, 800]; +} + +/// Current phase of the pallet. +#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug)] +pub enum Phase { + /// Nothing, the election is not happening. + Off, + /// Signed phase is open. + Signed, + /// Unsigned phase. First element is whether it is open or not, second the starting block + /// number. + Unsigned((bool, Bn)), +} + +impl Default for Phase { + fn default() -> Self { + Phase::Off + } +} + +impl Phase { + /// Weather the phase is signed or not. + pub fn is_signed(&self) -> bool { + matches!(self, Phase::Signed) + } + + /// Weather the phase is unsigned or not. + pub fn is_unsigned(&self) -> bool { + matches!(self, Phase::Unsigned(_)) + } + + /// Weather the phase is unsigned and open or not, with specific start. + pub fn is_unsigned_open_at(&self, at: Bn) -> bool { + matches!(self, Phase::Unsigned((true, real)) if *real == at) + } + + /// Weather the phase is unsigned and open or not. + pub fn is_unsigned_open(&self) -> bool { + matches!(self, Phase::Unsigned((true, _))) + } + + /// Weather the phase is off or not. + pub fn is_off(&self) -> bool { + matches!(self, Phase::Off) + } +} + +/// A configuration for the module to indicate what should happen in the case of a fallback i.e. +/// reaching a call to `elect` with no good solution. +#[cfg_attr(test, derive(Clone))] +pub enum FallbackStrategy { + /// Run a on-chain sequential phragmen. + /// + /// This might burn the chain for a few minutes due to a stall, but is generally a safe + /// approach to maintain a sensible validator set. + OnChain, + /// Nothing. Return an error. + Nothing, +} + +/// The type of `Computation` that provided this election data. +#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug)] +pub enum ElectionCompute { + /// Election was computed on-chain. + OnChain, + /// Election was computed with a signed submission. + Signed, + /// Election was computed with an unsigned submission. + Unsigned, +} + +impl Default for ElectionCompute { + fn default() -> Self { + ElectionCompute::OnChain + } +} + +/// A raw, unchecked solution. +/// +/// This is what will get submitted to the chain. +/// +/// Such a solution should never become effective in anyway before being checked by the +/// [`Pallet::feasibility_check`] +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] +pub struct RawSolution { + /// Compact election edges. + compact: C, + /// The _claimed_ score of the solution. + score: ElectionScore, + /// The round at which this solution should be submitted. + round: u32, +} + +impl Default for RawSolution { + fn default() -> Self { + // Round 0 is always invalid, only set this to 1. + Self { round: 1, compact: Default::default(), score: Default::default() } + } +} + +/// A checked solution, ready to be enacted. +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, Default)] +pub struct ReadySolution { + /// The final supports of the solution. + /// + /// This is target-major vector, storing each winners, total backing, and each individual + /// backer. + supports: Supports, + /// The score of the solution. + /// + /// This is needed to potentially challenge the solution. + score: ElectionScore, + /// How this election was computed. + compute: ElectionCompute, +} + +/// Solution size of the election. +/// +/// This is needed for proper weight calculation. +#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug, Default)] +pub struct SolutionSize { + /// Number of all voters. + /// + /// This must match the on-chain snapshot. + #[codec(compact)] + voters: u32, + /// Number of all targets. + /// + /// This must match the on-chain snapshot. + #[codec(compact)] + targets: u32, +} + +/// A snapshot of all the data that is needed for en entire round. They are provided by +/// [`ElectionDataProvider`] at the beginning of the signed phase (or the unsigned phase, if signed +/// phase is non-existent) and are kept around until the round is finished. +/// +/// These are stored together because they are often times accessed together. +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, Default)] +pub struct RoundSnapshot { + /// All of the voters. + pub voters: Vec<(A, VoteWeight, Vec)>, + /// All of the targets. + pub targets: Vec, +} + +/// Some metadata related to snapshot. +/// +/// In this pallet, there are cases where we want to read the whole snapshot (voters, targets, +/// desired), and cases that we are interested in just the length of these values. The former favors +/// the snapshot to be stored in one struct (as it is now) while the latter prefers them to be +/// separate to enable the use of `decode_len`. This approach is a middle ground, storing the +/// snapshot as one struct, whilst storing the lengths separately. +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, Default)] +pub struct RoundSnapshotMetadata { + /// The length of voters. + voters_len: u32, + /// The length of targets. + targets_len: u32, +} + +/// Internal errors of the pallet. +/// +/// Note that this is different from [`pallet::Error`]. +#[derive(RuntimeDebug, Eq, PartialEq)] +pub enum ElectionError { + /// A feasibility error. + Feasibility(FeasibilityError), + /// An error in the on-chain fallback. + OnChainFallback(onchain::Error), + /// No fallback is configured + NoFallbackConfigured, + /// An internal error in the NPoS elections crate. + NposElections(sp_npos_elections::Error), + /// Snapshot data was unavailable unexpectedly. + SnapshotUnAvailable, + /// Submitting a transaction to the pool failed. + /// + /// This can only happen in the unsigned phase. + PoolSubmissionFailed, +} + +impl From for ElectionError { + fn from(e: onchain::Error) -> Self { + ElectionError::OnChainFallback(e) + } +} + +impl From for ElectionError { + fn from(e: sp_npos_elections::Error) -> Self { + ElectionError::NposElections(e) + } +} + +impl From for ElectionError { + fn from(e: FeasibilityError) -> Self { + ElectionError::Feasibility(e) + } +} + +/// Errors that can happen in the feasibility check. +#[derive(RuntimeDebug, Eq, PartialEq)] +pub enum FeasibilityError { + /// Wrong number of winners presented. + WrongWinnerCount, + /// The snapshot is not available. + /// + /// This must be an internal error of the chain. + SnapshotUnavailable, + /// Internal error from the election crate. + NposElection(sp_npos_elections::Error), + /// A vote is invalid. + InvalidVote, + /// A voter is invalid. + InvalidVoter, + /// A winner is invalid. + InvalidWinner, + /// The given score was invalid. + InvalidScore, + /// The provided round is incorrect. + InvalidRound, +} + +impl From for FeasibilityError { + fn from(e: sp_npos_elections::Error) -> Self { + FeasibilityError::NposElection(e) + } +} + +pub use pallet::*; +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config + SendTransactionTypes> + where + ExtendedBalance: From>>, + ExtendedBalance: From>>, + { + type Event: From> + + Into<::Event> + + IsType<::Event>; + + /// Currency type. + type Currency: ReservableCurrency + Currency; + + /// Duration of the unsigned phase. + #[pallet::constant] + type UnsignedPhase: Get; + /// Duration of the signed phase. + #[pallet::constant] + type SignedPhase: Get; + + /// The minimum amount of improvement to the solution score that defines a solution as + /// "better". + #[pallet::constant] + type SolutionImprovementThreshold: Get; + + /// The priority of the unsigned transaction submitted in the unsigned-phase + type UnsignedPriority: Get; + /// Maximum number of iteration of balancing that will be executed in the embedded miner of + /// the pallet. + type MinerMaxIterations: Get; + /// Maximum weight that the miner should consume. + /// + /// The miner will ensure that the total weight of the unsigned solution will not exceed + /// this values, based on [`WeightInfo::submit_unsigned`]. + type MinerMaxWeight: Get; + + /// Something that will provide the election data. + type DataProvider: ElectionDataProvider; + + /// The compact solution type + type CompactSolution: codec::Codec + + Default + + PartialEq + + Eq + + Clone + + sp_std::fmt::Debug + + CompactSolution; + + /// Accuracy used for fallback on-chain election. + type OnChainAccuracy: PerThing128; + + /// Configuration for the fallback + type Fallback: Get; + + /// The configuration of benchmarking. + type BenchmarkingConfig: BenchmarkingConfig; + + /// The weight of the pallet. + type WeightInfo: WeightInfo; + } + + #[pallet::hooks] + impl Hooks> for Pallet + where + ExtendedBalance: From>>, + ExtendedBalance: From>>, + { + fn on_initialize(now: T::BlockNumber) -> Weight { + let next_election = T::DataProvider::next_election_prediction(now).max(now); + + let signed_deadline = T::SignedPhase::get() + T::UnsignedPhase::get(); + let unsigned_deadline = T::UnsignedPhase::get(); + + let remaining = next_election - now; + let current_phase = Self::current_phase(); + + match current_phase { + Phase::Off if remaining <= signed_deadline && remaining > unsigned_deadline => { + Self::on_initialize_open_signed(); + log!(info, "Starting signed phase at #{:?} , round {}.", now, Self::round()); + T::WeightInfo::on_initialize_open_signed() + } + Phase::Signed | Phase::Off + if remaining <= unsigned_deadline && remaining > 0u32.into() => + { + let (need_snapshot, enabled, additional) = if current_phase == Phase::Signed { + // followed by a signed phase: close the signed phase, no need for snapshot. + // TODO + (false, true, Weight::zero()) + } else { + // no signed phase + (true, true, Weight::zero()) + }; + + Self::on_initialize_open_unsigned(need_snapshot, enabled, now); + log!(info, "Starting unsigned phase({}) at #{:?}.", enabled, now); + let base_weight = if need_snapshot { + T::WeightInfo::on_initialize_open_unsigned_with_snapshot() } + else { + T::WeightInfo::on_initialize_open_unsigned_without_snapshot() + }; + base_weight.saturating_add(additional) + } + _ => T::WeightInfo::on_initialize_nothing(), + } + } + + fn offchain_worker(n: T::BlockNumber) { + // We only run the OCW in the fist block of the unsigned phase. + if Self::current_phase().is_unsigned_open_at(n) { + match Self::set_check_offchain_execution_status(n) { + Ok(_) => match Self::mine_and_submit() { + Ok(_) => { + log!(info, "successfully submitted a solution via OCW at block {:?}", n) + } + Err(e) => log!(error, "error while submitting transaction in OCW: {:?}", e), + }, + Err(why) => log!(error, "Error in unsigned offchain worker: {:?}", why), + } + } + } + + fn integrity_test() { + 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::>() <= size_of::()); + assert!(size_of::>() <= size_of::()); + + // ---------------------------- + // based on the requirements of [`sp_npos_elections::Assignment::try_normalize`]. + let max_vote: usize = as CompactSolution>::LIMIT; + + // 1. Maximum sum of [ChainAccuracy; 16] must fit into `UpperOf`.. + let maximum_chain_accuracy: Vec>> = + (0..max_vote).map(|_| >::one().deconstruct().into()).collect(); + let _: UpperOf> = maximum_chain_accuracy + .iter() + .fold(Zero::zero(), |acc, x| acc.checked_add(x).unwrap()); + + // 2. Maximum sum of [CompactAccuracy; 16] must fit into `UpperOf`. + let maximum_chain_accuracy: Vec>> = + (0..max_vote).map(|_| >::one().deconstruct().into()).collect(); + let _: UpperOf> = maximum_chain_accuracy + .iter() + .fold(Zero::zero(), |acc, x| acc.checked_add(x).unwrap()); + } + } + + #[pallet::call] + impl Pallet + where + ExtendedBalance: From>>, + ExtendedBalance: From>>, + { + /// Submit a solution for the unsigned phase. + /// + /// The dispatch origin fo this call must be __none__. + /// + /// This submission is checked on the fly, thus it is likely yo be more limited and smaller. + /// Moreover, this unsigned solution is only validated when submitted to the pool from the + /// local process. Effectively, this means that only active validators can submit this + /// transaction when authoring a block. + /// + /// To prevent any incorrect solution (and thus wasted time/weight), this transaction will + /// panic if the solution submitted by the validator is invalid, effectively putting their + /// authoring reward at risk. + /// + /// No deposit or reward is associated with this. + #[pallet::weight(T::WeightInfo::submit_unsigned( + witness.voters, + witness.targets, + solution.compact.voter_count() as u32, + solution.compact.unique_targets().len() as u32 + ))] + pub fn submit_unsigned( + origin: OriginFor, + solution: RawSolution>, + witness: SolutionSize, + ) -> DispatchResultWithPostInfo { + ensure_none(origin)?; + let error_message = "Invalid unsigned submission must produce invalid block and \ + deprive validator from their authoring reward."; + + // check phase and score. + // NOTE: since we do this in pre-dispatch, we can just ignore it here. + Self::unsigned_pre_dispatch_checks(&solution).expect(error_message); + + // ensure witness was correct. + let RoundSnapshotMetadata { voters_len, targets_len } = + Self::snapshot_metadata().expect(error_message); + + // NOTE: we are asserting, not `ensure`ing -- we want to panic here. + assert!(voters_len as u32 == witness.voters, error_message); + assert!(targets_len as u32 == witness.targets, error_message); + + let ready = + Self::feasibility_check(solution, ElectionCompute::Unsigned).expect(error_message); + + // store the newly received solution. + log!(info, "queued unsigned solution with score {:?}", ready.score); + >::put(ready); + Self::deposit_event(Event::SolutionStored(ElectionCompute::Unsigned)); + + Ok(None.into()) + } + } + + #[pallet::event] + #[pallet::metadata(::AccountId = "AccountId")] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event + where + ExtendedBalance: From>>, + ExtendedBalance: From>>, + { + /// A solution was stored with the given compute. + /// + /// If the solution is signed, this means that it hasn't yet been processed. If the + /// solution is unsigned, this means that it has also been processed. + SolutionStored(ElectionCompute), + /// The election has been finalized, with `Some` of the given computation, or else if the + /// election failed, `None`. + ElectionFinalized(Option), + /// An account has been rewarded for their signed submission being finalized. + Rewarded(::AccountId), + /// An account has been slashed for submitting an invalid signed submission. + Slashed(::AccountId), + /// The signed phase of the given round has started. + SignedPhaseStarted(u32), + /// The unsigned phase of the given round has started. + UnsignedPhaseStarted(u32), + } + + #[pallet::error] + pub enum Error { + /// Submission was too early. + EarlySubmission, + /// Wrong number of winners presented. + WrongWinnerCount, + /// Submission was too weak, score-wise. + WeakSubmission, + /// The queue was full, and the solution was not better than any of the existing ones. + QueueFull, + /// The origin failed to pay the deposit. + CannotPayDeposit, + /// witness data to dispatchable is invalid. + InvalidWitness, + /// The signed submission consumes too much weight + TooMuchWeight, + } + + #[pallet::origin] + pub struct Origin(PhantomData); + + #[pallet::validate_unsigned] + impl ValidateUnsigned for Pallet + where + ExtendedBalance: From>>, + ExtendedBalance: From>>, + { + type Call = Call; + fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity { + if let Call::submit_unsigned(solution, _) = call { + // discard solution not coming from the local OCW. + match source { + TransactionSource::Local | TransactionSource::InBlock => { /* allowed */ } + _ => { + return InvalidTransaction::Call.into(); + } + } + + let _ = Self::unsigned_pre_dispatch_checks(solution) + .map_err(|err| { + log!(error, "unsigned transaction validation failed due to {:?}", err); + err + }) + .map_err(dispatch_error_to_invalid)?; + + ValidTransaction::with_tag_prefix("OffchainElection") + // The higher the score[0], the better a solution is. + .priority( + T::UnsignedPriority::get() + .saturating_add(solution.score[0].saturated_into()), + ) + // used to deduplicate unsigned solutions: each validator should produce one + // solution per round at most, and solutions are not propagate. + .and_provides(solution.round) + // transaction should stay in the pool for the duration of the unsigned phase. + .longevity(T::UnsignedPhase::get().saturated_into::()) + // We don't propagate this. This can never the validated at a remote node. + .propagate(false) + .build() + } else { + InvalidTransaction::Call.into() + } + } + + fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> { + if let Call::submit_unsigned(solution, _) = call { + Self::unsigned_pre_dispatch_checks(solution) + .map_err(dispatch_error_to_invalid) + .map_err(Into::into) + } else { + Err(InvalidTransaction::Call.into()) + } + } + } + + #[pallet::type_value] + pub fn DefaultForRound() -> u32 { + 1 + } + + /// Internal counter for the number of rounds. + /// + /// This is useful for de-duplication of transactions submitted to the pool, and general + /// diagnostics of the module. + /// + /// This is merely incremented once per every time that an upstream `elect` is called. + #[pallet::storage] + #[pallet::getter(fn round)] + pub type Round = StorageValue<_, u32, ValueQuery, DefaultForRound>; + + /// Current phase. + #[pallet::storage] + #[pallet::getter(fn current_phase)] + pub type CurrentPhase = StorageValue<_, Phase, ValueQuery>; + + /// Current best solution, signed or unsigned. + #[pallet::storage] + #[pallet::getter(fn queued_solution)] + pub type QueuedSolution = StorageValue<_, ReadySolution>; + + /// Snapshot data of the round. + /// + /// This is created at the beginning of the signed phase and cleared upon calling `elect`. + #[pallet::storage] + #[pallet::getter(fn snapshot)] + pub type Snapshot = StorageValue<_, RoundSnapshot>; + + /// Desired number of targets to elect for this round. + /// + /// Only exists when [`Snapshot`] is present. + #[pallet::storage] + #[pallet::getter(fn desired_targets)] + pub type DesiredTargets = StorageValue<_, u32>; + + /// The metadata of the [`RoundSnapshot`] + /// + /// Only exists when [`Snapshot`] is present. + #[pallet::storage] + #[pallet::getter(fn snapshot_metadata)] + pub type SnapshotMetadata = StorageValue<_, RoundSnapshotMetadata>; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData); +} + +impl Pallet +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + /// Logic for `::on_initialize` when signed phase is being opened. + /// + /// This is decoupled for easy weight calculation. + pub fn on_initialize_open_signed() { + >::put(Phase::Signed); + Self::create_snapshot(); + Self::deposit_event(Event::SignedPhaseStarted(Self::round())); + } + + /// Logic for `>::on_initialize` when unsigned phase is being opened. + /// + /// This is decoupled for easy weight calculation. Note that the default weight benchmark of + /// this function will assume an empty signed queue for `finalize_signed_phase`. + pub fn on_initialize_open_unsigned( + need_snapshot: bool, + enabled: bool, + now: T::BlockNumber, + ) { + if need_snapshot { + // if not being followed by a signed phase, then create the snapshots. + debug_assert!(Self::snapshot().is_none()); + Self::create_snapshot(); + } + + // for now always start the unsigned phase. + >::put(Phase::Unsigned((enabled, now))); + Self::deposit_event(Event::UnsignedPhaseStarted(Self::round())); + } + + /// Creates the snapshot. Writes new data to: + /// + /// 1. [`SnapshotMetadata`] + /// 2. [`RoundSnapshot`] + /// 3. [`DesiredTargets`] + pub fn create_snapshot() { + // if any of them don't exist, create all of them. This is a bit conservative. + let targets = T::DataProvider::targets(); + let voters = T::DataProvider::voters(); + let desired_targets = T::DataProvider::desired_targets(); + + >::put(RoundSnapshotMetadata { + voters_len: voters.len() as u32, + targets_len: targets.len() as u32, + }); + >::put(desired_targets); + >::put(RoundSnapshot { voters, targets }); + } + + /// Checks the feasibility of a solution. + /// + /// This checks the solution for the following: + /// + /// 0. **all** of the used indices must be correct. + /// 1. present correct number of winners. + /// 2. any assignment is checked to match with [Snapshot::voters]. + /// 3. for each assignment, the check of `ElectionDataProvider` is also examined. + /// 4. the claimed score is valid. + fn feasibility_check( + solution: RawSolution>, + compute: ElectionCompute, + ) -> Result, FeasibilityError> { + let RawSolution { compact, score, round } = solution; + + // first, check round. + ensure!(Self::round() == round, FeasibilityError::InvalidRound); + + // winners are not directly encoded in the solution. + let winners = compact.unique_targets(); + + let desired_targets = + Self::desired_targets().ok_or(FeasibilityError::SnapshotUnavailable)?; + + // NOTE: this is a bit of duplicate, but we keep it around for veracity. The unsigned path + // already checked this in `unsigned_per_dispatch_checks`. The signed path *could* check it + // upon arrival. + ensure!(winners.len() as u32 == desired_targets, FeasibilityError::WrongWinnerCount,); + + // read the entire snapshot. + let RoundSnapshot { voters: snapshot_voters, targets: snapshot_targets } = + Self::snapshot().ok_or(FeasibilityError::SnapshotUnavailable)?; + + // ----- Start building. First, we need some closures. + let cache = helpers::generate_voter_cache::(&snapshot_voters); + let voter_at = helpers::voter_at_fn::(&snapshot_voters); + let target_at = helpers::target_at_fn::(&snapshot_targets); + let voter_index = helpers::voter_index_fn_usize::(&cache); + + // first, make sure that all the winners are sane. + let winners = winners + .into_iter() + .map(|i| target_at(i).ok_or(FeasibilityError::InvalidWinner)) + .collect::, FeasibilityError>>()?; + + // Then convert compact -> Assignment. This will fail if any of the indices are gibberish. + // that winner indices are already checked. + let assignments = compact + .into_assignment(voter_at, target_at) + .map_err::(Into::into)?; + + // Ensure that assignments is correct. + let _ = assignments + .iter() + .map(|ref assignment| { + // check that assignment.who is actually a voter (defensive-only). + // NOTE: while using the index map from `voter_index` is better than a blind linear + // search, this *still* has room for optimization. Note that we had the index when + // we did `compact -> assignment` and we lost it. Ideal is to keep the index around. + + // defensive-only: must exist in the snapshot. + let snapshot_index = + voter_index(&assignment.who).ok_or(FeasibilityError::InvalidVoter)?; + // defensive-only: index comes from the snapshot, must exist. + let (_voter, _stake, targets) = + snapshot_voters.get(snapshot_index).ok_or(FeasibilityError::InvalidVoter)?; + + // check that all of the targets are valid based on the snapshot. + if assignment.distribution.iter().any(|(d, _)| !targets.contains(d)) { + return Err(FeasibilityError::InvalidVote); + } + Ok(()) + }) + .collect::>()?; + + // ----- Start building support. First, we need one more closure. + let stake_of = helpers::stake_of_fn::(&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) + .map_err::(Into::into)?; + // This might fail if one of the voter edges is pointing to a non-winner, which is not + // really possible anymore because all the winners come from the same `compact`. + let supports = sp_npos_elections::to_supports(&winners, &staked_assignments) + .map_err::(Into::into)?; + + // Finally, check that the claimed score was indeed correct. + let known_score = (&supports).evaluate(); + ensure!(known_score == score, FeasibilityError::InvalidScore); + + Ok(ReadySolution { supports, compute, score }) + } + + /// Perform the tasks to be done after a new `elect` has been triggered: + /// + /// 1. Increment round. + /// 2. Change phase to [`Phase::Off`] + /// 3. Clear all snapshot data. + fn post_elect() { + // inc round + >::mutate(|r| *r = *r + 1); + + // change phase + >::put(Phase::Off); + + // kill snapshots + >::kill(); + >::kill(); + >::kill(); + } + + /// On-chain fallback of election. + fn onchain_fallback() -> Result, ElectionError> + where + ExtendedBalance: From<::Inner>, + { + > as ElectionProvider< + T::AccountId, + T::BlockNumber, + >>::elect() + .map_err(Into::into) + } + + fn do_elect() -> Result, ElectionError> { + // NOTE: SignedSubmission is guaranteed to be drained by the end of the signed phase too, + // thus no need for a manual cleanup: + // TODO + // debug_assert!(Self::signed_submissions().is_empty()); + >::take() + .map_or_else( + || match T::Fallback::get() { + FallbackStrategy::OnChain => Self::onchain_fallback() + .map(|r| (r, ElectionCompute::OnChain)) + .map_err(Into::into), + FallbackStrategy::Nothing => Err(ElectionError::NoFallbackConfigured), + }, + |ReadySolution { supports, compute, .. }| Ok((supports, compute)), + ) + .map(|(supports, compute)| { + Self::deposit_event(Event::ElectionFinalized(Some(compute))); + log!(info, "Finalized election round with compute {:?}.", compute); + supports + }) + .map_err(|err| { + Self::deposit_event(Event::ElectionFinalized(None)); + log!(warn, "Failed to finalize election round. reason {:?}", err); + err + }) + } +} + +impl ElectionProvider for Pallet +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + type Error = ElectionError; + type DataProvider = T::DataProvider; + + fn elect() -> Result, Self::Error> { + let outcome = Self::do_elect(); + // cleanup. + Self::post_elect(); + outcome + } +} + +/// convert a DispatchError to a custom InvalidTransaction with the inner code being the error +/// number. +pub fn dispatch_error_to_invalid(error: DispatchError) -> InvalidTransaction { + let error_number = match error { + DispatchError::Module { error, .. } => error, + _ => 0, + }; + InvalidTransaction::Custom(error_number) +} + +#[cfg(test)] +mod feasibility_check { + //! All of the tests here should be dedicated to only testing the feasibility check and nothing + //! more. The best way to audit and review these tests is to try and come up with a solution + //! that is invalid, but gets through the system as valid. + + use super::{mock::*, *}; + + const COMPUTE: ElectionCompute = ElectionCompute::OnChain; + + #[test] + fn snapshot_is_there() { + ExtBuilder::default().build_and_execute(|| { + roll_to(::get() - ::get() - ::get()); + assert!(TwoPhase::current_phase().is_signed()); + let solution = raw_solution(); + + // for whatever reason it might be: + >::kill(); + + assert_noop!( + TwoPhase::feasibility_check(solution, COMPUTE), + FeasibilityError::SnapshotUnavailable + ); + }) + } + + #[test] + fn round() { + ExtBuilder::default().build_and_execute(|| { + roll_to(::get() - ::get() - ::get()); + assert!(TwoPhase::current_phase().is_signed()); + + let mut solution = raw_solution(); + solution.round += 1; + assert_noop!( + TwoPhase::feasibility_check(solution, COMPUTE), + FeasibilityError::InvalidRound + ); + }) + } + + #[test] + fn desired_targets() { + ExtBuilder::default().desired_targets(8).build_and_execute(|| { + roll_to(::get() - ::get() - ::get()); + assert!(TwoPhase::current_phase().is_signed()); + + let solution = raw_solution(); + + assert_eq!(solution.compact.unique_targets().len(), 4); + assert_eq!(TwoPhase::desired_targets().unwrap(), 8); + + assert_noop!( + TwoPhase::feasibility_check(solution, COMPUTE), + FeasibilityError::WrongWinnerCount + ); + }) + } + + #[test] + fn winner_indices() { + ExtBuilder::default().desired_targets(2).build_and_execute(|| { + roll_to(::get() - ::get() - ::get()); + assert!(TwoPhase::current_phase().is_signed()); + + let mut solution = raw_solution(); + assert_eq!(TwoPhase::snapshot().unwrap().targets.len(), 4); + // ----------------------------------------------------^^ valid range is [0..3]. + + // swap all votes from 3 to 4. This will ensure that the number of unique winners + // will still be 4, but one of the indices will be gibberish. Requirement is to make + // sure 3 a winner, which we don't do here. + solution + .compact + .votes1 + .iter_mut() + .filter(|(_, t)| *t == 3u16) + .for_each(|(_, t)| *t += 1); + solution.compact.votes2.iter_mut().for_each(|(_, (t0, _), t1)| { + if *t0 == 3u16 { + *t0 += 1 + }; + if *t1 == 3u16 { + *t1 += 1 + }; + }); + assert_noop!( + TwoPhase::feasibility_check(solution, COMPUTE), + FeasibilityError::InvalidWinner + ); + }) + } + + #[test] + fn voter_indices() { + // should be caught in `compact.into_assignment`. + ExtBuilder::default().desired_targets(2).build_and_execute(|| { + roll_to(::get() - ::get() - ::get()); + assert!(TwoPhase::current_phase().is_signed()); + + let mut solution = raw_solution(); + assert_eq!(TwoPhase::snapshot().unwrap().voters.len(), 8); + // ----------------------------------------------------^^ valid range is [0..7]. + + // check that there is a index 7 in votes1, and flip to 8. + assert!( + solution + .compact + .votes1 + .iter_mut() + .filter(|(v, _)| *v == 7u32) + .map(|(v, _)| *v = 8) + .count() > 0 + ); + assert_noop!( + TwoPhase::feasibility_check(solution, COMPUTE), + FeasibilityError::NposElection(sp_npos_elections::Error::CompactInvalidIndex), + ); + }) + } + + #[test] + fn voter_votes() { + ExtBuilder::default().desired_targets(2).build_and_execute(|| { + roll_to(::get() - ::get() - ::get()); + assert!(TwoPhase::current_phase().is_signed()); + + let mut solution = raw_solution(); + assert_eq!(TwoPhase::snapshot().unwrap().voters.len(), 8); + // ----------------------------------------------------^^ valid range is [0..7]. + + // first, check that voter at index 7 (40) actually voted for 3 (40) -- this is self + // vote. Then, change the vote to 2 (30). + assert_eq!( + solution + .compact + .votes1 + .iter_mut() + .filter(|(v, t)| *v == 7 && *t == 3) + .map(|(_, t)| *t = 2) + .count(), + 1, + ); + assert_noop!( + TwoPhase::feasibility_check(solution, COMPUTE), + FeasibilityError::InvalidVote, + ); + }) + } + + #[test] + fn score() { + ExtBuilder::default().desired_targets(2).build_and_execute(|| { + roll_to(::get() - ::get() - ::get()); + assert!(TwoPhase::current_phase().is_signed()); + + let mut solution = raw_solution(); + assert_eq!(TwoPhase::snapshot().unwrap().voters.len(), 8); + + // simply faff with the score. + solution.score[0] += 1; + + assert_noop!( + TwoPhase::feasibility_check(solution, COMPUTE), + FeasibilityError::InvalidScore, + ); + }) + } +} + +#[cfg(test)] +mod tests { + use super::{mock::*, Event, *}; + use sp_election_providers::ElectionProvider; + use sp_npos_elections::Support; + + #[test] + fn phase_rotation_works() { + ExtBuilder::default().build_and_execute(|| { + // 0 ------- 15 ------- 25 ------- 30 ------- ------- 45 ------- 55 ------- 60 + // | | | | + // Signed Unsigned Signed Unsigned + + assert_eq!(System::block_number(), 0); + assert_eq!(TwoPhase::current_phase(), Phase::Off); + assert_eq!(TwoPhase::round(), 1); + + roll_to(4); + assert_eq!(TwoPhase::current_phase(), Phase::Off); + assert!(TwoPhase::snapshot().is_none()); + assert_eq!(TwoPhase::round(), 1); + + roll_to(15); + assert_eq!(TwoPhase::current_phase(), Phase::Signed); + assert_eq!(two_phase_events(), vec![Event::SignedPhaseStarted(1)]); + assert!(TwoPhase::snapshot().is_some()); + assert_eq!(TwoPhase::round(), 1); + + roll_to(24); + assert_eq!(TwoPhase::current_phase(), Phase::Signed); + assert!(TwoPhase::snapshot().is_some()); + assert_eq!(TwoPhase::round(), 1); + + roll_to(25); + assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!( + two_phase_events(), + vec![Event::SignedPhaseStarted(1), Event::UnsignedPhaseStarted(1)], + ); + assert!(TwoPhase::snapshot().is_some()); + + roll_to(29); + assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + assert!(TwoPhase::snapshot().is_some()); + + roll_to(30); + assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + assert!(TwoPhase::snapshot().is_some()); + + // we close when upstream tells us to elect. + roll_to(32); + assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + assert!(TwoPhase::snapshot().is_some()); + + TwoPhase::elect().unwrap(); + + assert!(TwoPhase::current_phase().is_off()); + assert!(TwoPhase::snapshot().is_none()); + assert_eq!(TwoPhase::round(), 2); + + roll_to(44); + assert!(TwoPhase::current_phase().is_off()); + + roll_to(45); + assert!(TwoPhase::current_phase().is_signed()); + + roll_to(55); + assert!(TwoPhase::current_phase().is_unsigned_open_at(55)); + }) + } + + #[test] + fn signed_phase_void() { + ExtBuilder::default().phases(0, 10).build_and_execute(|| { + roll_to(15); + assert!(TwoPhase::current_phase().is_off()); + + roll_to(19); + assert!(TwoPhase::current_phase().is_off()); + + roll_to(20); + assert!(TwoPhase::current_phase().is_unsigned_open_at(20)); + assert!(TwoPhase::snapshot().is_some()); + + roll_to(30); + assert!(TwoPhase::current_phase().is_unsigned_open_at(20)); + + TwoPhase::elect().unwrap(); + + assert!(TwoPhase::current_phase().is_off()); + assert!(TwoPhase::snapshot().is_none()); + }); + } + + #[test] + fn unsigned_phase_void() { + ExtBuilder::default().phases(10, 0).build_and_execute(|| { + roll_to(15); + assert!(TwoPhase::current_phase().is_off()); + + roll_to(19); + assert!(TwoPhase::current_phase().is_off()); + + roll_to(20); + assert!(TwoPhase::current_phase().is_signed()); + assert!(TwoPhase::snapshot().is_some()); + + roll_to(30); + assert!(TwoPhase::current_phase().is_signed()); + + let _ = TwoPhase::elect().unwrap(); + + assert!(TwoPhase::current_phase().is_off()); + assert!(TwoPhase::snapshot().is_none()); + }); + } + + #[test] + fn both_phases_void() { + ExtBuilder::default().phases(0, 0).build_and_execute(|| { + roll_to(15); + assert!(TwoPhase::current_phase().is_off()); + + roll_to(19); + assert!(TwoPhase::current_phase().is_off()); + + roll_to(20); + assert!(TwoPhase::current_phase().is_off()); + + roll_to(30); + assert!(TwoPhase::current_phase().is_off()); + + // this module is now only capable of doing on-chain backup. + let _ = TwoPhase::elect().unwrap(); + + assert!(TwoPhase::current_phase().is_off()); + }); + } + + #[test] + fn early_termination() { + // an early termination in the signed phase, with no queued solution. + ExtBuilder::default().build_and_execute(|| { + // signed phase started at block 15 and will end at 25. + roll_to(14); + assert_eq!(TwoPhase::current_phase(), Phase::Off); + + roll_to(15); + assert_eq!(two_phase_events(), vec![Event::SignedPhaseStarted(1)]); + assert_eq!(TwoPhase::current_phase(), Phase::Signed); + assert_eq!(TwoPhase::round(), 1); + + // an unexpected call to elect. + roll_to(20); + TwoPhase::elect().unwrap(); + + // we surely can't have any feasible solutions. This will cause an on-chain election. + assert_eq!( + two_phase_events(), + vec![ + Event::SignedPhaseStarted(1), + Event::ElectionFinalized(Some(ElectionCompute::OnChain)) + ], + ); + // all storage items must be cleared. + assert_eq!(TwoPhase::round(), 2); + assert!(TwoPhase::snapshot().is_none()); + assert!(TwoPhase::snapshot_metadata().is_none()); + assert!(TwoPhase::desired_targets().is_none()); + assert!(TwoPhase::queued_solution().is_none()); + }) + } + + #[test] + fn fallback_strategy_works() { + ExtBuilder::default().fallabck(FallbackStrategy::OnChain).build_and_execute(|| { + roll_to(15); + assert_eq!(TwoPhase::current_phase(), Phase::Signed); + + roll_to(25); + assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + + // zilch solutions thus far. + let supports = TwoPhase::elect().unwrap(); + + assert_eq!( + supports, + vec![ + (30, Support { total: 40, voters: vec![(2, 5), (4, 5), (30, 30)] }), + (40, Support { total: 60, voters: vec![(2, 5), (3, 10), (4, 5), (40, 40)] }) + ] + ) + }); + + ExtBuilder::default().fallabck(FallbackStrategy::Nothing).build_and_execute(|| { + roll_to(15); + assert_eq!(TwoPhase::current_phase(), Phase::Signed); + + roll_to(25); + assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + + // zilch solutions thus far. + assert_eq!(TwoPhase::elect().unwrap_err(), ElectionError::NoFallbackConfigured); + }) + } + + #[test] + fn number_of_voters_allowed_2sec_block() { + // Just a rough estimate with the substrate weights. + assert!(!MockWeightInfo::get()); + + let all_voters: u32 = 100_000; + let all_targets: u32 = 2_000; + let desired: u32 = 1_000; + let weight_with = |active| { + ::WeightInfo::submit_unsigned( + all_voters, + all_targets, + active, + desired, + ) + }; + + let mut active = 1; + while weight_with(active) + <= ::BlockWeights::get().max_block + { + active += 1; + } + + println!("can support {} voters to yield a weight of {}", active, weight_with(active)); + } +} diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs new file mode 100644 index 0000000000000..71102f3e6e526 --- /dev/null +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -0,0 +1,386 @@ +// 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 two_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, Block as BlockT, IdentityLookup}, + PerU16, +}; +use std::sync::Arc; + +pub type Block = sp_runtime::generic::Block; +pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; + +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Module, Call, Event}, + Balances: pallet_balances::{Module, Call, Event, Config}, + TwoPhase: two_phase::{Module, Call, Event}, + } +); + +pub(crate) type Balance = u64; +pub(crate) type AccountId = u64; + +sp_npos_elections::generate_solution_type!( + #[compact] + pub struct TestCompact::(16) +); + +/// All events of this pallet. +pub(crate) fn two_phase_events() -> Vec> { + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| if let Event::two_phase(inner) = e { Some(inner) } else { None }) + .collect::>() +} + +/// 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); + TwoPhase::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); + TwoPhase::on_initialize(i); + TwoPhase::offchain_worker(i); + } +} + +/// Get the free and reserved balance of some account. +pub fn balances(who: &AccountId) -> (Balance, Balance) { + (Balances::free_balance(who), Balances::reserved_balance(who)) +} + +/// Spit out a verifiable raw solution. +/// +/// This is a good example of what an offchain miner would do. +pub fn raw_solution() -> RawSolution> { + let RoundSnapshot { voters, targets } = TwoPhase::snapshot().unwrap(); + let desired_targets = TwoPhase::desired_targets().unwrap(); + + // closures + let cache = helpers::generate_voter_cache::(&voters); + let voter_index = helpers::voter_index_fn_linear::(&voters); + let target_index = helpers::target_index_fn_linear::(&targets); + let stake_of = helpers::stake_of_fn::(&voters, &cache); + + let ElectionResult { winners, assignments } = seq_phragmen::<_, CompactAccuracyOf>( + 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 = + >::from_assignment(assignments, &voter_index, &target_index).unwrap(); + + let round = TwoPhase::round(); + RawSolution { compact, score, round } +} + +pub fn witness() -> SolutionSize { + TwoPhase::snapshot() + .map(|snap| SolutionSize { + 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; + type Header = Header; + type Event = Event; + type BlockHashCount = (); + type DbWeight = (); + type BlockLength = (); + type BlockWeights = BlockWeights; + type Version = (); + type PalletInfo = (); + type AccountData = pallet_balances::AccountData; + 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 = vec![10, 20, 30, 40]; + pub static Voters: Vec<(AccountId, VoteWeight, Vec)> = 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 UnsignedPriority: 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 two_phase::weights::WeightInfo for DualMockWeightInfo { + fn on_initialize_nothing() -> Weight { + if MockWeightInfo::get() { + Zero::zero() + } else { + <() as two_phase::weights::WeightInfo>::on_initialize_nothing() + } + } + fn on_initialize_open_signed() -> Weight { + if MockWeightInfo::get() { + Zero::zero() + } else { + <() as two_phase::weights::WeightInfo>::on_initialize_open_signed() + } + } + fn on_initialize_open_unsigned_with_snapshot() -> Weight { + if MockWeightInfo::get() { + Zero::zero() + } else { + <() as two_phase::weights::WeightInfo>::on_initialize_open_unsigned_with_snapshot() + } + } + fn on_initialize_open_unsigned_without_snapshot() -> Weight { + if MockWeightInfo::get() { + Zero::zero() + } else { + <() as two_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 two_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 two_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 UnsignedPriority = UnsignedPriority; + type DataProvider = StakingMock; + type WeightInfo = DualMockWeightInfo; + type BenchmarkingConfig = (); + type OnChainAccuracy = Perbill; + type Fallback = Fallback; + type CompactSolution = TestCompact; +} + +impl frame_system::offchain::SendTransactionTypes for Runtime +where + Call: From, +{ + type OverarchingCall = Call; + type Extrinsic = Extrinsic; +} + +pub type Extrinsic = sp_runtime::testing::TestXt; + +#[derive(Default)] +pub struct ExtBuilder {} + +pub struct StakingMock; +impl ElectionDataProvider for StakingMock { + fn targets() -> Vec { + Targets::get() + } + fn voters() -> Vec<(AccountId, VoteWeight, Vec)> { + 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 unsigned_priority(self, p: u64) -> Self { + ::set(p); + self + } + pub fn solution_improvement_threshold(self, p: Perbill) -> Self { + ::set(p); + self + } + pub fn phases(self, signed: u64, unsigned: u64) -> Self { + ::set(signed); + ::set(unsigned); + self + } + pub fn fallabck(self, fallback: FallbackStrategy) -> Self { + ::set(fallback); + self + } + pub fn miner_weight(self, weight: Weight) -> Self { + ::set(weight); + self + } + pub fn mock_weight_info(self, mock: bool) -> Self { + ::set(mock); + self + } + pub fn desired_targets(self, t: u32) -> Self { + ::set(t); + self + } + pub fn add_voter(self, who: AccountId, stake: Balance, targets: Vec) -> 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::().unwrap(); + + let _ = pallet_balances::GenesisConfig:: { + 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>) { + 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) + } +} diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs new file mode 100644 index 0000000000000..e922dc32dfa34 --- /dev/null +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -0,0 +1,799 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 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 unsigned phase implementation. + +use crate::*; +use frame_support::dispatch::DispatchResult; +use frame_system::offchain::SubmitTransaction; +use sp_npos_elections::{seq_phragmen, CompactSolution, ElectionResult}; +use sp_runtime::{offchain::storage::StorageValueRef, traits::TrailingZeroInput}; +use sp_std::cmp::Ordering; + +/// Storage key used to store the persistent offchain worker status. +pub(crate) const OFFCHAIN_HEAD_DB: &[u8] = b"parity/multi-phase-unsigned-election/"; +/// The repeat threshold of the offchain worker. This means we won't run the offchain worker twice +/// within a window of 5 blocks. +pub(crate) const OFFCHAIN_REPEAT: u32 = 5; + +impl Pallet +where + ExtendedBalance: From>>, + ExtendedBalance: From>>, +{ + /// Min a new npos solution. + pub fn mine_solution( + iters: usize, + ) -> Result<(RawSolution>, SolutionSize), ElectionError> { + let RoundSnapshot { voters, targets } = + Self::snapshot().ok_or(ElectionError::SnapshotUnAvailable)?; + let desired_targets = Self::desired_targets().ok_or(ElectionError::SnapshotUnAvailable)?; + + seq_phragmen::<_, CompactAccuracyOf>( + desired_targets as usize, + targets, + voters, + Some((iters, 0)), + ) + .map_err(Into::into) + .and_then(|election_result| { + if election_result.winners.len() as u32 == desired_targets { + Ok(election_result) + } else { + Err(ElectionError::Feasibility(FeasibilityError::WrongWinnerCount)) + } + }) + .and_then(Self::prepare_election_result) + } + + /// 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>, + ) -> Result<(RawSolution>, SolutionSize), ElectionError> { + // storage items. Note: we have already read this from storage, they must be in cache. + let RoundSnapshot { voters, targets } = + Self::snapshot().ok_or(ElectionError::SnapshotUnAvailable)?; + let desired_targets = Self::desired_targets().ok_or(ElectionError::SnapshotUnAvailable)?; + + // closures. + let cache = helpers::generate_voter_cache::(&voters); + let voter_index = helpers::voter_index_fn::(&cache); + let target_index = helpers::target_index_fn_linear::(&targets); + let voter_at = helpers::voter_at_fn::(&voters); + let target_at = helpers::target_at_fn::(&targets); + let stake_of = helpers::stake_of_fn::(&voters, &cache); + + let ElectionResult { assignments, winners } = election_result; + + // convert to staked and reduce. + let mut staked = + sp_npos_elections::assignment_ratio_to_staked_normalized(assignments, &stake_of) + .map_err::(Into::into)?; + sp_npos_elections::reduce(&mut staked); + + // convert back to ration and make compact. + let ratio = sp_npos_elections::assignment_staked_to_ratio_normalized(staked)?; + let compact = >::from_assignment(ratio, &voter_index, &target_index)?; + + let size = SolutionSize { voters: voters.len() as u32, targets: targets.len() as u32 }; + let maximum_allowed_voters = Self::maximum_voter_for_weight::( + desired_targets, + size, + T::MinerMaxWeight::get(), + ); + log!( + debug, + "miner: current compact solution voters = {}, maximum_allowed = {}", + compact.voter_count(), + maximum_allowed_voters, + ); + let compact = Self::trim_compact(maximum_allowed_voters, compact, &voter_index)?; + + // re-calc score. + let winners = sp_npos_elections::to_without_backing(winners); + let score = compact.clone().score(&winners, stake_of, voter_at, target_at)?; + + let round = Self::round(); + Ok((RawSolution { compact, 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 `T::MinerMaxIterations`. + pub fn get_balancing_iters() -> usize { + match T::MinerMaxIterations::get() { + 0 => 0, + max @ _ => { + let seed = sp_io::offchain::random_seed(); + let random = ::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 a 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. + /// `compact.len()`). Aside from this, the other components of the weight are invariant. The + /// number of winners shall not be changed (otherwise the solution is invalid) and the + /// `ElectionSize` is merely a representation of the total number of stakers. + /// + /// Thus, we reside to stripping away some voters. This means only changing the `compact` + /// struct. + /// + /// Note that the solution is already computed, and the winners are elected based on the merit + /// of the entire stake in the system. Nonetheless, some of the voters will be removed further + /// down the line. + /// + /// Indeed, the score must be computed **after** this step. If this step reduces the score too + /// much, then the solution will be discarded. + pub fn trim_compact( + maximum_allowed_voters: u32, + mut compact: CompactOf, + nominator_index: FN, + ) -> Result, ElectionError> + where + for<'r> FN: Fn(&'r T::AccountId) -> Option>, + { + match compact.voter_count().checked_sub(maximum_allowed_voters as usize) { + Some(to_remove) if to_remove > 0 => { + // grab all voters and sort them by least stake. + let RoundSnapshot { voters, .. } = + Self::snapshot().ok_or(ElectionError::SnapshotUnAvailable)?; + let mut voters_sorted = voters + .into_iter() + .map(|(who, stake, _)| (who.clone(), stake)) + .collect::>(); + voters_sorted.sort_by_key(|(_, y)| *y); + + // start removing from the least stake. Iterate until we know enough have been + // removed. + let mut removed = 0; + for (maybe_index, _stake) in + voters_sorted.iter().map(|(who, stake)| (nominator_index(&who), stake)) + { + let index = maybe_index.ok_or(ElectionError::SnapshotUnAvailable)?; + if compact.remove_voter(index) { + removed += 1 + } + + if removed >= to_remove { + break; + } + } + + Ok(compact) + } + _ => { + // nada, return as-is + Ok(compact) + } + } + } + + /// Find the maximum `len` that a compact can have in order to fit into the block weight. + /// + /// This only returns a value between zero and `size.nominators`. + pub fn maximum_voter_for_weight( + desired_winners: u32, + size: SolutionSize, + max_weight: Weight, + ) -> u32 { + if size.voters < 1 { + return size.voters; + } + + let max_voters = size.voters.max(1); + let mut voters = max_voters; + + // helper closures. + let weight_with = |active_voters: u32| -> Weight { + W::submit_unsigned(size.voters, size.targets, active_voters, desired_winners) + }; + + let next_voters = |current_weight: Weight, voters: u32, step: u32| -> Result { + match current_weight.cmp(&max_weight) { + Ordering::Less => { + let next_voters = voters.checked_add(step); + match next_voters { + Some(voters) if voters < max_voters => Ok(voters), + _ => Err(()), + } + } + Ordering::Greater => voters.checked_sub(step).ok_or(()), + Ordering::Equal => Ok(voters), + } + }; + + // First binary-search the right amount of voters + let mut step = voters / 2; + let mut current_weight = weight_with(voters); + while step > 0 { + match next_voters(current_weight, voters, step) { + // proceed with the binary search + Ok(next) if next != voters => { + voters = next; + } + // we are out of bounds, break out of the loop. + Err(()) => { + break; + } + // we found the right value - early exit the function. + Ok(next) => return next, + } + step = step / 2; + current_weight = weight_with(voters); + } + + // Time to finish. We might have reduced less than expected due to rounding error. Increase + // one last time if we have any room left, the reduce until we are sure we are below limit. + while voters + 1 <= max_voters && weight_with(voters + 1) < max_weight { + voters += 1; + } + while voters.checked_sub(1).is_some() && weight_with(voters) > max_weight { + voters -= 1; + } + + debug_assert!( + weight_with(voters.min(size.voters)) <= max_weight, + "weight_with({}) <= {}", + voters.min(size.voters), + max_weight, + ); + voters.min(size.voters) + } + + /// Checks if an execution of the offchain worker is permitted at the given block number, or + /// not. + /// + /// This essentially makes sure that we don't run on previous blocks in case of a re-org, and we + /// don't run twice within a window of length [`OFFCHAIN_REPEAT`]. + /// + /// Returns `Ok(())` if offchain worker should happen, `Err(reason)` otherwise. + pub(crate) fn set_check_offchain_execution_status( + now: T::BlockNumber, + ) -> Result<(), &'static str> { + let storage = StorageValueRef::persistent(&OFFCHAIN_HEAD_DB); + let threshold = T::BlockNumber::from(OFFCHAIN_REPEAT); + + let mutate_stat = + storage.mutate::<_, &'static str, _>(|maybe_head: Option>| { + match maybe_head { + Some(Some(head)) if now < head => Err("fork."), + Some(Some(head)) if now >= head && now <= head + threshold => { + Err("recently executed.") + } + Some(Some(head)) if now > head + threshold => { + // we can run again now. Write the new head. + Ok(now) + } + _ => { + // value doesn't exists. Probably this node just booted up. Write, and run + Ok(now) + } + } + }); + + match mutate_stat { + // all good + Ok(Ok(_)) => Ok(()), + // failed to write. + Ok(Err(_)) => Err("failed to write to offchain db."), + // fork etc. + Err(why) => Err(why), + } + } + + /// Mine a new solution, and submit it back to the chain as an unsigned transaction. + pub(crate) fn mine_and_submit() -> Result<(), ElectionError> { + let balancing = Self::get_balancing_iters(); + let (raw_solution, witness) = Self::mine_solution(balancing)?; + + // submit the raw solution to the pool. + let call = Call::submit_unsigned(raw_solution, witness).into(); + + SubmitTransaction::>::submit_unsigned_transaction(call) + .map_err(|_| ElectionError::PoolSubmissionFailed) + } + + pub(crate) fn unsigned_pre_dispatch_checks( + solution: &RawSolution>, + ) -> DispatchResult { + // ensure solution is timely. Don't panic yet. This is a cheap check. + ensure!(Self::current_phase().is_unsigned_open(), Error::::EarlySubmission); + + // ensure correct number of winners. + ensure!( + Self::desired_targets().unwrap_or_default() + == solution.compact.unique_targets().len() as u32, + Error::::WrongWinnerCount, + ); + + // ensure score is being improved. Panic henceforth. + ensure!( + Self::queued_solution().map_or(true, |q: ReadySolution<_>| is_score_better::( + solution.score, + q.score, + T::SolutionImprovementThreshold::get() + )), + Error::::WeakSubmission + ); + + Ok(()) + } +} + +#[cfg(test)] +mod max_weight { + #![allow(unused_variables)] + use super::{mock::*, *}; + + struct TestWeight; + impl crate::weights::WeightInfo for TestWeight { + fn on_initialize_nothing() -> Weight { + unreachable!() + } + fn on_initialize_open_signed() -> Weight { + unreachable!() + } + fn on_initialize_open_unsigned_with_snapshot() -> Weight { + unreachable!() + } + fn on_initialize_open_unsigned_without_snapshot() -> Weight { + unreachable!() + } + fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight { + (0 * v + 0 * t + 1000 * a + 0 * d) as Weight + } + fn feasibility_check(v: u32, _t: u32, a: u32, d: u32) -> Weight { + unreachable!() + } + } + + #[test] + fn find_max_voter_binary_search_works() { + let w = SolutionSize { voters: 10, targets: 0 }; + + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 0), 0); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1), 0); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 999), 0); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1000), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1001), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1990), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1999), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2000), 2); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2001), 2); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2010), 2); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2990), 2); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2999), 2); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 3000), 3); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 3333), 3); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 5500), 5); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 7777), 7); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 9999), 9); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 10_000), 10); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 10_999), 10); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 11_000), 10); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 22_000), 10); + + let w = SolutionSize { voters: 1, targets: 0 }; + + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 0), 0); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1), 0); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 999), 0); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1000), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1001), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1990), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1999), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2000), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2001), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2010), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 3333), 1); + + let w = SolutionSize { voters: 2, targets: 0 }; + + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 0), 0); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1), 0); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 999), 0); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1000), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1001), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1999), 1); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2000), 2); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2001), 2); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2010), 2); + assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 3333), 2); + } +} + +#[cfg(test)] +mod tests { + use super::{ + mock::{Origin, *}, + Call, *, + }; + use frame_support::{dispatch::Dispatchable, traits::OffchainWorker}; + use mock::Call as OuterCall; + use sp_election_providers::Assignment; + use sp_runtime::{traits::ValidateUnsigned, PerU16}; + + #[test] + fn validate_unsigned_retracts_wrong_phase() { + ExtBuilder::default().desired_targets(0).build_and_execute(|| { + let solution = RawSolution:: { score: [5, 0, 0], ..Default::default() }; + let call = Call::submit_unsigned(solution.clone(), witness()); + + // initial + assert_eq!(TwoPhase::current_phase(), Phase::Off); + assert!(matches!( + ::validate_unsigned(TransactionSource::Local, &call) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom(0)) + )); + assert!(matches!( + ::pre_dispatch(&call).unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom(0)) + )); + + // signed + roll_to(15); + assert_eq!(TwoPhase::current_phase(), Phase::Signed); + assert!(matches!( + ::validate_unsigned(TransactionSource::Local, &call) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom(0)) + )); + assert!(matches!( + ::pre_dispatch(&call).unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom(0)) + )); + + // unsigned + roll_to(25); + assert!(TwoPhase::current_phase().is_unsigned()); + + assert!(::validate_unsigned( + TransactionSource::Local, + &call + ) + .is_ok()); + assert!(::pre_dispatch(&call).is_ok()); + }) + } + + #[test] + fn validate_unsigned_retracts_low_score() { + ExtBuilder::default().desired_targets(0).build_and_execute(|| { + roll_to(25); + assert!(TwoPhase::current_phase().is_unsigned()); + + let solution = RawSolution:: { score: [5, 0, 0], ..Default::default() }; + let call = Call::submit_unsigned(solution.clone(), witness()); + + // initial + assert!(::validate_unsigned( + TransactionSource::Local, + &call + ) + .is_ok()); + assert!(::pre_dispatch(&call).is_ok()); + + // set a better score + let ready = ReadySolution { score: [10, 0, 0], ..Default::default() }; + >::put(ready); + + // won't work anymore. + assert!(matches!( + ::validate_unsigned(TransactionSource::Local, &call) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom(2)) + )); + assert!(matches!( + ::pre_dispatch(&call).unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom(2)) + )); + }) + } + + #[test] + fn validate_unsigned_retracts_incorrect_winner_count() { + ExtBuilder::default().desired_targets(1).build_and_execute(|| { + roll_to(25); + assert!(TwoPhase::current_phase().is_unsigned()); + + let solution = RawSolution:: { score: [5, 0, 0], ..Default::default() }; + let call = Call::submit_unsigned(solution.clone(), witness()); + assert_eq!(solution.compact.unique_targets().len(), 0); + + // won't work anymore. + assert!(matches!( + ::validate_unsigned(TransactionSource::Local, &call) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom(1)) + )); + }) + } + + #[test] + fn priority_is_set() { + ExtBuilder::default().unsigned_priority(20).desired_targets(0).build_and_execute(|| { + roll_to(25); + assert!(TwoPhase::current_phase().is_unsigned()); + + let solution = RawSolution:: { score: [5, 0, 0], ..Default::default() }; + let call = Call::submit_unsigned(solution.clone(), witness()); + + assert_eq!( + ::validate_unsigned(TransactionSource::Local, &call) + .unwrap() + .priority, + 25 + ); + }) + } + + #[test] + #[should_panic(expected = "Invalid unsigned submission must produce invalid block and \ + deprive validator from their authoring reward.: \ + DispatchError::Module { index: 0, error: 1, message: \ + Some(\"WrongWinnerCount\") }")] + fn unfeasible_solution_panics() { + ExtBuilder::default().build_and_execute(|| { + roll_to(25); + assert!(TwoPhase::current_phase().is_unsigned()); + + // This is in itself an invalid BS solution. + let solution = RawSolution:: { score: [5, 0, 0], ..Default::default() }; + let call = Call::submit_unsigned(solution.clone(), witness()); + let outer_call: OuterCall = call.into(); + let _ = outer_call.dispatch(Origin::none()); + }) + } + + #[test] + #[should_panic(expected = "Invalid unsigned submission must produce invalid block and \ + deprive validator from their authoring reward.")] + fn wrong_witness_panics() { + ExtBuilder::default().build_and_execute(|| { + roll_to(25); + assert!(TwoPhase::current_phase().is_unsigned()); + + // This solution is unfeasible as well, but we won't even get there. + let solution = RawSolution:: { score: [5, 0, 0], ..Default::default() }; + + let mut correct_witness = witness(); + correct_witness.voters += 1; + correct_witness.targets -= 1; + let call = Call::submit_unsigned(solution.clone(), correct_witness); + let outer_call: OuterCall = call.into(); + let _ = outer_call.dispatch(Origin::none()); + }) + } + + #[test] + fn miner_works() { + ExtBuilder::default().build_and_execute(|| { + roll_to(25); + assert!(TwoPhase::current_phase().is_unsigned()); + + // ensure we have snapshots in place. + assert!(TwoPhase::snapshot().is_some()); + assert_eq!(TwoPhase::desired_targets().unwrap(), 2); + + // mine seq_phragmen solution with 2 iters. + let (solution, witness) = TwoPhase::mine_solution(2).unwrap(); + + // ensure this solution is valid. + assert!(TwoPhase::queued_solution().is_none()); + assert_ok!(TwoPhase::submit_unsigned(Origin::none(), solution, witness)); + assert!(TwoPhase::queued_solution().is_some()); + }) + } + + #[test] + fn miner_trims_weight() { + ExtBuilder::default().miner_weight(100).mock_weight_info(true).build_and_execute(|| { + roll_to(25); + assert!(TwoPhase::current_phase().is_unsigned()); + + let (solution, witness) = TwoPhase::mine_solution(2).unwrap(); + let solution_weight = ::WeightInfo::submit_unsigned( + witness.voters, + witness.targets, + solution.compact.voter_count() as u32, + solution.compact.unique_targets().len() as u32, + ); + // default solution will have 5 edges (5 * 5 + 10) + assert_eq!(solution_weight, 35); + assert_eq!(solution.compact.voter_count(), 5); + + // now reduce the max weight + ::set(25); + + let (solution, witness) = TwoPhase::mine_solution(2).unwrap(); + let solution_weight = ::WeightInfo::submit_unsigned( + witness.voters, + witness.targets, + solution.compact.voter_count() as u32, + solution.compact.unique_targets().len() as u32, + ); + // default solution will have 5 edges (5 * 5 + 10) + assert_eq!(solution_weight, 25); + assert_eq!(solution.compact.voter_count(), 3); + }) + } + + #[test] + fn miner_will_not_submit_if_not_enough_winners() { + ExtBuilder::default().desired_targets(8).build_and_execute(|| { + roll_to(25); + assert!(TwoPhase::current_phase().is_unsigned()); + + // mine seq_phragmen solution with 2 iters. + assert_eq!( + TwoPhase::mine_solution(2).unwrap_err(), + ElectionError::Feasibility(FeasibilityError::WrongWinnerCount), + ); + }) + } + + #[test] + fn unsigned_per_dispatch_checks_can_only_submit_threshold_better() { + ExtBuilder::default() + .desired_targets(1) + .add_voter(7, 2, vec![10]) + .add_voter(8, 5, vec![10]) + .solution_improvement_threshold(Perbill::from_percent(50)) + .build_and_execute(|| { + roll_to(25); + assert!(TwoPhase::current_phase().is_unsigned()); + assert_eq!(TwoPhase::desired_targets().unwrap(), 1); + + // an initial solution + let result = ElectionResult { + // note: This second element of backing stake is not important here. + winners: vec![(10, 10)], + assignments: vec![Assignment { + who: 10, + distribution: vec![(10, PerU16::one())], + }], + }; + let (solution, witness) = TwoPhase::prepare_election_result(result).unwrap(); + assert_ok!(TwoPhase::unsigned_pre_dispatch_checks(&solution)); + assert_ok!(TwoPhase::submit_unsigned(Origin::none(), solution, witness)); + assert_eq!(TwoPhase::queued_solution().unwrap().score[0], 10); + + // trial 1: a solution who's score is only 2, i.e. 20% better in the first element. + let result = ElectionResult { + winners: vec![(10, 12)], + assignments: vec![ + Assignment { who: 10, distribution: vec![(10, PerU16::one())] }, + Assignment { + who: 7, + // note: this percent doesn't even matter, in compact it is 100%. + distribution: vec![(10, PerU16::one())], + }, + ], + }; + let (solution, _) = TwoPhase::prepare_election_result(result).unwrap(); + // 12 is not 50% more than 10 + assert_eq!(solution.score[0], 12); + assert_noop!( + TwoPhase::unsigned_pre_dispatch_checks(&solution), + Error::::WeakSubmission, + ); + // submitting this will actually panic. + + // trial 2: a solution who's score is only 7, i.e. 70% better in the first element. + let result = ElectionResult { + winners: vec![(10, 12)], + assignments: vec![ + Assignment { who: 10, distribution: vec![(10, PerU16::one())] }, + Assignment { who: 7, distribution: vec![(10, PerU16::one())] }, + Assignment { + who: 8, + // note: this percent doesn't even matter, in compact it is 100%. + distribution: vec![(10, PerU16::one())], + }, + ], + }; + let (solution, witness) = TwoPhase::prepare_election_result(result).unwrap(); + assert_eq!(solution.score[0], 17); + + // and it is fine + assert_ok!(TwoPhase::unsigned_pre_dispatch_checks(&solution)); + assert_ok!(TwoPhase::submit_unsigned(Origin::none(), solution, witness)); + }) + } + + #[test] + fn ocw_check_prevent_duplicate() { + let (mut ext, _) = ExtBuilder::default().build_offchainify(0); + ext.execute_with(|| { + roll_to(25); + assert!(TwoPhase::current_phase().is_unsigned()); + + // first execution -- okay. + assert!(TwoPhase::set_check_offchain_execution_status(25).is_ok()); + + // next block: rejected. + assert!(TwoPhase::set_check_offchain_execution_status(26).is_err()); + + // allowed after `OFFCHAIN_REPEAT` + assert!(TwoPhase::set_check_offchain_execution_status((26 + OFFCHAIN_REPEAT).into()) + .is_ok()); + + // a fork like situation: re-execute last 3. + assert!(TwoPhase::set_check_offchain_execution_status( + (26 + OFFCHAIN_REPEAT - 3).into() + ) + .is_err()); + assert!(TwoPhase::set_check_offchain_execution_status( + (26 + OFFCHAIN_REPEAT - 2).into() + ) + .is_err()); + assert!(TwoPhase::set_check_offchain_execution_status( + (26 + OFFCHAIN_REPEAT - 1).into() + ) + .is_err()); + }) + } + + #[test] + fn ocw_only_runs_when_signed_open_now() { + let (mut ext, pool) = ExtBuilder::default().build_offchainify(0); + ext.execute_with(|| { + roll_to(25); + assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + + // we must clear the offchain storage to ensure the offchain execution check doesn't get + // in the way. + let mut storage = StorageValueRef::persistent(&OFFCHAIN_HEAD_DB); + + TwoPhase::offchain_worker(24); + assert!(pool.read().transactions.len().is_zero()); + storage.clear(); + + TwoPhase::offchain_worker(26); + assert!(pool.read().transactions.len().is_zero()); + storage.clear(); + + // submits! + TwoPhase::offchain_worker(25); + assert!(!pool.read().transactions.len().is_zero()); + }) + } + + #[test] + fn ocw_can_submit_to_pool() { + let (mut ext, pool) = ExtBuilder::default().build_offchainify(0); + ext.execute_with(|| { + roll_to_with_ocw(25); + assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + // OCW must have submitted now + + let encoded = pool.read().transactions[0].clone(); + let extrinsic: Extrinsic = Decode::decode(&mut &*encoded).unwrap(); + let call = extrinsic.call; + assert!(matches!(call, OuterCall::TwoPhase(Call::submit_unsigned(_, _)))); + }) + } +} diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs new file mode 100644 index 0000000000000..6070b771593ce --- /dev/null +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -0,0 +1,147 @@ +// 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. + +//! Autogenerated weights for pallet_two_phase_election_provider +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.1 +//! DATE: 2021-01-14, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 + +// Executed Command: +// target/release/substrate +// benchmark +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_two_phase_election_provider +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./frame/two-phase-election-provider/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{ + traits::Get, + weights::{Weight, constants::RocksDbWeight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_two_phase_election_provider. +pub trait WeightInfo { + fn on_initialize_nothing() -> Weight; + fn on_initialize_open_signed() -> Weight; + fn on_initialize_open_unsigned_without_snapshot() -> Weight; + fn on_initialize_open_unsigned_with_snapshot() -> Weight; + fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight; + fn feasibility_check(v: u32, t: u32, a: u32, d: u32) -> Weight; +} + +/// Weights for pallet_two_phase_election_provider using the Substrate node and recommended +/// hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn on_initialize_nothing() -> Weight { + (21_280_000 as Weight).saturating_add(T::DbWeight::get().reads(7 as Weight)) + } + fn on_initialize_open_signed() -> Weight { + (74_221_000 as Weight) + .saturating_add(T::DbWeight::get().reads(7 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn on_initialize_open_unsigned_with_snapshot() -> Weight { + (76_100_000 as Weight) + .saturating_add(T::DbWeight::get().reads(8 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn on_initialize_open_unsigned_without_snapshot() -> Weight { + (76_100_000 as Weight) + .saturating_add(T::DbWeight::get().reads(8 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn submit_unsigned(v: u32, _t: u32, a: u32, d: u32) -> Weight { + (0 as Weight) + // Standard Error: 21_000 + .saturating_add((2_606_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 21_000 + .saturating_add((11_405_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 108_000 + .saturating_add((2_651_000 as Weight).saturating_mul(d as Weight)) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn feasibility_check(v: u32, t: u32, a: u32, d: u32) -> Weight { + (0 as Weight) + // Standard Error: 12_000 + .saturating_add((2_788_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 41_000 + .saturating_add((601_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 12_000 + .saturating_add((9_722_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 61_000 + .saturating_add((3_706_000 as Weight).saturating_mul(d as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn on_initialize_nothing() -> Weight { + (21_280_000 as Weight).saturating_add(RocksDbWeight::get().reads(7 as Weight)) + } + fn on_initialize_open_signed() -> Weight { + (74_221_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(7 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + } + fn on_initialize_open_unsigned_with_snapshot() -> Weight { + (76_100_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(8 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + } + fn on_initialize_open_unsigned_without_snapshot() -> Weight { + (76_100_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(8 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + } + fn submit_unsigned(v: u32, _t: u32, a: u32, d: u32) -> Weight { + (0 as Weight) + // Standard Error: 21_000 + .saturating_add((2_606_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 21_000 + .saturating_add((11_405_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 108_000 + .saturating_add((2_651_000 as Weight).saturating_mul(d as Weight)) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn feasibility_check(v: u32, t: u32, a: u32, d: u32) -> Weight { + (0 as Weight) + // Standard Error: 12_000 + .saturating_add((2_788_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 41_000 + .saturating_add((601_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 12_000 + .saturating_add((9_722_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 61_000 + .saturating_add((3_706_000 as Weight).saturating_mul(d as Weight)) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + } +} diff --git a/frame/grandpa/Cargo.toml b/frame/grandpa/Cargo.toml index c6a76de23e454..d12eb6060a1a9 100644 --- a/frame/grandpa/Cargo.toml +++ b/frame/grandpa/Cargo.toml @@ -38,6 +38,7 @@ pallet-offences = { version = "2.0.0", path = "../offences" } pallet-staking = { version = "2.0.0", path = "../staking" } pallet-staking-reward-curve = { version = "2.0.0", path = "../staking/reward-curve" } pallet-timestamp = { version = "2.0.0", path = "../timestamp" } +sp-election-providers = { version = "2.0.0", path = "../../primitives/election-providers" } [features] default = ["std"] diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index bf4ce5a519e7c..2c63424c5abd8 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -40,6 +40,7 @@ use sp_runtime::{ DigestItem, Perbill, }; use sp_staking::SessionIndex; +use sp_election_providers::onchain; impl_outer_origin! { pub enum Origin for Test {} @@ -194,6 +195,13 @@ parameter_types! { pub const StakingUnsignedPriority: u64 = u64::max_value() / 2; } +impl onchain::Config for Test { + type AccountId = ::AccountId; + type BlockNumber = ::BlockNumber; + type Accuracy = Perbill; + type DataProvider = Staking; +} + impl pallet_staking::Config for Test { type RewardRemainder = (); type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; @@ -216,6 +224,7 @@ impl pallet_staking::Config for Test { type MaxIterations = (); type MinSolutionScoreBump = (); type OffchainSolutionWeightLimit = (); + type ElectionProvider = onchain::OnChainSequentialPhragmen; type WeightInfo = (); } diff --git a/frame/offences/benchmarking/Cargo.toml b/frame/offences/benchmarking/Cargo.toml index 80492288d74bf..ede129ce77228 100644 --- a/frame/offences/benchmarking/Cargo.toml +++ b/frame/offences/benchmarking/Cargo.toml @@ -26,6 +26,7 @@ pallet-session = { version = "2.0.0", default-features = false, path = "../../se pallet-staking = { version = "2.0.0", default-features = false, features = ["runtime-benchmarks"], path = "../../staking" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } sp-staking = { version = "2.0.0", default-features = false, path = "../../../primitives/staking" } +sp-election-providers = { version = "2.0.0", default-features = false, path = "../../../primitives/election-providers" } sp-std = { version = "2.0.0", default-features = false, path = "../../../primitives/std" } [dev-dependencies] @@ -50,6 +51,7 @@ std = [ "pallet-staking/std", "sp-runtime/std", "sp-staking/std", + "sp-election-providers/std", "sp-std/std", "codec/std", ] diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 8e0bb361e15ce..3ea1bbb8ebd1d 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -29,7 +29,7 @@ use sp_runtime::{ traits::{IdentityLookup, Block as BlockT}, testing::{Header, UintAuthorityId}, }; - +use sp_election_providers::onchain; type AccountId = u64; type AccountIndex = u32; @@ -147,6 +147,13 @@ parameter_types! { pub type Extrinsic = sp_runtime::testing::TestXt; +impl onchain::Config for Test { + type AccountId = AccountId; + type BlockNumber = BlockNumber; + type Accuracy = Perbill; + type DataProvider = Staking; +} + impl pallet_staking::Config for Test { type Currency = Balances; type UnixTime = pallet_timestamp::Module; @@ -169,6 +176,7 @@ impl pallet_staking::Config for Test { type MaxIterations = (); type MinSolutionScoreBump = (); type OffchainSolutionWeightLimit = (); + type ElectionProvider = onchain::OnChainSequentialPhragmen; type WeightInfo = (); } diff --git a/frame/session/benchmarking/Cargo.toml b/frame/session/benchmarking/Cargo.toml index fc3099e1b95cb..061af4d28e47b 100644 --- a/frame/session/benchmarking/Cargo.toml +++ b/frame/session/benchmarking/Cargo.toml @@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] sp-std = { version = "2.0.0", default-features = false, path = "../../../primitives/std" } sp-session = { version = "2.0.0", default-features = false, path = "../../../primitives/session" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } +sp-election-providers = { version = "2.0.0", default-features = false, path = "../../../primitives/election-providers" } frame-system = { version = "2.0.0", default-features = false, path = "../../system" } frame-benchmarking = { version = "2.0.0", default-features = false, path = "../../benchmarking" } frame-support = { version = "2.0.0", default-features = false, path = "../../support" } @@ -37,6 +38,7 @@ default = ["std"] std = [ "sp-std/std", "sp-session/std", + "sp-election-providers/std", "sp-runtime/std", "frame-system/std", "frame-benchmarking/std", diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 31593b3da54b3..9519b0bc79459 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -21,6 +21,7 @@ use sp_runtime::traits::IdentityLookup; use frame_support::{impl_outer_origin, impl_outer_dispatch, parameter_types}; +use sp_election_providers::onchain; type AccountId = u64; type AccountIndex = u32; @@ -147,13 +148,21 @@ parameter_types! { pub type Extrinsic = sp_runtime::testing::TestXt; -impl frame_system::offchain::SendTransactionTypes for Test where +impl frame_system::offchain::SendTransactionTypes for Test +where Call: From, { type OverarchingCall = Call; type Extrinsic = Extrinsic; } +impl onchain::Config for Test { + type AccountId = AccountId; + type BlockNumber = BlockNumber; + type Accuracy = sp_runtime::Perbill; + type DataProvider = Staking; +} + impl pallet_staking::Config for Test { type Currency = Balances; type UnixTime = pallet_timestamp::Module; @@ -176,6 +185,7 @@ impl pallet_staking::Config for Test { type MaxIterations = (); type MinSolutionScoreBump = (); type OffchainSolutionWeightLimit = (); + type ElectionProvider = onchain::OnChainSequentialPhragmen; type WeightInfo = (); } diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index 90eba3815a7a5..23ff157b9e813 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -158,21 +158,33 @@ impl< } impl< - BlockNumber: Rem + Sub + Zero + PartialOrd + Saturating + Clone, - Period: Get, - Offset: Get, -> EstimateNextSessionRotation for PeriodicSessions { + BlockNumber: Rem + + Sub + + Zero + + PartialOrd + + Saturating + + Clone, + Period: Get, + Offset: Get, + > EstimateNextSessionRotation for PeriodicSessions +{ + fn average_session_length() -> BlockNumber { + Period::get() + } + fn estimate_next_session_rotation(now: BlockNumber) -> Option { let offset = Offset::get(); let period = Period::get(); Some(if now > offset { - let block_after_last_session = (now.clone() - offset) % period.clone(); + let block_after_last_session = (now.clone() - offset.clone()) % period.clone(); if block_after_last_session > Zero::zero() { - now.saturating_add( - period.saturating_sub(block_after_last_session) - ) + now.saturating_add(period.saturating_sub(block_after_last_session)) } else { - now + // this branch happens when the session is already rotated or will rotate in this + // block (depending on being called before or after `session::on_initialize`). Here, + // we assume the latter, namely that this is called after `session::on_initialize`, + // and thus we add period to it as well. + now + period } } else { offset @@ -851,6 +863,10 @@ impl EstimateNextNewSession for Module { T::NextSessionRotation::estimate_next_session_rotation(now) } + fn average_session_length() -> T::BlockNumber { + T::NextSessionRotation::average_session_length() + } + fn weight(now: T::BlockNumber) -> Weight { T::NextSessionRotation::weight(now) } diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 7c2fc21fde54e..93ec34025bd1e 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -17,6 +17,7 @@ static_assertions = "1.1.0" serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.6", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +# TWO_PHASE_NOTE:: ideally we should be able to get rid of this. sp-npos-elections = { version = "2.0.0", default-features = false, path = "../../primitives/npos-elections" } sp-io ={ version = "2.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } @@ -25,6 +26,7 @@ frame-support = { version = "2.0.0", default-features = false, path = "../suppor frame-system = { version = "2.0.0", default-features = false, path = "../system" } pallet-session = { version = "2.0.0", default-features = false, features = ["historical"], path = "../session" } pallet-authorship = { version = "2.0.0", default-features = false, path = "../authorship" } +sp-election-providers = { version = "2.0.0", default-features = false, path = "../../primitives/election-providers" } sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } # Optional imports for benchmarking @@ -40,6 +42,7 @@ pallet-timestamp = { version = "2.0.0", path = "../timestamp" } pallet-staking-reward-curve = { version = "2.0.0", path = "../staking/reward-curve" } substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } frame-benchmarking = { version = "2.0.0", path = "../benchmarking" } +sp-election-providers = { version = "2.0.0", features = ["runtime-benchmarks"], path = "../../primitives/election-providers" } rand_chacha = { version = "0.2" } parking_lot = "0.11.1" hex = "0.4" @@ -59,8 +62,10 @@ std = [ "frame-system/std", "pallet-authorship/std", "sp-application-crypto/std", + "sp-election-providers/std", ] runtime-benchmarks = [ "frame-benchmarking", + "sp-election-providers/runtime-benchmarks", "rand_chacha", ] diff --git a/frame/staking/fuzzer/Cargo.toml b/frame/staking/fuzzer/Cargo.toml index db65e347d8e2a..dd28df6180173 100644 --- a/frame/staking/fuzzer/Cargo.toml +++ b/frame/staking/fuzzer/Cargo.toml @@ -27,6 +27,7 @@ sp-std = { version = "2.0.0", path = "../../../primitives/std" } sp-io ={ version = "2.0.0", path = "../../../primitives/io" } sp-core = { version = "2.0.0", path = "../../../primitives/core" } sp-npos-elections = { version = "2.0.0", path = "../../../primitives/npos-elections" } +sp-election-providers = { version = "2.0.0", path = "../../../primitives/election-providers" } sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } [[bin]] diff --git a/frame/staking/fuzzer/src/mock.rs b/frame/staking/fuzzer/src/mock.rs index b3c9dd9f57b60..da6617d0519b3 100644 --- a/frame/staking/fuzzer/src/mock.rs +++ b/frame/staking/fuzzer/src/mock.rs @@ -151,13 +151,28 @@ parameter_types! { pub type Extrinsic = sp_runtime::testing::TestXt; -impl frame_system::offchain::SendTransactionTypes for Test where +impl frame_system::offchain::SendTransactionTypes for Test +where Call: From, { type OverarchingCall = Call; type Extrinsic = Extrinsic; } +pub struct MockElectionProvider; +impl sp_election_providers::ElectionProvider for MockElectionProvider { + type Error = (); + type DataProvider = pallet_staking::Module; + + fn elect() -> Result, Self::Error> { + Err(()) + } + + fn ongoing() -> bool { + false + } +} + impl pallet_staking::Config for Test { type Currency = Balances; type UnixTime = pallet_timestamp::Module; @@ -181,4 +196,5 @@ impl pallet_staking::Config for Test { type UnsignedPriority = (); type OffchainSolutionWeightLimit = (); type WeightInfo = (); + type ElectionProvider = MockElectionProvider; } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 795f222158e05..109145ede5e31 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -232,10 +232,11 @@ //! //! The controller account can free a portion (or all) of the funds using the //! [`unbond`](enum.Call.html#variant.unbond) call. Note that the funds are not immediately -//! accessible. Instead, a duration denoted by [`BondingDuration`](./trait.Config.html#associatedtype.BondingDuration) -//! (in number of eras) must pass until the funds can actually be removed. Once the -//! `BondingDuration` is over, the [`withdraw_unbonded`](./enum.Call.html#variant.withdraw_unbonded) -//! call can be used to actually withdraw the funds. +//! accessible. Instead, a duration denoted by +//! [`BondingDuration`](./trait.Config.html#associatedtype.BondingDuration) (in number of eras) must +//! pass until the funds can actually be removed. Once the `BondingDuration` is over, the +//! [`withdraw_unbonded`](./enum.Call.html#variant.withdraw_unbonded) call can be used to actually +//! withdraw the funds. //! //! Note that there is a limitation to the number of fund-chunks that can be scheduled to be //! unlocked in the future via [`unbond`](enum.Call.html#variant.unbond). In case this maximum @@ -304,7 +305,7 @@ use frame_support::{ }; use pallet_session::historical; use sp_runtime::{ - Percent, Perbill, PerU16, PerThing, InnerOf, RuntimeDebug, DispatchError, + Percent, Perbill, PerU16, InnerOf, RuntimeDebug, DispatchError, curve::PiecewiseLinear, traits::{ Convert, Zero, StaticLookup, CheckedSub, Saturating, SaturatedConversion, @@ -327,15 +328,14 @@ use frame_system::{ }; use sp_npos_elections::{ ExtendedBalance, Assignment, ElectionScore, ElectionResult as PrimitiveElectionResult, - build_support_map, evaluate_support, seq_phragmen, generate_solution_type, - is_score_better, VotingLimit, SupportMap, VoteWeight, + to_supports, EvaluateSupport, seq_phragmen, generate_solution_type, is_score_better, Supports, + VoteWeight, CompactSolution, PerThing128, }; +use sp_election_providers::ElectionProvider; pub use weights::WeightInfo; const STAKING_ID: LockIdentifier = *b"staking "; pub const MAX_UNLOCKING_CHUNKS: usize = 32; -pub const MAX_NOMINATIONS: usize = ::LIMIT; - pub(crate) const LOG_TARGET: &'static str = "staking"; // syntactic sugar for logging. @@ -344,7 +344,7 @@ macro_rules! log { ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { frame_support::debug::$level!( target: crate::LOG_TARGET, - $patter $(, $values)* + concat!("💸 ", $patter) $(, $values)* ) }; } @@ -364,6 +364,8 @@ static_assertions::const_assert!(size_of::() <= size_of::() /// Maximum number of stakers that can be stored in a snapshot. pub(crate) const MAX_VALIDATORS: usize = ValidatorIndex::max_value() as usize; pub(crate) const MAX_NOMINATORS: usize = NominatorIndex::max_value() as usize; +pub const MAX_NOMINATIONS: usize = + ::LIMIT; /// Counter for the number of eras that have passed. pub type EraIndex = u32; @@ -387,10 +389,12 @@ pub type OffchainAccuracy = PerU16; pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; -type PositiveImbalanceOf = - <::Currency as Currency<::AccountId>>::PositiveImbalance; -type NegativeImbalanceOf = - <::Currency as Currency<::AccountId>>::NegativeImbalance; +type PositiveImbalanceOf = <::Currency as Currency< + ::AccountId, +>>::PositiveImbalance; +type NegativeImbalanceOf = <::Currency as Currency< + ::AccountId, +>>::NegativeImbalance; /// Information regarding the active era (era in used in session). #[derive(Encode, Decode, RuntimeDebug)] @@ -772,7 +776,7 @@ impl SessionInterface<::AccountId> for T w pub trait Config: frame_system::Config + SendTransactionTypes> { /// The staking balance. - type Currency: LockableCurrency; + type Currency: LockableCurrency; /// Time used for computing era duration. /// @@ -787,6 +791,14 @@ pub trait Config: frame_system::Config + SendTransactionTypes> { /// [`BalanceOf`]. type CurrencyToVote: CurrencyToVote>; + /// Something that provides the election functionality. + type ElectionProvider: sp_election_providers::ElectionProvider< + Self::AccountId, + Self::BlockNumber, + // we only accept an election provider that has staking as data provider. + DataProvider = Module, + >; + /// Tokens have been minted and are unused for validator-reward. /// See [Era payout](./index.html#era-payout). type RewardRemainder: OnUnbalanced>; @@ -883,7 +895,9 @@ pub enum Forcing { } impl Default for Forcing { - fn default() -> Self { Forcing::NotForcing } + fn default() -> Self { + Forcing::NotForcing + } } // A value placed in storage that represents the current version of the Staking storage. This value @@ -1059,28 +1073,45 @@ decl_storage! { /// The earliest era for which we have a pending, unapplied slash. EarliestUnappliedSlash: Option; + /// The last planned session scheduled by the session pallet. + /// + /// This is basically in sync with the call to [`SessionManager::new_session`]. + pub CurrentPlannedSession get(fn current_planned_session): SessionIndex; + /// Snapshot of validators at the beginning of the current election window. This should only /// have a value when [`EraElectionStatus`] == `ElectionStatus::Open(_)`. + /// + /// TWO_PHASE_NOTE: should be removed once we switch to multi-phase. pub SnapshotValidators get(fn snapshot_validators): Option>; /// Snapshot of nominators at the beginning of the current election window. This should only /// have a value when [`EraElectionStatus`] == `ElectionStatus::Open(_)`. + /// + /// TWO_PHASE_NOTE: should be removed once we switch to multi-phase. pub SnapshotNominators get(fn snapshot_nominators): Option>; /// The next validator set. At the end of an era, if this is available (potentially from the /// result of an offchain worker), it is immediately used. Otherwise, the on-chain election /// is executed. + /// + /// TWO_PHASE_NOTE: should be removed once we switch to multi-phase. pub QueuedElected get(fn queued_elected): Option>>; /// The score of the current [`QueuedElected`]. + /// + /// TWO_PHASE_NOTE: should be removed once we switch to multi-phase. pub QueuedScore get(fn queued_score): Option; /// Flag to control the execution of the offchain election. When `Open(_)`, we accept /// solutions to be submitted. + /// + /// TWO_PHASE_NOTE: should be removed once we switch to multi-phase. pub EraElectionStatus get(fn era_election_status): ElectionStatus; /// True if the current **planned** session is final. Note that this does not take era /// forcing into account. + /// + /// TWO_PHASE_NOTE: should be removed once we switch to multi-phase. pub IsCurrentSessionFinal get(fn is_current_session_final): bool = false; /// True if network has been upgraded to this version. @@ -1298,14 +1329,14 @@ decl_module! { ElectionStatus::::Open(now) ); add_weight(0, 1, 0); - log!(info, "💸 Election window is Open({:?}). Snapshot created", now); + log!(info, "Election window is Open({:?}). Snapshot created", now); } else { - log!(warn, "💸 Failed to create snapshot at {:?}.", now); + log!(warn, "Failed to create snapshot at {:?}.", now); } } } } else { - log!(warn, "💸 Estimating next session change failed."); + log!(warn, "Estimating next session change failed."); } add_weight(0, 0, T::NextNewSession::weight(now)) } @@ -1320,16 +1351,15 @@ decl_module! { /// to open. If so, it runs the offchain worker code. fn offchain_worker(now: T::BlockNumber) { use offchain_election::{set_check_offchain_execution_status, compute_offchain_election}; - if Self::era_election_status().is_open_at(now) { let offchain_status = set_check_offchain_execution_status::(now); if let Err(why) = offchain_status { - log!(warn, "💸 skipping offchain worker in open election window due to [{}]", why); + log!(warn, "skipping offchain worker in open election window due to [{}]", why); } else { if let Err(e) = compute_offchain_election::() { - log!(error, "💸 Error in election offchain worker: {:?}", e); + log!(error, "Error in election offchain worker: {:?}", e); } else { - log!(debug, "💸 Executed offchain worker thread without errors."); + log!(debug, "Executed offchain worker thread without errors."); } } } @@ -2102,7 +2132,7 @@ decl_module! { #[weight = T::WeightInfo::submit_solution_better( size.validators.into(), size.nominators.into(), - compact.len() as u32, + compact.voter_count() as u32, winners.len() as u32, )] pub fn submit_election_solution( @@ -2136,7 +2166,7 @@ decl_module! { #[weight = T::WeightInfo::submit_solution_better( size.validators.into(), size.nominators.into(), - compact.len() as u32, + compact.voter_count() as u32, winners.len() as u32, )] pub fn submit_election_solution_unsigned( @@ -2175,7 +2205,10 @@ impl Module { } /// Internal impl of [`Self::slashable_balance_of`] that returns [`VoteWeight`]. - pub fn slashable_balance_of_vote_weight(stash: &T::AccountId, issuance: BalanceOf) -> VoteWeight { + pub fn slashable_balance_of_vote_weight( + stash: &T::AccountId, + issuance: BalanceOf, + ) -> VoteWeight { T::CurrencyToVote::to_vote(Self::slashable_balance_of(stash), issuance) } @@ -2214,7 +2247,7 @@ impl Module { { log!( warn, - "💸 Snapshot size too big [{} <> {}][{} <> {}].", + "Snapshot size too big [{} <> {}][{} <> {}].", num_validators, MAX_VALIDATORS, num_nominators, @@ -2238,10 +2271,7 @@ impl Module { >::kill(); } - fn do_payout_stakers( - validator_stash: T::AccountId, - era: EraIndex, - ) -> DispatchResult { + fn do_payout_stakers(validator_stash: T::AccountId, era: EraIndex) -> DispatchResult { // Validate input data let current_era = CurrentEra::get().ok_or(Error::::InvalidEraToReward)?; ensure!(era <= current_era, Error::::InvalidEraToReward); @@ -2534,7 +2564,7 @@ impl Module { validator_at, ).map_err(|e| { // log the error since it is not propagated into the runtime error. - log!(warn, "💸 un-compacting solution failed due to {:?}", e); + log!(warn, "un-compacting solution failed due to {:?}", e); Error::::OffchainElectionBogusCompact })?; @@ -2549,7 +2579,7 @@ impl Module { // all of the indices must map to either a validator or a nominator. If this is ever // not the case, then the locking system of staking is most likely faulty, or we // have bigger problems. - log!(error, "💸 detected an error in the staking locking and snapshot."); + log!(error, "detected an error in the staking locking and snapshot."); // abort. return Err(Error::::OffchainElectionBogusNominator.into()); } @@ -2598,20 +2628,19 @@ impl Module { ); // build the support map thereof in order to evaluate. - let supports = build_support_map::( - &winners, - &staked_assignments, - ).map_err(|_| Error::::OffchainElectionBogusEdge)?; + let supports = to_supports(&winners, &staked_assignments) + .map_err(|_| Error::::OffchainElectionBogusEdge)?; // Check if the score is the same as the claimed one. - let submitted_score = evaluate_support(&supports); + let submitted_score = (&supports).evaluate(); ensure!(submitted_score == claimed_score, Error::::OffchainElectionBogusScore); // At last, alles Ok. Exposures and store the result. - let exposures = Self::collect_exposure(supports); + let exposures = Self::collect_exposures(supports); log!( info, - "💸 A better solution (with compute {:?} and score {:?}) has been validated and stored on chain.", + "A better solution (with compute {:?} and score {:?}) has been validated and stored \ + on chain.", compute, submitted_score, ); @@ -2744,6 +2773,7 @@ impl Module { // Set staking information for new era. let maybe_new_validators = Self::select_and_update_validators(current_era); + let _unused_new_validators = Self::enact_election(current_era); maybe_new_validators } @@ -2811,7 +2841,7 @@ impl Module { log!( info, - "💸 new validator set of size {:?} has been elected via {:?} for era {:?}", + "new validator set of size {:?} has been elected via {:?} for staring era {:?}", elected_stashes.len(), compute, current_era, @@ -2860,20 +2890,20 @@ impl Module { Self::slashable_balance_of_fn(), ); - let supports = build_support_map::( + let supports = to_supports( &elected_stashes, &staked_assignments, ) .map_err(|_| log!( error, - "💸 on-chain phragmen is failing due to a problem in the result. This must be a bug." + "on-chain phragmen is failing due to a problem in the result. This must be a bug." ) ) .ok()?; // collect exposures - let exposures = Self::collect_exposure(supports); + let exposures = Self::collect_exposures(supports); // In order to keep the property required by `on_session_ending` that we must return the // new validator set even if it's the same as the old, as long as any underlying @@ -2899,7 +2929,7 @@ impl Module { /// Self votes are added and nominations before the most recent slashing span are ignored. /// /// No storage item is updated. - pub fn do_phragmen( + pub fn do_phragmen( iterations: usize, ) -> Option> where @@ -2938,7 +2968,7 @@ impl Module { // If we don't have enough candidates, nothing to do. log!( warn, - "💸 Chain does not have enough staking candidates to operate. Era {:?}.", + "chain does not have enough staking candidates to operate. Era {:?}.", Self::current_era() ); None @@ -2949,14 +2979,15 @@ impl Module { all_nominators, Some((iterations, 0)), // exactly run `iterations` rounds. ) - .map_err(|err| log!(error, "Call to seq-phragmen failed due to {}", err)) + .map_err(|err| log!(error, "Call to seq-phragmen failed due to {:?}", err)) .ok() } } - /// Consume a set of [`Supports`] from [`sp_npos_elections`] and collect them into a [`Exposure`] - fn collect_exposure( - supports: SupportMap, + /// Consume a set of [`Supports`] from [`sp_npos_elections`] and collect them into a + /// [`Exposure`]. + fn collect_exposures( + supports: Supports, ) -> Vec<(T::AccountId, Exposure>)> { let total_issuance = T::Currency::total_issuance(); let to_currency = |e: ExtendedBalance| T::CurrencyToVote::to_currency(e, total_issuance); @@ -2988,6 +3019,80 @@ impl Module { }).collect::)>>() } + /// Process the output of the election. + /// + /// This ensures enough validators have been elected, converts all supports to exposures and + /// writes them to the associated storage. + /// + /// Returns `Err(())` if less than [`MinimumValidatorCount`] validators have been elected, `Ok` + /// otherwise. + // TWO_PHASE_NOTE: the deadcode + #[allow(dead_code)] + pub fn process_election( + flat_supports: sp_npos_elections::Supports, + current_era: EraIndex, + ) -> Result, ()> { + let exposures = Self::collect_exposures(flat_supports); + let elected_stashes = exposures.iter().cloned().map(|(x, _)| x).collect::>(); + + if (elected_stashes.len() as u32) <= Self::minimum_validator_count() { + log!( + warn, + "chain does not have enough staking candidates to operate for era {:?}", + current_era, + ); + return Err(()); + } + + // Populate Stakers and write slot stake. + let mut total_stake: BalanceOf = Zero::zero(); + exposures.into_iter().for_each(|(stash, exposure)| { + total_stake = total_stake.saturating_add(exposure.total); + >::insert(current_era, &stash, &exposure); + + let mut exposure_clipped = exposure; + let clipped_max_len = T::MaxNominatorRewardedPerValidator::get() as usize; + if exposure_clipped.others.len() > clipped_max_len { + exposure_clipped.others.sort_by(|a, b| a.value.cmp(&b.value).reverse()); + exposure_clipped.others.truncate(clipped_max_len); + } + >::insert(¤t_era, &stash, exposure_clipped); + }); + + // Insert current era staking information + >::insert(¤t_era, total_stake); + + // collect the pref of all winners + for stash in &elected_stashes { + let pref = Self::validators(stash); + >::insert(¤t_era, stash, pref); + } + + // emit event + // TWO_PHASE_NOTE: remove the inner value. + Self::deposit_event(RawEvent::StakingElection(ElectionCompute::Signed)); + + log!( + info, + "new validator set of size {:?} has been processed for era {:?}", + elected_stashes.len(), + current_era, + ); + + Ok(elected_stashes) + } + + /// Enact and process the election using the `ElectionProvider` type. + /// + /// This will also process the election, as noted in [`process_election`]. + fn enact_election(_current_era: EraIndex) -> Option> { + let outcome = T::ElectionProvider::elect().map(|_| ()); + log!(debug, "Experimental election provider outputted {:?}", outcome); + // TWO_PHASE_NOTE: This code path shall not return anything for now. Later on, redirect the + // results to `process_election`. + None + } + /// Remove all associated data of a stash account from the staking system. /// /// Assumes storage is upgraded before calling. @@ -3080,7 +3185,11 @@ impl Module { } #[cfg(feature = "runtime-benchmarks")] - pub fn add_era_stakers(current_era: EraIndex, controller: T::AccountId, exposure: Exposure>) { + pub fn add_era_stakers( + current_era: EraIndex, + controller: T::AccountId, + exposure: Exposure>, + ) { >::insert(¤t_era, &controller, &exposure); } @@ -3093,6 +3202,106 @@ impl Module { pub fn set_slash_reward_fraction(fraction: Perbill) { SlashRewardFraction::put(fraction); } + + /// Get all of the voters that are eligible for the npos election. + /// + /// This will use all on-chain nominators, and all the validators will inject a self vote. + /// + /// ### Slashing + /// + /// All nominations that have been submitted before the last non-zero slash of the validator are + /// auto-chilled. + /// + /// Note that this is VERY expensive. Use with care. + pub fn get_npos_voters() -> Vec<(T::AccountId, VoteWeight, Vec)> { + let weight_of = Self::slashable_balance_of_fn(); + let mut all_voters = Vec::new(); + + for (validator, _) in >::iter() { + // append self vote + let self_vote = (validator.clone(), weight_of(&validator), vec![validator.clone()]); + all_voters.push(self_vote); + } + + for (nominator, nominations) in >::iter() { + let Nominations { submitted_in, mut targets, suppressed: _ } = nominations; + + // Filter out nomination targets which were nominated before the most recent + // slashing span. + targets.retain(|stash| { + Self::slashing_spans(&stash) + .map_or(true, |spans| submitted_in >= spans.last_nonzero_slash()) + }); + + let vote_weight = weight_of(&nominator); + all_voters.push((nominator, vote_weight, targets)) + } + + all_voters + } + + pub fn get_npos_targets() -> Vec { + >::iter().map(|(v, _)| v).collect::>() + } +} + +impl sp_election_providers::ElectionDataProvider + for Module +{ + fn desired_targets() -> u32 { + Self::validator_count() + } + + fn voters() -> Vec<(T::AccountId, VoteWeight, Vec)> { + Self::get_npos_voters() + } + + fn targets() -> Vec { + Self::get_npos_targets() + } + + fn next_election_prediction(now: T::BlockNumber) -> T::BlockNumber { + let current_era = Self::current_era().unwrap_or(0); + let current_session = Self::current_planned_session(); + let current_era_start_session_index = + Self::eras_start_session_index(current_era).unwrap_or(0); + let era_length = current_session + .saturating_sub(current_era_start_session_index) + .min(T::SessionsPerEra::get()); + + let session_length = T::NextNewSession::average_session_length(); + + let until_this_session_end = T::NextNewSession::estimate_next_new_session(now) + .unwrap_or_default() + .saturating_sub(now); + + let sessions_left: T::BlockNumber = T::SessionsPerEra::get() + .saturating_sub(era_length) + // one session is computed in this_session_end. + .saturating_sub(1) + .into(); + + now.saturating_add( + until_this_session_end.saturating_add(sessions_left.saturating_mul(session_length)), + ) + } + + #[cfg(any(feature = "runtime-benchmarks", test))] + fn put_snapshot( + voters: Vec<(T::AccountId, VoteWeight, Vec)>, + targets: Vec, + ) { + targets.into_iter().for_each(|v| { + >::insert(v, ValidatorPrefs { commission: Perbill::zero() }); + }); + + voters.into_iter().for_each(|(v, _s, t)| { + >::insert( + v, + Nominations { targets: t, submitted_in: 0, suppressed: false }, + ); + }); + } } /// In this implementation `new_session(session)` must be called before `end_session(session-1)` @@ -3108,6 +3317,7 @@ impl pallet_session::SessionManager for Module { >::block_number(), new_index ); + CurrentPlannedSession::put(new_index); Self::new_session(new_index) } fn start_session(start_index: SessionIndex) { @@ -3130,10 +3340,12 @@ impl pallet_session::SessionManager for Module { } } -impl historical::SessionManager>> for Module { - fn new_session(new_index: SessionIndex) - -> Option>)>> - { +impl historical::SessionManager>> + for Module +{ + fn new_session( + new_index: SessionIndex, + ) -> Option>)>> { >::new_session(new_index).map(|validators| { let current_era = Self::current_era() // Must be some as a new era has been created. @@ -3158,8 +3370,8 @@ impl historical::SessionManager pallet_authorship::EventHandler for Module - where - T: Config + pallet_authorship::Config + pallet_session::Config +where + T: Config + pallet_authorship::Config + pallet_session::Config, { fn note_author(author: T::AccountId) { Self::reward_by_ids(vec![(author, 20)]) @@ -3202,9 +3414,10 @@ impl Convert } /// This is intended to be used with `FilterHistoricalOffences`. -impl +impl OnOffenceHandler, Weight> -for Module where + for Module +where T: pallet_session::Config::AccountId>, T: pallet_session::historical::Config< FullIdentification = Exposure<::AccountId, BalanceOf>, @@ -3218,12 +3431,15 @@ for Module where >, { fn on_offence( - offenders: &[OffenceDetails>], + offenders: &[OffenceDetails< + T::AccountId, + pallet_session::historical::IdentificationTuple, + >], slash_fraction: &[Perbill], slash_session: SessionIndex, ) -> Result { if !Self::can_report() { - return Err(()) + return Err(()); } let reward_proportion = SlashRewardFraction::get(); @@ -3334,6 +3550,7 @@ for Module where } fn can_report() -> bool { + // TWO_PHASE_NOTE: we can get rid of this API Self::era_election_status().is_closed() } } @@ -3344,7 +3561,8 @@ pub struct FilterHistoricalOffences { } impl ReportOffence - for FilterHistoricalOffences, R> where + for FilterHistoricalOffences, R> +where T: Config, R: ReportOffence, O: Offence, @@ -3401,7 +3619,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { return invalid.into(); } - log!(debug, "💸 validateUnsigned succeeded for a solution at era {}.", era); + log!(debug, "validateUnsigned succeeded for a solution at era {}.", era); ValidTransaction::with_tag_prefix("StakingOffchain") // The higher the score[0], the better a solution is. diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 048806b062395..d01f8b59a681f 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -27,7 +27,7 @@ use frame_support::{ use sp_core::H256; use sp_io; use sp_npos_elections::{ - build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment, ElectionScore, + to_supports, reduce, ExtendedBalance, StakedAssignment, ElectionScore, EvaluateSupport, }; use sp_runtime::{ curve::PiecewiseLinear, @@ -36,6 +36,7 @@ use sp_runtime::{ }; use sp_staking::offence::{OffenceDetails, OnOffenceHandler}; use std::{cell::RefCell, collections::HashSet}; +use sp_election_providers::onchain; pub const INIT_TIMESTAMP: u64 = 30_000; pub const BLOCK_TIME: u64 = 1000; @@ -252,6 +253,12 @@ impl OnUnbalanced> for RewardRemainderMock { } } +impl onchain::Config for Test { + type AccountId = AccountId; + type BlockNumber = BlockNumber; + type Accuracy = Perbill; + type DataProvider = Staking; +} impl Config for Test { type Currency = Balances; type UnixTime = Timestamp; @@ -274,6 +281,7 @@ impl Config for Test { type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type UnsignedPriority = UnsignedPriority; type OffchainSolutionWeightLimit = OffchainSolutionWeightLimit; + type ElectionProvider = onchain::OnChainSequentialPhragmen; type WeightInfo = (); } @@ -769,7 +777,7 @@ pub(crate) fn add_slash(who: &AccountId) { on_offence_now( &[ OffenceDetails { - offender: (who.clone(), Staking::eras_stakers(Staking::active_era().unwrap().index, who.clone())), + offender: (who.clone(), Staking::eras_stakers(active_era(), who.clone())), reporters: vec![], }, ], @@ -850,8 +858,8 @@ pub(crate) fn horrible_npos_solution( let score = { let (_, _, better_score) = prepare_submission_with(true, true, 0, |_| {}); - let support = build_support_map::(&winners, &staked_assignment).unwrap(); - let score = evaluate_support(&support); + let support = to_supports::(&winners, &staked_assignment).unwrap(); + let score = support.evaluate(); assert!(sp_npos_elections::is_score_better::( better_score, @@ -950,11 +958,11 @@ pub(crate) fn prepare_submission_with( Staking::slashable_balance_of_fn(), ); - let support_map = build_support_map::( + let support_map = to_supports( winners.as_slice(), staked.as_slice(), ).unwrap(); - evaluate_support::(&support_map) + support_map.evaluate() } else { Default::default() }; @@ -971,9 +979,8 @@ pub(crate) fn prepare_submission_with( /// Make all validator and nominator request their payment pub(crate) fn make_all_reward_payment(era: EraIndex) { - let validators_with_reward = ErasRewardPoints::::get(era).individual.keys() - .cloned() - .collect::>(); + let validators_with_reward = + ErasRewardPoints::::get(era).individual.keys().cloned().collect::>(); // reward validators for validator_controller in validators_with_reward.iter().filter_map(Staking::bonded) { @@ -990,19 +997,19 @@ pub(crate) fn make_all_reward_payment(era: EraIndex) { macro_rules! assert_session_era { ($session:expr, $era:expr) => { assert_eq!( - Session::current_index(), - $session, - "wrong session {} != {}", - Session::current_index(), - $session, - ); - assert_eq!( - Staking::active_era().unwrap().index, - $era, - "wrong active era {} != {}", - Staking::active_era().unwrap().index, - $era, - ); + Session::current_index(), + $session, + "wrong session {} != {}", + Session::current_index(), + $session, + ); + assert_eq!( + Staking::current_era().unwrap(), + $era, + "wrong current era {} != {}", + Staking::current_era().unwrap(), + $era, + ); }; } diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 433e02261cc58..bec4174ad42d3 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -25,8 +25,8 @@ use codec::Decode; use frame_support::{traits::Get, weights::Weight, IterableStorageMap}; use frame_system::offchain::SubmitTransaction; use sp_npos_elections::{ - build_support_map, evaluate_support, reduce, Assignment, ElectionResult, ElectionScore, - ExtendedBalance, + to_supports, EvaluateSupport, reduce, Assignment, ElectionResult, ElectionScore, + ExtendedBalance, CompactSolution, }; use sp_runtime::{ offchain::storage::StorageValueRef, traits::TrailingZeroInput, PerThing, RuntimeDebug, @@ -127,7 +127,7 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElect crate::log!( info, - "💸 prepared a seq-phragmen solution with {} balancing iterations and score {:?}", + "prepared a seq-phragmen solution with {} balancing iterations and score {:?}", iters, score, ); @@ -265,7 +265,7 @@ pub fn trim_to_weight( where for<'r> FN: Fn(&'r T::AccountId) -> Option, { - match compact.len().checked_sub(maximum_allowed_voters as usize) { + match compact.voter_count().checked_sub(maximum_allowed_voters as usize) { Some(to_remove) if to_remove > 0 => { // grab all voters and sort them by least stake. let balance_of = >::slashable_balance_of_fn(); @@ -284,7 +284,7 @@ where if compact.remove_voter(index) { crate::log!( trace, - "💸 removed a voter at index {} with stake {:?} from compact to reduce the size", + "removed a voter at index {} with stake {:?} from compact to reduce the size", index, _stake, ); @@ -297,19 +297,17 @@ where } crate::log!( - warn, - "💸 {} nominators out of {} had to be removed from compact solution due to size limits.", - removed, - compact.len() + removed, - ); + warn, + "{} nominators out of {} had to be removed from compact solution due to size \ + limits.", + removed, + compact.voter_count() + removed, + ); Ok(compact) } _ => { // nada, return as-is - crate::log!( - info, - "💸 Compact solution did not get trimmed due to block weight limits.", - ); + crate::log!(info, "Compact solution did not get trimmed due to block weight limits.",); Ok(compact) } } @@ -324,12 +322,7 @@ pub fn prepare_submission( do_reduce: bool, maximum_weight: Weight, ) -> Result< - ( - Vec, - CompactAssignments, - ElectionScore, - ElectionSize, - ), + (Vec, CompactAssignments, ElectionScore, ElectionSize), OffchainElectionError, > where @@ -398,16 +391,19 @@ where let maximum_allowed_voters = maximum_compact_len::(winners.len() as u32, size, maximum_weight); - crate::log!(debug, "💸 Maximum weight = {:?} // current weight = {:?} // maximum voters = {:?} // current votes = {:?}", + crate::log!( + debug, + "Maximum weight = {:?} // current weight = {:?} // maximum voters = {:?} // current votes \ + = {:?}", maximum_weight, T::WeightInfo::submit_solution_better( - size.validators.into(), - size.nominators.into(), - compact.len() as u32, - winners.len() as u32, + size.validators.into(), + size.nominators.into(), + compact.voter_count() as u32, + winners.len() as u32, ), maximum_allowed_voters, - compact.len(), + compact.voter_count(), ); let compact = trim_to_weight::(maximum_allowed_voters, compact, &nominator_index)?; @@ -423,9 +419,9 @@ where >::slashable_balance_of_fn(), ); - let support_map = build_support_map::(&winners, &staked) + let support_map = to_supports::(&winners, &staked) .map_err(|_| OffchainElectionError::ElectionFailed)?; - evaluate_support::(&support_map) + support_map.evaluate() }; // winners to index. Use a simple for loop for a more expressive early exit in case of error. diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index d3139b53e6f97..f96d23ffb5c3b 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -244,11 +244,9 @@ pub fn get_weak_solution( >::slashable_balance_of_fn(), ); - let support_map = build_support_map::( - winners.as_slice(), - staked.as_slice(), - ).unwrap(); - evaluate_support::(&support_map) + let support_map = + to_supports::(winners.as_slice(), staked.as_slice()).unwrap(); + support_map.evaluate() }; // compact encode the assignment. diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index bf0b2bf0da484..56a7e4388927a 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -1788,6 +1788,7 @@ fn bond_with_duplicate_vote_should_be_ignored_by_npos_election() { .minimum_validator_count(1) .build() .execute_with(|| { + // disable the nominator assert_ok!(Staking::chill(Origin::signed(100))); // make stakes equal. @@ -1808,6 +1809,7 @@ fn bond_with_duplicate_vote_should_be_ignored_by_npos_election() { } assert_ok!(Staking::bond(Origin::signed(1), 2, 1000, RewardDestination::Controller)); + // 11 should not be elected. All of these count as ONE vote. assert_ok!(Staking::nominate(Origin::signed(2), vec![11, 11, 11, 21, 31,])); assert_ok!(Staking::bond(Origin::signed(3), 4, 1000, RewardDestination::Controller)); @@ -1861,7 +1863,6 @@ fn bond_with_duplicate_vote_should_be_ignored_by_npos_election_elected() { assert_ok!(Staking::nominate(Origin::signed(4), vec![21, 31])); // winners should be 21 and 31. Otherwise this election is taking duplicates into account. - let sp_npos_elections::ElectionResult { winners, assignments, @@ -2004,7 +2005,7 @@ fn reward_from_authorship_event_handler_works() { fn add_reward_points_fns_works() { ExtBuilder::default().build_and_execute(|| { // Not mandatory but must be coherent with rewards - assert_eq!(Session::validators(), vec![21, 11]); + assert_eq_uvec!(Session::validators(), vec![21, 11]); >::reward_by_ids(vec![ (21, 1), @@ -3023,7 +3024,7 @@ mod offchain_election { assert_eq!(Staking::era_election_status(), ElectionStatus::Open(37)); run_to_block(40); - assert_session_era!(4, 0); + assert_session_era!(4, 1); assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); @@ -3041,7 +3042,7 @@ mod offchain_election { assert!(Staking::snapshot_validators().is_some()); run_to_block(90); - assert_session_era!(9, 1); + assert_session_era!(9, 2); assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); @@ -4950,3 +4951,92 @@ fn cannot_bond_extra_to_lower_than_ed() { ); }) } + +mod election_data_provider { + use super::*; + use sp_election_providers::ElectionDataProvider; + + #[test] + fn voters_include_self_vote() { + ExtBuilder::default().nominate(false).build().execute_with(|| { + assert!(>::iter().map(|(x, _)| x).all(|v| Staking::voters() + .into_iter() + .find(|(w, _, t)| { v == *w && t[0] == *w }) + .is_some())) + }) + } + + #[test] + fn voters_exclude_slashed() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); + assert_eq!( + >::voters() + .iter() + .find(|x| x.0 == 101) + .unwrap() + .2, + vec![11, 21] + ); + + start_active_era(1); + add_slash(&11); + + // 11 is gone. + start_active_era(2); + assert_eq!( + >::voters() + .iter() + .find(|x| x.0 == 101) + .unwrap() + .2, + vec![21] + ); + + // resubmit and it is back + assert_ok!(Staking::nominate(Origin::signed(100), vec![11, 21])); + assert_eq!( + >::voters() + .iter() + .find(|x| x.0 == 101) + .unwrap() + .2, + vec![11, 21] + ); + }) + } + + #[test] + fn estimate_next_election_works() { + ExtBuilder::default().session_per_era(5).period(5).build().execute_with(|| { + // first session is always length 0. + for b in 1..20 { + run_to_block(b); + assert_eq!(Staking::next_election_prediction(System::block_number()), 20); + } + + // election + run_to_block(20); + assert_eq!(Staking::next_election_prediction(System::block_number()), 45); + assert_eq!(staking_events().len(), 1); + assert_eq!( + *staking_events().last().unwrap(), + RawEvent::StakingElection(ElectionCompute::OnChain) + ); + + for b in 21..45 { + run_to_block(b); + assert_eq!(Staking::next_election_prediction(System::block_number()), 45); + } + + // election + run_to_block(45); + assert_eq!(Staking::next_election_prediction(System::block_number()), 70); + assert_eq!(staking_events().len(), 3); + assert_eq!( + *staking_events().last().unwrap(), + RawEvent::StakingElection(ElectionCompute::OnChain) + ); + }) + } +} diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 0b2d3bceea5ec..eabe93d2ae008 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -347,22 +347,23 @@ impl Happened for () { /// be the default value), or where the account is being removed or reset back to the default value /// where previously it did exist (though may have been in a default state). This works well with /// system module's `CallOnCreatedAccount` and `CallKillAccount`. -pub struct StorageMapShim< - S, - Created, - Removed, - K, - T ->(sp_std::marker::PhantomData<(S, Created, Removed, K, T)>); +pub struct StorageMapShim( + sp_std::marker::PhantomData<(S, Created, Removed, K, T)>, +); impl< - S: StorageMap, - Created: Happened, - Removed: Happened, - K: FullCodec, - T: FullCodec, -> StoredMap for StorageMapShim { - fn get(k: &K) -> T { S::get(k) } - fn is_explicit(k: &K) -> bool { S::contains_key(k) } + S: StorageMap, + Created: Happened, + Removed: Happened, + K: FullCodec, + T: FullCodec, + > StoredMap for StorageMapShim +{ + fn get(k: &K) -> T { + S::get(k) + } + fn is_explicit(k: &K) -> bool { + S::contains_key(k) + } fn insert(k: &K, t: T) { let existed = S::contains_key(&k); S::insert(k, t); @@ -413,10 +414,16 @@ impl< } } -/// Something that can estimate at which block the next session rotation will happen. This should -/// be the same logical unit that dictates `ShouldEndSession` to the session module. No Assumptions -/// are made about the scheduling of the sessions. +/// Something that can estimate at which block the next session rotation will happen. +/// +/// This should be the same logical unit that dictates `ShouldEndSession` to the session module. No +/// Assumptions are made about the scheduling of the sessions. pub trait EstimateNextSessionRotation { + /// Return the average length of a session. + /// + /// This may or may not be accurate. + fn average_session_length() -> BlockNumber; + /// Return the block number at which the next session rotation is estimated to happen. /// /// None should be returned if the estimation fails to come to an answer @@ -426,7 +433,11 @@ pub trait EstimateNextSessionRotation { fn weight(now: BlockNumber) -> Weight; } -impl EstimateNextSessionRotation for () { +impl EstimateNextSessionRotation for () { + fn average_session_length() -> BlockNumber { + Default::default() + } + fn estimate_next_session_rotation(_: BlockNumber) -> Option { Default::default() } @@ -436,9 +447,15 @@ impl EstimateNextSessionRotation for () { } } -/// Something that can estimate at which block the next `new_session` will be triggered. This must -/// always be implemented by the session module. +/// Something that can estimate at which block the next `new_session` will be triggered. +/// +/// This must always be implemented by the session module. pub trait EstimateNextNewSession { + /// Return the average length of a session. + /// + /// This may or may not be accurate. + fn average_session_length() -> BlockNumber; + /// Return the block number at which the next new session is estimated to happen. fn estimate_next_new_session(now: BlockNumber) -> Option; @@ -446,7 +463,11 @@ pub trait EstimateNextNewSession { fn weight(now: BlockNumber) -> Weight; } -impl EstimateNextNewSession for () { +impl EstimateNextNewSession for () { + fn average_session_length() -> BlockNumber { + Default::default() + } + fn estimate_next_new_session(_: BlockNumber) -> Option { Default::default() } From 632e107d46f9493831e3beb4c5036e702cd4e03a Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 15 Jan 2021 20:57:38 +0000 Subject: [PATCH 03/57] Undo bad formattings. --- frame/staking/src/mock.rs | 26 +++++++++++++------------- frame/support/src/traits.rs | 31 +++++++++++++++---------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index d01f8b59a681f..46f1c16c971e0 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -997,19 +997,19 @@ pub(crate) fn make_all_reward_payment(era: EraIndex) { macro_rules! assert_session_era { ($session:expr, $era:expr) => { assert_eq!( - Session::current_index(), - $session, - "wrong session {} != {}", - Session::current_index(), - $session, - ); - assert_eq!( - Staking::current_era().unwrap(), - $era, - "wrong current era {} != {}", - Staking::current_era().unwrap(), - $era, - ); + Session::current_index(), + $session, + "wrong session {} != {}", + Session::current_index(), + $session, + ); + assert_eq!( + Staking::current_era().unwrap(), + $era, + "wrong current era {} != {}", + Staking::current_era().unwrap(), + $era, + ); }; } diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index eabe93d2ae008..d4dbdbf5ef115 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -347,23 +347,22 @@ impl Happened for () { /// be the default value), or where the account is being removed or reset back to the default value /// where previously it did exist (though may have been in a default state). This works well with /// system module's `CallOnCreatedAccount` and `CallKillAccount`. -pub struct StorageMapShim( - sp_std::marker::PhantomData<(S, Created, Removed, K, T)>, -); +pub struct StorageMapShim< + S, + Created, + Removed, + K, + T +>(sp_std::marker::PhantomData<(S, Created, Removed, K, T)>); impl< - S: StorageMap, - Created: Happened, - Removed: Happened, - K: FullCodec, - T: FullCodec, - > StoredMap for StorageMapShim -{ - fn get(k: &K) -> T { - S::get(k) - } - fn is_explicit(k: &K) -> bool { - S::contains_key(k) - } + S: StorageMap, + Created: Happened, + Removed: Happened, + K: FullCodec, + T: FullCodec, +> StoredMap for StorageMapShim { + fn get(k: &K) -> T { S::get(k) } + fn is_explicit(k: &K) -> bool { S::contains_key(k) } fn insert(k: &K, t: T) { let existed = S::contains_key(&k); S::insert(k, t); From b4fc5e1701ad9960fc90fe0063fb76136c98392f Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 18 Jan 2021 13:21:18 +0000 Subject: [PATCH 04/57] some formatting cleanup. --- bin/node/runtime/src/lib.rs | 6 ++--- frame/session/src/lib.rs | 24 ++++++++------------ primitives/npos-elections/compact/src/lib.rs | 1 + primitives/npos-elections/src/phragmen.rs | 5 +--- primitives/npos-elections/src/tests.rs | 8 +------ 5 files changed, 15 insertions(+), 29 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index bc3526af195f7..d5f0d663e0081 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -504,9 +504,9 @@ impl pallet_staking::Config for Runtime { use pallet_election_provider_multi_phase::FallbackStrategy; parameter_types! { - // phase durations - pub const SignedPhase: u32 = 100; - pub const UnsignedPhase: u32 = 100; + // phase durations. 1/4 of the last session for each. + pub const SignedPhase: u32 = EPOCH_DURATION_IN_BLOCKS / 4; + pub const UnsignedPhase: u32 = EPOCH_DURATION_IN_BLOCKS / 4; // fallback: no need to do on-chain phragmen initially. pub const Fallback: FallbackStrategy = FallbackStrategy::Nothing; diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index 6f619129d8eaa..1ee4d123aa79b 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -158,25 +158,15 @@ impl< } impl< - BlockNumber: Rem - + Sub - + Zero - + PartialOrd - + Saturating - + Clone, - Period: Get, - Offset: Get, - > EstimateNextSessionRotation for PeriodicSessions -{ - fn average_session_length() -> BlockNumber { - Period::get() - } - + BlockNumber: Rem + Sub + Zero + PartialOrd + Saturating + Clone, + Period: Get, + Offset: Get, +> EstimateNextSessionRotation for PeriodicSessions { fn estimate_next_session_rotation(now: BlockNumber) -> Option { let offset = Offset::get(); let period = Period::get(); Some(if now > offset { - let block_after_last_session = (now.clone() - offset.clone()) % period.clone(); + let block_after_last_session = (now.clone() - offset) % period.clone(); if block_after_last_session > Zero::zero() { now.saturating_add(period.saturating_sub(block_after_last_session)) } else { @@ -198,6 +188,10 @@ impl< // reasonable to come back here and properly calculate the weight of this function. 0 } + + fn average_session_length() -> BlockNumber { + Period::get() + } } /// A trait for managing creation of new validator set. diff --git a/primitives/npos-elections/compact/src/lib.rs b/primitives/npos-elections/compact/src/lib.rs index 01b59c1a43549..191998a341924 100644 --- a/primitives/npos-elections/compact/src/lib.rs +++ b/primitives/npos-elections/compact/src/lib.rs @@ -362,6 +362,7 @@ fn imports() -> Result { } } } + struct SolutionDef { vis: syn::Visibility, ident: syn::Ident, diff --git a/primitives/npos-elections/src/phragmen.rs b/primitives/npos-elections/src/phragmen.rs index bdd64503a56d7..24a6b81af31a7 100644 --- a/primitives/npos-elections/src/phragmen.rs +++ b/primitives/npos-elections/src/phragmen.rs @@ -107,10 +107,7 @@ where .map(|w_ptr| (w_ptr.borrow().who.clone(), w_ptr.borrow().backed_stake)) .collect(); - Ok(ElectionResult { - winners, - assignments, - }) + Ok(ElectionResult { winners, assignments }) } /// Core implementation of seq-phragmen. diff --git a/primitives/npos-elections/src/tests.rs b/primitives/npos-elections/src/tests.rs index a6c50b2fcba30..bc148f118ce42 100644 --- a/primitives/npos-elections/src/tests.rs +++ b/primitives/npos-elections/src/tests.rs @@ -59,13 +59,7 @@ fn float_phragmen_poc_works() { &_Support { own: 0.0, total: 35.0, others: vec![(20u64, 20.0), (30u64, 15.0)] } ); - equalize_float( - phragmen_result.assignments, - &mut support_map, - 0.0, - 2, - stake_of, - ); + equalize_float(phragmen_result.assignments, &mut support_map, 0.0, 2, stake_of); assert_eq!( support_map.get(&2).unwrap(), From cc26881f03f799fe49f5f96f657073cd9d597ff8 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 18 Jan 2021 15:44:08 +0000 Subject: [PATCH 05/57] Small self-cleanup. --- .../election-provider-multi-phase/src/lib.rs | 130 ++++++++---------- .../election-provider-multi-phase/src/mock.rs | 13 +- .../src/unsigned.rs | 66 ++++++--- 3 files changed, 112 insertions(+), 97 deletions(-) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 79112297f869f..3028ee9ee9b06 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -15,10 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! # Two phase, offchain election provider pallet. +//! # Multi phase, offchain election provider pallet. //! -//! As the name suggests, this election-provider has two distinct phases (see [`Phase`]), signed and -//! unsigned. +//! Currently, this election-provider has two distinct phases (see [`Phase`]), **signed** and +//! **unsigned**. //! //! ## Phases //! @@ -35,13 +35,15 @@ //! ``` //! //! Note that the unsigned phase starts [`pallet::Config::UnsignedPhase`] blocks before the -//! `next_election_prediction`, but only ends when a call to [`ElectionProvider::elect`] happens. +//! `next_election_prediction`, but only ends when a call to [`ElectionProvider::elect`] happens. If +//! no `elect` happens, the signed phase is extended. //! //! > Given this, it is rather important for the user of this pallet to ensure it always terminates //! election via `elect` before requesting a new one. //! //! Each of the phases can be disabled by essentially setting their length to zero. If both phases -//! have length zero, then the pallet essentially runs only the on-chain backup. +//! have length zero, then the pallet essentially runs only the fallback strategy, denoted by +//! [`Config::FallbackStrategy`]. //! //! ### Signed Phase //! @@ -102,6 +104,10 @@ //! than the best queued one (see [`pallet::Config::SolutionImprovementThreshold`]) and will limit //! the weigh of the solution to [`pallet::Config::MinerMaxWeight`]. //! +//! 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 +//! active. +//! //! ### Fallback //! //! If we reach the end of both phases (i.e. call to [`ElectionProvider::elect`] happens) and no @@ -165,19 +171,19 @@ //! on-chain election provider as fallback, or special _noop_ fallback that simply returns an error, //! thus replicating [`FallbackStrategy::Nothing`]. //! -//! **Score based on size**: We should always prioritize small solutions over bigger ones, if there -//! is a tie. Even more harsh should be to enforce the bound of the `reduce` algorithm. +//! **Score based on (byte) size**: We should always prioritize small solutions over bigger ones, if +//! there is a tie. Even more harsh should be to enforce the bound of the `reduce` algorithm. #![cfg_attr(not(feature = "std"), no_std)] -use codec::{Decode, Encode, HasCompact}; +use codec::{Decode, Encode}; use frame_support::{ dispatch::DispatchResultWithPostInfo, ensure, traits::{Currency, Get, ReservableCurrency}, weights::Weight, }; -use frame_system::{ensure_none, ensure_signed, offchain::SendTransactionTypes}; +use frame_system::{ensure_none, offchain::SendTransactionTypes}; use sp_election_providers::{ElectionDataProvider, ElectionProvider, onchain}; use sp_npos_elections::{ assignment_ratio_to_staked_normalized, is_score_better, CompactSolution, ElectionScore, @@ -205,20 +211,11 @@ pub mod helpers; const LOG_TARGET: &'static str = "election-provider"; -// for the helper macros -#[doc(hidden)] -pub use sp_runtime::traits::UniqueSaturatedInto; -#[doc(hidden)] -pub use sp_std; - pub mod unsigned; pub mod weights; use weights::WeightInfo; -// pub mod signed; -// use signed::SignedSubmission; - /// The compact solution type used by this crate. pub type CompactOf = ::CompactSolution; @@ -231,6 +228,7 @@ pub type CompactAccuracyOf = as CompactSolution>::Accuracy; /// The accuracy of the election, when computed on-chain. Equal to [`Config::OnChainAccuracy`]. pub type OnChainAccuracyOf = ::OnChainAccuracy; +/// Wrapper type that implements the configurations needed for the on-chain backup. struct OnChainConfig(sp_std::marker::PhantomData) where ExtendedBalance: From>>, @@ -310,7 +308,7 @@ impl Phase { } } -/// A configuration for the module to indicate what should happen in the case of a fallback i.e. +/// A configuration for the pallet to indicate what should happen in the case of a fallback i.e. /// reaching a call to `elect` with no good solution. #[cfg_attr(test, derive(Clone))] pub enum FallbackStrategy { @@ -379,7 +377,7 @@ pub struct ReadySolution { compute: ElectionCompute, } -/// Solution size of the election. +/// Size of the snapshot from which the solution was derived. /// /// This is needed for proper weight calculation. #[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug, Default)] @@ -397,10 +395,9 @@ pub struct SolutionSize { } /// A snapshot of all the data that is needed for en entire round. They are provided by -/// [`ElectionDataProvider`] at the beginning of the signed phase (or the unsigned phase, if signed -/// phase is non-existent) and are kept around until the round is finished. +/// [`ElectionDataProvider`] and are kept around until the round is finished. /// -/// These are stored together because they are often times accessed together. +/// These are stored together because they are often accessed together. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, Default)] pub struct RoundSnapshot { /// All of the voters. @@ -433,16 +430,16 @@ pub enum ElectionError { Feasibility(FeasibilityError), /// An error in the on-chain fallback. OnChainFallback(onchain::Error), - /// No fallback is configured + /// No fallback is configured. NoFallbackConfigured, /// An internal error in the NPoS elections crate. NposElections(sp_npos_elections::Error), /// Snapshot data was unavailable unexpectedly. SnapshotUnAvailable, /// Submitting a transaction to the pool failed. - /// - /// This can only happen in the unsigned phase. PoolSubmissionFailed, + /// The pre-dispatch checks failed for the mined solution. + PreDispatchChecksFailed, } impl From for ElectionError { @@ -470,7 +467,8 @@ pub enum FeasibilityError { WrongWinnerCount, /// The snapshot is not available. /// - /// This must be an internal error of the chain. + /// Kinda defensive: The pallet should technically never attempt to do a feasibility check when + /// no snapshot is present. SnapshotUnavailable, /// Internal error from the election crate. NposElection(sp_npos_elections::Error), @@ -520,12 +518,12 @@ pub mod pallet { type SignedPhase: Get; /// The minimum amount of improvement to the solution score that defines a solution as - /// "better". + /// "better" (in any phase). #[pallet::constant] type SolutionImprovementThreshold: Get; /// The priority of the unsigned transaction submitted in the unsigned-phase - type UnsignedPriority: Get; + type MinerTxPriority: Get; /// Maximum number of iteration of balancing that will be executed in the embedded miner of /// the pallet. type MinerMaxIterations: Get; @@ -586,15 +584,17 @@ pub mod pallet { { let (need_snapshot, enabled, additional) = if current_phase == Phase::Signed { // followed by a signed phase: close the signed phase, no need for snapshot. - // TODO + // NOTE: SIGNED_PHASE (false, true, Weight::zero()) } else { - // no signed phase + // no signed phase: create a new snapshot, definitely `enable` the unsigned + // phase. (true, true, Weight::zero()) }; Self::on_initialize_open_unsigned(need_snapshot, enabled, now); log!(info, "Starting unsigned phase({}) at #{:?}.", enabled, now); + let base_weight = if need_snapshot { T::WeightInfo::on_initialize_open_unsigned_with_snapshot() } else { @@ -616,7 +616,7 @@ pub mod pallet { } Err(e) => log!(error, "error while submitting transaction in OCW: {:?}", e), }, - Err(why) => log!(error, "Error in unsigned offchain worker: {:?}", why), + Err(why) => log!(warn, "denied offchain worker: {:?}", why), } } } @@ -658,16 +658,16 @@ pub mod pallet { /// /// The dispatch origin fo this call must be __none__. /// - /// This submission is checked on the fly, thus it is likely yo be more limited and smaller. - /// Moreover, this unsigned solution is only validated when submitted to the pool from the - /// local process. Effectively, this means that only active validators can submit this - /// transaction when authoring a block. + /// This submission is checked on the fly. Moreover, this unsigned solution is only + /// validated when submitted to the pool from the **local** node. Effectively, this means + /// that only active validators can submit this transaction when authoring a block (similar + /// to an inherent). /// /// To prevent any incorrect solution (and thus wasted time/weight), this transaction will - /// panic if the solution submitted by the validator is invalid, effectively putting their - /// authoring reward at risk. + /// panic if the solution submitted by the validator is invalid in any way, effectively + /// putting their authoring reward at risk. /// - /// No deposit or reward is associated with this. + /// No deposit or reward is associated with this submission. #[pallet::weight(T::WeightInfo::submit_unsigned( witness.voters, witness.targets, @@ -683,8 +683,7 @@ pub mod pallet { let error_message = "Invalid unsigned submission must produce invalid block and \ deprive validator from their authoring reward."; - // check phase and score. - // NOTE: since we do this in pre-dispatch, we can just ignore it here. + // NOTE: since we do this in pre-dispatch, we _could_ just ignore it here. Self::unsigned_pre_dispatch_checks(&solution).expect(error_message); // ensure witness was correct. @@ -733,6 +732,7 @@ pub mod pallet { UnsignedPhaseStarted(u32), } + /// Error of the pallet that can be returned in response to dispatches. #[pallet::error] pub enum Error { /// Submission was too early. @@ -781,15 +781,14 @@ pub mod pallet { ValidTransaction::with_tag_prefix("OffchainElection") // The higher the score[0], the better a solution is. .priority( - T::UnsignedPriority::get() - .saturating_add(solution.score[0].saturated_into()), + T::MinerTxPriority::get().saturating_add(solution.score[0].saturated_into()), ) // used to deduplicate unsigned solutions: each validator should produce one // solution per round at most, and solutions are not propagate. .and_provides(solution.round) // transaction should stay in the pool for the duration of the unsigned phase. .longevity(T::UnsignedPhase::get().saturated_into::()) - // We don't propagate this. This can never the validated at a remote node. + // We don't propagate this. This can never be validated at a remote node. .propagate(false) .build() } else { @@ -816,7 +815,7 @@ pub mod pallet { /// Internal counter for the number of rounds. /// /// This is useful for de-duplication of transactions submitted to the pool, and general - /// diagnostics of the module. + /// diagnostics of the pallet. /// /// This is merely incremented once per every time that an upstream `elect` is called. #[pallet::storage] @@ -828,7 +827,7 @@ pub mod pallet { #[pallet::getter(fn current_phase)] pub type CurrentPhase = StorageValue<_, Phase, ValueQuery>; - /// Current best solution, signed or unsigned. + /// Current best solution, signed or unsigned, queued to be returned upon `elect`. #[pallet::storage] #[pallet::getter(fn queued_solution)] pub type QueuedSolution = StorageValue<_, ReadySolution>; @@ -867,7 +866,7 @@ where /// Logic for `::on_initialize` when signed phase is being opened. /// /// This is decoupled for easy weight calculation. - pub fn on_initialize_open_signed() { + pub(crate) fn on_initialize_open_signed() { >::put(Phase::Signed); Self::create_snapshot(); Self::deposit_event(Event::SignedPhaseStarted(Self::round())); @@ -877,7 +876,7 @@ where /// /// This is decoupled for easy weight calculation. Note that the default weight benchmark of /// this function will assume an empty signed queue for `finalize_signed_phase`. - pub fn on_initialize_open_unsigned( + pub(crate) fn on_initialize_open_unsigned( need_snapshot: bool, enabled: bool, now: T::BlockNumber, @@ -888,7 +887,6 @@ where Self::create_snapshot(); } - // for now always start the unsigned phase. >::put(Phase::Unsigned((enabled, now))); Self::deposit_event(Event::UnsignedPhaseStarted(Self::round())); } @@ -898,7 +896,7 @@ where /// 1. [`SnapshotMetadata`] /// 2. [`RoundSnapshot`] /// 3. [`DesiredTargets`] - pub fn create_snapshot() { + pub(crate) fn create_snapshot() { // if any of them don't exist, create all of them. This is a bit conservative. let targets = T::DataProvider::targets(); let voters = T::DataProvider::voters(); @@ -912,15 +910,14 @@ where >::put(RoundSnapshot { voters, targets }); } + /// Kill everything created by [`Pallet::create_snapshot`]. + pub(crate) fn kill_snapshot() { + >::kill(); + >::kill(); + >::kill(); + } + /// Checks the feasibility of a solution. - /// - /// This checks the solution for the following: - /// - /// 0. **all** of the used indices must be correct. - /// 1. present correct number of winners. - /// 2. any assignment is checked to match with [Snapshot::voters]. - /// 3. for each assignment, the check of `ElectionDataProvider` is also examined. - /// 4. the claimed score is valid. fn feasibility_check( solution: RawSolution>, compute: ElectionCompute, @@ -938,7 +935,7 @@ where // NOTE: this is a bit of duplicate, but we keep it around for veracity. The unsigned path // already checked this in `unsigned_per_dispatch_checks`. The signed path *could* check it - // upon arrival. + // upon arrival, thus we would then remove it here. Given overlay it is cheap anyhow ensure!(winners.len() as u32 == desired_targets, FeasibilityError::WrongWinnerCount,); // read the entire snapshot. @@ -957,8 +954,7 @@ where .map(|i| target_at(i).ok_or(FeasibilityError::InvalidWinner)) .collect::, FeasibilityError>>()?; - // Then convert compact -> Assignment. This will fail if any of the indices are gibberish. - // that winner indices are already checked. + // Then convert compact -> assignment. This will fail if any of the indices are gibberish. let assignments = compact .into_assignment(voter_at, target_at) .map_err::(Into::into)?; @@ -993,6 +989,7 @@ where // This might fail if the normalization fails. Very unlikely. See `integrity_test`. let staked_assignments = assignment_ratio_to_staked_normalized(assignments, stake_of) .map_err::(Into::into)?; + // This might fail if one of the voter edges is pointing to a non-winner, which is not // really possible anymore because all the winners come from the same `compact`. let supports = sp_npos_elections::to_supports(&winners, &staked_assignments) @@ -1018,9 +1015,7 @@ where >::put(Phase::Off); // kill snapshots - >::kill(); - >::kill(); - >::kill(); + Self::kill_snapshot(); } /// On-chain fallback of election. @@ -1036,10 +1031,6 @@ where } fn do_elect() -> Result, ElectionError> { - // NOTE: SignedSubmission is guaranteed to be drained by the end of the signed phase too, - // thus no need for a manual cleanup: - // TODO - // debug_assert!(Self::signed_submissions().is_empty()); >::take() .map_or_else( || match T::Fallback::get() { @@ -1073,7 +1064,6 @@ where fn elect() -> Result, Self::Error> { let outcome = Self::do_elect(); - // cleanup. Self::post_elect(); outcome } @@ -1144,7 +1134,7 @@ mod feasibility_check { assert_noop!( TwoPhase::feasibility_check(solution, COMPUTE), - FeasibilityError::WrongWinnerCount + FeasibilityError::WrongWinnerCount, ); }) } diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 71102f3e6e526..6fcebe7f08eaf 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -93,11 +93,6 @@ pub fn roll_to_with_ocw(n: u64) { } } -/// Get the free and reserved balance of some account. -pub fn balances(who: &AccountId) -> (Balance, Balance) { - (Balances::free_balance(who), Balances::reserved_balance(who)) -} - /// Spit out a verifiable raw solution. /// /// This is a good example of what an offchain miner would do. @@ -204,7 +199,7 @@ parameter_types! { pub static MaxSignedSubmissions: u32 = 5; pub static MinerMaxIterations: u32 = 5; - pub static UnsignedPriority: u64 = 100; + pub static MinerTxPriority: u64 = 100; pub static SolutionImprovementThreshold: Perbill = Perbill::zero(); pub static MinerMaxWeight: Weight = BlockWeights::get().max_block; pub static MockWeightInfo: bool = false; @@ -272,7 +267,7 @@ impl crate::Config for Runtime { type SolutionImprovementThreshold = SolutionImprovementThreshold; type MinerMaxIterations = MinerMaxIterations; type MinerMaxWeight = MinerMaxWeight; - type UnsignedPriority = UnsignedPriority; + type MinerTxPriority = MinerTxPriority; type DataProvider = StakingMock; type WeightInfo = DualMockWeightInfo; type BenchmarkingConfig = (); @@ -311,8 +306,8 @@ impl ElectionDataProvider for StakingMock { } impl ExtBuilder { - pub fn unsigned_priority(self, p: u64) -> Self { - ::set(p); + pub fn miner_tx_priority(self, p: u64) -> Self { + ::set(p); self } pub fn solution_improvement_threshold(self, p: Perbill) -> Self { diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index e922dc32dfa34..4907d3f0e761a 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -20,14 +20,20 @@ use crate::*; use frame_support::dispatch::DispatchResult; use frame_system::offchain::SubmitTransaction; -use sp_npos_elections::{seq_phragmen, CompactSolution, ElectionResult}; +use sp_npos_elections::{ + seq_phragmen, CompactSolution, ElectionResult, assignment_ratio_to_staked_normalized, reduce, + assignment_staked_to_ratio_normalized, +}; use sp_runtime::{offchain::storage::StorageValueRef, traits::TrailingZeroInput}; use sp_std::cmp::Ordering; /// Storage key used to store the persistent offchain worker status. pub(crate) const OFFCHAIN_HEAD_DB: &[u8] = b"parity/multi-phase-unsigned-election/"; + /// The repeat threshold of the offchain worker. This means we won't run the offchain worker twice /// within a window of 5 blocks. +// TODO: this should go into config, and we should store the solution an repeat with this threshold +// until we can submit it, or if the election happened. Okay for now though pub(crate) const OFFCHAIN_REPEAT: u32 = 5; impl Pallet @@ -35,7 +41,7 @@ where ExtendedBalance: From>>, ExtendedBalance: From>>, { - /// Min a new npos solution. + /// Mine a new npos solution. pub fn mine_solution( iters: usize, ) -> Result<(RawSolution>, SolutionSize), ElectionError> { @@ -50,14 +56,15 @@ where Some((iters, 0)), ) .map_err(Into::into) - .and_then(|election_result| { - if election_result.winners.len() as u32 == desired_targets { - Ok(election_result) - } else { - Err(ElectionError::Feasibility(FeasibilityError::WrongWinnerCount)) - } - }) .and_then(Self::prepare_election_result) + .and_then(|(raw_solution, size)| { + Self::unsigned_pre_dispatch_checks(&raw_solution) + .map_err(|e| { + log!(warn, "pre-disaptch-checks failed for mined solution: {:?}", e); + ElectionError::PreDispatchChecksFailed + })?; + Ok((raw_solution, size)) + }) } /// Convert a raw solution from [`sp_npos_elections::ElectionResult`] to [`RawSolution`], which @@ -67,6 +74,9 @@ where pub fn prepare_election_result( election_result: ElectionResult>, ) -> Result<(RawSolution>, SolutionSize), ElectionError> { + // NOTE: This code path is generally not optimized as it is run offchain. Could use some at + // some point though. + // storage items. Note: we have already read this from storage, they must be in cache. let RoundSnapshot { voters, targets } = Self::snapshot().ok_or(ElectionError::SnapshotUnAvailable)?; @@ -83,13 +93,12 @@ where let ElectionResult { assignments, winners } = election_result; // convert to staked and reduce. - let mut staked = - sp_npos_elections::assignment_ratio_to_staked_normalized(assignments, &stake_of) - .map_err::(Into::into)?; + let mut staked = assignment_ratio_to_staked_normalized(assignments, &stake_of) + .map_err::(Into::into)?; sp_npos_elections::reduce(&mut staked); // convert back to ration and make compact. - let ratio = sp_npos_elections::assignment_staked_to_ratio_normalized(staked)?; + let ratio = assignment_staked_to_ratio_normalized(staked)?; let compact = >::from_assignment(ratio, &voter_index, &target_index)?; let size = SolutionSize { voters: voters.len() as u32, targets: targets.len() as u32 }; @@ -116,7 +125,8 @@ where /// Get a random number of iterations to run the balancing in the OCW. /// - /// Uses the offchain seed to generate a random number, maxed with `T::MinerMaxIterations`. + /// 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, @@ -315,11 +325,18 @@ where .map_err(|_| ElectionError::PoolSubmissionFailed) } + /// Do the basics checks that MUST happen during the validation and pre-dispatch of an unsigned + /// transaction. + /// + /// Can optionally also be called during dispatch, if needed. + /// + /// NOTE: Ideally, these tests should move more and more outside of this and more to the miner's + /// code, so that we do less and less storage reads here. pub(crate) fn unsigned_pre_dispatch_checks( solution: &RawSolution>, ) -> DispatchResult { // ensure solution is timely. Don't panic yet. This is a cheap check. - ensure!(Self::current_phase().is_unsigned_open(), Error::::EarlySubmission); + ensure!(Self::current_phase().is_unsigned_open(), Error::::EarlySubmission,); // ensure correct number of winners. ensure!( @@ -335,7 +352,7 @@ where q.score, T::SolutionImprovementThreshold::get() )), - Error::::WeakSubmission + Error::::WeakSubmission, ); Ok(()) @@ -476,6 +493,19 @@ mod tests { ) .is_ok()); assert!(::pre_dispatch(&call).is_ok()); + + // unsigned -- but not enabled. + >::put(Phase::Unsigned((false, 25))); + assert!(TwoPhase::current_phase().is_unsigned()); + assert!(matches!( + ::validate_unsigned(TransactionSource::Local, &call) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom(0)) + )); + assert!(matches!( + ::pre_dispatch(&call).unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom(0)) + )); }) } @@ -534,7 +564,7 @@ mod tests { #[test] fn priority_is_set() { - ExtBuilder::default().unsigned_priority(20).desired_targets(0).build_and_execute(|| { + ExtBuilder::default().miner_tx_priority(20).desired_targets(0).build_and_execute(|| { roll_to(25); assert!(TwoPhase::current_phase().is_unsigned()); @@ -650,7 +680,7 @@ mod tests { // mine seq_phragmen solution with 2 iters. assert_eq!( TwoPhase::mine_solution(2).unwrap_err(), - ElectionError::Feasibility(FeasibilityError::WrongWinnerCount), + ElectionError::PreDispatchChecksFailed, ); }) } From 75eca3f450076eee1e022945ce928a57e496412f Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 27 Jan 2021 09:33:39 +0000 Subject: [PATCH 06/57] Make it all build --- bin/node/runtime/src/lib.rs | 2 +- .../src/benchmarking.rs | 18 +--- .../src/helpers.rs | 60 +++---------- .../election-provider-multi-phase/src/lib.rs | 90 +++++++------------ .../election-provider-multi-phase/src/mock.rs | 2 +- .../src/unsigned.rs | 26 ++---- frame/staking/src/lib.rs | 5 +- 7 files changed, 62 insertions(+), 141 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 636e0b4b784cc..0636817e456d4 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -530,7 +530,7 @@ impl pallet_election_provider_multi_phase::Config for Runtime { type SolutionImprovementThreshold = MinSolutionScoreBump; type MinerMaxIterations = MinerMaxIterations; type MinerMaxWeight = MinerMaxWeight; - type UnsignedPriority = TwoPhaseUnsignedPriority; + type MinerTxPriority = TwoPhaseUnsignedPriority; type DataProvider = Staking; type OnChainAccuracy = Perbill; type CompactSolution = pallet_staking::CompactAssignments; diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index a7a4eed852850..4cd6bdf60a4ef 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -25,9 +25,8 @@ use frame_support::{assert_ok, traits::OnInitialize}; use frame_system::RawOrigin; use rand::{prelude::SliceRandom, rngs::SmallRng, SeedableRng}; use sp_election_providers::Assignment; -use sp_npos_elections::ExtendedBalance; -use sp_runtime::InnerOf; use sp_arithmetic::traits::One; +use sp_runtime::InnerOf; use sp_std::convert::TryInto; const SEED: u32 = 0; @@ -39,12 +38,7 @@ fn solution_with_size( size: SolutionSize, active_voters_count: u32, desired_targets: u32, -) -> RawSolution> -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, - > as sp_std::convert::TryFrom>::Error: sp_std::fmt::Debug, -{ +) -> RawSolution> { assert!(size.targets >= desired_targets, "must have enough targets"); assert!( size.targets >= (>::LIMIT * 2) as u32, @@ -129,7 +123,7 @@ where .iter() .map(|(voter, _stake, votes)| { let percent_per_edge: InnerOf> = - (100 / votes.len()).try_into().unwrap(); + (100 / votes.len()).try_into().unwrap_or_else(|_| panic!("failed to convert")); Assignment { who: voter.clone(), distribution: votes @@ -148,12 +142,6 @@ where } benchmarks! { - where_clause { - where ExtendedBalance: From>>, - > as sp_std::convert::TryFrom>::Error: sp_std::fmt::Debug, - ExtendedBalance: From>>, - } - on_initialize_nothing { assert!(>::current_phase().is_off()); }: { diff --git a/frame/election-provider-multi-phase/src/helpers.rs b/frame/election-provider-multi-phase/src/helpers.rs index da4a092653ae7..9b2f817f1ca09 100644 --- a/frame/election-provider-multi-phase/src/helpers.rs +++ b/frame/election-provider-multi-phase/src/helpers.rs @@ -17,11 +17,7 @@ //! Some helper functions/macros for this crate. -use super::{ - Config, VoteWeight, CompactVoterIndexOf, CompactTargetIndexOf, CompactAccuracyOf, - OnChainAccuracyOf, ExtendedBalance, -}; -use sp_runtime::InnerOf; +use super::{Config, VoteWeight, CompactVoterIndexOf, CompactTargetIndexOf}; use sp_std::{collections::btree_map::BTreeMap, convert::TryInto, boxed::Box, prelude::*}; #[macro_export] @@ -39,11 +35,7 @@ macro_rules! log { /// This can be used to efficiently build index getter closures. pub fn generate_voter_cache( snapshot: &Vec<(T::AccountId, VoteWeight, Vec)>, -) -> BTreeMap -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +) -> BTreeMap { let mut cache: BTreeMap = BTreeMap::new(); snapshot.iter().enumerate().for_each(|(i, (x, _, _))| { let _existed = cache.insert(x.clone(), i); @@ -64,11 +56,7 @@ where /// The snapshot must be the same is the one used to create `cache`. pub fn voter_index_fn( cache: &BTreeMap, -) -> Box Option> + '_> -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +) -> Box Option> + '_> { Box::new(move |who| { cache.get(who).and_then(|i| >>::try_into(*i).ok()) }) @@ -81,11 +69,7 @@ where /// The snapshot must be the same is the one used to create `cache`. pub fn voter_index_fn_usize( cache: &BTreeMap, -) -> Box Option + '_> -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +) -> Box Option + '_> { Box::new(move |who| cache.get(who).cloned()) } @@ -97,11 +81,7 @@ where /// Not meant to be used in production. pub fn voter_index_fn_linear( snapshot: &Vec<(T::AccountId, VoteWeight, Vec)>, -) -> Box Option> + '_> -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +) -> Box Option> + '_> { Box::new(move |who| { snapshot .iter() @@ -115,11 +95,7 @@ where /// The returning index type is the same as the one defined in [`T::CompactSolution::Target`]. pub fn target_index_fn_linear( snapshot: &Vec, -) -> Box Option> + '_> -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +) -> Box Option> + '_> { Box::new(move |who| { snapshot .iter() @@ -132,11 +108,7 @@ where /// account using a linearly indexible snapshot. pub fn voter_at_fn( snapshot: &Vec<(T::AccountId, VoteWeight, Vec)>, -) -> Box) -> Option + '_> -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +) -> Box) -> Option + '_> { Box::new(move |i| { as TryInto>::try_into(i) .ok() @@ -148,11 +120,7 @@ where /// account using a linearly indexible snapshot. pub fn target_at_fn( snapshot: &Vec, -) -> Box) -> Option + '_> -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +) -> Box) -> Option + '_> { Box::new(move |i| { as TryInto>::try_into(i) .ok() @@ -165,11 +133,7 @@ where /// This is not optimized and uses a linear search. pub fn stake_of_fn_linear( snapshot: &Vec<(T::AccountId, VoteWeight, Vec)>, -) -> Box VoteWeight + '_> -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +) -> Box VoteWeight + '_> { Box::new(move |who| { snapshot.iter().find(|(x, _, _)| x == who).map(|(_, x, _)| *x).unwrap_or_default() }) @@ -184,11 +148,7 @@ where pub fn stake_of_fn<'a, T: Config>( snapshot: &'a Vec<(T::AccountId, VoteWeight, Vec)>, cache: &'a BTreeMap, -) -> Box VoteWeight + 'a> -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +) -> Box VoteWeight + 'a> { Box::new(move |who| { if let Some(index) = cache.get(who) { snapshot.get(*index).map(|(_, x, _)| x).cloned().unwrap_or_default() diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 3028ee9ee9b06..f528f37a82fbf 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -173,6 +173,10 @@ //! //! **Score based on (byte) size**: We should always prioritize small solutions over bigger ones, if //! there is a tie. Even more harsh should be to enforce the bound of the `reduce` algorithm. +//! +//! **Offchain resubmit**: Essentially port https://github.com/paritytech/substrate/pull/7976 to +//! this pallet as well. The `OFFCHAIN_REPEAT` also needs to become an adjustable parameter of the +//! pallet. #![cfg_attr(not(feature = "std"), no_std)] @@ -187,14 +191,14 @@ use frame_system::{ensure_none, offchain::SendTransactionTypes}; use sp_election_providers::{ElectionDataProvider, ElectionProvider, onchain}; use sp_npos_elections::{ assignment_ratio_to_staked_normalized, is_score_better, CompactSolution, ElectionScore, - EvaluateSupport, ExtendedBalance, PerThing128, Supports, VoteWeight, + EvaluateSupport, PerThing128, Supports, VoteWeight, }; use sp_runtime::{ transaction_validity::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, }, - DispatchError, InnerOf, PerThing, Perbill, RuntimeDebug, SaturatedConversion, + DispatchError, PerThing, Perbill, RuntimeDebug, SaturatedConversion, }; use sp_std::prelude::*; use sp_arithmetic::{ @@ -229,15 +233,8 @@ pub type CompactAccuracyOf = as CompactSolution>::Accuracy; pub type OnChainAccuracyOf = ::OnChainAccuracy; /// Wrapper type that implements the configurations needed for the on-chain backup. -struct OnChainConfig(sp_std::marker::PhantomData) -where - ExtendedBalance: From>>, - ExtendedBalance: From>>; -impl onchain::Config for OnChainConfig -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +struct OnChainConfig(sp_std::marker::PhantomData); +impl onchain::Config for OnChainConfig { type AccountId = T::AccountId; type BlockNumber = T::BlockNumber; type Accuracy = T::OnChainAccuracy; @@ -498,11 +495,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::config] - pub trait Config: frame_system::Config + SendTransactionTypes> - where - ExtendedBalance: From>>, - ExtendedBalance: From>>, - { + pub trait Config: frame_system::Config + SendTransactionTypes> { type Event: From> + Into<::Event> + IsType<::Event>; @@ -559,11 +552,7 @@ pub mod pallet { } #[pallet::hooks] - impl Hooks> for Pallet - where - ExtendedBalance: From>>, - ExtendedBalance: From>>, - { + impl Hooks> for Pallet { fn on_initialize(now: T::BlockNumber) -> Weight { let next_election = T::DataProvider::next_election_prediction(now).max(now); @@ -609,7 +598,7 @@ pub mod pallet { fn offchain_worker(n: T::BlockNumber) { // We only run the OCW in the fist block of the unsigned phase. if Self::current_phase().is_unsigned_open_at(n) { - match Self::set_check_offchain_execution_status(n) { + match Self::try_acquire_offchain_lock(n) { Ok(_) => match Self::mine_and_submit() { Ok(_) => { log!(info, "successfully submitted a solution via OCW at block {:?}", n) @@ -633,15 +622,25 @@ pub mod pallet { let max_vote: usize = as CompactSolution>::LIMIT; // 1. Maximum sum of [ChainAccuracy; 16] must fit into `UpperOf`.. - let maximum_chain_accuracy: Vec>> = - (0..max_vote).map(|_| >::one().deconstruct().into()).collect(); + let maximum_chain_accuracy: Vec>> = (0..max_vote) + .map(|_| { + >>::from( + >::one().deconstruct(), + ) + }) + .collect(); let _: UpperOf> = maximum_chain_accuracy .iter() .fold(Zero::zero(), |acc, x| acc.checked_add(x).unwrap()); // 2. Maximum sum of [CompactAccuracy; 16] must fit into `UpperOf`. - let maximum_chain_accuracy: Vec>> = - (0..max_vote).map(|_| >::one().deconstruct().into()).collect(); + let maximum_chain_accuracy: Vec>> = (0..max_vote) + .map(|_| { + >>::from( + >::one().deconstruct(), + ) + }) + .collect(); let _: UpperOf> = maximum_chain_accuracy .iter() .fold(Zero::zero(), |acc, x| acc.checked_add(x).unwrap()); @@ -649,11 +648,7 @@ pub mod pallet { } #[pallet::call] - impl Pallet - where - ExtendedBalance: From>>, - ExtendedBalance: From>>, - { + impl Pallet { /// Submit a solution for the unsigned phase. /// /// The dispatch origin fo this call must be __none__. @@ -709,11 +704,7 @@ pub mod pallet { #[pallet::event] #[pallet::metadata(::AccountId = "AccountId")] #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event - where - ExtendedBalance: From>>, - ExtendedBalance: From>>, - { + pub enum Event { /// A solution was stored with the given compute. /// /// If the solution is signed, this means that it hasn't yet been processed. If the @@ -755,11 +746,7 @@ pub mod pallet { pub struct Origin(PhantomData); #[pallet::validate_unsigned] - impl ValidateUnsigned for Pallet - where - ExtendedBalance: From>>, - ExtendedBalance: From>>, - { + impl ValidateUnsigned for Pallet { type Call = Call; fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity { if let Call::submit_unsigned(solution, _) = call { @@ -781,7 +768,9 @@ pub mod pallet { ValidTransaction::with_tag_prefix("OffchainElection") // The higher the score[0], the better a solution is. .priority( - T::MinerTxPriority::get().saturating_add(solution.score[0].saturated_into()), + T::MinerTxPriority::get().saturating_add( + solution.score[0].saturated_into() + ), ) // used to deduplicate unsigned solutions: each validator should produce one // solution per round at most, and solutions are not propagate. @@ -858,11 +847,7 @@ pub mod pallet { pub struct Pallet(PhantomData); } -impl Pallet -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +impl Pallet { /// Logic for `::on_initialize` when signed phase is being opened. /// /// This is decoupled for easy weight calculation. @@ -1019,10 +1004,7 @@ where } /// On-chain fallback of election. - fn onchain_fallback() -> Result, ElectionError> - where - ExtendedBalance: From<::Inner>, - { + fn onchain_fallback() -> Result, ElectionError> { > as ElectionProvider< T::AccountId, T::BlockNumber, @@ -1054,11 +1036,7 @@ where } } -impl ElectionProvider for Pallet -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +impl ElectionProvider for Pallet { type Error = ElectionError; type DataProvider = T::DataProvider; diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 6fcebe7f08eaf..8f9c6b58d4268 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -38,7 +38,7 @@ use sp_npos_elections::{ }; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, Block as BlockT, IdentityLookup}, + traits::{BlakeTwo256, IdentityLookup}, PerU16, }; use std::sync::Arc; diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index 4907d3f0e761a..01a9fe3d6e4f0 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -21,7 +21,7 @@ use crate::*; use frame_support::dispatch::DispatchResult; use frame_system::offchain::SubmitTransaction; use sp_npos_elections::{ - seq_phragmen, CompactSolution, ElectionResult, assignment_ratio_to_staked_normalized, reduce, + seq_phragmen, CompactSolution, ElectionResult, assignment_ratio_to_staked_normalized, assignment_staked_to_ratio_normalized, }; use sp_runtime::{offchain::storage::StorageValueRef, traits::TrailingZeroInput}; @@ -32,15 +32,9 @@ pub(crate) const OFFCHAIN_HEAD_DB: &[u8] = b"parity/multi-phase-unsigned-electio /// The repeat threshold of the offchain worker. This means we won't run the offchain worker twice /// within a window of 5 blocks. -// TODO: this should go into config, and we should store the solution an repeat with this threshold -// until we can submit it, or if the election happened. Okay for now though pub(crate) const OFFCHAIN_REPEAT: u32 = 5; -impl Pallet -where - ExtendedBalance: From>>, - ExtendedBalance: From>>, -{ +impl Pallet { /// Mine a new npos solution. pub fn mine_solution( iters: usize, @@ -279,9 +273,7 @@ where /// don't run twice within a window of length [`OFFCHAIN_REPEAT`]. /// /// Returns `Ok(())` if offchain worker should happen, `Err(reason)` otherwise. - pub(crate) fn set_check_offchain_execution_status( - now: T::BlockNumber, - ) -> Result<(), &'static str> { + pub(crate) fn try_acquire_offchain_lock(now: T::BlockNumber) -> Result<(), &'static str> { let storage = StorageValueRef::persistent(&OFFCHAIN_HEAD_DB); let threshold = T::BlockNumber::from(OFFCHAIN_REPEAT); @@ -762,25 +754,25 @@ mod tests { assert!(TwoPhase::current_phase().is_unsigned()); // first execution -- okay. - assert!(TwoPhase::set_check_offchain_execution_status(25).is_ok()); + assert!(TwoPhase::try_acquire_offchain_lock(25).is_ok()); // next block: rejected. - assert!(TwoPhase::set_check_offchain_execution_status(26).is_err()); + assert!(TwoPhase::try_acquire_offchain_lock(26).is_err()); // allowed after `OFFCHAIN_REPEAT` - assert!(TwoPhase::set_check_offchain_execution_status((26 + OFFCHAIN_REPEAT).into()) + assert!(TwoPhase::try_acquire_offchain_lock((26 + OFFCHAIN_REPEAT).into()) .is_ok()); // a fork like situation: re-execute last 3. - assert!(TwoPhase::set_check_offchain_execution_status( + assert!(TwoPhase::try_acquire_offchain_lock( (26 + OFFCHAIN_REPEAT - 3).into() ) .is_err()); - assert!(TwoPhase::set_check_offchain_execution_status( + assert!(TwoPhase::try_acquire_offchain_lock( (26 + OFFCHAIN_REPEAT - 2).into() ) .is_err()); - assert!(TwoPhase::set_check_offchain_execution_status( + assert!(TwoPhase::try_acquire_offchain_lock( (26 + OFFCHAIN_REPEAT - 1).into() ) .is_err()); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 685b9fd380293..f7ea7d5f98b58 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -3381,7 +3381,10 @@ impl sp_election_providers::ElectionDataProvider, ) { targets.into_iter().for_each(|v| { - >::insert(v, ValidatorPrefs { commission: Perbill::zero() }); + >::insert( + v, + ValidatorPrefs { commission: Perbill::zero(), blocked: false }, + ); }); voters.into_iter().for_each(|(v, _s, t)| { From 8daec3a9e38a5ebe717a5c71f673fb35afd1ae9e Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 27 Jan 2021 10:14:56 +0000 Subject: [PATCH 07/57] self-review --- .../src/benchmarking.rs | 12 +- .../election-provider-multi-phase/src/lib.rs | 127 ++++++++---------- .../election-provider-multi-phase/src/mock.rs | 4 +- .../src/unsigned.rs | 67 +++++---- 4 files changed, 106 insertions(+), 104 deletions(-) diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index 4cd6bdf60a4ef..98a874967fc03 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -35,7 +35,7 @@ const SEED: u32 = 0; /// /// The snapshot is also created internally. fn solution_with_size( - size: SolutionSize, + size: SolutionOrSnapshotSize, active_voters_count: u32, desired_targets: u32, ) -> RawSolution> { @@ -102,9 +102,9 @@ fn solution_with_size( assert_eq!(all_voters.len() as u32, size.voters); assert_eq!(winners.len() as u32, desired_targets); - >::put(RoundSnapshotMetadata { - voters_len: all_voters.len() as u32, - targets_len: targets.len() as u32, + >::put(SolutionOrSnapshotSize { + voters: all_voters.len() as u32, + targets: targets.len() as u32, }); >::put(desired_targets); >::put(RoundSnapshot { voters: all_voters.clone(), targets: targets.clone() }); @@ -205,7 +205,7 @@ benchmarks! { // number of desired targets. Must be a subset of `t` component. let d in (T::BenchmarkingConfig::DESIRED_TARGETS[0]) .. T::BenchmarkingConfig::DESIRED_TARGETS[1]; - let witness = SolutionSize { voters: v, targets: t }; + let witness = SolutionOrSnapshotSize { voters: v, targets: t }; let raw_solution = solution_with_size::(witness, a, d); assert!(>::queued_solution().is_none()); @@ -227,7 +227,7 @@ benchmarks! { // number of desired targets. Must be a subset of `t` component. let d in (T::BenchmarkingConfig::DESIRED_TARGETS[0]) .. T::BenchmarkingConfig::DESIRED_TARGETS[1]; - let size = SolutionSize { voters: v, targets: t }; + let size = SolutionOrSnapshotSize { voters: v, targets: t }; let raw_solution = solution_with_size::(size, a, d); assert_eq!(raw_solution.compact.voter_count() as u32, a); diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index f528f37a82fbf..567b2aadb1de8 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -44,7 +44,6 @@ //! Each of the phases can be disabled by essentially setting their length to zero. If both phases //! have length zero, then the pallet essentially runs only the fallback strategy, denoted by //! [`Config::FallbackStrategy`]. -//! //! ### Signed Phase //! //! In the signed phase, solutions (of type [`RawSolution`]) are submitted and queued on chain. A @@ -137,6 +136,24 @@ //! possible, reducing solutions size/weight. The on-chain solution can use more space for accuracy, //! but should still be fast to prevent massively large blocks in case of a fallback. //! +//! ## Error types +//! +//! This pallet provides a verbose error system to ease future debugging and debugging. The +//! overall hierarchy of errors is as follows: +//! +//! 1. [`pallet::Error`]: These are the errors that can be returned in the dispatchables of the +//! pallet, either signed or unsigned. Since decomposition with nested enums is not possible +//! here, they are prefixed with the logical sub-system to which they belong. +//! 2. [`ElectionError`]: These are the errors that can be generated while the pallet is doing +//! something in automatic scenarios, such as `offchain_worker` or `on_initialize`. These errors +//! are helpful for logging and are thus nested as: +//! - [`ElectionError::Miner`]: wraps a [`unsigned::MinerError`]. +//! - [`ElectionError::Feasibility`]: wraps a [`FeasibilityError`]. +//! - [`ElectionError::OnChainFallback`]: wraps a [`sp_election_providers::onchain::Error`]. +//! +//! Note that there could be an overlap between these sub-errors. For example, A +//! `SnapshotUnavailable` can happen in both miner and feasibility check phase. +//! //! ## Future Plans //! //! **Challenge Phase**. We plan adding a third phase to the pallet, called the challenge phase. @@ -374,23 +391,6 @@ pub struct ReadySolution { compute: ElectionCompute, } -/// Size of the snapshot from which the solution was derived. -/// -/// This is needed for proper weight calculation. -#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug, Default)] -pub struct SolutionSize { - /// Number of all voters. - /// - /// This must match the on-chain snapshot. - #[codec(compact)] - voters: u32, - /// Number of all targets. - /// - /// This must match the on-chain snapshot. - #[codec(compact)] - targets: u32, -} - /// A snapshot of all the data that is needed for en entire round. They are provided by /// [`ElectionDataProvider`] and are kept around until the round is finished. /// @@ -403,40 +403,34 @@ pub struct RoundSnapshot { pub targets: Vec, } -/// Some metadata related to snapshot. +/// Encodes the length of a solution or a snapshot. /// -/// In this pallet, there are cases where we want to read the whole snapshot (voters, targets, -/// desired), and cases that we are interested in just the length of these values. The former favors -/// the snapshot to be stored in one struct (as it is now) while the latter prefers them to be -/// separate to enable the use of `decode_len`. This approach is a middle ground, storing the -/// snapshot as one struct, whilst storing the lengths separately. -#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, Default)] -pub struct RoundSnapshotMetadata { +/// This is stored automatically on-chain, and it contains the **size of the entire snapshot**. +/// This is also used in dispatchables as weight witness data and should **only contain the size of +/// the presented solution**, not the entire snapshot. +#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug, Default)] +pub struct SolutionOrSnapshotSize { /// The length of voters. - voters_len: u32, + #[codec(compact)] + voters: u32, /// The length of targets. - targets_len: u32, + #[codec(compact)] + targets: u32, } /// Internal errors of the pallet. /// /// Note that this is different from [`pallet::Error`]. -#[derive(RuntimeDebug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq)] pub enum ElectionError { - /// A feasibility error. + /// An error happened in the feasibility check sub-system. Feasibility(FeasibilityError), + /// An error in the miner (offchain) sub-system. + Miner(unsigned::MinerError), /// An error in the on-chain fallback. OnChainFallback(onchain::Error), - /// No fallback is configured. + /// No fallback is configured. This is a special case. NoFallbackConfigured, - /// An internal error in the NPoS elections crate. - NposElections(sp_npos_elections::Error), - /// Snapshot data was unavailable unexpectedly. - SnapshotUnAvailable, - /// Submitting a transaction to the pool failed. - PoolSubmissionFailed, - /// The pre-dispatch checks failed for the mined solution. - PreDispatchChecksFailed, } impl From for ElectionError { @@ -445,20 +439,20 @@ impl From for ElectionError { } } -impl From for ElectionError { - fn from(e: sp_npos_elections::Error) -> Self { - ElectionError::NposElections(e) - } -} - impl From for ElectionError { fn from(e: FeasibilityError) -> Self { ElectionError::Feasibility(e) } } +impl From for ElectionError { + fn from(e: unsigned::MinerError) -> Self { + ElectionError::Miner(e) + } +} + /// Errors that can happen in the feasibility check. -#[derive(RuntimeDebug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq)] pub enum FeasibilityError { /// Wrong number of winners presented. WrongWinnerCount, @@ -599,12 +593,10 @@ pub mod pallet { // We only run the OCW in the fist block of the unsigned phase. if Self::current_phase().is_unsigned_open_at(n) { match Self::try_acquire_offchain_lock(n) { - Ok(_) => match Self::mine_and_submit() { - Ok(_) => { - log!(info, "successfully submitted a solution via OCW at block {:?}", n) - } - Err(e) => log!(error, "error while submitting transaction in OCW: {:?}", e), - }, + Ok(_) => { + let outcome = Self::mine_and_submit().map_err(ElectionError::from); + log!(info, "miner exeuction done: {:?}", outcome); + } Err(why) => log!(warn, "denied offchain worker: {:?}", why), } } @@ -672,7 +664,7 @@ pub mod pallet { pub fn submit_unsigned( origin: OriginFor, solution: RawSolution>, - witness: SolutionSize, + witness: SolutionOrSnapshotSize, ) -> DispatchResultWithPostInfo { ensure_none(origin)?; let error_message = "Invalid unsigned submission must produce invalid block and \ @@ -682,12 +674,12 @@ pub mod pallet { Self::unsigned_pre_dispatch_checks(&solution).expect(error_message); // ensure witness was correct. - let RoundSnapshotMetadata { voters_len, targets_len } = + let SolutionOrSnapshotSize { voters, targets } = Self::snapshot_metadata().expect(error_message); // NOTE: we are asserting, not `ensure`ing -- we want to panic here. - assert!(voters_len as u32 == witness.voters, error_message); - assert!(targets_len as u32 == witness.targets, error_message); + assert!(voters as u32 == witness.voters, error_message); + assert!(targets as u32 == witness.targets, error_message); let ready = Self::feasibility_check(solution, ElectionCompute::Unsigned).expect(error_message); @@ -727,24 +719,15 @@ pub mod pallet { #[pallet::error] pub enum Error { /// Submission was too early. - EarlySubmission, + PreDispatchEarlySubmission, /// Wrong number of winners presented. - WrongWinnerCount, + PreDispatchWrongWinnerCount, /// Submission was too weak, score-wise. - WeakSubmission, - /// The queue was full, and the solution was not better than any of the existing ones. - QueueFull, - /// The origin failed to pay the deposit. - CannotPayDeposit, - /// witness data to dispatchable is invalid. - InvalidWitness, - /// The signed submission consumes too much weight - TooMuchWeight, + PreDispatchWeakSubmission, } #[pallet::origin] pub struct Origin(PhantomData); - #[pallet::validate_unsigned] impl ValidateUnsigned for Pallet { type Call = Call; @@ -840,7 +823,7 @@ pub mod pallet { /// Only exists when [`Snapshot`] is present. #[pallet::storage] #[pallet::getter(fn snapshot_metadata)] - pub type SnapshotMetadata = StorageValue<_, RoundSnapshotMetadata>; + pub type SnapshotMetadata = StorageValue<_, SolutionOrSnapshotSize>; #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] @@ -887,9 +870,9 @@ impl Pallet { let voters = T::DataProvider::voters(); let desired_targets = T::DataProvider::desired_targets(); - >::put(RoundSnapshotMetadata { - voters_len: voters.len() as u32, - targets_len: targets.len() as u32, + >::put(SolutionOrSnapshotSize { + voters: voters.len() as u32, + targets: targets.len() as u32, }); >::put(desired_targets); >::put(RoundSnapshot { voters, targets }); diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 8f9c6b58d4268..402886703a1e7 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -127,9 +127,9 @@ pub fn raw_solution() -> RawSolution> { RawSolution { compact, score, round } } -pub fn witness() -> SolutionSize { +pub fn witness() -> SolutionOrSnapshotSize { TwoPhase::snapshot() - .map(|snap| SolutionSize { + .map(|snap| SolutionOrSnapshotSize { voters: snap.voters.len() as u32, targets: snap.targets.len() as u32, }) diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index 01a9fe3d6e4f0..a796986bd12c9 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -34,14 +34,32 @@ pub(crate) const OFFCHAIN_HEAD_DB: &[u8] = b"parity/multi-phase-unsigned-electio /// within a window of 5 blocks. pub(crate) const OFFCHAIN_REPEAT: u32 = 5; +#[derive(Debug, Eq, PartialEq)] +pub enum MinerError { + /// An internal error in the NPoS elections crate. + NposElections(sp_npos_elections::Error), + /// Snapshot data was unavailable unexpectedly. + SnapshotUnAvailable, + /// Submitting a transaction to the pool failed. + PoolSubmissionFailed, + /// The pre-dispatch checks failed for the mined solution. + PreDispatchChecksFailed, +} + +impl From for MinerError { + fn from(e: sp_npos_elections::Error) -> Self { + MinerError::NposElections(e) + } +} + impl Pallet { /// Mine a new npos solution. pub fn mine_solution( iters: usize, - ) -> Result<(RawSolution>, SolutionSize), ElectionError> { + ) -> Result<(RawSolution>, SolutionOrSnapshotSize), MinerError> { let RoundSnapshot { voters, targets } = - Self::snapshot().ok_or(ElectionError::SnapshotUnAvailable)?; - let desired_targets = Self::desired_targets().ok_or(ElectionError::SnapshotUnAvailable)?; + Self::snapshot().ok_or(MinerError::SnapshotUnAvailable)?; + let desired_targets = Self::desired_targets().ok_or(MinerError::SnapshotUnAvailable)?; seq_phragmen::<_, CompactAccuracyOf>( desired_targets as usize, @@ -55,7 +73,7 @@ impl Pallet { Self::unsigned_pre_dispatch_checks(&raw_solution) .map_err(|e| { log!(warn, "pre-disaptch-checks failed for mined solution: {:?}", e); - ElectionError::PreDispatchChecksFailed + MinerError::PreDispatchChecksFailed })?; Ok((raw_solution, size)) }) @@ -67,14 +85,14 @@ impl Pallet { /// Will always reduce the solution as well. pub fn prepare_election_result( election_result: ElectionResult>, - ) -> Result<(RawSolution>, SolutionSize), ElectionError> { + ) -> Result<(RawSolution>, SolutionOrSnapshotSize), MinerError> { // NOTE: This code path is generally not optimized as it is run offchain. Could use some at // some point though. // storage items. Note: we have already read this from storage, they must be in cache. let RoundSnapshot { voters, targets } = - Self::snapshot().ok_or(ElectionError::SnapshotUnAvailable)?; - let desired_targets = Self::desired_targets().ok_or(ElectionError::SnapshotUnAvailable)?; + Self::snapshot().ok_or(MinerError::SnapshotUnAvailable)?; + let desired_targets = Self::desired_targets().ok_or(MinerError::SnapshotUnAvailable)?; // closures. let cache = helpers::generate_voter_cache::(&voters); @@ -88,14 +106,15 @@ impl Pallet { // convert to staked and reduce. let mut staked = assignment_ratio_to_staked_normalized(assignments, &stake_of) - .map_err::(Into::into)?; + .map_err::(Into::into)?; sp_npos_elections::reduce(&mut staked); // convert back to ration and make compact. let ratio = assignment_staked_to_ratio_normalized(staked)?; let compact = >::from_assignment(ratio, &voter_index, &target_index)?; - let size = SolutionSize { voters: voters.len() as u32, targets: targets.len() as u32 }; + let size = + SolutionOrSnapshotSize { voters: voters.len() as u32, targets: targets.len() as u32 }; let maximum_allowed_voters = Self::maximum_voter_for_weight::( desired_targets, size, @@ -154,7 +173,7 @@ impl Pallet { maximum_allowed_voters: u32, mut compact: CompactOf, nominator_index: FN, - ) -> Result, ElectionError> + ) -> Result, MinerError> where for<'r> FN: Fn(&'r T::AccountId) -> Option>, { @@ -162,7 +181,7 @@ impl Pallet { Some(to_remove) if to_remove > 0 => { // grab all voters and sort them by least stake. let RoundSnapshot { voters, .. } = - Self::snapshot().ok_or(ElectionError::SnapshotUnAvailable)?; + Self::snapshot().ok_or(MinerError::SnapshotUnAvailable)?; let mut voters_sorted = voters .into_iter() .map(|(who, stake, _)| (who.clone(), stake)) @@ -175,7 +194,7 @@ impl Pallet { for (maybe_index, _stake) in voters_sorted.iter().map(|(who, stake)| (nominator_index(&who), stake)) { - let index = maybe_index.ok_or(ElectionError::SnapshotUnAvailable)?; + let index = maybe_index.ok_or(MinerError::SnapshotUnAvailable)?; if compact.remove_voter(index) { removed += 1 } @@ -199,7 +218,7 @@ impl Pallet { /// This only returns a value between zero and `size.nominators`. pub fn maximum_voter_for_weight( desired_winners: u32, - size: SolutionSize, + size: SolutionOrSnapshotSize, max_weight: Weight, ) -> u32 { if size.voters < 1 { @@ -306,7 +325,7 @@ impl Pallet { } /// Mine a new solution, and submit it back to the chain as an unsigned transaction. - pub(crate) fn mine_and_submit() -> Result<(), ElectionError> { + pub(crate) fn mine_and_submit() -> Result<(), MinerError> { let balancing = Self::get_balancing_iters(); let (raw_solution, witness) = Self::mine_solution(balancing)?; @@ -314,7 +333,7 @@ impl Pallet { let call = Call::submit_unsigned(raw_solution, witness).into(); SubmitTransaction::>::submit_unsigned_transaction(call) - .map_err(|_| ElectionError::PoolSubmissionFailed) + .map_err(|_| MinerError::PoolSubmissionFailed) } /// Do the basics checks that MUST happen during the validation and pre-dispatch of an unsigned @@ -328,13 +347,13 @@ impl Pallet { solution: &RawSolution>, ) -> DispatchResult { // ensure solution is timely. Don't panic yet. This is a cheap check. - ensure!(Self::current_phase().is_unsigned_open(), Error::::EarlySubmission,); + ensure!(Self::current_phase().is_unsigned_open(), Error::::PreDispatchEarlySubmission); // ensure correct number of winners. ensure!( Self::desired_targets().unwrap_or_default() == solution.compact.unique_targets().len() as u32, - Error::::WrongWinnerCount, + Error::::PreDispatchWrongWinnerCount, ); // ensure score is being improved. Panic henceforth. @@ -344,7 +363,7 @@ impl Pallet { q.score, T::SolutionImprovementThreshold::get() )), - Error::::WeakSubmission, + Error::::PreDispatchWeakSubmission, ); Ok(()) @@ -380,7 +399,7 @@ mod max_weight { #[test] fn find_max_voter_binary_search_works() { - let w = SolutionSize { voters: 10, targets: 0 }; + let w = SolutionOrSnapshotSize { voters: 10, targets: 0 }; assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 0), 0); assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1), 0); @@ -404,7 +423,7 @@ mod max_weight { assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 11_000), 10); assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 22_000), 10); - let w = SolutionSize { voters: 1, targets: 0 }; + let w = SolutionOrSnapshotSize { voters: 1, targets: 0 }; assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 0), 0); assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1), 0); @@ -418,7 +437,7 @@ mod max_weight { assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2010), 1); assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 3333), 1); - let w = SolutionSize { voters: 2, targets: 0 }; + let w = SolutionOrSnapshotSize { voters: 2, targets: 0 }; assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 0), 0); assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1), 0); @@ -576,7 +595,7 @@ mod tests { #[should_panic(expected = "Invalid unsigned submission must produce invalid block and \ deprive validator from their authoring reward.: \ DispatchError::Module { index: 0, error: 1, message: \ - Some(\"WrongWinnerCount\") }")] + Some(\"PreDispatchWrongWinnerCount\") }")] fn unfeasible_solution_panics() { ExtBuilder::default().build_and_execute(|| { roll_to(25); @@ -672,7 +691,7 @@ mod tests { // mine seq_phragmen solution with 2 iters. assert_eq!( TwoPhase::mine_solution(2).unwrap_err(), - ElectionError::PreDispatchChecksFailed, + MinerError::PreDispatchChecksFailed, ); }) } @@ -720,7 +739,7 @@ mod tests { assert_eq!(solution.score[0], 12); assert_noop!( TwoPhase::unsigned_pre_dispatch_checks(&solution), - Error::::WeakSubmission, + Error::::PreDispatchWeakSubmission, ); // submitting this will actually panic. From 49613edd1df346f6a82184d7075ecc228828cdf0 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 27 Jan 2021 10:42:35 +0000 Subject: [PATCH 08/57] Some doc tests. --- Cargo.lock | 1 - bin/node/runtime/Cargo.toml | 2 -- frame/election-provider-multi-phase/src/lib.rs | 8 +++++++- frame/staking/src/lib.rs | 2 +- frame/staking/src/tests.rs | 1 - primitives/npos-elections/src/tests.rs | 1 + 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a50ab923b0f04..91af7e22e2c19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3986,7 +3986,6 @@ dependencies = [ "sp-inherents", "sp-io", "sp-keyring", - "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-session", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index e1f87cd3cc6ca..96e110ea1ec50 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -35,7 +35,6 @@ sp-keyring = { version = "2.0.0", optional = true, path = "../../../primitives/k sp-session = { version = "2.0.0", default-features = false, path = "../../../primitives/session" } sp-transaction-pool = { version = "2.0.0", default-features = false, path = "../../../primitives/transaction-pool" } sp-version = { version = "2.0.0", default-features = false, path = "../../../primitives/version" } -sp-npos-elections = { version = "2.0.0", default-features = false, path = "../../../primitives/npos-elections" } # frame dependencies frame-executive = { version = "2.0.0", default-features = false, path = "../../../frame/executive" } @@ -116,7 +115,6 @@ std = [ "pallet-im-online/std", "pallet-indices/std", "sp-inherents/std", - "sp-npos-elections/std", "pallet-lottery/std", "pallet-membership/std", "pallet-mmr/std", diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 567b2aadb1de8..236a3db8ce1c7 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -186,7 +186,8 @@ //! of doing this would be to have the fallback be another //! [`sp_election_providers::ElectionProvider`]. In this case, this pallet can even have the //! on-chain election provider as fallback, or special _noop_ fallback that simply returns an error, -//! thus replicating [`FallbackStrategy::Nothing`]. +//! thus replicating [`FallbackStrategy::Nothing`]. In this case, we won't need the additional +//! config OnChainAccuracy either. //! //! **Score based on (byte) size**: We should always prioritize small solutions over bigger ones, if //! there is a tie. Even more harsh should be to enforce the bound of the `reduce` algorithm. @@ -194,6 +195,11 @@ //! **Offchain resubmit**: Essentially port https://github.com/paritytech/substrate/pull/7976 to //! this pallet as well. The `OFFCHAIN_REPEAT` also needs to become an adjustable parameter of the //! pallet. +//! +//! **Make the number of nominators configurable from the runtime**. Remove `sp_npos_elections` +//! dependency from staking and the compact solution type. It should be generated at runtime, there +//! it should be encoded how many votes each nominators have. Essentially translate +//! https://github.com/paritytech/substrate/pull/7929 to this pallet. #![cfg_attr(not(feature = "std"), no_std)] diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index f7ea7d5f98b58..c69a680a5267f 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -3115,7 +3115,7 @@ impl Module { /// /// Returns `Err(())` if less than [`MinimumValidatorCount`] validators have been elected, `Ok` /// otherwise. - // TWO_PHASE_NOTE: the deadcode + // TWO_PHASE_NOTE: remove the dead code. #[allow(dead_code)] pub fn process_election( flat_supports: sp_npos_elections::Supports, diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 4f17d05694b88..3d90f412943af 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -1813,7 +1813,6 @@ fn bond_with_duplicate_vote_should_be_ignored_by_npos_election() { .minimum_validator_count(1) .build() .execute_with(|| { - // disable the nominator assert_ok!(Staking::chill(Origin::signed(100))); // make stakes equal. diff --git a/primitives/npos-elections/src/tests.rs b/primitives/npos-elections/src/tests.rs index bc148f118ce42..edfea038ebc50 100644 --- a/primitives/npos-elections/src/tests.rs +++ b/primitives/npos-elections/src/tests.rs @@ -1149,6 +1149,7 @@ mod solution_type { type TestAccuracy = Percent; generate_solution_type!(pub struct TestSolutionCompact::(16)); + #[allow(dead_code)] mod __private { // This is just to make sure that that the compact can be generated in a scope without any From 35f1fafe91026c73803a1f2a3349e6d559b54041 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 27 Jan 2021 12:24:25 +0000 Subject: [PATCH 09/57] Some changes from other PR --- .../src/unsigned.rs | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index a796986bd12c9..d0b7400cb0f2b 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -53,6 +53,24 @@ impl From for MinerError { } impl Pallet { + /// Mine a new solution, and submit it back to the chain as an unsigned transaction. + pub(crate) fn mine_and_submit() -> Result<(), MinerError> { + let balancing = Self::get_balancing_iters(); + let (raw_solution, witness) = Self::mine_solution(balancing)?; + + // ensure that this will pass the pre-dispatch checks + Self::unsigned_pre_dispatch_checks(&raw_solution).map_err(|e| { + log!(warn, "pre-disaptch-checks failed for mined solution: {:?}", e); + MinerError::PreDispatchChecksFailed + })?; + + // submit the raw solution to the pool. + let call = Call::submit_unsigned(raw_solution, witness).into(); + + SubmitTransaction::>::submit_unsigned_transaction(call) + .map_err(|_| MinerError::PoolSubmissionFailed) + } + /// Mine a new npos solution. pub fn mine_solution( iters: usize, @@ -69,14 +87,6 @@ impl Pallet { ) .map_err(Into::into) .and_then(Self::prepare_election_result) - .and_then(|(raw_solution, size)| { - Self::unsigned_pre_dispatch_checks(&raw_solution) - .map_err(|e| { - log!(warn, "pre-disaptch-checks failed for mined solution: {:?}", e); - MinerError::PreDispatchChecksFailed - })?; - Ok((raw_solution, size)) - }) } /// Convert a raw solution from [`sp_npos_elections::ElectionResult`] to [`RawSolution`], which @@ -324,18 +334,6 @@ impl Pallet { } } - /// Mine a new solution, and submit it back to the chain as an unsigned transaction. - pub(crate) fn mine_and_submit() -> Result<(), MinerError> { - let balancing = Self::get_balancing_iters(); - let (raw_solution, witness) = Self::mine_solution(balancing)?; - - // submit the raw solution to the pool. - let call = Call::submit_unsigned(raw_solution, witness).into(); - - SubmitTransaction::>::submit_unsigned_transaction(call) - .map_err(|_| MinerError::PoolSubmissionFailed) - } - /// Do the basics checks that MUST happen during the validation and pre-dispatch of an unsigned /// transaction. /// @@ -684,13 +682,14 @@ mod tests { #[test] fn miner_will_not_submit_if_not_enough_winners() { - ExtBuilder::default().desired_targets(8).build_and_execute(|| { + let (mut ext, _) = ExtBuilder::default().desired_targets(8).build_offchainify(0); + ext.execute_with(|| { roll_to(25); assert!(TwoPhase::current_phase().is_unsigned()); // mine seq_phragmen solution with 2 iters. assert_eq!( - TwoPhase::mine_solution(2).unwrap_err(), + TwoPhase::mine_and_submit().unwrap_err(), MinerError::PreDispatchChecksFailed, ); }) From e01cacc54a41bcec3d2a79f20b3f4e5a6aa6030d Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 27 Jan 2021 13:04:31 +0000 Subject: [PATCH 10/57] Fix session test --- frame/session/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/session/src/tests.rs b/frame/session/src/tests.rs index 7c1d3c9dcdd24..3a665a5cbea17 100644 --- a/frame/session/src/tests.rs +++ b/frame/session/src/tests.rs @@ -274,7 +274,7 @@ fn periodic_session_works() { } assert!(P::should_end_session(13u64)); - assert_eq!(P::estimate_next_session_rotation(13u64).unwrap(), 13); + assert_eq!(P::estimate_next_session_rotation(13u64).unwrap(), 23); assert!(!P::should_end_session(14u64)); assert_eq!(P::estimate_next_session_rotation(14u64).unwrap(), 23); From 4c516cb420731b08e1241ce0b34cff035b7caf8a Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Thu, 4 Feb 2021 19:25:09 -0400 Subject: [PATCH 11/57] Update Cargo.lock --- Cargo.lock | 351 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 199 insertions(+), 152 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c39ba87321b65..5451bb6d77e0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -455,6 +455,16 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitvec" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" +dependencies = [ + "either", + "radium 0.3.0", +] + [[package]] name = "bitvec" version = "0.20.1" @@ -462,7 +472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5011ffc90248764d7005b0e10c7294f5aa1bd87d9dd7248f4ad475b347c294d" dependencies = [ "funty", - "radium", + "radium 0.6.2", "tap", "wyz", ] @@ -618,6 +628,12 @@ version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" +[[package]] +name = "byte-slice-cast" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" + [[package]] name = "byte-slice-cast" version = "1.0.0" @@ -1575,7 +1591,7 @@ dependencies = [ "futures-timer 3.0.2", "log", "num-traits", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "rand 0.8.3", ] @@ -1621,7 +1637,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" name = "fork-tree" version = "2.0.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", ] [[package]] @@ -1642,7 +1658,7 @@ dependencies = [ "frame-system", "hex-literal", "linregress", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "paste 1.0.4", "sp-api", "sp-io", @@ -1660,7 +1676,7 @@ dependencies = [ "chrono", "frame-benchmarking", "handlebars", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sc-cli", "sc-client-db", "sc-executor", @@ -1684,7 +1700,7 @@ dependencies = [ "pallet-balances", "pallet-indices", "pallet-transaction-payment", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -1698,7 +1714,7 @@ dependencies = [ name = "frame-metadata" version = "12.0.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-std", @@ -1715,7 +1731,7 @@ dependencies = [ "impl-trait-for-tuples 0.2.0", "log", "once_cell", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-util-mem", "paste 1.0.4", "pretty_assertions", @@ -1772,7 +1788,7 @@ dependencies = [ "frame-metadata", "frame-support", "frame-system", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "pretty_assertions", "rustversion", "serde", @@ -1792,7 +1808,7 @@ dependencies = [ "criterion", "frame-support", "impl-trait-for-tuples 0.2.0", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-externalities", @@ -1810,7 +1826,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -1822,7 +1838,7 @@ dependencies = [ name = "frame-system-rpc-runtime-api" version = "2.0.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-api", ] @@ -2530,7 +2546,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df170efa359aebdd5cb7fe78edcc67107748e4737bdca8a8fb40d15ea7a877ed" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", ] [[package]] @@ -3883,7 +3899,7 @@ dependencies = [ "pallet-staking", "pallet-timestamp", "pallet-transaction-payment", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "platforms", "rand 0.7.3", @@ -3953,7 +3969,7 @@ dependencies = [ "pallet-timestamp", "pallet-transaction-payment", "pallet-treasury", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sc-executor", "sp-application-crypto", "sp-core", @@ -3974,7 +3990,7 @@ version = "0.8.0" dependencies = [ "derive_more", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sc-cli", "sc-client-api", "sc-service", @@ -3989,7 +4005,7 @@ name = "node-primitives" version = "2.0.0" dependencies = [ "frame-system", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "pretty_assertions", "sp-application-crypto", "sp-core", @@ -4093,7 +4109,7 @@ dependencies = [ "pallet-treasury", "pallet-utility", "pallet-vesting", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-api", "sp-authority-discovery", @@ -4171,7 +4187,7 @@ dependencies = [ "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-api", "sp-block-builder", @@ -4210,7 +4226,7 @@ dependencies = [ "pallet-timestamp", "pallet-transaction-payment", "pallet-treasury", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sc-block-builder", "sc-cli", "sc-client-api", @@ -4398,7 +4414,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4413,7 +4429,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4430,7 +4446,7 @@ dependencies = [ "lazy_static", "pallet-session", "pallet-timestamp", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "serde", "sp-application-crypto", @@ -4449,7 +4465,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-session", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-application-crypto", "sp-authority-discovery", @@ -4467,7 +4483,7 @@ dependencies = [ "frame-support", "frame-system", "impl-trait-for-tuples 0.2.0", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-authorship", "sp-core", @@ -4491,7 +4507,7 @@ dependencies = [ "pallet-staking", "pallet-staking-reward-curve", "pallet-timestamp", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-application-crypto", "sp-consensus-babe", @@ -4515,7 +4531,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-transaction-payment", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4532,7 +4548,7 @@ dependencies = [ "frame-system", "pallet-balances", "pallet-treasury", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4550,7 +4566,7 @@ dependencies = [ "frame-system", "hex-literal", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4572,7 +4588,7 @@ dependencies = [ "pallet-contracts-proc-macro", "pallet-randomness-collective-flip", "pallet-timestamp", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-wasm 0.41.0", "paste 1.0.4", "pretty_assertions", @@ -4594,7 +4610,7 @@ name = "pallet-contracts-primitives" version = "2.0.1" dependencies = [ "bitflags", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-runtime", "sp-std", ] @@ -4617,7 +4633,7 @@ dependencies = [ "jsonrpc-derive", "pallet-contracts-primitives", "pallet-contracts-rpc-runtime-api", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "serde_json", "sp-api", @@ -4632,7 +4648,7 @@ name = "pallet-contracts-rpc-runtime-api" version = "0.8.1" dependencies = [ "pallet-contracts-primitives", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-api", "sp-runtime", "sp-std", @@ -4648,7 +4664,7 @@ dependencies = [ "hex-literal", "pallet-balances", "pallet-scheduler", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4667,9 +4683,9 @@ dependencies = [ "frame-system", "hex-literal", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 1.3.6", "parking_lot 0.11.1", - "paste 1.0.3", + "paste 1.0.4", "rand 0.7.3", "serde", "sp-arithmetic", @@ -4692,7 +4708,7 @@ dependencies = [ "frame-system", "hex-literal", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4709,7 +4725,7 @@ dependencies = [ "frame-system", "hex-literal", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4727,7 +4743,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4742,7 +4758,7 @@ dependencies = [ "frame-support", "frame-system", "lite-json", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4757,7 +4773,7 @@ version = "2.0.1" dependencies = [ "frame-support", "frame-system", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-core", "sp-io", "sp-runtime", @@ -4780,7 +4796,7 @@ dependencies = [ "pallet-staking", "pallet-staking-reward-curve", "pallet-timestamp", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-application-crypto", "sp-core", @@ -4803,7 +4819,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4820,7 +4836,7 @@ dependencies = [ "frame-system", "pallet-authorship", "pallet-session", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-application-crypto", "sp-core", @@ -4838,7 +4854,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4855,7 +4871,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4869,7 +4885,7 @@ version = "2.0.1" dependencies = [ "frame-support", "frame-system", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4888,7 +4904,7 @@ dependencies = [ "frame-system", "hex-literal", "pallet-mmr-primitives", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4903,7 +4919,7 @@ dependencies = [ "frame-support", "frame-system", "hex-literal", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-api", "sp-core", @@ -4919,7 +4935,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4934,7 +4950,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4948,7 +4964,7 @@ version = "2.0.0" dependencies = [ "frame-support", "frame-system", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4963,7 +4979,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -4988,7 +5004,7 @@ dependencies = [ "pallet-staking", "pallet-staking-reward-curve", "pallet-timestamp", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-election-providers", @@ -5007,7 +5023,7 @@ dependencies = [ "frame-system", "pallet-balances", "pallet-utility", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -5021,7 +5037,7 @@ version = "2.0.1" dependencies = [ "frame-support", "frame-system", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "safe-mix", "serde", "sp-core", @@ -5038,7 +5054,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -5053,7 +5069,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -5069,7 +5085,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -5086,7 +5102,7 @@ dependencies = [ "impl-trait-for-tuples 0.1.3", "lazy_static", "pallet-timestamp", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-application-crypto", "sp-core", @@ -5110,7 +5126,7 @@ dependencies = [ "pallet-staking", "pallet-staking-reward-curve", "pallet-timestamp", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "rand 0.7.3", "serde", "sp-core", @@ -5128,7 +5144,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "rand_chacha 0.2.2", "serde", "sp-core", @@ -5150,7 +5166,7 @@ dependencies = [ "pallet-session", "pallet-staking-reward-curve", "pallet-timestamp", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "rand_chacha 0.2.2", "serde", @@ -5181,7 +5197,7 @@ dependencies = [ "pallet-staking", "pallet-staking-reward-curve", "pallet-timestamp", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-core", "sp-election-providers", "sp-io", @@ -5207,7 +5223,7 @@ version = "2.0.1" dependencies = [ "frame-support", "frame-system", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -5221,7 +5237,7 @@ version = "2.0.0" dependencies = [ "frame-support", "frame-system", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -5236,7 +5252,7 @@ dependencies = [ "frame-support", "frame-system", "impl-trait-for-tuples 0.2.0", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-inherents", @@ -5255,7 +5271,7 @@ dependencies = [ "frame-system", "pallet-balances", "pallet-treasury", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -5271,7 +5287,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "serde_json", "smallvec 1.6.1", @@ -5290,7 +5306,7 @@ dependencies = [ "jsonrpc-core-client", "jsonrpc-derive", "pallet-transaction-payment-rpc-runtime-api", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-api", "sp-blockchain", "sp-core", @@ -5303,7 +5319,7 @@ name = "pallet-transaction-payment-rpc-runtime-api" version = "2.0.1" dependencies = [ "pallet-transaction-payment", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-api", "sp-runtime", ] @@ -5317,7 +5333,7 @@ dependencies = [ "frame-system", "impl-trait-for-tuples 0.2.0", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -5334,7 +5350,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -5352,7 +5368,7 @@ dependencies = [ "frame-system", "hex-literal", "pallet-balances", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -5395,6 +5411,19 @@ dependencies = [ "url 2.2.0", ] +[[package]] +name = "parity-scale-codec" +version = "1.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79602888a81ace83e3d1d4b2873286c1f5f906c84db667594e8db8da3506c383" +dependencies = [ + "arrayvec 0.5.2", + "bitvec 0.17.4", + "byte-slice-cast 0.3.5", + "parity-scale-codec-derive 1.2.2", + "serde", +] + [[package]] name = "parity-scale-codec" version = "2.0.0" @@ -5402,12 +5431,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c823fdae1bb5ff5708ee61a62697e6296175dc671710876871c853f48592b3" dependencies = [ "arrayvec 0.5.2", - "bitvec", - "byte-slice-cast", - "parity-scale-codec-derive", + "bitvec 0.20.1", + "byte-slice-cast 1.0.0", + "parity-scale-codec-derive 2.0.0", "serde", ] +[[package]] +name = "parity-scale-codec-derive" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "198db82bb1c18fc00176004462dd809b2a6d851669550aa17af6dacd21ae0c14" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "parity-scale-codec-derive" version = "2.0.0" @@ -6079,6 +6120,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" + [[package]] name = "radium" version = "0.6.2" @@ -6559,7 +6606,7 @@ dependencies = [ "futures-timer 3.0.2", "libp2p", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "prost", "prost-build", "quickcheck", @@ -6586,7 +6633,7 @@ dependencies = [ "futures 0.3.12", "futures-timer 3.0.2", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sc-block-builder", "sc-client-api", @@ -6608,7 +6655,7 @@ dependencies = [ name = "sc-block-builder" version = "0.8.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sc-client-api", "sp-api", "sp-block-builder", @@ -6627,7 +6674,7 @@ name = "sc-chain-spec" version = "2.0.1" dependencies = [ "impl-trait-for-tuples 0.2.0", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sc-chain-spec-derive", "sc-consensus-babe", "sc-consensus-epochs", @@ -6663,7 +6710,7 @@ dependencies = [ "libp2p", "log", "names", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "rand 0.7.3", "regex", "rpassword", @@ -6702,7 +6749,7 @@ dependencies = [ "kvdb-memorydb", "lazy_static", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sc-executor", "sp-api", @@ -6739,7 +6786,7 @@ dependencies = [ "linked-hash-map", "log", "parity-db", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-util-mem", "parking_lot 0.11.1", "quickcheck", @@ -6780,7 +6827,7 @@ dependencies = [ "futures-timer 3.0.2", "getrandom 0.2.2", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sc-block-builder", "sc-client-api", @@ -6825,7 +6872,7 @@ dependencies = [ "num-bigint", "num-rational", "num-traits", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "pdqselect", "rand 0.7.3", @@ -6901,7 +6948,7 @@ name = "sc-consensus-epochs" version = "0.8.1" dependencies = [ "fork-tree", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sc-client-api", "sp-blockchain", @@ -6919,7 +6966,7 @@ dependencies = [ "jsonrpc-core-client", "jsonrpc-derive", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sc-basic-authorship", "sc-client-api", @@ -6954,7 +7001,7 @@ dependencies = [ "futures 0.3.12", "futures-timer 3.0.2", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sc-client-api", "sp-api", @@ -6976,7 +7023,7 @@ dependencies = [ "futures 0.3.12", "futures-timer 3.0.2", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sc-client-api", "sc-telemetry", @@ -7018,7 +7065,7 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-wasm 0.41.0", "parking_lot 0.11.1", "paste 1.0.4", @@ -7053,7 +7100,7 @@ name = "sc-executor-common" version = "0.8.1" dependencies = [ "derive_more", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-wasm 0.41.0", "sp-allocator", "sp-core", @@ -7068,7 +7115,7 @@ name = "sc-executor-wasmi" version = "0.8.1" dependencies = [ "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sc-executor-common", "sp-allocator", "sp-core", @@ -7083,7 +7130,7 @@ version = "0.8.1" dependencies = [ "assert_matches", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-wasm 0.41.0", "pwasm-utils 0.14.0", "sc-executor-common", @@ -7107,7 +7154,7 @@ dependencies = [ "futures-timer 3.0.2", "linked-hash-map", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "pin-project 1.0.4", "rand 0.7.3", @@ -7154,7 +7201,7 @@ dependencies = [ "jsonrpc-pubsub", "lazy_static", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sc-block-builder", "sc-client-api", "sc-finality-grandpa", @@ -7179,7 +7226,7 @@ dependencies = [ "futures 0.3.12", "log", "num-traits", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "prost", "sc-client-api", @@ -7233,7 +7280,7 @@ version = "2.0.1" dependencies = [ "hash-db", "lazy_static", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sc-client-api", "sc-executor", @@ -7272,7 +7319,7 @@ dependencies = [ "log", "lru", "nohash-hasher", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "pin-project 1.0.4", "prost", @@ -7364,7 +7411,7 @@ dependencies = [ "lazy_static", "log", "num_cpus", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "rand 0.7.3", "sc-block-builder", @@ -7419,7 +7466,7 @@ dependencies = [ "jsonrpc-pubsub", "lazy_static", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sc-block-builder", "sc-cli", @@ -7460,7 +7507,7 @@ dependencies = [ "jsonrpc-derive", "jsonrpc-pubsub", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "serde", "serde_json", @@ -7518,7 +7565,7 @@ dependencies = [ "jsonrpc-pubsub", "lazy_static", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-util-mem", "parking_lot 0.11.1", "pin-project 1.0.4", @@ -7582,7 +7629,7 @@ dependencies = [ "futures 0.3.12", "hex-literal", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sc-block-builder", "sc-client-api", @@ -7614,7 +7661,7 @@ name = "sc-state-db" version = "0.8.1" dependencies = [ "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-util-mem", "parity-util-mem-derive", "parking_lot 0.11.1", @@ -7710,7 +7757,7 @@ dependencies = [ "futures 0.3.12", "linked-hash-map", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-util-mem", "parking_lot 0.11.1", "retain_mut", @@ -7735,7 +7782,7 @@ dependencies = [ "hex", "intervalier", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-util-mem", "parking_lot 0.11.1", "sc-block-builder", @@ -8152,7 +8199,7 @@ name = "sp-api" version = "2.0.1" dependencies = [ "hash-db", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-api-proc-macro", "sp-core", "sp-runtime", @@ -8179,7 +8226,7 @@ name = "sp-api-test" version = "2.0.1" dependencies = [ "criterion", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "rustversion", "sc-block-builder", "sp-api", @@ -8197,7 +8244,7 @@ dependencies = [ name = "sp-application-crypto" version = "2.0.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-core", "sp-io", @@ -8223,7 +8270,7 @@ dependencies = [ "criterion", "integer-sqrt", "num-traits", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "primitive-types", "rand 0.7.3", "serde", @@ -8247,7 +8294,7 @@ dependencies = [ name = "sp-authority-discovery" version = "2.0.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-api", "sp-application-crypto", "sp-runtime", @@ -8258,7 +8305,7 @@ dependencies = [ name = "sp-authorship" version = "2.0.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-inherents", "sp-runtime", "sp-std", @@ -8268,7 +8315,7 @@ dependencies = [ name = "sp-block-builder" version = "2.0.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-api", "sp-inherents", "sp-runtime", @@ -8282,7 +8329,7 @@ dependencies = [ "futures 0.3.12", "log", "lru", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sp-api", "sp-consensus", @@ -8308,7 +8355,7 @@ dependencies = [ "futures-timer 3.0.2", "libp2p", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "serde", "sp-api", @@ -8330,7 +8377,7 @@ dependencies = [ name = "sp-consensus-aura" version = "0.8.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-api", "sp-application-crypto", "sp-consensus-slots", @@ -8345,7 +8392,7 @@ name = "sp-consensus-babe" version = "0.8.1" dependencies = [ "merlin", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-api", "sp-application-crypto", "sp-consensus", @@ -8363,7 +8410,7 @@ dependencies = [ name = "sp-consensus-pow" version = "0.8.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-api", "sp-core", "sp-runtime", @@ -8374,7 +8421,7 @@ dependencies = [ name = "sp-consensus-slots" version = "0.8.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-arithmetic", "sp-runtime", ] @@ -8383,7 +8430,7 @@ dependencies = [ name = "sp-consensus-vrf" version = "0.8.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "schnorrkel", "sp-core", "sp-runtime", @@ -8411,7 +8458,7 @@ dependencies = [ "log", "merlin", "num-traits", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-util-mem", "parking_lot 0.11.1", "pretty_assertions", @@ -8460,7 +8507,7 @@ dependencies = [ name = "sp-election-providers" version = "2.0.0" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-arithmetic", "sp-npos-elections", "sp-runtime", @@ -8472,7 +8519,7 @@ name = "sp-externalities" version = "0.8.1" dependencies = [ "environmental", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-std", "sp-storage", ] @@ -8483,7 +8530,7 @@ version = "2.0.1" dependencies = [ "finality-grandpa", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-api", "sp-application-crypto", @@ -8497,7 +8544,7 @@ dependencies = [ name = "sp-inherents" version = "2.0.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sp-core", "sp-std", @@ -8512,7 +8559,7 @@ dependencies = [ "hash-db", "libsecp256k1", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sp-core", "sp-externalities", @@ -8545,7 +8592,7 @@ dependencies = [ "derive_more", "futures 0.3.12", "merlin", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "rand 0.7.3", "rand_chacha 0.2.2", @@ -8559,7 +8606,7 @@ dependencies = [ name = "sp-npos-elections" version = "2.0.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "rand 0.7.3", "serde", "sp-arithmetic", @@ -8585,7 +8632,7 @@ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ "honggfuzz", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "rand 0.7.3", "sp-npos-elections", "sp-runtime", @@ -8626,7 +8673,7 @@ dependencies = [ "hash256-std-hasher", "impl-trait-for-tuples 0.2.0", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-util-mem", "paste 1.0.4", "rand 0.7.3", @@ -8645,7 +8692,7 @@ name = "sp-runtime-interface" version = "2.0.1" dependencies = [ "impl-trait-for-tuples 0.2.0", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "primitive-types", "rustversion", "sp-core", @@ -8716,7 +8763,7 @@ name = "sp-sandbox" version = "0.8.1" dependencies = [ "assert_matches", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-core", "sp-io", "sp-std", @@ -8737,7 +8784,7 @@ dependencies = [ name = "sp-session" version = "2.0.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-api", "sp-core", "sp-runtime", @@ -8749,7 +8796,7 @@ dependencies = [ name = "sp-staking" version = "2.0.1" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-runtime", "sp-std", ] @@ -8762,7 +8809,7 @@ dependencies = [ "hex-literal", "log", "num-traits", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "pretty_assertions", "rand 0.7.3", @@ -8787,7 +8834,7 @@ name = "sp-storage" version = "2.0.1" dependencies = [ "impl-serde", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "ref-cast", "serde", "sp-debug-derive", @@ -8799,7 +8846,7 @@ name = "sp-tasks" version = "2.0.0" dependencies = [ "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-core", "sp-externalities", "sp-io", @@ -8811,7 +8858,7 @@ dependencies = [ name = "sp-test-primitives" version = "2.0.0" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-util-mem", "serde", "sp-application-crypto", @@ -8824,7 +8871,7 @@ name = "sp-timestamp" version = "2.0.1" dependencies = [ "impl-trait-for-tuples 0.2.0", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-api", "sp-inherents", "sp-runtime", @@ -8837,7 +8884,7 @@ name = "sp-tracing" version = "2.0.1" dependencies = [ "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-std", "tracing", "tracing-core", @@ -8851,7 +8898,7 @@ dependencies = [ "derive_more", "futures 0.3.12", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-api", "sp-blockchain", @@ -8867,7 +8914,7 @@ dependencies = [ "hash-db", "hex-literal", "memory-db", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-core", "sp-runtime", "sp-std", @@ -8893,7 +8940,7 @@ name = "sp-version" version = "2.0.1" dependencies = [ "impl-serde", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "serde", "sp-runtime", "sp-std", @@ -8904,7 +8951,7 @@ name = "sp-wasm-interface" version = "2.0.1" dependencies = [ "impl-trait-for-tuples 0.2.0", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sp-std", "wasmi", ] @@ -9080,7 +9127,7 @@ dependencies = [ "futures 0.3.12", "jsonrpc-client-transports", "jsonrpc-core", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sc-rpc-api", "serde", "sp-storage", @@ -9097,7 +9144,7 @@ dependencies = [ "jsonrpc-core-client", "jsonrpc-derive", "log", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sc-client-api", "sc-rpc-api", "sc-transaction-pool", @@ -9133,7 +9180,7 @@ dependencies = [ "futures 0.3.12", "hash-db", "hex", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sc-client-api", "sc-client-db", "sc-consensus", @@ -9164,7 +9211,7 @@ dependencies = [ "memory-db", "pallet-babe", "pallet-timestamp", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parity-util-mem", "sc-block-builder", "sc-executor", @@ -9200,7 +9247,7 @@ name = "substrate-test-runtime-client" version = "2.0.0" dependencies = [ "futures 0.3.12", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "sc-block-builder", "sc-client-api", "sc-consensus", @@ -9221,7 +9268,7 @@ version = "2.0.0" dependencies = [ "derive_more", "futures 0.3.12", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "parking_lot 0.11.1", "sc-transaction-graph", "sp-blockchain", @@ -9851,7 +9898,7 @@ dependencies = [ "hash-db", "keccak-hasher", "memory-db", - "parity-scale-codec", + "parity-scale-codec 2.0.0", "trie-db", "trie-root", "trie-standardmap", From 8c8d1e62b120e037dd9726d9d1a7c2b6aeb804fe Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Thu, 11 Feb 2021 16:01:28 +0000 Subject: [PATCH 12/57] Update frame/election-provider-multi-phase/src/lib.rs Co-authored-by: Guillaume Thiolliere --- frame/election-provider-multi-phase/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 236a3db8ce1c7..d2648225e7f47 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -910,7 +910,7 @@ impl Pallet { // NOTE: this is a bit of duplicate, but we keep it around for veracity. The unsigned path // already checked this in `unsigned_per_dispatch_checks`. The signed path *could* check it // upon arrival, thus we would then remove it here. Given overlay it is cheap anyhow - ensure!(winners.len() as u32 == desired_targets, FeasibilityError::WrongWinnerCount,); + ensure!(winners.len() as u32 == desired_targets, FeasibilityError::WrongWinnerCount); // read the entire snapshot. let RoundSnapshot { voters: snapshot_voters, targets: snapshot_targets } = From 4b58c91f3b98999b9964333a17862ab52fff3051 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 11 Feb 2021 16:14:29 +0000 Subject: [PATCH 13/57] Some review comments --- bin/node/runtime/src/lib.rs | 2 +- frame/election-provider-multi-phase/src/lib.rs | 10 ++++------ frame/election-provider-multi-phase/src/mock.rs | 4 ++-- frame/election-provider-multi-phase/src/unsigned.rs | 6 +++--- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 0636817e456d4..37fb51fa8e8de 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1368,6 +1368,7 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_contracts, Contracts); add_benchmark!(params, batches, pallet_democracy, Democracy); add_benchmark!(params, batches, pallet_elections_phragmen, Elections); + add_benchmark!(params, batches, pallet_election_provider_multi_phase, ElectionProviderMultiPhase); add_benchmark!(params, batches, pallet_grandpa, Grandpa); add_benchmark!(params, batches, pallet_identity, Identity); add_benchmark!(params, batches, pallet_im_online, ImOnline); @@ -1386,7 +1387,6 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_treasury, Treasury); add_benchmark!(params, batches, pallet_utility, Utility); add_benchmark!(params, batches, pallet_vesting, Vesting); - add_benchmark!(params, batches, pallet_election_provider_multi_phase, ElectionProviderMultiPhase); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok(batches) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 236a3db8ce1c7..5fb60afc66835 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -496,9 +496,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config + SendTransactionTypes> { - type Event: From> - + Into<::Event> - + IsType<::Event>; + type Event: From> + IsType<::Event>; /// Currency type. type Currency: ReservableCurrency + Currency; @@ -585,8 +583,8 @@ pub mod pallet { log!(info, "Starting unsigned phase({}) at #{:?}.", enabled, now); let base_weight = if need_snapshot { - T::WeightInfo::on_initialize_open_unsigned_with_snapshot() } - else { + T::WeightInfo::on_initialize_open_unsigned_with_snapshot() + } else { T::WeightInfo::on_initialize_open_unsigned_without_snapshot() }; base_weight.saturating_add(additional) @@ -596,7 +594,7 @@ pub mod pallet { } fn offchain_worker(n: T::BlockNumber) { - // We only run the OCW in the fist block of the unsigned phase. + // We only run the OCW in the first block of the unsigned phase. if Self::current_phase().is_unsigned_open_at(n) { match Self::try_acquire_offchain_lock(n) { Ok(_) => { diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 402886703a1e7..0b9fed1d21cf2 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -52,7 +52,7 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: frame_system::{Module, Call, Event}, + System: frame_system::{Module, Call, Event, Config}, Balances: pallet_balances::{Module, Call, Event, Config}, TwoPhase: two_phase::{Module, Call, Event}, } @@ -154,7 +154,7 @@ impl frame_system::Config for Runtime { type BlockLength = (); type BlockWeights = BlockWeights; type Version = (); - type PalletInfo = (); + type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index d0b7400cb0f2b..31d3aaf030066 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -28,7 +28,7 @@ use sp_runtime::{offchain::storage::StorageValueRef, traits::TrailingZeroInput}; use sp_std::cmp::Ordering; /// Storage key used to store the persistent offchain worker status. -pub(crate) const OFFCHAIN_HEAD_DB: &[u8] = b"parity/multi-phase-unsigned-election/"; +pub(crate) const OFFCHAIN_HEAD_DB: &[u8] = b"parity/multi-phase-unsigned-election"; /// The repeat threshold of the offchain worker. This means we won't run the offchain worker twice /// within a window of 5 blocks. @@ -182,7 +182,7 @@ impl Pallet { pub fn trim_compact( maximum_allowed_voters: u32, mut compact: CompactOf, - nominator_index: FN, + voter_index: FN, ) -> Result, MinerError> where for<'r> FN: Fn(&'r T::AccountId) -> Option>, @@ -202,7 +202,7 @@ impl Pallet { // removed. let mut removed = 0; for (maybe_index, _stake) in - voters_sorted.iter().map(|(who, stake)| (nominator_index(&who), stake)) + voters_sorted.iter().map(|(who, stake)| (voter_index(&who), stake)) { let index = maybe_index.ok_or(MinerError::SnapshotUnAvailable)?; if compact.remove_voter(index) { From 528917e6adf7e558e0d7e9384e9363a408cd4604 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 12 Feb 2021 10:17:06 +0000 Subject: [PATCH 14/57] Rename + make encode/decode --- bin/node/runtime/src/lib.rs | 4 +- .../src/benchmarking.rs | 69 ++--- .../election-provider-multi-phase/src/lib.rs | 177 ++++++------- .../election-provider-multi-phase/src/mock.rs | 36 +-- .../src/unsigned.rs | 235 +++++++++--------- .../src/weights.rs | 8 +- 6 files changed, 273 insertions(+), 256 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 5e472bdb0359f..6d014193cec88 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -516,7 +516,7 @@ parameter_types! { pub SolutionImprovementThreshold: Perbill = Perbill::from_rational_approximation(1u32, 10_000); // miner configs - pub const TwoPhaseUnsignedPriority: TransactionPriority = StakingUnsignedPriority::get() - 1u64; + pub const MultiPhaseUnsignedPriority: TransactionPriority = StakingUnsignedPriority::get() - 1u64; pub const MinerMaxIterations: u32 = 10; pub MinerMaxWeight: Weight = RuntimeBlockWeights::get() .get(DispatchClass::Normal) @@ -532,7 +532,7 @@ impl pallet_election_provider_multi_phase::Config for Runtime { type SolutionImprovementThreshold = MinSolutionScoreBump; type MinerMaxIterations = MinerMaxIterations; type MinerMaxWeight = MinerMaxWeight; - type MinerTxPriority = TwoPhaseUnsignedPriority; + type MinerTxPriority = MultiPhaseUnsignedPriority; type DataProvider = Staking; type OnChainAccuracy = Perbill; type CompactSolution = pallet_staking::CompactAssignments; diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index 98a874967fc03..daac85f06790a 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -18,7 +18,7 @@ //! Two phase election pallet benchmarking. use super::*; -use crate::Module as TwoPhase; +use crate::Module as MultiPhase; pub use frame_benchmarking::{account, benchmarks, whitelist_account, whitelisted_caller}; use frame_support::{assert_ok, traits::OnInitialize}; @@ -137,61 +137,61 @@ fn solution_with_size( let compact = >::from_assignment(assignments, &voter_index, &target_index).unwrap(); let score = compact.clone().score(&winners, stake_of, voter_at, target_at).unwrap(); - let round = >::round(); + let round = >::round(); RawSolution { compact, score, round } } benchmarks! { on_initialize_nothing { - assert!(>::current_phase().is_off()); + assert!(>::current_phase().is_off()); }: { - >::on_initialize(1u32.into()); + >::on_initialize(1u32.into()); } verify { - assert!(>::current_phase().is_off()); + assert!(>::current_phase().is_off()); } on_initialize_open_signed { // NOTE: this benchmark currently doesn't have any components because the length of a db // read/write is not captured. Otherwise, it is quite influenced by how much data // `T::ElectionDataProvider` is reading and passing on. - assert!(>::snapshot().is_none()); - assert!(>::current_phase().is_off()); + assert!(>::snapshot().is_none()); + assert!(>::current_phase().is_off()); }: { - >::on_initialize_open_signed(); + >::on_initialize_open_signed(); } verify { - assert!(>::snapshot().is_some()); - assert!(>::current_phase().is_signed()); + assert!(>::snapshot().is_some()); + assert!(>::current_phase().is_signed()); } on_initialize_open_unsigned_with_snapshot { - assert!(>::snapshot().is_none()); - assert!(>::current_phase().is_off()); + assert!(>::snapshot().is_none()); + assert!(>::current_phase().is_off()); }: { - >::on_initialize_open_unsigned(true, true, 1u32.into()); + >::on_initialize_open_unsigned(true, true, 1u32.into()); } verify { - assert!(>::snapshot().is_some()); - assert!(>::current_phase().is_unsigned()); + assert!(>::snapshot().is_some()); + assert!(>::current_phase().is_unsigned()); } on_initialize_open_unsigned_without_snapshot { // need to assume signed phase was open before - >::on_initialize_open_signed(); - assert!(>::snapshot().is_some()); - assert!(>::current_phase().is_signed()); + >::on_initialize_open_signed(); + assert!(>::snapshot().is_some()); + assert!(>::current_phase().is_signed()); }: { - >::on_initialize_open_unsigned(false, true, 1u32.into()); + >::on_initialize_open_unsigned(false, true, 1u32.into()); } verify { - assert!(>::snapshot().is_some()); - assert!(>::current_phase().is_unsigned()); + assert!(>::snapshot().is_some()); + assert!(>::current_phase().is_unsigned()); } #[extra] create_snapshot { - assert!(>::snapshot().is_none()); + assert!(>::snapshot().is_none()); }: { - >::create_snapshot() + >::create_snapshot() } verify { - assert!(>::snapshot().is_some()); + assert!(>::snapshot().is_some()); } submit_unsigned { @@ -208,11 +208,17 @@ benchmarks! { let witness = SolutionOrSnapshotSize { voters: v, targets: t }; let raw_solution = solution_with_size::(witness, a, d); - assert!(>::queued_solution().is_none()); + assert!(>::queued_solution().is_none()); >::put(Phase::Unsigned((true, 1u32.into()))); - }: _(RawOrigin::None, raw_solution, witness) - verify { - assert!(>::queued_solution().is_some()); + + // encode the most significant storage item that needs to be decoded in the dispatch. + let encoded_snapshot = >::snapshot().encode(); + }: { + assert_ok!(>::submit_unsigned(RawOrigin::None.into(), raw_solution, witness)); + let _decoded = as Decode>::decode(&mut &*encoded_snapshot).unwrap(); + // NOTE: assert that this line is optimized away by the compiler in any way! + } verify { + assert!(>::queued_solution().is_some()); } // This is checking a valid solution. The worse case is indeed a valid solution. @@ -232,8 +238,13 @@ benchmarks! { assert_eq!(raw_solution.compact.voter_count() as u32, a); assert_eq!(raw_solution.compact.unique_targets().len() as u32, d); + + // encode the most significant storage item that needs to be decoded in the dispatch. + let encoded_snapshot = >::snapshot().encode(); }: { - assert_ok!(>::feasibility_check(raw_solution, ElectionCompute::Unsigned)); + assert_ok!(>::feasibility_check(raw_solution, ElectionCompute::Unsigned)); + let _decoded = as Decode>::decode(&mut &*encoded_snapshot).unwrap(); + // NOTE: assert that this line is optimized away by the compiler in any way! } } diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 5c78c9a1c59ea..ee89beb04a10b 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -1058,14 +1058,14 @@ mod feasibility_check { fn snapshot_is_there() { ExtBuilder::default().build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(TwoPhase::current_phase().is_signed()); + assert!(MultiPhase::current_phase().is_signed()); let solution = raw_solution(); // for whatever reason it might be: >::kill(); assert_noop!( - TwoPhase::feasibility_check(solution, COMPUTE), + MultiPhase::feasibility_check(solution, COMPUTE), FeasibilityError::SnapshotUnavailable ); }) @@ -1075,12 +1075,12 @@ mod feasibility_check { fn round() { ExtBuilder::default().build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(TwoPhase::current_phase().is_signed()); + assert!(MultiPhase::current_phase().is_signed()); let mut solution = raw_solution(); solution.round += 1; assert_noop!( - TwoPhase::feasibility_check(solution, COMPUTE), + MultiPhase::feasibility_check(solution, COMPUTE), FeasibilityError::InvalidRound ); }) @@ -1090,15 +1090,15 @@ mod feasibility_check { fn desired_targets() { ExtBuilder::default().desired_targets(8).build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(TwoPhase::current_phase().is_signed()); + assert!(MultiPhase::current_phase().is_signed()); let solution = raw_solution(); assert_eq!(solution.compact.unique_targets().len(), 4); - assert_eq!(TwoPhase::desired_targets().unwrap(), 8); + assert_eq!(MultiPhase::desired_targets().unwrap(), 8); assert_noop!( - TwoPhase::feasibility_check(solution, COMPUTE), + MultiPhase::feasibility_check(solution, COMPUTE), FeasibilityError::WrongWinnerCount, ); }) @@ -1108,10 +1108,10 @@ mod feasibility_check { fn winner_indices() { ExtBuilder::default().desired_targets(2).build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(TwoPhase::current_phase().is_signed()); + assert!(MultiPhase::current_phase().is_signed()); let mut solution = raw_solution(); - assert_eq!(TwoPhase::snapshot().unwrap().targets.len(), 4); + assert_eq!(MultiPhase::snapshot().unwrap().targets.len(), 4); // ----------------------------------------------------^^ valid range is [0..3]. // swap all votes from 3 to 4. This will ensure that the number of unique winners @@ -1132,7 +1132,7 @@ mod feasibility_check { }; }); assert_noop!( - TwoPhase::feasibility_check(solution, COMPUTE), + MultiPhase::feasibility_check(solution, COMPUTE), FeasibilityError::InvalidWinner ); }) @@ -1143,10 +1143,10 @@ mod feasibility_check { // should be caught in `compact.into_assignment`. ExtBuilder::default().desired_targets(2).build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(TwoPhase::current_phase().is_signed()); + assert!(MultiPhase::current_phase().is_signed()); let mut solution = raw_solution(); - assert_eq!(TwoPhase::snapshot().unwrap().voters.len(), 8); + assert_eq!(MultiPhase::snapshot().unwrap().voters.len(), 8); // ----------------------------------------------------^^ valid range is [0..7]. // check that there is a index 7 in votes1, and flip to 8. @@ -1160,7 +1160,7 @@ mod feasibility_check { .count() > 0 ); assert_noop!( - TwoPhase::feasibility_check(solution, COMPUTE), + MultiPhase::feasibility_check(solution, COMPUTE), FeasibilityError::NposElection(sp_npos_elections::Error::CompactInvalidIndex), ); }) @@ -1170,10 +1170,10 @@ mod feasibility_check { fn voter_votes() { ExtBuilder::default().desired_targets(2).build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(TwoPhase::current_phase().is_signed()); + assert!(MultiPhase::current_phase().is_signed()); let mut solution = raw_solution(); - assert_eq!(TwoPhase::snapshot().unwrap().voters.len(), 8); + assert_eq!(MultiPhase::snapshot().unwrap().voters.len(), 8); // ----------------------------------------------------^^ valid range is [0..7]. // first, check that voter at index 7 (40) actually voted for 3 (40) -- this is self @@ -1189,7 +1189,7 @@ mod feasibility_check { 1, ); assert_noop!( - TwoPhase::feasibility_check(solution, COMPUTE), + MultiPhase::feasibility_check(solution, COMPUTE), FeasibilityError::InvalidVote, ); }) @@ -1199,16 +1199,16 @@ mod feasibility_check { fn score() { ExtBuilder::default().desired_targets(2).build_and_execute(|| { roll_to(::get() - ::get() - ::get()); - assert!(TwoPhase::current_phase().is_signed()); + assert!(MultiPhase::current_phase().is_signed()); let mut solution = raw_solution(); - assert_eq!(TwoPhase::snapshot().unwrap().voters.len(), 8); + assert_eq!(MultiPhase::snapshot().unwrap().voters.len(), 8); // simply faff with the score. solution.score[0] += 1; assert_noop!( - TwoPhase::feasibility_check(solution, COMPUTE), + MultiPhase::feasibility_check(solution, COMPUTE), FeasibilityError::InvalidScore, ); }) @@ -1229,60 +1229,60 @@ mod tests { // Signed Unsigned Signed Unsigned assert_eq!(System::block_number(), 0); - assert_eq!(TwoPhase::current_phase(), Phase::Off); - assert_eq!(TwoPhase::round(), 1); + assert_eq!(MultiPhase::current_phase(), Phase::Off); + assert_eq!(MultiPhase::round(), 1); roll_to(4); - assert_eq!(TwoPhase::current_phase(), Phase::Off); - assert!(TwoPhase::snapshot().is_none()); - assert_eq!(TwoPhase::round(), 1); + assert_eq!(MultiPhase::current_phase(), Phase::Off); + assert!(MultiPhase::snapshot().is_none()); + assert_eq!(MultiPhase::round(), 1); roll_to(15); - assert_eq!(TwoPhase::current_phase(), Phase::Signed); - assert_eq!(two_phase_events(), vec![Event::SignedPhaseStarted(1)]); - assert!(TwoPhase::snapshot().is_some()); - assert_eq!(TwoPhase::round(), 1); + assert_eq!(MultiPhase::current_phase(), Phase::Signed); + assert_eq!(multi_phase_events(), vec![Event::SignedPhaseStarted(1)]); + assert!(MultiPhase::snapshot().is_some()); + assert_eq!(MultiPhase::round(), 1); roll_to(24); - assert_eq!(TwoPhase::current_phase(), Phase::Signed); - assert!(TwoPhase::snapshot().is_some()); - assert_eq!(TwoPhase::round(), 1); + assert_eq!(MultiPhase::current_phase(), Phase::Signed); + assert!(MultiPhase::snapshot().is_some()); + assert_eq!(MultiPhase::round(), 1); roll_to(25); - assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); assert_eq!( - two_phase_events(), + multi_phase_events(), vec![Event::SignedPhaseStarted(1), Event::UnsignedPhaseStarted(1)], ); - assert!(TwoPhase::snapshot().is_some()); + assert!(MultiPhase::snapshot().is_some()); roll_to(29); - assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); - assert!(TwoPhase::snapshot().is_some()); + assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); + assert!(MultiPhase::snapshot().is_some()); roll_to(30); - assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); - assert!(TwoPhase::snapshot().is_some()); + assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); + assert!(MultiPhase::snapshot().is_some()); // we close when upstream tells us to elect. roll_to(32); - assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); - assert!(TwoPhase::snapshot().is_some()); + assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); + assert!(MultiPhase::snapshot().is_some()); - TwoPhase::elect().unwrap(); + MultiPhase::elect().unwrap(); - assert!(TwoPhase::current_phase().is_off()); - assert!(TwoPhase::snapshot().is_none()); - assert_eq!(TwoPhase::round(), 2); + assert!(MultiPhase::current_phase().is_off()); + assert!(MultiPhase::snapshot().is_none()); + assert_eq!(MultiPhase::round(), 2); roll_to(44); - assert!(TwoPhase::current_phase().is_off()); + assert!(MultiPhase::current_phase().is_off()); roll_to(45); - assert!(TwoPhase::current_phase().is_signed()); + assert!(MultiPhase::current_phase().is_signed()); roll_to(55); - assert!(TwoPhase::current_phase().is_unsigned_open_at(55)); + assert!(MultiPhase::current_phase().is_unsigned_open_at(55)); }) } @@ -1290,22 +1290,22 @@ mod tests { fn signed_phase_void() { ExtBuilder::default().phases(0, 10).build_and_execute(|| { roll_to(15); - assert!(TwoPhase::current_phase().is_off()); + assert!(MultiPhase::current_phase().is_off()); roll_to(19); - assert!(TwoPhase::current_phase().is_off()); + assert!(MultiPhase::current_phase().is_off()); roll_to(20); - assert!(TwoPhase::current_phase().is_unsigned_open_at(20)); - assert!(TwoPhase::snapshot().is_some()); + assert!(MultiPhase::current_phase().is_unsigned_open_at(20)); + assert!(MultiPhase::snapshot().is_some()); roll_to(30); - assert!(TwoPhase::current_phase().is_unsigned_open_at(20)); + assert!(MultiPhase::current_phase().is_unsigned_open_at(20)); - TwoPhase::elect().unwrap(); + MultiPhase::elect().unwrap(); - assert!(TwoPhase::current_phase().is_off()); - assert!(TwoPhase::snapshot().is_none()); + assert!(MultiPhase::current_phase().is_off()); + assert!(MultiPhase::snapshot().is_none()); }); } @@ -1313,22 +1313,22 @@ mod tests { fn unsigned_phase_void() { ExtBuilder::default().phases(10, 0).build_and_execute(|| { roll_to(15); - assert!(TwoPhase::current_phase().is_off()); + assert!(MultiPhase::current_phase().is_off()); roll_to(19); - assert!(TwoPhase::current_phase().is_off()); + assert!(MultiPhase::current_phase().is_off()); roll_to(20); - assert!(TwoPhase::current_phase().is_signed()); - assert!(TwoPhase::snapshot().is_some()); + assert!(MultiPhase::current_phase().is_signed()); + assert!(MultiPhase::snapshot().is_some()); roll_to(30); - assert!(TwoPhase::current_phase().is_signed()); + assert!(MultiPhase::current_phase().is_signed()); - let _ = TwoPhase::elect().unwrap(); + let _ = MultiPhase::elect().unwrap(); - assert!(TwoPhase::current_phase().is_off()); - assert!(TwoPhase::snapshot().is_none()); + assert!(MultiPhase::current_phase().is_off()); + assert!(MultiPhase::snapshot().is_none()); }); } @@ -1336,21 +1336,21 @@ mod tests { fn both_phases_void() { ExtBuilder::default().phases(0, 0).build_and_execute(|| { roll_to(15); - assert!(TwoPhase::current_phase().is_off()); + assert!(MultiPhase::current_phase().is_off()); roll_to(19); - assert!(TwoPhase::current_phase().is_off()); + assert!(MultiPhase::current_phase().is_off()); roll_to(20); - assert!(TwoPhase::current_phase().is_off()); + assert!(MultiPhase::current_phase().is_off()); roll_to(30); - assert!(TwoPhase::current_phase().is_off()); + assert!(MultiPhase::current_phase().is_off()); // this module is now only capable of doing on-chain backup. - let _ = TwoPhase::elect().unwrap(); + let _ = MultiPhase::elect().unwrap(); - assert!(TwoPhase::current_phase().is_off()); + assert!(MultiPhase::current_phase().is_off()); }); } @@ -1360,31 +1360,31 @@ mod tests { ExtBuilder::default().build_and_execute(|| { // signed phase started at block 15 and will end at 25. roll_to(14); - assert_eq!(TwoPhase::current_phase(), Phase::Off); + assert_eq!(MultiPhase::current_phase(), Phase::Off); roll_to(15); - assert_eq!(two_phase_events(), vec![Event::SignedPhaseStarted(1)]); - assert_eq!(TwoPhase::current_phase(), Phase::Signed); - assert_eq!(TwoPhase::round(), 1); + assert_eq!(multi_phase_events(), vec![Event::SignedPhaseStarted(1)]); + assert_eq!(MultiPhase::current_phase(), Phase::Signed); + assert_eq!(MultiPhase::round(), 1); // an unexpected call to elect. roll_to(20); - TwoPhase::elect().unwrap(); + MultiPhase::elect().unwrap(); // we surely can't have any feasible solutions. This will cause an on-chain election. assert_eq!( - two_phase_events(), + multi_phase_events(), vec![ Event::SignedPhaseStarted(1), Event::ElectionFinalized(Some(ElectionCompute::OnChain)) ], ); // all storage items must be cleared. - assert_eq!(TwoPhase::round(), 2); - assert!(TwoPhase::snapshot().is_none()); - assert!(TwoPhase::snapshot_metadata().is_none()); - assert!(TwoPhase::desired_targets().is_none()); - assert!(TwoPhase::queued_solution().is_none()); + assert_eq!(MultiPhase::round(), 2); + assert!(MultiPhase::snapshot().is_none()); + assert!(MultiPhase::snapshot_metadata().is_none()); + assert!(MultiPhase::desired_targets().is_none()); + assert!(MultiPhase::queued_solution().is_none()); }) } @@ -1392,13 +1392,13 @@ mod tests { fn fallback_strategy_works() { ExtBuilder::default().fallabck(FallbackStrategy::OnChain).build_and_execute(|| { roll_to(15); - assert_eq!(TwoPhase::current_phase(), Phase::Signed); + assert_eq!(MultiPhase::current_phase(), Phase::Signed); roll_to(25); - assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); // zilch solutions thus far. - let supports = TwoPhase::elect().unwrap(); + let supports = MultiPhase::elect().unwrap(); assert_eq!( supports, @@ -1411,13 +1411,13 @@ mod tests { ExtBuilder::default().fallabck(FallbackStrategy::Nothing).build_and_execute(|| { roll_to(15); - assert_eq!(TwoPhase::current_phase(), Phase::Signed); + assert_eq!(MultiPhase::current_phase(), Phase::Signed); roll_to(25); - assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); // zilch solutions thus far. - assert_eq!(TwoPhase::elect().unwrap_err(), ElectionError::NoFallbackConfigured); + assert_eq!(MultiPhase::elect().unwrap_err(), ElectionError::NoFallbackConfigured); }) } @@ -1426,8 +1426,8 @@ mod tests { // Just a rough estimate with the substrate weights. assert!(!MockWeightInfo::get()); - let all_voters: u32 = 100_000; - let all_targets: u32 = 2_000; + let all_voters: u32 = 10_000; + let all_targets: u32 = 5_000; let desired: u32 = 1_000; let weight_with = |active| { ::WeightInfo::submit_unsigned( @@ -1441,6 +1441,7 @@ mod tests { let mut active = 1; while weight_with(active) <= ::BlockWeights::get().max_block + || active == all_voters { active += 1; } diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 0b9fed1d21cf2..eb38a4cd52e95 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -16,7 +16,7 @@ // limitations under the License. use super::*; -use crate as two_phase; +use crate as multi_phase; pub use frame_support::{assert_noop, assert_ok}; use frame_support::{ parameter_types, @@ -54,7 +54,7 @@ frame_support::construct_runtime!( { System: frame_system::{Module, Call, Event, Config}, Balances: pallet_balances::{Module, Call, Event, Config}, - TwoPhase: two_phase::{Module, Call, Event}, + MultiPhase: multi_phase::{Module, Call, Event}, } ); @@ -67,11 +67,11 @@ sp_npos_elections::generate_solution_type!( ); /// All events of this pallet. -pub(crate) fn two_phase_events() -> Vec> { +pub(crate) fn multi_phase_events() -> Vec> { System::events() .into_iter() .map(|r| r.event) - .filter_map(|e| if let Event::two_phase(inner) = e { Some(inner) } else { None }) + .filter_map(|e| if let Event::multi_phase(inner) = e { Some(inner) } else { None }) .collect::>() } @@ -80,7 +80,7 @@ pub fn roll_to(n: u64) { let now = System::block_number(); for i in now + 1..=n { System::set_block_number(i); - TwoPhase::on_initialize(i); + MultiPhase::on_initialize(i); } } @@ -88,8 +88,8 @@ pub fn roll_to_with_ocw(n: u64) { let now = System::block_number(); for i in now + 1..=n { System::set_block_number(i); - TwoPhase::on_initialize(i); - TwoPhase::offchain_worker(i); + MultiPhase::on_initialize(i); + MultiPhase::offchain_worker(i); } } @@ -97,8 +97,8 @@ pub fn roll_to_with_ocw(n: u64) { /// /// This is a good example of what an offchain miner would do. pub fn raw_solution() -> RawSolution> { - let RoundSnapshot { voters, targets } = TwoPhase::snapshot().unwrap(); - let desired_targets = TwoPhase::desired_targets().unwrap(); + let RoundSnapshot { voters, targets } = MultiPhase::snapshot().unwrap(); + let desired_targets = MultiPhase::desired_targets().unwrap(); // closures let cache = helpers::generate_voter_cache::(&voters); @@ -123,12 +123,12 @@ pub fn raw_solution() -> RawSolution> { let compact = >::from_assignment(assignments, &voter_index, &target_index).unwrap(); - let round = TwoPhase::round(); + let round = MultiPhase::round(); RawSolution { compact, score, round } } pub fn witness() -> SolutionOrSnapshotSize { - TwoPhase::snapshot() + MultiPhase::snapshot() .map(|snap| SolutionOrSnapshotSize { voters: snap.voters.len() as u32, targets: snap.targets.len() as u32, @@ -210,33 +210,33 @@ parameter_types! { // Hopefully this won't be too much of a hassle to maintain. pub struct DualMockWeightInfo; -impl two_phase::weights::WeightInfo for DualMockWeightInfo { +impl multi_phase::weights::WeightInfo for DualMockWeightInfo { fn on_initialize_nothing() -> Weight { if MockWeightInfo::get() { Zero::zero() } else { - <() as two_phase::weights::WeightInfo>::on_initialize_nothing() + <() as multi_phase::weights::WeightInfo>::on_initialize_nothing() } } fn on_initialize_open_signed() -> Weight { if MockWeightInfo::get() { Zero::zero() } else { - <() as two_phase::weights::WeightInfo>::on_initialize_open_signed() + <() as multi_phase::weights::WeightInfo>::on_initialize_open_signed() } } fn on_initialize_open_unsigned_with_snapshot() -> Weight { if MockWeightInfo::get() { Zero::zero() } else { - <() as two_phase::weights::WeightInfo>::on_initialize_open_unsigned_with_snapshot() + <() 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 two_phase::weights::WeightInfo>::on_initialize_open_unsigned_without_snapshot() + <() as multi_phase::weights::WeightInfo>::on_initialize_open_unsigned_without_snapshot() } } fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight { @@ -245,7 +245,7 @@ impl two_phase::weights::WeightInfo for DualMockWeightInfo { // 5 per edge. (10 as Weight).saturating_add((5 as Weight).saturating_mul(a as Weight)) } else { - <() as two_phase::weights::WeightInfo>::submit_unsigned(v, t, a, d) + <() as multi_phase::weights::WeightInfo>::submit_unsigned(v, t, a, d) } } fn feasibility_check(v: u32, t: u32, a: u32, d: u32) -> Weight { @@ -254,7 +254,7 @@ impl two_phase::weights::WeightInfo for DualMockWeightInfo { // 5 per edge. (10 as Weight).saturating_add((5 as Weight).saturating_mul(a as Weight)) } else { - <() as two_phase::weights::WeightInfo>::feasibility_check(v, t, a, d) + <() as multi_phase::weights::WeightInfo>::feasibility_check(v, t, a, d) } } } diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index ecc48557124a6..de66e2d77347e 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -399,54 +399,54 @@ mod max_weight { fn find_max_voter_binary_search_works() { let w = SolutionOrSnapshotSize { voters: 10, targets: 0 }; - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 0), 0); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1), 0); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 999), 0); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1000), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1001), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1990), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1999), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2000), 2); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2001), 2); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2010), 2); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2990), 2); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2999), 2); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 3000), 3); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 3333), 3); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 5500), 5); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 7777), 7); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 9999), 9); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 10_000), 10); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 10_999), 10); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 11_000), 10); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 22_000), 10); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 0), 0); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1), 0); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 999), 0); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1000), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1001), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1990), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1999), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 2000), 2); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 2001), 2); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 2010), 2); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 2990), 2); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 2999), 2); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 3000), 3); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 3333), 3); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 5500), 5); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 7777), 7); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 9999), 9); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 10_000), 10); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 10_999), 10); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 11_000), 10); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 22_000), 10); let w = SolutionOrSnapshotSize { voters: 1, targets: 0 }; - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 0), 0); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1), 0); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 999), 0); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1000), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1001), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1990), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1999), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2000), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2001), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2010), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 3333), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 0), 0); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1), 0); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 999), 0); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1000), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1001), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1990), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1999), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 2000), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 2001), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 2010), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 3333), 1); let w = SolutionOrSnapshotSize { voters: 2, targets: 0 }; - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 0), 0); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1), 0); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 999), 0); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1000), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1001), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 1999), 1); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2000), 2); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2001), 2); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 2010), 2); - assert_eq!(TwoPhase::maximum_voter_for_weight::(0, w, 3333), 2); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 0), 0); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1), 0); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 999), 0); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1000), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1001), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 1999), 1); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 2000), 2); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 2001), 2); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 2010), 2); + assert_eq!(MultiPhase::maximum_voter_for_weight::(0, w, 3333), 2); } } @@ -468,51 +468,51 @@ mod tests { let call = Call::submit_unsigned(solution.clone(), witness()); // initial - assert_eq!(TwoPhase::current_phase(), Phase::Off); + assert_eq!(MultiPhase::current_phase(), Phase::Off); assert!(matches!( - ::validate_unsigned(TransactionSource::Local, &call) + ::validate_unsigned(TransactionSource::Local, &call) .unwrap_err(), TransactionValidityError::Invalid(InvalidTransaction::Custom(0)) )); assert!(matches!( - ::pre_dispatch(&call).unwrap_err(), + ::pre_dispatch(&call).unwrap_err(), TransactionValidityError::Invalid(InvalidTransaction::Custom(0)) )); // signed roll_to(15); - assert_eq!(TwoPhase::current_phase(), Phase::Signed); + assert_eq!(MultiPhase::current_phase(), Phase::Signed); assert!(matches!( - ::validate_unsigned(TransactionSource::Local, &call) + ::validate_unsigned(TransactionSource::Local, &call) .unwrap_err(), TransactionValidityError::Invalid(InvalidTransaction::Custom(0)) )); assert!(matches!( - ::pre_dispatch(&call).unwrap_err(), + ::pre_dispatch(&call).unwrap_err(), TransactionValidityError::Invalid(InvalidTransaction::Custom(0)) )); // unsigned roll_to(25); - assert!(TwoPhase::current_phase().is_unsigned()); + assert!(MultiPhase::current_phase().is_unsigned()); - assert!(::validate_unsigned( + assert!(::validate_unsigned( TransactionSource::Local, &call ) .is_ok()); - assert!(::pre_dispatch(&call).is_ok()); + assert!(::pre_dispatch(&call).is_ok()); // unsigned -- but not enabled. >::put(Phase::Unsigned((false, 25))); - assert!(TwoPhase::current_phase().is_unsigned()); + assert!(MultiPhase::current_phase().is_unsigned()); assert!(matches!( - ::validate_unsigned(TransactionSource::Local, &call) + ::validate_unsigned(TransactionSource::Local, &call) .unwrap_err(), TransactionValidityError::Invalid(InvalidTransaction::Custom(0)) )); assert!(matches!( - ::pre_dispatch(&call).unwrap_err(), + ::pre_dispatch(&call).unwrap_err(), TransactionValidityError::Invalid(InvalidTransaction::Custom(0)) )); }) @@ -522,18 +522,18 @@ mod tests { fn validate_unsigned_retracts_low_score() { ExtBuilder::default().desired_targets(0).build_and_execute(|| { roll_to(25); - assert!(TwoPhase::current_phase().is_unsigned()); + assert!(MultiPhase::current_phase().is_unsigned()); let solution = RawSolution:: { score: [5, 0, 0], ..Default::default() }; let call = Call::submit_unsigned(solution.clone(), witness()); // initial - assert!(::validate_unsigned( + assert!(::validate_unsigned( TransactionSource::Local, &call ) .is_ok()); - assert!(::pre_dispatch(&call).is_ok()); + assert!(::pre_dispatch(&call).is_ok()); // set a better score let ready = ReadySolution { score: [10, 0, 0], ..Default::default() }; @@ -541,12 +541,15 @@ mod tests { // won't work anymore. assert!(matches!( - ::validate_unsigned(TransactionSource::Local, &call) - .unwrap_err(), + ::validate_unsigned( + TransactionSource::Local, + &call + ) + .unwrap_err(), TransactionValidityError::Invalid(InvalidTransaction::Custom(2)) )); assert!(matches!( - ::pre_dispatch(&call).unwrap_err(), + ::pre_dispatch(&call).unwrap_err(), TransactionValidityError::Invalid(InvalidTransaction::Custom(2)) )); }) @@ -556,7 +559,7 @@ mod tests { fn validate_unsigned_retracts_incorrect_winner_count() { ExtBuilder::default().desired_targets(1).build_and_execute(|| { roll_to(25); - assert!(TwoPhase::current_phase().is_unsigned()); + assert!(MultiPhase::current_phase().is_unsigned()); let solution = RawSolution:: { score: [5, 0, 0], ..Default::default() }; let call = Call::submit_unsigned(solution.clone(), witness()); @@ -564,8 +567,11 @@ mod tests { // won't work anymore. assert!(matches!( - ::validate_unsigned(TransactionSource::Local, &call) - .unwrap_err(), + ::validate_unsigned( + TransactionSource::Local, + &call + ) + .unwrap_err(), TransactionValidityError::Invalid(InvalidTransaction::Custom(1)) )); }) @@ -575,15 +581,18 @@ mod tests { fn priority_is_set() { ExtBuilder::default().miner_tx_priority(20).desired_targets(0).build_and_execute(|| { roll_to(25); - assert!(TwoPhase::current_phase().is_unsigned()); + assert!(MultiPhase::current_phase().is_unsigned()); let solution = RawSolution:: { score: [5, 0, 0], ..Default::default() }; let call = Call::submit_unsigned(solution.clone(), witness()); assert_eq!( - ::validate_unsigned(TransactionSource::Local, &call) - .unwrap() - .priority, + ::validate_unsigned( + TransactionSource::Local, + &call + ) + .unwrap() + .priority, 25 ); }) @@ -597,7 +606,7 @@ mod tests { fn unfeasible_solution_panics() { ExtBuilder::default().build_and_execute(|| { roll_to(25); - assert!(TwoPhase::current_phase().is_unsigned()); + assert!(MultiPhase::current_phase().is_unsigned()); // This is in itself an invalid BS solution. let solution = RawSolution:: { score: [5, 0, 0], ..Default::default() }; @@ -613,7 +622,7 @@ mod tests { fn wrong_witness_panics() { ExtBuilder::default().build_and_execute(|| { roll_to(25); - assert!(TwoPhase::current_phase().is_unsigned()); + assert!(MultiPhase::current_phase().is_unsigned()); // This solution is unfeasible as well, but we won't even get there. let solution = RawSolution:: { score: [5, 0, 0], ..Default::default() }; @@ -631,19 +640,19 @@ mod tests { fn miner_works() { ExtBuilder::default().build_and_execute(|| { roll_to(25); - assert!(TwoPhase::current_phase().is_unsigned()); + assert!(MultiPhase::current_phase().is_unsigned()); // ensure we have snapshots in place. - assert!(TwoPhase::snapshot().is_some()); - assert_eq!(TwoPhase::desired_targets().unwrap(), 2); + assert!(MultiPhase::snapshot().is_some()); + assert_eq!(MultiPhase::desired_targets().unwrap(), 2); // mine seq_phragmen solution with 2 iters. - let (solution, witness) = TwoPhase::mine_solution(2).unwrap(); + let (solution, witness) = MultiPhase::mine_solution(2).unwrap(); // ensure this solution is valid. - assert!(TwoPhase::queued_solution().is_none()); - assert_ok!(TwoPhase::submit_unsigned(Origin::none(), solution, witness)); - assert!(TwoPhase::queued_solution().is_some()); + assert!(MultiPhase::queued_solution().is_none()); + assert_ok!(MultiPhase::submit_unsigned(Origin::none(), solution, witness)); + assert!(MultiPhase::queued_solution().is_some()); }) } @@ -651,9 +660,9 @@ mod tests { fn miner_trims_weight() { ExtBuilder::default().miner_weight(100).mock_weight_info(true).build_and_execute(|| { roll_to(25); - assert!(TwoPhase::current_phase().is_unsigned()); + assert!(MultiPhase::current_phase().is_unsigned()); - let (solution, witness) = TwoPhase::mine_solution(2).unwrap(); + let (solution, witness) = MultiPhase::mine_solution(2).unwrap(); let solution_weight = ::WeightInfo::submit_unsigned( witness.voters, witness.targets, @@ -667,7 +676,7 @@ mod tests { // now reduce the max weight ::set(25); - let (solution, witness) = TwoPhase::mine_solution(2).unwrap(); + let (solution, witness) = MultiPhase::mine_solution(2).unwrap(); let solution_weight = ::WeightInfo::submit_unsigned( witness.voters, witness.targets, @@ -685,11 +694,11 @@ mod tests { let (mut ext, _) = ExtBuilder::default().desired_targets(8).build_offchainify(0); ext.execute_with(|| { roll_to(25); - assert!(TwoPhase::current_phase().is_unsigned()); + assert!(MultiPhase::current_phase().is_unsigned()); // mine seq_phragmen solution with 2 iters. assert_eq!( - TwoPhase::mine_and_submit().unwrap_err(), + MultiPhase::mine_and_submit().unwrap_err(), MinerError::PreDispatchChecksFailed, ); }) @@ -704,8 +713,8 @@ mod tests { .solution_improvement_threshold(Perbill::from_percent(50)) .build_and_execute(|| { roll_to(25); - assert!(TwoPhase::current_phase().is_unsigned()); - assert_eq!(TwoPhase::desired_targets().unwrap(), 1); + assert!(MultiPhase::current_phase().is_unsigned()); + assert_eq!(MultiPhase::desired_targets().unwrap(), 1); // an initial solution let result = ElectionResult { @@ -716,10 +725,10 @@ mod tests { distribution: vec![(10, PerU16::one())], }], }; - let (solution, witness) = TwoPhase::prepare_election_result(result).unwrap(); - assert_ok!(TwoPhase::unsigned_pre_dispatch_checks(&solution)); - assert_ok!(TwoPhase::submit_unsigned(Origin::none(), solution, witness)); - assert_eq!(TwoPhase::queued_solution().unwrap().score[0], 10); + let (solution, witness) = MultiPhase::prepare_election_result(result).unwrap(); + assert_ok!(MultiPhase::unsigned_pre_dispatch_checks(&solution)); + assert_ok!(MultiPhase::submit_unsigned(Origin::none(), solution, witness)); + assert_eq!(MultiPhase::queued_solution().unwrap().score[0], 10); // trial 1: a solution who's score is only 2, i.e. 20% better in the first element. let result = ElectionResult { @@ -733,11 +742,11 @@ mod tests { }, ], }; - let (solution, _) = TwoPhase::prepare_election_result(result).unwrap(); + let (solution, _) = MultiPhase::prepare_election_result(result).unwrap(); // 12 is not 50% more than 10 assert_eq!(solution.score[0], 12); assert_noop!( - TwoPhase::unsigned_pre_dispatch_checks(&solution), + MultiPhase::unsigned_pre_dispatch_checks(&solution), Error::::PreDispatchWeakSubmission, ); // submitting this will actually panic. @@ -755,12 +764,12 @@ mod tests { }, ], }; - let (solution, witness) = TwoPhase::prepare_election_result(result).unwrap(); + let (solution, witness) = MultiPhase::prepare_election_result(result).unwrap(); assert_eq!(solution.score[0], 17); // and it is fine - assert_ok!(TwoPhase::unsigned_pre_dispatch_checks(&solution)); - assert_ok!(TwoPhase::submit_unsigned(Origin::none(), solution, witness)); + assert_ok!(MultiPhase::unsigned_pre_dispatch_checks(&solution)); + assert_ok!(MultiPhase::submit_unsigned(Origin::none(), solution, witness)); }) } @@ -769,31 +778,27 @@ mod tests { let (mut ext, _) = ExtBuilder::default().build_offchainify(0); ext.execute_with(|| { roll_to(25); - assert!(TwoPhase::current_phase().is_unsigned()); + assert!(MultiPhase::current_phase().is_unsigned()); // first execution -- okay. - assert!(TwoPhase::try_acquire_offchain_lock(25).is_ok()); + assert!(MultiPhase::try_acquire_offchain_lock(25).is_ok()); // next block: rejected. - assert!(TwoPhase::try_acquire_offchain_lock(26).is_err()); + assert!(MultiPhase::try_acquire_offchain_lock(26).is_err()); // allowed after `OFFCHAIN_REPEAT` - assert!(TwoPhase::try_acquire_offchain_lock((26 + OFFCHAIN_REPEAT).into()) - .is_ok()); + assert!(MultiPhase::try_acquire_offchain_lock((26 + OFFCHAIN_REPEAT).into()).is_ok()); // a fork like situation: re-execute last 3. - assert!(TwoPhase::try_acquire_offchain_lock( - (26 + OFFCHAIN_REPEAT - 3).into() - ) - .is_err()); - assert!(TwoPhase::try_acquire_offchain_lock( - (26 + OFFCHAIN_REPEAT - 2).into() - ) - .is_err()); - assert!(TwoPhase::try_acquire_offchain_lock( - (26 + OFFCHAIN_REPEAT - 1).into() - ) - .is_err()); + assert!( + MultiPhase::try_acquire_offchain_lock((26 + OFFCHAIN_REPEAT - 3).into()).is_err() + ); + assert!( + MultiPhase::try_acquire_offchain_lock((26 + OFFCHAIN_REPEAT - 2).into()).is_err() + ); + assert!( + MultiPhase::try_acquire_offchain_lock((26 + OFFCHAIN_REPEAT - 1).into()).is_err() + ); }) } @@ -802,22 +807,22 @@ mod tests { let (mut ext, pool) = ExtBuilder::default().build_offchainify(0); ext.execute_with(|| { roll_to(25); - assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); // we must clear the offchain storage to ensure the offchain execution check doesn't get // in the way. let mut storage = StorageValueRef::persistent(&OFFCHAIN_HEAD_DB); - TwoPhase::offchain_worker(24); + MultiPhase::offchain_worker(24); assert!(pool.read().transactions.len().is_zero()); storage.clear(); - TwoPhase::offchain_worker(26); + MultiPhase::offchain_worker(26); assert!(pool.read().transactions.len().is_zero()); storage.clear(); // submits! - TwoPhase::offchain_worker(25); + MultiPhase::offchain_worker(25); assert!(!pool.read().transactions.len().is_zero()); }) } @@ -827,13 +832,13 @@ mod tests { let (mut ext, pool) = ExtBuilder::default().build_offchainify(0); ext.execute_with(|| { roll_to_with_ocw(25); - assert_eq!(TwoPhase::current_phase(), Phase::Unsigned((true, 25))); + assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); // OCW must have submitted now let encoded = pool.read().transactions[0].clone(); let extrinsic: Extrinsic = Decode::decode(&mut &*encoded).unwrap(); let call = extrinsic.call; - assert!(matches!(call, OuterCall::TwoPhase(Call::submit_unsigned(_, _)))); + assert!(matches!(call, OuterCall::MultiPhase(Call::submit_unsigned(_, _)))); }) } } diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs index 6070b771593ce..02fdd3531279f 100644 --- a/frame/election-provider-multi-phase/src/weights.rs +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_two_phase_election_provider +//! Autogenerated weights for pallet_multi_phase_election_provider //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.1 //! DATE: 2021-01-14, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] @@ -27,7 +27,7 @@ // --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_two_phase_election_provider +// --pallet=pallet_multi_phase_election_provider // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -44,7 +44,7 @@ use frame_support::{ }; use sp_std::marker::PhantomData; -/// Weight functions needed for pallet_two_phase_election_provider. +/// Weight functions needed for pallet_multi_phase_election_provider. pub trait WeightInfo { fn on_initialize_nothing() -> Weight; fn on_initialize_open_signed() -> Weight; @@ -54,7 +54,7 @@ pub trait WeightInfo { fn feasibility_check(v: u32, t: u32, a: u32, d: u32) -> Weight; } -/// Weights for pallet_two_phase_election_provider using the Substrate node and recommended +/// Weights for pallet_multi_phase_election_provider using the Substrate node and recommended /// hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { From 1a5794a6c076be2881f55aaf50ecae3d11a73b28 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 12 Feb 2021 10:33:31 +0000 Subject: [PATCH 15/57] Do an assert as well, just in case. --- .../src/benchmarking.rs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index daac85f06790a..b1ab6bfe0e9e3 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -212,11 +212,16 @@ benchmarks! { >::put(Phase::Unsigned((true, 1u32.into()))); // encode the most significant storage item that needs to be decoded in the dispatch. - let encoded_snapshot = >::snapshot().encode(); + let snapshot = >::snapshot(); + let encoded_snapshot = snapshot.encode(); + let voters_len = snapshot.voters.len(); }: { assert_ok!(>::submit_unsigned(RawOrigin::None.into(), raw_solution, witness)); - let _decoded = as Decode>::decode(&mut &*encoded_snapshot).unwrap(); - // NOTE: assert that this line is optimized away by the compiler in any way! + assert_eq!( + as Decode>::decode(&mut &*encoded_snapshot).unwrap().voters.len(), + voters_len, + ); + // This whole assert is to ensure that the statement is not optimized away. } verify { assert!(>::queued_solution().is_some()); } @@ -240,11 +245,17 @@ benchmarks! { assert_eq!(raw_solution.compact.unique_targets().len() as u32, d); // encode the most significant storage item that needs to be decoded in the dispatch. - let encoded_snapshot = >::snapshot().encode(); + let snapshot = >::snapshot(); + let encoded_snapshot = snapshot.encode(); + let voters_len = snapshot.voters.len(); }: { assert_ok!(>::feasibility_check(raw_solution, ElectionCompute::Unsigned)); - let _decoded = as Decode>::decode(&mut &*encoded_snapshot).unwrap(); - // NOTE: assert that this line is optimized away by the compiler in any way! + assert_eq!( + as Decode>::decode(&mut &*encoded_snapshot).unwrap().voters.len(), + voters_len, + ); + // This whole assert is to ensure that the statement is not optimized away. + } } From 01e63ed222c4e467e8873637f346655ea781e7e6 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 12 Feb 2021 10:44:33 +0000 Subject: [PATCH 16/57] Fix build --- frame/election-provider-multi-phase/src/benchmarking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index b1ab6bfe0e9e3..c069ade162fdc 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -212,7 +212,7 @@ benchmarks! { >::put(Phase::Unsigned((true, 1u32.into()))); // encode the most significant storage item that needs to be decoded in the dispatch. - let snapshot = >::snapshot(); + let snapshot = >::snapshot().unwrap(); let encoded_snapshot = snapshot.encode(); let voters_len = snapshot.voters.len(); }: { @@ -245,7 +245,7 @@ benchmarks! { assert_eq!(raw_solution.compact.unique_targets().len() as u32, d); // encode the most significant storage item that needs to be decoded in the dispatch. - let snapshot = >::snapshot(); + let snapshot = >::snapshot().unwrap(); let encoded_snapshot = snapshot.encode(); let voters_len = snapshot.voters.len(); }: { From 4ccecdf1d30cd775349c451492320dc29ca83e56 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Fri, 12 Feb 2021 11:48:07 +0000 Subject: [PATCH 17/57] Update frame/election-provider-multi-phase/src/unsigned.rs Co-authored-by: Guillaume Thiolliere --- frame/election-provider-multi-phase/src/unsigned.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index de66e2d77347e..be143e55694b6 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -178,7 +178,7 @@ impl Pallet { /// down the line. /// /// Indeed, the score must be computed **after** this step. If this step reduces the score too - /// much, then the solution will be discarded. + /// much or remove a winner, then the solution must be discarded **after** this step. pub fn trim_compact( maximum_allowed_voters: u32, mut compact: CompactOf, From 83a789ece85c49383e1e68656894ece52f853cfc Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 12 Feb 2021 11:48:41 +0000 Subject: [PATCH 18/57] Las comment --- frame/election-provider-multi-phase/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index ee89beb04a10b..935b8c1fb7c9a 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -921,6 +921,10 @@ impl Pallet { let voter_index = helpers::voter_index_fn_usize::(&cache); // first, make sure that all the winners are sane. + // OPTIMIZATION: we could first build the assignments, and then extract the winners directly + // from that, as that would eliminate a little bit of duplicate work. For now, we keep them + // separate: First extract winners separately from compact, and then assignments. This is + // also better, because we can reject solutions that don't meet `desired_targets` early on. let winners = winners .into_iter() .map(|i| target_at(i).ok_or(FeasibilityError::InvalidWinner)) From 7c71df031cd63d5428b8a7d0bddea5ca63af80c7 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 12 Feb 2021 12:20:52 +0000 Subject: [PATCH 19/57] fix staking fuzzer. --- frame/staking/fuzzer/src/submit_solution.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/staking/fuzzer/src/submit_solution.rs b/frame/staking/fuzzer/src/submit_solution.rs index d94ee49b96db4..b661a83a1bdd0 100644 --- a/frame/staking/fuzzer/src/submit_solution.rs +++ b/frame/staking/fuzzer/src/submit_solution.rs @@ -164,7 +164,7 @@ fn main() { assert_eq!( call.dispatch_bypass_filter(origin.into()).unwrap_err().error, DispatchError::Module { - index: 0, + index: 2, error: 16, message: Some("OffchainElectionWeakSubmission"), }, From 12b1640963edde54da77da31496277142e8e79a1 Mon Sep 17 00:00:00 2001 From: Parity Benchmarking Bot Date: Fri, 12 Feb 2021 13:18:20 +0000 Subject: [PATCH 20/57] 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 --- .../src/weights.rs | 113 +++++++++--------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs index 02fdd3531279f..e34b3b4d7f226 100644 --- a/frame/election-provider-multi-phase/src/weights.rs +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -15,10 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_multi_phase_election_provider +//! Autogenerated weights for pallet_election_provider_multi_phase //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.1 -//! DATE: 2021-01-14, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 +//! DATE: 2021-02-12, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -27,76 +27,76 @@ // --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_multi_phase_election_provider +// --pallet=pallet_election_provider_multi_phase // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/two-phase-election-provider/src/weights.rs +// --output=./frame/election-provider-multi-phase/src/weights.rs // --template=./.maintain/frame-weight-template.hbs + #![allow(unused_parens)] #![allow(unused_imports)] -use frame_support::{ - traits::Get, - weights::{Weight, constants::RocksDbWeight}, -}; +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use sp_std::marker::PhantomData; -/// Weight functions needed for pallet_multi_phase_election_provider. +/// Weight functions needed for pallet_election_provider_multi_phase. pub trait WeightInfo { fn on_initialize_nothing() -> Weight; fn on_initialize_open_signed() -> Weight; - fn on_initialize_open_unsigned_without_snapshot() -> Weight; fn on_initialize_open_unsigned_with_snapshot() -> Weight; - fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight; - fn feasibility_check(v: u32, t: u32, a: u32, d: u32) -> Weight; + fn on_initialize_open_unsigned_without_snapshot() -> Weight; + fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight; + fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight; } -/// Weights for pallet_multi_phase_election_provider using the Substrate node and recommended -/// hardware. +/// Weights for pallet_election_provider_multi_phase using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn on_initialize_nothing() -> Weight { - (21_280_000 as Weight).saturating_add(T::DbWeight::get().reads(7 as Weight)) + (23_272_000 as Weight) + .saturating_add(T::DbWeight::get().reads(7 as Weight)) } fn on_initialize_open_signed() -> Weight { - (74_221_000 as Weight) + (78_018_000 as Weight) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_with_snapshot() -> Weight { - (76_100_000 as Weight) - .saturating_add(T::DbWeight::get().reads(8 as Weight)) + (76_963_000 as Weight) + .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_without_snapshot() -> Weight { - (76_100_000 as Weight) - .saturating_add(T::DbWeight::get().reads(8 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) + (21_235_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn submit_unsigned(v: u32, _t: u32, a: u32, d: u32) -> Weight { + fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 21_000 - .saturating_add((2_606_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 21_000 - .saturating_add((11_405_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 108_000 - .saturating_add((2_651_000 as Weight).saturating_mul(d as Weight)) + // Standard Error: 22_000 + .saturating_add((4_261_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 73_000 + .saturating_add((311_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 22_000 + .saturating_add((13_490_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 110_000 + .saturating_add((4_677_000 as Weight).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn feasibility_check(v: u32, t: u32, a: u32, d: u32) -> Weight { + fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) // Standard Error: 12_000 - .saturating_add((2_788_000 as Weight).saturating_mul(v as Weight)) + .saturating_add((4_335_000 as Weight).saturating_mul(v as Weight)) // Standard Error: 41_000 - .saturating_add((601_000 as Weight).saturating_mul(t as Weight)) + .saturating_add((564_000 as Weight).saturating_mul(t as Weight)) // Standard Error: 12_000 - .saturating_add((9_722_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 61_000 - .saturating_add((3_706_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((10_563_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 62_000 + .saturating_add((4_750_000 as Weight).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) } } @@ -104,44 +104,47 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn on_initialize_nothing() -> Weight { - (21_280_000 as Weight).saturating_add(RocksDbWeight::get().reads(7 as Weight)) + (23_272_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(7 as Weight)) } fn on_initialize_open_signed() -> Weight { - (74_221_000 as Weight) + (78_018_000 as Weight) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_with_snapshot() -> Weight { - (76_100_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(8 as Weight)) + (76_963_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_without_snapshot() -> Weight { - (76_100_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(8 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + (21_235_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn submit_unsigned(v: u32, _t: u32, a: u32, d: u32) -> Weight { + fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 21_000 - .saturating_add((2_606_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 21_000 - .saturating_add((11_405_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 108_000 - .saturating_add((2_651_000 as Weight).saturating_mul(d as Weight)) + // Standard Error: 22_000 + .saturating_add((4_261_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 73_000 + .saturating_add((311_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 22_000 + .saturating_add((13_490_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 110_000 + .saturating_add((4_677_000 as Weight).saturating_mul(d as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn feasibility_check(v: u32, t: u32, a: u32, d: u32) -> Weight { + fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) // Standard Error: 12_000 - .saturating_add((2_788_000 as Weight).saturating_mul(v as Weight)) + .saturating_add((4_335_000 as Weight).saturating_mul(v as Weight)) // Standard Error: 41_000 - .saturating_add((601_000 as Weight).saturating_mul(t as Weight)) + .saturating_add((564_000 as Weight).saturating_mul(t as Weight)) // Standard Error: 12_000 - .saturating_add((9_722_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 61_000 - .saturating_add((3_706_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((10_563_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 62_000 + .saturating_add((4_750_000 as Weight).saturating_mul(d as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) } } From 6397d4a0dc182f8ad98ce5787ce44cc98e2b6ff1 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 12 Feb 2021 13:25:36 +0000 Subject: [PATCH 21/57] Add one last layer of feasibility check as well. --- .../election-provider-multi-phase/src/lib.rs | 2 +- .../src/unsigned.rs | 39 +++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 935b8c1fb7c9a..1c28921338d8d 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -236,7 +236,7 @@ mod mock; #[macro_use] pub mod helpers; -const LOG_TARGET: &'static str = "election-provider"; +const LOG_TARGET: &'static str = "runtime::election-provider"; pub mod unsigned; pub mod weights; diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index be143e55694b6..b7589ca2d327c 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -44,6 +44,8 @@ pub enum MinerError { PoolSubmissionFailed, /// The pre-dispatch checks failed for the mined solution. PreDispatchChecksFailed, + /// The solution generated from the miner is not feasible. + Feasibility(FeasibilityError), } impl From for MinerError { @@ -52,23 +54,46 @@ impl From for MinerError { } } +impl From for MinerError { + fn from(e: FeasibilityError) -> Self { + MinerError::Feasibility(e) + } +} + impl Pallet { /// Mine a new solution, and submit it back to the chain as an unsigned transaction. - pub(crate) fn mine_and_submit() -> Result<(), MinerError> { - let balancing = Self::get_balancing_iters(); + pub fn mine_check_and_submit() -> Result<(), MinerError> { + let iters = Self::get_balancing_iters(); + // 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 call = Call::submit_unsigned(raw_solution, witness).into(); + SubmitTransaction::>::submit_unsigned_transaction(call) + .map_err(|_| MinerError::PoolSubmissionFailed) + } + + /// Mine a new npos solution, with all the relevant checks to make sure that it will be accepted + /// to the chain. + /// + /// If you want an unchecked solution, use [`Pallet::mine_solution`]. + /// If you want a checked solution and submit it at the same time, use + /// [`Pallet::mine_check_and_submit`]. + pub fn mine_and_check(iters: usize) -> Result<(RawSolution>, SolutionOrSnapshotSize), MinerError> { let (raw_solution, witness) = Self::mine_solution(balancing)?; // ensure that this will pass the pre-dispatch checks Self::unsigned_pre_dispatch_checks(&raw_solution).map_err(|e| { - log!(warn, "pre-disaptch-checks failed for mined solution: {:?}", e); + log!(warn, "pre-dispatch-checks failed for mined solution: {:?}", e); MinerError::PreDispatchChecksFailed })?; - // submit the raw solution to the pool. - let call = Call::submit_unsigned(raw_solution, witness).into(); + // ensure that this is a feasible solution + let _ = Self::feasibility_check(solution.clone(), ElectionCompute::Unsigned).map_err(|e| { + log!(warn, "feasibility-check failed for mined solution: {:?}", e); + e.into() + })?; - SubmitTransaction::>::submit_unsigned_transaction(call) - .map_err(|_| MinerError::PoolSubmissionFailed) + Ok((raw_solution, witness)) } /// Mine a new npos solution. From 7d6f6adc8312da71d9b2418a80f80e5424555798 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 12 Feb 2021 13:33:32 +0000 Subject: [PATCH 22/57] Last fixes to benchmarks --- .../src/benchmarking.rs | 23 +++++-------------- .../election-provider-multi-phase/src/lib.rs | 2 +- .../src/unsigned.rs | 23 +++++++++++++------ 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index c069ade162fdc..74db28c6e3929 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -212,16 +212,12 @@ benchmarks! { >::put(Phase::Unsigned((true, 1u32.into()))); // encode the most significant storage item that needs to be decoded in the dispatch. - let snapshot = >::snapshot().unwrap(); - let encoded_snapshot = snapshot.encode(); - let voters_len = snapshot.voters.len(); + let encoded_snapshot = >::snapshot().unwrap().encode(); + let encoded_call = >::submit_unsigned(raw_solution.clone(), witness).encode(); }: { assert_ok!(>::submit_unsigned(RawOrigin::None.into(), raw_solution, witness)); - assert_eq!( - as Decode>::decode(&mut &*encoded_snapshot).unwrap().voters.len(), - voters_len, - ); - // This whole assert is to ensure that the statement is not optimized away. + let _decoded_snap = as Decode>::decode(&mut &*encoded_snapshot).unwrap(); + let _decoded_call = as Decode>::decode(&mut &*encoded_call).unwrap(); } verify { assert!(>::queued_solution().is_some()); } @@ -245,17 +241,10 @@ benchmarks! { assert_eq!(raw_solution.compact.unique_targets().len() as u32, d); // encode the most significant storage item that needs to be decoded in the dispatch. - let snapshot = >::snapshot().unwrap(); - let encoded_snapshot = snapshot.encode(); - let voters_len = snapshot.voters.len(); + let encoded_snapshot = >::snapshot().unwrap().encode(); }: { assert_ok!(>::feasibility_check(raw_solution, ElectionCompute::Unsigned)); - assert_eq!( - as Decode>::decode(&mut &*encoded_snapshot).unwrap().voters.len(), - voters_len, - ); - // This whole assert is to ensure that the statement is not optimized away. - + let _decoded_snap = as Decode>::decode(&mut &*encoded_snapshot).unwrap(); } } diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 1c28921338d8d..ce6ad9ffac0ba 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -598,7 +598,7 @@ pub mod pallet { if Self::current_phase().is_unsigned_open_at(n) { match Self::try_acquire_offchain_lock(n) { Ok(_) => { - let outcome = Self::mine_and_submit().map_err(ElectionError::from); + let outcome = Self::mine_check_and_submit().map_err(ElectionError::from); log!(info, "miner exeuction done: {:?}", outcome); } Err(why) => log!(warn, "denied offchain worker: {:?}", why), diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index b7589ca2d327c..770eab98c1471 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -78,8 +78,10 @@ impl Pallet { /// If you want an unchecked solution, use [`Pallet::mine_solution`]. /// If you want a checked solution and submit it at the same time, use /// [`Pallet::mine_check_and_submit`]. - pub fn mine_and_check(iters: usize) -> Result<(RawSolution>, SolutionOrSnapshotSize), MinerError> { - let (raw_solution, witness) = Self::mine_solution(balancing)?; + pub fn mine_and_check( + iters: usize, + ) -> Result<(RawSolution>, SolutionOrSnapshotSize), MinerError> { + let (raw_solution, witness) = Self::mine_solution(iters)?; // ensure that this will pass the pre-dispatch checks Self::unsigned_pre_dispatch_checks(&raw_solution).map_err(|e| { @@ -88,10 +90,12 @@ impl Pallet { })?; // ensure that this is a feasible solution - let _ = Self::feasibility_check(solution.clone(), ElectionCompute::Unsigned).map_err(|e| { - log!(warn, "feasibility-check failed for mined solution: {:?}", e); - e.into() - })?; + let _ = Self::feasibility_check(raw_solution.clone(), ElectionCompute::Unsigned).map_err( + |e| { + log!(warn, "feasibility-check failed for mined solution: {:?}", e); + MinerError::from(e) + }, + )?; Ok((raw_solution, witness)) } @@ -723,12 +727,17 @@ mod tests { // mine seq_phragmen solution with 2 iters. assert_eq!( - MultiPhase::mine_and_submit().unwrap_err(), + MultiPhase::mine_check_and_submit().unwrap_err(), MinerError::PreDispatchChecksFailed, ); }) } + #[test] + fn miner_will_not_submit_if_not_feasible() { + todo!() + } + #[test] fn unsigned_per_dispatch_checks_can_only_submit_threshold_better() { ExtBuilder::default() From 2e284374fb803b39c087e7cc0646e6df7601ca1c Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 12 Feb 2021 14:03:44 +0000 Subject: [PATCH 23/57] Some more docs. --- frame/election-provider-multi-phase/src/lib.rs | 3 ++- frame/staking/src/lib.rs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index ce6ad9ffac0ba..757ef7bdebdfd 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -571,7 +571,8 @@ pub mod pallet { { let (need_snapshot, enabled, additional) = if current_phase == Phase::Signed { // followed by a signed phase: close the signed phase, no need for snapshot. - // NOTE: SIGNED_PHASE + // TWO_PHASE_NOTE: later on once we have signed phase, this should return + // something else. (false, true, Weight::zero()) } else { // no signed phase: create a new snapshot, definitely `enable` the unsigned diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index c69a680a5267f..53f20d6c3b400 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -2865,6 +2865,7 @@ impl Module { // Set staking information for new era. let maybe_new_validators = Self::select_and_update_validators(current_era); + // TWO_PHASE_NOTE: use this later on. let _unused_new_validators = Self::enact_election(current_era); maybe_new_validators @@ -3175,8 +3176,8 @@ impl Module { /// /// This will also process the election, as noted in [`process_election`]. fn enact_election(_current_era: EraIndex) -> Option> { - let outcome = T::ElectionProvider::elect().map(|_| ()); - log!(debug, "Experimental election provider outputted {:?}", outcome); + let _outcome = T::ElectionProvider::elect().map(|_| ()); + log!(debug, "Experimental election provider outputted {:?}", _outcome); // TWO_PHASE_NOTE: This code path shall not return anything for now. Later on, redirect the // results to `process_election`. None From 5fda74464bfc34d9914626043b32dc832d203d16 Mon Sep 17 00:00:00 2001 From: Parity Benchmarking Bot Date: Fri, 12 Feb 2021 14:21:22 +0000 Subject: [PATCH 24/57] 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 --- .../src/weights.rs | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs index e34b3b4d7f226..a53f6f022ee41 100644 --- a/frame/election-provider-multi-phase/src/weights.rs +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -56,47 +56,47 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn on_initialize_nothing() -> Weight { - (23_272_000 as Weight) + (23_608_000 as Weight) .saturating_add(T::DbWeight::get().reads(7 as Weight)) } fn on_initialize_open_signed() -> Weight { - (78_018_000 as Weight) + (80_022_000 as Weight) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_with_snapshot() -> Weight { - (76_963_000 as Weight) + (79_071_000 as Weight) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_without_snapshot() -> Weight { - (21_235_000 as Weight) + (21_407_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 22_000 - .saturating_add((4_261_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 21_000 + .saturating_add((4_113_000 as Weight).saturating_mul(v as Weight)) // Standard Error: 73_000 - .saturating_add((311_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 22_000 - .saturating_add((13_490_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 110_000 - .saturating_add((4_677_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((48_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 21_000 + .saturating_add((13_747_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 109_000 + .saturating_add((4_526_000 as Weight).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) // Standard Error: 12_000 - .saturating_add((4_335_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 41_000 - .saturating_add((564_000 as Weight).saturating_mul(t as Weight)) + .saturating_add((4_190_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 40_000 + .saturating_add((494_000 as Weight).saturating_mul(t as Weight)) // Standard Error: 12_000 - .saturating_add((10_563_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 62_000 - .saturating_add((4_750_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((10_391_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 60_000 + .saturating_add((4_573_000 as Weight).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) } } @@ -104,47 +104,47 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn on_initialize_nothing() -> Weight { - (23_272_000 as Weight) + (23_608_000 as Weight) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) } fn on_initialize_open_signed() -> Weight { - (78_018_000 as Weight) + (80_022_000 as Weight) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_with_snapshot() -> Weight { - (76_963_000 as Weight) + (79_071_000 as Weight) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_without_snapshot() -> Weight { - (21_235_000 as Weight) + (21_407_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 22_000 - .saturating_add((4_261_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 21_000 + .saturating_add((4_113_000 as Weight).saturating_mul(v as Weight)) // Standard Error: 73_000 - .saturating_add((311_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 22_000 - .saturating_add((13_490_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 110_000 - .saturating_add((4_677_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((48_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 21_000 + .saturating_add((13_747_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 109_000 + .saturating_add((4_526_000 as Weight).saturating_mul(d as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) // Standard Error: 12_000 - .saturating_add((4_335_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 41_000 - .saturating_add((564_000 as Weight).saturating_mul(t as Weight)) + .saturating_add((4_190_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 40_000 + .saturating_add((494_000 as Weight).saturating_mul(t as Weight)) // Standard Error: 12_000 - .saturating_add((10_563_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 62_000 - .saturating_add((4_750_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((10_391_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 60_000 + .saturating_add((4_573_000 as Weight).saturating_mul(d as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) } } From 1ce6c8e8a040d0d601bf0051235790724c83c744 Mon Sep 17 00:00:00 2001 From: Parity Benchmarking Bot Date: Fri, 12 Feb 2021 15:05:57 +0000 Subject: [PATCH 25/57] 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 --- .../src/weights.rs | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs index a53f6f022ee41..cbdc5b39bf3ee 100644 --- a/frame/election-provider-multi-phase/src/weights.rs +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -56,47 +56,47 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn on_initialize_nothing() -> Weight { - (23_608_000 as Weight) + (23_401_000 as Weight) .saturating_add(T::DbWeight::get().reads(7 as Weight)) } fn on_initialize_open_signed() -> Weight { - (80_022_000 as Weight) + (79_260_000 as Weight) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_with_snapshot() -> Weight { - (79_071_000 as Weight) + (77_745_000 as Weight) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_without_snapshot() -> Weight { - (21_407_000 as Weight) + (21_764_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 21_000 - .saturating_add((4_113_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 73_000 - .saturating_add((48_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 21_000 - .saturating_add((13_747_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 109_000 - .saturating_add((4_526_000 as Weight).saturating_mul(d as Weight)) + // Standard Error: 23_000 + .saturating_add((4_171_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 78_000 + .saturating_add((229_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 23_000 + .saturating_add((13_661_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 117_000 + .saturating_add((4_499_000 as Weight).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) // Standard Error: 12_000 - .saturating_add((4_190_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 40_000 - .saturating_add((494_000 as Weight).saturating_mul(t as Weight)) + .saturating_add((4_232_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 42_000 + .saturating_add((636_000 as Weight).saturating_mul(t as Weight)) // Standard Error: 12_000 - .saturating_add((10_391_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 60_000 - .saturating_add((4_573_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((10_294_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 64_000 + .saturating_add((4_428_000 as Weight).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) } } @@ -104,47 +104,47 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn on_initialize_nothing() -> Weight { - (23_608_000 as Weight) + (23_401_000 as Weight) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) } fn on_initialize_open_signed() -> Weight { - (80_022_000 as Weight) + (79_260_000 as Weight) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_with_snapshot() -> Weight { - (79_071_000 as Weight) + (77_745_000 as Weight) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_without_snapshot() -> Weight { - (21_407_000 as Weight) + (21_764_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 21_000 - .saturating_add((4_113_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 73_000 - .saturating_add((48_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 21_000 - .saturating_add((13_747_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 109_000 - .saturating_add((4_526_000 as Weight).saturating_mul(d as Weight)) + // Standard Error: 23_000 + .saturating_add((4_171_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 78_000 + .saturating_add((229_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 23_000 + .saturating_add((13_661_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 117_000 + .saturating_add((4_499_000 as Weight).saturating_mul(d as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) // Standard Error: 12_000 - .saturating_add((4_190_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 40_000 - .saturating_add((494_000 as Weight).saturating_mul(t as Weight)) + .saturating_add((4_232_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 42_000 + .saturating_add((636_000 as Weight).saturating_mul(t as Weight)) // Standard Error: 12_000 - .saturating_add((10_391_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 60_000 - .saturating_add((4_573_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((10_294_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 64_000 + .saturating_add((4_428_000 as Weight).saturating_mul(d as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) } } From f63c5b83989c7482ef06e88ab9b3575c897f47cf Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 12 Feb 2021 16:11:52 +0000 Subject: [PATCH 26/57] Some nits --- bin/node/runtime/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 6d014193cec88..207ac1f236458 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -504,14 +504,14 @@ impl pallet_staking::Config for Runtime { type WeightInfo = pallet_staking::weights::SubstrateWeight; } -use pallet_election_provider_multi_phase::FallbackStrategy; parameter_types! { // phase durations. 1/4 of the last session for each. pub const SignedPhase: u32 = EPOCH_DURATION_IN_BLOCKS / 4; pub const UnsignedPhase: u32 = EPOCH_DURATION_IN_BLOCKS / 4; // fallback: no need to do on-chain phragmen initially. - pub const Fallback: FallbackStrategy = FallbackStrategy::Nothing; + pub const Fallback: pallet_election_provider_multi_phase::FallbackStrategy = + pallet_election_provider_multi_phase::FallbackStrategy::Nothing; pub SolutionImprovementThreshold: Perbill = Perbill::from_rational_approximation(1u32, 10_000); From 701e4a228b07917e236ec4fa8c9c382baa562e18 Mon Sep 17 00:00:00 2001 From: Parity Benchmarking Bot Date: Sat, 13 Feb 2021 10:09:53 +0000 Subject: [PATCH 27/57] 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 --- frame/staking/src/weights.rs | 232 +++++++++++++++++------------------ 1 file changed, 116 insertions(+), 116 deletions(-) diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index b70563ccf41b3..c7b7edad5518a 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -17,8 +17,8 @@ //! Autogenerated weights for pallet_staking //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.1 -//! DATE: 2021-01-19, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 +//! DATE: 2021-02-13, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -75,171 +75,171 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn bond() -> Weight { - (76_281_000 as Weight) + (81_642_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn bond_extra() -> Weight { - (62_062_000 as Weight) + (66_025_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn unbond() -> Weight { - (57_195_000 as Weight) + (60_810_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_update(s: u32, ) -> Weight { - (58_043_000 as Weight) + (61_537_000 as Weight) // Standard Error: 1_000 - .saturating_add((52_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((60_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_kill(s: u32, ) -> Weight { - (89_920_000 as Weight) - // Standard Error: 3_000 - .saturating_add((2_526_000 as Weight).saturating_mul(s as Weight)) + (95_741_000 as Weight) + // Standard Error: 1_000 + .saturating_add((2_754_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(8 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn validate() -> Weight { - (20_228_000 as Weight) + (21_009_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn kick(k: u32, ) -> Weight { - (31_066_000 as Weight) - // Standard Error: 11_000 - .saturating_add((17_754_000 as Weight).saturating_mul(k as Weight)) + (31_832_000 as Weight) + // Standard Error: 15_000 + .saturating_add((19_418_000 as Weight).saturating_mul(k as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(k as Weight))) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) } fn nominate(n: u32, ) -> Weight { - (33_494_000 as Weight) - // Standard Error: 23_000 - .saturating_add((5_253_000 as Weight).saturating_mul(n as Weight)) + (34_304_000 as Weight) + // Standard Error: 20_000 + .saturating_add((5_643_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn chill() -> Weight { - (19_396_000 as Weight) + (20_103_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn set_payee() -> Weight { - (13_449_000 as Weight) + (13_858_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_controller() -> Weight { - (29_184_000 as Weight) + (30_269_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn set_validator_count() -> Weight { - (2_266_000 as Weight) + (2_444_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_no_eras() -> Weight { - (2_462_000 as Weight) + (2_766_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_new_era() -> Weight { - (2_483_000 as Weight) + (2_724_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_new_era_always() -> Weight { - (2_495_000 as Weight) + (2_702_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_invulnerables(v: u32, ) -> Weight { - (2_712_000 as Weight) + (2_914_000 as Weight) // Standard Error: 0 - .saturating_add((9_000 as Weight).saturating_mul(v as Weight)) + .saturating_add((35_000 as Weight).saturating_mul(v as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_unstake(s: u32, ) -> Weight { - (60_508_000 as Weight) - // Standard Error: 1_000 - .saturating_add((2_525_000 as Weight).saturating_mul(s as Weight)) + (64_032_000 as Weight) + // Standard Error: 2_000 + .saturating_add((2_787_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(8 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn cancel_deferred_slash(s: u32, ) -> Weight { - (5_886_772_000 as Weight) - // Standard Error: 393_000 - .saturating_add((34_849_000 as Weight).saturating_mul(s as Weight)) + (5_903_394_000 as Weight) + // Standard Error: 391_000 + .saturating_add((34_834_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn payout_stakers_dead_controller(n: u32, ) -> Weight { - (127_627_000 as Weight) - // Standard Error: 27_000 - .saturating_add((49_354_000 as Weight).saturating_mul(n as Weight)) + (141_724_000 as Weight) + // Standard Error: 24_000 + .saturating_add((53_018_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(11 as Weight)) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) } fn payout_stakers_alive_staked(n: u32, ) -> Weight { - (156_838_000 as Weight) - // Standard Error: 24_000 - .saturating_add((62_653_000 as Weight).saturating_mul(n as Weight)) + (159_994_000 as Weight) + // Standard Error: 28_000 + .saturating_add((67_746_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(12 as Weight)) .saturating_add(T::DbWeight::get().reads((5 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(3 as Weight)) .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) } fn rebond(l: u32, ) -> Weight { - (40_110_000 as Weight) + (42_177_000 as Weight) // Standard Error: 1_000 - .saturating_add((78_000 as Weight).saturating_mul(l as Weight)) + .saturating_add((82_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn set_history_depth(e: u32, ) -> Weight { (0 as Weight) - // Standard Error: 70_000 - .saturating_add((32_883_000 as Weight).saturating_mul(e as Weight)) + // Standard Error: 65_000 + .saturating_add((34_151_000 as Weight).saturating_mul(e as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) .saturating_add(T::DbWeight::get().writes((7 as Weight).saturating_mul(e as Weight))) } fn reap_stash(s: u32, ) -> Weight { - (64_605_000 as Weight) - // Standard Error: 1_000 - .saturating_add((2_506_000 as Weight).saturating_mul(s as Weight)) + (68_377_000 as Weight) + // Standard Error: 0 + .saturating_add((2_757_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(8 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn new_era(v: u32, n: u32, ) -> Weight { (0 as Weight) - // Standard Error: 926_000 - .saturating_add((548_212_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 46_000 - .saturating_add((78_343_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(T::DbWeight::get().reads(7 as Weight)) + // Standard Error: 908_000 + .saturating_add((588_562_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 45_000 + .saturating_add((83_485_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(9 as Weight)) .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(v as Weight))) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) - .saturating_add(T::DbWeight::get().writes(8 as Weight)) + .saturating_add(T::DbWeight::get().writes(13 as Weight)) .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(v as Weight))) } fn submit_solution_better(v: u32, n: u32, a: u32, w: u32, ) -> Weight { (0 as Weight) - // Standard Error: 48_000 - .saturating_add((937_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 19_000 - .saturating_add((657_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 48_000 - .saturating_add((70_669_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 101_000 - .saturating_add((7_658_000 as Weight).saturating_mul(w as Weight)) + // Standard Error: 52_000 + .saturating_add((750_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 20_000 + .saturating_add((556_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 52_000 + .saturating_add((76_201_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 108_000 + .saturating_add((7_271_000 as Weight).saturating_mul(w as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(a as Weight))) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(w as Weight))) @@ -250,171 +250,171 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn bond() -> Weight { - (76_281_000 as Weight) + (81_642_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn bond_extra() -> Weight { - (62_062_000 as Weight) + (66_025_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn unbond() -> Weight { - (57_195_000 as Weight) + (60_810_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_update(s: u32, ) -> Weight { - (58_043_000 as Weight) + (61_537_000 as Weight) // Standard Error: 1_000 - .saturating_add((52_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((60_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_kill(s: u32, ) -> Weight { - (89_920_000 as Weight) - // Standard Error: 3_000 - .saturating_add((2_526_000 as Weight).saturating_mul(s as Weight)) + (95_741_000 as Weight) + // Standard Error: 1_000 + .saturating_add((2_754_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(8 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn validate() -> Weight { - (20_228_000 as Weight) + (21_009_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn kick(k: u32, ) -> Weight { - (31_066_000 as Weight) - // Standard Error: 11_000 - .saturating_add((17_754_000 as Weight).saturating_mul(k as Weight)) + (31_832_000 as Weight) + // Standard Error: 15_000 + .saturating_add((19_418_000 as Weight).saturating_mul(k as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(k as Weight))) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) } fn nominate(n: u32, ) -> Weight { - (33_494_000 as Weight) - // Standard Error: 23_000 - .saturating_add((5_253_000 as Weight).saturating_mul(n as Weight)) + (34_304_000 as Weight) + // Standard Error: 20_000 + .saturating_add((5_643_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn chill() -> Weight { - (19_396_000 as Weight) + (20_103_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn set_payee() -> Weight { - (13_449_000 as Weight) + (13_858_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_controller() -> Weight { - (29_184_000 as Weight) + (30_269_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn set_validator_count() -> Weight { - (2_266_000 as Weight) + (2_444_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_no_eras() -> Weight { - (2_462_000 as Weight) + (2_766_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_new_era() -> Weight { - (2_483_000 as Weight) + (2_724_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_new_era_always() -> Weight { - (2_495_000 as Weight) + (2_702_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_invulnerables(v: u32, ) -> Weight { - (2_712_000 as Weight) + (2_914_000 as Weight) // Standard Error: 0 - .saturating_add((9_000 as Weight).saturating_mul(v as Weight)) + .saturating_add((35_000 as Weight).saturating_mul(v as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_unstake(s: u32, ) -> Weight { - (60_508_000 as Weight) - // Standard Error: 1_000 - .saturating_add((2_525_000 as Weight).saturating_mul(s as Weight)) + (64_032_000 as Weight) + // Standard Error: 2_000 + .saturating_add((2_787_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(8 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn cancel_deferred_slash(s: u32, ) -> Weight { - (5_886_772_000 as Weight) - // Standard Error: 393_000 - .saturating_add((34_849_000 as Weight).saturating_mul(s as Weight)) + (5_903_394_000 as Weight) + // Standard Error: 391_000 + .saturating_add((34_834_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn payout_stakers_dead_controller(n: u32, ) -> Weight { - (127_627_000 as Weight) - // Standard Error: 27_000 - .saturating_add((49_354_000 as Weight).saturating_mul(n as Weight)) + (141_724_000 as Weight) + // Standard Error: 24_000 + .saturating_add((53_018_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(11 as Weight)) .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) } fn payout_stakers_alive_staked(n: u32, ) -> Weight { - (156_838_000 as Weight) - // Standard Error: 24_000 - .saturating_add((62_653_000 as Weight).saturating_mul(n as Weight)) + (159_994_000 as Weight) + // Standard Error: 28_000 + .saturating_add((67_746_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(12 as Weight)) .saturating_add(RocksDbWeight::get().reads((5 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) } fn rebond(l: u32, ) -> Weight { - (40_110_000 as Weight) + (42_177_000 as Weight) // Standard Error: 1_000 - .saturating_add((78_000 as Weight).saturating_mul(l as Weight)) + .saturating_add((82_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn set_history_depth(e: u32, ) -> Weight { (0 as Weight) - // Standard Error: 70_000 - .saturating_add((32_883_000 as Weight).saturating_mul(e as Weight)) + // Standard Error: 65_000 + .saturating_add((34_151_000 as Weight).saturating_mul(e as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) .saturating_add(RocksDbWeight::get().writes((7 as Weight).saturating_mul(e as Weight))) } fn reap_stash(s: u32, ) -> Weight { - (64_605_000 as Weight) - // Standard Error: 1_000 - .saturating_add((2_506_000 as Weight).saturating_mul(s as Weight)) + (68_377_000 as Weight) + // Standard Error: 0 + .saturating_add((2_757_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(8 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn new_era(v: u32, n: u32, ) -> Weight { (0 as Weight) - // Standard Error: 926_000 - .saturating_add((548_212_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 46_000 - .saturating_add((78_343_000 as Weight).saturating_mul(n as Weight)) - .saturating_add(RocksDbWeight::get().reads(7 as Weight)) + // Standard Error: 908_000 + .saturating_add((588_562_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 45_000 + .saturating_add((83_485_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads(9 as Weight)) .saturating_add(RocksDbWeight::get().reads((4 as Weight).saturating_mul(v as Weight))) .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) - .saturating_add(RocksDbWeight::get().writes(8 as Weight)) + .saturating_add(RocksDbWeight::get().writes(13 as Weight)) .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(v as Weight))) } fn submit_solution_better(v: u32, n: u32, a: u32, w: u32, ) -> Weight { (0 as Weight) - // Standard Error: 48_000 - .saturating_add((937_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 19_000 - .saturating_add((657_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 48_000 - .saturating_add((70_669_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 101_000 - .saturating_add((7_658_000 as Weight).saturating_mul(w as Weight)) + // Standard Error: 52_000 + .saturating_add((750_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 20_000 + .saturating_add((556_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 52_000 + .saturating_add((76_201_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 108_000 + .saturating_add((7_271_000 as Weight).saturating_mul(w as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().reads((4 as Weight).saturating_mul(a as Weight))) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(w as Weight))) From ea531e17c73aeec611943e08206340ecc9529d5d Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 17 Feb 2021 09:21:20 +0000 Subject: [PATCH 28/57] Fix doc --- frame/election-provider-multi-phase/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 757ef7bdebdfd..5e1bc8df9a7c7 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -675,7 +675,7 @@ pub mod pallet { let error_message = "Invalid unsigned submission must produce invalid block and \ deprive validator from their authoring reward."; - // NOTE: since we do this in pre-dispatch, we _could_ just ignore it here. + // Check score being an improvement, phase, and desired targets. Self::unsigned_pre_dispatch_checks(&solution).expect(error_message); // ensure witness was correct. From 2c6ac8002e22db55d9b8abae8ceb32808eec9f3d Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 17 Feb 2021 16:26:08 +0000 Subject: [PATCH 29/57] Mkae ci green --- Cargo.lock | 1 + frame/election-provider-multi-phase/src/helpers.rs | 2 +- frame/election-provider-multi-phase/src/unsigned.rs | 5 ----- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7735ac36c21f9..57a505f309e7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4511,6 +4511,7 @@ dependencies = [ "sp-consensus-babe", "sp-consensus-vrf", "sp-core", + "sp-election-providers", "sp-io", "sp-runtime", "sp-session", diff --git a/frame/election-provider-multi-phase/src/helpers.rs b/frame/election-provider-multi-phase/src/helpers.rs index 9b2f817f1ca09..be074594e6603 100644 --- a/frame/election-provider-multi-phase/src/helpers.rs +++ b/frame/election-provider-multi-phase/src/helpers.rs @@ -25,7 +25,7 @@ macro_rules! log { ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { frame_support::debug::$level!( target: $crate::LOG_TARGET, - concat!("🏦 ", $patter) $(, $values)* + concat!("🗳 ", $patter) $(, $values)* ) }; } diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index 770eab98c1471..2039e5d9f0754 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -733,11 +733,6 @@ mod tests { }) } - #[test] - fn miner_will_not_submit_if_not_feasible() { - todo!() - } - #[test] fn unsigned_per_dispatch_checks_can_only_submit_threshold_better() { ExtBuilder::default() From 6caf093cd330077bc951fef860ee0da1d16215e3 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sun, 21 Feb 2021 15:07:02 +0000 Subject: [PATCH 30/57] Audit fixes for election-provider: part 2 signed phase. --- .../src/benchmarking.rs | 8 +- .../src/helpers.rs | 2 +- .../election-provider-multi-phase/src/lib.rs | 149 +++++++++++++----- .../election-provider-multi-phase/src/mock.rs | 34 ++-- frame/staking/src/lib.rs | 37 ++++- frame/staking/src/tests.rs | 23 ++- primitives/election-providers/src/lib.rs | 66 +++++--- primitives/election-providers/src/onchain.rs | 38 +++-- primitives/npos-elections/src/lib.rs | 16 +- 9 files changed, 275 insertions(+), 98 deletions(-) diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index 74db28c6e3929..2f7925be4b86e 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -157,7 +157,7 @@ benchmarks! { assert!(>::snapshot().is_none()); assert!(>::current_phase().is_off()); }: { - >::on_initialize_open_signed(); + >::on_initialize_open_signed().unwrap(); } verify { assert!(>::snapshot().is_some()); assert!(>::current_phase().is_signed()); @@ -167,7 +167,7 @@ benchmarks! { assert!(>::snapshot().is_none()); assert!(>::current_phase().is_off()); }: { - >::on_initialize_open_unsigned(true, true, 1u32.into()); + >::on_initialize_open_unsigned(true, true, 1u32.into()).unwrap(); } verify { assert!(>::snapshot().is_some()); assert!(>::current_phase().is_unsigned()); @@ -175,11 +175,11 @@ benchmarks! { on_initialize_open_unsigned_without_snapshot { // need to assume signed phase was open before - >::on_initialize_open_signed(); + >::on_initialize_open_signed().unwrap(); assert!(>::snapshot().is_some()); assert!(>::current_phase().is_signed()); }: { - >::on_initialize_open_unsigned(false, true, 1u32.into()); + >::on_initialize_open_unsigned(false, true, 1u32.into()).unwrap(); } verify { assert!(>::snapshot().is_some()); assert!(>::current_phase().is_unsigned()); diff --git a/frame/election-provider-multi-phase/src/helpers.rs b/frame/election-provider-multi-phase/src/helpers.rs index be074594e6603..a47f476efa39b 100644 --- a/frame/election-provider-multi-phase/src/helpers.rs +++ b/frame/election-provider-multi-phase/src/helpers.rs @@ -25,7 +25,7 @@ macro_rules! log { ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { frame_support::debug::$level!( target: $crate::LOG_TARGET, - concat!("🗳 ", $patter) $(, $values)* + concat!("[#{:?}] 🗳 ", $patter), >::block_number() $(, $values)* ) }; } diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 5e1bc8df9a7c7..149a0c6f2074a 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -222,6 +222,7 @@ use sp_runtime::{ TransactionValidityError, ValidTransaction, }, DispatchError, PerThing, Perbill, RuntimeDebug, SaturatedConversion, + traits::Bounded, }; use sp_std::prelude::*; use sp_arithmetic::{ @@ -435,6 +436,8 @@ pub enum ElectionError { Miner(unsigned::MinerError), /// An error in the on-chain fallback. OnChainFallback(onchain::Error), + /// An error happened in the data provider. + DataProvider(&'static str), /// No fallback is configured. This is a special case. NoFallbackConfigured, } @@ -524,8 +527,13 @@ pub mod pallet { /// this values, based on [`WeightInfo::submit_unsigned`]. type MinerMaxWeight: Get; - /// Something that will provide the election data. - type DataProvider: ElectionDataProvider; + /// Something that will provide the election data. This pallet constrain the data provider + /// to return its weight as additional data for accurate metering. + type DataProvider: ElectionDataProvider< + Self::AccountId, + Self::BlockNumber, + Additional = Weight, + >; /// The compact solution type type CompactSolution: codec::Codec @@ -562,14 +570,28 @@ pub mod pallet { match current_phase { Phase::Off if remaining <= signed_deadline && remaining > unsigned_deadline => { - Self::on_initialize_open_signed(); - log!(info, "Starting signed phase at #{:?} , round {}.", now, Self::round()); - T::WeightInfo::on_initialize_open_signed() + // NOTE: if signed-phase length is zero, second part will fail. + match Self::on_initialize_open_signed() { + Ok(snap_weight) => { + log!(info, "Starting signed phase round {}.", Self::round()); + T::WeightInfo::on_initialize_open_signed().saturating_add(snap_weight) + } + Err(why) => { + // not much we can do about this at this point. + log!(warn, "failed to open signed phase due to {:?}", why); + T::WeightInfo::on_initialize_nothing() + // NOTE: ^^ this is a bit influenced by the fact that we know the + // implementation of the data provider does not consume much resources + // in case of error. Could be made more generalized. + } + } } Phase::Signed | Phase::Off - if remaining <= unsigned_deadline && remaining > 0u32.into() => + if remaining <= unsigned_deadline && remaining > Zero::zero() => { - let (need_snapshot, enabled, additional) = if current_phase == Phase::Signed { + // Decide on the state of the phase: followed by signed or not? + let (need_snapshot, enabled, signed_weight) = if current_phase == Phase::Signed + { // followed by a signed phase: close the signed phase, no need for snapshot. // TWO_PHASE_NOTE: later on once we have signed phase, this should return // something else. @@ -580,15 +602,23 @@ pub mod pallet { (true, true, Weight::zero()) }; - Self::on_initialize_open_unsigned(need_snapshot, enabled, now); - log!(info, "Starting unsigned phase({}) at #{:?}.", enabled, now); - - let base_weight = if need_snapshot { - T::WeightInfo::on_initialize_open_unsigned_with_snapshot() - } else { - T::WeightInfo::on_initialize_open_unsigned_without_snapshot() - }; - base_weight.saturating_add(additional) + match Self::on_initialize_open_unsigned(need_snapshot, enabled, now) { + Ok(snap_weight) => { + log!(info, "Starting unsigned phase({}).", enabled); + let base_weight = if need_snapshot { + T::WeightInfo::on_initialize_open_unsigned_with_snapshot() + } else { + T::WeightInfo::on_initialize_open_unsigned_without_snapshot() + }; + + base_weight.saturating_add(snap_weight).saturating_add(signed_weight) + } + Err(why) => { + // not much we can do about this at this point. + log!(warn, "failed to open unsigned phase due to {:?}", why); + T::WeightInfo::on_initialize_nothing() + } + } } _ => T::WeightInfo::on_initialize_nothing(), } @@ -600,7 +630,7 @@ pub mod pallet { match Self::try_acquire_offchain_lock(n) { Ok(_) => { let outcome = Self::mine_check_and_submit().map_err(ElectionError::from); - log!(info, "miner exeuction done: {:?}", outcome); + log!(info, "mine_check_and_submit execution done: {:?}", outcome); } Err(why) => log!(warn, "denied offchain worker: {:?}", why), } @@ -836,32 +866,41 @@ pub mod pallet { } impl Pallet { - /// Logic for `::on_initialize` when signed phase is being opened. + /// Logic for [`::on_initialize`] when signed phase is being opened. /// /// This is decoupled for easy weight calculation. - pub(crate) fn on_initialize_open_signed() { + /// + /// Returns `Ok(snapshot_weight)` if success, where `snapshot_weight` is the weight that + /// needs to recorded for the creation of snapshot. + pub(crate) fn on_initialize_open_signed() -> Result { + let weight = Self::create_snapshot()?; >::put(Phase::Signed); - Self::create_snapshot(); Self::deposit_event(Event::SignedPhaseStarted(Self::round())); + Ok(weight) } - /// Logic for `>::on_initialize` when unsigned phase is being opened. + /// Logic for [`>::on_initialize`] when unsigned phase is being opened. + /// + /// This is decoupled for easy weight calculation. /// - /// This is decoupled for easy weight calculation. Note that the default weight benchmark of - /// this function will assume an empty signed queue for `finalize_signed_phase`. + /// Returns `Ok(snapshot_weight)` if success, where `snapshot_weight` is the weight that + /// needs to recorded for the creation of snapshot. pub(crate) fn on_initialize_open_unsigned( need_snapshot: bool, enabled: bool, now: T::BlockNumber, - ) { - if need_snapshot { + ) -> Result { + let weight = if need_snapshot { // if not being followed by a signed phase, then create the snapshots. debug_assert!(Self::snapshot().is_none()); - Self::create_snapshot(); - } + Self::create_snapshot()? + } else { + 0 + }; >::put(Phase::Unsigned((enabled, now))); Self::deposit_event(Event::UnsignedPhaseStarted(Self::round())); + Ok(weight) } /// Creates the snapshot. Writes new data to: @@ -869,18 +908,34 @@ impl Pallet { /// 1. [`SnapshotMetadata`] /// 2. [`RoundSnapshot`] /// 3. [`DesiredTargets`] - pub(crate) fn create_snapshot() { + /// + /// Returns `Ok(consumed_weight)` if operation is okay. + pub(crate) fn create_snapshot() -> Result { + let target_limit = >::max_value().saturated_into::(); + let voter_limit = >::max_value().saturated_into::(); + // if any of them don't exist, create all of them. This is a bit conservative. - let targets = T::DataProvider::targets(); - let voters = T::DataProvider::voters(); - let desired_targets = T::DataProvider::desired_targets(); + let (targets, w1) = + T::DataProvider::targets(Some(target_limit)).map_err(ElectionError::DataProvider)?; + let (voters, w2) = + T::DataProvider::voters(Some(voter_limit)).map_err(ElectionError::DataProvider)?; + let (desired_targets, w3) = + T::DataProvider::desired_targets().map_err(ElectionError::DataProvider)?; + + // defensive-only + if targets.len() > target_limit || voters.len() > voter_limit { + debug_assert!(false, "Snapshot limit has not been respected."); + return Err(ElectionError::DataProvider("Snapshot too big for submission.")); + } + // only write snapshot if all existed. >::put(SolutionOrSnapshotSize { voters: voters.len() as u32, targets: targets.len() as u32, }); >::put(desired_targets); >::put(RoundSnapshot { voters, targets }); + Ok(w1.saturating_add(w2).saturating_add(w3)) } /// Kill everything created by [`Pallet::create_snapshot`]. @@ -1126,13 +1181,13 @@ mod feasibility_check { .compact .votes1 .iter_mut() - .filter(|(_, t)| *t == 3u16) + .filter(|(_, t)| *t == 3 as TargetIndex) .for_each(|(_, t)| *t += 1); solution.compact.votes2.iter_mut().for_each(|(_, (t0, _), t1)| { - if *t0 == 3u16 { + if *t0 == 3 as TargetIndex { *t0 += 1 }; - if *t1 == 3u16 { + if *t1 == 3 as TargetIndex { *t1 += 1 }; }); @@ -1160,7 +1215,7 @@ mod feasibility_check { .compact .votes1 .iter_mut() - .filter(|(v, _)| *v == 7u32) + .filter(|(v, _)| *v == 7 as VoterIndex) .map(|(v, _)| *v = 8) .count() > 0 ); @@ -1395,7 +1450,7 @@ mod tests { #[test] fn fallback_strategy_works() { - ExtBuilder::default().fallabck(FallbackStrategy::OnChain).build_and_execute(|| { + ExtBuilder::default().fallback(FallbackStrategy::OnChain).build_and_execute(|| { roll_to(15); assert_eq!(MultiPhase::current_phase(), Phase::Signed); @@ -1414,7 +1469,7 @@ mod tests { ) }); - ExtBuilder::default().fallabck(FallbackStrategy::Nothing).build_and_execute(|| { + ExtBuilder::default().fallback(FallbackStrategy::Nothing).build_and_execute(|| { roll_to(15); assert_eq!(MultiPhase::current_phase(), Phase::Signed); @@ -1426,6 +1481,26 @@ mod tests { }) } + #[test] + fn snapshot_creation_fails_if_too_big() { + ExtBuilder::default().build_and_execute(|| { + Targets::set((0..(TargetIndex::max_value() as AccountId) + 1).collect::>()); + + // signed phase failed to open. + roll_to(15); + assert_eq!(MultiPhase::current_phase(), Phase::Off); + + // unsigned phase failed to open. + roll_to(25); + assert_eq!(MultiPhase::current_phase(), Phase::Off); + + // on-chain backup works though. + roll_to(29); + let supports = MultiPhase::elect().unwrap(); + assert!(supports.len() > 0); + }) + } + #[test] fn number_of_voters_allowed_2sec_block() { // Just a rough estimate with the substrate weights. diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index eb38a4cd52e95..48f8c183875e7 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -31,7 +31,7 @@ use sp_core::{ }, H256, }; -use sp_election_providers::ElectionDataProvider; +use sp_election_providers::{ElectionDataProvider, data_provider::ReturnValue}; use sp_npos_elections::{ assignment_ratio_to_staked_normalized, seq_phragmen, to_supports, to_without_backing, CompactSolution, ElectionResult, EvaluateSupport, @@ -60,10 +60,12 @@ frame_support::construct_runtime!( pub(crate) type Balance = u64; pub(crate) type AccountId = u64; +pub(crate) type VoterIndex = u32; +pub(crate) type TargetIndex = u16; sp_npos_elections::generate_solution_type!( #[compact] - pub struct TestCompact::(16) + pub struct TestCompact::(16) ); /// All events of this pallet. @@ -291,15 +293,29 @@ pub struct ExtBuilder {} pub struct StakingMock; impl ElectionDataProvider for StakingMock { - fn targets() -> Vec { - Targets::get() + type Additional = u64; + + fn targets(maybe_max_len: Option) -> ReturnValue, Self::Additional> { + if maybe_max_len.map_or(false, |max_len| Targets::get().len() > max_len) { + return Err("Targets too big"); + } + + Ok((Targets::get(), 0)) } - fn voters() -> Vec<(AccountId, VoteWeight, Vec)> { - Voters::get() + + fn voters( + maybe_max_len: Option, + ) -> ReturnValue)>, Self::Additional> { + if maybe_max_len.map_or(false, |max_len| Voters::get().len() > max_len) { + return Err("Voters too big"); + } + + Ok((Voters::get(), 0)) } - fn desired_targets() -> u32 { - DesiredTargets::get() + fn desired_targets() -> ReturnValue { + Ok((DesiredTargets::get(), 0)) } + fn next_election_prediction(now: u64) -> u64 { now + EpochLength::get() - now % EpochLength::get() } @@ -319,7 +335,7 @@ impl ExtBuilder { ::set(unsigned); self } - pub fn fallabck(self, fallback: FallbackStrategy) -> Self { + pub fn fallback(self, fallback: FallbackStrategy) -> Self { ::set(fallback); self } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 53f20d6c3b400..24f2e1cabbb31 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -3335,19 +3335,44 @@ impl Module { } } +use sp_election_providers::data_provider::ReturnValue; + impl sp_election_providers::ElectionDataProvider for Module { - fn desired_targets() -> u32 { - Self::validator_count() + type Additional = Weight; + fn desired_targets() -> ReturnValue { + Ok((Self::validator_count(), ::DbWeight::get().reads(1))) } - fn voters() -> Vec<(T::AccountId, VoteWeight, Vec)> { - Self::get_npos_voters() + fn voters( + maybe_max_len: Option, + ) -> ReturnValue)>, Self::Additional> { + let nominator_count = >::iter().count(); + let validator_count = >::iter().count(); + let voter_count = nominator_count.saturating_add(validator_count); + + if maybe_max_len.map_or(false, |max_len| voter_count > max_len) { + return Err("Voter snapshot too big"); + } + + // NOTE: in the grand scheme of things, given that we iterate all validators and only + // 'alive' validators can have slashing spans, we ought to read all slashing spans at least + // once, given overlay cache EXACTLY once from the database. + let read_count = voter_count.saturating_add(>::iter().count()); + let weight = ::DbWeight::get().reads(read_count as u64); + Ok((Self::get_npos_voters(), weight)) } - fn targets() -> Vec { - Self::get_npos_targets() + fn targets(maybe_max_len: Option) -> ReturnValue, Self::Additional> { + let target_count = >::iter().count(); + + if maybe_max_len.map_or(false, |max_len| target_count > max_len) { + return Err("Target snapshot too big"); + } + + let weight = ::DbWeight::get().reads(target_count as u64); + Ok((Self::get_npos_targets(), weight)) } fn next_election_prediction(now: T::BlockNumber) -> T::BlockNumber { diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 3d90f412943af..984d25c31860d 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -4986,7 +4986,9 @@ mod election_data_provider { #[test] fn voters_include_self_vote() { ExtBuilder::default().nominate(false).build().execute_with(|| { - assert!(>::iter().map(|(x, _)| x).all(|v| Staking::voters() + assert!(>::iter().map(|(x, _)| x).all(|v| Staking::voters(None) + .unwrap() + .0 .into_iter() .find(|(w, _, t)| { v == *w && t[0] == *w }) .is_some())) @@ -4998,7 +5000,9 @@ mod election_data_provider { ExtBuilder::default().build().execute_with(|| { assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); assert_eq!( - >::voters() + >::voters(None) + .unwrap() + .0 .iter() .find(|x| x.0 == 101) .unwrap() @@ -5012,7 +5016,9 @@ mod election_data_provider { // 11 is gone. start_active_era(2); assert_eq!( - >::voters() + >::voters(None) + .unwrap() + .0 .iter() .find(|x| x.0 == 101) .unwrap() @@ -5023,7 +5029,9 @@ mod election_data_provider { // resubmit and it is back assert_ok!(Staking::nominate(Origin::signed(100), vec![11, 21])); assert_eq!( - >::voters() + >::voters(None) + .unwrap() + .0 .iter() .find(|x| x.0 == 101) .unwrap() @@ -5033,6 +5041,13 @@ mod election_data_provider { }) } + #[test] + fn respects_len_limits() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(Staking::voters(Some(1)).unwrap_err(), "Voter snapshot too big"); + }); + } + #[test] fn estimate_next_election_works() { ExtBuilder::default().session_per_era(5).period(5).build().execute_with(|| { diff --git a/primitives/election-providers/src/lib.rs b/primitives/election-providers/src/lib.rs index 73ea58c176b26..3dde253d7510d 100644 --- a/primitives/election-providers/src/lib.rs +++ b/primitives/election-providers/src/lib.rs @@ -78,7 +78,7 @@ //! ## Example //! //! ```rust -//! # use sp_election_providers::*; +//! # use sp_election_providers::{*, data_provider::ReturnValue}; //! # use sp_npos_elections::{Support, Assignment}; //! //! type AccountId = u64; @@ -99,14 +99,15 @@ //! pub struct Module(std::marker::PhantomData); //! //! impl ElectionDataProvider for Module { -//! fn desired_targets() -> u32 { -//! 1 +//! type Additional = (); +//! fn desired_targets() -> ReturnValue { +//! Ok((1, ())) //! } -//! fn voters() -> Vec<(AccountId, VoteWeight, Vec)> { -//! Default::default() +//! fn voters(maybe_max_len: Option) -> ReturnValue)>, Self::Additional> { +//! Ok((Default::default(), ())) //! } -//! fn targets() -> Vec { -//! vec![10, 20, 30] +//! fn targets(maybe_max_len: Option) -> ReturnValue, Self::Additional> { +//! Ok((vec![10, 20, 30], ())) //! } //! fn next_election_prediction(now: BlockNumber) -> BlockNumber { //! 0 @@ -129,10 +130,12 @@ //! type DataProvider = T::DataProvider; //! //! fn elect() -> Result, Self::Error> { -//! Self::DataProvider::targets() -//! .first() -//! .map(|winner| vec![(*winner, Support::default())]) -//! .ok_or(()) +//! Self::DataProvider::targets(None) +//! .map_err(|_| ()) +//! .and_then(|(t, _)| { +//! t.first().map(|winner| vec![(*winner, Support::default())]) +//! .ok_or(()) +//! }) //! } //! } //! } @@ -160,23 +163,43 @@ pub mod onchain; use sp_std::{prelude::*, fmt::Debug}; +use data_provider::ReturnValue; /// Re-export some type as they are used in the interface. pub use sp_arithmetic::PerThing; pub use sp_npos_elections::{Assignment, ExtendedBalance, PerThing128, Supports, VoteWeight}; +pub mod data_provider { + /// Alias for the return value of the election data provider. + pub type ReturnValue = Result<(T, Aux)>; + /// Alias for the result type of the election data provider. + pub type Result = sp_std::result::Result; +} + /// Something that can provide the data to an [`ElectionProvider`]. pub trait ElectionDataProvider { + /// Additional data can optionally be returned with the data. An example is an indication of the + /// resourced consumed during the operation. + type Additional: Default; + /// All possible targets for the election, i.e. the candidates. - fn targets() -> Vec; + /// + /// If `maybe_max_len` is `Some(v)` then the resulting vector MUST NOT be longer than `v` items + /// long. + fn targets(maybe_max_len: Option) -> ReturnValue, Self::Additional>; /// All possible voters for the election. /// /// Note that if a notion of self-vote exists, it should be represented here. - fn voters() -> Vec<(AccountId, VoteWeight, Vec)>; + /// + /// If `maybe_max_len` is `Some(v)` then the resulting vector MUST NOT be longer than `v` items + /// long. + fn voters( + maybe_max_len: Option, + ) -> ReturnValue)>, Self::Additional>; /// The number of targets to elect. - fn desired_targets() -> u32; + fn desired_targets() -> ReturnValue; /// Provide a best effort prediction about when the next election is about to happen. /// @@ -198,14 +221,17 @@ pub trait ElectionDataProvider { #[cfg(feature = "std")] impl ElectionDataProvider for () { - fn targets() -> Vec { - Default::default() + type Additional = (); + fn targets(_maybe_max_len: Option) -> ReturnValue, Self::Additional> { + Ok(Default::default()) } - fn voters() -> Vec<(AccountId, VoteWeight, Vec)> { - Default::default() + fn voters( + _maybe_max_len: Option, + ) -> ReturnValue)>, Self::Additional> { + Ok(Default::default()) } - fn desired_targets() -> u32 { - Default::default() + fn desired_targets() -> ReturnValue { + Ok(Default::default()) } fn next_election_prediction(now: BlockNumber) -> BlockNumber { now diff --git a/primitives/election-providers/src/onchain.rs b/primitives/election-providers/src/onchain.rs index b50dff2ff17d9..8e7de87863494 100644 --- a/primitives/election-providers/src/onchain.rs +++ b/primitives/election-providers/src/onchain.rs @@ -26,6 +26,8 @@ use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, prelude::*}; pub enum Error { /// An internal error in the NPoS elections crate. NposElections(sp_npos_elections::Error), + /// Errors from the data provider. + DataProvider(&'static str), } impl From for Error { @@ -40,7 +42,12 @@ impl From for Error { /// /// ### Warning /// -/// This can be very expensive to run frequently on-chain. Use with care. +/// This can be very expensive to run frequently on-chain. Use with care. Moreover, this +/// implementation ignores the additional data of the election data provider and gives no insight on +/// how much weight was consumed. +/// +/// Finally, this implementation does not impose any limits on the number of voters and targets that +/// are provided. pub struct OnChainSequentialPhragmen(PhantomData); /// Configuration trait of [`OnChainSequentialPhragmen`]. @@ -62,9 +69,10 @@ impl ElectionProvider for OnChainSequen type DataProvider = T::DataProvider; fn elect() -> Result, Self::Error> { - let voters = Self::DataProvider::voters(); - let targets = Self::DataProvider::targets(); - let desired_targets = Self::DataProvider::desired_targets() as usize; + let (voters, _) = Self::DataProvider::voters(None).map_err(Error::DataProvider)?; + let (targets, _) = Self::DataProvider::targets(None).map_err(Error::DataProvider)?; + let (desired_targets, _) = + Self::DataProvider::desired_targets().map_err(Error::DataProvider)?; let mut stake_map: BTreeMap = BTreeMap::new(); @@ -77,7 +85,7 @@ impl ElectionProvider for OnChainSequen }; let ElectionResult { winners, assignments } = - seq_phragmen::<_, T::Accuracy>(desired_targets, targets, voters, None) + seq_phragmen::<_, T::Accuracy>(desired_targets as usize, targets, voters, None) .map_err(Error::from)?; let staked = assignment_ratio_to_staked_normalized(assignments, &stake_of)?; @@ -108,24 +116,24 @@ mod tests { mod mock_data_provider { use super::*; + use crate::data_provider::ReturnValue; pub struct DataProvider; impl ElectionDataProvider for DataProvider { - fn voters() -> Vec<(AccountId, VoteWeight, Vec)> { - vec![ - (1, 10, vec![10, 20]), - (2, 20, vec![30, 20]), - (3, 30, vec![10, 30]), - ] + type Additional = (); + fn voters( + _: Option, + ) -> ReturnValue)>, Self::Additional> { + Ok((vec![(1, 10, vec![10, 20]), (2, 20, vec![30, 20]), (3, 30, vec![10, 30])], ())) } - fn targets() -> Vec { - vec![10, 20, 30] + fn targets(_: Option) -> ReturnValue, Self::Additional> { + Ok((vec![10, 20, 30], ())) } - fn desired_targets() -> u32 { - 2 + fn desired_targets() -> ReturnValue { + Ok((2, ())) } fn next_election_prediction(_: BlockNumber) -> BlockNumber { diff --git a/primitives/npos-elections/src/lib.rs b/primitives/npos-elections/src/lib.rs index d45698e1747bb..0d90fd9fc5568 100644 --- a/primitives/npos-elections/src/lib.rs +++ b/primitives/npos-elections/src/lib.rs @@ -139,10 +139,22 @@ pub trait CompactSolution: Sized { const LIMIT: usize; /// The voter type. Needs to be an index (convert to usize). - type Voter: UniqueSaturatedInto + TryInto + TryFrom + Debug + Copy + Clone; + type Voter: UniqueSaturatedInto + + TryInto + + TryFrom + + Debug + + Copy + + Clone + + Bounded; /// The target type. Needs to be an index (convert to usize). - type Target: UniqueSaturatedInto + TryInto + TryFrom + Debug + Copy + Clone; + type Target: UniqueSaturatedInto + + TryInto + + TryFrom + + Debug + + Copy + + Clone + + Bounded; /// The weight/accuracy type of each vote. type Accuracy: PerThing128; From 044f71fdf22151f354b0e68603b98cfc5b5aa6d5 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 2 Mar 2021 14:39:27 +0100 Subject: [PATCH 31/57] Fix weight --- frame/election-provider-multi-phase/src/benchmarking.rs | 2 +- primitives/election-providers/src/lib.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index 2f7925be4b86e..6ccb8bc9fc71d 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -189,7 +189,7 @@ benchmarks! { create_snapshot { assert!(>::snapshot().is_none()); }: { - >::create_snapshot() + >::create_snapshot().unwrap() } verify { assert!(>::snapshot().is_some()); } diff --git a/primitives/election-providers/src/lib.rs b/primitives/election-providers/src/lib.rs index 3dde253d7510d..fa8b21f191d5d 100644 --- a/primitives/election-providers/src/lib.rs +++ b/primitives/election-providers/src/lib.rs @@ -186,6 +186,9 @@ pub trait ElectionDataProvider { /// /// If `maybe_max_len` is `Some(v)` then the resulting vector MUST NOT be longer than `v` items /// long. + /// + /// It is assumed that this function will only consume a notable amount of weight, when it + /// returns `Ok(_)`. fn targets(maybe_max_len: Option) -> ReturnValue, Self::Additional>; /// All possible voters for the election. @@ -194,6 +197,9 @@ pub trait ElectionDataProvider { /// /// If `maybe_max_len` is `Some(v)` then the resulting vector MUST NOT be longer than `v` items /// long. + /// + /// It is assumed that this function will only consume a notable amount of weight, when it + /// returns `Ok(_)`. fn voters( maybe_max_len: Option, ) -> ReturnValue)>, Self::Additional>; From e090f23fc108a7c5cf3452340b466cc5f40b9a0c Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 2 Mar 2021 15:46:36 +0100 Subject: [PATCH 32/57] Some grumbles. --- .../election-provider-multi-phase/src/lib.rs | 1 + .../election-provider-multi-phase/src/mock.rs | 8 +++--- frame/staking/src/lib.rs | 11 ++++---- primitives/election-providers/src/lib.rs | 26 +++++++++---------- primitives/election-providers/src/onchain.rs | 8 +++--- 5 files changed, 26 insertions(+), 28 deletions(-) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 70082050b3516..5951516cdfd85 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -1089,6 +1089,7 @@ impl ElectionProvider for Pallet { fn elect() -> Result, Self::Error> { let outcome = Self::do_elect(); + // IMPORTANT: regardless of if election was `Ok` or `Err`, we shall do some cleanup. Self::post_elect(); outcome } diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 48f8c183875e7..417ed625288aa 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -31,7 +31,7 @@ use sp_core::{ }, H256, }; -use sp_election_providers::{ElectionDataProvider, data_provider::ReturnValue}; +use sp_election_providers::{ElectionDataProvider, data_provider}; use sp_npos_elections::{ assignment_ratio_to_staked_normalized, seq_phragmen, to_supports, to_without_backing, CompactSolution, ElectionResult, EvaluateSupport, @@ -295,7 +295,7 @@ pub struct StakingMock; impl ElectionDataProvider for StakingMock { type Additional = u64; - fn targets(maybe_max_len: Option) -> ReturnValue, Self::Additional> { + fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)> { if maybe_max_len.map_or(false, |max_len| Targets::get().len() > max_len) { return Err("Targets too big"); } @@ -305,14 +305,14 @@ impl ElectionDataProvider for StakingMock { fn voters( maybe_max_len: Option, - ) -> ReturnValue)>, Self::Additional> { + ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { if maybe_max_len.map_or(false, |max_len| Voters::get().len() > max_len) { return Err("Voters too big"); } Ok((Voters::get(), 0)) } - fn desired_targets() -> ReturnValue { + fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { Ok((DesiredTargets::get(), 0)) } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 72e72cda95740..5f498ae4d80ab 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -331,7 +331,7 @@ use sp_npos_elections::{ to_supports, EvaluateSupport, seq_phragmen, generate_solution_type, is_score_better, Supports, VoteWeight, CompactSolution, PerThing128, }; -use sp_election_providers::ElectionProvider; +use sp_election_providers::{ElectionProvider, data_provider}; pub use weights::WeightInfo; const STAKING_ID: LockIdentifier = *b"staking "; @@ -3335,19 +3335,18 @@ impl Module { } } -use sp_election_providers::data_provider::ReturnValue; - impl sp_election_providers::ElectionDataProvider for Module { type Additional = Weight; - fn desired_targets() -> ReturnValue { + + fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { Ok((Self::validator_count(), ::DbWeight::get().reads(1))) } fn voters( maybe_max_len: Option, - ) -> ReturnValue)>, Self::Additional> { + ) -> data_provider::Result<(Vec<(T::AccountId, VoteWeight, Vec)>, Self::Additional)> { let nominator_count = >::iter().count(); let validator_count = >::iter().count(); let voter_count = nominator_count.saturating_add(validator_count); @@ -3364,7 +3363,7 @@ impl sp_election_providers::ElectionDataProvider) -> ReturnValue, Self::Additional> { + fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)> { let target_count = >::iter().count(); if maybe_max_len.map_or(false, |max_len| target_count > max_len) { diff --git a/primitives/election-providers/src/lib.rs b/primitives/election-providers/src/lib.rs index fa8b21f191d5d..865820e6eb2cd 100644 --- a/primitives/election-providers/src/lib.rs +++ b/primitives/election-providers/src/lib.rs @@ -78,7 +78,7 @@ //! ## Example //! //! ```rust -//! # use sp_election_providers::{*, data_provider::ReturnValue}; +//! # use sp_election_providers::{*, data_provider}; //! # use sp_npos_elections::{Support, Assignment}; //! //! type AccountId = u64; @@ -100,13 +100,13 @@ //! //! impl ElectionDataProvider for Module { //! type Additional = (); -//! fn desired_targets() -> ReturnValue { +//! fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { //! Ok((1, ())) //! } -//! fn voters(maybe_max_len: Option) -> ReturnValue)>, Self::Additional> { +//! fn voters(maybe_max_len: Option) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { //! Ok((Default::default(), ())) //! } -//! fn targets(maybe_max_len: Option) -> ReturnValue, Self::Additional> { +//! fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional).> { //! Ok((vec![10, 20, 30], ())) //! } //! fn next_election_prediction(now: BlockNumber) -> BlockNumber { @@ -163,15 +163,13 @@ pub mod onchain; use sp_std::{prelude::*, fmt::Debug}; -use data_provider::ReturnValue; /// Re-export some type as they are used in the interface. pub use sp_arithmetic::PerThing; pub use sp_npos_elections::{Assignment, ExtendedBalance, PerThing128, Supports, VoteWeight}; +/// Types that are used by the data provider trait. pub mod data_provider { - /// Alias for the return value of the election data provider. - pub type ReturnValue = Result<(T, Aux)>; /// Alias for the result type of the election data provider. pub type Result = sp_std::result::Result; } @@ -180,7 +178,7 @@ pub mod data_provider { pub trait ElectionDataProvider { /// Additional data can optionally be returned with the data. An example is an indication of the /// resourced consumed during the operation. - type Additional: Default; + type Additional; /// All possible targets for the election, i.e. the candidates. /// @@ -189,7 +187,7 @@ pub trait ElectionDataProvider { /// /// It is assumed that this function will only consume a notable amount of weight, when it /// returns `Ok(_)`. - fn targets(maybe_max_len: Option) -> ReturnValue, Self::Additional>; + fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)>; /// All possible voters for the election. /// @@ -202,10 +200,10 @@ pub trait ElectionDataProvider { /// returns `Ok(_)`. fn voters( maybe_max_len: Option, - ) -> ReturnValue)>, Self::Additional>; + ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)>; /// The number of targets to elect. - fn desired_targets() -> ReturnValue; + fn desired_targets() -> data_provider::Result<(u32, Self::Additional)>; /// Provide a best effort prediction about when the next election is about to happen. /// @@ -228,15 +226,15 @@ pub trait ElectionDataProvider { #[cfg(feature = "std")] impl ElectionDataProvider for () { type Additional = (); - fn targets(_maybe_max_len: Option) -> ReturnValue, Self::Additional> { + fn targets(_maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)> { Ok(Default::default()) } fn voters( _maybe_max_len: Option, - ) -> ReturnValue)>, Self::Additional> { + ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { Ok(Default::default()) } - fn desired_targets() -> ReturnValue { + fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { Ok(Default::default()) } fn next_election_prediction(now: BlockNumber) -> BlockNumber { diff --git a/primitives/election-providers/src/onchain.rs b/primitives/election-providers/src/onchain.rs index 8e7de87863494..92532800f73d9 100644 --- a/primitives/election-providers/src/onchain.rs +++ b/primitives/election-providers/src/onchain.rs @@ -116,7 +116,7 @@ mod tests { mod mock_data_provider { use super::*; - use crate::data_provider::ReturnValue; + use crate::data_provider; pub struct DataProvider; @@ -124,15 +124,15 @@ mod tests { type Additional = (); fn voters( _: Option, - ) -> ReturnValue)>, Self::Additional> { + ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { Ok((vec![(1, 10, vec![10, 20]), (2, 20, vec![30, 20]), (3, 30, vec![10, 30])], ())) } - fn targets(_: Option) -> ReturnValue, Self::Additional> { + fn targets(_: Option) -> data_provider::Result<(Vec, Self::Additional)> { Ok((vec![10, 20, 30], ())) } - fn desired_targets() -> ReturnValue { + fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { Ok((2, ())) } From d37f77075e80dc205215707431f583d75635cc98 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 3 Mar 2021 11:53:55 +0100 Subject: [PATCH 33/57] Try and weigh to get_npos_voters --- frame/staking/src/benchmarking.rs | 43 ++++++++++++++++++++++---- frame/staking/src/lib.rs | 10 +++--- frame/staking/src/offchain_election.rs | 6 ++++ frame/staking/src/testing_utils.rs | 6 ++-- frame/staking/src/weights.rs | 14 +++++++++ 5 files changed, 65 insertions(+), 14 deletions(-) diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index ecaa9889b5fb2..fa92236619c73 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -25,12 +25,9 @@ use sp_npos_elections::CompactSolution; use sp_runtime::traits::One; use frame_system::RawOrigin; pub use frame_benchmarking::{ - benchmarks, - account, - whitelisted_caller, - whitelist_account, - impl_benchmark_test_suite, + benchmarks, account, whitelisted_caller, whitelist_account, impl_benchmark_test_suite, }; + const SEED: u32 = 0; const MAX_SPANS: u32 = 100; const MAX_VALIDATORS: u32 = 1000; @@ -768,6 +765,41 @@ benchmarks! { ).is_err() ); } + + get_npos_voters { + // number of validator intention. + let v in 200 .. 400; + // number of nominator intention. + let n in 200 .. 400; + // total number of slashing spans. Assigned to validators randomly. + let s in 1 .. 20; + + let mut rng = ChaChaRng::from_seed(SEED.using_encoded(sp_io::hashing::blake2_256)); + let validators = create_validators_with_nominators_for_era::(v, n, MAX_NOMINATIONS, false, None)? + .into_iter() + .map(|v| T::Lookup::lookup(v).unwrap()) + .collect::>(); + + (0..s).for_each(|index| { + add_slashing_spans::(&validators[index as usize], 10); + }); + }: { + let voters = >::get_npos_voters(); + assert_eq!(voters.len() as u32, v + n); + } + + get_npos_targets { + // number of validator intention. + let v in 200 .. 400; + // number of nominator intention. + let n = 500; + + let mut rng = ChaChaRng::from_seed(SEED.using_encoded(sp_io::hashing::blake2_256)); + let _ = create_validators_with_nominators_for_era::(v, n, MAX_NOMINATIONS, false, None)?; + }: { + let targets = >::get_npos_targets(); + assert_eq!(targets.len() as u32, v); + } } #[cfg(test)] @@ -875,7 +907,6 @@ mod tests { assert_ok!(test_benchmark_submit_solution_weaker::()); }); } - } impl_benchmark_test_suite!( diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 5f498ae4d80ab..3ba08251734d6 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -3347,6 +3347,9 @@ impl sp_election_providers::ElectionDataProvider, ) -> data_provider::Result<(Vec<(T::AccountId, VoteWeight, Vec)>, Self::Additional)> { + // NOTE: reading these counts already needs to iterate a lot of storage keys, but they get + // cached. This is okay for the case of `Ok(_)`, but bad for `Err(_)`, as the trait does not + // report weight in failures. TODO: https://github.com/paritytech/substrate/issues/8246 let nominator_count = >::iter().count(); let validator_count = >::iter().count(); let voter_count = nominator_count.saturating_add(validator_count); @@ -3355,11 +3358,8 @@ impl sp_election_providers::ElectionDataProvider>::iter().count()); - let weight = ::DbWeight::get().reads(read_count as u64); + let slashing_spans = >::iter().count(); + let weight = T::WeightInfo::get_npos_voters(nominator_count as u32, validator_count as u32, slashing_spans as u32); Ok((Self::get_npos_voters(), weight)) } diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 8398c2022fc3f..52bf8b52aaf03 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -521,6 +521,12 @@ mod test { fn kick(w: u32) -> Weight { unimplemented!() } + fn get_npos_voters(v: u32, n: u32, s: u32) -> Weight { + unimplemented!() + } + fn get_npos_targets(v: u32, n: u32, s: u32) -> Weight { + unimplemented!() + } } #[test] diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index f6ee89704d8d2..5affc50d81df7 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -112,10 +112,10 @@ pub fn create_validators( /// - `nominators`: number of bonded nominators. /// - `edge_per_nominator`: number of edge (vote) per nominator. /// - `randomize_stake`: whether to randomize the stakes. -/// - `to_nominate`: if `Some(n)`, only the first `n` bonded validator are voted upon. -/// Else, all of them are considered and `edge_per_nominator` random validators are voted for. +/// - `to_nominate`: if `Some(n)`, only the first `n` bonded validator are voted upon. Else, all of +/// them are considered and `edge_per_nominator` random validators are voted for. /// -/// Return the validators choosen to be nominated. +/// Return the validators chosen to be nominated. pub fn create_validators_with_nominators_for_era( validators: u32, nominators: u32, diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index c7b7edad5518a..70fd73f42a33c 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -69,6 +69,8 @@ pub trait WeightInfo { fn reap_stash(s: u32, ) -> Weight; fn new_era(v: u32, n: u32, ) -> Weight; fn submit_solution_better(v: u32, n: u32, a: u32, w: u32, ) -> Weight; + fn get_npos_voters(v: u32, n: u32, s: u32) -> Weight; + fn get_npos_targets(v: u32, n: u32, s: u32) -> Weight; } /// Weights for pallet_staking using the Substrate node and recommended hardware. @@ -245,6 +247,12 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(w as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } + fn get_npos_voters(v: u32, n: u32, s: u32) -> Weight { + 0 + } + fn get_npos_targets(v: u32, n: u32, s: u32) -> Weight { + 0 + } } // For backwards compatibility and tests @@ -420,4 +428,10 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(w as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } + fn get_npos_voters(v: u32, n: u32, s: u32) -> Weight { + 0 + } + fn get_npos_targets(v: u32, n: u32, s: u32) -> Weight { + 0 + } } From 924d1140620cf8ec2fe98f8153dfa2221b871690 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 3 Mar 2021 11:56:23 +0100 Subject: [PATCH 34/57] Fix build --- frame/staking/src/benchmarking.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index fa92236619c73..8e0273622b059 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -774,7 +774,6 @@ benchmarks! { // total number of slashing spans. Assigned to validators randomly. let s in 1 .. 20; - let mut rng = ChaChaRng::from_seed(SEED.using_encoded(sp_io::hashing::blake2_256)); let validators = create_validators_with_nominators_for_era::(v, n, MAX_NOMINATIONS, false, None)? .into_iter() .map(|v| T::Lookup::lookup(v).unwrap()) @@ -794,7 +793,6 @@ benchmarks! { // number of nominator intention. let n = 500; - let mut rng = ChaChaRng::from_seed(SEED.using_encoded(sp_io::hashing::blake2_256)); let _ = create_validators_with_nominators_for_era::(v, n, MAX_NOMINATIONS, false, None)?; }: { let targets = >::get_npos_targets(); From 644189135397472ead420d7e119a83925b245ba6 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 3 Mar 2021 12:00:52 +0100 Subject: [PATCH 35/57] Fix line width --- frame/staking/src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 3ba08251734d6..1be107ac910f8 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -3359,7 +3359,11 @@ impl sp_election_providers::ElectionDataProvider>::iter().count(); - let weight = T::WeightInfo::get_npos_voters(nominator_count as u32, validator_count as u32, slashing_spans as u32); + let weight = T::WeightInfo::get_npos_voters( + nominator_count as u32, + validator_count as u32, + slashing_spans as u32, + ); Ok((Self::get_npos_voters(), weight)) } From 1f24f6d8ec9b33acc75a078401c8b17023f06bde Mon Sep 17 00:00:00 2001 From: Parity Benchmarking Bot Date: Wed, 3 Mar 2021 11:48:36 +0000 Subject: [PATCH 36/57] 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 --- frame/staking/src/weights.rs | 260 +++++++++++++++++++---------------- 1 file changed, 143 insertions(+), 117 deletions(-) diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index 70fd73f42a33c..118e030f65978 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_staking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-02-13, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! DATE: 2021-03-03, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -69,163 +69,163 @@ pub trait WeightInfo { fn reap_stash(s: u32, ) -> Weight; fn new_era(v: u32, n: u32, ) -> Weight; fn submit_solution_better(v: u32, n: u32, a: u32, w: u32, ) -> Weight; - fn get_npos_voters(v: u32, n: u32, s: u32) -> Weight; - fn get_npos_targets(v: u32, n: u32, s: u32) -> Weight; + fn get_npos_voters(v: u32, n: u32, s: u32, ) -> Weight; + fn get_npos_targets(v: u32, ) -> Weight; } /// Weights for pallet_staking using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn bond() -> Weight { - (81_642_000 as Weight) + (82_888_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn bond_extra() -> Weight { - (66_025_000 as Weight) + (66_676_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn unbond() -> Weight { - (60_810_000 as Weight) + (60_847_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_update(s: u32, ) -> Weight { - (61_537_000 as Weight) - // Standard Error: 1_000 - .saturating_add((60_000 as Weight).saturating_mul(s as Weight)) + (61_940_000 as Weight) + // Standard Error: 0 + .saturating_add((73_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_kill(s: u32, ) -> Weight { - (95_741_000 as Weight) - // Standard Error: 1_000 - .saturating_add((2_754_000 as Weight).saturating_mul(s as Weight)) + (98_008_000 as Weight) + // Standard Error: 2_000 + .saturating_add((2_916_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(8 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn validate() -> Weight { - (21_009_000 as Weight) + (21_018_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn kick(k: u32, ) -> Weight { - (31_832_000 as Weight) - // Standard Error: 15_000 - .saturating_add((19_418_000 as Weight).saturating_mul(k as Weight)) + (28_311_000 as Weight) + // Standard Error: 14_000 + .saturating_add((19_701_000 as Weight).saturating_mul(k as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(k as Weight))) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) } fn nominate(n: u32, ) -> Weight { - (34_304_000 as Weight) - // Standard Error: 20_000 - .saturating_add((5_643_000 as Weight).saturating_mul(n as Weight)) + (34_340_000 as Weight) + // Standard Error: 21_000 + .saturating_add((5_879_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn chill() -> Weight { - (20_103_000 as Weight) + (20_287_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn set_payee() -> Weight { - (13_858_000 as Weight) + (14_044_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_controller() -> Weight { - (30_269_000 as Weight) + (30_776_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn set_validator_count() -> Weight { - (2_444_000 as Weight) + (2_390_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_no_eras() -> Weight { - (2_766_000 as Weight) + (2_682_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_new_era() -> Weight { - (2_724_000 as Weight) + (2_662_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_new_era_always() -> Weight { - (2_702_000 as Weight) + (2_667_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_invulnerables(v: u32, ) -> Weight { - (2_914_000 as Weight) + (2_820_000 as Weight) // Standard Error: 0 .saturating_add((35_000 as Weight).saturating_mul(v as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_unstake(s: u32, ) -> Weight { - (64_032_000 as Weight) + (66_791_000 as Weight) // Standard Error: 2_000 - .saturating_add((2_787_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((2_911_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(8 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn cancel_deferred_slash(s: u32, ) -> Weight { - (5_903_394_000 as Weight) + (5_937_192_000 as Weight) // Standard Error: 391_000 - .saturating_add((34_834_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((34_816_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn payout_stakers_dead_controller(n: u32, ) -> Weight { - (141_724_000 as Weight) - // Standard Error: 24_000 - .saturating_add((53_018_000 as Weight).saturating_mul(n as Weight)) + (142_187_000 as Weight) + // Standard Error: 23_000 + .saturating_add((54_299_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(11 as Weight)) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) } fn payout_stakers_alive_staked(n: u32, ) -> Weight { - (159_994_000 as Weight) - // Standard Error: 28_000 - .saturating_add((67_746_000 as Weight).saturating_mul(n as Weight)) + (161_199_000 as Weight) + // Standard Error: 21_000 + .saturating_add((69_141_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(12 as Weight)) .saturating_add(T::DbWeight::get().reads((5 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(3 as Weight)) .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) } fn rebond(l: u32, ) -> Weight { - (42_177_000 as Weight) + (42_500_000 as Weight) // Standard Error: 1_000 - .saturating_add((82_000 as Weight).saturating_mul(l as Weight)) + .saturating_add((83_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn set_history_depth(e: u32, ) -> Weight { (0 as Weight) - // Standard Error: 65_000 - .saturating_add((34_151_000 as Weight).saturating_mul(e as Weight)) + // Standard Error: 69_000 + .saturating_add((34_842_000 as Weight).saturating_mul(e as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) .saturating_add(T::DbWeight::get().writes((7 as Weight).saturating_mul(e as Weight))) } fn reap_stash(s: u32, ) -> Weight { - (68_377_000 as Weight) - // Standard Error: 0 - .saturating_add((2_757_000 as Weight).saturating_mul(s as Weight)) + (69_772_000 as Weight) + // Standard Error: 1_000 + .saturating_add((2_913_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(8 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn new_era(v: u32, n: u32, ) -> Weight { (0 as Weight) - // Standard Error: 908_000 - .saturating_add((588_562_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 45_000 - .saturating_add((83_485_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 883_000 + .saturating_add((603_316_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 44_000 + .saturating_add((85_313_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(9 as Weight)) .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(v as Weight))) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) @@ -234,179 +234,192 @@ impl WeightInfo for SubstrateWeight { } fn submit_solution_better(v: u32, n: u32, a: u32, w: u32, ) -> Weight { (0 as Weight) - // Standard Error: 52_000 - .saturating_add((750_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 20_000 - .saturating_add((556_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 52_000 - .saturating_add((76_201_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 108_000 - .saturating_add((7_271_000 as Weight).saturating_mul(w as Weight)) + // Standard Error: 64_000 + .saturating_add((1_505_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 25_000 + .saturating_add((682_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 64_000 + .saturating_add((78_804_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 134_000 + .saturating_add((8_484_000 as Weight).saturating_mul(w as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(a as Weight))) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(w as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - fn get_npos_voters(v: u32, n: u32, s: u32) -> Weight { - 0 + fn get_npos_voters(v: u32, n: u32, s: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 113_000 + .saturating_add((29_967_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 113_000 + .saturating_add((69_931_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 1_540_000 + .saturating_add((26_303_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(v as Weight))) + .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) } - fn get_npos_targets(v: u32, n: u32, s: u32) -> Weight { - 0 + fn get_npos_targets(v: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 33_000 + .saturating_add((11_344_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(v as Weight))) } } // For backwards compatibility and tests impl WeightInfo for () { fn bond() -> Weight { - (81_642_000 as Weight) + (82_888_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn bond_extra() -> Weight { - (66_025_000 as Weight) + (66_676_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn unbond() -> Weight { - (60_810_000 as Weight) + (60_847_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_update(s: u32, ) -> Weight { - (61_537_000 as Weight) - // Standard Error: 1_000 - .saturating_add((60_000 as Weight).saturating_mul(s as Weight)) + (61_940_000 as Weight) + // Standard Error: 0 + .saturating_add((73_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_kill(s: u32, ) -> Weight { - (95_741_000 as Weight) - // Standard Error: 1_000 - .saturating_add((2_754_000 as Weight).saturating_mul(s as Weight)) + (98_008_000 as Weight) + // Standard Error: 2_000 + .saturating_add((2_916_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(8 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn validate() -> Weight { - (21_009_000 as Weight) + (21_018_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn kick(k: u32, ) -> Weight { - (31_832_000 as Weight) - // Standard Error: 15_000 - .saturating_add((19_418_000 as Weight).saturating_mul(k as Weight)) + (28_311_000 as Weight) + // Standard Error: 14_000 + .saturating_add((19_701_000 as Weight).saturating_mul(k as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(k as Weight))) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) } fn nominate(n: u32, ) -> Weight { - (34_304_000 as Weight) - // Standard Error: 20_000 - .saturating_add((5_643_000 as Weight).saturating_mul(n as Weight)) + (34_340_000 as Weight) + // Standard Error: 21_000 + .saturating_add((5_879_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn chill() -> Weight { - (20_103_000 as Weight) + (20_287_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn set_payee() -> Weight { - (13_858_000 as Weight) + (14_044_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_controller() -> Weight { - (30_269_000 as Weight) + (30_776_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn set_validator_count() -> Weight { - (2_444_000 as Weight) + (2_390_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_no_eras() -> Weight { - (2_766_000 as Weight) + (2_682_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_new_era() -> Weight { - (2_724_000 as Weight) + (2_662_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_new_era_always() -> Weight { - (2_702_000 as Weight) + (2_667_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_invulnerables(v: u32, ) -> Weight { - (2_914_000 as Weight) + (2_820_000 as Weight) // Standard Error: 0 .saturating_add((35_000 as Weight).saturating_mul(v as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_unstake(s: u32, ) -> Weight { - (64_032_000 as Weight) + (66_791_000 as Weight) // Standard Error: 2_000 - .saturating_add((2_787_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((2_911_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(8 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn cancel_deferred_slash(s: u32, ) -> Weight { - (5_903_394_000 as Weight) + (5_937_192_000 as Weight) // Standard Error: 391_000 - .saturating_add((34_834_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((34_816_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn payout_stakers_dead_controller(n: u32, ) -> Weight { - (141_724_000 as Weight) - // Standard Error: 24_000 - .saturating_add((53_018_000 as Weight).saturating_mul(n as Weight)) + (142_187_000 as Weight) + // Standard Error: 23_000 + .saturating_add((54_299_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(11 as Weight)) .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) } fn payout_stakers_alive_staked(n: u32, ) -> Weight { - (159_994_000 as Weight) - // Standard Error: 28_000 - .saturating_add((67_746_000 as Weight).saturating_mul(n as Weight)) + (161_199_000 as Weight) + // Standard Error: 21_000 + .saturating_add((69_141_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(12 as Weight)) .saturating_add(RocksDbWeight::get().reads((5 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) } fn rebond(l: u32, ) -> Weight { - (42_177_000 as Weight) + (42_500_000 as Weight) // Standard Error: 1_000 - .saturating_add((82_000 as Weight).saturating_mul(l as Weight)) + .saturating_add((83_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn set_history_depth(e: u32, ) -> Weight { (0 as Weight) - // Standard Error: 65_000 - .saturating_add((34_151_000 as Weight).saturating_mul(e as Weight)) + // Standard Error: 69_000 + .saturating_add((34_842_000 as Weight).saturating_mul(e as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) .saturating_add(RocksDbWeight::get().writes((7 as Weight).saturating_mul(e as Weight))) } fn reap_stash(s: u32, ) -> Weight { - (68_377_000 as Weight) - // Standard Error: 0 - .saturating_add((2_757_000 as Weight).saturating_mul(s as Weight)) + (69_772_000 as Weight) + // Standard Error: 1_000 + .saturating_add((2_913_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(8 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn new_era(v: u32, n: u32, ) -> Weight { (0 as Weight) - // Standard Error: 908_000 - .saturating_add((588_562_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 45_000 - .saturating_add((83_485_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 883_000 + .saturating_add((603_316_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 44_000 + .saturating_add((85_313_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(9 as Weight)) .saturating_add(RocksDbWeight::get().reads((4 as Weight).saturating_mul(v as Weight))) .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) @@ -415,23 +428,36 @@ impl WeightInfo for () { } fn submit_solution_better(v: u32, n: u32, a: u32, w: u32, ) -> Weight { (0 as Weight) - // Standard Error: 52_000 - .saturating_add((750_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 20_000 - .saturating_add((556_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 52_000 - .saturating_add((76_201_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 108_000 - .saturating_add((7_271_000 as Weight).saturating_mul(w as Weight)) + // Standard Error: 64_000 + .saturating_add((1_505_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 25_000 + .saturating_add((682_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 64_000 + .saturating_add((78_804_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 134_000 + .saturating_add((8_484_000 as Weight).saturating_mul(w as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().reads((4 as Weight).saturating_mul(a as Weight))) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(w as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - fn get_npos_voters(v: u32, n: u32, s: u32) -> Weight { - 0 + fn get_npos_voters(v: u32, n: u32, s: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 113_000 + .saturating_add((29_967_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 113_000 + .saturating_add((69_931_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 1_540_000 + .saturating_add((26_303_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().reads((4 as Weight).saturating_mul(v as Weight))) + .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) } - fn get_npos_targets(v: u32, n: u32, s: u32) -> Weight { - 0 + fn get_npos_targets(v: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 33_000 + .saturating_add((11_344_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(v as Weight))) } } From b23781f621e9bea2d986bf32ba1b8fad9e1fed5f Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 3 Mar 2021 13:51:38 +0100 Subject: [PATCH 37/57] Fix tests. --- frame/staking/src/offchain_election.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 52bf8b52aaf03..cacfe454ec709 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -524,7 +524,7 @@ mod test { fn get_npos_voters(v: u32, n: u32, s: u32) -> Weight { unimplemented!() } - fn get_npos_targets(v: u32, n: u32, s: u32) -> Weight { + fn get_npos_targets(v: u32) -> Weight { unimplemented!() } } From 2bb4ed99abf3ac4a13eca2bb156959c2280409a2 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 5 Mar 2021 15:08:03 +0100 Subject: [PATCH 38/57] Fix build --- frame/election-provider-multi-phase/src/lib.rs | 11 +++++------ primitives/election-providers/src/lib.rs | 12 ++++++------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 1744bf750490b..aaed7795ee120 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -581,16 +581,15 @@ pub mod pallet { // not much we can do about this at this point. log!(warn, "failed to open signed phase due to {:?}", why); T::WeightInfo::on_initialize_nothing() - // NOTE: ^^ this is a bit influenced by the fact that we know the - // implementation of the data provider does not consume much resources - // in case of error. Could be made more generalized. + // NOTE: ^^ The trait specifies that this is a noop in terms of weight + // in case of error. } } } Phase::Signed | Phase::Off if remaining <= unsigned_deadline && remaining > Zero::zero() => { - // Decide on the state of the phase: followed by signed or not? + // followed by signed or not? let (need_snapshot, enabled, signed_weight) = if current_phase == Phase::Signed { // followed by a signed phase: close the signed phase, no need for snapshot. // TWO_PHASE_NOTE: later on once we have signed phase, this should return @@ -877,7 +876,7 @@ impl Pallet { let weight = Self::create_snapshot()?; >::put(Phase::Signed); Self::deposit_event(Event::SignedPhaseStarted(Self::round())); - Ok(weight) + Ok(weight.saturating_add(T::DbWeight::get().writes(1))) } /// Logic for [`>::on_initialize`] when unsigned phase is being opened. @@ -936,7 +935,7 @@ impl Pallet { }); >::put(desired_targets); >::put(RoundSnapshot { voters, targets }); - Ok(w1.saturating_add(w2).saturating_add(w3)) + Ok(w1.saturating_add(w2).saturating_add(w3).saturating_add(T::DbWeight::get().writes(3))) } /// Kill everything created by [`Pallet::create_snapshot`]. diff --git a/primitives/election-providers/src/lib.rs b/primitives/election-providers/src/lib.rs index 865820e6eb2cd..00564062be3f8 100644 --- a/primitives/election-providers/src/lib.rs +++ b/primitives/election-providers/src/lib.rs @@ -85,7 +85,7 @@ //! type Balance = u64; //! type BlockNumber = u32; //! -//! mod data_provider { +//! mod data_provider_mod { //! use super::*; //! //! pub trait Config: Sized { @@ -99,14 +99,14 @@ //! pub struct Module(std::marker::PhantomData); //! //! impl ElectionDataProvider for Module { -//! type Additional = (); +//! type Additional = (); //! fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { //! Ok((1, ())) //! } //! fn voters(maybe_max_len: Option) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { //! Ok((Default::default(), ())) //! } -//! fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional).> { +//! fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)> { //! Ok((vec![10, 20, 30], ())) //! } //! fn next_election_prediction(now: BlockNumber) -> BlockNumber { @@ -142,15 +142,15 @@ //! //! mod runtime { //! use super::generic_election_provider; -//! use super::data_provider; +//! use super::data_provider_mod; //! use super::AccountId; //! //! struct Runtime; //! impl generic_election_provider::Config for Runtime { -//! type DataProvider = data_provider::Module; +//! type DataProvider = data_provider_mod::Module; //! } //! -//! impl data_provider::Config for Runtime { +//! impl data_provider_mod::Config for Runtime { //! type ElectionProvider = generic_election_provider::GenericElectionProvider; //! } //! From f5100f3178ac6a48253799d906b22b2532e10eb8 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 5 Mar 2021 15:46:27 +0100 Subject: [PATCH 39/57] Reorg some stuff --- Cargo.lock | 11 + Cargo.toml | 2 +- frame/babe/Cargo.toml | 2 +- frame/babe/src/mock.rs | 2 +- .../election-provider-multi-phase/Cargo.toml | 6 +- .../src/benchmarking.rs | 2 +- .../election-provider-multi-phase/src/lib.rs | 12 +- .../election-provider-multi-phase/src/mock.rs | 2 +- .../src/unsigned.rs | 2 +- frame/election-provider-support/Cargo.toml | 33 +++ frame/election-provider-support/src/lib.rs | 271 ++++++++++++++++++ .../election-provider-support/src/onchain.rs | 167 +++++++++++ frame/grandpa/Cargo.toml | 2 +- frame/grandpa/src/mock.rs | 2 +- frame/offences/benchmarking/Cargo.toml | 4 +- frame/offences/benchmarking/src/mock.rs | 2 +- frame/session/benchmarking/Cargo.toml | 4 +- frame/session/benchmarking/src/mock.rs | 2 +- frame/staking/Cargo.toml | 8 +- frame/staking/fuzzer/Cargo.toml | 2 +- frame/staking/fuzzer/src/mock.rs | 2 +- frame/staking/src/lib.rs | 6 +- frame/staking/src/mock.rs | 2 +- frame/staking/src/tests.rs | 2 +- primitives/election-providers/src/lib.rs | 2 +- 25 files changed, 517 insertions(+), 35 deletions(-) create mode 100644 frame/election-provider-support/Cargo.toml create mode 100644 frame/election-provider-support/src/lib.rs create mode 100644 frame/election-provider-support/src/onchain.rs diff --git a/Cargo.lock b/Cargo.lock index 38e11a7fed1d7..314c62138a788 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1452,6 +1452,17 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "election-provider-support" +version = "3.0.0" +dependencies = [ + "parity-scale-codec", + "sp-arithmetic", + "sp-npos-elections", + "sp-runtime", + "sp-std", +] + [[package]] name = "enumflags2" version = "0.6.4" diff --git a/Cargo.toml b/Cargo.toml index 9a494d6aff39f..8f9dab6d653e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ members = [ "frame/try-runtime", "frame/elections", "frame/election-provider-multi-phase", + "frame/election-provider-support", "frame/example", "frame/example-offchain-worker", "frame/example-parallel", @@ -144,7 +145,6 @@ members = [ "primitives/database", "primitives/debug-derive", "primitives/externalities", - "primitives/election-providers", "primitives/finality-grandpa", "primitives/inherents", "primitives/io", diff --git a/frame/babe/Cargo.toml b/frame/babe/Cargo.toml index 2d7467d82e5b7..b32d7c0b1e68f 100644 --- a/frame/babe/Cargo.toml +++ b/frame/babe/Cargo.toml @@ -38,7 +38,7 @@ pallet-offences = { version = "3.0.0", path = "../offences" } pallet-staking = { version = "3.0.0", path = "../staking" } pallet-staking-reward-curve = { version = "3.0.0", path = "../staking/reward-curve" } sp-core = { version = "3.0.0", path = "../../primitives/core" } -sp-election-providers = { version = "3.0.0", path = "../../primitives/election-providers" } +election-provider-support = { version = "3.0.0", path = "../election-provider-support" } [features] default = ["std"] diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 412f13f6a2df8..dfa58d270305d 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -37,7 +37,7 @@ use sp_consensus_babe::{AuthorityId, AuthorityPair, Slot}; use sp_consensus_vrf::schnorrkel::{VRFOutput, VRFProof}; use sp_staking::SessionIndex; use pallet_staking::EraIndex; -use sp_election_providers::onchain; +use election_provider_support::onchain; use pallet_session::historical as pallet_session_historical; type DummyValidatorId = u64; diff --git a/frame/election-provider-multi-phase/Cargo.toml b/frame/election-provider-multi-phase/Cargo.toml index 1d63f9df40a25..9a39b7980f86f 100644 --- a/frame/election-provider-multi-phase/Cargo.toml +++ b/frame/election-provider-multi-phase/Cargo.toml @@ -26,7 +26,7 @@ sp-std = { version = "3.0.0", default-features = false, path = "../../primitives sp-runtime = { version = "3.0.0", default-features = false, path = "../../primitives/runtime" } sp-npos-elections = { version = "3.0.0", default-features = false, path = "../../primitives/npos-elections" } sp-arithmetic = { version = "3.0.0", default-features = false, path = "../../primitives/arithmetic" } -sp-election-providers = { version = "3.0.0", default-features = false, path = "../../primitives/election-providers" } +election-provider-support = { version = "3.0.0", default-features = false, path = "../election-provider-support" } # Optional imports for benchmarking frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true } @@ -41,7 +41,7 @@ substrate-test-utils = { version = "3.0.0", path = "../../test-utils" } sp-io = { version = "3.0.0", path = "../../primitives/io" } sp-core = { version = "3.0.0", path = "../../primitives/core" } sp-tracing = { version = "3.0.0", path = "../../primitives/tracing" } -sp-election-providers = { version = "3.0.0", features = ["runtime-benchmarks"], path = "../../primitives/election-providers" } +election-provider-support = { version = "3.0.0", features = ["runtime-benchmarks"], path = "../election-provider-support" } pallet-balances = { version = "3.0.0", path = "../balances" } frame-benchmarking = { path = "../benchmarking" , version = "3.1.0"} @@ -60,7 +60,7 @@ std = [ "sp-runtime/std", "sp-npos-elections/std", "sp-arithmetic/std", - "sp-election-providers/std", + "election-provider-support/std", "log/std", ] runtime-benchmarks = [ diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index 6ccb8bc9fc71d..b1282a408972a 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -24,7 +24,7 @@ pub use frame_benchmarking::{account, benchmarks, whitelist_account, whitelisted use frame_support::{assert_ok, traits::OnInitialize}; use frame_system::RawOrigin; use rand::{prelude::SliceRandom, rngs::SmallRng, SeedableRng}; -use sp_election_providers::Assignment; +use election_provider_support::Assignment; use sp_arithmetic::traits::One; use sp_runtime::InnerOf; use sp_std::convert::TryInto; diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index aaed7795ee120..3605f21bb90fa 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -23,8 +23,8 @@ //! ## Phases //! //! The timeline of pallet is as follows. At each block, -//! [`sp_election_providers::ElectionDataProvider::next_election_prediction`] is used to estimate -//! the time remaining to the next call to [`sp_election_providers::ElectionProvider::elect`]. Based +//! [`election_provider_support::ElectionDataProvider::next_election_prediction`] is used to estimate +//! the time remaining to the next call to [`election_provider_support::ElectionProvider::elect`]. Based //! on this, a phase is chosen. The timeline is as follows. //! //! ```ignore @@ -149,7 +149,7 @@ //! are helpful for logging and are thus nested as: //! - [`ElectionError::Miner`]: wraps a [`unsigned::MinerError`]. //! - [`ElectionError::Feasibility`]: wraps a [`FeasibilityError`]. -//! - [`ElectionError::OnChainFallback`]: wraps a [`sp_election_providers::onchain::Error`]. +//! - [`ElectionError::OnChainFallback`]: wraps a [`election_provider_support::onchain::Error`]. //! //! Note that there could be an overlap between these sub-errors. For example, A //! `SnapshotUnavailable` can happen in both miner and feasibility check phase. @@ -184,7 +184,7 @@ //! //! **Recursive Fallback**: Currently, the fallback is a separate enum. A different and fancier way //! of doing this would be to have the fallback be another -//! [`sp_election_providers::ElectionProvider`]. In this case, this pallet can even have the +//! [`election_provider_support::ElectionProvider`]. In this case, this pallet can even have the //! on-chain election provider as fallback, or special _noop_ fallback that simply returns an error, //! thus replicating [`FallbackStrategy::Nothing`]. In this case, we won't need the additional //! config OnChainAccuracy either. @@ -211,7 +211,7 @@ use frame_support::{ weights::Weight, }; use frame_system::{ensure_none, offchain::SendTransactionTypes}; -use sp_election_providers::{ElectionDataProvider, ElectionProvider, onchain}; +use election_provider_support::{ElectionDataProvider, ElectionProvider, onchain}; use sp_npos_elections::{ assignment_ratio_to_staked_normalized, is_score_better, CompactSolution, ElectionScore, EvaluateSupport, PerThing128, Supports, VoteWeight, @@ -1279,7 +1279,7 @@ mod feasibility_check { #[cfg(test)] mod tests { use super::{mock::*, Event, *}; - use sp_election_providers::ElectionProvider; + use election_provider_support::ElectionProvider; use sp_npos_elections::Support; #[test] diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 417ed625288aa..e98fbd61c5f8d 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -31,7 +31,7 @@ use sp_core::{ }, H256, }; -use sp_election_providers::{ElectionDataProvider, data_provider}; +use election_provider_support::{ElectionDataProvider, data_provider}; use sp_npos_elections::{ assignment_ratio_to_staked_normalized, seq_phragmen, to_supports, to_without_backing, CompactSolution, ElectionResult, EvaluateSupport, diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index 2039e5d9f0754..d60412ccd99ed 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -487,7 +487,7 @@ mod tests { }; use frame_support::{dispatch::Dispatchable, traits::OffchainWorker}; use mock::Call as OuterCall; - use sp_election_providers::Assignment; + use election_provider_support::Assignment; use sp_runtime::{traits::ValidateUnsigned, PerU16}; #[test] diff --git a/frame/election-provider-support/Cargo.toml b/frame/election-provider-support/Cargo.toml new file mode 100644 index 0000000000000..367547238ff29 --- /dev/null +++ b/frame/election-provider-support/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "election-provider-support" +version = "3.0.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "Apache-2.0" +homepage = "https://substrate.dev" +repository = "https://github.com/paritytech/substrate/" +description = "election provider supporting traits" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] } +sp-std = { version = "3.0.0", default-features = false, path = "../../primitives/std" } +sp-arithmetic = { version = "3.0.0", default-features = false, path = "../../primitives/arithmetic" } +sp-npos-elections = { version = "3.0.0", default-features = false, path = "../../primitives/npos-elections" } + +[dev-dependencies] +sp-npos-elections = { version = "3.0.0", path = "../../primitives/npos-elections" } +sp-runtime = { version = "3.0.0", path = "../../primitives/runtime" } + +[features] +default = ["std"] +runtime-benchmarks = [] +std = [ + "codec/std", + "sp-std/std", + "sp-npos-elections/std", + "sp-arithmetic/std", +] diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs new file mode 100644 index 0000000000000..ab4b2250382b0 --- /dev/null +++ b/frame/election-provider-support/src/lib.rs @@ -0,0 +1,271 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 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. + +//! Primitive traits for providing election functionality. +//! +//! This crate provides two traits that could interact to enable extensible election functionality +//! within FRAME pallets. +//! +//! Something that will provide the functionality of election will implement [`ElectionProvider`], +//! whilst needing an associated [`ElectionProvider::DataProvider`], which needs to be fulfilled by +//! an entity implementing [`ElectionDataProvider`]. Most often, *the data provider is* the receiver +//! of the election, resulting in a diagram as below: +//! +//! ```ignore +//! ElectionDataProvider +//! <------------------------------------------+ +//! | | +//! v | +//! +-----+----+ +------+---+ +//! | | | | +//! pallet-do-election | | | | pallet-needs-election +//! | | | | +//! | | | | +//! +-----+----+ +------+---+ +//! | ^ +//! | | +//! +------------------------------------------+ +//! ElectionProvider +//! ``` +//! +//! > It could also be possible that a third party pallet (C), provides the data of election to an +//! > election provider (B), which then passes the election result to another pallet (A). +//! +//! ## Election Types +//! +//! Typically, two types of elections exist: +//! +//! 1. **Stateless**: Election data is provided, and the election result is immediately ready. +//! 2. **Stateful**: Election data is is queried ahead of time, and the election result might be +//! ready some number of blocks in the future. +//! +//! To accommodate both type of elections in one trait, the traits lean toward **stateful +//! election**, as it is more general than the stateless. This is why [`ElectionProvider::elect`] +//! has no parameters. All value and type parameter must be provided by the [`ElectionDataProvider`] +//! trait, even if the election happens immediately. +//! +//! ## Election Data +//! +//! The data associated with an election, essentially what the [`ElectionDataProvider`] must convey +//! is as follows: +//! +//! 1. A list of voters, with their stake. +//! 2. A list of targets (i.e. _candidates_). +//! 3. A number of desired targets to be elected (i.e. _winners_) +//! +//! In addition to that, the [`ElectionDataProvider`] must also hint [`ElectionProvider`] at when +//! the next election might happen ([`ElectionDataProvider::next_election_prediction`]). A stateless +//! election provider would probably ignore this. A stateful election provider can use this to +//! prepare the election result in advance. +//! +//! Nonetheless, an [`ElectionProvider`] shan't rely on this and should preferably provide some +//! means of fallback election as well, in case the `elect` was called immaturely early. +//! +//! ## Example +//! +//! ```rust +//! # use election_provider_support::{*, data_provider}; +//! # use sp_npos_elections::{Support, Assignment}; +//! +//! type AccountId = u64; +//! type Balance = u64; +//! type BlockNumber = u32; +//! +//! mod data_provider_mod { +//! use super::*; +//! +//! pub trait Config: Sized { +//! type ElectionProvider: ElectionProvider< +//! AccountId, +//! BlockNumber, +//! DataProvider = Module, +//! >; +//! } +//! +//! pub struct Module(std::marker::PhantomData); +//! +//! impl ElectionDataProvider for Module { +//! type Additional = (); +//! fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { +//! Ok((1, ())) +//! } +//! fn voters(maybe_max_len: Option) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { +//! Ok((Default::default(), ())) +//! } +//! fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)> { +//! Ok((vec![10, 20, 30], ())) +//! } +//! fn next_election_prediction(now: BlockNumber) -> BlockNumber { +//! 0 +//! } +//! } +//! } +//! +//! +//! mod generic_election_provider { +//! use super::*; +//! +//! pub struct GenericElectionProvider(std::marker::PhantomData); +//! +//! pub trait Config { +//! type DataProvider: ElectionDataProvider; +//! } +//! +//! impl ElectionProvider for GenericElectionProvider { +//! type Error = (); +//! type DataProvider = T::DataProvider; +//! +//! fn elect() -> Result, Self::Error> { +//! Self::DataProvider::targets(None) +//! .map_err(|_| ()) +//! .and_then(|(t, _)| { +//! t.first().map(|winner| vec![(*winner, Support::default())]) +//! .ok_or(()) +//! }) +//! } +//! } +//! } +//! +//! mod runtime { +//! use super::generic_election_provider; +//! use super::data_provider_mod; +//! use super::AccountId; +//! +//! struct Runtime; +//! impl generic_election_provider::Config for Runtime { +//! type DataProvider = data_provider_mod::Module; +//! } +//! +//! impl data_provider_mod::Config for Runtime { +//! type ElectionProvider = generic_election_provider::GenericElectionProvider; +//! } +//! +//! } +//! +//! # fn main() {} +//! ``` + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod onchain; +use sp_std::{prelude::*, fmt::Debug}; + +/// Re-export some type as they are used in the interface. +pub use sp_arithmetic::PerThing; +pub use sp_npos_elections::{Assignment, ExtendedBalance, PerThing128, Supports, VoteWeight}; + +/// Types that are used by the data provider trait. +pub mod data_provider { + /// Alias for the result type of the election data provider. + pub type Result = sp_std::result::Result; +} + +/// Something that can provide the data to an [`ElectionProvider`]. +pub trait ElectionDataProvider { + /// Additional data can optionally be returned with the data. An example is an indication of the + /// resourced consumed during the operation. + type Additional; + + /// All possible targets for the election, i.e. the candidates. + /// + /// If `maybe_max_len` is `Some(v)` then the resulting vector MUST NOT be longer than `v` items + /// long. + /// + /// It is assumed that this function will only consume a notable amount of weight, when it + /// returns `Ok(_)`. + fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)>; + + /// All possible voters for the election. + /// + /// Note that if a notion of self-vote exists, it should be represented here. + /// + /// If `maybe_max_len` is `Some(v)` then the resulting vector MUST NOT be longer than `v` items + /// long. + /// + /// It is assumed that this function will only consume a notable amount of weight, when it + /// returns `Ok(_)`. + fn voters( + maybe_max_len: Option, + ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)>; + + /// The number of targets to elect. + fn desired_targets() -> data_provider::Result<(u32, Self::Additional)>; + + /// Provide a best effort prediction about when the next election is about to happen. + /// + /// In essence, the implementor should predict with this function when it will trigger the + /// [`ElectionProvider::elect`]. + /// + /// This is only useful for stateful election providers. + fn next_election_prediction(now: BlockNumber) -> BlockNumber; + + /// Utility function only to be used in benchmarking scenarios, to be implemented optionally, + /// else a noop. + #[cfg(any(feature = "runtime-benchmarks", test))] + fn put_snapshot( + _voters: Vec<(AccountId, VoteWeight, Vec)>, + _targets: Vec, + ) { + } +} + +#[cfg(feature = "std")] +impl ElectionDataProvider for () { + type Additional = (); + fn targets(_maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)> { + Ok(Default::default()) + } + fn voters( + _maybe_max_len: Option, + ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { + Ok(Default::default()) + } + fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { + Ok(Default::default()) + } + fn next_election_prediction(now: BlockNumber) -> BlockNumber { + now + } +} + +/// Something that can compute the result of an election and pass it back to the caller. +/// +/// This trait only provides an interface to _request_ an election, i.e. +/// [`ElectionProvider::elect`]. That data required for the election need to be passed to the +/// implemented of this trait through [`ElectionProvider::DataProvider`]. +pub trait ElectionProvider { + /// The error type that is returned by the provider. + type Error: Debug; + + /// The data provider of the election. + type DataProvider: ElectionDataProvider; + + /// Elect a new set of winners. + /// + /// The result is returned in a target major format, namely as vector of supports. + fn elect() -> Result, Self::Error>; +} + +#[cfg(feature = "std")] +impl ElectionProvider for () { + type Error = &'static str; + type DataProvider = (); + + fn elect() -> Result, Self::Error> { + Err("<() as ElectionProvider> cannot do anything.") + } +} diff --git a/frame/election-provider-support/src/onchain.rs b/frame/election-provider-support/src/onchain.rs new file mode 100644 index 0000000000000..92532800f73d9 --- /dev/null +++ b/frame/election-provider-support/src/onchain.rs @@ -0,0 +1,167 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 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. + +//! An implementation of [`ElectionProvider`] that does an on-chain sequential phragmen. + +use crate::{ElectionDataProvider, ElectionProvider}; +use sp_npos_elections::*; +use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, prelude::*}; + +/// Errors of the on-chain election. +#[derive(Eq, PartialEq, Debug)] +pub enum Error { + /// An internal error in the NPoS elections crate. + NposElections(sp_npos_elections::Error), + /// Errors from the data provider. + DataProvider(&'static str), +} + +impl From for Error { + fn from(e: sp_npos_elections::Error) -> Self { + Error::NposElections(e) + } +} + +/// A simple on-chain implementation of the election provider trait. +/// +/// This will accept voting data on the fly and produce the results immediately. +/// +/// ### Warning +/// +/// This can be very expensive to run frequently on-chain. Use with care. Moreover, this +/// implementation ignores the additional data of the election data provider and gives no insight on +/// how much weight was consumed. +/// +/// Finally, this implementation does not impose any limits on the number of voters and targets that +/// are provided. +pub struct OnChainSequentialPhragmen(PhantomData); + +/// Configuration trait of [`OnChainSequentialPhragmen`]. +/// +/// Note that this is similar to a pallet traits, but [`OnChainSequentialPhragmen`] is not a pallet. +pub trait Config { + /// The account identifier type. + type AccountId: IdentifierT; + /// The block number type. + type BlockNumber; + /// The accuracy used to compute the election: + type Accuracy: PerThing128; + /// Something that provides the data for election. + type DataProvider: ElectionDataProvider; +} + +impl ElectionProvider for OnChainSequentialPhragmen { + type Error = Error; + type DataProvider = T::DataProvider; + + fn elect() -> Result, Self::Error> { + let (voters, _) = Self::DataProvider::voters(None).map_err(Error::DataProvider)?; + let (targets, _) = Self::DataProvider::targets(None).map_err(Error::DataProvider)?; + let (desired_targets, _) = + Self::DataProvider::desired_targets().map_err(Error::DataProvider)?; + + let mut stake_map: BTreeMap = BTreeMap::new(); + + voters.iter().for_each(|(v, s, _)| { + stake_map.insert(v.clone(), *s); + }); + + let stake_of = |w: &T::AccountId| -> VoteWeight { + stake_map.get(w).cloned().unwrap_or_default() + }; + + let ElectionResult { winners, assignments } = + seq_phragmen::<_, T::Accuracy>(desired_targets as usize, targets, voters, None) + .map_err(Error::from)?; + + let staked = assignment_ratio_to_staked_normalized(assignments, &stake_of)?; + let winners = to_without_backing(winners); + + to_supports(&winners, &staked).map_err(Error::from) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_npos_elections::Support; + use sp_runtime::Perbill; + + type AccountId = u64; + type BlockNumber = u32; + + struct Runtime; + impl Config for Runtime { + type AccountId = AccountId; + type BlockNumber = BlockNumber; + type Accuracy = Perbill; + type DataProvider = mock_data_provider::DataProvider; + } + + type OnChainPhragmen = OnChainSequentialPhragmen; + + mod mock_data_provider { + use super::*; + use crate::data_provider; + + pub struct DataProvider; + + impl ElectionDataProvider for DataProvider { + type Additional = (); + fn voters( + _: Option, + ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { + Ok((vec![(1, 10, vec![10, 20]), (2, 20, vec![30, 20]), (3, 30, vec![10, 30])], ())) + } + + fn targets(_: Option) -> data_provider::Result<(Vec, Self::Additional)> { + Ok((vec![10, 20, 30], ())) + } + + fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { + Ok((2, ())) + } + + fn next_election_prediction(_: BlockNumber) -> BlockNumber { + 0 + } + } + } + + #[test] + fn onchain_seq_phragmen_works() { + assert_eq!( + OnChainPhragmen::elect().unwrap(), + vec![ + ( + 10, + Support { + total: 25, + voters: vec![(1, 10), (3, 15)] + } + ), + ( + 30, + Support { + total: 35, + voters: vec![(2, 20), (3, 15)] + } + ) + ] + ); + } +} diff --git a/frame/grandpa/Cargo.toml b/frame/grandpa/Cargo.toml index 2bf7306f58e15..0c8b387fed354 100644 --- a/frame/grandpa/Cargo.toml +++ b/frame/grandpa/Cargo.toml @@ -39,7 +39,7 @@ pallet-offences = { version = "3.0.0", path = "../offences" } pallet-staking = { version = "3.0.0", path = "../staking" } pallet-staking-reward-curve = { version = "3.0.0", path = "../staking/reward-curve" } pallet-timestamp = { version = "3.0.0", path = "../timestamp" } -sp-election-providers = { version = "3.0.0", path = "../../primitives/election-providers" } +election-provider-support = { version = "3.0.0", path = "../election-provider-support" } [features] default = ["std"] diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 0a24a2344547e..7b9fb71b707f4 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -40,7 +40,7 @@ use sp_runtime::{ }; use sp_staking::SessionIndex; use pallet_session::historical as pallet_session_historical; -use sp_election_providers::onchain; +use election_provider_support::onchain; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; diff --git a/frame/offences/benchmarking/Cargo.toml b/frame/offences/benchmarking/Cargo.toml index 6be2787734a4f..9c21798897354 100644 --- a/frame/offences/benchmarking/Cargo.toml +++ b/frame/offences/benchmarking/Cargo.toml @@ -27,7 +27,7 @@ pallet-staking = { version = "3.0.0", default-features = false, features = ["run sp-runtime = { version = "3.0.0", default-features = false, path = "../../../primitives/runtime" } sp-staking = { version = "3.0.0", default-features = false, path = "../../../primitives/staking" } sp-std = { version = "3.0.0", default-features = false, path = "../../../primitives/std" } -sp-election-providers = { version = "3.0.0", default-features = false, path = "../../../primitives/election-providers" } +election-provider-support = { version = "3.0.0", default-features = false, path = "../../election-provider-support" } [dev-dependencies] pallet-staking-reward-curve = { version = "3.0.0", path = "../../staking/reward-curve" } @@ -51,7 +51,7 @@ std = [ "pallet-staking/std", "sp-runtime/std", "sp-staking/std", - "sp-election-providers/std", + "election-provider-support/std", "sp-std/std", "codec/std", ] diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 124e6b13b77ac..2d30a4e082367 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -29,7 +29,7 @@ use sp_runtime::{ traits::IdentityLookup, testing::{Header, UintAuthorityId}, }; -use sp_election_providers::onchain; +use election_provider_support::onchain; use pallet_session::historical as pallet_session_historical; type AccountId = u64; diff --git a/frame/session/benchmarking/Cargo.toml b/frame/session/benchmarking/Cargo.toml index 47265ed5ef7a8..dfb1ece4cec94 100644 --- a/frame/session/benchmarking/Cargo.toml +++ b/frame/session/benchmarking/Cargo.toml @@ -31,14 +31,14 @@ pallet-staking-reward-curve = { version = "3.0.0", path = "../../staking/reward- sp-io ={ version = "3.0.0", path = "../../../primitives/io" } pallet-timestamp = { version = "3.0.0", path = "../../timestamp" } pallet-balances = { version = "3.0.0", path = "../../balances" } -sp-election-providers = { version = "3.0.0", path = "../../../primitives/election-providers" } +election-provider-support = { version = "3.0.0", path = "../../election-provider-support" } [features] default = ["std"] std = [ "sp-std/std", "sp-session/std", - "sp-election-providers/std", + "election-provider-support/std", "sp-runtime/std", "frame-system/std", "frame-benchmarking/std", diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 0eba5452b28d0..4d544a7b3ab46 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -20,7 +20,7 @@ #![cfg(test)] use sp_runtime::traits::IdentityLookup; -use sp_election_providers::onchain; +use election_provider_support::onchain; use frame_support::parameter_types; type AccountId = u64; diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 1f9f29570a223..1ea2ca524dc83 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -28,7 +28,7 @@ pallet-session = { version = "3.0.0", default-features = false, features = ["his pallet-authorship = { version = "3.0.0", default-features = false, path = "../authorship" } sp-application-crypto = { version = "3.0.0", default-features = false, path = "../../primitives/application-crypto" } log = { version = "0.4.14", default-features = false } -sp-election-providers = { version = "3.0.0", default-features = false, path = "../../primitives/election-providers" } +election-provider-support = { version = "3.0.0", default-features = false, path = "../election-provider-support" } # Optional imports for benchmarking frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true } @@ -43,7 +43,7 @@ pallet-timestamp = { version = "3.0.0", path = "../timestamp" } pallet-staking-reward-curve = { version = "3.0.0", path = "../staking/reward-curve" } substrate-test-utils = { version = "3.0.0", path = "../../test-utils" } frame-benchmarking = { version = "3.1.0", path = "../benchmarking" } -sp-election-providers = { version = "3.0.0", features = ["runtime-benchmarks"], path = "../../primitives/election-providers" } +election-provider-support = { version = "3.0.0", features = ["runtime-benchmarks"], path = "../election-provider-support" } rand_chacha = { version = "0.2" } parking_lot = "0.11.1" hex = "0.4" @@ -64,11 +64,11 @@ std = [ "pallet-authorship/std", "sp-application-crypto/std", "log/std", - "sp-election-providers/std", + "election-provider-support/std", ] runtime-benchmarks = [ "frame-benchmarking", - "sp-election-providers/runtime-benchmarks", + "election-provider-support/runtime-benchmarks", "rand_chacha", ] try-runtime = ["frame-support/try-runtime"] diff --git a/frame/staking/fuzzer/Cargo.toml b/frame/staking/fuzzer/Cargo.toml index 84758c6bf65ce..aa424add7f434 100644 --- a/frame/staking/fuzzer/Cargo.toml +++ b/frame/staking/fuzzer/Cargo.toml @@ -28,7 +28,7 @@ sp-io ={ version = "3.0.0", path = "../../../primitives/io" } sp-core = { version = "3.0.0", path = "../../../primitives/core" } sp-npos-elections = { version = "3.0.0", path = "../../../primitives/npos-elections" } sp-runtime = { version = "3.0.0", path = "../../../primitives/runtime" } -sp-election-providers = { version = "3.0.0", path = "../../../primitives/election-providers" } +election-provider-support = { version = "3.0.0", path = "../../election-provider-support" } serde = "1.0.101" [features] diff --git a/frame/staking/fuzzer/src/mock.rs b/frame/staking/fuzzer/src/mock.rs index 05d001d23858e..6a637f5b660e5 100644 --- a/frame/staking/fuzzer/src/mock.rs +++ b/frame/staking/fuzzer/src/mock.rs @@ -158,7 +158,7 @@ where } pub struct MockElectionProvider; -impl sp_election_providers::ElectionProvider for MockElectionProvider { +impl election_provider_support::ElectionProvider for MockElectionProvider { type Error = (); type DataProvider = pallet_staking::Module; diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 1be107ac910f8..8274e7c104a00 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -331,7 +331,7 @@ use sp_npos_elections::{ to_supports, EvaluateSupport, seq_phragmen, generate_solution_type, is_score_better, Supports, VoteWeight, CompactSolution, PerThing128, }; -use sp_election_providers::{ElectionProvider, data_provider}; +use election_provider_support::{ElectionProvider, data_provider}; pub use weights::WeightInfo; const STAKING_ID: LockIdentifier = *b"staking "; @@ -798,7 +798,7 @@ pub trait Config: frame_system::Config + SendTransactionTypes> { type CurrencyToVote: CurrencyToVote>; /// Something that provides the election functionality. - type ElectionProvider: sp_election_providers::ElectionProvider< + type ElectionProvider: election_provider_support::ElectionProvider< Self::AccountId, Self::BlockNumber, // we only accept an election provider that has staking as data provider. @@ -3335,7 +3335,7 @@ impl Module { } } -impl sp_election_providers::ElectionDataProvider +impl election_provider_support::ElectionDataProvider for Module { type Additional = Weight; diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 0d6701c48b894..931643d61c078 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -37,7 +37,7 @@ use sp_runtime::{ }; use sp_staking::offence::{OffenceDetails, OnOffenceHandler}; use std::{cell::RefCell, collections::HashSet}; -use sp_election_providers::onchain; +use election_provider_support::onchain; pub const INIT_TIMESTAMP: u64 = 30_000; pub const BLOCK_TIME: u64 = 1000; diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index e52c6545f4717..304a8bbef1eb6 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -5018,7 +5018,7 @@ fn do_not_die_when_active_is_ed() { mod election_data_provider { use super::*; - use sp_election_providers::ElectionDataProvider; + use election_provider_support::ElectionDataProvider; #[test] fn voters_include_self_vote() { diff --git a/primitives/election-providers/src/lib.rs b/primitives/election-providers/src/lib.rs index 00564062be3f8..ab4b2250382b0 100644 --- a/primitives/election-providers/src/lib.rs +++ b/primitives/election-providers/src/lib.rs @@ -78,7 +78,7 @@ //! ## Example //! //! ```rust -//! # use sp_election_providers::{*, data_provider}; +//! # use election_provider_support::{*, data_provider}; //! # use sp_npos_elections::{Support, Assignment}; //! //! type AccountId = u64; From 96bce0e284d74aaa95a70fd0be17179eae23fafb Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 5 Mar 2021 15:57:15 +0100 Subject: [PATCH 40/57] More reorg. --- Cargo.lock | 26 +- .../election-provider-multi-phase/src/lib.rs | 6 +- .../election-provider-multi-phase/src/mock.rs | 8 +- frame/election-provider-support/Cargo.toml | 2 + frame/election-provider-support/src/lib.rs | 32 +-- .../election-provider-support/src/onchain.rs | 14 +- frame/staking/src/lib.rs | 8 +- primitives/election-providers/Cargo.toml | 33 --- primitives/election-providers/src/lib.rs | 271 ------------------ primitives/election-providers/src/onchain.rs | 167 ----------- 10 files changed, 38 insertions(+), 529 deletions(-) delete mode 100644 primitives/election-providers/Cargo.toml delete mode 100644 primitives/election-providers/src/lib.rs delete mode 100644 primitives/election-providers/src/onchain.rs diff --git a/Cargo.lock b/Cargo.lock index 314c62138a788..336cc81ac5530 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1456,6 +1456,7 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" name = "election-provider-support" version = "3.0.0" dependencies = [ + "frame-support", "parity-scale-codec", "sp-arithmetic", "sp-npos-elections", @@ -4534,6 +4535,7 @@ dependencies = [ name = "pallet-babe" version = "3.0.0" dependencies = [ + "election-provider-support", "frame-benchmarking", "frame-support", "frame-system", @@ -4551,7 +4553,6 @@ dependencies = [ "sp-consensus-babe", "sp-consensus-vrf", "sp-core", - "sp-election-providers", "sp-io", "sp-runtime", "sp-session", @@ -4718,6 +4719,7 @@ dependencies = [ name = "pallet-election-provider-multi-phase" version = "3.0.0" dependencies = [ + "election-provider-support", "frame-benchmarking", "frame-support", "frame-system", @@ -4731,7 +4733,6 @@ dependencies = [ "serde", "sp-arithmetic", "sp-core", - "sp-election-providers", "sp-io", "sp-npos-elections", "sp-runtime", @@ -4846,6 +4847,7 @@ dependencies = [ name = "pallet-grandpa" version = "3.0.0" dependencies = [ + "election-provider-support", "finality-grandpa", "frame-benchmarking", "frame-support", @@ -4862,7 +4864,6 @@ dependencies = [ "serde", "sp-application-crypto", "sp-core", - "sp-election-providers", "sp-finality-grandpa", "sp-io", "sp-keyring", @@ -5058,6 +5059,7 @@ dependencies = [ name = "pallet-offences-benchmarking" version = "3.0.0" dependencies = [ + "election-provider-support", "frame-benchmarking", "frame-support", "frame-system", @@ -5073,7 +5075,6 @@ dependencies = [ "parity-scale-codec", "serde", "sp-core", - "sp-election-providers", "sp-io", "sp-runtime", "sp-staking", @@ -5185,6 +5186,7 @@ dependencies = [ name = "pallet-session-benchmarking" version = "3.0.0" dependencies = [ + "election-provider-support", "frame-benchmarking", "frame-support", "frame-system", @@ -5197,7 +5199,6 @@ dependencies = [ "rand 0.7.3", "serde", "sp-core", - "sp-election-providers", "sp-io", "sp-runtime", "sp-session", @@ -5224,6 +5225,7 @@ dependencies = [ name = "pallet-staking" version = "3.0.0" dependencies = [ + "election-provider-support", "frame-benchmarking", "frame-support", "frame-system", @@ -5240,7 +5242,6 @@ dependencies = [ "serde", "sp-application-crypto", "sp-core", - "sp-election-providers", "sp-io", "sp-npos-elections", "sp-runtime", @@ -5256,6 +5257,7 @@ dependencies = [ name = "pallet-staking-fuzz" version = "0.0.0" dependencies = [ + "election-provider-support", "frame-support", "frame-system", "honggfuzz", @@ -5268,7 +5270,6 @@ dependencies = [ "parity-scale-codec", "serde", "sp-core", - "sp-election-providers", "sp-io", "sp-npos-elections", "sp-runtime", @@ -8591,17 +8592,6 @@ dependencies = [ "syn", ] -[[package]] -name = "sp-election-providers" -version = "3.0.0" -dependencies = [ - "parity-scale-codec", - "sp-arithmetic", - "sp-npos-elections", - "sp-runtime", - "sp-std", -] - [[package]] name = "sp-externalities" version = "0.9.0" diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 3605f21bb90fa..dce43c4ae0690 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -530,11 +530,7 @@ pub mod pallet { /// Something that will provide the election data. This pallet constrain the data provider /// to return its weight as additional data for accurate metering. - type DataProvider: ElectionDataProvider< - Self::AccountId, - Self::BlockNumber, - Additional = Weight, - >; + type DataProvider: ElectionDataProvider; /// The compact solution type type CompactSolution: codec::Codec diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index e98fbd61c5f8d..175bc5a4f5a3d 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -293,9 +293,7 @@ pub struct ExtBuilder {} pub struct StakingMock; impl ElectionDataProvider for StakingMock { - type Additional = u64; - - fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)> { + fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Weight)> { if maybe_max_len.map_or(false, |max_len| Targets::get().len() > max_len) { return Err("Targets too big"); } @@ -305,14 +303,14 @@ impl ElectionDataProvider for StakingMock { fn voters( maybe_max_len: Option, - ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { + ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Weight)> { if maybe_max_len.map_or(false, |max_len| Voters::get().len() > max_len) { return Err("Voters too big"); } Ok((Voters::get(), 0)) } - fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { + fn desired_targets() -> data_provider::Result<(u32, Weight)> { Ok((DesiredTargets::get(), 0)) } diff --git a/frame/election-provider-support/Cargo.toml b/frame/election-provider-support/Cargo.toml index 367547238ff29..99c6b38148f3d 100644 --- a/frame/election-provider-support/Cargo.toml +++ b/frame/election-provider-support/Cargo.toml @@ -17,6 +17,7 @@ codec = { package = "parity-scale-codec", version = "2.0.0", default-features = sp-std = { version = "3.0.0", default-features = false, path = "../../primitives/std" } sp-arithmetic = { version = "3.0.0", default-features = false, path = "../../primitives/arithmetic" } sp-npos-elections = { version = "3.0.0", default-features = false, path = "../../primitives/npos-elections" } +frame-support = { version = "3.0.0", default-feature = false, path = "../support" } [dev-dependencies] sp-npos-elections = { version = "3.0.0", path = "../../primitives/npos-elections" } @@ -30,4 +31,5 @@ std = [ "sp-std/std", "sp-npos-elections/std", "sp-arithmetic/std", + "frame-support/std", ] diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index ab4b2250382b0..9d7a2a6ce0157 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -80,6 +80,7 @@ //! ```rust //! # use election_provider_support::{*, data_provider}; //! # use sp_npos_elections::{Support, Assignment}; +//! # use frame_support::weights::Weight; //! //! type AccountId = u64; //! type Balance = u64; @@ -99,15 +100,14 @@ //! pub struct Module(std::marker::PhantomData); //! //! impl ElectionDataProvider for Module { -//! type Additional = (); -//! fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { -//! Ok((1, ())) +//! fn desired_targets() -> data_provider::Result<(u32, Weight)> { +//! Ok((1, 0)) //! } -//! fn voters(maybe_max_len: Option) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { -//! Ok((Default::default(), ())) +//! fn voters(maybe_max_len: Option) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Weight)> { +//! Ok((Default::default(), 0)) //! } -//! fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)> { -//! Ok((vec![10, 20, 30], ())) +//! fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Weight)> { +//! Ok((vec![10, 20, 30], 0)) //! } //! fn next_election_prediction(now: BlockNumber) -> BlockNumber { //! 0 @@ -163,6 +163,7 @@ pub mod onchain; use sp_std::{prelude::*, fmt::Debug}; +use frame_support::weights::Weight; /// Re-export some type as they are used in the interface. pub use sp_arithmetic::PerThing; @@ -176,10 +177,6 @@ pub mod data_provider { /// Something that can provide the data to an [`ElectionProvider`]. pub trait ElectionDataProvider { - /// Additional data can optionally be returned with the data. An example is an indication of the - /// resourced consumed during the operation. - type Additional; - /// All possible targets for the election, i.e. the candidates. /// /// If `maybe_max_len` is `Some(v)` then the resulting vector MUST NOT be longer than `v` items @@ -187,7 +184,7 @@ pub trait ElectionDataProvider { /// /// It is assumed that this function will only consume a notable amount of weight, when it /// returns `Ok(_)`. - fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)>; + fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Weight)>; /// All possible voters for the election. /// @@ -200,10 +197,10 @@ pub trait ElectionDataProvider { /// returns `Ok(_)`. fn voters( maybe_max_len: Option, - ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)>; + ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Weight)>; /// The number of targets to elect. - fn desired_targets() -> data_provider::Result<(u32, Self::Additional)>; + fn desired_targets() -> data_provider::Result<(u32, Weight)>; /// Provide a best effort prediction about when the next election is about to happen. /// @@ -225,16 +222,15 @@ pub trait ElectionDataProvider { #[cfg(feature = "std")] impl ElectionDataProvider for () { - type Additional = (); - fn targets(_maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)> { + fn targets(_maybe_max_len: Option) -> data_provider::Result<(Vec, Weight)> { Ok(Default::default()) } fn voters( _maybe_max_len: Option, - ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { + ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Weight)> { Ok(Default::default()) } - fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { + fn desired_targets() -> data_provider::Result<(u32, Weight)> { Ok(Default::default()) } fn next_election_prediction(now: BlockNumber) -> BlockNumber { diff --git a/frame/election-provider-support/src/onchain.rs b/frame/election-provider-support/src/onchain.rs index 92532800f73d9..3f9a6f54e4bb9 100644 --- a/frame/election-provider-support/src/onchain.rs +++ b/frame/election-provider-support/src/onchain.rs @@ -100,6 +100,7 @@ mod tests { use super::*; use sp_npos_elections::Support; use sp_runtime::Perbill; + use frame_support::weights::Weight; type AccountId = u64; type BlockNumber = u32; @@ -121,19 +122,18 @@ mod tests { pub struct DataProvider; impl ElectionDataProvider for DataProvider { - type Additional = (); fn voters( _: Option, - ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { - Ok((vec![(1, 10, vec![10, 20]), (2, 20, vec![30, 20]), (3, 30, vec![10, 30])], ())) + ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Weight)> { + Ok((vec![(1, 10, vec![10, 20]), (2, 20, vec![30, 20]), (3, 30, vec![10, 30])], 0)) } - fn targets(_: Option) -> data_provider::Result<(Vec, Self::Additional)> { - Ok((vec![10, 20, 30], ())) + fn targets(_: Option) -> data_provider::Result<(Vec, Weight)> { + Ok((vec![10, 20, 30], 0)) } - fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { - Ok((2, ())) + fn desired_targets() -> data_provider::Result<(u32, Weight)> { + Ok((2, 0)) } fn next_election_prediction(_: BlockNumber) -> BlockNumber { diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 8274e7c104a00..93797ef82edba 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -3338,15 +3338,13 @@ impl Module { impl election_provider_support::ElectionDataProvider for Module { - type Additional = Weight; - - fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { + fn desired_targets() -> data_provider::Result<(u32, Weight)> { Ok((Self::validator_count(), ::DbWeight::get().reads(1))) } fn voters( maybe_max_len: Option, - ) -> data_provider::Result<(Vec<(T::AccountId, VoteWeight, Vec)>, Self::Additional)> { + ) -> data_provider::Result<(Vec<(T::AccountId, VoteWeight, Vec)>, Weight)> { // NOTE: reading these counts already needs to iterate a lot of storage keys, but they get // cached. This is okay for the case of `Ok(_)`, but bad for `Err(_)`, as the trait does not // report weight in failures. TODO: https://github.com/paritytech/substrate/issues/8246 @@ -3367,7 +3365,7 @@ impl election_provider_support::ElectionDataProvider) -> data_provider::Result<(Vec, Self::Additional)> { + fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Weight)> { let target_count = >::iter().count(); if maybe_max_len.map_or(false, |max_len| target_count > max_len) { diff --git a/primitives/election-providers/Cargo.toml b/primitives/election-providers/Cargo.toml deleted file mode 100644 index cf12dce8098d7..0000000000000 --- a/primitives/election-providers/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "sp-election-providers" -version = "3.0.0" -authors = ["Parity Technologies "] -edition = "2018" -license = "Apache-2.0" -homepage = "https://substrate.dev" -repository = "https://github.com/paritytech/substrate/" -description = "Primitive election providers" -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] } -sp-std = { version = "3.0.0", default-features = false, path = "../std" } -sp-arithmetic = { version = "3.0.0", default-features = false, path = "../arithmetic" } -sp-npos-elections = { version = "3.0.0", default-features = false, path = "../npos-elections" } - -[dev-dependencies] -sp-npos-elections = { version = "3.0.0", path = "../npos-elections" } -sp-runtime = { version = "3.0.0", path = "../runtime" } - -[features] -default = ["std"] -runtime-benchmarks = [] -std = [ - "codec/std", - "sp-std/std", - "sp-npos-elections/std", - "sp-arithmetic/std", -] diff --git a/primitives/election-providers/src/lib.rs b/primitives/election-providers/src/lib.rs deleted file mode 100644 index ab4b2250382b0..0000000000000 --- a/primitives/election-providers/src/lib.rs +++ /dev/null @@ -1,271 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2020 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. - -//! Primitive traits for providing election functionality. -//! -//! This crate provides two traits that could interact to enable extensible election functionality -//! within FRAME pallets. -//! -//! Something that will provide the functionality of election will implement [`ElectionProvider`], -//! whilst needing an associated [`ElectionProvider::DataProvider`], which needs to be fulfilled by -//! an entity implementing [`ElectionDataProvider`]. Most often, *the data provider is* the receiver -//! of the election, resulting in a diagram as below: -//! -//! ```ignore -//! ElectionDataProvider -//! <------------------------------------------+ -//! | | -//! v | -//! +-----+----+ +------+---+ -//! | | | | -//! pallet-do-election | | | | pallet-needs-election -//! | | | | -//! | | | | -//! +-----+----+ +------+---+ -//! | ^ -//! | | -//! +------------------------------------------+ -//! ElectionProvider -//! ``` -//! -//! > It could also be possible that a third party pallet (C), provides the data of election to an -//! > election provider (B), which then passes the election result to another pallet (A). -//! -//! ## Election Types -//! -//! Typically, two types of elections exist: -//! -//! 1. **Stateless**: Election data is provided, and the election result is immediately ready. -//! 2. **Stateful**: Election data is is queried ahead of time, and the election result might be -//! ready some number of blocks in the future. -//! -//! To accommodate both type of elections in one trait, the traits lean toward **stateful -//! election**, as it is more general than the stateless. This is why [`ElectionProvider::elect`] -//! has no parameters. All value and type parameter must be provided by the [`ElectionDataProvider`] -//! trait, even if the election happens immediately. -//! -//! ## Election Data -//! -//! The data associated with an election, essentially what the [`ElectionDataProvider`] must convey -//! is as follows: -//! -//! 1. A list of voters, with their stake. -//! 2. A list of targets (i.e. _candidates_). -//! 3. A number of desired targets to be elected (i.e. _winners_) -//! -//! In addition to that, the [`ElectionDataProvider`] must also hint [`ElectionProvider`] at when -//! the next election might happen ([`ElectionDataProvider::next_election_prediction`]). A stateless -//! election provider would probably ignore this. A stateful election provider can use this to -//! prepare the election result in advance. -//! -//! Nonetheless, an [`ElectionProvider`] shan't rely on this and should preferably provide some -//! means of fallback election as well, in case the `elect` was called immaturely early. -//! -//! ## Example -//! -//! ```rust -//! # use election_provider_support::{*, data_provider}; -//! # use sp_npos_elections::{Support, Assignment}; -//! -//! type AccountId = u64; -//! type Balance = u64; -//! type BlockNumber = u32; -//! -//! mod data_provider_mod { -//! use super::*; -//! -//! pub trait Config: Sized { -//! type ElectionProvider: ElectionProvider< -//! AccountId, -//! BlockNumber, -//! DataProvider = Module, -//! >; -//! } -//! -//! pub struct Module(std::marker::PhantomData); -//! -//! impl ElectionDataProvider for Module { -//! type Additional = (); -//! fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { -//! Ok((1, ())) -//! } -//! fn voters(maybe_max_len: Option) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { -//! Ok((Default::default(), ())) -//! } -//! fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)> { -//! Ok((vec![10, 20, 30], ())) -//! } -//! fn next_election_prediction(now: BlockNumber) -> BlockNumber { -//! 0 -//! } -//! } -//! } -//! -//! -//! mod generic_election_provider { -//! use super::*; -//! -//! pub struct GenericElectionProvider(std::marker::PhantomData); -//! -//! pub trait Config { -//! type DataProvider: ElectionDataProvider; -//! } -//! -//! impl ElectionProvider for GenericElectionProvider { -//! type Error = (); -//! type DataProvider = T::DataProvider; -//! -//! fn elect() -> Result, Self::Error> { -//! Self::DataProvider::targets(None) -//! .map_err(|_| ()) -//! .and_then(|(t, _)| { -//! t.first().map(|winner| vec![(*winner, Support::default())]) -//! .ok_or(()) -//! }) -//! } -//! } -//! } -//! -//! mod runtime { -//! use super::generic_election_provider; -//! use super::data_provider_mod; -//! use super::AccountId; -//! -//! struct Runtime; -//! impl generic_election_provider::Config for Runtime { -//! type DataProvider = data_provider_mod::Module; -//! } -//! -//! impl data_provider_mod::Config for Runtime { -//! type ElectionProvider = generic_election_provider::GenericElectionProvider; -//! } -//! -//! } -//! -//! # fn main() {} -//! ``` - -#![cfg_attr(not(feature = "std"), no_std)] - -pub mod onchain; -use sp_std::{prelude::*, fmt::Debug}; - -/// Re-export some type as they are used in the interface. -pub use sp_arithmetic::PerThing; -pub use sp_npos_elections::{Assignment, ExtendedBalance, PerThing128, Supports, VoteWeight}; - -/// Types that are used by the data provider trait. -pub mod data_provider { - /// Alias for the result type of the election data provider. - pub type Result = sp_std::result::Result; -} - -/// Something that can provide the data to an [`ElectionProvider`]. -pub trait ElectionDataProvider { - /// Additional data can optionally be returned with the data. An example is an indication of the - /// resourced consumed during the operation. - type Additional; - - /// All possible targets for the election, i.e. the candidates. - /// - /// If `maybe_max_len` is `Some(v)` then the resulting vector MUST NOT be longer than `v` items - /// long. - /// - /// It is assumed that this function will only consume a notable amount of weight, when it - /// returns `Ok(_)`. - fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)>; - - /// All possible voters for the election. - /// - /// Note that if a notion of self-vote exists, it should be represented here. - /// - /// If `maybe_max_len` is `Some(v)` then the resulting vector MUST NOT be longer than `v` items - /// long. - /// - /// It is assumed that this function will only consume a notable amount of weight, when it - /// returns `Ok(_)`. - fn voters( - maybe_max_len: Option, - ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)>; - - /// The number of targets to elect. - fn desired_targets() -> data_provider::Result<(u32, Self::Additional)>; - - /// Provide a best effort prediction about when the next election is about to happen. - /// - /// In essence, the implementor should predict with this function when it will trigger the - /// [`ElectionProvider::elect`]. - /// - /// This is only useful for stateful election providers. - fn next_election_prediction(now: BlockNumber) -> BlockNumber; - - /// Utility function only to be used in benchmarking scenarios, to be implemented optionally, - /// else a noop. - #[cfg(any(feature = "runtime-benchmarks", test))] - fn put_snapshot( - _voters: Vec<(AccountId, VoteWeight, Vec)>, - _targets: Vec, - ) { - } -} - -#[cfg(feature = "std")] -impl ElectionDataProvider for () { - type Additional = (); - fn targets(_maybe_max_len: Option) -> data_provider::Result<(Vec, Self::Additional)> { - Ok(Default::default()) - } - fn voters( - _maybe_max_len: Option, - ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { - Ok(Default::default()) - } - fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { - Ok(Default::default()) - } - fn next_election_prediction(now: BlockNumber) -> BlockNumber { - now - } -} - -/// Something that can compute the result of an election and pass it back to the caller. -/// -/// This trait only provides an interface to _request_ an election, i.e. -/// [`ElectionProvider::elect`]. That data required for the election need to be passed to the -/// implemented of this trait through [`ElectionProvider::DataProvider`]. -pub trait ElectionProvider { - /// The error type that is returned by the provider. - type Error: Debug; - - /// The data provider of the election. - type DataProvider: ElectionDataProvider; - - /// Elect a new set of winners. - /// - /// The result is returned in a target major format, namely as vector of supports. - fn elect() -> Result, Self::Error>; -} - -#[cfg(feature = "std")] -impl ElectionProvider for () { - type Error = &'static str; - type DataProvider = (); - - fn elect() -> Result, Self::Error> { - Err("<() as ElectionProvider> cannot do anything.") - } -} diff --git a/primitives/election-providers/src/onchain.rs b/primitives/election-providers/src/onchain.rs deleted file mode 100644 index 92532800f73d9..0000000000000 --- a/primitives/election-providers/src/onchain.rs +++ /dev/null @@ -1,167 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2020 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. - -//! An implementation of [`ElectionProvider`] that does an on-chain sequential phragmen. - -use crate::{ElectionDataProvider, ElectionProvider}; -use sp_npos_elections::*; -use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, prelude::*}; - -/// Errors of the on-chain election. -#[derive(Eq, PartialEq, Debug)] -pub enum Error { - /// An internal error in the NPoS elections crate. - NposElections(sp_npos_elections::Error), - /// Errors from the data provider. - DataProvider(&'static str), -} - -impl From for Error { - fn from(e: sp_npos_elections::Error) -> Self { - Error::NposElections(e) - } -} - -/// A simple on-chain implementation of the election provider trait. -/// -/// This will accept voting data on the fly and produce the results immediately. -/// -/// ### Warning -/// -/// This can be very expensive to run frequently on-chain. Use with care. Moreover, this -/// implementation ignores the additional data of the election data provider and gives no insight on -/// how much weight was consumed. -/// -/// Finally, this implementation does not impose any limits on the number of voters and targets that -/// are provided. -pub struct OnChainSequentialPhragmen(PhantomData); - -/// Configuration trait of [`OnChainSequentialPhragmen`]. -/// -/// Note that this is similar to a pallet traits, but [`OnChainSequentialPhragmen`] is not a pallet. -pub trait Config { - /// The account identifier type. - type AccountId: IdentifierT; - /// The block number type. - type BlockNumber; - /// The accuracy used to compute the election: - type Accuracy: PerThing128; - /// Something that provides the data for election. - type DataProvider: ElectionDataProvider; -} - -impl ElectionProvider for OnChainSequentialPhragmen { - type Error = Error; - type DataProvider = T::DataProvider; - - fn elect() -> Result, Self::Error> { - let (voters, _) = Self::DataProvider::voters(None).map_err(Error::DataProvider)?; - let (targets, _) = Self::DataProvider::targets(None).map_err(Error::DataProvider)?; - let (desired_targets, _) = - Self::DataProvider::desired_targets().map_err(Error::DataProvider)?; - - let mut stake_map: BTreeMap = BTreeMap::new(); - - voters.iter().for_each(|(v, s, _)| { - stake_map.insert(v.clone(), *s); - }); - - let stake_of = |w: &T::AccountId| -> VoteWeight { - stake_map.get(w).cloned().unwrap_or_default() - }; - - let ElectionResult { winners, assignments } = - seq_phragmen::<_, T::Accuracy>(desired_targets as usize, targets, voters, None) - .map_err(Error::from)?; - - let staked = assignment_ratio_to_staked_normalized(assignments, &stake_of)?; - let winners = to_without_backing(winners); - - to_supports(&winners, &staked).map_err(Error::from) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use sp_npos_elections::Support; - use sp_runtime::Perbill; - - type AccountId = u64; - type BlockNumber = u32; - - struct Runtime; - impl Config for Runtime { - type AccountId = AccountId; - type BlockNumber = BlockNumber; - type Accuracy = Perbill; - type DataProvider = mock_data_provider::DataProvider; - } - - type OnChainPhragmen = OnChainSequentialPhragmen; - - mod mock_data_provider { - use super::*; - use crate::data_provider; - - pub struct DataProvider; - - impl ElectionDataProvider for DataProvider { - type Additional = (); - fn voters( - _: Option, - ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Self::Additional)> { - Ok((vec![(1, 10, vec![10, 20]), (2, 20, vec![30, 20]), (3, 30, vec![10, 30])], ())) - } - - fn targets(_: Option) -> data_provider::Result<(Vec, Self::Additional)> { - Ok((vec![10, 20, 30], ())) - } - - fn desired_targets() -> data_provider::Result<(u32, Self::Additional)> { - Ok((2, ())) - } - - fn next_election_prediction(_: BlockNumber) -> BlockNumber { - 0 - } - } - } - - #[test] - fn onchain_seq_phragmen_works() { - assert_eq!( - OnChainPhragmen::elect().unwrap(), - vec![ - ( - 10, - Support { - total: 25, - voters: vec![(1, 10), (3, 15)] - } - ), - ( - 30, - Support { - total: 35, - voters: vec![(2, 20), (3, 15)] - } - ) - ] - ); - } -} From d5a35e38673709a43c62db092787b2333c3811fc Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 5 Mar 2021 16:50:57 +0100 Subject: [PATCH 41/57] Reorg done. --- Cargo.lock | 1 + frame/babe/src/mock.rs | 1 + .../election-provider-multi-phase/Cargo.toml | 2 +- .../election-provider-multi-phase/src/lib.rs | 23 ++++++++++--------- frame/election-provider-support/Cargo.toml | 4 +++- frame/election-provider-support/src/lib.rs | 4 ++-- .../election-provider-support/src/onchain.rs | 11 +++++---- frame/grandpa/src/mock.rs | 1 + frame/offences/benchmarking/src/mock.rs | 1 + frame/session/benchmarking/src/mock.rs | 1 + frame/staking/fuzzer/src/mock.rs | 2 +- frame/staking/src/mock.rs | 1 + 12 files changed, 32 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 336cc81ac5530..4006b38798be9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1457,6 +1457,7 @@ name = "election-provider-support" version = "3.0.0" dependencies = [ "frame-support", + "frame-system", "parity-scale-codec", "sp-arithmetic", "sp-npos-elections", diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index dfa58d270305d..64cfa012e78cb 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -187,6 +187,7 @@ parameter_types! { impl onchain::Config for Test { type AccountId = ::AccountId; type BlockNumber = ::BlockNumber; + type BlockWeights = (); type Accuracy = Perbill; type DataProvider = Staking; } diff --git a/frame/election-provider-multi-phase/Cargo.toml b/frame/election-provider-multi-phase/Cargo.toml index 9a39b7980f86f..cc5451fad6ca6 100644 --- a/frame/election-provider-multi-phase/Cargo.toml +++ b/frame/election-provider-multi-phase/Cargo.toml @@ -43,7 +43,7 @@ sp-core = { version = "3.0.0", path = "../../primitives/core" } sp-tracing = { version = "3.0.0", path = "../../primitives/tracing" } election-provider-support = { version = "3.0.0", features = ["runtime-benchmarks"], path = "../election-provider-support" } pallet-balances = { version = "3.0.0", path = "../balances" } -frame-benchmarking = { path = "../benchmarking" , version = "3.1.0"} +frame-benchmarking = { version = "3.1.0", path = "../benchmarking", } [features] default = ["std"] diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index dce43c4ae0690..cf5797b77404f 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -262,6 +262,7 @@ struct OnChainConfig(sp_std::marker::PhantomData); impl onchain::Config for OnChainConfig { type AccountId = T::AccountId; type BlockNumber = T::BlockNumber; + type BlockWeights = T::BlockWeights; type Accuracy = T::OnChainAccuracy; type DataProvider = T::DataProvider; } @@ -1047,7 +1048,7 @@ impl Pallet { } /// On-chain fallback of election. - fn onchain_fallback() -> Result, ElectionError> { + fn onchain_fallback() -> Result<(Supports, Weight), ElectionError> { > as ElectionProvider< T::AccountId, T::BlockNumber, @@ -1055,21 +1056,21 @@ impl Pallet { .map_err(Into::into) } - fn do_elect() -> Result, ElectionError> { + fn do_elect() -> Result<(Supports, Weight), ElectionError> { >::take() .map_or_else( || match T::Fallback::get() { FallbackStrategy::OnChain => Self::onchain_fallback() - .map(|r| (r, ElectionCompute::OnChain)) + .map(|(s, w)| (s, w, ElectionCompute::OnChain)) .map_err(Into::into), FallbackStrategy::Nothing => Err(ElectionError::NoFallbackConfigured), }, - |ReadySolution { supports, compute, .. }| Ok((supports, compute)), + |ReadySolution { supports, compute, .. }| Ok((supports, 0, compute)), ) - .map(|(supports, compute)| { + .map(|(supports, weight, compute)| { Self::deposit_event(Event::ElectionFinalized(Some(compute))); log!(info, "Finalized election round with compute {:?}.", compute); - supports + (supports, weight) }) .map_err(|err| { Self::deposit_event(Event::ElectionFinalized(None)); @@ -1083,11 +1084,11 @@ impl ElectionProvider for Pallet { type Error = ElectionError; type DataProvider = T::DataProvider; - fn elect() -> Result, Self::Error> { - let outcome = Self::do_elect(); + fn elect() -> Result<(Supports, Weight), Self::Error> { + let outcome_and_weight = Self::do_elect(); // IMPORTANT: regardless of if election was `Ok` or `Err`, we shall do some cleanup. Self::post_elect(); - outcome + outcome_and_weight } } @@ -1455,7 +1456,7 @@ mod tests { assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); // zilch solutions thus far. - let supports = MultiPhase::elect().unwrap(); + let (supports, _) = MultiPhase::elect().unwrap(); assert_eq!( supports, @@ -1493,7 +1494,7 @@ mod tests { // on-chain backup works though. roll_to(29); - let supports = MultiPhase::elect().unwrap(); + let (supports, _) = MultiPhase::elect().unwrap(); assert!(supports.len() > 0); }) } diff --git a/frame/election-provider-support/Cargo.toml b/frame/election-provider-support/Cargo.toml index 99c6b38148f3d..d9079b253eb77 100644 --- a/frame/election-provider-support/Cargo.toml +++ b/frame/election-provider-support/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "election-provider-support" +name = "election-provider-support" # TODO: rename to a prefix with frame- version = "3.0.0" authors = ["Parity Technologies "] edition = "2018" @@ -18,6 +18,7 @@ sp-std = { version = "3.0.0", default-features = false, path = "../../primitives sp-arithmetic = { version = "3.0.0", default-features = false, path = "../../primitives/arithmetic" } sp-npos-elections = { version = "3.0.0", default-features = false, path = "../../primitives/npos-elections" } frame-support = { version = "3.0.0", default-feature = false, path = "../support" } +frame-system = { version = "3.0.0", default-feature = false, path = "../system" } [dev-dependencies] sp-npos-elections = { version = "3.0.0", path = "../../primitives/npos-elections" } @@ -32,4 +33,5 @@ std = [ "sp-npos-elections/std", "sp-arithmetic/std", "frame-support/std", + "frame-system/std", ] diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index 9d7a2a6ce0157..385356c86c69d 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -253,7 +253,7 @@ pub trait ElectionProvider { /// Elect a new set of winners. /// /// The result is returned in a target major format, namely as vector of supports. - fn elect() -> Result, Self::Error>; + fn elect() -> Result<(Supports, Weight), Self::Error>; } #[cfg(feature = "std")] @@ -261,7 +261,7 @@ impl ElectionProvider for () { type Error = &'static str; type DataProvider = (); - fn elect() -> Result, Self::Error> { + fn elect() -> Result<(Supports, Weight), Self::Error> { Err("<() as ElectionProvider> cannot do anything.") } } diff --git a/frame/election-provider-support/src/onchain.rs b/frame/election-provider-support/src/onchain.rs index 3f9a6f54e4bb9..b00c8698037c1 100644 --- a/frame/election-provider-support/src/onchain.rs +++ b/frame/election-provider-support/src/onchain.rs @@ -20,6 +20,7 @@ use crate::{ElectionDataProvider, ElectionProvider}; use sp_npos_elections::*; use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, prelude::*}; +use frame_support::{traits::Get, weights::Weight}; /// Errors of the on-chain election. #[derive(Eq, PartialEq, Debug)] @@ -54,6 +55,8 @@ pub struct OnChainSequentialPhragmen(PhantomData); /// /// Note that this is similar to a pallet traits, but [`OnChainSequentialPhragmen`] is not a pallet. pub trait Config { + /// The block limits. + type BlockWeights: Get; /// The account identifier type. type AccountId: IdentifierT; /// The block number type. @@ -68,7 +71,7 @@ impl ElectionProvider for OnChainSequen type Error = Error; type DataProvider = T::DataProvider; - fn elect() -> Result, Self::Error> { + fn elect() -> Result<(Supports, Weight), Self::Error> { let (voters, _) = Self::DataProvider::voters(None).map_err(Error::DataProvider)?; let (targets, _) = Self::DataProvider::targets(None).map_err(Error::DataProvider)?; let (desired_targets, _) = @@ -91,7 +94,7 @@ impl ElectionProvider for OnChainSequen let staked = assignment_ratio_to_staked_normalized(assignments, &stake_of)?; let winners = to_without_backing(winners); - to_supports(&winners, &staked).map_err(Error::from) + to_supports(&winners, &staked).map_err(Error::from).map(|s| (s, T::BlockWeights::get().max_block)) } } @@ -104,9 +107,9 @@ mod tests { type AccountId = u64; type BlockNumber = u32; - struct Runtime; impl Config for Runtime { + type BlockWeights = (); type AccountId = AccountId; type BlockNumber = BlockNumber; type Accuracy = Perbill; @@ -145,7 +148,7 @@ mod tests { #[test] fn onchain_seq_phragmen_works() { assert_eq!( - OnChainPhragmen::elect().unwrap(), + OnChainPhragmen::elect().unwrap().0, vec![ ( 10, diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 7b9fb71b707f4..0d30916a5f5f2 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -193,6 +193,7 @@ parameter_types! { impl onchain::Config for Test { type AccountId = ::AccountId; type BlockNumber = ::BlockNumber; + type BlockWeights = (); type Accuracy = Perbill; type DataProvider = Staking; } diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 2d30a4e082367..4bcfc436825f2 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -152,6 +152,7 @@ pub type Extrinsic = sp_runtime::testing::TestXt; impl onchain::Config for Test { type AccountId = AccountId; type BlockNumber = BlockNumber; + type BlockWeights = (); type Accuracy = Perbill; type DataProvider = Staking; } diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 4d544a7b3ab46..3a681835ea3f4 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -157,6 +157,7 @@ where impl onchain::Config for Test { type AccountId = AccountId; type BlockNumber = BlockNumber; + type BlockWeights = (); type Accuracy = sp_runtime::Perbill; type DataProvider = Staking; } diff --git a/frame/staking/fuzzer/src/mock.rs b/frame/staking/fuzzer/src/mock.rs index 6a637f5b660e5..204b9c6ab2bbc 100644 --- a/frame/staking/fuzzer/src/mock.rs +++ b/frame/staking/fuzzer/src/mock.rs @@ -162,7 +162,7 @@ impl election_provider_support::ElectionProvider for Moc type Error = (); type DataProvider = pallet_staking::Module; - fn elect() -> Result, Self::Error> { + fn elect() -> Result<(sp_npos_elections::Supports, frame_support::weights::Weight), Self::Error> { Err(()) } } diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 931643d61c078..010a7228be169 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -243,6 +243,7 @@ impl OnUnbalanced> for RewardRemainderMock { impl onchain::Config for Test { type AccountId = AccountId; type BlockNumber = BlockNumber; + type BlockWeights = BlockWeights; type Accuracy = Perbill; type DataProvider = Staking; } From 745b76f20fb1b532cd17e069c7a015e433ad9cf4 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 5 Mar 2021 18:10:18 +0100 Subject: [PATCH 42/57] Fix build --- frame/election-provider-multi-phase/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/election-provider-multi-phase/Cargo.toml b/frame/election-provider-multi-phase/Cargo.toml index cc5451fad6ca6..a462f95324a80 100644 --- a/frame/election-provider-multi-phase/Cargo.toml +++ b/frame/election-provider-multi-phase/Cargo.toml @@ -43,7 +43,7 @@ sp-core = { version = "3.0.0", path = "../../primitives/core" } sp-tracing = { version = "3.0.0", path = "../../primitives/tracing" } election-provider-support = { version = "3.0.0", features = ["runtime-benchmarks"], path = "../election-provider-support" } pallet-balances = { version = "3.0.0", path = "../balances" } -frame-benchmarking = { version = "3.1.0", path = "../benchmarking", } +frame-benchmarking = { version = "3.1.0", path = "../benchmarking" } [features] default = ["std"] From deeda2d31eab0ae5e769158047706832dbc347cd Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 8 Mar 2021 13:58:21 +0100 Subject: [PATCH 43/57] Another rename --- frame/babe/Cargo.toml | 2 +- frame/babe/src/mock.rs | 2 +- .../election-provider-multi-phase/Cargo.toml | 6 ++--- .../src/benchmarking.rs | 2 +- .../election-provider-multi-phase/src/lib.rs | 22 ++++++++++--------- .../election-provider-multi-phase/src/mock.rs | 2 +- .../src/unsigned.rs | 2 +- frame/election-provider-support/Cargo.toml | 8 +++---- frame/election-provider-support/src/lib.rs | 2 +- frame/grandpa/Cargo.toml | 2 +- frame/grandpa/src/mock.rs | 2 +- frame/offences/benchmarking/Cargo.toml | 4 ++-- frame/offences/benchmarking/src/mock.rs | 2 +- frame/session/benchmarking/Cargo.toml | 4 ++-- frame/session/benchmarking/src/mock.rs | 2 +- frame/staking/Cargo.toml | 8 +++---- frame/staking/fuzzer/Cargo.toml | 2 +- frame/staking/fuzzer/src/mock.rs | 9 ++++++-- frame/staking/src/lib.rs | 6 ++--- frame/staking/src/mock.rs | 2 +- frame/staking/src/tests.rs | 2 +- 21 files changed, 50 insertions(+), 43 deletions(-) diff --git a/frame/babe/Cargo.toml b/frame/babe/Cargo.toml index b32d7c0b1e68f..33a7f873d510f 100644 --- a/frame/babe/Cargo.toml +++ b/frame/babe/Cargo.toml @@ -38,7 +38,7 @@ pallet-offences = { version = "3.0.0", path = "../offences" } pallet-staking = { version = "3.0.0", path = "../staking" } pallet-staking-reward-curve = { version = "3.0.0", path = "../staking/reward-curve" } sp-core = { version = "3.0.0", path = "../../primitives/core" } -election-provider-support = { version = "3.0.0", path = "../election-provider-support" } +frame-election-provider-support = { version = "3.0.0", path = "../election-provider-support" } [features] default = ["std"] diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 64cfa012e78cb..2af8afb81822c 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -37,7 +37,7 @@ use sp_consensus_babe::{AuthorityId, AuthorityPair, Slot}; use sp_consensus_vrf::schnorrkel::{VRFOutput, VRFProof}; use sp_staking::SessionIndex; use pallet_staking::EraIndex; -use election_provider_support::onchain; +use frame_election_provider_support::onchain; use pallet_session::historical as pallet_session_historical; type DummyValidatorId = u64; diff --git a/frame/election-provider-multi-phase/Cargo.toml b/frame/election-provider-multi-phase/Cargo.toml index a462f95324a80..4b5178faa8e86 100644 --- a/frame/election-provider-multi-phase/Cargo.toml +++ b/frame/election-provider-multi-phase/Cargo.toml @@ -26,7 +26,7 @@ sp-std = { version = "3.0.0", default-features = false, path = "../../primitives sp-runtime = { version = "3.0.0", default-features = false, path = "../../primitives/runtime" } sp-npos-elections = { version = "3.0.0", default-features = false, path = "../../primitives/npos-elections" } sp-arithmetic = { version = "3.0.0", default-features = false, path = "../../primitives/arithmetic" } -election-provider-support = { version = "3.0.0", default-features = false, path = "../election-provider-support" } +frame-election-provider-support = { version = "3.0.0", default-features = false, path = "../election-provider-support" } # Optional imports for benchmarking frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true } @@ -41,7 +41,7 @@ substrate-test-utils = { version = "3.0.0", path = "../../test-utils" } sp-io = { version = "3.0.0", path = "../../primitives/io" } sp-core = { version = "3.0.0", path = "../../primitives/core" } sp-tracing = { version = "3.0.0", path = "../../primitives/tracing" } -election-provider-support = { version = "3.0.0", features = ["runtime-benchmarks"], path = "../election-provider-support" } +frame-election-provider-support = { version = "3.0.0", features = ["runtime-benchmarks"], path = "../election-provider-support" } pallet-balances = { version = "3.0.0", path = "../balances" } frame-benchmarking = { version = "3.1.0", path = "../benchmarking" } @@ -60,7 +60,7 @@ std = [ "sp-runtime/std", "sp-npos-elections/std", "sp-arithmetic/std", - "election-provider-support/std", + "frame-election-provider-support/std", "log/std", ] runtime-benchmarks = [ diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index b1282a408972a..85c6ec593a258 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -24,7 +24,7 @@ pub use frame_benchmarking::{account, benchmarks, whitelist_account, whitelisted use frame_support::{assert_ok, traits::OnInitialize}; use frame_system::RawOrigin; use rand::{prelude::SliceRandom, rngs::SmallRng, SeedableRng}; -use election_provider_support::Assignment; +use frame_election_provider_support::Assignment; use sp_arithmetic::traits::One; use sp_runtime::InnerOf; use sp_std::convert::TryInto; diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index df3786b772409..1f325225893ba 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -23,9 +23,10 @@ //! ## Phases //! //! The timeline of pallet is as follows. At each block, -//! [`election_provider_support::ElectionDataProvider::next_election_prediction`] is used to estimate -//! the time remaining to the next call to [`election_provider_support::ElectionProvider::elect`]. Based -//! on this, a phase is chosen. The timeline is as follows. +//! [`frame_election_provider_support::ElectionDataProvider::next_election_prediction`] is used to +//! estimate the time remaining to the next call to +//! [`frame_election_provider_support::ElectionProvider::elect`]. Based on this, a phase is chosen. +//! The timeline is as follows. //! //! ```ignore //! elect() @@ -149,7 +150,8 @@ //! are helpful for logging and are thus nested as: //! - [`ElectionError::Miner`]: wraps a [`unsigned::MinerError`]. //! - [`ElectionError::Feasibility`]: wraps a [`FeasibilityError`]. -//! - [`ElectionError::OnChainFallback`]: wraps a [`election_provider_support::onchain::Error`]. +//! - [`ElectionError::OnChainFallback`]: wraps a +//! [`frame_election_provider_support::onchain::Error`]. //! //! Note that there could be an overlap between these sub-errors. For example, A //! `SnapshotUnavailable` can happen in both miner and feasibility check phase. @@ -184,10 +186,10 @@ //! //! **Recursive Fallback**: Currently, the fallback is a separate enum. A different and fancier way //! of doing this would be to have the fallback be another -//! [`election_provider_support::ElectionProvider`]. In this case, this pallet can even have the -//! on-chain election provider as fallback, or special _noop_ fallback that simply returns an error, -//! thus replicating [`FallbackStrategy::Nothing`]. In this case, we won't need the additional -//! config OnChainAccuracy either. +//! [`frame_election_provider_support::ElectionProvider`]. In this case, this pallet can even have +//! the on-chain election provider as fallback, or special _noop_ fallback that simply returns an +//! error, thus replicating [`FallbackStrategy::Nothing`]. In this case, we won't need the +//! additional config OnChainAccuracy either. //! //! **Score based on (byte) size**: We should always prioritize small solutions over bigger ones, if //! there is a tie. Even more harsh should be to enforce the bound of the `reduce` algorithm. @@ -211,7 +213,7 @@ use frame_support::{ weights::Weight, }; use frame_system::{ensure_none, offchain::SendTransactionTypes}; -use election_provider_support::{ElectionDataProvider, ElectionProvider, onchain}; +use frame_election_provider_support::{ElectionDataProvider, ElectionProvider, onchain}; use sp_npos_elections::{ assignment_ratio_to_staked_normalized, is_score_better, CompactSolution, ElectionScore, EvaluateSupport, PerThing128, Supports, VoteWeight, @@ -1276,7 +1278,7 @@ mod feasibility_check { #[cfg(test)] mod tests { use super::{mock::*, Event, *}; - use election_provider_support::ElectionProvider; + use frame_election_provider_support::ElectionProvider; use sp_npos_elections::Support; #[test] diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 175bc5a4f5a3d..4c492ce89592c 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -31,7 +31,7 @@ use sp_core::{ }, H256, }; -use election_provider_support::{ElectionDataProvider, data_provider}; +use frame_election_provider_support::{ElectionDataProvider, data_provider}; use sp_npos_elections::{ assignment_ratio_to_staked_normalized, seq_phragmen, to_supports, to_without_backing, CompactSolution, ElectionResult, EvaluateSupport, diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index d60412ccd99ed..6c0ffcbea1579 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -487,7 +487,7 @@ mod tests { }; use frame_support::{dispatch::Dispatchable, traits::OffchainWorker}; use mock::Call as OuterCall; - use election_provider_support::Assignment; + use frame_election_provider_support::Assignment; use sp_runtime::{traits::ValidateUnsigned, PerU16}; #[test] diff --git a/frame/election-provider-support/Cargo.toml b/frame/election-provider-support/Cargo.toml index d9079b253eb77..b360cd89eb57b 100644 --- a/frame/election-provider-support/Cargo.toml +++ b/frame/election-provider-support/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "election-provider-support" # TODO: rename to a prefix with frame- +name = "frame-election-provider-support" version = "3.0.0" authors = ["Parity Technologies "] edition = "2018" @@ -17,8 +17,8 @@ codec = { package = "parity-scale-codec", version = "2.0.0", default-features = sp-std = { version = "3.0.0", default-features = false, path = "../../primitives/std" } sp-arithmetic = { version = "3.0.0", default-features = false, path = "../../primitives/arithmetic" } sp-npos-elections = { version = "3.0.0", default-features = false, path = "../../primitives/npos-elections" } -frame-support = { version = "3.0.0", default-feature = false, path = "../support" } -frame-system = { version = "3.0.0", default-feature = false, path = "../system" } +frame-support = { version = "3.0.0", default-features = false, path = "../support" } +frame-system = { version = "3.0.0", default-features = false, path = "../system" } [dev-dependencies] sp-npos-elections = { version = "3.0.0", path = "../../primitives/npos-elections" } @@ -26,7 +26,6 @@ sp-runtime = { version = "3.0.0", path = "../../primitives/runtime" } [features] default = ["std"] -runtime-benchmarks = [] std = [ "codec/std", "sp-std/std", @@ -35,3 +34,4 @@ std = [ "frame-support/std", "frame-system/std", ] +runtime-benchmarks = [] diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index 385356c86c69d..4a393aaf79091 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -78,7 +78,7 @@ //! ## Example //! //! ```rust -//! # use election_provider_support::{*, data_provider}; +//! # use frame_election_provider_support::{*, data_provider}; //! # use sp_npos_elections::{Support, Assignment}; //! # use frame_support::weights::Weight; //! diff --git a/frame/grandpa/Cargo.toml b/frame/grandpa/Cargo.toml index 0c8b387fed354..547e3966d52a4 100644 --- a/frame/grandpa/Cargo.toml +++ b/frame/grandpa/Cargo.toml @@ -39,7 +39,7 @@ pallet-offences = { version = "3.0.0", path = "../offences" } pallet-staking = { version = "3.0.0", path = "../staking" } pallet-staking-reward-curve = { version = "3.0.0", path = "../staking/reward-curve" } pallet-timestamp = { version = "3.0.0", path = "../timestamp" } -election-provider-support = { version = "3.0.0", path = "../election-provider-support" } +frame-election-provider-support = { version = "3.0.0", path = "../election-provider-support" } [features] default = ["std"] diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 0d30916a5f5f2..6e83ae481d271 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -40,7 +40,7 @@ use sp_runtime::{ }; use sp_staking::SessionIndex; use pallet_session::historical as pallet_session_historical; -use election_provider_support::onchain; +use frame_election_provider_support::onchain; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; diff --git a/frame/offences/benchmarking/Cargo.toml b/frame/offences/benchmarking/Cargo.toml index 9c21798897354..6c249ebcc61d8 100644 --- a/frame/offences/benchmarking/Cargo.toml +++ b/frame/offences/benchmarking/Cargo.toml @@ -27,7 +27,7 @@ pallet-staking = { version = "3.0.0", default-features = false, features = ["run sp-runtime = { version = "3.0.0", default-features = false, path = "../../../primitives/runtime" } sp-staking = { version = "3.0.0", default-features = false, path = "../../../primitives/staking" } sp-std = { version = "3.0.0", default-features = false, path = "../../../primitives/std" } -election-provider-support = { version = "3.0.0", default-features = false, path = "../../election-provider-support" } +frame-election-provider-support = { version = "3.0.0", default-features = false, path = "../../election-provider-support" } [dev-dependencies] pallet-staking-reward-curve = { version = "3.0.0", path = "../../staking/reward-curve" } @@ -51,7 +51,7 @@ std = [ "pallet-staking/std", "sp-runtime/std", "sp-staking/std", - "election-provider-support/std", + "frame-election-provider-support/std", "sp-std/std", "codec/std", ] diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 4bcfc436825f2..8590449ca8037 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -29,7 +29,7 @@ use sp_runtime::{ traits::IdentityLookup, testing::{Header, UintAuthorityId}, }; -use election_provider_support::onchain; +use frame_election_provider_support::onchain; use pallet_session::historical as pallet_session_historical; type AccountId = u64; diff --git a/frame/session/benchmarking/Cargo.toml b/frame/session/benchmarking/Cargo.toml index dfb1ece4cec94..0c83347b1991f 100644 --- a/frame/session/benchmarking/Cargo.toml +++ b/frame/session/benchmarking/Cargo.toml @@ -31,14 +31,14 @@ pallet-staking-reward-curve = { version = "3.0.0", path = "../../staking/reward- sp-io ={ version = "3.0.0", path = "../../../primitives/io" } pallet-timestamp = { version = "3.0.0", path = "../../timestamp" } pallet-balances = { version = "3.0.0", path = "../../balances" } -election-provider-support = { version = "3.0.0", path = "../../election-provider-support" } +frame-election-provider-support = { version = "3.0.0", path = "../../election-provider-support" } [features] default = ["std"] std = [ "sp-std/std", "sp-session/std", - "election-provider-support/std", + "frame-election-provider-support/std", "sp-runtime/std", "frame-system/std", "frame-benchmarking/std", diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 3a681835ea3f4..8c392c4e10969 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -20,7 +20,7 @@ #![cfg(test)] use sp_runtime::traits::IdentityLookup; -use election_provider_support::onchain; +use frame_election_provider_support::onchain; use frame_support::parameter_types; type AccountId = u64; diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 1ea2ca524dc83..b4c2819403720 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -28,7 +28,7 @@ pallet-session = { version = "3.0.0", default-features = false, features = ["his pallet-authorship = { version = "3.0.0", default-features = false, path = "../authorship" } sp-application-crypto = { version = "3.0.0", default-features = false, path = "../../primitives/application-crypto" } log = { version = "0.4.14", default-features = false } -election-provider-support = { version = "3.0.0", default-features = false, path = "../election-provider-support" } +frame-election-provider-support = { version = "3.0.0", default-features = false, path = "../election-provider-support" } # Optional imports for benchmarking frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true } @@ -43,7 +43,7 @@ pallet-timestamp = { version = "3.0.0", path = "../timestamp" } pallet-staking-reward-curve = { version = "3.0.0", path = "../staking/reward-curve" } substrate-test-utils = { version = "3.0.0", path = "../../test-utils" } frame-benchmarking = { version = "3.1.0", path = "../benchmarking" } -election-provider-support = { version = "3.0.0", features = ["runtime-benchmarks"], path = "../election-provider-support" } +frame-election-provider-support = { version = "3.0.0", features = ["runtime-benchmarks"], path = "../election-provider-support" } rand_chacha = { version = "0.2" } parking_lot = "0.11.1" hex = "0.4" @@ -64,11 +64,11 @@ std = [ "pallet-authorship/std", "sp-application-crypto/std", "log/std", - "election-provider-support/std", + "frame-election-provider-support/std", ] runtime-benchmarks = [ "frame-benchmarking", - "election-provider-support/runtime-benchmarks", + "frame-election-provider-support/runtime-benchmarks", "rand_chacha", ] try-runtime = ["frame-support/try-runtime"] diff --git a/frame/staking/fuzzer/Cargo.toml b/frame/staking/fuzzer/Cargo.toml index aa424add7f434..fb36327e5e919 100644 --- a/frame/staking/fuzzer/Cargo.toml +++ b/frame/staking/fuzzer/Cargo.toml @@ -28,7 +28,7 @@ sp-io ={ version = "3.0.0", path = "../../../primitives/io" } sp-core = { version = "3.0.0", path = "../../../primitives/core" } sp-npos-elections = { version = "3.0.0", path = "../../../primitives/npos-elections" } sp-runtime = { version = "3.0.0", path = "../../../primitives/runtime" } -election-provider-support = { version = "3.0.0", path = "../../election-provider-support" } +frame-election-provider-support = { version = "3.0.0", path = "../../election-provider-support" } serde = "1.0.101" [features] diff --git a/frame/staking/fuzzer/src/mock.rs b/frame/staking/fuzzer/src/mock.rs index 204b9c6ab2bbc..a87e1fc083013 100644 --- a/frame/staking/fuzzer/src/mock.rs +++ b/frame/staking/fuzzer/src/mock.rs @@ -158,11 +158,16 @@ where } pub struct MockElectionProvider; -impl election_provider_support::ElectionProvider for MockElectionProvider { +impl frame_election_provider_support::ElectionProvider + for MockElectionProvider +{ type Error = (); type DataProvider = pallet_staking::Module; - fn elect() -> Result<(sp_npos_elections::Supports, frame_support::weights::Weight), Self::Error> { + fn elect() -> Result< + (sp_npos_elections::Supports, frame_support::weights::Weight), + Self::Error + > { Err(()) } } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 93797ef82edba..56805acc8318e 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -331,7 +331,7 @@ use sp_npos_elections::{ to_supports, EvaluateSupport, seq_phragmen, generate_solution_type, is_score_better, Supports, VoteWeight, CompactSolution, PerThing128, }; -use election_provider_support::{ElectionProvider, data_provider}; +use frame_election_provider_support::{ElectionProvider, data_provider}; pub use weights::WeightInfo; const STAKING_ID: LockIdentifier = *b"staking "; @@ -798,7 +798,7 @@ pub trait Config: frame_system::Config + SendTransactionTypes> { type CurrencyToVote: CurrencyToVote>; /// Something that provides the election functionality. - type ElectionProvider: election_provider_support::ElectionProvider< + type ElectionProvider: frame_election_provider_support::ElectionProvider< Self::AccountId, Self::BlockNumber, // we only accept an election provider that has staking as data provider. @@ -3335,7 +3335,7 @@ impl Module { } } -impl election_provider_support::ElectionDataProvider +impl frame_election_provider_support::ElectionDataProvider for Module { fn desired_targets() -> data_provider::Result<(u32, Weight)> { diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 010a7228be169..b0e3d9629a358 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -37,7 +37,7 @@ use sp_runtime::{ }; use sp_staking::offence::{OffenceDetails, OnOffenceHandler}; use std::{cell::RefCell, collections::HashSet}; -use election_provider_support::onchain; +use frame_election_provider_support::onchain; pub const INIT_TIMESTAMP: u64 = 30_000; pub const BLOCK_TIME: u64 = 1000; diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 304a8bbef1eb6..0b9ea71bfdd8d 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -5018,7 +5018,7 @@ fn do_not_die_when_active_is_ed() { mod election_data_provider { use super::*; - use election_provider_support::ElectionDataProvider; + use frame_election_provider_support::ElectionDataProvider; #[test] fn voters_include_self_vote() { From 5c429b85350232785bd0d6d528c1ba79b9f0a2a8 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 8 Mar 2021 15:06:25 +0100 Subject: [PATCH 44/57] Fix build --- Cargo.lock | 724 +++++++++--------- .../src/benchmarking.rs | 80 +- .../election-provider-multi-phase/src/lib.rs | 10 +- .../election-provider-multi-phase/src/mock.rs | 7 + .../src/unsigned.rs | 3 + .../src/weights.rs | 16 +- frame/election-provider-support/src/lib.rs | 2 +- 7 files changed, 435 insertions(+), 407 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d37de24a1321..c6731047a1651 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,9 +21,9 @@ dependencies = [ [[package]] name = "adler" -version = "0.2.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aead" @@ -129,9 +129,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "0.4.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db55d72333851e17d572bec876e390cd3b11eb1ef53ae821dd9f3b653d2b4569" +checksum = "698b65a961a9d730fb45b6b0327e20207810c9f61ee421b082b27ba003f49e2b" [[package]] name = "arrayref" @@ -175,10 +175,11 @@ dependencies = [ [[package]] name = "assert_cmd" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dc1679af9a1ab4bea16f228b05d18f8363f8327b1fa8db00d2760cfafc6b61e" +checksum = "f2475b58cd94eb4f70159f4fd8844ba3b807532fe3131b3373fae060bbe30396" dependencies = [ + "bstr", "doc-comment", "predicates", "predicates-core", @@ -188,9 +189,9 @@ dependencies = [ [[package]] name = "assert_matches" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695579f0f2520f3774bb40461e5adb066459d4e0af4d59d20175484fb8e9edf1" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-attributes" @@ -204,9 +205,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59740d83946db6a5af71ae25ddf9562c2b176b2ca42cf99a455f09f4a220d6b9" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" dependencies = [ "concurrent-queue", "event-listener", @@ -283,13 +284,13 @@ dependencies = [ [[package]] name = "async-process" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8cea09c1fb10a317d1b5af8024eeba256d6554763e85ecd90ff8df31c7bbda" +checksum = "ef37b86e2fa961bae5a4d212708ea0154f904ce31d1a4a7f47e1bbc33a0c040b" dependencies = [ "async-io", "blocking", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "event-listener", "futures-lite", "once_cell", @@ -309,7 +310,7 @@ dependencies = [ "async-io", "async-lock", "async-process", - "crossbeam-utils 0.8.1", + "crossbeam-utils 0.8.3", "futures-channel", "futures-core", "futures-io", @@ -320,7 +321,7 @@ dependencies = [ "memchr", "num_cpus", "once_cell", - "pin-project-lite 0.2.5", + "pin-project-lite 0.2.6", "pin-utils", "slab", "wasm-bindgen-futures", @@ -334,9 +335,9 @@ checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" [[package]] name = "async-trait" -version = "0.1.42" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" +checksum = "7e098e9c493fdf92832223594d9a164f96bdf17ba81a42aff86f85c76768726a" dependencies = [ "proc-macro2", "quote", @@ -353,7 +354,7 @@ dependencies = [ "futures-sink", "futures-util", "memchr", - "pin-project-lite 0.2.5", + "pin-project-lite 0.2.6", ] [[package]] @@ -366,7 +367,7 @@ dependencies = [ "futures-sink", "futures-util", "memchr", - "pin-project-lite 0.2.5", + "pin-project-lite 0.2.6", ] [[package]] @@ -441,9 +442,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bincode" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" +checksum = "d175dfa69e619905c4c3cdb7c3c203fa3bdd5d51184e3afdb2742c0280493772" dependencies = [ "byteorder", "serde", @@ -481,9 +482,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitvec" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5011ffc90248764d7005b0e10c7294f5aa1bd87d9dd7248f4ad475b347c294d" +checksum = "1f682656975d3a682daff957be4ddeb65d6ad656737cd821f2d00685ae466af1" dependencies = [ "funty", "radium", @@ -558,7 +559,7 @@ dependencies = [ "block-padding 0.1.5", "byte-tools", "byteorder", - "generic-array 0.12.3", + "generic-array 0.12.4", ] [[package]] @@ -617,9 +618,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf" +checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" dependencies = [ "lazy_static", "memchr", @@ -638,9 +639,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.6.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" [[package]] name = "byte-slice-cast" @@ -656,9 +657,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" -version = "1.4.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "bytes" @@ -722,9 +723,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" dependencies = [ "jobserver", ] @@ -804,9 +805,9 @@ dependencies = [ [[package]] name = "cid" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d88f30b1e74e7063df5711496f3ee6e74a9735d62062242d70cddf77717f18e" +checksum = "ff0e3bc0b6446b3f9663c1a6aba6ef06c5aeaa1bc92bd18077be337198ab9768" dependencies = [ "multibase", "multihash", @@ -885,12 +886,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "const_fn" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" - [[package]] name = "constant_time_eq" version = "0.1.5" @@ -1081,7 +1076,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.1", + "crossbeam-utils 0.8.3", ] [[package]] @@ -1102,8 +1097,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" dependencies = [ "cfg-if 1.0.0", - "crossbeam-epoch 0.9.1", - "crossbeam-utils 0.8.1", + "crossbeam-epoch 0.9.3", + "crossbeam-utils 0.8.3", ] [[package]] @@ -1123,13 +1118,12 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" +checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" dependencies = [ "cfg-if 1.0.0", - "const_fn", - "crossbeam-utils 0.8.1", + "crossbeam-utils 0.8.3", "lazy_static", "memoffset 0.6.1", "scopeguard", @@ -1159,9 +1153,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" dependencies = [ "autocfg", "cfg-if 1.0.0", @@ -1180,7 +1174,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ - "generic-array 0.12.3", + "generic-array 0.12.4", "subtle 1.0.0", ] @@ -1196,9 +1190,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d58633299b24b515ac72a3f869f8b91306a3cec616a602843a383acd6f9e97" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" dependencies = [ "bstr", "csv-core", @@ -1227,9 +1221,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10bcb9d7dcbf7002aaffbb53eac22906b64cdcc127971dcc387d8eb7c95d5560" +checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19" dependencies = [ "quote", "syn", @@ -1321,7 +1315,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "generic-array 0.12.3", + "generic-array 0.12.4", ] [[package]] @@ -1452,19 +1446,6 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -[[package]] -name = "election-provider-support" -version = "3.0.0" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "sp-arithmetic", - "sp-npos-elections", - "sp-runtime", - "sp-std", -] - [[package]] name = "enumflags2" version = "0.6.4" @@ -1500,9 +1481,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" +checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" dependencies = [ "atty", "humantime 2.1.0", @@ -1559,7 +1540,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", ] [[package]] @@ -1631,7 +1612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6447e2f8178843749e8c8003206def83ec124a7859475395777a28b5338647c" dependencies = [ "either", - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "log", "num-traits", @@ -1686,9 +1667,9 @@ dependencies = [ [[package]] name = "form_urlencoded" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" dependencies = [ "matches", "percent-encoding 2.1.0", @@ -1736,6 +1717,19 @@ dependencies = [ "structopt", ] +[[package]] +name = "frame-election-provider-support" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "sp-arithmetic", + "sp-npos-elections", + "sp-runtime", + "sp-std", +] + [[package]] name = "frame-executive" version = "3.0.0" @@ -1910,6 +1904,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "fs_extra" version = "1.2.0" @@ -1946,15 +1950,15 @@ checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" [[package]] name = "futures" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7e4c2612746b0df8fed4ce0c69156021b704c9aefa360311c04e6e9e002eed" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150" +checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1" dependencies = [ "futures-channel", "futures-core", @@ -1967,9 +1971,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" +checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" dependencies = [ "futures-core", "futures-sink", @@ -1977,9 +1981,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" +checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" [[package]] name = "futures-cpupool" @@ -1987,7 +1991,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" dependencies = [ - "futures 0.1.30", + "futures 0.1.31", "num_cpus", ] @@ -1997,8 +2001,8 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdcef58a173af8148b182684c9f2d5250875adbcaff7b5794073894f9d8634a9" dependencies = [ - "futures 0.1.30", - "futures 0.3.12", + "futures 0.1.31", + "futures 0.3.13", "lazy_static", "log", "parking_lot 0.9.0", @@ -2009,9 +2013,9 @@ dependencies = [ [[package]] name = "futures-executor" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9" +checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1" dependencies = [ "futures-core", "futures-task", @@ -2021,9 +2025,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" +checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" [[package]] name = "futures-lite" @@ -2036,15 +2040,15 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite 0.2.5", + "pin-project-lite 0.2.6", "waker-fn", ] [[package]] name = "futures-macro" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" +checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -2065,18 +2069,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" +checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" [[package]] name = "futures-task" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" -dependencies = [ - "once_cell", -] +checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" [[package]] name = "futures-timer" @@ -2096,11 +2097,11 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" +checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" dependencies = [ - "futures 0.1.30", + "futures 0.1.31", "futures-channel", "futures-core", "futures-io", @@ -2108,7 +2109,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.5", + "pin-project-lite 0.2.6", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -2123,18 +2124,18 @@ checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" [[package]] name = "generic-array" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" dependencies = [ "typenum", ] [[package]] name = "generic-array" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" +checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" dependencies = [ "typenum", ] @@ -2237,7 +2238,7 @@ dependencies = [ "byteorder", "bytes 0.4.12", "fnv", - "futures 0.1.30", + "futures 0.1.31", "http 0.1.21", "indexmap", "log", @@ -2274,9 +2275,9 @@ checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" [[package]] name = "handlebars" -version = "3.5.2" +version = "3.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "964d0e99a61fe9b1b347389b77ebf8b7e1587b70293676aaca7d27e59b9073b2" +checksum = "cdb0867bbc5a3da37a753e78021d5fcf8a4db00e18dd2dd90fd36e24190e162d" dependencies = [ "log", "pest", @@ -2330,9 +2331,9 @@ dependencies = [ [[package]] name = "hex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-literal" @@ -2373,15 +2374,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" dependencies = [ "digest 0.8.1", - "generic-array 0.12.3", + "generic-array 0.12.4", "hmac 0.7.1", ] [[package]] name = "honggfuzz" -version = "0.5.52" +version = "0.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ead88897bcad1c396806d6ccba260a0363e11da997472e9e19ab9889969083a2" +checksum = "bea09577d948a98a5f59b7c891e274c4fb35ad52f67782b3d0cb53b9c05301f1" dependencies = [ "arbitrary", "lazy_static", @@ -2417,7 +2418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" dependencies = [ "bytes 0.4.12", - "futures 0.1.30", + "futures 0.1.31", "http 0.1.21", "tokio-buf", ] @@ -2434,9 +2435,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.3.4" +version = "1.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" +checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" [[package]] name = "httpdate" @@ -2461,12 +2462,12 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.12.35" +version = "0.12.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" +checksum = "5c843caf6296fc1f93444735205af9ed4e109a539005abb2564ae1d6fad34c52" dependencies = [ "bytes 0.4.12", - "futures 0.1.30", + "futures 0.1.31", "futures-cpupool", "h2 0.1.26", "http 0.1.21", @@ -2491,9 +2492,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.13.9" +version = "0.13.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf" +checksum = "8a6f157065790a3ed2f88679250419b5cdd96e714a0d65f7797fd337186e96bb" dependencies = [ "bytes 0.5.6", "futures-channel", @@ -2505,7 +2506,7 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project 1.0.4", + "pin-project 1.0.5", "socket2", "tokio 0.2.25", "tower-service", @@ -2522,7 +2523,7 @@ dependencies = [ "bytes 0.5.6", "ct-logs", "futures-util", - "hyper 0.13.9", + "hyper 0.13.10", "log", "rustls 0.18.1", "rustls-native-certs", @@ -2544,9 +2545,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21" dependencies = [ "matches", "unicode-bidi", @@ -2581,7 +2582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b8538953a3f0d0d3868f0a706eb4273535e10d72acb5c82c1c23ae48835c85" dependencies = [ "async-io", - "futures 0.3.12", + "futures 0.3.13", "futures-lite", "if-addrs", "ipnet", @@ -2621,9 +2622,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" dependencies = [ "autocfg", "hashbrown", @@ -2657,7 +2658,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64fa110ec7b8f493f416eed552740d10e7030ad5f63b2308f82c9608ec2df275" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "futures-timer 2.0.2", ] @@ -2717,9 +2718,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.46" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" +checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" dependencies = [ "wasm-bindgen", ] @@ -2731,8 +2732,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "489b9c612e60c766f751ab40fcb43cbb55a1e10bb44a9b4307ed510ca598cbd7" dependencies = [ "failure", - "futures 0.1.30", - "hyper 0.12.35", + "futures 0.1.31", + "hyper 0.12.36", "jsonrpc-core", "jsonrpc-pubsub", "log", @@ -2747,7 +2748,7 @@ version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0745a6379e3edc893c84ec203589790774e4247420033e71a76d3ab4687991fa" dependencies = [ - "futures 0.1.30", + "futures 0.1.31", "log", "serde", "serde_derive", @@ -2781,7 +2782,7 @@ version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb5c4513b7b542f42da107942b7b759f27120b5cc894729f88254b28dff44b7" dependencies = [ - "hyper 0.12.35", + "hyper 0.12.36", "jsonrpc-core", "jsonrpc-server-utils", "log", @@ -2854,8 +2855,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "124797a4ea7430d0675db78e065e53316e3f1a3cbf0ee4d6dbdd42db7b08e193" dependencies = [ "async-trait", - "futures 0.3.12", - "hyper 0.13.9", + "futures 0.3.13", + "hyper 0.13.10", "jsonrpsee-types", "jsonrpsee-utils", "log", @@ -2863,7 +2864,7 @@ dependencies = [ "serde_json", "thiserror", "unicase", - "url 2.2.0", + "url 2.2.1", ] [[package]] @@ -2885,7 +2886,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbf718f9a0d09f50621ea35f507679cf3ab66910a6d95844850076c1281a203c" dependencies = [ "async-trait", - "futures 0.3.12", + "futures 0.3.13", "log", "serde", "serde_json", @@ -2899,9 +2900,9 @@ version = "0.2.0-alpha" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0e45394ec3175a767c3c5bac584560e6ad9b56ebd73216c85ec8bab49619244" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "globset", - "hyper 0.13.9", + "hyper 0.13.10", "jsonrpsee-types", "lazy_static", "log", @@ -2989,7 +2990,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb1e98ba343d0b35f9009a8844cd2b87fa3192f7e79033ac05b00aeae0f3b0b5" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "js-sys", "kvdb", "kvdb-memorydb", @@ -3021,9 +3022,9 @@ checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" [[package]] name = "libc" -version = "0.2.84" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cca32fa0182e8c0989459524dc356b8f2b5c10f1b9eb521b7d182c03cf8c5ff" +checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a" [[package]] name = "libloading" @@ -3049,7 +3050,7 @@ checksum = "adc225a49973cf9ab10d0cdd6a4b8f0cda299df9b760824bbb623f15f8f0c95a" dependencies = [ "atomic", "bytes 1.0.1", - "futures 0.3.12", + "futures 0.3.13", "lazy_static", "libp2p-core", "libp2p-deflate", @@ -3074,7 +3075,7 @@ dependencies = [ "libp2p-yamux", "parity-multiaddr", "parking_lot 0.11.1", - "pin-project 1.0.4", + "pin-project 1.0.5", "smallvec 1.6.1", "wasm-timer", ] @@ -3090,7 +3091,7 @@ dependencies = [ "ed25519-dalek", "either", "fnv", - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "lazy_static", "libsecp256k1", @@ -3099,7 +3100,7 @@ dependencies = [ "multistream-select", "parity-multiaddr", "parking_lot 0.11.1", - "pin-project 1.0.4", + "pin-project 1.0.5", "prost", "prost-build", "rand 0.7.3", @@ -3120,7 +3121,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d42eed63305f0420736fa487f9acef720c4528bd7852a6a760f5ccde4813345" dependencies = [ "flate2", - "futures 0.3.12", + "futures 0.3.13", "libp2p-core", ] @@ -3130,7 +3131,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5153b6db68fd4baa3b304e377db744dd8fea8ff4e4504509ee636abcde88d3e3" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "libp2p-core", "log", ] @@ -3143,7 +3144,7 @@ checksum = "b3c63dfa06581b24b1d12bf9815b43689a784424be217d6545c800c7c75a207f" dependencies = [ "cuckoofilter", "fnv", - "futures 0.3.12", + "futures 0.3.13", "libp2p-core", "libp2p-swarm", "log", @@ -3164,7 +3165,7 @@ dependencies = [ "byteorder", "bytes 1.0.1", "fnv", - "futures 0.3.12", + "futures 0.3.13", "hex_fmt", "libp2p-core", "libp2p-swarm", @@ -3185,7 +3186,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b40fb36a059b7a8cce1514bd8b546fa612e006c9937caa7f5950cb20021fe91e" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "libp2p-core", "libp2p-swarm", "log", @@ -3206,7 +3207,7 @@ dependencies = [ "bytes 1.0.1", "either", "fnv", - "futures 0.3.12", + "futures 0.3.13", "libp2p-core", "libp2p-swarm", "log", @@ -3230,7 +3231,7 @@ dependencies = [ "async-io", "data-encoding", "dns-parser", - "futures 0.3.12", + "futures 0.3.13", "if-watch", "lazy_static", "libp2p-core", @@ -3250,7 +3251,7 @@ checksum = "350ce8b3923594aedabd5d6e3f875d058435052a29c3f32df378bc70d10be464" dependencies = [ "asynchronous-codec 0.6.0", "bytes 1.0.1", - "futures 0.3.12", + "futures 0.3.13", "libp2p-core", "log", "nohash-hasher", @@ -3268,7 +3269,7 @@ checksum = "4aca322b52a0c5136142a7c3971446fb1e9964923a526c9cc6ef3b7c94e57778" dependencies = [ "bytes 1.0.1", "curve25519-dalek 3.0.2", - "futures 0.3.12", + "futures 0.3.13", "lazy_static", "libp2p-core", "log", @@ -3288,7 +3289,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f3813276d0708c8db0f500d8beda1bda9ad955723b9cb272c41f4727256f73c" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "libp2p-core", "libp2p-swarm", "log", @@ -3305,7 +3306,7 @@ checksum = "9d58defcadb646ae4b033e130b48d87410bf76394dc3335496cae99dac803e61" dependencies = [ "asynchronous-codec 0.6.0", "bytes 1.0.1", - "futures 0.3.12", + "futures 0.3.13", "libp2p-core", "log", "prost", @@ -3320,9 +3321,9 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce3374f3b28162db9d3442c9347c4f14cb01e8290052615c7d341d40eae0599" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "log", - "pin-project 1.0.4", + "pin-project 1.0.5", "rand 0.7.3", "salsa20", "sha3", @@ -3336,7 +3337,7 @@ checksum = "10e5552827c33d8326502682da73a0ba4bfa40c1b55b216af3c303f32169dd89" dependencies = [ "async-trait", "bytes 1.0.1", - "futures 0.3.12", + "futures 0.3.13", "libp2p-core", "libp2p-swarm", "log", @@ -3355,7 +3356,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7955b973e1fd2bd61ffd43ce261c1223f61f4aacd5bae362a924993f9a25fd98" dependencies = [ "either", - "futures 0.3.12", + "futures 0.3.13", "libp2p-core", "log", "rand 0.7.3", @@ -3381,7 +3382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88a5aef80e519a6cb8e2663605142f97baaaea1a252eecbf8756184765f7471b" dependencies = [ "async-io", - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "if-watch", "ipnet", @@ -3398,7 +3399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80ac51ce419f60be966e02103c17f67ff5dc4422ba83ba54d251d6c62a4ed487" dependencies = [ "async-std", - "futures 0.3.12", + "futures 0.3.13", "libp2p-core", "log", ] @@ -3409,7 +3410,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6149c46cb76935c80bc8be6ec6e3ebd5f5e1679765a255fb34331d54610f15dd" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "js-sys", "libp2p-core", "parity-send-wrapper", @@ -3424,14 +3425,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3b1c6a3431045da8b925ed83384e4c5163e14b990572307fca9c507435d4d22" dependencies = [ "either", - "futures 0.3.12", + "futures 0.3.13", "futures-rustls", "libp2p-core", "log", "quicksink", "rw-stream-sink", "soketto", - "url 2.2.0", + "url 2.2.1", "webpki-roots", ] @@ -3441,7 +3442,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4819358c542a86ff95f6ae691efb4b94ddaf477079b01a686f5705b79bfc232a" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "libp2p-core", "parking_lot 0.11.1", "thiserror", @@ -3630,9 +3631,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e73be3b7d04a0123e933fea1d50d126cc7196bbc0362c0ce426694f777194eee" +checksum = "04e3e85b970d650e2ae6d70592474087051c11c54da7f7b4949725c5735fbcc6" dependencies = [ "libc", ] @@ -3686,18 +3687,18 @@ dependencies = [ [[package]] name = "minicbor" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3265a9f5210bb726f81ef9c456ae0aff5321cd95748c0e71889b0e19d8f0332b" +checksum = "1c2b2c73f9640fccab53947e2b3474d5071fcbc8f82cac51ddf6c8041a30a9ea" dependencies = [ "minicbor-derive", ] [[package]] name = "minicbor-derive" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "130b9455e28a3f308f6579671816a6f2621e2e0cbf55dc2f886345bef699481e" +checksum = "19ce18b5423c573a13e80cb3046ea0af6379ef725dc3af4886bdb8f4e5093068" dependencies = [ "proc-macro2", "quote", @@ -3706,9 +3707,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", "autocfg", @@ -3846,16 +3847,16 @@ checksum = "1255076139a83bb467426e7f8d0134968a8118844faa755985e077cf31850333" [[package]] name = "multistream-select" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10ddc0eb0117736f19d556355464fc87efc8ad98b29e3fd84f02531eb6e90840" +checksum = "7d91ec0a2440aaff5f78ec35631a7027d50386c6163aa975f7caa0d5da4b6ff8" dependencies = [ "bytes 1.0.1", - "futures 0.3.12", + "futures 0.3.13", "log", - "pin-project 1.0.4", + "pin-project 1.0.5", "smallvec 1.6.1", - "unsigned-varint 0.6.0", + "unsigned-varint 0.7.0", ] [[package]] @@ -3865,7 +3866,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6b6147c3d50b4f3cdabfe2ecc94a0191fd3d6ad58aefd9664cf396285883486" dependencies = [ "approx", - "generic-array 0.13.2", + "generic-array 0.13.3", "matrixmultiply", "num-complex", "num-rational", @@ -3924,7 +3925,7 @@ version = "0.8.0" dependencies = [ "derive_more", "fs_extra", - "futures 0.3.12", + "futures 0.3.13", "hash-db", "hex", "kvdb", @@ -3960,7 +3961,7 @@ dependencies = [ name = "node-browser-testing" version = "2.0.0" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "jsonrpc-core", "libp2p", @@ -3982,7 +3983,7 @@ dependencies = [ "frame-benchmarking-cli", "frame-support", "frame-system", - "futures 0.3.12", + "futures 0.3.13", "hex-literal", "log", "nix", @@ -4152,8 +4153,8 @@ dependencies = [ name = "node-rpc-client" version = "2.0.0" dependencies = [ - "futures 0.1.30", - "hyper 0.12.35", + "futures 0.1.31", + "hyper 0.12.36", "jsonrpc-core-client", "log", "node-primitives", @@ -4318,7 +4319,7 @@ dependencies = [ "frame-support", "frame-system", "fs_extra", - "futures 0.3.12", + "futures 0.3.13", "log", "node-executor", "node-primitives", @@ -4457,9 +4458,9 @@ checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" [[package]] name = "once_cell" -version = "1.5.2" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" dependencies = [ "parking_lot 0.11.1", ] @@ -4597,8 +4598,8 @@ dependencies = [ name = "pallet-babe" version = "3.0.0" dependencies = [ - "election-provider-support", "frame-benchmarking", + "frame-election-provider-support", "frame-support", "frame-system", "log", @@ -4781,8 +4782,8 @@ dependencies = [ name = "pallet-election-provider-multi-phase" version = "3.0.0" dependencies = [ - "election-provider-support", "frame-benchmarking", + "frame-election-provider-support", "frame-support", "frame-system", "hex-literal", @@ -4909,9 +4910,9 @@ dependencies = [ name = "pallet-grandpa" version = "3.0.0" dependencies = [ - "election-provider-support", "finality-grandpa", "frame-benchmarking", + "frame-election-provider-support", "frame-support", "frame-system", "log", @@ -5024,7 +5025,7 @@ name = "pallet-mmr" version = "3.0.0" dependencies = [ "ckb-merkle-mountain-range", - "env_logger 0.8.2", + "env_logger 0.8.3", "frame-benchmarking", "frame-support", "frame-system", @@ -5121,8 +5122,8 @@ dependencies = [ name = "pallet-offences-benchmarking" version = "3.0.0" dependencies = [ - "election-provider-support", "frame-benchmarking", + "frame-election-provider-support", "frame-support", "frame-system", "pallet-babe", @@ -5248,8 +5249,8 @@ dependencies = [ name = "pallet-session-benchmarking" version = "3.0.0" dependencies = [ - "election-provider-support", "frame-benchmarking", + "frame-election-provider-support", "frame-support", "frame-system", "pallet-balances", @@ -5287,8 +5288,8 @@ dependencies = [ name = "pallet-staking" version = "3.0.0" dependencies = [ - "election-provider-support", "frame-benchmarking", + "frame-election-provider-support", "frame-support", "frame-system", "hex", @@ -5319,7 +5320,7 @@ dependencies = [ name = "pallet-staking-fuzz" version = "0.0.0" dependencies = [ - "election-provider-support", + "frame-election-provider-support", "frame-support", "frame-system", "honggfuzz", @@ -5513,12 +5514,13 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "111e193c96758d476d272093a853882668da17489f76bf4361b8decae0b6c515" +checksum = "495197c078e54b8735181aa35c00a327f7f3a3cc00a1ee8c95926dd010f0ec6b" dependencies = [ "blake2-rfc", "crc32fast", + "fs2", "hex", "libc", "log", @@ -5542,14 +5544,14 @@ dependencies = [ "serde", "static_assertions", "unsigned-varint 0.7.0", - "url 2.2.0", + "url 2.2.1", ] [[package]] name = "parity-scale-codec" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c823fdae1bb5ff5708ee61a62697e6296175dc671710876871c853f48592b3" +checksum = "0cd3dab59b5cf4bc81069ade0fc470341a1ef3ad5fa73e5a8943bed2ec12b2e8" dependencies = [ "arrayvec 0.5.2", "bitvec", @@ -5560,9 +5562,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9029e65297c7fd6d7013f0579e193ec2b34ae78eabca854c9417504ad8a2d214" +checksum = "fa04976a81fde04924b40cc4036c4d12841e8bb04325a5cf2ada75731a150a7d" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -5583,7 +5585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e57fea504fea33f9fbb5f49f378359030e7e026a6ab849bb9e8f0787376f1bf" dependencies = [ "bytes 0.4.12", - "futures 0.1.30", + "futures 0.1.31", "libc", "log", "mio-named-pipes", @@ -5652,7 +5654,7 @@ dependencies = [ "rand 0.7.3", "sha-1 0.8.2", "slab", - "url 2.2.0", + "url 2.2.1", ] [[package]] @@ -5690,7 +5692,7 @@ checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ "instant", "lock_api 0.4.2", - "parking_lot_core 0.8.2", + "parking_lot_core 0.8.3", ] [[package]] @@ -5724,14 +5726,14 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall 0.1.57", + "redox_syscall 0.2.5", "smallvec 1.6.1", "winapi 0.3.9", ] @@ -5868,11 +5870,11 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b70b68509f17aa2857863b6fa00bf21fc93674c7a8893de2f469f6aa7ca2f2" +checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" dependencies = [ - "pin-project-internal 1.0.4", + "pin-project-internal 1.0.5", ] [[package]] @@ -5888,9 +5890,9 @@ dependencies = [ [[package]] name = "pin-project-internal" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caa25a6393f22ce819b0f50e0be89287292fda8d425be38ee0ca14c4931d9e71" +checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" dependencies = [ "proc-macro2", "quote", @@ -5905,9 +5907,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cf491442e4b033ed1c722cb9f0df5fcfcf4de682466c46469c36bc47dc5548a" +checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" [[package]] name = "pin-utils" @@ -6213,7 +6215,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" dependencies = [ - "env_logger 0.8.2", + "env_logger 0.8.3", "log", "rand 0.8.3", ] @@ -6231,9 +6233,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ "proc-macro2", ] @@ -6419,7 +6421,7 @@ checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" dependencies = [ "crossbeam-channel", "crossbeam-deque 0.8.0", - "crossbeam-utils 0.8.1", + "crossbeam-utils 0.8.3", "lazy_static", "num_cpus", ] @@ -6441,9 +6443,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_syscall" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" dependencies = [ "bitflags", ] @@ -6466,7 +6468,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom 0.2.2", - "redox_syscall 0.2.4", + "redox_syscall 0.2.5", ] [[package]] @@ -6545,7 +6547,7 @@ name = "remote-externalities" version = "0.9.0" dependencies = [ "async-std", - "env_logger 0.8.2", + "env_logger 0.8.3", "hex-literal", "jsonrpsee-http-client", "jsonrpsee-proc-macros", @@ -6573,9 +6575,9 @@ checksum = "53552c6c49e1e13f1a203ef0080ab3bbef0beb570a528993e83df057a9d9bba1" [[package]] name = "ring" -version = "0.16.19" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", "libc", @@ -6615,7 +6617,7 @@ dependencies = [ "base64 0.13.0", "blake2b_simd", "constant_time_eq", - "crossbeam-utils 0.8.1", + "crossbeam-utils 0.8.3", ] [[package]] @@ -6695,7 +6697,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4da5fcb054c46f5a5dff833b129285a93d3f0179531735e6c866e8cc307d2020" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "pin-project 0.4.27", "static_assertions", ] @@ -6740,7 +6742,7 @@ dependencies = [ "async-trait", "derive_more", "either", - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "libp2p", "log", @@ -6768,7 +6770,7 @@ dependencies = [ name = "sc-basic-authorship" version = "0.9.0" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "log", "parity-scale-codec", @@ -6842,7 +6844,7 @@ version = "0.9.0" dependencies = [ "chrono", "fdlimit", - "futures 0.3.12", + "futures 0.3.13", "hex", "libp2p", "log", @@ -6880,7 +6882,7 @@ version = "3.0.0" dependencies = [ "derive_more", "fnv", - "futures 0.3.12", + "futures 0.3.13", "hash-db", "kvdb", "kvdb-memorydb", @@ -6960,7 +6962,7 @@ name = "sc-consensus-aura" version = "0.9.0" dependencies = [ "derive_more", - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "getrandom 0.2.2", "log", @@ -7002,7 +7004,7 @@ version = "0.9.0" dependencies = [ "derive_more", "fork-tree", - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "log", "merlin", @@ -7056,7 +7058,7 @@ name = "sc-consensus-babe-rpc" version = "0.9.0" dependencies = [ "derive_more", - "futures 0.3.12", + "futures 0.3.13", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -7098,7 +7100,7 @@ version = "0.9.0" dependencies = [ "assert_matches", "derive_more", - "futures 0.3.12", + "futures 0.3.13", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -7135,7 +7137,7 @@ name = "sc-consensus-pow" version = "0.9.0" dependencies = [ "derive_more", - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "log", "parity-scale-codec", @@ -7157,7 +7159,7 @@ dependencies = [ name = "sc-consensus-slots" version = "0.9.0" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "log", "parity-scale-codec", @@ -7288,13 +7290,13 @@ dependencies = [ "dyn-clone", "finality-grandpa", "fork-tree", - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "linked-hash-map", "log", "parity-scale-codec", "parking_lot 0.11.1", - "pin-project 1.0.4", + "pin-project 1.0.5", "rand 0.7.3", "sc-block-builder", "sc-client-api", @@ -7332,7 +7334,7 @@ version = "0.9.0" dependencies = [ "derive_more", "finality-grandpa", - "futures 0.3.12", + "futures 0.3.13", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -7362,7 +7364,7 @@ version = "0.9.0" dependencies = [ "derive_more", "finality-grandpa", - "futures 0.3.12", + "futures 0.3.13", "log", "num-traits", "parity-scale-codec", @@ -7387,7 +7389,7 @@ name = "sc-informant" version = "0.9.0" dependencies = [ "ansi_term 0.12.1", - "futures 0.3.12", + "futures 0.3.13", "log", "parity-util-mem", "sc-client-api", @@ -7405,7 +7407,7 @@ version = "3.0.0" dependencies = [ "async-trait", "derive_more", - "futures 0.3.12", + "futures 0.3.13", "futures-util", "hex", "merlin", @@ -7454,7 +7456,7 @@ dependencies = [ "erased-serde", "fnv", "fork-tree", - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "hex", "ip_network", @@ -7466,7 +7468,7 @@ dependencies = [ "nohash-hasher", "parity-scale-codec", "parking_lot 0.11.1", - "pin-project 1.0.4", + "pin-project 1.0.5", "prost", "prost-build", "quickcheck", @@ -7502,7 +7504,7 @@ name = "sc-network-gossip" version = "0.9.0" dependencies = [ "async-std", - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "libp2p", "log", @@ -7521,7 +7523,7 @@ name = "sc-network-test" version = "0.8.0" dependencies = [ "async-std", - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "libp2p", "log", @@ -7549,9 +7551,9 @@ version = "3.0.0" dependencies = [ "bytes 0.5.6", "fnv", - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", - "hyper 0.13.9", + "hyper 0.13.10", "hyper-rustls", "lazy_static", "log", @@ -7582,7 +7584,7 @@ dependencies = [ name = "sc-peerset" version = "3.0.0" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "libp2p", "log", "rand 0.7.3", @@ -7604,8 +7606,8 @@ name = "sc-rpc" version = "3.0.0" dependencies = [ "assert_matches", - "futures 0.1.30", - "futures 0.3.12", + "futures 0.1.31", + "futures 0.3.13", "hash-db", "jsonrpc-core", "jsonrpc-pubsub", @@ -7646,7 +7648,7 @@ name = "sc-rpc-api" version = "0.9.0" dependencies = [ "derive_more", - "futures 0.3.12", + "futures 0.3.13", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -7668,7 +7670,7 @@ dependencies = [ name = "sc-rpc-server" version = "3.0.0" dependencies = [ - "futures 0.1.30", + "futures 0.1.31", "jsonrpc-core", "jsonrpc-http-server", "jsonrpc-ipc-server", @@ -7702,8 +7704,8 @@ dependencies = [ "async-std", "directories", "exit-future", - "futures 0.1.30", - "futures 0.3.12", + "futures 0.1.31", + "futures 0.3.13", "futures-timer 3.0.2", "hash-db", "jsonrpc-core", @@ -7713,7 +7715,7 @@ dependencies = [ "parity-scale-codec", "parity-util-mem", "parking_lot 0.11.1", - "pin-project 1.0.4", + "pin-project 1.0.5", "rand 0.7.3", "sc-block-builder", "sc-chain-spec", @@ -7771,8 +7773,8 @@ name = "sc-service-test" version = "2.0.0" dependencies = [ "fdlimit", - "futures 0.1.30", - "futures 0.3.12", + "futures 0.1.31", + "futures 0.3.13", "hex-literal", "log", "parity-scale-codec", @@ -7840,11 +7842,11 @@ name = "sc-telemetry" version = "3.0.0" dependencies = [ "chrono", - "futures 0.3.12", + "futures 0.3.13", "libp2p", "log", "parking_lot 0.11.1", - "pin-project 1.0.4", + "pin-project 1.0.5", "rand 0.7.3", "serde", "serde_json", @@ -7900,7 +7902,7 @@ dependencies = [ "assert_matches", "criterion", "derive_more", - "futures 0.3.12", + "futures 0.3.13", "linked-hash-map", "log", "parity-scale-codec", @@ -7923,7 +7925,7 @@ name = "sc-transaction-pool" version = "3.0.0" dependencies = [ "assert_matches", - "futures 0.3.12", + "futures 0.3.13", "futures-diagnose", "hex", "intervalier", @@ -8110,9 +8112,9 @@ checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7" [[package]] name = "serde" -version = "1.0.123" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" dependencies = [ "serde_derive", ] @@ -8129,9 +8131,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.123" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" dependencies = [ "proc-macro2", "quote", @@ -8140,9 +8142,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" dependencies = [ "itoa", "ryu", @@ -8163,9 +8165,9 @@ dependencies = [ [[package]] name = "sha-1" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c" +checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f" dependencies = [ "block-buffer 0.9.0", "cfg-if 1.0.0", @@ -8228,9 +8230,9 @@ checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] name = "signal-hook" -version = "0.1.17" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" +checksum = "8a7f3f92a1da3d6b1d32245d0cbcbbab0cfc45996d8df619c42bccfa6d2bbb5f" dependencies = [ "libc", "signal-hook-registry", @@ -8322,11 +8324,11 @@ dependencies = [ "base64 0.12.3", "bytes 0.5.6", "flate2", - "futures 0.3.12", + "futures 0.3.13", "httparse", "log", "rand 0.7.3", - "sha-1 0.9.2", + "sha-1 0.9.4", ] [[package]] @@ -8475,7 +8477,7 @@ dependencies = [ name = "sp-blockchain" version = "3.0.0" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "log", "lru", "parity-scale-codec", @@ -8500,7 +8502,7 @@ dependencies = [ name = "sp-consensus" version = "0.9.0" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "futures-timer 3.0.2", "libp2p", "log", @@ -8596,7 +8598,7 @@ dependencies = [ "criterion", "dyn-clonable", "ed25519-dalek", - "futures 0.3.12", + "futures 0.3.13", "hash-db", "hash256-std-hasher", "hex", @@ -8693,7 +8695,7 @@ dependencies = [ name = "sp-io" version = "3.0.0" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "hash-db", "libsecp256k1", "log", @@ -8728,7 +8730,7 @@ version = "0.9.0" dependencies = [ "async-trait", "derive_more", - "futures 0.3.12", + "futures 0.3.13", "merlin", "parity-scale-codec", "parking_lot 0.11.1", @@ -9037,7 +9039,7 @@ name = "sp-transaction-pool" version = "3.0.0" dependencies = [ "derive_more", - "futures 0.3.12", + "futures 0.3.13", "log", "parity-scale-codec", "serde", @@ -9069,7 +9071,7 @@ dependencies = [ name = "sp-utils" version = "3.0.0" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "futures-core", "futures-timer 3.0.2", "lazy_static", @@ -9221,8 +9223,8 @@ version = "0.9.0" dependencies = [ "chrono", "console_error_panic_hook", - "futures 0.1.30", - "futures 0.3.12", + "futures 0.1.31", + "futures 0.3.13", "futures-timer 3.0.2", "getrandom 0.2.2", "js-sys", @@ -9265,7 +9267,7 @@ version = "3.0.0" dependencies = [ "frame-support", "frame-system", - "futures 0.3.12", + "futures 0.3.13", "jsonrpc-client-transports", "jsonrpc-core", "parity-scale-codec", @@ -9280,7 +9282,7 @@ name = "substrate-frame-rpc-system" version = "3.0.0" dependencies = [ "frame-system-rpc-runtime-api", - "futures 0.3.12", + "futures 0.3.13", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -9307,7 +9309,7 @@ dependencies = [ "async-std", "derive_more", "futures-util", - "hyper 0.13.9", + "hyper 0.13.10", "log", "prometheus", "tokio 0.2.25", @@ -9317,8 +9319,8 @@ dependencies = [ name = "substrate-test-client" version = "2.0.1" dependencies = [ - "futures 0.1.30", - "futures 0.3.12", + "futures 0.1.31", + "futures 0.3.13", "hash-db", "hex", "parity-scale-codec", @@ -9386,7 +9388,7 @@ dependencies = [ name = "substrate-test-runtime-client" version = "2.0.0" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "parity-scale-codec", "sc-block-builder", "sc-client-api", @@ -9407,7 +9409,7 @@ name = "substrate-test-runtime-transaction-pool" version = "2.0.0" dependencies = [ "derive_more", - "futures 0.3.12", + "futures 0.3.13", "parity-scale-codec", "parking_lot 0.11.1", "sc-transaction-graph", @@ -9421,7 +9423,7 @@ dependencies = [ name = "substrate-test-utils" version = "3.0.0" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "sc-service", "substrate-test-utils-derive", "tokio 0.2.25", @@ -9474,9 +9476,9 @@ checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" [[package]] name = "syn" -version = "1.0.60" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512" dependencies = [ "proc-macro2", "quote", @@ -9503,15 +9505,15 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "tap" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36474e732d1affd3a6ed582781b3683df3d0563714c59c39591e8ff707cf078e" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee5a98e506fb7231a304c3a1bd7c132a55016cf65001e0282480665870dfcb9" +checksum = "422045212ea98508ae3d28025bc5aaa2bd4a9cdaecd442a08da2ee620ee9ea95" [[package]] name = "tempfile" @@ -9522,7 +9524,7 @@ dependencies = [ "cfg-if 1.0.0", "libc", "rand 0.8.3", - "redox_syscall 0.2.4", + "redox_syscall 0.2.5", "remove_dir_all", "winapi 0.3.9", ] @@ -9547,18 +9549,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" dependencies = [ "proc-macro2", "quote", @@ -9623,9 +9625,9 @@ dependencies = [ [[package]] name = "tinytemplate" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ada8616fad06a2d0c455adc530de4ef57605a8120cc65da9653e0e9623ca74" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", @@ -9653,7 +9655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" dependencies = [ "bytes 0.4.12", - "futures 0.1.30", + "futures 0.1.31", "mio", "num_cpus", "tokio-codec", @@ -9701,7 +9703,7 @@ checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" dependencies = [ "bytes 0.4.12", "either", - "futures 0.1.30", + "futures 0.1.31", ] [[package]] @@ -9711,7 +9713,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" dependencies = [ "bytes 0.4.12", - "futures 0.1.30", + "futures 0.1.31", "tokio-io", ] @@ -9721,7 +9723,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" dependencies = [ - "futures 0.1.30", + "futures 0.1.31", "tokio-executor", ] @@ -9732,7 +9734,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ "crossbeam-utils 0.7.2", - "futures 0.1.30", + "futures 0.1.31", ] [[package]] @@ -9741,7 +9743,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" dependencies = [ - "futures 0.1.30", + "futures 0.1.31", "tokio-io", "tokio-threadpool", ] @@ -9753,7 +9755,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ "bytes 0.4.12", - "futures 0.1.30", + "futures 0.1.31", "log", ] @@ -9775,7 +9777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d282d483052288b2308ba5ee795f5673b159c9bdf63c385a05609da782a5eae" dependencies = [ "bytes 0.4.12", - "futures 0.1.30", + "futures 0.1.31", "mio", "mio-named-pipes", "tokio 0.1.22", @@ -9788,7 +9790,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ "crossbeam-utils 0.7.2", - "futures 0.1.30", + "futures 0.1.31", "lazy_static", "log", "mio", @@ -9818,7 +9820,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" dependencies = [ - "futures 0.1.30", + "futures 0.1.31", ] [[package]] @@ -9828,7 +9830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" dependencies = [ "fnv", - "futures 0.1.30", + "futures 0.1.31", ] [[package]] @@ -9838,7 +9840,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" dependencies = [ "bytes 0.4.12", - "futures 0.1.30", + "futures 0.1.31", "iovec", "mio", "tokio-io", @@ -9854,7 +9856,7 @@ dependencies = [ "crossbeam-deque 0.7.3", "crossbeam-queue", "crossbeam-utils 0.7.2", - "futures 0.1.30", + "futures 0.1.31", "lazy_static", "log", "num_cpus", @@ -9869,7 +9871,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ "crossbeam-utils 0.7.2", - "futures 0.1.30", + "futures 0.1.31", "slab", "tokio-executor", ] @@ -9881,7 +9883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" dependencies = [ "bytes 0.4.12", - "futures 0.1.30", + "futures 0.1.31", "log", "mio", "tokio-codec", @@ -9896,7 +9898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" dependencies = [ "bytes 0.4.12", - "futures 0.1.30", + "futures 0.1.31", "iovec", "libc", "log", @@ -9938,22 +9940,22 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.22" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" +checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" dependencies = [ "cfg-if 1.0.0", "log", - "pin-project-lite 0.2.5", + "pin-project-lite 0.2.6", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada" +checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07" dependencies = [ "proc-macro2", "quote", @@ -9971,19 +9973,19 @@ dependencies = [ [[package]] name = "tracing-futures" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" dependencies = [ - "pin-project 0.4.27", + "pin-project 1.0.5", "tracing", ] [[package]] name = "tracing-log" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e0f8c7178e13481ff6765bd169b33e8d554c5d2bbede5e32c356194be02b9b9" +checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" dependencies = [ "lazy_static", "log", @@ -10002,9 +10004,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1fa8f0c8f4c594e4fc9debc1990deab13238077271ba84dd853d54902ee3401" +checksum = "8ab8966ac3ca27126141f7999361cc97dd6fb4b71da04c02044fa9045d98bb96" dependencies = [ "ansi_term 0.12.1", "chrono", @@ -10105,9 +10107,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.39" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c9594b802f041389d2baac680663573dde3103bb4a4926d61d6aba689465978" +checksum = "99471a206425fba51842a9186315f32d91c56eadc21ea4c21f847b59cf778f8b" dependencies = [ "dissimilar", "glob", @@ -10173,9 +10175,9 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" dependencies = [ "tinyvec", ] @@ -10257,12 +10259,12 @@ dependencies = [ [[package]] name = "url" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b" dependencies = [ "form_urlencoded", - "idna 0.2.0", + "idna 0.2.2", "matches", "percent-encoding 2.1.0", ] @@ -10338,7 +10340,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" dependencies = [ - "futures 0.1.30", + "futures 0.1.31", "log", "try-lock", ] @@ -10394,9 +10396,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe9756085a84584ee9457a002b7cdfe0bfff169f45d2591d8be1345a6780e35" +checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -10435,9 +10437,9 @@ checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" [[package]] name = "wasm-bindgen-test" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0355fa0c1f9b792a09b6dcb6a8be24d51e71e6d74972f9eb4a44c4c004d24a25" +checksum = "f0d4da138503a4cf86801b94d95781ee3619faa8feca830569cc6b54997b8b5c" dependencies = [ "console_error_panic_hook", "js-sys", @@ -10449,9 +10451,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27e07b46b98024c2ba2f9e83a10c2ef0515f057f2da299c1762a2017de80438b" +checksum = "c3199c33f06500c731d5544664c24d0c2b742b98debc6b1c6f0c6d6e8fb7c19b" dependencies = [ "proc-macro2", "quote", @@ -10474,7 +10476,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "js-sys", "parking_lot 0.11.1", "pin-utils", @@ -10702,27 +10704,27 @@ dependencies = [ [[package]] name = "wast" -version = "32.0.0" +version = "35.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c24a3ee360d01d60ed0a0f960ab76a6acce64348cdb0bf8699c2a866fad57c7c" +checksum = "db5ae96da18bb5926341516fd409b5a8ce4e4714da7f0a1063d3b20ac9f9a1e1" dependencies = [ "leb128", ] [[package]] name = "wat" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e8f7f34773fa6318e8897283abf7941c1f250faae4e1a52f82df09c3bad7cce" +checksum = "0b0fa059022c5dabe129f02b429d67086400deb8277f89c975555dacc1dadbcc" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.46" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" +checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" dependencies = [ "js-sys", "wasm-bindgen", @@ -10851,7 +10853,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cc7bd8c983209ed5d527f44b01c41b7dc146fd960c61cf9e1d25399841dc271" dependencies = [ - "futures 0.3.12", + "futures 0.3.13", "log", "nohash-hasher", "parking_lot 0.11.1", diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index 85c6ec593a258..72865b33da6e7 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,8 +19,7 @@ use super::*; use crate::Module as MultiPhase; - -pub use frame_benchmarking::{account, benchmarks, whitelist_account, whitelisted_caller}; +use frame_benchmarking::impl_benchmark_test_suite; use frame_support::{assert_ok, traits::OnInitialize}; use frame_system::RawOrigin; use rand::{prelude::SliceRandom, rngs::SmallRng, SeedableRng}; @@ -55,7 +54,7 @@ fn solution_with_size( // first generates random targets. let targets: Vec = - (0..size.targets).map(|i| account("Targets", i, SEED)).collect(); + (0..size.targets).map(|i| frame_benchmarking::account("Targets", i, SEED)).collect(); let mut rng = SmallRng::seed_from_u64(999u64); @@ -75,7 +74,7 @@ fn solution_with_size( .choose_multiple(&mut rng, >::LIMIT) .cloned() .collect::>(); - let voter = account::("Voter", i, SEED); + let voter = frame_benchmarking::account::("Voter", i, SEED); (voter, stake, winner_votes) }) .collect::>(); @@ -89,7 +88,7 @@ fn solution_with_size( .choose_multiple(&mut rng, >::LIMIT) .cloned() .collect::>(); - let voter = account::("Voter", i, SEED); + let voter = frame_benchmarking::account::("Voter", i, SEED); (voter, stake, votes) }) .collect::>(); @@ -141,7 +140,7 @@ fn solution_with_size( RawSolution { compact, score, round } } -benchmarks! { +frame_benchmarking::benchmarks! { on_initialize_nothing { assert!(>::current_phase().is_off()); }: { @@ -185,6 +184,36 @@ benchmarks! { assert!(>::current_phase().is_unsigned()); } + // a call to `::elect` where we only return the queued solution. + elect_queued { + // assume largest values for the election status. These will merely affect the decoding. + let v = T::BenchmarkingConfig::VOTERS[1]; + let t = T::BenchmarkingConfig::TARGETS[1]; + let a = T::BenchmarkingConfig::ACTIVE_VOTERS[1]; + let d = T::BenchmarkingConfig::DESIRED_TARGETS[1]; + + let witness = SolutionOrSnapshotSize { voters: v, targets: t }; + let raw_solution = solution_with_size::(witness, a, d); + let ready_solution = + >::feasibility_check(raw_solution, ElectionCompute::Signed).unwrap(); + + // these are set by the `solution_with_size` function. + assert!(>::get().is_some()); + assert!(>::get().is_some()); + assert!(>::get().is_some()); + >::put(Phase::Signed); + // assume a queued solution is stored, regardless of where it comes from. + >::put(ready_solution); + }: { + let _ = as ElectionProvider>::elect(); + } verify { + assert!(>::queued_solution().is_none()); + assert!(>::get().is_none()); + assert!(>::get().is_none()); + assert!(>::get().is_none()); + assert_eq!(>::get(), >::Off); + } + #[extra] create_snapshot { assert!(>::snapshot().is_none()); @@ -248,35 +277,8 @@ benchmarks! { } } -#[cfg(test)] -mod test { - use super::*; - use crate::mock::*; - - #[test] - fn test_benchmarks() { - ExtBuilder::default().build_and_execute(|| { - assert_ok!(test_benchmark_feasibility_check::()); - }); - - ExtBuilder::default().build_and_execute(|| { - assert_ok!(test_benchmark_submit_unsigned::()); - }); - - ExtBuilder::default().build_and_execute(|| { - assert_ok!(test_benchmark_on_initialize_open_unsigned_with_snapshot::()); - }); - - ExtBuilder::default().build_and_execute(|| { - assert_ok!(test_benchmark_on_initialize_open_unsigned_without_snapshot::()); - }); - - ExtBuilder::default().build_and_execute(|| { - assert_ok!(test_benchmark_on_initialize_nothing::()); - }); - - ExtBuilder::default().build_and_execute(|| { - assert_ok!(test_benchmark_create_snapshot::()); - }); - } -} +impl_benchmark_test_suite!( + MultiPhase, + crate::mock::ExtBuilder::default().build(), + crate::mock::Runtime, +); diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 1f325225893ba..0c732b14be137 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -202,6 +202,10 @@ //! dependency from staking and the compact solution type. It should be generated at runtime, there //! it should be encoded how many votes each nominators have. Essentially translate //! to this pallet. +//! +//! **More accurate weight for error cases**: Both `ElectionDataProvider` and `ElectionProvider` +//! assume no weight is consumed in their functions, when operations fail with `Err`. This can +//! clearly be improved. #![cfg_attr(not(feature = "std"), no_std)] @@ -1067,7 +1071,11 @@ impl Pallet { .map_err(Into::into), FallbackStrategy::Nothing => Err(ElectionError::NoFallbackConfigured), }, - |ReadySolution { supports, compute, .. }| Ok((supports, 0, compute)), + |ReadySolution { supports, compute, .. }| Ok(( + supports, + T::WeightInfo::elect_queued(), + compute + )), ) .map(|(supports, weight, compute)| { Self::deposit_event(Event::ElectionFinalized(Some(compute))); diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 4c492ce89592c..5468d80f7e943 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -241,6 +241,13 @@ impl multi_phase::weights::WeightInfo for DualMockWeightInfo { <() as multi_phase::weights::WeightInfo>::on_initialize_open_unsigned_without_snapshot() } } + fn elect_queued() -> Weight { + if MockWeightInfo::get() { + Zero::zero() + } else { + <() as multi_phase::weights::WeightInfo>::elect_queued() + } + } fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight { if MockWeightInfo::get() { // 10 base diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index 6c0ffcbea1579..55b3f6b81ce95 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -413,6 +413,9 @@ mod max_weight { fn on_initialize_open_unsigned_with_snapshot() -> Weight { unreachable!() } + fn elect_queued() -> Weight { + 0 + } fn on_initialize_open_unsigned_without_snapshot() -> Weight { unreachable!() } diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs index 276bba330d24c..ae358786c75a7 100644 --- a/frame/election-provider-multi-phase/src/weights.rs +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -35,7 +35,6 @@ // --output=./frame/election-provider-multi-phase/src/weights.rs // --template=./.maintain/frame-weight-template.hbs - #![allow(unused_parens)] #![allow(unused_imports)] @@ -48,8 +47,9 @@ pub trait WeightInfo { fn on_initialize_open_signed() -> Weight; fn on_initialize_open_unsigned_with_snapshot() -> Weight; fn on_initialize_open_unsigned_without_snapshot() -> Weight; - fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight; - fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight; + fn elect_queued() -> Weight; + fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight; + fn feasibility_check(v: u32, t: u32, a: u32, d: u32) -> Weight; } /// Weights for pallet_election_provider_multi_phase using the Substrate node and recommended hardware. @@ -74,7 +74,10 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight { + fn elect_queued() -> Weight { + 0 + } + fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight { (0 as Weight) // Standard Error: 23_000 .saturating_add((4_171_000 as Weight).saturating_mul(v as Weight)) @@ -122,7 +125,10 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight { + fn elect_queued() -> Weight { + 0 + } + fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight { (0 as Weight) // Standard Error: 23_000 .saturating_add((4_171_000 as Weight).saturating_mul(v as Weight)) diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index 4a393aaf79091..ccc448e5c78d5 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -252,7 +252,7 @@ pub trait ElectionProvider { /// Elect a new set of winners. /// - /// The result is returned in a target major format, namely as vector of supports. + /// The result is returned in a target major format, namely as vector of supports. fn elect() -> Result<(Supports, Weight), Self::Error>; } From 47a8ab3ce09bfcfc8b31eb763132053d7c6b3845 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Tue, 9 Mar 2021 09:06:08 +0000 Subject: [PATCH 45/57] Update frame/election-provider-multi-phase/src/mock.rs Co-authored-by: Peter Goodspeed-Niklaus --- frame/election-provider-multi-phase/src/mock.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 5468d80f7e943..bbf1e5ebc6ed3 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -301,11 +301,13 @@ pub struct ExtBuilder {} pub struct StakingMock; impl ElectionDataProvider for StakingMock { fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Weight)> { - if maybe_max_len.map_or(false, |max_len| Targets::get().len() > max_len) { + let targets = Targets::get(); + + if maybe_max_len.map_or(false, |max_len| targets.len() > max_len) { return Err("Targets too big"); } - Ok((Targets::get(), 0)) + Ok((targets, 0)) } fn voters( From 1a85cc98f8c8bc385e9dffb2114a59bb76a27cc7 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 9 Mar 2021 10:07:07 +0100 Subject: [PATCH 46/57] nit --- frame/election-provider-multi-phase/src/mock.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 5468d80f7e943..d473dadef9bc0 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -311,11 +311,12 @@ impl ElectionDataProvider for StakingMock { fn voters( maybe_max_len: Option, ) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Weight)> { - if maybe_max_len.map_or(false, |max_len| Voters::get().len() > max_len) { + let voters = Voters::get(); + if maybe_max_len.map_or(false, |max_len| voters.len() > max_len) { return Err("Voters too big"); } - Ok((Voters::get(), 0)) + Ok((voters, 0)) } fn desired_targets() -> data_provider::Result<(u32, Weight)> { Ok((DesiredTargets::get(), 0)) From 0d76b7a38e18d87ce4050e9417ccbe7c0200dc46 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 9 Mar 2021 10:07:54 +0100 Subject: [PATCH 47/57] better doc --- frame/election-provider-multi-phase/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 0c732b14be137..e8f6eeb769a04 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -574,7 +574,7 @@ pub mod pallet { match current_phase { Phase::Off if remaining <= signed_deadline && remaining > unsigned_deadline => { - // NOTE: if signed-phase length is zero, second part will fail. + // NOTE: if signed-phase length is zero, second part of the if-condition fails. match Self::on_initialize_open_signed() { Ok(snap_weight) => { log!(info, "Starting signed phase round {}.", Self::round()); From 91439818c5d1692ad3d11402bc683ef0fbf1fcb4 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 9 Mar 2021 10:14:52 +0100 Subject: [PATCH 48/57] Line width --- frame/election-provider-support/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index ccc448e5c78d5..222ee49394da9 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -103,7 +103,9 @@ //! fn desired_targets() -> data_provider::Result<(u32, Weight)> { //! Ok((1, 0)) //! } -//! fn voters(maybe_max_len: Option) -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Weight)> { +//! fn voters(maybe_max_len: Option) +//! -> data_provider::Result<(Vec<(AccountId, VoteWeight, Vec)>, Weight)> +//! { //! Ok((Default::default(), 0)) //! } //! fn targets(maybe_max_len: Option) -> data_provider::Result<(Vec, Weight)> { From 30af70efbc0db83a418ddb7e09989b0b49f56cb1 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 9 Mar 2021 15:00:29 +0100 Subject: [PATCH 49/57] Fix build --- frame/election-provider-support/src/lib.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index 222ee49394da9..fe09e24f696f8 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -128,15 +128,14 @@ //! } //! //! impl ElectionProvider for GenericElectionProvider { -//! type Error = (); +//! type Error = &'static str; //! type DataProvider = T::DataProvider; //! -//! fn elect() -> Result, Self::Error> { +//! fn elect() -> Result<(Supports, Weight), Self::Error> { //! Self::DataProvider::targets(None) -//! .map_err(|_| ()) -//! .and_then(|(t, _)| { -//! t.first().map(|winner| vec![(*winner, Support::default())]) -//! .ok_or(()) +//! .map_err(|_| "failed to elect") +//! .map(|(t, weight)| { +//! (vec![(t[0], Support::default())], weight) //! }) //! } //! } From 4ea63714ad6367af3161881a4325931220c01875 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sun, 14 Mar 2021 11:45:45 +0100 Subject: [PATCH 50/57] Self-review --- Cargo.lock | 1 + frame/election-provider-multi-phase/src/lib.rs | 11 +++++------ frame/election-provider-multi-phase/src/unsigned.rs | 11 ++++++++++- frame/staking/src/lib.rs | 6 +++--- frame/staking/src/tests.rs | 1 + frame/system/src/offchain.rs | 11 +++++------ 6 files changed, 25 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58eb0fc69b251..536a26ac20614 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7585,6 +7585,7 @@ dependencies = [ "fnv", "futures 0.3.13", "futures-timer 3.0.2", + "hex", "hyper 0.13.10", "hyper-rustls", "lazy_static", diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index e8f6eeb769a04..62d65dca37c4b 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -205,7 +205,8 @@ //! //! **More accurate weight for error cases**: Both `ElectionDataProvider` and `ElectionProvider` //! assume no weight is consumed in their functions, when operations fail with `Err`. This can -//! clearly be improved. +//! clearly be improved, but not a priority as we generally expect snapshot creation to fail only +//! due to extreme circumstances. #![cfg_attr(not(feature = "std"), no_std)] @@ -535,8 +536,7 @@ pub mod pallet { /// this values, based on [`WeightInfo::submit_unsigned`]. type MinerMaxWeight: Get; - /// Something that will provide the election data. This pallet constrain the data provider - /// to return its weight as additional data for accurate metering. + /// Something that will provide the election data. type DataProvider: ElectionDataProvider; /// The compact solution type @@ -592,11 +592,10 @@ pub mod pallet { Phase::Signed | Phase::Off if remaining <= unsigned_deadline && remaining > Zero::zero() => { - // followed by signed or not? + // determine if followed by signed or not. let (need_snapshot, enabled, signed_weight) = if current_phase == Phase::Signed { // followed by a signed phase: close the signed phase, no need for snapshot. - // TWO_PHASE_NOTE: later on once we have signed phase, this should return - // something else. + // TODO: proper weight https://github.com/paritytech/substrate/pull/7910. (false, true, Weight::zero()) } else { // no signed phase: create a new snapshot, definitely `enable` the unsigned diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index 55b3f6b81ce95..3004e69c23c8e 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -66,8 +66,17 @@ impl Pallet { let iters = Self::get_balancing_iters(); // 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 score = raw_solution.score.clone(); + + let call: >>::OverarchingCall = + Call::submit_unsigned(raw_solution, witness).into(); + log!( + info, + "mined a solution with score {:?} and size {}", + score, + call.using_encoded(|b| b.len()) + ); - let call = Call::submit_unsigned(raw_solution, witness).into(); SubmitTransaction::>::submit_unsigned_transaction(call) .map_err(|_| MinerError::PoolSubmissionFailed) } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index fe38602a6794b..e3064122a8914 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -3353,7 +3353,7 @@ impl frame_election_provider_support::ElectionDataProvider data_provider::Result<(Vec<(T::AccountId, VoteWeight, Vec)>, Weight)> { // NOTE: reading these counts already needs to iterate a lot of storage keys, but they get // cached. This is okay for the case of `Ok(_)`, but bad for `Err(_)`, as the trait does not - // report weight in failures. TODO: https://github.com/paritytech/substrate/issues/8246 + // report weight in failures. let nominator_count = >::iter().count(); let validator_count = >::iter().count(); let voter_count = nominator_count.saturating_add(validator_count); @@ -3362,11 +3362,11 @@ impl frame_election_provider_support::ElectionDataProvider>::iter().count(); + let slashing_span_count = >::iter().count(); let weight = T::WeightInfo::get_npos_voters( nominator_count as u32, validator_count as u32, - slashing_spans as u32, + slashing_span_count as u32, ); Ok((Self::get_npos_voters(), weight)) } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 15485447b9c9b..0008f8cdba6dc 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -5083,6 +5083,7 @@ mod election_data_provider { fn respects_len_limits() { ExtBuilder::default().build().execute_with(|| { assert_eq!(Staking::voters(Some(1)).unwrap_err(), "Voter snapshot too big"); + assert_eq!(Staking::targets(Some(1)).unwrap_err(), "Target snapshot too big"); }); } diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index aa8bce966192e..fe601f995ce51 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -38,10 +38,10 @@ //! //! To be able to use signing, the following trait should be implemented: //! -//! - [`AppCrypto`](./trait.AppCrypto.html): where an application-specific key -//! is defined and can be used by this module's helpers for signing. -//! - [`CreateSignedTransaction`](./trait.CreateSignedTransaction.html): where -//! the manner in which the transaction is constructed is defined. +//! - [`AppCrypto`](./trait.AppCrypto.html): where an application-specific key is defined and can be +//! used by this module's helpers for signing. +//! - [`CreateSignedTransaction`](./trait.CreateSignedTransaction.html): where the manner in which +//! the transaction is constructed is defined. //! //! #### Submit an unsigned transaction with a signed payload //! @@ -53,7 +53,6 @@ //! #### Submit a signed transaction //! //! [`Signer`](./struct.Signer.html) can be used to sign/verify payloads -//! #![warn(missing_docs)] @@ -473,7 +472,7 @@ pub trait SendTransactionTypes { /// The runtime's call type. /// /// This has additional bound to be able to be created from pallet-local `Call` types. - type OverarchingCall: From; + type OverarchingCall: From + codec::Encode; } /// Create signed transaction. From d3d595785eefddd10a5160c1856e3322e34e5b63 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sun, 14 Mar 2021 13:48:54 +0100 Subject: [PATCH 51/57] Self-review --- .../src/benchmarking.rs | 11 ++++--- .../election-provider-multi-phase/src/lib.rs | 5 ++- .../election-provider-multi-phase/src/mock.rs | 10 ++++++ frame/election-provider-support/src/lib.rs | 3 +- frame/staking/src/lib.rs | 33 ++++++++++++++++++- 5 files changed, 55 insertions(+), 7 deletions(-) diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index 72865b33da6e7..3b1b7bd7a2290 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -28,7 +28,7 @@ use sp_arithmetic::traits::One; use sp_runtime::InnerOf; use sp_std::convert::TryInto; -const SEED: u32 = 0; +const SEED: u32 = 999; /// Creates a **valid** solution with exactly the given size. /// @@ -56,7 +56,7 @@ fn solution_with_size( let targets: Vec = (0..size.targets).map(|i| frame_benchmarking::account("Targets", i, SEED)).collect(); - let mut rng = SmallRng::seed_from_u64(999u64); + let mut rng = SmallRng::seed_from_u64(SEED as u64); // decide who are the winners. let winners = targets @@ -108,8 +108,9 @@ fn solution_with_size( >::put(desired_targets); >::put(RoundSnapshot { voters: all_voters.clone(), targets: targets.clone() }); - // write the snapshot to staking or whoever is the data provider. - T::DataProvider::put_snapshot(all_voters.clone(), targets.clone()); + // write the snapshot to staking or whoever is the data provider, in case it is needed further + // down the road. + T::DataProvider::put_snapshot(all_voters.clone(), targets.clone(), Some(stake)); let cache = helpers::generate_voter_cache::(&all_voters); let stake_of = helpers::stake_of_fn::(&all_voters, &cache); @@ -137,6 +138,8 @@ fn solution_with_size( >::from_assignment(assignments, &voter_index, &target_index).unwrap(); let score = compact.clone().score(&winners, stake_of, voter_at, target_at).unwrap(); let round = >::round(); + + assert!(score[0] > 0, "score is zero, this probably means that the stakes are not set."); RawSolution { compact, score, round } } diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 62d65dca37c4b..a2e1d1cf2c761 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -207,6 +207,10 @@ //! assume no weight is consumed in their functions, when operations fail with `Err`. This can //! clearly be improved, but not a priority as we generally expect snapshot creation to fail only //! due to extreme circumstances. +//! +//! **Take into account the encode/decode weight in benchmarks.** Currently, we only take into +//! account the weight of encode/decode in the `submit_unsigned` given its priority. Nonetheless, +//! all operations on the solution and the snapshot are worthy of taking this into account. #![cfg_attr(not(feature = "std"), no_std)] @@ -916,7 +920,6 @@ impl Pallet { let target_limit = >::max_value().saturated_into::(); let voter_limit = >::max_value().saturated_into::(); - // if any of them don't exist, create all of them. This is a bit conservative. let (targets, w1) = T::DataProvider::targets(Some(target_limit)).map_err(ElectionError::DataProvider)?; let (voters, w2) = diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 4ddcf25be44f8..970f3ab9ffcd0 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -327,6 +327,16 @@ impl ElectionDataProvider for StakingMock { fn next_election_prediction(now: u64) -> u64 { now + EpochLength::get() - now % EpochLength::get() } + + #[cfg(any(feature = "runtime-benchmarks", test))] + fn put_snapshot( + voters: Vec<(AccountId, VoteWeight, Vec)>, + targets: Vec, + _target_stake: Option, + ) { + Targets::set(targets); + Voters::set(voters); + } } impl ExtBuilder { diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index fe09e24f696f8..3d7c2dbac90aa 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -135,7 +135,7 @@ //! Self::DataProvider::targets(None) //! .map_err(|_| "failed to elect") //! .map(|(t, weight)| { -//! (vec![(t[0], Support::default())], weight) +//! (vec![(t[0], Support::default())], weight) //! }) //! } //! } @@ -217,6 +217,7 @@ pub trait ElectionDataProvider { fn put_snapshot( _voters: Vec<(AccountId, VoteWeight, Vec)>, _targets: Vec, + _target_stake: Option, ) { } } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index e3064122a8914..ebe7964254939 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -3413,15 +3413,46 @@ impl frame_election_provider_support::ElectionDataProvider)>, targets: Vec, + target_stake: Option, ) { + use sp_std::convert::TryFrom; targets.into_iter().for_each(|v| { + let stake: BalanceOf = target_stake + .and_then(|w| >::try_from(w).ok()) + .unwrap_or(T::Currency::minimum_balance() * 100u32.into()); + >::insert(v.clone(), v.clone()); + >::insert( + v.clone(), + StakingLedger { + stash: v.clone(), + active: stake, + total: stake, + unlocking: vec![], + claimed_rewards: vec![], + }, + ); >::insert( v, ValidatorPrefs { commission: Perbill::zero(), blocked: false }, ); }); - voters.into_iter().for_each(|(v, _s, t)| { + voters.into_iter().for_each(|(v, s, t)| { + use sp_std::convert::TryFrom; + let stake = >::try_from(s).unwrap_or_else(|_| { + panic!("cannot convert a VoteWeight into BalanceOf, benchmark needs reconfiguring.") + }); + >::insert(v.clone(), v.clone()); + >::insert( + v.clone(), + StakingLedger { + stash: v.clone(), + active: stake, + total: stake, + unlocking: vec![], + claimed_rewards: vec![], + }, + ); >::insert( v, Nominations { targets: t, submitted_in: 0, suppressed: false }, From f3e5397a83a8847621f9807db38e2b00ad22f5d5 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sun, 14 Mar 2021 14:35:02 +0100 Subject: [PATCH 52/57] Fix wan --- frame/staking/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index ebe7964254939..d2db7b03fdd56 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -3438,7 +3438,6 @@ impl frame_election_provider_support::ElectionDataProvider>::try_from(s).unwrap_or_else(|_| { panic!("cannot convert a VoteWeight into BalanceOf, benchmark needs reconfiguring.") }); From e3dafc234340938018c31687a39a2c454430ad5c Mon Sep 17 00:00:00 2001 From: Parity Benchmarking Bot Date: Sun, 14 Mar 2021 14:45:22 +0000 Subject: [PATCH 53/57] 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 --- .../src/weights.rs | 99 ++++++++++--------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs index ae358786c75a7..e13b82f53a177 100644 --- a/frame/election-provider-multi-phase/src/weights.rs +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_election_provider_multi_phase //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-02-12, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! DATE: 2021-03-14, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -35,6 +35,7 @@ // --output=./frame/election-provider-multi-phase/src/weights.rs // --template=./.maintain/frame-weight-template.hbs + #![allow(unused_parens)] #![allow(unused_imports)] @@ -48,58 +49,60 @@ pub trait WeightInfo { fn on_initialize_open_unsigned_with_snapshot() -> Weight; fn on_initialize_open_unsigned_without_snapshot() -> Weight; fn elect_queued() -> Weight; - fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight; - fn feasibility_check(v: u32, t: u32, a: u32, d: u32) -> Weight; + fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight; + fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight; } /// Weights for pallet_election_provider_multi_phase using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn on_initialize_nothing() -> Weight { - (23_401_000 as Weight) + (22_833_000 as Weight) .saturating_add(T::DbWeight::get().reads(7 as Weight)) } fn on_initialize_open_signed() -> Weight { - (79_260_000 as Weight) - .saturating_add(T::DbWeight::get().reads(7 as Weight)) + (106_993_000 as Weight) + .saturating_add(T::DbWeight::get().reads(8 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_with_snapshot() -> Weight { - (77_745_000 as Weight) - .saturating_add(T::DbWeight::get().reads(7 as Weight)) + (106_490_000 as Weight) + .saturating_add(T::DbWeight::get().reads(8 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_without_snapshot() -> Weight { - (21_764_000 as Weight) + (21_275_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn elect_queued() -> Weight { - 0 + (7_274_346_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(6 as Weight)) } - fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight { + fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 23_000 - .saturating_add((4_171_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 78_000 - .saturating_add((229_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 23_000 - .saturating_add((13_661_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 117_000 - .saturating_add((4_499_000 as Weight).saturating_mul(d as Weight)) + // Standard Error: 19_000 + .saturating_add((4_017_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 66_000 + .saturating_add((130_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 19_000 + .saturating_add((13_057_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 99_000 + .saturating_add((4_558_000 as Weight).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) // Standard Error: 12_000 - .saturating_add((4_232_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 42_000 - .saturating_add((636_000 as Weight).saturating_mul(t as Weight)) + .saturating_add((4_186_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 40_000 + .saturating_add((803_000 as Weight).saturating_mul(t as Weight)) // Standard Error: 12_000 - .saturating_add((10_294_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 64_000 - .saturating_add((4_428_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((9_806_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 61_000 + .saturating_add((4_156_000 as Weight).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) } } @@ -107,50 +110,52 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn on_initialize_nothing() -> Weight { - (23_401_000 as Weight) + (22_833_000 as Weight) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) } fn on_initialize_open_signed() -> Weight { - (79_260_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(7 as Weight)) + (106_993_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(8 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_with_snapshot() -> Weight { - (77_745_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(7 as Weight)) + (106_490_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(8 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_without_snapshot() -> Weight { - (21_764_000 as Weight) + (21_275_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn elect_queued() -> Weight { - 0 + (7_274_346_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } - fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight { + fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 23_000 - .saturating_add((4_171_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 78_000 - .saturating_add((229_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 23_000 - .saturating_add((13_661_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 117_000 - .saturating_add((4_499_000 as Weight).saturating_mul(d as Weight)) + // Standard Error: 19_000 + .saturating_add((4_017_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 66_000 + .saturating_add((130_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 19_000 + .saturating_add((13_057_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 99_000 + .saturating_add((4_558_000 as Weight).saturating_mul(d as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) // Standard Error: 12_000 - .saturating_add((4_232_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 42_000 - .saturating_add((636_000 as Weight).saturating_mul(t as Weight)) + .saturating_add((4_186_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 40_000 + .saturating_add((803_000 as Weight).saturating_mul(t as Weight)) // Standard Error: 12_000 - .saturating_add((10_294_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 64_000 - .saturating_add((4_428_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((9_806_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 61_000 + .saturating_add((4_156_000 as Weight).saturating_mul(d as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) } } From 7ddb4c57214ffa39fda1926faf808a0d39bfbbee Mon Sep 17 00:00:00 2001 From: Parity Benchmarking Bot Date: Sun, 14 Mar 2021 15:30:06 +0000 Subject: [PATCH 54/57] 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 --- frame/staking/src/weights.rs | 246 +++++++++++++++++------------------ 1 file changed, 123 insertions(+), 123 deletions(-) diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index 118e030f65978..e895a9e4d51ea 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_staking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-03-03, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! DATE: 2021-03-14, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -77,155 +77,155 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn bond() -> Weight { - (82_888_000 as Weight) + (80_317_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn bond_extra() -> Weight { - (66_676_000 as Weight) + (64_495_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn unbond() -> Weight { - (60_847_000 as Weight) + (59_679_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_update(s: u32, ) -> Weight { - (61_940_000 as Weight) - // Standard Error: 0 - .saturating_add((73_000 as Weight).saturating_mul(s as Weight)) + (61_078_000 as Weight) + // Standard Error: 1_000 + .saturating_add((40_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_kill(s: u32, ) -> Weight { - (98_008_000 as Weight) + (95_129_000 as Weight) // Standard Error: 2_000 - .saturating_add((2_916_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((2_755_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(8 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn validate() -> Weight { - (21_018_000 as Weight) + (20_608_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn kick(k: u32, ) -> Weight { - (28_311_000 as Weight) - // Standard Error: 14_000 - .saturating_add((19_701_000 as Weight).saturating_mul(k as Weight)) + (33_365_000 as Weight) + // Standard Error: 11_000 + .saturating_add((18_830_000 as Weight).saturating_mul(k as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(k as Weight))) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) } fn nominate(n: u32, ) -> Weight { - (34_340_000 as Weight) - // Standard Error: 21_000 - .saturating_add((5_879_000 as Weight).saturating_mul(n as Weight)) + (33_885_000 as Weight) + // Standard Error: 22_000 + .saturating_add((5_562_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn chill() -> Weight { - (20_287_000 as Weight) + (19_741_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn set_payee() -> Weight { - (14_044_000 as Weight) + (13_674_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_controller() -> Weight { - (30_776_000 as Weight) + (29_691_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn set_validator_count() -> Weight { - (2_390_000 as Weight) + (2_375_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_no_eras() -> Weight { - (2_682_000 as Weight) + (2_601_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_new_era() -> Weight { - (2_662_000 as Weight) + (2_605_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_new_era_always() -> Weight { - (2_667_000 as Weight) + (2_584_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_invulnerables(v: u32, ) -> Weight { - (2_820_000 as Weight) + (2_725_000 as Weight) // Standard Error: 0 .saturating_add((35_000 as Weight).saturating_mul(v as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_unstake(s: u32, ) -> Weight { - (66_791_000 as Weight) - // Standard Error: 2_000 - .saturating_add((2_911_000 as Weight).saturating_mul(s as Weight)) + (63_551_000 as Weight) + // Standard Error: 7_000 + .saturating_add((2_844_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(8 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn cancel_deferred_slash(s: u32, ) -> Weight { - (5_937_192_000 as Weight) + (5_905_400_000 as Weight) // Standard Error: 391_000 - .saturating_add((34_816_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((34_785_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn payout_stakers_dead_controller(n: u32, ) -> Weight { - (142_187_000 as Weight) - // Standard Error: 23_000 - .saturating_add((54_299_000 as Weight).saturating_mul(n as Weight)) + (142_264_000 as Weight) + // Standard Error: 22_000 + .saturating_add((52_542_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(11 as Weight)) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(2 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) } fn payout_stakers_alive_staked(n: u32, ) -> Weight { - (161_199_000 as Weight) - // Standard Error: 21_000 - .saturating_add((69_141_000 as Weight).saturating_mul(n as Weight)) + (180_166_000 as Weight) + // Standard Error: 23_000 + .saturating_add((66_767_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(12 as Weight)) .saturating_add(T::DbWeight::get().reads((5 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(3 as Weight)) .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) } fn rebond(l: u32, ) -> Weight { - (42_500_000 as Weight) - // Standard Error: 1_000 - .saturating_add((83_000 as Weight).saturating_mul(l as Weight)) + (42_577_000 as Weight) + // Standard Error: 12_000 + .saturating_add((60_000 as Weight).saturating_mul(l as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn set_history_depth(e: u32, ) -> Weight { (0 as Weight) - // Standard Error: 69_000 - .saturating_add((34_842_000 as Weight).saturating_mul(e as Weight)) + // Standard Error: 68_000 + .saturating_add((33_362_000 as Weight).saturating_mul(e as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) .saturating_add(T::DbWeight::get().writes((7 as Weight).saturating_mul(e as Weight))) } fn reap_stash(s: u32, ) -> Weight { - (69_772_000 as Weight) + (68_474_000 as Weight) // Standard Error: 1_000 - .saturating_add((2_913_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((2_770_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(8 as Weight)) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn new_era(v: u32, n: u32, ) -> Weight { (0 as Weight) - // Standard Error: 883_000 - .saturating_add((603_316_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 44_000 - .saturating_add((85_313_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 903_000 + .saturating_add((594_145_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 45_000 + .saturating_add((83_373_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(9 as Weight)) .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(v as Weight))) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) @@ -234,14 +234,14 @@ impl WeightInfo for SubstrateWeight { } fn submit_solution_better(v: u32, n: u32, a: u32, w: u32, ) -> Weight { (0 as Weight) - // Standard Error: 64_000 - .saturating_add((1_505_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 25_000 - .saturating_add((682_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 64_000 - .saturating_add((78_804_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 134_000 - .saturating_add((8_484_000 as Weight).saturating_mul(w as Weight)) + // Standard Error: 52_000 + .saturating_add((1_460_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 20_000 + .saturating_add((754_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 52_000 + .saturating_add((74_798_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 108_000 + .saturating_add((8_108_000 as Weight).saturating_mul(w as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(a as Weight))) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(w as Weight))) @@ -249,20 +249,20 @@ impl WeightInfo for SubstrateWeight { } fn get_npos_voters(v: u32, n: u32, s: u32, ) -> Weight { (0 as Weight) - // Standard Error: 113_000 - .saturating_add((29_967_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 113_000 - .saturating_add((69_931_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 1_540_000 - .saturating_add((26_303_000 as Weight).saturating_mul(s as Weight)) + // Standard Error: 94_000 + .saturating_add((29_321_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 94_000 + .saturating_add((66_885_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 1_283_000 + .saturating_add((22_991_000 as Weight).saturating_mul(s as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(v as Weight))) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) } fn get_npos_targets(v: u32, ) -> Weight { (0 as Weight) - // Standard Error: 33_000 - .saturating_add((11_344_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 26_000 + .saturating_add((10_972_000 as Weight).saturating_mul(v as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(v as Weight))) } @@ -271,155 +271,155 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn bond() -> Weight { - (82_888_000 as Weight) + (80_317_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn bond_extra() -> Weight { - (66_676_000 as Weight) + (64_495_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn unbond() -> Weight { - (60_847_000 as Weight) + (59_679_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_update(s: u32, ) -> Weight { - (61_940_000 as Weight) - // Standard Error: 0 - .saturating_add((73_000 as Weight).saturating_mul(s as Weight)) + (61_078_000 as Weight) + // Standard Error: 1_000 + .saturating_add((40_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn withdraw_unbonded_kill(s: u32, ) -> Weight { - (98_008_000 as Weight) + (95_129_000 as Weight) // Standard Error: 2_000 - .saturating_add((2_916_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((2_755_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(8 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn validate() -> Weight { - (21_018_000 as Weight) + (20_608_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn kick(k: u32, ) -> Weight { - (28_311_000 as Weight) - // Standard Error: 14_000 - .saturating_add((19_701_000 as Weight).saturating_mul(k as Weight)) + (33_365_000 as Weight) + // Standard Error: 11_000 + .saturating_add((18_830_000 as Weight).saturating_mul(k as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(k as Weight))) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(k as Weight))) } fn nominate(n: u32, ) -> Weight { - (34_340_000 as Weight) - // Standard Error: 21_000 - .saturating_add((5_879_000 as Weight).saturating_mul(n as Weight)) + (33_885_000 as Weight) + // Standard Error: 22_000 + .saturating_add((5_562_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn chill() -> Weight { - (20_287_000 as Weight) + (19_741_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn set_payee() -> Weight { - (14_044_000 as Weight) + (13_674_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_controller() -> Weight { - (30_776_000 as Weight) + (29_691_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn set_validator_count() -> Weight { - (2_390_000 as Weight) + (2_375_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_no_eras() -> Weight { - (2_682_000 as Weight) + (2_601_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_new_era() -> Weight { - (2_662_000 as Weight) + (2_605_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_new_era_always() -> Weight { - (2_667_000 as Weight) + (2_584_000 as Weight) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn set_invulnerables(v: u32, ) -> Weight { - (2_820_000 as Weight) + (2_725_000 as Weight) // Standard Error: 0 .saturating_add((35_000 as Weight).saturating_mul(v as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn force_unstake(s: u32, ) -> Weight { - (66_791_000 as Weight) - // Standard Error: 2_000 - .saturating_add((2_911_000 as Weight).saturating_mul(s as Weight)) + (63_551_000 as Weight) + // Standard Error: 7_000 + .saturating_add((2_844_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(8 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn cancel_deferred_slash(s: u32, ) -> Weight { - (5_937_192_000 as Weight) + (5_905_400_000 as Weight) // Standard Error: 391_000 - .saturating_add((34_816_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((34_785_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn payout_stakers_dead_controller(n: u32, ) -> Weight { - (142_187_000 as Weight) - // Standard Error: 23_000 - .saturating_add((54_299_000 as Weight).saturating_mul(n as Weight)) + (142_264_000 as Weight) + // Standard Error: 22_000 + .saturating_add((52_542_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(11 as Weight)) .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) } fn payout_stakers_alive_staked(n: u32, ) -> Weight { - (161_199_000 as Weight) - // Standard Error: 21_000 - .saturating_add((69_141_000 as Weight).saturating_mul(n as Weight)) + (180_166_000 as Weight) + // Standard Error: 23_000 + .saturating_add((66_767_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(12 as Weight)) .saturating_add(RocksDbWeight::get().reads((5 as Weight).saturating_mul(n as Weight))) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) } fn rebond(l: u32, ) -> Weight { - (42_500_000 as Weight) - // Standard Error: 1_000 - .saturating_add((83_000 as Weight).saturating_mul(l as Weight)) + (42_577_000 as Weight) + // Standard Error: 12_000 + .saturating_add((60_000 as Weight).saturating_mul(l as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn set_history_depth(e: u32, ) -> Weight { (0 as Weight) - // Standard Error: 69_000 - .saturating_add((34_842_000 as Weight).saturating_mul(e as Weight)) + // Standard Error: 68_000 + .saturating_add((33_362_000 as Weight).saturating_mul(e as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) .saturating_add(RocksDbWeight::get().writes((7 as Weight).saturating_mul(e as Weight))) } fn reap_stash(s: u32, ) -> Weight { - (69_772_000 as Weight) + (68_474_000 as Weight) // Standard Error: 1_000 - .saturating_add((2_913_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((2_770_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(8 as Weight)) .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) } fn new_era(v: u32, n: u32, ) -> Weight { (0 as Weight) - // Standard Error: 883_000 - .saturating_add((603_316_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 44_000 - .saturating_add((85_313_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 903_000 + .saturating_add((594_145_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 45_000 + .saturating_add((83_373_000 as Weight).saturating_mul(n as Weight)) .saturating_add(RocksDbWeight::get().reads(9 as Weight)) .saturating_add(RocksDbWeight::get().reads((4 as Weight).saturating_mul(v as Weight))) .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) @@ -428,14 +428,14 @@ impl WeightInfo for () { } fn submit_solution_better(v: u32, n: u32, a: u32, w: u32, ) -> Weight { (0 as Weight) - // Standard Error: 64_000 - .saturating_add((1_505_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 25_000 - .saturating_add((682_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 64_000 - .saturating_add((78_804_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 134_000 - .saturating_add((8_484_000 as Weight).saturating_mul(w as Weight)) + // Standard Error: 52_000 + .saturating_add((1_460_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 20_000 + .saturating_add((754_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 52_000 + .saturating_add((74_798_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 108_000 + .saturating_add((8_108_000 as Weight).saturating_mul(w as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().reads((4 as Weight).saturating_mul(a as Weight))) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(w as Weight))) @@ -443,20 +443,20 @@ impl WeightInfo for () { } fn get_npos_voters(v: u32, n: u32, s: u32, ) -> Weight { (0 as Weight) - // Standard Error: 113_000 - .saturating_add((29_967_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 113_000 - .saturating_add((69_931_000 as Weight).saturating_mul(n as Weight)) - // Standard Error: 1_540_000 - .saturating_add((26_303_000 as Weight).saturating_mul(s as Weight)) + // Standard Error: 94_000 + .saturating_add((29_321_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 94_000 + .saturating_add((66_885_000 as Weight).saturating_mul(n as Weight)) + // Standard Error: 1_283_000 + .saturating_add((22_991_000 as Weight).saturating_mul(s as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().reads((4 as Weight).saturating_mul(v as Weight))) .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) } fn get_npos_targets(v: u32, ) -> Weight { (0 as Weight) - // Standard Error: 33_000 - .saturating_add((11_344_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 26_000 + .saturating_add((10_972_000 as Weight).saturating_mul(v as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(v as Weight))) } From 7cf67c65931c22502db138fedd5e9920586a041a Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 15 Mar 2021 20:16:02 +0100 Subject: [PATCH 55/57] fix build and review comments. --- frame/election-provider-multi-phase/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 119eccbd9d3bd..de3f0b3a6bbb8 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -1084,7 +1084,7 @@ impl Pallet { if Self::round() != 1 { log!(info, "Finalized election round with compute {:?}.", compute); } - supports + (supports, weight) }) .map_err(|err| { Self::deposit_event(Event::ElectionFinalized(None)); @@ -1195,13 +1195,13 @@ mod feasibility_check { .compact .votes1 .iter_mut() - .filter(|(_, t)| *t == 3 as TargetIndex) + .filter(|(_, t)| *t == TargetIndex::from(3u16)) .for_each(|(_, t)| *t += 1); solution.compact.votes2.iter_mut().for_each(|(_, (t0, _), t1)| { - if *t0 == 3 as TargetIndex { + if *t0 == TargetIndex::from(3u16) { *t0 += 1 }; - if *t1 == 3 as TargetIndex { + if *t1 == TargetIndex::from(3u16) { *t1 += 1 }; }); From 1c7b20ed4fab6aa32d5fa8453b527b2abcc90d3c Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Tue, 16 Mar 2021 05:47:35 +0100 Subject: [PATCH 56/57] Update frame/election-provider-multi-phase/src/lib.rs Co-authored-by: Shawn Tabrizi --- frame/election-provider-multi-phase/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index de3f0b3a6bbb8..ca36d1101e38f 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -1229,7 +1229,7 @@ mod feasibility_check { .compact .votes1 .iter_mut() - .filter(|(v, _)| *v == 7 as VoterIndex) + .filter(|(v, _)| *v == VoterIndex::from(7u32)) .map(|(v, _)| *v = 8) .count() > 0 ); From 13daa47a3d8beb1879fe3573eb5a3efe13465e1b Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 16 Mar 2021 07:36:44 +0100 Subject: [PATCH 57/57] add comment --- frame/election-provider-multi-phase/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index de3f0b3a6bbb8..3c932fe6b0a9c 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -622,6 +622,8 @@ pub mod pallet { // not much we can do about this at this point. log!(warn, "failed to open unsigned phase due to {:?}", why); T::WeightInfo::on_initialize_nothing() + // NOTE: ^^ The trait specifies that this is a noop in terms of weight + // in case of error. } } } @@ -906,7 +908,7 @@ impl Pallet { >::put(Phase::Unsigned((enabled, now))); Self::deposit_event(Event::UnsignedPhaseStarted(Self::round())); - Ok(weight) + Ok(weight.saturating_add(T::DbWeight::get().writes(1))) } /// Creates the snapshot. Writes new data to: