From 46ecd58715af7a422aa8c7ff8f9b01e5fe06424e Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 2 Jan 2020 11:33:28 +0100 Subject: [PATCH 001/106] Initial skeleton for offchain phragmen --- Cargo.lock | 4 - bin/node/runtime/src/lib.rs | 1 + frame/babe/Cargo.toml | 4 - frame/babe/src/lib.rs | 34 ++++- frame/babe/src/mock.rs | 51 ++++++- frame/babe/src/tests.rs | 41 ++--- frame/staking/src/lib.rs | 295 +++++++++++++++++++++++++++++++----- frame/staking/src/mock.rs | 158 ++++++++++++++++--- frame/staking/src/tests.rs | 78 +++++++++- frame/support/src/traits.rs | 15 +- 10 files changed, 580 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e53a3e440e17b..0dcd65e4bbfdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3523,12 +3523,9 @@ version = "2.0.0" dependencies = [ "frame-support 2.0.0", "frame-system 2.0.0", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "pallet-session 2.0.0", "pallet-timestamp 2.0.0", "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "sp-consensus-babe 0.8.0", "sp-core 2.0.0", @@ -3539,7 +3536,6 @@ dependencies = [ "sp-std 2.0.0", "sp-timestamp 2.0.0", "sp-version 2.0.0", - "substrate-test-runtime 2.0.0", ] [[package]] diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 679bad2371749..f149cd3a01e84 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -439,6 +439,7 @@ impl pallet_sudo::Trait for Runtime { type SubmitTransaction = TransactionSubmitter; parameter_types! { + // assume 1 slot == 1 block pub const SessionDuration: BlockNumber = EPOCH_DURATION_IN_SLOTS as _; } diff --git a/frame/babe/Cargo.toml b/frame/babe/Cargo.toml index 30dbda4e53c29..a1a7924366c07 100644 --- a/frame/babe/Cargo.toml +++ b/frame/babe/Cargo.toml @@ -5,7 +5,6 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -hex-literal = "0.2.1" codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true } sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } @@ -21,11 +20,8 @@ sp-consensus-babe = { version = "0.8", default-features = false, path = "../../p sp-io ={ path = "../../primitives/io", default-features = false } [dev-dependencies] -lazy_static = "1.4.0" -parking_lot = "0.9.0" sp-version = { version = "2.0.0", default-features = false, path = "../../primitives/version" } sp-core = { version = "2.0.0", path = "../../primitives/core" } -substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" } [features] default = ["std"] diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index cdc29e203cca9..b33635311a37f 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -26,7 +26,7 @@ use sp_std::{result, prelude::*}; use frame_support::{decl_storage, decl_module, traits::FindAuthor, traits::Get}; use sp_timestamp::OnTimestampSet; use sp_runtime::{generic::DigestItem, ConsensusEngineId, Perbill}; -use sp_runtime::traits::{IsMember, SaturatedConversion, Saturating, RandomnessBeacon}; +use sp_runtime::traits::{IsMember, SaturatedConversion, Saturating, RandomnessBeacon, One, Bounded}; use sp_staking::{ SessionIndex, offence::{Offence, Kind}, @@ -305,12 +305,31 @@ impl Module { // epoch 0 as having started at the slot of block 1. We want to use // the same randomness and validator set as signalled in the genesis, // so we don't rotate the epoch. - now != sp_runtime::traits::One::one() && { + now != One::one() && { let diff = CurrentSlot::get().saturating_sub(Self::current_epoch_start()); diff >= T::EpochDuration::get() } } + /// Return the _best guess_ block number, at which the next epoch change is predicted to happen. + /// + /// 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 -------------- + /// 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. + pub fn next_epoch_change(now: T::BlockNumber) -> T::BlockNumber { + let next_slot = Self::current_epoch_start().saturating_add(T::EpochDuration::get()); + let slots_remaining = next_slot + .checked_sub(CurrentSlot::get()) + .unwrap_or(Bounded::max_value()); + // This is a best effort guess. Drifts in the slot/block ratio will cause errors here. + let blocks_remaining: T::BlockNumber = slots_remaining.saturated_into(); + now.saturating_add(blocks_remaining) + } + /// DANGEROUS: Enact an epoch change. Should be done on every block where `should_epoch_change` has returned `true`, /// and the caller is the only caller of this function. /// @@ -322,10 +341,7 @@ impl Module { ) { // PRECONDITION: caller has done initialization and is guaranteed // by the session module to be called before this. - #[cfg(debug_assertions)] - { - assert!(Self::initialized().is_some()) - } + debug_assert!(Self::initialized().is_some()); // Update epoch index let epoch_index = EpochIndex::get() @@ -471,6 +487,12 @@ impl OnTimestampSet for Module { fn on_timestamp_set(_moment: T::Moment) { } } +impl frame_support::traits::PredictNextSessionChange for Module { + fn predict_next_session_change(now: T::BlockNumber) -> T::BlockNumber { + Self::next_epoch_change(now) + } +} + impl sp_runtime::BoundToRuntimeAppPublic for Module { type Public = AuthorityId; } diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index fb9804dfb77c4..745b48f04f886 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -17,13 +17,16 @@ //! Test utilities #![allow(dead_code, unused_imports)] -use super::{Trait, Module, GenesisConfig}; +use codec::Encode; +use super::{Trait, Module, GenesisConfig, CurrentSlot}; use sp_consensus_babe::AuthorityId; use sp_runtime::{ - traits::IdentityLookup, Perbill, testing::{Header, UintAuthorityId}, impl_opaque_keys, + Perbill, impl_opaque_keys, + testing::{Header, UintAuthorityId, Digest, DigestItem}, + traits::{IdentityLookup, OnInitialize}, }; use sp_version::RuntimeVersion; -use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; +use frame_support::{impl_outer_origin, parameter_types, StorageValue, weights::Weight}; use sp_io; use sp_core::{H256, Blake2Hasher}; @@ -45,7 +48,6 @@ parameter_types! { pub const MinimumPeriod: u64 = 1; pub const EpochDuration: u64 = 3; pub const ExpectedBlockTime: u64 = 1; - pub const Version: RuntimeVersion = substrate_test_runtime::VERSION; pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(16); } @@ -55,7 +57,7 @@ impl frame_system::Trait for Test { type BlockNumber = u64; type Call = (); type Hash = H256; - type Version = Version; + type Version = (); type Hashing = sp_runtime::traits::BlakeTwo256; type AccountId = DummyValidatorId; type Lookup = IdentityLookup; @@ -78,7 +80,7 @@ impl pallet_session::Trait for Test { type Event = (); type ValidatorId = ::AccountId; type ShouldEndSession = Babe; - type SessionHandler = (Babe,Babe,); + type SessionHandler = (Babe,); type OnSessionEnding = (); type ValidatorIdOf = (); type SelectInitialValidators = (); @@ -106,5 +108,42 @@ pub fn new_test_ext(authorities: Vec) -> sp_io::TestExternalit t.into() } +pub fn go_to_block(n: u64, s: u64) { + let pre_digest = make_pre_digest(0, s, [1; 32], [0xff; 64]); + System::initialize(&n, &Default::default(), &Default::default(), &pre_digest); + System::set_block_number(n); + if s > 1 { + CurrentSlot::put(s); + } + // includes a call into `Babe::do_initialize`. + Session::on_initialize(n); +} + +/// Slots will grow accordingly to blocks +pub fn progress_to_block(n: u64) { + let mut slot = Babe::current_slot() + 1; + for i in System::block_number()+1..=n { + go_to_block(i, slot); + slot += 1; + } +} + +pub fn make_pre_digest( + authority_index: sp_consensus_babe::AuthorityIndex, + slot_number: sp_consensus_babe::SlotNumber, + vrf_output: [u8; sp_consensus_babe::VRF_OUTPUT_LENGTH], + vrf_proof: [u8; sp_consensus_babe::VRF_PROOF_LENGTH], +) -> Digest { + let digest_data = sp_consensus_babe::RawBabePreDigest::Primary { + authority_index, + slot_number, + vrf_output, + vrf_proof, + }; + let log = DigestItem::PreRuntime(sp_consensus_babe::BABE_ENGINE_ID, digest_data.encode()); + Digest { logs: vec![log] } +} + pub type System = frame_system::Module; pub type Babe = Module; +pub type Session = pallet_session::Module; diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 1ae57a6d6e71a..89bfbf580341f 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -17,8 +17,8 @@ //! Consensus extension module tests for BABE consensus. use super::*; -use mock::{new_test_ext, Babe, Test}; -use sp_runtime::{traits::OnFinalize, testing::{Digest, DigestItem}}; +use mock::*; +use sp_runtime::{traits::OnFinalize}; use pallet_session::ShouldEndSession; const EMPTY_RANDOMNESS: [u8; 32] = [ @@ -28,22 +28,6 @@ const EMPTY_RANDOMNESS: [u8; 32] = [ 217, 153, 138, 37, 48, 192, 248, 0, ]; -fn make_pre_digest( - authority_index: sp_consensus_babe::AuthorityIndex, - slot_number: sp_consensus_babe::SlotNumber, - vrf_output: [u8; sp_consensus_babe::VRF_OUTPUT_LENGTH], - vrf_proof: [u8; sp_consensus_babe::VRF_PROOF_LENGTH], -) -> Digest { - let digest_data = sp_consensus_babe::RawBabePreDigest::Primary { - authority_index, - slot_number, - vrf_output, - vrf_proof, - }; - let log = DigestItem::PreRuntime(sp_consensus_babe::BABE_ENGINE_ID, digest_data.encode()); - Digest { logs: vec![log] } -} - #[test] fn empty_randomness_is_correct() { let s = compute_randomness([0; RANDOMNESS_LENGTH], 0, std::iter::empty(), None); @@ -124,3 +108,24 @@ fn authority_index() { "Trivially invalid authorities are ignored") }) } + +#[test] +fn can_predict_next_epoch_change() { + new_test_ext(vec![]).execute_with(|| { + assert_eq!(::EpochDuration::get(), 3); + // this sets the genesis slot to 6; + go_to_block(1, 6); + assert_eq!(Babe::genesis_slot(), 6); + assert_eq!(Babe::current_slot(), 6); + assert_eq!(Babe::epoch_index(), 0); + + progress_to_block(5); + + assert_eq!(Babe::epoch_index(), 5 / 3); + assert_eq!(Babe::current_slot(), 10); + + // next epoch change will be at + assert_eq!(Babe::current_epoch_start(), 9); // next change will be 12, 2 slots from now + assert_eq!(Babe::next_epoch_change(System::block_number()), 5 + 2); + }) +} diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 34c42da359bb5..9bff2bb2c74eb 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -262,13 +262,12 @@ use frame_support::{ weights::SimpleDispatchInfo, traits::{ Currency, OnFreeBalanceZero, LockIdentifier, LockableCurrency, - WithdrawReasons, OnUnbalanced, Imbalance, Get, Time + WithdrawReasons, OnUnbalanced, Imbalance, Get, Time, PredictNextSessionChange, } }; -use pallet_session::{historical::OnSessionEnding, SelectInitialValidators}; +use pallet_session::{historical, SelectInitialValidators}; use sp_runtime::{ - Perbill, - RuntimeDebug, + Perbill, RuntimeDebug, RuntimeAppPublic, curve::PiecewiseLinear, traits::{ Convert, Zero, One, StaticLookup, CheckedSub, Saturating, Bounded, SaturatedConversion, @@ -281,13 +280,15 @@ use sp_staking::{ }; #[cfg(feature = "std")] use sp_runtime::{Serialize, Deserialize}; -use frame_system::{self as system, ensure_signed, ensure_root}; +use frame_system::{self as system, ensure_signed, ensure_root, offchain::SubmitSignedTransaction}; use sp_phragmen::{ExtendedBalance, PhragmenStakedAssignment}; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; const MAX_NOMINATIONS: usize = 16; const MAX_UNLOCKING_CHUNKS: usize = 32; +const ELECTION_TOLERANCE: ExtendedBalance = 0; +const ELECTION_ITERATIONS: usize = 2; const STAKING_ID: LockIdentifier = *b"staking "; /// Counter for the number of eras that have passed. @@ -520,6 +521,48 @@ pub struct UnappliedSlash { payout: Balance, } +/// Indicate how an election round was computed. +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] +pub enum ElectionCompute { + /// Result was forcefully computed on chain at the end of the session. + OnChain, + /// Result was submitted and accepted to the chain. + Submitted, +} + +/// The result of an election round. +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] +pub struct ElectionResult { + /// Era at which this election has happened. + era: EraIndex, + /// Type of the result, + compute: ElectionCompute, + /// The new computed slot stake. + slot_stake: Balance, + /// Flat list of validators who have been elected. + elected_stashes: Vec, + /// Flat list of new exposures, to be updated in the [`Exposure`] storage. + exposures: Vec<(AccountId, Exposure)>, +} + +/// The status of the upcoming (offchain) election. +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] +pub enum OffchainElectionStatus { + /// Nothing has happened yet. An offchain worker might be triggered now. + None, + /// An offchain worker as been triggered but no result has been observed yet. No further + /// offchain workers shall be dispatched now. + Triggered(BlockNumber), + /// A result has been returned and should be used in the upcoming era. + Received, +} + +impl Default for OffchainElectionStatus { + fn default() -> Self { + Self::None + } +} + pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type PositiveImbalanceOf = @@ -542,6 +585,8 @@ pub trait SessionInterface: frame_system::Trait { fn validators() -> Vec; /// Prune historical session tries up to but not including the given index. fn prune_historical_up_to(up_to: SessionIndex); + /// Current session index. + fn current_index() -> SessionIndex; } impl SessionInterface<::AccountId> for T where @@ -566,6 +611,10 @@ impl SessionInterface<::AccountId> for T whe fn prune_historical_up_to(up_to: SessionIndex) { >::prune_up_to(up_to); } + + fn current_index() -> SessionIndex { + >::current_index() + } } pub trait Trait: frame_system::Trait { @@ -613,6 +662,24 @@ pub trait Trait: frame_system::Trait { /// The NPoS reward curve to use. type RewardCurve: Get<&'static PiecewiseLinear<'static>>; + + /// Something that can predict the next session change. + type NextSessionChange: PredictNextSessionChange; + + /// How many blocks ahead of the epoch do we try to run the phragmen offchain? Setting this to + /// zero will disable the offchain compute and only on-chain seq-phragmen will be used. + type ElectionLookahead: Get; + + /// A (potentially unknown) key type used to sign the transactions. + type SigningKeyType: RuntimeAppPublic + Clone + /*IdentifyAccount*/; + + /// The overarching call type. + // TODO: This is needed just to bound it to `From>`. Otherwise could have `Self as system` + type Call: From>; + + /// A transaction submitter. + // type SubmitTransaction: SubmitSignedTransaction::Call>; + type SubmitTransaction; } /// Mode of era-forcing. @@ -674,6 +741,14 @@ decl_storage! { /// The currently elected validator set keyed by stash account ID. pub CurrentElected get(fn current_elected): Vec; + /// 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. + pub QueuedElected get(fn queued_elected): Option>>; + + /// Flag to control the execution of the offchain election. + pub ElectionStatus get(fn election_status): OffchainElectionStatus; + /// The current era index. pub CurrentEra get(fn current_era) config(): EraIndex; @@ -693,7 +768,7 @@ decl_storage! { config.stakers.iter().map(|&(_, _, value, _)| value).min().unwrap_or_default() }): BalanceOf; - /// True if the next session change will be a new era regardless of index. + /// Mode of era forcing. pub ForceEra get(fn force_era) config(): Forcing; /// The percentage of the slash that is distributed to reporters. @@ -780,6 +855,8 @@ decl_event!( /// An old slashing report from a prior era was discarded because it could /// not be processed. OldSlashingReportDiscarded(SessionIndex), + /// A new set of stakers was elected with the given computation method. + StakingElection(ElectionCompute), } ); @@ -819,8 +896,49 @@ decl_module! { fn deposit_event() = default; - fn on_initialize() { - Self::ensure_storage_upgraded(); + fn on_initialize(now: T::BlockNumber) { + if + // if we don't have any ongoing offchain compute. + Self::election_status() == OffchainElectionStatus::None && + // and an era is about to be changed. + Self::is_current_session_final() + { + // TODO: maybe we can be naive like im-online and just assume block == slot? + let next_session_change = + T::NextSessionChange::predict_next_session_change(now); + if let Some(remaining) = next_session_change.checked_sub(&now) { + if remaining <= T::ElectionLookahead::get() && !remaining.is_zero() { + // Set the flag to make sure we don't waste any compute here in the same era + // after we have triggered the offline compute. + >::put( + OffchainElectionStatus::::Triggered(now) + ); + frame_support::print("detected a good block to trigger offchain worker."); + } + } else { + frame_support::print("predicted next authority set change to be in the past."); + } + } + } + + fn offchain_worker(now: T::BlockNumber) { + // TODO: add runtime logging. + if sp_io::offchain::is_validator() { + if Self::election_status() == OffchainElectionStatus::::Triggered(now) { + let era = CurrentEra::get(); + if let Some(election_result) = Self::do_phragmen(ElectionCompute::Submitted) { + if let Some(key) = Self::local_signing_key() { + let call: ::Call = Call::submit_election_result().into(); + use system::offchain::SubmitSignedTransaction; + // T::SubmitTransaction::sign_and_submit(call, key); + } else { + frame_support::print("validator with not signing key..."); + } + } + } else { + frame_support::print("validator did not start offchain election."); + } + } } fn on_finalize() { @@ -830,6 +948,11 @@ decl_module! { } } + fn submit_election_result(origin) { + let who = ensure_signed(origin)?; + // TODO: nothing for now, needs encoding. + } + /// Take the origin account as a stash and lock up `value` of its balance. `controller` will /// be the account that controls it. /// @@ -1221,6 +1344,41 @@ impl Module { Self::bonded(stash).and_then(Self::ledger).map(|l| l.active).unwrap_or_default() } + /// Make sure that the account corresponding with the given account-id is a validator. + // TODO: needs a storage item to keep the most recent validators. ATM this is WRONG. + pub fn is_current_validator(who: &T::AccountId) -> bool { + Self::current_elected().contains(who) + } + + /// Find a local `AccountId` we can sign with. + /// TODO: this needs a way to ge from either of account_id <-> public_key. Linked to the + /// transaction signing stuff + fn local_signing_key() -> Option { + // Find all local keys accessible to this app through the localised KeyType. Then go through + // all keys currently stored on chain and check them against the list of local keys until a + // match is found, otherwise return `None`. + let local_keys = T::SigningKeyType::all().iter().map(|i| + (*i).clone() + ).collect::>(); + + // TODO: this is WRONG. current elected is not accurate and is one behind + Self::current_elected().into_iter().find_map(|v| { + Some(v) + }) + } + + /// returns true if the end of the current session leads to an era change. + fn is_current_session_final() -> bool { + let current_index = T::SessionInterface::current_index(); + let era_length = current_index + .checked_sub(Self::current_era_start_session_index()) + .unwrap_or(0); + let session_per_era = T::SessionsPerEra::get() + .checked_sub(One::one()) + .unwrap_or(0); + era_length >= session_per_era + } + // MUTABLES (DANGEROUS) /// Update the ledger for a controller. This will also update the stash lock. The lock will @@ -1305,7 +1463,9 @@ impl Module { fn new_session(session_index: SessionIndex) -> Option<(Vec, Vec<(T::AccountId, Exposure>)>)> { - let era_length = session_index.checked_sub(Self::current_era_start_session_index()).unwrap_or(0); + let era_length = session_index + .checked_sub(Self::current_era_start_session_index()) + .unwrap_or(0); match ForceEra::get() { Forcing::ForceNew => ForceEra::kill(), Forcing::ForceAlways => (), @@ -1325,6 +1485,7 @@ impl Module { /// NOTE: This always happens immediately before a session change to ensure that new validators /// get a chance to set their session keys. fn new_era(start_session_index: SessionIndex) -> Option> { + // TODO: further clean this function. Payment stuff can go elsewhere. // Payout let points = CurrentEraPointsEarned::take(); let now = T::Time::now(); @@ -1396,7 +1557,7 @@ impl Module { }); // Reassign all Stakers. - let (_slot_stake, maybe_new_validators) = Self::select_validators(); + let maybe_new_validators = Self::select_and_update_validators(); Self::apply_unapplied_slashes(current_era); maybe_new_validators @@ -1418,12 +1579,72 @@ impl Module { }) } - /// Select a new validator set from the assembled stakers and their role preferences. + /// Runs [`try_do_phragmen`] and updates the following storage items: + /// - [`Stakers`] + /// - [`SlotStake`] + /// - [`CurrentElected] /// - /// Returns the new `SlotStake` value and a set of newly selected _stash_ IDs. + /// If the election has been successful. It passes the new set upwards. + fn select_and_update_validators() -> Option> { + if let Some(ElectionResult::> { + elected_stashes, + exposures, + era, + slot_stake, + compute, + }) = Self::try_do_phragmen() { + // Clear Stakers. + for v in Self::current_elected().iter() { + >::remove(v); + } + + // Populate Stakers and write slot stake. + exposures.into_iter().for_each(|(s, e)| { + >::insert(s, e); + }); + + // Update slot stake. + >::put(&slot_stake); + + // Set the new validator set in sessions. + // Update current elected. + >::put(&elected_stashes); + + // emit event + Self::deposit_event(RawEvent::StakingElection(compute)); + + Some(elected_stashes) + } else { + None + } + } + + /// Select a new validator set from the assembled stakers and their role preferences. It tries + /// first to peek into [`QueuedElected`]. Otherwise, it runs a new phragmen. /// - /// Assumes storage is coherent with the declaration. - fn select_validators() -> (BalanceOf, Option>) { + /// No storage item is updated. + fn try_do_phragmen() -> Option>> { + // a phragmen result from either a stored submission or locally executed one. + let somehow_phragmen_results = >::take().map(|offchain_result| { + debug_assert!( + Self::election_status() == OffchainElectionStatus::Received, + "offchain result exist but should not.", + ); + + offchain_result + }).or_else(|| Self::do_phragmen(ElectionCompute::OnChain)); + + // Either way, kill the flag. No more submitted solutions until further notice. + >::kill(); + + somehow_phragmen_results + } + + /// Execute phragmen and return the new results. + /// + /// No storage item is updated. + fn do_phragmen(compute: ElectionCompute) -> Option>> { + let era = Self::current_era(); let mut all_nominators: Vec<(T::AccountId, Vec)> = Vec::new(); let all_validator_candidates_iter = >::enumerate(); let all_validators = all_validator_candidates_iter.map(|(who, _pref)| { @@ -1464,8 +1685,6 @@ impl Module { let to_votes = |b: BalanceOf| , u64>>::convert(b) as ExtendedBalance; - let to_balance = |e: ExtendedBalance| - >>::convert(e); let mut supports = sp_phragmen::build_support_map::<_, _, _, T::CurrencyToVote>( &elected_stashes, @@ -1496,13 +1715,11 @@ impl Module { staked_assignments.push((n.clone(), staked_assignment)); } - let tolerance = 0_u128; - let iterations = 2_usize; sp_phragmen::equalize::<_, _, T::CurrencyToVote, _>( staked_assignments, &mut supports, - tolerance, - iterations, + ELECTION_TOLERANCE, + ELECTION_ITERATIONS, Self::slashable_balance_of, ); } @@ -1512,39 +1729,43 @@ impl Module { >::remove(v); } - // Populate Stakers and figure out the minimum stake behind a slot. + let to_balance = |e: ExtendedBalance| + >>::convert(e); + + // collect exposures and slot stake. let mut slot_stake = BalanceOf::::max_value(); - for (c, s) in supports.into_iter() { + let exposures = supports.into_iter().map(|(staker, support)| { // build `struct exposure` from `support` let exposure = Exposure { - own: to_balance(s.own), + own: to_balance(support.own), // This might reasonably saturate and we cannot do much about it. The sum of // someone's stake might exceed the balance type if they have the maximum amount // of balance and receive some support. This is super unlikely to happen, yet // we simulate it in some tests. - total: to_balance(s.total), - others: s.others + total: to_balance(support.total), + others: support.others .into_iter() .map(|(who, value)| IndividualExposure { who, value: to_balance(value) }) .collect::>>(), }; + if exposure.total < slot_stake { slot_stake = exposure.total; } - >::insert(&c, exposure.clone()); - } - - // Update slot stake. - >::put(&slot_stake); - - // Set the new validator set in sessions. - >::put(&elected_stashes); + (staker, exposure) + }).collect::)>>(); // In order to keep the property required by `n_session_ending` // that we must return the new validator set even if it's the same as the old, // as long as any underlying economic conditions have changed, we don't attempt // to do any optimization where we compare against the prior set. - (slot_stake, Some(elected_stashes)) + Some(ElectionResult::> { + elected_stashes, + slot_stake, + exposures, + era, + compute, + }) } else { // There were not enough candidates for even our minimal level of functionality. // This is bad. @@ -1552,7 +1773,7 @@ impl Module { // and let the chain keep producing blocks until we can decide on a sufficiently // substantial set. // TODO: #2494 - (Self::slot_stake(), None) + None } } @@ -1632,7 +1853,7 @@ impl pallet_session::OnSessionEnding for Module { } } -impl OnSessionEnding>> for Module { +impl historical::OnSessionEnding>> for Module { fn on_session_ending(_ending: SessionIndex, start_session: SessionIndex) -> Option<(Vec, Vec<(T::AccountId, Exposure>)>)> { @@ -1688,7 +1909,7 @@ impl Convert> impl SelectInitialValidators for Module { fn select_initial_validators() -> Option> { - >::select_validators().1 + >::select_and_update_validators() } } diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 81066f9dd81a2..2d43ba7bfc461 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -19,14 +19,17 @@ use std::{collections::HashSet, cell::RefCell}; use sp_runtime::{Perbill, KeyTypeId}; use sp_runtime::curve::PiecewiseLinear; -use sp_runtime::traits::{IdentityLookup, Convert, OpaqueKeys, OnInitialize, SaturatedConversion}; -use sp_runtime::testing::{Header, UintAuthorityId}; +use sp_runtime::traits::{ + IdentityLookup, Convert, OpaqueKeys, OnInitialize, SaturatedConversion, Extrinsic as ExtrinsicT, +}; +use sp_runtime::testing::{Header, UintAuthorityId, TestXt}; use sp_staking::{SessionIndex, offence::{OffenceDetails, OnOffenceHandler}}; use sp_core::{H256, crypto::key_types}; use sp_io; use frame_support::{ - assert_ok, impl_outer_origin, parameter_types, StorageLinkedMap, StorageValue, - traits::{Currency, Get, FindAuthor}, + assert_ok, impl_outer_origin, parameter_types, impl_outer_dispatch, impl_outer_event, + StorageLinkedMap, StorageValue, + traits::{Currency, Get, FindAuthor, PredictNextSessionChange}, weights::Weight, }; use crate::{ @@ -35,9 +38,10 @@ use crate::{ }; /// The AccountId alias in this test module. -pub type AccountId = u64; -pub type BlockNumber = u64; -pub type Balance = u64; +type AccountId = u64; +type AccountIndex = u64; +type BlockNumber = u64; +type Balance = u64; /// Simple structure that exposes how u64 currency can be represented as... u64. pub struct CurrencyToVoteHandler; @@ -50,8 +54,11 @@ impl Convert for CurrencyToVoteHandler { thread_local! { static SESSION: RefCell<(Vec, HashSet)> = RefCell::new(Default::default()); + static SESSION_PER_ERA: RefCell = RefCell::new(3); static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); static SLASH_DEFER_DURATION: RefCell = RefCell::new(0); + static ELECTION_LOOKAHEAD: RefCell = RefCell::new(0); + static PERIOD: RefCell = RefCell::new(1); } pub struct TestSessionHandler; @@ -91,6 +98,32 @@ impl Get for ExistentialDeposit { } } +pub struct SessionsPerEra; +impl Get for SessionsPerEra { + fn get() -> SessionIndex { + SESSION_PER_ERA.with(|v| *v.borrow()) + } +} +impl Get for SessionsPerEra { + fn get() -> BlockNumber { + SESSION_PER_ERA.with(|v| *v.borrow() as BlockNumber) + } +} + +pub struct ElectionLookahead; +impl Get for ElectionLookahead { + fn get() -> BlockNumber { + ELECTION_LOOKAHEAD.with(|v| *v.borrow()) + } +} + +pub struct Period; +impl Get for Period { + fn get() -> BlockNumber { + PERIOD.with(|v| *v.borrow()) + } +} + pub struct SlashDeferDuration; impl Get for SlashDeferDuration { fn get() -> EraIndex { @@ -102,6 +135,37 @@ impl_outer_origin!{ pub enum Origin for Test where system = frame_system {} } +impl_outer_dispatch! { + pub enum Call for Test where origin: Origin { + staking::Staking, + } +} + +mod staking { + pub use super::super::*; + use frame_support::impl_outer_event; +} +use pallet_balances as balances; +use pallet_session as session; +use frame_system as system; + +impl_outer_event! { + pub enum MetaEvent for Test { + staking, balances, session, + } +} + +pub struct PeriodicSessionChange

(sp_std::marker::PhantomData

); +impl

PredictNextSessionChange for PeriodicSessionChange

+ where P: Get, +{ + fn predict_next_session_change(now: BlockNumber) -> BlockNumber { + let period = P::get(); + let excess = now % period; + now - excess + period + } +} + /// Author of block is always 11 pub struct Author11; impl FindAuthor for Author11 { @@ -112,9 +176,9 @@ impl FindAuthor for Author11 { } } -// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Eq, PartialEq, Debug)] pub struct Test; + parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: Weight = 1024; @@ -123,15 +187,15 @@ parameter_types! { } impl frame_system::Trait for Test { type Origin = Origin; - type Index = u64; + type Index = AccountIndex; type BlockNumber = BlockNumber; - type Call = (); + type Call = Call; type Hash = H256; type Hashing = ::sp_runtime::traits::BlakeTwo256; type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; - type Event = (); + type Event = MetaEvent; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; type AvailableBlockRatio = AvailableBlockRatio; @@ -147,7 +211,7 @@ impl pallet_balances::Trait for Test { type Balance = Balance; type OnFreeBalanceZero = Staking; type OnNewAccount = (); - type Event = (); + type Event = MetaEvent; type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; @@ -155,7 +219,6 @@ impl pallet_balances::Trait for Test { type CreationFee = CreationFee; } parameter_types! { - pub const Period: BlockNumber = 1; pub const Offset: BlockNumber = 0; pub const UncleGenerations: u64 = 0; pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(25); @@ -165,7 +228,7 @@ impl pallet_session::Trait for Test { type Keys = UintAuthorityId; type ShouldEndSession = pallet_session::PeriodicSessions; type SessionHandler = TestSessionHandler; - type Event = (); + type Event = MetaEvent; type ValidatorId = AccountId; type ValidatorIdOf = crate::StashOf; type SelectInitialValidators = Staking; @@ -201,7 +264,6 @@ pallet_staking_reward_curve::build! { ); } parameter_types! { - pub const SessionsPerEra: SessionIndex = 3; pub const BondingDuration: EraIndex = 3; pub const RewardCurve: &'static PiecewiseLinear<'static> = &I_NPOS; } @@ -210,7 +272,7 @@ impl Trait for Test { type Time = pallet_timestamp::Module; type CurrencyToVote = CurrencyToVoteHandler; type RewardRemainder = (); - type Event = (); + type Event = MetaEvent; type Slash = (); type Reward = (); type SessionsPerEra = SessionsPerEra; @@ -219,9 +281,34 @@ impl Trait for Test { type BondingDuration = BondingDuration; type SessionInterface = Self; type RewardCurve = RewardCurve; -} + type NextSessionChange = PeriodicSessionChange; + type SigningKeyType = UintAuthorityId; + type ElectionLookahead = ElectionLookahead; + type Call = Call; + type SubmitTransaction = (); +} + +// impl frame_system::offchain::CreateTransaction for Test { +// type Signature = ::Signature; +// type Public = <::Pair as sp_core::crypto::Pair>::Public; +// fn create_transaction>( +// call: Call, +// public: Self::Public, +// account: AccountId, +// _index: AccountIndex, +// ) -> Option<(::Call, ::SignaturePayload)> { +// let extra = (); +// Some((call, (account, extra))) +// } +// } + +// pub type Extrinsic = TestXt; +// type SubmitTransaction = frame_system::offchain::TransactionSubmitter<(), Call, Extrinsic>; pub struct ExtBuilder { + session_length: BlockNumber, + election_lookahead: BlockNumber, + session_per_era: SessionIndex, existential_deposit: u64, validator_pool: bool, nominate: bool, @@ -236,6 +323,9 @@ pub struct ExtBuilder { impl Default for ExtBuilder { fn default() -> Self { Self { + session_length: 1, + election_lookahead: 0, + session_per_era: 3, existential_deposit: 0, validator_pool: false, nominate: true, @@ -286,12 +376,27 @@ impl ExtBuilder { self.invulnerables = invulnerables; self } - pub fn set_associated_consts(&self) { + pub fn session_per_era(mut self, length: SessionIndex) -> Self { + self.session_per_era = length; + self + } + pub fn election_lookahead(mut self, look: BlockNumber) -> Self { + self.election_lookahead = look; + self + } + pub fn session_length(mut self, length: BlockNumber) -> Self { + self.session_length = length; + self + } + pub fn set_associated_constants(&self) { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); SLASH_DEFER_DURATION.with(|v| *v.borrow_mut() = self.slash_defer_duration); + SESSION_PER_ERA.with(|v| *v.borrow_mut() = self.session_per_era); + ELECTION_LOOKAHEAD.with(|v| *v.borrow_mut() = self.election_lookahead); + PERIOD.with(|v| *v.borrow_mut() = self.session_length); } pub fn build(self) -> sp_io::TestExternalities { - self.set_associated_consts(); + self.set_associated_constants(); let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); let balance_factor = if self.existential_deposit > 0 { 256 @@ -438,25 +543,34 @@ pub fn bond_nominator(acc: u64, val: u64, target: Vec) { assert_ok!(Staking::nominate(Origin::signed(acc), target)); } +pub fn run_to_block(n: BlockNumber) { + for b in System::block_number()+1..=n { + System::set_block_number(b); + Session::on_initialize(b); + Staking::on_initialize(b); + } +} + pub fn advance_session() { let current_index = Session::current_index(); start_session(current_index + 1); } pub fn start_session(session_index: SessionIndex) { - // Compensate for session delay + // Compensate for session delay TODO: double check this. let session_index = session_index + 1; for i in Session::current_index()..session_index { System::set_block_number((i + 1).into()); Timestamp::set_timestamp(System::block_number() * 1000); Session::on_initialize(System::block_number()); + Staking::on_initialize(System::block_number()); } assert_eq!(Session::current_index(), session_index); } pub fn start_era(era_index: EraIndex) { - start_session((era_index * 3).into()); + start_session((era_index * >::get()).into()); assert_eq!(Staking::current_era(), era_index); } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 9bb10610a082f..255f9c860edd7 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -1784,11 +1784,13 @@ fn era_is_always_same_length() { // This ensures that the sessions is always of the same length if there is no forcing no // session changes. ExtBuilder::default().build().execute_with(|| { + let session_per_era = >::get(); + start_era(1); - assert_eq!(Staking::current_era_start_session_index(), SessionsPerEra::get()); + assert_eq!(Staking::current_era_start_session_index(), session_per_era); start_era(2); - assert_eq!(Staking::current_era_start_session_index(), SessionsPerEra::get() * 2); + assert_eq!(Staking::current_era_start_session_index(), session_per_era * 2); let session = Session::current_index(); ForceEra::put(Forcing::ForceNew); @@ -1797,7 +1799,7 @@ fn era_is_always_same_length() { assert_eq!(Staking::current_era_start_session_index(), session + 1); start_era(4); - assert_eq!(Staking::current_era_start_session_index(), session + SessionsPerEra::get() + 1); + assert_eq!(Staking::current_era_start_session_index(), session + session_per_era + 1); }); } @@ -2557,3 +2559,73 @@ fn version_initialized() { assert_eq!(::StorageVersion::get(), crate::migration::CURRENT_VERSION); }); } + +#[test] +fn offchain_election_flag_is_triggered() { + ExtBuilder::default() + .session_per_era(5) + .session_length(10) + .election_lookahead(3) + .build() + .execute_with( + || { + + run_to_block(10); + assert_eq!(System::block_number(), 10); + assert_eq!(Session::current_index(), 1); + assert_eq!(Staking::current_era(), 0); + assert_eq!(Staking::election_status(), OffchainElectionStatus::None); + + run_to_block(20); + assert_eq!(Session::current_index(), 2); + + run_to_block(40); + assert_eq!(Session::current_index(), 4); + assert_eq!(System::block_number(), 40); + assert_eq!(Staking::current_era(), 0); + assert_eq!(Staking::election_status(), OffchainElectionStatus::None); + + run_to_block(46); + assert_eq!(Staking::election_status(), OffchainElectionStatus::None); + run_to_block(47); + assert_eq!(Staking::election_status(), OffchainElectionStatus::Triggered(47)); + + run_to_block(50); + assert_eq!(Staking::election_status(), OffchainElectionStatus::None); + }) +} + +#[test] +fn election_on_chain_fallback_works() { + ExtBuilder::default().build().execute_with(|| { + start_session(1); + start_session(2); + assert_eq!(Staking::election_status(), OffchainElectionStatus::None); + // some election must have happened by now + assert_eq!( + System::events()[4].event, + MetaEvent::staking(RawEvent::StakingElection(ElectionCompute::OnChain)), + ); + }) +} + +#[test] +fn is_current_session_final_works() { + ExtBuilder::default().session_per_era(3).build().execute_with(|| { + start_era(1); + assert_eq!(Session::current_index(), 4); + assert_eq!(Staking::current_era(), 1); + assert_eq!(Staking::is_current_session_final(), false); + + start_session(4); + assert_eq!(Session::current_index(), 5); + assert_eq!(Staking::current_era(), 1); + assert_eq!(Staking::is_current_session_final(), true); + + start_session(5); + assert_eq!(Session::current_index(), 6); + // era changed. + assert_eq!(Staking::current_era(), 2); + assert_eq!(Staking::is_current_session_final(), false); + }) +} diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 379f964d271a0..2ff3a9f1e397a 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -23,11 +23,24 @@ use codec::{FullCodec, Codec, Encode, Decode}; use sp_core::u32_trait::Value as U32; use sp_runtime::{ ConsensusEngineId, DispatchResult, DispatchError, - traits::{MaybeSerializeDeserialize, SimpleArithmetic, Saturating}, + traits::{MaybeSerializeDeserialize, SimpleArithmetic, Saturating, Bounded}, }; use crate::dispatch::Parameter; +/// Something that can predict at which block number the next era change will happen. +pub trait PredictNextSessionChange { + /// Return the block number at which the next era change will happen. + fn predict_next_session_change(now: BlockNumber) -> BlockNumber; +} + +impl PredictNextSessionChange for () { + fn predict_next_session_change(_: BlockNumber) -> BlockNumber { + // practically never + Bounded::max_value() + } +} + /// Anything that can have a `::len()` method. pub trait Len { /// Return the length of data type. From a830ec9422ae114e5efab42204abca454393f6b9 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 6 Jan 2020 16:43:25 +0100 Subject: [PATCH 002/106] Basic compact encoding decoding for results --- Cargo.lock | 13 +++++++ Cargo.toml | 1 + frame/staking/src/lib.rs | 14 +++---- primitives/phragmen/Cargo.toml | 1 + primitives/phragmen/src/lib.rs | 61 +++++++++++++++++------------ primitives/phragmen/src/mock.rs | 18 ++++----- primitives/phragmen/src/tests.rs | 66 ++++++++++++++++++++++++-------- primitives/runtime/src/lib.rs | 5 ++- 8 files changed, 122 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0dcd65e4bbfdb..76812a59ae8cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4287,6 +4287,17 @@ dependencies = [ "fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "phragmen-compact" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-runtime 2.0.0", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pin-project" version = "0.4.6" @@ -6396,6 +6407,8 @@ dependencies = [ name = "sp-phragmen" version = "2.0.0" dependencies = [ + "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "phragmen-compact 2.0.0", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "sp-io 2.0.0", diff --git a/Cargo.toml b/Cargo.toml index 505cd299d9c71..a2e5dc7826910 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -117,6 +117,7 @@ members = [ "primitives/offchain", "primitives/panic-handler", "primitives/phragmen", + "primitives/phragmen/compact", "primitives/rpc", "primitives/runtime-interface", "primitives/runtime-interface/proc-macro", diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 9bff2bb2c74eb..8297f548a9348 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -282,7 +282,7 @@ use sp_staking::{ use sp_runtime::{Serialize, Deserialize}; use frame_system::{self as system, ensure_signed, ensure_root, offchain::SubmitSignedTransaction}; -use sp_phragmen::{ExtendedBalance, PhragmenStakedAssignment}; +use sp_phragmen::{ExtendedBalance, PhragmenStakedAssignment, Assignment}; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; const MAX_NOMINATIONS: usize = 16; @@ -1696,23 +1696,23 @@ impl Module { let mut staked_assignments : Vec<(T::AccountId, Vec>)> = Vec::with_capacity(assignments.len()); - for (n, assignment) in assignments.iter() { + for Assignment { who, distribution } in assignments.iter() { let mut staked_assignment : Vec> - = Vec::with_capacity(assignment.len()); + = Vec::with_capacity(distribution.len()); // If this is a self vote, then we don't need to equalise it at all. While the // staking system does not allow nomination and validation at the same time, // this must always be 100% support. - if assignment.len() == 1 && assignment[0].0 == *n { + if distribution.len() == 1 && distribution[0].0 == *who { continue; } - for (c, per_thing) in assignment.iter() { - let nominator_stake = to_votes(Self::slashable_balance_of(n)); + for (c, per_thing) in distribution.iter() { + let nominator_stake = to_votes(Self::slashable_balance_of(who)); let other_stake = *per_thing * nominator_stake; staked_assignment.push((c.clone(), other_stake)); } - staked_assignments.push((n.clone(), staked_assignment)); + staked_assignments.push((who.clone(), staked_assignment)); } sp_phragmen::equalize::<_, _, T::CurrencyToVote, _>( diff --git a/primitives/phragmen/Cargo.toml b/primitives/phragmen/Cargo.toml index 92807376de3b5..34299de7513a9 100644 --- a/primitives/phragmen/Cargo.toml +++ b/primitives/phragmen/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" serde = { version = "1.0.101", optional = true, features = ["derive"] } sp-std = { version = "2.0.0", default-features = false, path = "../std" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +phragmen-compact = { path = "./compact"} [dev-dependencies] substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index a06ef9497b216..a60104e7a245c 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -34,8 +34,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use sp_std::{prelude::*, collections::btree_map::BTreeMap}; -use sp_runtime::RuntimeDebug; -use sp_runtime::{helpers_128bit::multiply_by_rational, Perbill, Rational128}; +use sp_runtime::{helpers_128bit::multiply_by_rational, Perbill, Rational128, RuntimeDebug}; use sp_runtime::traits::{Zero, Convert, Member, SimpleArithmetic, Saturating, Bounded}; #[cfg(test)] @@ -58,11 +57,11 @@ const DEN: u128 = u128::max_value(); /// A candidate entity for phragmen election. #[derive(Clone, Default, RuntimeDebug)] -pub struct Candidate { +struct Candidate { /// Identifier. - pub who: AccountId, + who: AccountId, /// Intermediary value used to sort candidates. - pub score: Rational128, + score: Rational128, /// Sum of the stake of this candidate based on received votes. approval_stake: ExtendedBalance, /// Flag for being elected. @@ -71,7 +70,7 @@ pub struct Candidate { /// A voter entity. #[derive(Clone, Default, RuntimeDebug)] -pub struct Voter { +struct Voter { /// Identifier. who: AccountId, /// List of candidates proposed by this voter. @@ -84,7 +83,7 @@ pub struct Voter { /// A candidate being backed by a voter. #[derive(Clone, Default, RuntimeDebug)] -pub struct Edge { +struct Edge { /// Identifier. who: AccountId, /// Load of this vote. @@ -107,7 +106,16 @@ pub struct PhragmenResult { pub winners: Vec<(AccountId, ExtendedBalance)>, /// Individual assignments. for each tuple, the first elements is a voter and the second /// is the list of candidates that it supports. - pub assignments: Vec<(AccountId, Vec>)> + pub assignments: Vec> +} + +#[derive(RuntimeDebug, Clone)] +#[cfg_attr(feature = "std", derive(PartialEq, Eq))] +pub struct Assignment { + /// Voter's identifier + pub who: AccountId, + /// The distribution of the voter's stake. + pub distribution: Vec<(AccountId, Perbill)>, } /// A structure to demonstrate the phragmen result from the perspective of the candidate, i.e. how @@ -163,7 +171,7 @@ pub fn elect( // return structures let mut elected_candidates: Vec<(AccountId, ExtendedBalance)>; - let mut assigned: Vec<(AccountId, Vec>)>; + let mut assigned: Vec>; // used to cache and access candidates index. let mut c_idx_cache = BTreeMap::::new(); @@ -271,7 +279,10 @@ pub fn elect( // update backing stake of candidates and voters for n in &mut voters { - let mut assignment = (n.who.clone(), vec![]); + let mut assignment = Assignment { + who: n.who.clone(), + distribution: vec![], + }; for e in &mut n.edges { if elected_candidates.iter().position(|(ref c, _)| *c == e.who).is_some() { let per_bill_parts = @@ -299,16 +310,16 @@ pub fn elect( let per_thing = Perbill::from_parts( per_bill_parts.min(Perbill::accuracy().into()) as u32 ); - assignment.1.push((e.who.clone(), per_thing)); + assignment.distribution.push((e.who.clone(), per_thing)); } } - if assignment.1.len() > 0 { + if assignment.distribution.len() > 0 { // To ensure an assertion indicating: no stake from the nominator going to waste, // we add a minimal post-processing to equally assign all of the leftover stake ratios. - let vote_count = assignment.1.len() as u32; - let len = assignment.1.len(); - let sum = assignment.1.iter() + let vote_count = assignment.distribution.len() as u32; + let len = assignment.distribution.len(); + let sum = assignment.distribution.iter() .map(|a| a.1.deconstruct()) .sum::(); let accuracy = Perbill::accuracy(); @@ -317,10 +328,10 @@ pub fn elect( if diff_per_vote > 0 { for i in 0..len { - let current_ratio = assignment.1[i % len].1; + let current_ratio = assignment.distribution[i % len].1; let next_ratio = current_ratio .saturating_add(Perbill::from_parts(diff_per_vote)); - assignment.1[i % len].1 = next_ratio; + assignment.distribution[i % len].1 = next_ratio; } } @@ -328,9 +339,9 @@ pub fn elect( // safe to cast it to usize. let remainder = diff - diff_per_vote * vote_count; for i in 0..remainder as usize { - let current_ratio = assignment.1[i % len].1; + let current_ratio = assignment.distribution[i % len].1; let next_ratio = current_ratio.saturating_add(Perbill::from_parts(1)); - assignment.1[i % len].1 = next_ratio; + assignment.distribution[i % len].1 = next_ratio; } assigned.push(assignment); } @@ -345,7 +356,7 @@ pub fn elect( /// Build the support map from the given phragmen result. pub fn build_support_map( elected_stashes: &Vec, - assignments: &Vec<(AccountId, Vec>)>, + assignments: &Vec>, stake_of: FS, ) -> SupportMap where AccountId: Default + Ord + Member, @@ -361,14 +372,14 @@ pub fn build_support_map( .for_each(|e| { supports.insert(e.clone(), Default::default()); }); // build support struct. - for (n, assignment) in assignments.iter() { - for (c, per_thing) in assignment.iter() { - let nominator_stake = to_votes(stake_of(n)); + for Assignment { who, distribution } in assignments.iter() { + for (c, per_thing) in distribution.iter() { + let nominator_stake = to_votes(stake_of(who)); // AUDIT: it is crucially important for the `Mul` implementation of all // per-things to be sound. let other_stake = *per_thing * nominator_stake; if let Some(support) = supports.get_mut(c) { - if c == n { + if c == who { // This is a nomination from `n` to themselves. This will increase both the // `own` and `total` field. debug_assert!(*per_thing == Perbill::one()); // TODO: deal with this: do we want it? @@ -380,7 +391,7 @@ pub fn build_support_map( // For an astronomically rich validator with more astronomically rich // set of nominators, this might saturate. support.total = support.total.saturating_add(other_stake); - support.others.push((n.clone(), other_stake)); + support.others.push((who.clone(), other_stake)); } } } diff --git a/primitives/phragmen/src/mock.rs b/primitives/phragmen/src/mock.rs index 3074258bbbe66..1063959cc3e88 100644 --- a/primitives/phragmen/src/mock.rs +++ b/primitives/phragmen/src/mock.rs @@ -18,7 +18,7 @@ #![cfg(test)] -use crate::{elect, PhragmenResult, PhragmenAssignment}; +use crate::{elect, PhragmenResult, Assignment}; use sp_runtime::{ assert_eq_error_rate, Perbill, traits::{Convert, Member, SaturatedConversion} @@ -320,10 +320,10 @@ pub(crate) fn create_stake_of(stakes: &[(AccountId, Balance)]) } -pub fn check_assignments(assignments: Vec<(AccountId, Vec>)>) { - for (_, a) in assignments { - let sum: u32 = a.iter().map(|(_, p)| p.deconstruct()).sum(); - assert_eq_error_rate!(sum, Perbill::accuracy(), 5); +pub fn check_assignments_sum(assignments: Vec>) { + for Assignment { distribution, .. } in assignments { + let sum: u32 = distribution.iter().map(|(_, p)| p.deconstruct()).sum(); + assert_eq_error_rate!(sum, Perbill::accuracy(), 1); } } @@ -354,9 +354,9 @@ pub(crate) fn run_and_compare( assert_eq!(winners, truth_value.winners); - for (nominator, assigned) in assignments.clone() { - if let Some(float_assignments) = truth_value.assignments.iter().find(|x| x.0 == nominator) { - for (candidate, per_thingy) in assigned { + for Assignment { who, distribution } in assignments.clone() { + if let Some(float_assignments) = truth_value.assignments.iter().find(|x| x.0 == who) { + for (candidate, per_thingy) in distribution { if let Some(float_assignment) = float_assignments.1.iter().find(|x| x.0 == candidate ) { assert_eq_error_rate!( Perbill::from_fraction(float_assignment.1).deconstruct(), @@ -372,7 +372,7 @@ pub(crate) fn run_and_compare( } } - check_assignments(assignments); + check_assignments_sum(assignments); } pub(crate) fn build_support_map( diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index 09491b3b903ff..c4540e079caaf 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -19,7 +19,7 @@ #![cfg(test)] use crate::mock::*; -use crate::{elect, PhragmenResult}; +use crate::{elect, PhragmenResult, Assignment}; use substrate_test_utils::assert_eq_uvec; use sp_runtime::Perbill; @@ -90,9 +90,21 @@ fn phragmen_poc_works() { assert_eq_uvec!( assignments, vec![ - (10, vec![(2, Perbill::from_percent(100))]), - (20, vec![(3, Perbill::from_percent(100))]), - (30, vec![(2, Perbill::from_percent(100/2)), (3, Perbill::from_percent(100/2))]), + Assignment { + who: 10u64, + distribution: vec![(2, Perbill::from_percent(100))], + }, + Assignment { + who: 20, + distribution: vec![(3, Perbill::from_percent(100))], + }, + Assignment { + who: 30, + distribution: vec![ + (2, Perbill::from_percent(100/2)), + (3, Perbill::from_percent(100/2)), + ], + }, ] ); } @@ -157,7 +169,7 @@ fn phragmen_accuracy_on_large_scale_only_validators() { assert_eq_uvec!(winners, vec![(1, 18446744073709551614u128), (5, 18446744073709551613u128)]); assert_eq!(assignments.len(), 2); - check_assignments(assignments); + check_assignments_sum(assignments); } #[test] @@ -190,13 +202,25 @@ fn phragmen_accuracy_on_large_scale_validators_and_nominators() { assert_eq!( assignments, vec![ - (13, vec![(1, Perbill::one())]), - (14, vec![(2, Perbill::one())]), - (1, vec![(1, Perbill::one())]), - (2, vec![(2, Perbill::one())]), + Assignment { + who: 13u64, + distribution: vec![(1, Perbill::one())], + }, + Assignment { + who: 14, + distribution: vec![(2, Perbill::one())], + }, + Assignment { + who: 1, + distribution: vec![(1, Perbill::one())], + }, + Assignment { + who: 2, + distribution: vec![(2, Perbill::one())], + }, ] ); - check_assignments(assignments); + check_assignments_sum(assignments); } #[test] @@ -284,7 +308,7 @@ fn phragmen_large_scale_test() { ).unwrap(); assert_eq_uvec!(winners, vec![(24, 1490000000000200000u128), (22, 1490000000000100000u128)]); - check_assignments(assignments); + check_assignments_sum(assignments); } #[test] @@ -314,12 +338,24 @@ fn phragmen_large_scale_test_2() { assert_eq!( assignments, vec![ - (50, vec![(2, Perbill::from_parts(500000001)), (4, Perbill::from_parts(499999999))]), - (2, vec![(2, Perbill::one())]), - (4, vec![(4, Perbill::one())]), + Assignment { + who: 50u64, + distribution: vec![ + (2, Perbill::from_parts(500000001)), + (4, Perbill::from_parts(499999999)) + ], + }, + Assignment { + who: 2, + distribution: vec![(2, Perbill::one())], + }, + Assignment { + who: 4, + distribution: vec![(4, Perbill::one())], + }, ], ); - check_assignments(assignments); + check_assignments_sum(assignments); } #[test] diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 80ef992f6b928..b72ffc019b48d 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -65,7 +65,10 @@ pub use sp_application_crypto::{RuntimeAppPublic, BoundToRuntimeAppPublic}; pub use sp_core::RuntimeDebug; /// Re-export top-level arithmetic stuff. -pub use sp_arithmetic::{Perquintill, Perbill, Permill, Percent, Rational128, Fixed64}; +pub use sp_arithmetic::{ + Perquintill, Perbill, Permill, Percent, Rational128, Fixed64, + traits::Saturating, +}; /// Re-export 128 bit helpers. pub use sp_arithmetic::helpers_128bit; /// Re-export big_uint stuff. From 8448ee9cfdbfb695f061aa776d64942a746e275b Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 6 Jan 2020 16:46:15 +0100 Subject: [PATCH 003/106] add compact files --- primitives/phragmen/compact/Cargo.toml | 15 ++ primitives/phragmen/compact/src/lib.rs | 218 +++++++++++++++++++++++ primitives/phragmen/src/tests_compact.rs | 82 +++++++++ 3 files changed, 315 insertions(+) create mode 100644 primitives/phragmen/compact/Cargo.toml create mode 100644 primitives/phragmen/compact/src/lib.rs create mode 100644 primitives/phragmen/src/tests_compact.rs diff --git a/primitives/phragmen/compact/Cargo.toml b/primitives/phragmen/compact/Cargo.toml new file mode 100644 index 0000000000000..fe5b37013b296 --- /dev/null +++ b/primitives/phragmen/compact/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "phragmen-compact" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } +syn = { version = "1.0.7", features = ["full", "visit"] } +quote = "1.0" +proc-macro2 = "1.0.6" diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs new file mode 100644 index 0000000000000..4f463fb12fa2a --- /dev/null +++ b/primitives/phragmen/compact/src/lib.rs @@ -0,0 +1,218 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Proc macro for phragmen compact assignment. + +extern crate proc_macro; + +use proc_macro::TokenStream; +use proc_macro2::{TokenStream as TokenStream2, Span, Ident}; +use quote::quote; +use syn::parse::{Parse, ParseStream}; + +const PREFIX: &'static str = "votes"; + +struct CompactSolutionDef { + vis: syn::Visibility, + ident: syn::Ident, + count: usize, +} + +impl Parse for CompactSolutionDef { + fn parse(input: ParseStream) -> syn::Result { + let vis: syn::Visibility = input.parse()?; + let ident: syn::Ident = input.parse()?; + let _ = ::parse(input)?; + let count_literal: syn::LitInt = input.parse()?; + let count = count_literal.base10_parse::()?; + Ok(Self { vis, ident, count } ) + } +} + +fn field_name_for(n: usize) -> Ident { + Ident::new(&format!("{}{}", PREFIX, n), Span::call_site()) +} + +/// Generates a struct to store the phragmen assignments in a compact way. The struct can only store +/// distributions up to the given input count. The given count must be greater than 2. +/// +/// The generated structure creates one key for each possible count of distributions from 1 up to +/// the given length. A normal distribution is a tuple of `(candidate, weight)` where `candidate` is +/// a generic `AccountId` and `weight` is a `Perbill`. The following rules hold regarding the +/// compact representation: +/// - For single distribution, no weight is stored. The weight is known to be 100%. +/// - For all the rest, the weight if the last distribution is omitted. This value can be computed +/// from the rest. +#[proc_macro] +pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { + let CompactSolutionDef { + vis, + ident, + count, + } = syn::parse_macro_input!(item as CompactSolutionDef); + let account_type = quote!(AccountId); + + if count <= 2 { + panic!("cannot build compact solution struct with capacity less than 2."); + } + + let singles = { + let name = field_name_for(1); + quote!(#name: Vec<(#account_type, #account_type)>,) + }; + let doubles = { + let name = field_name_for(2); + quote!(#name: Vec<(#account_type, (#account_type, Perbill), #account_type)>,) + }; + let rest = (3..=count).map(|c| { + let field_name = field_name_for(c); + let array_len = c - 1; + quote!( + #field_name: Vec<( + #account_type, + [(#account_type, sp_runtime::Perbill); #array_len], + #account_type + )>, + ) + }).collect::(); + + let compact_def = quote! ( + #[derive(Default, PartialEq, Eq, sp_runtime::RuntimeDebug)] + #vis struct #ident<#account_type> { + #singles + #doubles + #rest + } + ); + + let from_impl_single = { + let name = field_name_for(1); + quote!(1 => compact.#name.push((who, distribution[0].0)),) + }; + let from_impl_double = { + let name = field_name_for(2); + quote!(2 => compact.#name.push((who, distribution[0], distribution[1].0)),) + }; + let from_impl_rest = (3..=count).map(|c| { + let inner = (0..c-1).map(|i| quote!(distribution[#i],)).collect::(); + // println!("inner = {}", inner) + + let field_name = field_name_for(c); + let last_index = c - 1; + let last = quote!(distribution[#last_index].0); + + quote!( + #c => compact.#field_name.push((who, [#inner], #last)), + ) + }).collect::(); + + let from_impl = quote!( + impl<#account_type: codec::Codec + Default + Copy> + From>> + for #ident<#account_type> + { + fn from( + assignments: Vec>, + ) -> Self { + let mut compact: #ident<#account_type> = Default::default(); + assignments.into_iter().for_each(|Assignment { who, distribution } | { + match distribution.len() { + #from_impl_single + #from_impl_double + #from_impl_rest + _ => { + sp_runtime::print("assignment length too big. ignoring"); + } + } + }); + compact + } + } + ); + + let into_impl_single = { + let name = field_name_for(1); + quote!( + for (who, target) in self.#name { + assignments.push(Assignment { + who, + distribution: vec![(target, Perbill::one())], + }) + } + ) + }; + let into_impl_double = { + let name = field_name_for(2); + quote!( + for (who, (t1, p1), t2) in self.#name { + let p2 = Perbill::one().saturating_sub(p1); + assignments.push( Assignment { + who, + distribution: vec![ + (t1, p1), + (t2, p2), + ] + }); + } + ) + }; + let into_impl_rest = (3..=count).map(|c| { + let name = field_name_for(c); + quote!( + for (who, inners, t_last) in self.#name { + let mut sum = Perbill::zero(); + let mut inners_parsed = inners + .into_iter() + .map(|(c, p)| { + sum = sum.saturating_add(*p); + (c.clone(), *p) + }).collect::>(); + + let p_last = Perbill::one().saturating_sub(sum); + inners_parsed.push((t_last, p_last)); + + assignments.push(Assignment { + who, + distribution: inners_parsed, + }); + } + ) + }).collect::(); + + let into_impl = quote!( + impl<#account_type: codec::Codec + Default + Copy> + Into>> + for #ident<#account_type> + { + fn into(self) -> Vec> { + let mut assignments: Vec> = Default::default(); + #into_impl_single + #into_impl_double + #into_impl_rest + + assignments + } + } + ); + + quote!( + use sp_runtime::Saturating; + + #compact_def + #from_impl + #into_impl + ).into() +} diff --git a/primitives/phragmen/src/tests_compact.rs b/primitives/phragmen/src/tests_compact.rs new file mode 100644 index 0000000000000..3c6a715d07406 --- /dev/null +++ b/primitives/phragmen/src/tests_compact.rs @@ -0,0 +1,82 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Tests for phragmen compact. + +use crate::Assignment; +use sp_runtime::Perbill; +use phragmen_compact::generate_compact_solution_type; + +generate_compact_solution_type!(TestCompact, 16); + +#[test] +fn basic_from_and_into_compact_works() { + let assignments = vec![ + Assignment { + who: 2u32, + distribution: vec![(20, Perbill::from_percent(100))] + }, + Assignment { + who: 4u32, + distribution: vec![(40, Perbill::from_percent(100))], + }, + Assignment { + who: 1u32, + distribution: vec![ + (10, Perbill::from_percent(80)), + (11, Perbill::from_percent(20)) + ], + }, + Assignment { + who: 5u32, distribution: + vec![ + (50, Perbill::from_percent(85)), + (51, Perbill::from_percent(15)), + ] + }, + Assignment { + who: 3u32, + distribution: vec![ + (30, Perbill::from_percent(50)), + (31, Perbill::from_percent(25)), + (32, Perbill::from_percent(25)), + ], + }, + ]; + + let compacted: TestCompact = assignments.clone().into(); + + assert_eq!( + compacted, + TestCompact { + votes1: vec![(2, 20), (4, 40)], + votes2: vec![ + (1, (10, Perbill::from_percent(80)), 11), + (5, (50, Perbill::from_percent(85)), 51), + ], + votes3: vec![ + (3, [(30, Perbill::from_percent(50)), (31, Perbill::from_percent(25))], 32), + ], + ..Default::default() + } + ); + + assert_eq!( + as Into>>>::into(compacted), + assignments + ); +} + From 5e26a683ed9223eefba646dd3a897cc72529e2cc Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 6 Jan 2020 16:47:23 +0100 Subject: [PATCH 004/106] Bring back Self::ensure_storage_upgraded(); --- frame/staking/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 8297f548a9348..f520ca68d4b8d 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -897,6 +897,7 @@ decl_module! { fn deposit_event() = default; fn on_initialize(now: T::BlockNumber) { + Self::ensure_storage_upgraded(); if // if we don't have any ongoing offchain compute. Self::election_status() == OffchainElectionStatus::None && From 65b2df6a967d3c635efdfaaa1bc3bf7a493d5496 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 7 Jan 2020 09:09:36 +0100 Subject: [PATCH 005/106] Make staking use compact stuff. --- Cargo.lock | 25 ++--- frame/staking/Cargo.toml | 1 + frame/staking/src/lib.rs | 111 ++++++++++++++--------- primitives/phragmen/Cargo.toml | 3 +- primitives/phragmen/compact/Cargo.toml | 2 +- primitives/phragmen/compact/src/lib.rs | 20 ++-- primitives/phragmen/src/tests.rs | 84 ++++++++++++++++- primitives/phragmen/src/tests_compact.rs | 82 ----------------- 8 files changed, 178 insertions(+), 150 deletions(-) delete mode 100644 primitives/phragmen/src/tests_compact.rs diff --git a/Cargo.lock b/Cargo.lock index 76812a59ae8cd..08875d4766a00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3920,6 +3920,7 @@ dependencies = [ "sp-io 2.0.0", "sp-keyring 2.0.0", "sp-phragmen 2.0.0", + "sp-phragmen-compact 2.0.0", "sp-runtime 2.0.0", "sp-staking 2.0.0", "sp-std 2.0.0", @@ -4287,17 +4288,6 @@ dependencies = [ "fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "phragmen-compact" -version = "2.0.0" -dependencies = [ - "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime 2.0.0", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "pin-project" version = "0.4.6" @@ -6408,15 +6398,26 @@ name = "sp-phragmen" version = "2.0.0" dependencies = [ "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "phragmen-compact 2.0.0", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "sp-io 2.0.0", + "sp-phragmen-compact 2.0.0", "sp-runtime 2.0.0", "sp-std 2.0.0", "substrate-test-utils 2.0.0", ] +[[package]] +name = "sp-phragmen-compact" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-runtime 2.0.0", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sp-rpc" version = "2.0.0" diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 8cf6d9f80e81e..b586897c42bf3 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -11,6 +11,7 @@ codec = { package = "parity-scale-codec", version = "1.0.0", default-features = sp-keyring = { version = "2.0.0", optional = true, path = "../../primitives/keyring" } sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } sp-phragmen = { version = "2.0.0", default-features = false, path = "../../primitives/phragmen" } +sp-phragmen-compact = { version = "2.0.0", path = "../../primitives/phragmen/compact" } sp-io ={ path = "../../primitives/io", default-features = false } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } sp-staking = { version = "2.0.0", default-features = false, path = "../../primitives/staking" } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index f520ca68d4b8d..42eb18c806e49 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -285,6 +285,7 @@ use frame_system::{self as system, ensure_signed, ensure_root, offchain::SubmitS use sp_phragmen::{ExtendedBalance, PhragmenStakedAssignment, Assignment}; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; +// ------------- IMPORTANT NOTE: must be the same as `generate_compact_solution_type`. const MAX_NOMINATIONS: usize = 16; const MAX_UNLOCKING_CHUNKS: usize = 32; const ELECTION_TOLERANCE: ExtendedBalance = 0; @@ -522,7 +523,7 @@ pub struct UnappliedSlash { } /// Indicate how an election round was computed. -#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] +#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug)] pub enum ElectionCompute { /// Result was forcefully computed on chain at the end of the session. OnChain, @@ -563,6 +564,9 @@ impl Default for OffchainElectionStatus { } } +// ------------- IMPORTANT NOTE: must be the same as `MAX_NOMINATIONS`. +sp_phragmen_compact::generate_compact_solution_type!(pub CompactAssignments, 16); + pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type PositiveImbalanceOf = @@ -929,11 +933,22 @@ decl_module! { let era = CurrentEra::get(); if let Some(election_result) = Self::do_phragmen(ElectionCompute::Submitted) { if let Some(key) = Self::local_signing_key() { - let call: ::Call = Call::submit_election_result().into(); - use system::offchain::SubmitSignedTransaction; - // T::SubmitTransaction::sign_and_submit(call, key); + if let Some(sp_phragmen::PhragmenResult { + winners, + assignments, + }) = Self::do_phragmen(ElectionCompute::Submitted) { + let winners = winners.into_iter().map(|(w, _)| w).collect(); + let compact_assignments: CompactAssignments = assignments.into(); + let call: ::Call = Call::submit_election_result( + winners, + compact_assignments, + ).into(); + // T::SubmitTransaction::sign_and_submit(call, key); + } else { + frame_support::print("ran phragmen offchain but None was returned."); + } } else { - frame_support::print("validator with not signing key..."); + frame_support::print("validator with not signing key."); } } } else { @@ -949,7 +964,11 @@ decl_module! { } } - fn submit_election_result(origin) { + fn submit_election_result( + origin, + winners: Vec, + assignments: CompactAssignments, + ) { let who = ensure_signed(origin)?; // TODO: nothing for now, needs encoding. } @@ -1633,7 +1652,7 @@ impl Module { ); offchain_result - }).or_else(|| Self::do_phragmen(ElectionCompute::OnChain)); + }).or_else(|| Self::do_phragmen_with_post_processing(ElectionCompute::OnChain)); // Either way, kill the flag. No more submitted solutions until further notice. >::kill(); @@ -1641,44 +1660,12 @@ impl Module { somehow_phragmen_results } - /// Execute phragmen and return the new results. + /// Execute phragmen and return the new results. The edge weights are processed into support values. /// /// No storage item is updated. - fn do_phragmen(compute: ElectionCompute) -> Option>> { + fn do_phragmen_with_post_processing(compute: ElectionCompute) -> Option>> { let era = Self::current_era(); - let mut all_nominators: Vec<(T::AccountId, Vec)> = Vec::new(); - let all_validator_candidates_iter = >::enumerate(); - let all_validators = all_validator_candidates_iter.map(|(who, _pref)| { - let self_vote = (who.clone(), vec![who.clone()]); - all_nominators.push(self_vote); - who - }).collect::>(); - - let nominator_votes = >::enumerate().map(|(nominator, nominations)| { - let Nominations { submitted_in, mut targets, suppressed: _ } = nominations; - - // Filter out nomination targets which were nominated before the most recent - // slashing span. - targets.retain(|stash| { - ::SlashingSpans::get(&stash).map_or( - true, - |spans| submitted_in >= spans.last_start(), - ) - }); - - (nominator, targets) - }); - all_nominators.extend(nominator_votes); - - let maybe_phragmen_result = sp_phragmen::elect::<_, _, _, T::CurrencyToVote>( - Self::validator_count() as usize, - Self::minimum_validator_count().max(1) as usize, - all_validators, - all_nominators, - Self::slashable_balance_of, - ); - - if let Some(phragmen_result) = maybe_phragmen_result { + if let Some(phragmen_result) = Self::do_phragmen(compute) { let elected_stashes = phragmen_result.winners.iter() .map(|(s, _)| s.clone()) .collect::>(); @@ -1778,6 +1765,46 @@ impl Module { } } + /// Execute phragmen and return the new results. No post-processing is applied and the raw edge + /// weights are returned. + /// + /// No storage item is updated. + fn do_phragmen(compute: ElectionCompute) -> Option> { + let mut all_nominators: Vec<(T::AccountId, Vec)> = Vec::new(); + let all_validator_candidates_iter = >::enumerate(); + let all_validators = all_validator_candidates_iter.map(|(who, _pref)| { + // append self vote + let self_vote = (who.clone(), vec![who.clone()]); + all_nominators.push(self_vote); + + who + }).collect::>(); + + let nominator_votes = >::enumerate().map(|(nominator, nominations)| { + let Nominations { submitted_in, mut targets, suppressed: _ } = nominations; + + // Filter out nomination targets which were nominated before the most recent + // slashing span. + targets.retain(|stash| { + ::SlashingSpans::get(&stash).map_or( + true, + |spans| submitted_in >= spans.last_start(), + ) + }); + + (nominator, targets) + }); + all_nominators.extend(nominator_votes); + + sp_phragmen::elect::<_, _, _, T::CurrencyToVote>( + Self::validator_count() as usize, + Self::minimum_validator_count().max(1) as usize, + all_validators, + all_nominators, + Self::slashable_balance_of, + ) + } + /// Remove all associated data of a stash account from the staking system. /// /// Assumes storage is upgraded before calling. diff --git a/primitives/phragmen/Cargo.toml b/primitives/phragmen/Cargo.toml index 34299de7513a9..3e7ac09c95cf0 100644 --- a/primitives/phragmen/Cargo.toml +++ b/primitives/phragmen/Cargo.toml @@ -8,12 +8,13 @@ edition = "2018" serde = { version = "1.0.101", optional = true, features = ["derive"] } sp-std = { version = "2.0.0", default-features = false, path = "../std" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } -phragmen-compact = { path = "./compact"} +sp-phragmen-compact = { path = "./compact"} [dev-dependencies] substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } sp-io ={ version = "2.0.0", path = "../../primitives/io" } rand = "0.7.2" +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } [features] default = ["std"] diff --git a/primitives/phragmen/compact/Cargo.toml b/primitives/phragmen/compact/Cargo.toml index fe5b37013b296..6625cd053dbd9 100644 --- a/primitives/phragmen/compact/Cargo.toml +++ b/primitives/phragmen/compact/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "phragmen-compact" +name = "sp-phragmen-compact" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index 4f463fb12fa2a..33e992cdd42a2 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -90,7 +90,8 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { }).collect::(); let compact_def = quote! ( - #[derive(Default, PartialEq, Eq, sp_runtime::RuntimeDebug)] + // TODO: clean imports: how to deal with codec? + #[derive(Default, PartialEq, Eq, Clone, sp_runtime::RuntimeDebug, codec::Encode, codec::Decode)] #vis struct #ident<#account_type> { #singles #doubles @@ -100,19 +101,18 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { let from_impl_single = { let name = field_name_for(1); - quote!(1 => compact.#name.push((who, distribution[0].0)),) + quote!(1 => compact.#name.push((who, distribution[0].clone().0)),) }; let from_impl_double = { let name = field_name_for(2); - quote!(2 => compact.#name.push((who, distribution[0], distribution[1].0)),) + quote!(2 => compact.#name.push((who, distribution[0].clone(), distribution[1].clone().0)),) }; let from_impl_rest = (3..=count).map(|c| { - let inner = (0..c-1).map(|i| quote!(distribution[#i],)).collect::(); - // println!("inner = {}", inner) + let inner = (0..c-1).map(|i| quote!(distribution[#i].clone(),)).collect::(); let field_name = field_name_for(c); let last_index = c - 1; - let last = quote!(distribution[#last_index].0); + let last = quote!(distribution[#last_index].clone().0); quote!( #c => compact.#field_name.push((who, [#inner], #last)), @@ -120,7 +120,7 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { }).collect::(); let from_impl = quote!( - impl<#account_type: codec::Codec + Default + Copy> + impl<#account_type: codec::Codec + Default + Clone> From>> for #ident<#account_type> { @@ -176,7 +176,7 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { let mut sum = Perbill::zero(); let mut inners_parsed = inners .into_iter() - .map(|(c, p)| { + .map(|(ref c, p)| { sum = sum.saturating_add(*p); (c.clone(), *p) }).collect::>(); @@ -193,7 +193,7 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { }).collect::(); let into_impl = quote!( - impl<#account_type: codec::Codec + Default + Copy> + impl<#account_type: codec::Codec + Default + Clone> Into>> for #ident<#account_type> { @@ -209,8 +209,6 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { ); quote!( - use sp_runtime::Saturating; - #compact_def #from_impl #into_impl diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index c4540e079caaf..b55d99633038d 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -21,7 +21,11 @@ use crate::mock::*; use crate::{elect, PhragmenResult, Assignment}; use substrate_test_utils::assert_eq_uvec; -use sp_runtime::Perbill; +use sp_runtime::{Perbill, Saturating}; +use codec::{Encode, Decode}; +use sp_phragmen_compact::generate_compact_solution_type; + +generate_compact_solution_type!(TestCompact, 16); #[test] fn float_phragmen_poc_works() { @@ -441,3 +445,81 @@ fn minimum_to_elect_is_respected() { assert!(maybe_result.is_none()); } + +#[test] +fn compact_struct_is_codec() { + let compact = TestCompact { + votes1: vec![(2, 20), (4, 40)], + votes2: vec![ + (1, (10, Perbill::from_percent(80)), 11), + (5, (50, Perbill::from_percent(85)), 51), + ], + ..Default::default() + }; + + let encoded = compact.encode(); + + assert_eq!( + compact, + Decode::decode(&mut &encoded[..]).unwrap(), + ); +} + +#[test] +fn basic_from_and_into_compact_works() { + let assignments = vec![ + Assignment { + who: 2u32, + distribution: vec![(20, Perbill::from_percent(100))] + }, + Assignment { + who: 4u32, + distribution: vec![(40, Perbill::from_percent(100))], + }, + Assignment { + who: 1u32, + distribution: vec![ + (10, Perbill::from_percent(80)), + (11, Perbill::from_percent(20)) + ], + }, + Assignment { + who: 5u32, distribution: + vec![ + (50, Perbill::from_percent(85)), + (51, Perbill::from_percent(15)), + ] + }, + Assignment { + who: 3u32, + distribution: vec![ + (30, Perbill::from_percent(50)), + (31, Perbill::from_percent(25)), + (32, Perbill::from_percent(25)), + ], + }, + ]; + + let compacted: TestCompact = assignments.clone().into(); + + assert_eq!( + compacted, + TestCompact { + votes1: vec![(2, 20), (4, 40)], + votes2: vec![ + (1, (10, Perbill::from_percent(80)), 11), + (5, (50, Perbill::from_percent(85)), 51), + ], + votes3: vec![ + (3, [(30, Perbill::from_percent(50)), (31, Perbill::from_percent(25))], 32), + ], + ..Default::default() + } + ); + + assert_eq!( + as Into>>>::into(compacted), + assignments + ); +} + diff --git a/primitives/phragmen/src/tests_compact.rs b/primitives/phragmen/src/tests_compact.rs deleted file mode 100644 index 3c6a715d07406..0000000000000 --- a/primitives/phragmen/src/tests_compact.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Tests for phragmen compact. - -use crate::Assignment; -use sp_runtime::Perbill; -use phragmen_compact::generate_compact_solution_type; - -generate_compact_solution_type!(TestCompact, 16); - -#[test] -fn basic_from_and_into_compact_works() { - let assignments = vec![ - Assignment { - who: 2u32, - distribution: vec![(20, Perbill::from_percent(100))] - }, - Assignment { - who: 4u32, - distribution: vec![(40, Perbill::from_percent(100))], - }, - Assignment { - who: 1u32, - distribution: vec![ - (10, Perbill::from_percent(80)), - (11, Perbill::from_percent(20)) - ], - }, - Assignment { - who: 5u32, distribution: - vec![ - (50, Perbill::from_percent(85)), - (51, Perbill::from_percent(15)), - ] - }, - Assignment { - who: 3u32, - distribution: vec![ - (30, Perbill::from_percent(50)), - (31, Perbill::from_percent(25)), - (32, Perbill::from_percent(25)), - ], - }, - ]; - - let compacted: TestCompact = assignments.clone().into(); - - assert_eq!( - compacted, - TestCompact { - votes1: vec![(2, 20), (4, 40)], - votes2: vec![ - (1, (10, Perbill::from_percent(80)), 11), - (5, (50, Perbill::from_percent(85)), 51), - ], - votes3: vec![ - (3, [(30, Perbill::from_percent(50)), (31, Perbill::from_percent(25))], 32), - ], - ..Default::default() - } - ); - - assert_eq!( - as Into>>>::into(compacted), - assignments - ); -} - From 85b726c8ca64bb9f3448341935ee6c9c560fe952 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 13 Jan 2020 16:47:20 +0100 Subject: [PATCH 006/106] First seemingly working version of reduce, full of todos --- primitives/phragmen/src/lib.rs | 584 +++++++++++++++++++++++++++++-- primitives/phragmen/src/mock.rs | 28 +- primitives/phragmen/src/node.rs | 138 ++++++++ primitives/phragmen/src/tests.rs | 212 ++++++++++- 4 files changed, 927 insertions(+), 35 deletions(-) create mode 100644 primitives/phragmen/src/node.rs diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index a60104e7a245c..1a56fea3684d4 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -42,6 +42,8 @@ mod mock; #[cfg(test)] mod tests; +mod node; + /// A type in which performing operations on balances and stakes of candidates and voters are safe. /// /// This module's functions expect a `Convert` type to convert all balances to u64. Hence, u128 is @@ -92,12 +94,6 @@ struct Edge { candidate_index: usize, } -/// Means a particular `AccountId` was backed by `Perbill`th of a nominator's stake. -pub type PhragmenAssignment = (AccountId, Perbill); - -/// Means a particular `AccountId` was backed by `ExtendedBalance` of a nominator's stake. -pub type PhragmenStakedAssignment = (AccountId, ExtendedBalance); - /// Final result of the phragmen election. #[derive(RuntimeDebug)] pub struct PhragmenResult { @@ -118,6 +114,34 @@ pub struct Assignment { pub distribution: Vec<(AccountId, Perbill)>, } +impl Assignment { + fn into_staked(self, stake_of: FS) -> StakedAssignment + where + C: Convert, + for<'r> FS: Fn(&'r AccountId) -> Balance, + { + let stake = C::convert(stake_of(&self.who)); + let distribution = self.distribution.into_iter().map(|(target, p)| { + let distribution_stake = p * stake; + (target, distribution_stake) + }).collect::>(); + + StakedAssignment { + who: self.who, + distribution, + } + } +} + +#[derive(RuntimeDebug, Clone)] +#[cfg_attr(feature = "std", derive(PartialEq, Eq))] +pub struct StakedAssignment { + /// Voter's identifier + pub who: AccountId, + /// The distribution of the voter's stake. + pub distribution: Vec<(AccountId, ExtendedBalance)>, +} + /// A structure to demonstrate the phragmen result from the perspective of the candidate, i.e. how /// much support each candidate is receiving. /// @@ -133,7 +157,7 @@ pub struct Support { /// Total support. pub total: ExtendedBalance, /// Support from voters. - pub others: Vec>, + pub others: Vec<(AccountId, ExtendedBalance)>, } /// A linkage from a candidate and its [`Support`]. @@ -165,6 +189,7 @@ pub fn elect( AccountId: Default + Ord + Member, Balance: Default + Copy + SimpleArithmetic, for<'r> FS: Fn(&'r AccountId) -> Balance, + // TODO: btw now we can remove the backward convert! C: Convert + Convert, { let to_votes = |b: Balance| >::convert(b) as ExtendedBalance; @@ -353,18 +378,38 @@ pub fn elect( }) } -/// Build the support map from the given phragmen result. -pub fn build_support_map( +/// Build the support map from the given phragmen result. It maps a flat structure like +/// +/// ```nocompile +/// assignments: vec![ +/// voter1, vec![(candidate1, w11), (candidate2, w12)], +/// voter2, vec![(candidate1, w21), (candidate2, w22)] +/// ] +/// ``` +/// +/// into a mapping of candidates and their respective support: +/// +/// ```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)] +/// }, +/// } +/// ``` +pub fn build_support_map( elected_stashes: &Vec, - assignments: &Vec>, - stake_of: FS, + assignments: &Vec>, ) -> SupportMap where AccountId: Default + Ord + Member, Balance: Default + Copy + SimpleArithmetic, - C: Convert + Convert, - for<'r> FS: Fn(&'r AccountId) -> Balance, { - let to_votes = |b: Balance| >::convert(b) as ExtendedBalance; // Initialize the support of each candidate. let mut supports = >::new(); elected_stashes @@ -372,26 +417,20 @@ pub fn build_support_map( .for_each(|e| { supports.insert(e.clone(), Default::default()); }); // build support struct. - for Assignment { who, distribution } in assignments.iter() { - for (c, per_thing) in distribution.iter() { - let nominator_stake = to_votes(stake_of(who)); - // AUDIT: it is crucially important for the `Mul` implementation of all - // per-things to be sound. - let other_stake = *per_thing * nominator_stake; + for StakedAssignment { who, distribution } in assignments.iter() { + for (c, weight_extended) in distribution.iter() { if let Some(support) = supports.get_mut(c) { if c == who { // This is a nomination from `n` to themselves. This will increase both the // `own` and `total` field. - debug_assert!(*per_thing == Perbill::one()); // TODO: deal with this: do we want it? - support.own = support.own.saturating_add(other_stake); - support.total = support.total.saturating_add(other_stake); + support.own = support.own.saturating_add(*weight_extended); + support.total = support.total.saturating_add(*weight_extended); } else { - // This is a nomination from `n` to someone else. Increase `total` and add an entry - // inside `others`. - // For an astronomically rich validator with more astronomically rich - // set of nominators, this might saturate. - support.total = support.total.saturating_add(other_stake); - support.others.push((who.clone(), other_stake)); + // This is a nomination from `n` to someone else. Increase `total` and add an + // entry inside `others`. For an astronomically rich validator with more + // astronomically rich set of nominators, this might saturate. + support.total = support.total.saturating_add(*weight_extended); + support.others.push((who.clone(), *weight_extended)); } } } @@ -399,6 +438,489 @@ pub fn build_support_map( supports } +/// Returns all combinations of size two in the collection `input` with no repetition. +fn combinations_2(input: &[T]) -> Vec<(T, T)> { + let n = input.len(); + if n < 2 { + return Default::default() + } + + let mut comb = Vec::with_capacity(n * (n-1) / 2); + for i in 0..n { + for j in i+1..n { + comb.push((input[i].clone(), input[j].clone())) + } + } + comb +} + +/// Returns the count of trailing common elements in a slice. +/// TODO: this need not to be in the public interface. +/// ```rust +/// use sp_phragmen::trailing_common; +/// +/// fn main() { +/// assert_eq!( +/// trailing_common(&vec![1u8, 2, 3, 4, 5], &vec![7u8, 8, 4, 5]), +/// 2, +/// ); +/// +/// assert_eq!( +/// trailing_common(&vec![1u8, 2], &vec![7u8, 8]), +/// 0, +/// ); +/// +/// assert_eq!( +/// trailing_common(&vec![1u8, 2, 3, 4, 5], &vec![3u8, 4, 5]), +/// 3, +/// ) +/// } +/// ``` +pub fn trailing_common(t1: &[T], t2: &[T]) -> usize { + let mut t1_pointer = t1.len() - 1; + let mut t2_pointer = t2.len() - 1; + let mut common = 0usize; + + while t1[t1_pointer] == t2[t2_pointer] { + common += 1; + if t1_pointer == 0 || t2_pointer == 0 { + break; + } + t1_pointer -= 1; + t2_pointer -= 1; + } + + common +} + +// TODO: maybe replace with BTreeMap if we want to support this in no_std? +type Map = std::collections::HashMap<(A, A), A>; + +fn reduce_4( + assignments: &mut Vec>, +) -> u32 { + use std::collections::{HashMap, hash_map::{Entry::*}}; + + let mut combination_map: Map = Map::new(); + let mut num_changed: u32 = Zero::zero(); + + // NOTE: we have to use the old fashioned style loops here with manual indexing. Borrowing + // assignments will not work since then there is NO way to mutate it inside. + for i in 0..assignments.len() { + let who = assignments[i].who.clone(); + // immutable copy -- needed for further read operations. TODO: As an optimization at the + // expense of readability, we can remove this. + let distribution = &assignments[i].distribution.clone(); + + // all combinations for this particular voter + let candidate_combinations = combinations_2( + &distribution.iter().map(|(t, _p)| t.clone()).collect::>(), + ); + + for (v1, v2) in candidate_combinations { + match combination_map.entry((v1.clone(), v2.clone())) { + Vacant(entry) => { + entry.insert(who.clone()); + }, + Occupied(mut entry) => { + let other_who = entry.get_mut(); + println!("Occupied {:?} -> ({:?} {:?}) other: {:?}", &who, &v1, &v2, &other_who); + + // check if other_who voted for the same pair v1, v2. + let maybe_other_assignments = assignments.iter().find(|a| a.who == *other_who); + if maybe_other_assignments.is_none() { + // TODO: test for this path? + // This combination is not a cycle. + continue; + } + let other_assignment = maybe_other_assignments.expect("value is checked to be 'Some'"); + + // Collect potential cycle votes + let mut other_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); + other_assignment.distribution.iter().for_each(|(t, w)| { + if *t == v1 || *t == v2 { other_cycle_votes.push((t.clone(), *w)); } + }); + + // This is not a cycle. Replace and continue. + let other_votes_count = other_cycle_votes.len(); + // TODO: this might need testing. Some duplicate can cause this and this + // function should reject them. + debug_assert!(other_votes_count <= 2); + + if other_votes_count < 2 { + // Not a cycle. Replace and move on. + // TODO test fro this path?? + *other_who = who.clone(); + continue; + } else { + println!("And it is a cycle! "); + // This is a cycle. + let mut who_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); + distribution.iter().for_each(|(t, w)| { + if *t == v1 || *t == v2 { who_cycle_votes.push((t.clone(), *w)); } + }); + + if who_cycle_votes.len() != 2 { continue; } + + // Align the targets similarly. This helps with the circulation below. + if other_cycle_votes[0].0 != who_cycle_votes[0].0 { + other_cycle_votes.swap(0, 1); + } + + // TODO: remove later. + debug_assert_eq!(who_cycle_votes[0].0, other_cycle_votes[0].0); + debug_assert_eq!(who_cycle_votes[1].0, other_cycle_votes[1].0); + + // Find min + let mut min_value: ExtendedBalance = Bounded::max_value(); + let mut min_index: usize = 0; + let cycle = who_cycle_votes + .iter() + .chain(other_cycle_votes.iter()) + .enumerate() + .map(|(index, (t, w))| { + if *w <= min_value { min_value = *w; min_index = index; } + (t.clone(), *w) + }).collect::>(); + dbg!(&cycle, &min_value); + + // min was in the first part of the chained iters + let mut increase_indices: Vec = Vec::new(); + let mut decrease_indices: Vec = Vec::new(); + decrease_indices.push(min_index); + if min_index < 2 { + // min_index == 0 => sibling_index <- 1 + // min_index == 1 => sibling_index <- 0 + let sibling_index = 1 - min_index; + increase_indices.push(sibling_index); + // valid because the two chained sections of `cycle` are aligned; + // index [0, 2] are both voting for v1 or both v2. Same goes for [1, 3]. + decrease_indices.push(sibling_index + 2); + increase_indices.push(min_index + 2); + } else { + // min_index == 2 => sibling_index <- 3 + // min_index == 3 => sibling_index <- 2 + let sibling_index = 3 - min_index % 2; + increase_indices.push(sibling_index); + // valid because the two chained sections of `cycle` are aligned; + // index [0, 2] are both voting for v1 or both v2. Same goes for [1, 3]. + decrease_indices.push(sibling_index - 2); + increase_indices.push(min_index - 2); + } + + dbg!(&increase_indices, &decrease_indices); + + // apply changes + increase_indices.into_iter().for_each(|i| { + assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { + ass.distribution + .iter_mut() + .position(|(t, _)| *t == cycle[i].0) + .map(|idx| { + let next_value = ass.distribution[idx].1.saturating_add(min_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); + }); + decrease_indices.into_iter().for_each(|i| { + assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { + ass.distribution + .iter_mut() + .position(|(t, _)| *t == cycle[i].0) + .map(|idx| { + let next_value = ass.distribution[idx].1.saturating_sub(min_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + num_changed += 1; + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); + }); + } + } + } + } + } + + println!("DoNE {:?}", assignments); + num_changed +} + +fn reduce_all( + assignments: &mut Vec>, +) -> u32 { + dbg!(&assignments); + use std::collections::{HashMap, hash_map::{Entry::*}}; + use std::cell::{Cell, RefCell}; + let mut change_buffer: HashMap> = HashMap::new(); + let mut num_changed: u32 = Zero::zero(); + + // ----------------- Phase 2: remove any other cycle + use node::{Node, NodeRef, NodeRole}; + let mut tree: HashMap> = HashMap::new(); + + // needless to say, this can be improved. TODO + let edge_weight_of = |voter: &AccountId, candidate: &AccountId| { + if let Some(a) = assignments.iter().find(|a| a.who == *voter) { + if let Some((_, w)) = a.distribution.iter().find(|(t, _)| t == candidate) { + Some(w) + } else { + None + } + } else { + None + } + }; + + + // TODO: unify this terminology: we only have VOTER -> TARGET. no candidate. no staking terms. + // a flat iterator of (voter, candidate) over all pairs of votes. Similar to reduce_4, we loop + // without borrowing. + for i in 0..assignments.len() { + let voter = assignments[i].who.clone(); + + for j in 0..assignments[i].distribution.len() { + let (target, _) = assignments[i].distribution[j].clone(); + println!("+++++ {:?} -> {:?}, Tree", voter, target); + for (key, value) in tree.iter() { + println!("{:?}", value.borrow()); + } + + let voter_node = tree.entry(voter.clone()) + .or_insert(Node::new(voter.clone(), NodeRole::Voter).into_ref()).clone(); + let target_node = tree.entry(target.clone()) + .or_insert(Node::new(target.clone(), NodeRole::Target).into_ref()).clone(); + + if !voter_node.borrow().has_parent() { + Node::set_parent_of(&voter_node, &target_node); + continue; + } + if !target_node.borrow().has_parent() { + Node::set_parent_of(&target_node, &voter_node); + continue; + } + + let (voter_root, voter_root_path) = Node::root(&voter_node); + let (target_root, target_root_path) = Node::root(&target_node); + + if voter_root != target_root { + // swap + // TODO: test case for this path + if voter_root_path.len() <= target_root_path.len() { + // iterate from last to beginning, skipping the first one. This asserts that + // indexing is always correct. + voter_root_path + .iter() + .skip(1) + .rev() + .enumerate() + .map(|(index, r)| (voter_root_path.len() - index - 1, r)) + .for_each(|(index, r)| { + let index = voter_root_path.len() - index; + Node::set_parent_of(r, &voter_root_path[index-1]) + }); + debug_assert_eq!(voter_root_path[0], voter_node); + Node::set_parent_of(&voter_node, &target_node); + } else { + target_root_path + .iter() + .skip(1) + .rev() + .enumerate() + .map(|(index, r)| (target_root_path.len() - index - 1, r)) + .for_each(|(index, r)| { + let index = target_root_path.len() - index; + Node::set_parent_of(r, &target_root_path[index-1]) + }); + debug_assert_eq!(target_root_path[0], target_node); + Node::set_parent_of(&target_node, &voter_node); + } + } else { + debug_assert_eq!(target_root_path.last().unwrap(), voter_root_path.last().unwrap()); + + // find common and cycle. + let common_count = trailing_common(&voter_root_path, &target_root_path); + + // because roots are the same. TODO: replace with a bail-out + debug_assert!(common_count > 0); + + // cycle part of each path will be `path[path.len() - common_count - 1 : 0]` + // NOTE: the order of chaining is important! it is always build from [target, ..., + // voter] + // TODO: check borrows panic! + let cycle = + target_root_path.iter().take(target_root_path.len() - common_count + 1).cloned() + .chain(voter_root_path.iter().take(voter_root_path.len() - common_count).rev().cloned()) + .collect::>>(); + + println!("cycle = {:?}", cycle.iter().map(|w| w.borrow().who.clone()).collect::>()); + + // TODO: a cycle struct that gives you min + to which chunk it belonged. + // find minimum of cycle. + let mut min_value: ExtendedBalance = Bounded::max_value(); + // Note that this can only ever point to a target, not a voter. + let mut min_who: AccountId = Default::default(); + let mut min_neighbor: AccountId = Default::default(); + let mut min_index = 0usize; + // 1 -> next // 0 -> prev TOOD: I have some ideas of fixing this. + let mut min_direction = 0u32; + // helpers + let next_index = |i| { if i < (cycle.len() - 1) { i + 1 } else { 0 } }; + let prev_index = |i| { if i > 0 { i - 1 } else { cycle.len() - 1 } }; + for i in 0..cycle.len() { + if cycle[i].borrow().role == NodeRole::Voter { + // NOTE: sadly way too many clones since I don't want to make AccountId: Copy + let current = cycle[i].borrow().who.clone(); + let next = cycle[next_index(i)].borrow().who.clone(); + let prev = cycle[prev_index(i)].borrow().who.clone(); + assignments.iter().find(|a| a.who == current).map(|ass| { + ass.distribution.iter().find(|d| d.0 == next).map(|(_, w)| { + if *w < min_value { + min_value = *w; + min_who = next.clone(); + min_index = i; + min_direction = 1; + } + }) + }); + assignments.iter().find(|a| a.who == current).map(|ass| { + ass.distribution.iter().find(|d| d.0 == prev).map(|(_, w)| { + if *w < min_value { + min_value = *w; + min_who = prev.clone(); + min_index = i; + min_direction = 0; + } + }) + }); + } + } + // if the min edge is in the voter's sub-chain. + let cycle_len = cycle.len(); + let target_chunk = target_root_path.len() - common_count; + let voter_chunk = voter_root_path.len() - common_count; + let min_chain_in_voter = (min_index + min_direction as usize) >= target_chunk; + + dbg!(min_value, min_index, &min_who, min_direction); + + // walk over the cycle and update the weights + // TODO: or at least unify and merge the process of adding this flow in both places. and surely add dedicated tests for this supply demand circulation math + // if the circulation begins with an addition. It is assumed that a cycle always starts with a target. + let start_operation_add = ((min_index % 2) + min_direction as usize) % 2 == 1; + for i in 0..cycle.len() { + let current = cycle[i].borrow(); + if current.role == NodeRole::Voter { + let prev = cycle[prev_index(i)].borrow(); + + assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { + ass.distribution.iter_mut().position(|(t, _)| *t == prev.who).map(|idx| { + let next_value = if i % 2 == 0 { + if start_operation_add { + ass.distribution[idx].1.saturating_add(min_value) + } else { + ass.distribution[idx].1.saturating_sub(min_value) + } + } else { + if start_operation_add { + ass.distribution[idx].1.saturating_sub(min_value) + } else { + ass.distribution[idx].1.saturating_add(min_value) + } + }; + // println!("Next value for edge {:?} -> {:?} is {}", current.who, prev.who, next_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + num_changed += 1; + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); + + let next = cycle[next_index(i)].borrow(); + + assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { + ass.distribution.iter_mut().position(|(t, _)| *t == next.who).map(|idx| { + let next_value = if i % 2 == 0 { + if start_operation_add { + ass.distribution[idx].1.saturating_sub(min_value) + } else { + ass.distribution[idx].1.saturating_add(min_value) + } + } else { + if start_operation_add { + ass.distribution[idx].1.saturating_add(min_value) + } else { + ass.distribution[idx].1.saturating_sub(min_value) + } + }; + // println!("Next value for edge {:?} -> {:?} is {}", current.who, next.who, next_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + num_changed += 1; + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); + } + }; + + // don't do anything if the edge removed itself + if min_index == (cycle.len() - 1) && min_direction == 1 { continue; } + + // TODO: this is most likely buggy + // re-org otherwise. + if min_chain_in_voter { + // NOTE: safe; voter_root_path is always bigger than 1 element. + for i in 0..voter_root_path.len()-1 { + let next = voter_root_path[i + 1].clone(); + if next.borrow().who == min_who { + break; + } + Node::set_parent_of(&voter_root_path[i + 1], &voter_root_path[i]); + } + Node::set_parent_of(&voter_node, &target_node); + } else { + // NOTE: safe; target_root_path is always bigger than 1 element. + for i in 0..target_root_path.len()-1 { + if target_root_path[i].borrow().who == min_who { + break; + } + Node::set_parent_of(&target_root_path[i + 1], &target_root_path[i]); + } + Node::set_parent_of(&target_node, &voter_node); + } + + + + } + } + } + + num_changed +} + +/// Reduce the given [`PhragmenResult`]. This removes redundant edges from without changing the +/// overall backing of any of the elected candidates. +/// +/// TODO: add complexity to all functions. +pub fn reduce< + AccountId: Clone + Eq + Default + std::hash::Hash + std::fmt::Debug, +>( + assignments: &mut Vec>, +) -> u32 where { + let mut num_changed = reduce_4(assignments); + num_changed += reduce_all(assignments); + num_changed +} + /// Performs equalize post-processing to the output of the election algorithm. This happens in /// rounds. The number of rounds and the maximum diff-per-round tolerance can be tuned through input /// parameters. @@ -411,7 +933,7 @@ pub fn build_support_map( /// * `iterations`: maximum number of iterations that will be processed. /// * `stake_of`: something that can return the stake stake of a particular candidate or voter. pub fn equalize( - mut assignments: Vec<(AccountId, Vec>)>, + mut assignments: Vec<(AccountId, Vec<(AccountId, ExtendedBalance)>)>, supports: &mut SupportMap, tolerance: ExtendedBalance, iterations: usize, @@ -449,7 +971,7 @@ pub fn equalize( fn do_equalize( voter: &AccountId, budget_balance: Balance, - elected_edges: &mut Vec>, + elected_edges: &mut Vec<(AccountId, ExtendedBalance)>, support_map: &mut SupportMap, tolerance: ExtendedBalance ) -> ExtendedBalance where diff --git a/primitives/phragmen/src/mock.rs b/primitives/phragmen/src/mock.rs index 1063959cc3e88..628b3d3fe975a 100644 --- a/primitives/phragmen/src/mock.rs +++ b/primitives/phragmen/src/mock.rs @@ -18,7 +18,10 @@ #![cfg(test)] -use crate::{elect, PhragmenResult, Assignment}; +use crate::{ + elect, build_support_map, + PhragmenResult, Assignment, StakedAssignment, +}; use sp_runtime::{ assert_eq_error_rate, Perbill, traits::{Convert, Member, SaturatedConversion} @@ -375,7 +378,7 @@ pub(crate) fn run_and_compare( check_assignments_sum(assignments); } -pub(crate) fn build_support_map( +pub(crate) fn build_support_map_float( result: &mut _PhragmenResult, stake_of: FS, ) -> _SupportMap @@ -403,3 +406,24 @@ pub(crate) fn build_support_map( } supports } + + + +fn dummy_stake_of(who: &AccountId) -> Balance { + (*who * 100) as Balance +} + +pub fn assert_assignments_equal( + winners: &Vec, + ass1: &Vec>, + ass2: &Vec>, +) { + let support_1 = build_support_map::(winners, ass1); + let support_2 = build_support_map::(winners, ass2); + + for (who, support) in support_1.iter() { + assert_eq!(support.total, support_2.get(who).unwrap().total); + assert_eq!(support.others, support_2.get(who).unwrap().others); + + } +} diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs new file mode 100644 index 0000000000000..d728607e0ca25 --- /dev/null +++ b/primitives/phragmen/src/node.rs @@ -0,0 +1,138 @@ +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(PartialEq, Eq, std::fmt::Debug)] +pub(crate) enum NodeRole { + Voter, + Target, +} + +pub(crate) type NodeRef = Rc>>; + +#[derive(PartialEq, Eq)] +pub(crate) struct Node { + pub(crate) who: A, + pub(crate) role: NodeRole, + pub(crate) parent: Option>, +} + +use std::fmt; +impl fmt::Debug for Node { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({:?} [--> {:?})]", self.who, self.parent.as_ref().map(|p| p.borrow().who.clone())) + } +} + +impl Node { + pub fn new(who: A, role: NodeRole) -> Node { + Self { + who, + role, + parent: None, + } + } + + pub fn has_parent(&self) -> bool { + self.parent.is_some() + } + + pub fn set_parent(&mut self, parent: &NodeRef) { + self.parent = Some(parent.clone()); + } + + pub fn set_parent_of(target: &NodeRef, parent: &NodeRef) { + target.borrow_mut().parent = Some(parent.clone()); + } + + pub fn root(start: &NodeRef) -> (NodeRef, Vec>) { + let mut parent_path: Vec> = Vec::new(); + parent_path.push(start.clone()); + + let mut current = parent_path[0].clone(); + while let Some(ref next_parent) = current.clone().borrow().parent { + parent_path.push(next_parent.clone()); + current = next_parent.clone(); + } + + (current, parent_path) + } + + pub fn into_ref(self) -> NodeRef { + Rc::from(RefCell::from(self)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic_create_works() { + let node = Node::new(10u32, NodeRole::Target); + assert_eq!(node, Node { who: 10u32, parent: None, role: NodeRole::Target }); + } + + #[test] + fn set_parent_works() { + let a = Node::new(10u32, NodeRole::Target).into_ref(); + let b = Node::new(20u32, NodeRole::Target).into_ref(); + + assert_eq!(a.borrow().parent, None); + Node::set_parent_of(&a, &b); + assert_eq!(*a.borrow().parent.as_ref().unwrap(), b); + } + + #[test] + fn get_root_singular() { + let a = Node::new(1u32, NodeRole::Target).into_ref(); + assert_eq!(Node::root(&a), (a.clone(), vec![a.clone()])); + } + + #[test] + fn get_root_works() { + // D <-- A <-- B <-- C + // \ + // <-- E + let d = Node::new(1u32, NodeRole::Target).into_ref(); + let a = Node::new(1u32, NodeRole::Target).into_ref(); + let b = Node::new(1u32, NodeRole::Target).into_ref(); + let c = Node::new(1u32, NodeRole::Target).into_ref(); + let e = Node::new(1u32, NodeRole::Target).into_ref(); + let f = Node::new(1u32, NodeRole::Target).into_ref(); + + Node::set_parent_of(&c, &b); + Node::set_parent_of(&b, &a); + Node::set_parent_of(&e, &a); + Node::set_parent_of(&a, &d); + + assert_eq!( + Node::root(&e), + (d.clone(), vec![e.clone(), a.clone(), d.clone()]), + ); + + assert_eq!( + Node::root(&a), + (d.clone(), vec![a.clone(), d.clone()]), + ); + + assert_eq!( + Node::root(&c), + (d.clone(), vec![c.clone(), b.clone(), a.clone(), d.clone()]), + ); + + // D A <-- B <-- C + // F <-- / \ + // <-- E + Node::set_parent_of(&a, &f); + + assert_eq!( + Node::root(&a), + (f.clone(), vec![a.clone(), f.clone()]), + ); + + assert_eq!( + Node::root(&c), + (d.clone(), vec![c.clone(), b.clone(), a.clone(), f.clone()]), + ); + } +} diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index b55d99633038d..16e8daf895aab 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -19,7 +19,7 @@ #![cfg(test)] use crate::mock::*; -use crate::{elect, PhragmenResult, Assignment}; +use crate::{elect, reduce, reduce_4, reduce_all, PhragmenResult, Assignment, StakedAssignment}; use substrate_test_utils::assert_eq_uvec; use sp_runtime::{Perbill, Saturating}; use codec::{Encode, Decode}; @@ -50,7 +50,7 @@ fn float_phragmen_poc_works() { ] ); - let mut support_map = build_support_map(&mut phragmen_result, &stake_of); + let mut support_map = build_support_map_float(&mut phragmen_result, &stake_of); assert_eq!( support_map.get(&2).unwrap(), @@ -523,3 +523,211 @@ fn basic_from_and_into_compact_works() { ); } + +#[test] +fn basic_reduce_4_cycle_works() { + let mut assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 25), + (20, 75), + ], + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 50), + (20, 50), + ], + }, + ]; + + let mut new_assignments = assignments.clone(); + let num_reduced = reduce_4(&mut new_assignments); + + assert_eq!(num_reduced, 1); + assert_eq!( + new_assignments, + vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (20, 100), + ], + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 75), + (20, 25), + ], + }, + ], + ); +} + + +#[test] +fn basic_reduce_all_cycles_works() { + let mut assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![(10, 10)] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 15), + (40, 15) + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (20, 10), + (30, 10), + (40, 20), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 20), + (30, 10), + (40, 20), + ], + }, + ]; + let winners = vec![10, 20, 30, 40]; + + assert_eq!(3, reduce_all(&mut assignments)); + + assert_eq!( + assignments, + vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 10), + ] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 30), + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (40, 40), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 15), + (30, 20), + (40, 15), + ], + }, + ], + ) +} + +#[test] +fn basic_reduce_works() { + let mut assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![(10, 10)] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 15), + (40, 15) + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (20, 10), + (30, 10), + (40, 20), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 20), + (30, 10), + (40, 20), + ], + }, + ]; + let winners = vec![10, 20, 30, 40]; + + assert_eq!(3, reduce_4(&mut assignments)); + + assert_eq!( + assignments, + vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 10), + ] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 30), + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (40, 40), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 15), + (30, 20), + (40, 15), + ], + }, + ], + ) +} From ca68e24d1879026a4c1b8f2123bd5caaef613f4b Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 13 Jan 2020 17:37:41 +0100 Subject: [PATCH 007/106] Everything phragmen related works again. --- frame/elections-phragmen/src/lib.rs | 12 ++++++---- frame/staking/src/lib.rs | 30 ++++++++++++++----------- frame/staking/src/tests.rs | 3 +++ frame/support/src/traits.rs | 2 +- primitives/phragmen/src/lib.rs | 34 +++++------------------------ primitives/phragmen/src/mock.rs | 21 +++++++++++++----- primitives/phragmen/src/node.rs | 4 ---- primitives/phragmen/src/tests.rs | 7 ++---- 8 files changed, 51 insertions(+), 62 deletions(-) diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 1d9573cbbf320..bf296082520a2 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -91,7 +91,7 @@ use frame_support::{ ChangeMembers, OnUnbalanced, WithdrawReason, Contains } }; -use sp_phragmen::ExtendedBalance; +use sp_phragmen::{build_support_map, ExtendedBalance, StakedAssignment}; use frame_system::{self as system, ensure_signed, ensure_root}; const MODULE_ID: LockIdentifier = *b"phrelect"; @@ -665,10 +665,14 @@ impl Module { .filter_map(|(m, a)| if a.is_zero() { None } else { Some(m) } ) .collect::>(); - let support_map = sp_phragmen::build_support_map::<_, _, _, T::CurrencyToVote>( + let staked_assignments: Vec> = phragmen_result.assignments + .into_iter() + .map(|a| a.into_staked::<_, T::CurrencyToVote, _>(Self::locked_stake_of)) + .collect(); + + let support_map = build_support_map::, T::AccountId>( &new_set, - &phragmen_result.assignments, - Self::locked_stake_of, + &staked_assignments, ); let to_balance = |e: ExtendedBalance| diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 2d233c7d3bb59..a96a2c513c03b 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -282,7 +282,7 @@ use sp_staking::{ use sp_runtime::{Serialize, Deserialize}; use frame_system::{self as system, ensure_signed, ensure_root, offchain::SubmitSignedTransaction}; -use sp_phragmen::{ExtendedBalance, Assignment}; +use sp_phragmen::{ExtendedBalance, Assignment, StakedAssignment}; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; // ------------- IMPORTANT NOTE: must be the same as `generate_compact_solution_type`. @@ -1717,13 +1717,17 @@ impl Module { .collect::>(); let assignments = phragmen_result.assignments; + let staked_assignments: Vec> = assignments + .into_iter() + .map(|a| a.into_staked::<_, T::CurrencyToVote, _>(Self::slashable_balance_of)) + .collect(); + let to_votes = |b: BalanceOf| , u64>>::convert(b) as ExtendedBalance; - let mut supports = sp_phragmen::build_support_map::<_, _, _, T::CurrencyToVote>( + let mut supports = sp_phragmen::build_support_map::, T::AccountId>( &elected_stashes, - &assignments, - Self::slashable_balance_of, + &staked_assignments, ); // Clear Stakers. @@ -1736,21 +1740,21 @@ impl Module { // collect exposures and slot stake. let mut slot_stake = BalanceOf::::max_value(); - let exposures = supports.into_iter().map(|(staker, support)| { + let exposures = supports.into_iter().map(|(voter, support)| { // build `struct exposure` from `support` let mut others = Vec::new(); let mut own: BalanceOf = Zero::zero(); let mut total: BalanceOf = Zero::zero(); - s.voters + support.voters .into_iter() - .map(|(who, value)| (who, to_balance(value))) - .for_each(|(who, value)| { - if who == c { - own = own.saturating_add(value); + .map(|(target, weight)| (target, to_balance(weight))) + .for_each(|(target, stake)| { + if target == voter { + own = own.saturating_add(stake); } else { - others.push(IndividualExposure { who, value }); + others.push(IndividualExposure { who: target, value: stake }); } - total = total.saturating_add(value); + total = total.saturating_add(stake); }); let exposure = Exposure { own, @@ -1765,7 +1769,7 @@ impl Module { if exposure.total < slot_stake { slot_stake = exposure.total; } - (staker, exposure) + (voter, exposure) }).collect::)>>(); // In order to keep the property required by `n_session_ending` diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 164826e965a28..dc7b5e1e39a0d 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2865,6 +2865,9 @@ fn is_current_session_final_works() { assert_eq!(Staking::current_era(), 2); assert_eq!(Staking::is_current_session_final(), false); }) +} + +#[test] fn slash_kicks_validators_not_nominators() { ExtBuilder::default().build().execute_with(|| { start_era(1); diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index bf8fe01258f7c..90606f3b50970 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -23,7 +23,7 @@ use codec::{FullCodec, Codec, Encode, Decode}; use sp_core::u32_trait::Value as U32; use sp_runtime::{ ConsensusEngineId, DispatchResult, DispatchError, - traits::{MaybeSerializeDeserialize, SimpleArithmetic, Saturating, TrailingZeroInput}, + traits::{MaybeSerializeDeserialize, SimpleArithmetic, Saturating, TrailingZeroInput, Bounded}, }; use crate::dispatch::Parameter; diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index da51729e25e06..93b7be16b72ba 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -115,12 +115,12 @@ pub struct Assignment { } impl Assignment { - fn into_staked(self, stake_of: FS) -> StakedAssignment + pub fn into_staked(self, stake_of: FS) -> StakedAssignment where - C: Convert, + C: Convert, for<'r> FS: Fn(&'r AccountId) -> Balance, { - let stake = C::convert(stake_of(&self.who)); + let stake = C::convert(stake_of(&self.who)) as ExtendedBalance; let distribution = self.distribution.into_iter().map(|(target, p)| { let distribution_stake = p * stake; (target, distribution_stake) @@ -487,7 +487,7 @@ type Map = std::collections::HashMap<(A, A), A>; fn reduce_4( assignments: &mut Vec>, ) -> u32 { - use std::collections::{HashMap, hash_map::{Entry::*}}; + use std::collections::{hash_map::{Entry::*}}; let mut combination_map: Map = Map::new(); let mut num_changed: u32 = Zero::zero(); @@ -644,29 +644,13 @@ fn reduce_all>, ) -> u32 { dbg!(&assignments); - use std::collections::{HashMap, hash_map::{Entry::*}}; - use std::cell::{Cell, RefCell}; - let mut change_buffer: HashMap> = HashMap::new(); + use std::collections::{HashMap}; let mut num_changed: u32 = Zero::zero(); // ----------------- Phase 2: remove any other cycle use node::{Node, NodeRef, NodeRole}; let mut tree: HashMap> = HashMap::new(); - // needless to say, this can be improved. TODO - let edge_weight_of = |voter: &AccountId, candidate: &AccountId| { - if let Some(a) = assignments.iter().find(|a| a.who == *voter) { - if let Some((_, w)) = a.distribution.iter().find(|(t, _)| t == candidate) { - Some(w) - } else { - None - } - } else { - None - } - }; - - // TODO: unify this terminology: we only have VOTER -> TARGET. no candidate. no staking terms. // a flat iterator of (voter, candidate) over all pairs of votes. Similar to reduce_4, we loop // without borrowing. @@ -675,10 +659,6 @@ fn reduce_all {:?}, Tree", voter, target); - for (key, value) in tree.iter() { - println!("{:?}", value.borrow()); - } let voter_node = tree.entry(voter.clone()) .or_insert(Node::new(voter.clone(), NodeRole::Voter).into_ref()).clone(); @@ -747,14 +727,12 @@ fn reduce_all>>(); - println!("cycle = {:?}", cycle.iter().map(|w| w.borrow().who.clone()).collect::>()); // TODO: a cycle struct that gives you min + to which chunk it belonged. // find minimum of cycle. let mut min_value: ExtendedBalance = Bounded::max_value(); // Note that this can only ever point to a target, not a voter. let mut min_who: AccountId = Default::default(); - let mut min_neighbor: AccountId = Default::default(); let mut min_index = 0usize; // 1 -> next // 0 -> prev TOOD: I have some ideas of fixing this. let mut min_direction = 0u32; @@ -790,9 +768,7 @@ fn reduce_all= target_chunk; dbg!(min_value, min_index, &min_who, min_direction); diff --git a/primitives/phragmen/src/mock.rs b/primitives/phragmen/src/mock.rs index ef6aaf3292b7e..40060d129da93 100644 --- a/primitives/phragmen/src/mock.rs +++ b/primitives/phragmen/src/mock.rs @@ -407,12 +407,7 @@ pub(crate) fn build_support_map_float( supports } - - -fn dummy_stake_of(who: &AccountId) -> Balance { - (*who * 100) as Balance -} - +#[allow(dead_code)] // to be used with fuzzing pub fn assert_assignments_equal( winners: &Vec, ass1: &Vec>, @@ -427,3 +422,17 @@ pub fn assert_assignments_equal( } } + +#[allow(dead_code)] // to be used with fuzzing +pub fn reduce_and_compare( + assignment: &Vec>, + winners: &Vec, +) { + let mut altered_assignment = assignment.clone(); + crate::reduce(&mut altered_assignment); + assert_assignments_equal( + winners, + &assignment, + &altered_assignment, + ); +} diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index d728607e0ca25..beadd8a16a70b 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -36,10 +36,6 @@ impl Node { self.parent.is_some() } - pub fn set_parent(&mut self, parent: &NodeRef) { - self.parent = Some(parent.clone()); - } - pub fn set_parent_of(target: &NodeRef, parent: &NodeRef) { target.borrow_mut().parent = Some(parent.clone()); } diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index d66f4ac608af8..fea3c60a9ce81 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -529,7 +529,7 @@ fn basic_from_and_into_compact_works() { #[test] fn basic_reduce_4_cycle_works() { - let mut assignments = vec![ + let assignments = vec![ StakedAssignment { who: 1, distribution: vec![ @@ -570,7 +570,6 @@ fn basic_reduce_4_cycle_works() { ); } - #[test] fn basic_reduce_all_cycles_works() { let mut assignments = vec![ @@ -609,7 +608,6 @@ fn basic_reduce_all_cycles_works() { ], }, ]; - let winners = vec![10, 20, 30, 40]; assert_eq!(3, reduce_all(&mut assignments)); @@ -691,9 +689,8 @@ fn basic_reduce_works() { ], }, ]; - let winners = vec![10, 20, 30, 40]; - assert_eq!(3, reduce_4(&mut assignments)); + assert_eq!(3, reduce(&mut assignments)); assert_eq!( assignments, From 62e61dbd6468c37acf126a4caddba2cd689bd3ff Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 14 Jan 2020 14:29:43 +0100 Subject: [PATCH 008/106] Signing made easier, still issues. --- Cargo.lock | 1 + frame/staking/Cargo.toml | 1 + frame/staking/src/lib.rs | 48 +++++++++++++++++------------ frame/staking/src/mock.rs | 60 ++++++++++++++++++++++++------------ frame/system/src/offchain.rs | 2 +- 5 files changed, 71 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 606e0a5c8b63f..a537030ca1c2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3956,6 +3956,7 @@ dependencies = [ "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-application-crypto 2.0.0", "sp-core 2.0.0", "sp-io 2.0.0", "sp-keyring 2.0.0", diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index d131a03bd2566..11f27ec75c17a 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -26,6 +26,7 @@ pallet-balances = { version = "2.0.0", path = "../balances" } 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" } +sp-application-crypto = { version = "2.0.0", path = "../../primitives/application-crypto" } [features] migrate = [] diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index a96a2c513c03b..084f65c80c8a8 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -280,7 +280,10 @@ use sp_staking::{ }; #[cfg(feature = "std")] use sp_runtime::{Serialize, Deserialize}; -use frame_system::{self as system, ensure_signed, ensure_root, offchain::SubmitSignedTransaction}; +use frame_system::{ + self as system, ensure_signed, ensure_root, + offchain::{SignAndSubmitTransaction, SubmitSignedTransaction}, +}; use sp_phragmen::{ExtendedBalance, Assignment, StakedAssignment}; @@ -706,8 +709,7 @@ pub trait Trait: frame_system::Trait { type Call: From>; /// A transaction submitter. - // type SubmitTransaction: SubmitSignedTransaction::Call>; - type SubmitTransaction; + type SubmitTransaction: SignAndSubmitTransaction::Call> + SubmitSignedTransaction::Call>; } /// Mode of era-forcing. @@ -957,25 +959,31 @@ decl_module! { if sp_io::offchain::is_validator() { if Self::election_status() == OffchainElectionStatus::::Triggered(now) { let era = CurrentEra::get(); - if let Some(election_result) = Self::do_phragmen(ElectionCompute::Submitted) { - if let Some(key) = Self::local_signing_key() { - if let Some(sp_phragmen::PhragmenResult { - winners, - assignments, - }) = Self::do_phragmen(ElectionCompute::Submitted) { - let winners = winners.into_iter().map(|(w, _)| w).collect(); - let compact_assignments: CompactAssignments = assignments.into(); - let call: ::Call = Call::submit_election_result( - winners, - compact_assignments, - ).into(); - // T::SubmitTransaction::sign_and_submit(call, key); - } else { - frame_support::print("ran phragmen offchain but None was returned."); - } + // get all local keys that are among the current elected stakers. + let local_keys = T::SubmitTransaction::find_local_keys(Some(Self::current_elected())); + if local_keys.len() > 0 { + // run phragmen + if let Some(sp_phragmen::PhragmenResult { + winners, + assignments, + }) = Self::do_phragmen(ElectionCompute::Submitted) { + let winners = winners.into_iter().map(|(w, _)| w).collect(); + // reduce the assignments. This will remove some additional edges. + // let reduced_assignments = sp_phragmen::reduce(&mut assignments); + // compact encode the assignment. + // let reduced_compact_assignments: CompactAssignments = assignments.into(); + let compact_assignments: CompactAssignments = assignments.into(); + + let call: ::Call = Call::submit_election_result(winners, compact_assignments).into(); + + // TODO: we could allow offchain submitter to handler this as well. + // NOTE: uncomment this and rustc dies. + // ::Call>>::sign_and_submit(call, local_keys[0].1.into()); } else { - frame_support::print("validator with not signing key."); + frame_support::print("ran phragmen offchain, but None was returned."); } + } else { + frame_support::print("Have no key to sign this with"); } } else { frame_support::print("validator did not start offchain election."); diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index e851841539a3b..c8cf849ae7cdc 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -32,6 +32,9 @@ use frame_support::{ traits::{Currency, Get, FindAuthor, PredictNextSessionChange}, weights::Weight, }; +use frame_system::offchain::{ + TransactionSubmitter, SignAndSubmitTransaction, Signer, CreateTransaction, +}; use crate::{ EraIndex, GenesisConfig, Module, Trait, StakerStatus, ValidatorPrefs, RewardDestination, Nominators, inflation @@ -283,28 +286,45 @@ impl Trait for Test { type SessionInterface = Self; type RewardCurve = RewardCurve; type NextSessionChange = PeriodicSessionChange; - type SigningKeyType = UintAuthorityId; + type SigningKeyType = dummy_sr25519::AuthorityId; type ElectionLookahead = ElectionLookahead; type Call = Call; - type SubmitTransaction = (); -} - -// impl frame_system::offchain::CreateTransaction for Test { -// type Signature = ::Signature; -// type Public = <::Pair as sp_core::crypto::Pair>::Public; -// fn create_transaction>( -// call: Call, -// public: Self::Public, -// account: AccountId, -// _index: AccountIndex, -// ) -> Option<(::Call, ::SignaturePayload)> { -// let extra = (); -// Some((call, (account, extra))) -// } -// } - -// pub type Extrinsic = TestXt; -// type SubmitTransaction = frame_system::offchain::TransactionSubmitter<(), Call, Extrinsic>; + type SubmitTransaction = SubmitTransaction; +} + +pub mod dummy_sr25519 { + mod app_sr25519 { + use sp_application_crypto::{app_crypto, key_types::DUMMY, sr25519}; + app_crypto!(sr25519, DUMMY); + } + + #[cfg(feature = "std")] + pub type AuthorityPair = app_sr25519::Pair; + pub type AuthoritySignature = app_sr25519::Signature; + pub type AuthorityId = app_sr25519::Public; + + impl sp_runtime::traits::IdentifyAccount for AuthorityId { + type AccountId = u64; + fn into_account(self) -> Self::AccountId { 11u64 } + } +} + +impl CreateTransaction for Test { + type Signature = dummy_sr25519::AuthoritySignature; + type Public = dummy_sr25519::AuthorityId; + fn create_transaction>( + call: Call, + public: Self::Public, + account: AccountId, + _index: AccountIndex, + ) -> Option<(::Call, ::SignaturePayload)> { + let extra = (); + Some((call, (account, extra))) + } +} + +pub type Extrinsic = TestXt; +type SubmitTransaction = TransactionSubmitter; pub struct ExtBuilder { session_length: BlockNumber, diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index f5fda34585d4d..0ccd60a320078 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -68,7 +68,7 @@ pub trait Signer { /// A `Signer` implementation for any `AppPublic` type. /// -/// This implementation additionaly supports conversion to/from multi-signature/multi-signer +/// This implementation additionally supports conversion to/from multi-signature/multi-signer /// wrappers. /// If the wrapped crypto doesn't match `AppPublic`s crypto `None` is returned. impl Signer for TAnyAppPublic where From c4a77ac8c8bded224d9bba281bbb42757aec3f83 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 15 Jan 2020 08:09:27 +0100 Subject: [PATCH 009/106] =?UTF-8?q?Signing=20from=20offchain=20compile=20f?= =?UTF-8?q?ine=20=F0=9F=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/node/runtime/src/lib.rs | 11 ++++----- frame/staking/src/lib.rs | 48 +++++++++---------------------------- frame/staking/src/mock.rs | 1 - 3 files changed, 16 insertions(+), 44 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 9fd5359ee5641..32c2bac017773 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -71,6 +71,8 @@ use constants::{time::*, currency::*}; #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +pub type TransactionSubmitterOf = TransactionSubmitter; + /// Runtime version. pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node"), @@ -448,9 +450,6 @@ impl pallet_sudo::Trait for Runtime { type Proposal = Call; } -/// A runtime transaction submitter. -pub type SubmitTransaction = TransactionSubmitter; - parameter_types! { // assume 1 slot == 1 block pub const SessionDuration: BlockNumber = EPOCH_DURATION_IN_SLOTS as _; @@ -460,7 +459,7 @@ impl pallet_im_online::Trait for Runtime { type AuthorityId = ImOnlineId; type Call = Call; type Event = Event; - type SubmitTransaction = SubmitTransaction; + type SubmitTransaction = TransactionSubmitterOf; type ReportUnresponsiveness = Offences; type SessionDuration = SessionDuration; } @@ -779,8 +778,8 @@ mod tests { >, {} - is_submit_signed_transaction::(); - is_sign_and_submit_transaction::(); + is_submit_signed_transaction::>(); + is_sign_and_submit_transaction::>(); } #[test] diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 084f65c80c8a8..6986e4d704ec1 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -701,12 +701,9 @@ pub trait Trait: frame_system::Trait { /// zero will disable the offchain compute and only on-chain seq-phragmen will be used. type ElectionLookahead: Get; - /// A (potentially unknown) key type used to sign the transactions. - type SigningKeyType: RuntimeAppPublic + Clone + /*IdentifyAccount*/; - /// The overarching call type. // TODO: This is needed just to bound it to `From>`. Otherwise could have `Self as system` - type Call: From>; + type Call: From> + Clone; /// A transaction submitter. type SubmitTransaction: SignAndSubmitTransaction::Call> + SubmitSignedTransaction::Call>; @@ -958,11 +955,12 @@ decl_module! { // TODO: add runtime logging. if sp_io::offchain::is_validator() { if Self::election_status() == OffchainElectionStatus::::Triggered(now) { - let era = CurrentEra::get(); // get all local keys that are among the current elected stakers. - let local_keys = T::SubmitTransaction::find_local_keys(Some(Self::current_elected())); - if local_keys.len() > 0 { - // run phragmen + // TODO: well this is outdated, neh? + let current_elected = Self::current_elected(); + if T::SubmitTransaction::can_sign_with(Some(current_elected.clone())) { + // We have at least some local key which corresponds to an elected + // validator. run phragmen if let Some(sp_phragmen::PhragmenResult { winners, assignments, @@ -975,10 +973,8 @@ decl_module! { let compact_assignments: CompactAssignments = assignments.into(); let call: ::Call = Call::submit_election_result(winners, compact_assignments).into(); - - // TODO: we could allow offchain submitter to handler this as well. - // NOTE: uncomment this and rustc dies. - // ::Call>>::sign_and_submit(call, local_keys[0].1.into()); + // TODO: maybe we want to send from just one of them? we'll see. + T::SubmitTransaction::submit_signed_from(call, current_elected); } else { frame_support::print("ran phragmen offchain, but None was returned."); } @@ -1418,29 +1414,6 @@ impl Module { Self::bonded(stash).and_then(Self::ledger).map(|l| l.active).unwrap_or_default() } - /// Make sure that the account corresponding with the given account-id is a validator. - // TODO: needs a storage item to keep the most recent validators. ATM this is WRONG. - pub fn is_current_validator(who: &T::AccountId) -> bool { - Self::current_elected().contains(who) - } - - /// Find a local `AccountId` we can sign with. - /// TODO: this needs a way to ge from either of account_id <-> public_key. Linked to the - /// transaction signing stuff - fn local_signing_key() -> Option { - // Find all local keys accessible to this app through the localised KeyType. Then go through - // all keys currently stored on chain and check them against the list of local keys until a - // match is found, otherwise return `None`. - let local_keys = T::SigningKeyType::all().iter().map(|i| - (*i).clone() - ).collect::>(); - - // TODO: this is WRONG. current elected is not accurate and is one behind - Self::current_elected().into_iter().find_map(|v| { - Some(v) - }) - } - /// returns true if the end of the current session leads to an era change. fn is_current_session_final() -> bool { let current_index = T::SessionInterface::current_index(); @@ -1714,7 +1687,8 @@ impl Module { somehow_phragmen_results } - /// Execute phragmen and return the new results. The edge weights are processed into support values. + /// Execute phragmen and return the new results. The edge weights are processed into support + /// values. /// /// No storage item is updated. fn do_phragmen_with_post_processing(compute: ElectionCompute) -> Option>> { @@ -1733,7 +1707,7 @@ impl Module { let to_votes = |b: BalanceOf| , u64>>::convert(b) as ExtendedBalance; - let mut supports = sp_phragmen::build_support_map::, T::AccountId>( + let supports = sp_phragmen::build_support_map::, T::AccountId>( &elected_stashes, &staked_assignments, ); diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index c8cf849ae7cdc..d3e580fa9faba 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -286,7 +286,6 @@ impl Trait for Test { type SessionInterface = Self; type RewardCurve = RewardCurve; type NextSessionChange = PeriodicSessionChange; - type SigningKeyType = dummy_sr25519::AuthorityId; type ElectionLookahead = ElectionLookahead; type Call = Call; type SubmitTransaction = SubmitTransaction; From 31b699aa4cb52cbb9ed54241d7c7398a8d9bef2c Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 15 Jan 2020 10:18:05 +0100 Subject: [PATCH 010/106] make compact work with staked asssignment --- primitives/phragmen/compact/src/lib.rs | 173 +++++++++++++++++++++++-- primitives/phragmen/src/tests.rs | 79 +++++++++-- 2 files changed, 232 insertions(+), 20 deletions(-) diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index 33e992cdd42a2..770c5983d6706 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -51,11 +51,14 @@ fn field_name_for(n: usize) -> Ident { /// /// The generated structure creates one key for each possible count of distributions from 1 up to /// the given length. A normal distribution is a tuple of `(candidate, weight)` where `candidate` is -/// a generic `AccountId` and `weight` is a `Perbill`. The following rules hold regarding the -/// compact representation: +/// a generic `AccountId` and `weight` is an encodable `T`. Typically, the weight can refer to +/// either the ratio of the voter's support or its absolute value. The following rules hold +/// regarding the compact representation: /// - For single distribution, no weight is stored. The weight is known to be 100%. /// - For all the rest, the weight if the last distribution is omitted. This value can be computed /// from the rest. +/// +/// An example expansion of length 16 is as follows: #[proc_macro] pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { let CompactSolutionDef { @@ -64,6 +67,7 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { count, } = syn::parse_macro_input!(item as CompactSolutionDef); let account_type = quote!(AccountId); + let weight = quote!(W); if count <= 2 { panic!("cannot build compact solution struct with capacity less than 2."); @@ -75,7 +79,7 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { }; let doubles = { let name = field_name_for(2); - quote!(#name: Vec<(#account_type, (#account_type, Perbill), #account_type)>,) + quote!(#name: Vec<(#account_type, (#account_type, #weight), #account_type)>,) }; let rest = (3..=count).map(|c| { let field_name = field_name_for(c); @@ -83,7 +87,7 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { quote!( #field_name: Vec<( #account_type, - [(#account_type, sp_runtime::Perbill); #array_len], + [(#account_type, #weight); #array_len], #account_type )>, ) @@ -92,13 +96,38 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { let compact_def = quote! ( // TODO: clean imports: how to deal with codec? #[derive(Default, PartialEq, Eq, Clone, sp_runtime::RuntimeDebug, codec::Encode, codec::Decode)] - #vis struct #ident<#account_type> { + #vis struct #ident<#account_type, #weight> { #singles #doubles #rest } ); + // TODO: we can remove this entirely and make the staked impl a bit more generic. A perbill's max value is always ::one. + let from_into_impl_assignment = convert_impl_for_assignment( + ident.clone(), + account_type.clone(), + count, + ); + let from_into_impl_staked_assignment = convert_impl_for_staked_assignment( + ident, + account_type, + count, + ); + + + quote!( + #compact_def + #from_into_impl_assignment + #from_into_impl_staked_assignment + ).into() +} + +fn convert_impl_for_assignment( + ident: syn::Ident, + account_type: TokenStream2, + count: usize +) -> TokenStream2 { let from_impl_single = { let name = field_name_for(1); quote!(1 => compact.#name.push((who, distribution[0].clone().0)),) @@ -122,12 +151,12 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { let from_impl = quote!( impl<#account_type: codec::Codec + Default + Clone> From>> - for #ident<#account_type> + for #ident<#account_type, Perbill> { fn from( assignments: Vec>, ) -> Self { - let mut compact: #ident<#account_type> = Default::default(); + let mut compact: #ident<#account_type, Perbill> = Default::default(); assignments.into_iter().for_each(|Assignment { who, distribution } | { match distribution.len() { #from_impl_single @@ -195,10 +224,10 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { let into_impl = quote!( impl<#account_type: codec::Codec + Default + Clone> Into>> - for #ident<#account_type> + for #ident<#account_type, Perbill> { fn into(self) -> Vec> { - let mut assignments: Vec> = Default::default(); + let mut assignments: Vec> = Default::default(); #into_impl_single #into_impl_double #into_impl_rest @@ -209,8 +238,130 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { ); quote!( - #compact_def #from_impl #into_impl - ).into() + ) +} + +fn convert_impl_for_staked_assignment( + ident: syn::Ident, + account_type: TokenStream2, + count: usize +) -> TokenStream2 { + let from_impl_single = { + let name = field_name_for(1); + quote!(1 => compact.#name.push((who, distribution[0].clone().0)),) + }; + let from_impl_double = { + let name = field_name_for(2); + quote!(2 => compact.#name.push( + ( + who, + distribution[0].clone(), + distribution[1].clone().0, + ) + ),) + }; + let from_impl_rest = (3..=count).map(|c| { + let inner = (0..c-1).map(|i| quote!(distribution[#i].clone(),)).collect::(); + + let field_name = field_name_for(c); + let last_index = c - 1; + let last = quote!(distribution[#last_index].clone().0); + + quote!( + #c => compact.#field_name.push((who, [#inner], #last)), + ) + }).collect::(); + + let into_impl_single = { + let name = field_name_for(1); + quote!( + for (who, target) in self.#name { + let all_stake = C::convert(max_of(&who)); + assignments.push(StakedAssignment { + who, + distribution: vec![(target, all_stake)], + }) + } + ) + }; + let into_impl_double = { + let name = field_name_for(2); + quote!( + for (who, (t1, w1), t2) in self.#name { + let all_stake = C::convert(max_of(&who)); + let w2 = all_stake.saturating_sub(w1); + assignments.push( StakedAssignment { + who, + distribution: vec![ + (t1, w1), + (t2, w2), + ] + }); + } + ) + }; + let into_impl_rest = (3..=count).map(|c| { + let name = field_name_for(c); + quote!( + for (who, inners, t_last) in self.#name { + let mut sum = u128::min_value(); + let all_stake = C::convert(max_of(&who)); + let mut inners_parsed = inners + .into_iter() + .map(|(ref c, w)| { + sum = sum.saturating_add(*w); + (c.clone(), *w) + }).collect::>(); + + let w_last = all_stake.saturating_sub(sum); + inners_parsed.push((t_last, w_last)); + + assignments.push(StakedAssignment { + who, + distribution: inners_parsed, + }); + } + ) + }).collect::(); + + let final_impl = quote!( + impl<#account_type: codec::Codec + Default + Clone> + #ident<#account_type, u128> + { + fn into_compact_staked(self, max_of: FM) + -> Vec> + where + for<'r> FM: Fn(&'r #account_type) -> Balance, + C: Convert + { + let mut assignments: Vec> = Default::default(); + #into_impl_single + #into_impl_double + #into_impl_rest + + assignments + } + + fn from_staked(assignments: Vec>) -> Self { + let mut compact: #ident<#account_type, u128> = Default::default(); + assignments.into_iter().for_each(|StakedAssignment { who, distribution } | { + match distribution.len() { + #from_impl_single + #from_impl_double + #from_impl_rest + _ => { + sp_runtime::print("staked assignment length too big. ignoring"); + } + } + }); + compact + } + } + ); + + quote!( + #final_impl + ) } diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index fea3c60a9ce81..6e1f5d4312037 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -24,7 +24,7 @@ use crate::{ Support, StakedAssignment, Assignment, PhragmenResult, }; use substrate_test_utils::assert_eq_uvec; -use sp_runtime::{Perbill, Saturating}; +use sp_runtime::{Perbill, Saturating, traits::Convert}; use codec::{Encode, Decode}; use sp_phragmen_compact::generate_compact_solution_type; @@ -469,32 +469,32 @@ fn compact_struct_is_codec() { } #[test] -fn basic_from_and_into_compact_works() { +fn basic_from_and_into_compact_works_assignments() { let assignments = vec![ Assignment { - who: 2u32, + who: 2u64, distribution: vec![(20, Perbill::from_percent(100))] }, Assignment { - who: 4u32, + who: 4u64, distribution: vec![(40, Perbill::from_percent(100))], }, Assignment { - who: 1u32, + who: 1u64, distribution: vec![ (10, Perbill::from_percent(80)), (11, Perbill::from_percent(20)) ], }, Assignment { - who: 5u32, distribution: + who: 5u64, distribution: vec![ (50, Perbill::from_percent(85)), (51, Perbill::from_percent(15)), ] }, Assignment { - who: 3u32, + who: 3u64, distribution: vec![ (30, Perbill::from_percent(50)), (31, Perbill::from_percent(25)), @@ -503,7 +503,7 @@ fn basic_from_and_into_compact_works() { }, ]; - let compacted: TestCompact = assignments.clone().into(); + let compacted: TestCompact = assignments.clone().into(); assert_eq!( compacted, @@ -521,7 +521,68 @@ fn basic_from_and_into_compact_works() { ); assert_eq!( - as Into>>>::into(compacted), + as Into>>>::into(compacted), + assignments + ); +} + +#[test] +fn basic_from_and_into_compact_works_staked_assignments() { + let assignments = vec![ + StakedAssignment { + who: 2u64, + distribution: vec![(20, 100u128)] + }, + StakedAssignment { + who: 4u64, + distribution: vec![(40, 100)], + }, + StakedAssignment { + who: 1u64, + distribution: vec![ + (10, 80), + (11, 20) + ], + }, + StakedAssignment { + who: 5u64, distribution: + vec![ + (50, 85), + (51, 15), + ] + }, + StakedAssignment { + who: 3u64, + distribution: vec![ + (30, 50), + (31, 25), + (32, 25), + ], + }, + ]; + + let compacted = >::from_staked(assignments.clone()); + + assert_eq!( + compacted, + TestCompact { + votes1: vec![(2, 20), (4, 40)], + votes2: vec![ + (1, (10, 80), 11), + (5, (50, 85), 51), + ], + votes3: vec![ + (3, [(30, 50), (31, 25)], 32), + ], + ..Default::default() + } + ); + + let max_of_fn = |a: &AccountId| -> Balance { 100u128 }; + let max_of: Box Balance> = Box::new(max_of_fn); + + assert_eq!( + compacted.into_compact_staked::(&max_of), assignments ); } From c5d8c513aaf2dee9b7f2aa4f9f3833aa5e6b5417 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 15 Jan 2020 15:29:57 +0100 Subject: [PATCH 011/106] Evaluation basics are in place. --- Cargo.lock | 76 ++++++------ frame/staking/src/lib.rs | 158 ++++++++++++++++++++----- frame/staking/src/mock.rs | 10 +- primitives/phragmen/Cargo.toml | 4 +- primitives/phragmen/compact/src/lib.rs | 10 +- primitives/phragmen/src/lib.rs | 60 +++++++--- primitives/phragmen/src/mock.rs | 4 +- primitives/phragmen/src/tests.rs | 6 +- 8 files changed, 227 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a537030ca1c2c..41ded5a5f41bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -383,7 +383,7 @@ dependencies = [ "libp2p 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sc-chain-spec 2.0.0", "sc-network 0.8.0", "sc-service 2.0.0", @@ -520,7 +520,7 @@ version = "2.0.0" dependencies = [ "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "node-cli 2.0.0", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sc-keystore 2.0.0", "sp-core 2.0.0", "structopt 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -618,7 +618,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1035,7 +1035,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1249,7 +1249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1736,7 +1736,7 @@ dependencies = [ "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "grafana-data-source 2.0.0", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2411,7 +2411,7 @@ dependencies = [ "pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", "rw-stream-sink 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2464,7 +2464,7 @@ dependencies = [ "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-swarm 0.4.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2503,7 +2503,7 @@ dependencies = [ "parity-multiaddr 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multihash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2561,7 +2561,7 @@ dependencies = [ "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", "snow 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2579,7 +2579,7 @@ dependencies = [ "libp2p-swarm 0.4.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-multiaddr 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2616,7 +2616,7 @@ dependencies = [ "parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "quicksink 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", "rw-stream-sink 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2732,7 +2732,7 @@ dependencies = [ "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3043,7 +3043,7 @@ dependencies = [ "pallet-timestamp 2.0.0", "pallet-transaction-payment 2.0.0", "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sc-authority-discovery 2.0.0", "sc-basic-authority 2.0.0", "sc-chain-spec 2.0.0", @@ -4145,7 +4145,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4554,7 +4554,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4625,7 +4625,7 @@ dependencies = [ [[package]] name = "rand" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5004,7 +5004,7 @@ dependencies = [ "prost 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "prost-build 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "quickcheck 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sc-client-api 2.0.0", "sc-keystore 2.0.0", "sc-network 0.8.0", @@ -5270,7 +5270,7 @@ dependencies = [ "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sc-block-builder 2.0.0", "sc-client 2.0.0", "sc-client-api 2.0.0", @@ -5454,7 +5454,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sc-client 2.0.0", "sc-client-api 2.0.0", "sc-keystore 2.0.0", @@ -5486,7 +5486,7 @@ dependencies = [ "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", "sp-application-crypto 2.0.0", "sp-core 2.0.0", @@ -5517,7 +5517,7 @@ dependencies = [ "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "quickcheck 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "sc-block-builder 2.0.0", "sc-client 2.0.0", @@ -5570,7 +5570,7 @@ dependencies = [ "libp2p 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sc-block-builder 2.0.0", "sc-client 2.0.0", "sc-client-api 2.0.0", @@ -5602,7 +5602,7 @@ dependencies = [ "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sc-client-api 2.0.0", "sc-client-db 2.0.0", "sc-keystore 2.0.0", @@ -5625,7 +5625,7 @@ dependencies = [ "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5805,7 +5805,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -6144,7 +6144,7 @@ dependencies = [ "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -6230,7 +6230,7 @@ dependencies = [ "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "sp-debug-derive 2.0.0", "sp-std 2.0.0", @@ -6364,7 +6364,7 @@ dependencies = [ "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -6482,7 +6482,7 @@ name = "sp-phragmen" version = "2.0.0" dependencies = [ "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "sp-io 2.0.0", "sp-phragmen-compact 2.0.0", @@ -6519,7 +6519,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", "sp-application-crypto 2.0.0", @@ -6633,7 +6633,7 @@ dependencies = [ "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0", "sp-externalities 2.0.0", "sp-panic-handler 2.0.0", @@ -6830,7 +6830,7 @@ dependencies = [ "pallet-balances 2.0.0", "pallet-transaction-payment 2.0.0", "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rpassword 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "sc-rpc 2.0.0", @@ -7077,7 +7077,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -7564,7 +7564,7 @@ name = "twox-hash" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -8135,7 +8135,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", "mio-extras 2.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -8175,7 +8175,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "nohash-hasher 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -8595,7 +8595,7 @@ dependencies = [ "checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 6986e4d704ec1..4611393c38f01 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -267,7 +267,7 @@ use frame_support::{ }; use pallet_session::{historical, SelectInitialValidators}; use sp_runtime::{ - Perbill, RuntimeDebug, RuntimeAppPublic, + Perbill, RuntimeDebug, curve::PiecewiseLinear, traits::{ Convert, Zero, One, StaticLookup, CheckedSub, Saturating, Bounded, SaturatedConversion, @@ -291,8 +291,6 @@ const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; // ------------- IMPORTANT NOTE: must be the same as `generate_compact_solution_type`. const MAX_NOMINATIONS: usize = 16; const MAX_UNLOCKING_CHUNKS: usize = 32; -const ELECTION_TOLERANCE: ExtendedBalance = 0; -const ELECTION_ITERATIONS: usize = 2; const STAKING_ID: LockIdentifier = *b"staking "; /// Counter for the number of eras that have passed. @@ -561,8 +559,6 @@ pub enum ElectionCompute { /// The result of an election round. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub struct ElectionResult { - /// Era at which this election has happened. - era: EraIndex, /// Type of the result, compute: ElectionCompute, /// The new computed slot stake. @@ -571,6 +567,8 @@ pub struct ElectionResult { elected_stashes: Vec, /// Flat list of new exposures, to be updated in the [`Exposure`] storage. exposures: Vec<(AccountId, Exposure)>, + /// The score of the result based on [`sp_phragmen::evaluate_support`]. + score: [ExtendedBalance; 3], } /// The status of the upcoming (offchain) election. @@ -910,6 +908,8 @@ decl_error! { NoMoreChunks, /// Can not rebond without unlocking chunks. NoUnlockChunk, + /// The submitted result is not as good as the one stored on chain. + WeakPhragmenResult } } @@ -964,17 +964,24 @@ decl_module! { if let Some(sp_phragmen::PhragmenResult { winners, assignments, - }) = Self::do_phragmen(ElectionCompute::Submitted) { + }) = Self::do_phragmen() { let winners = winners.into_iter().map(|(w, _)| w).collect(); + + // convert into staked. This is needed to be able to reduce. + let mut staked: Vec> = assignments + .into_iter() + .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::slashable_balance_of)) + .collect(); + // reduce the assignments. This will remove some additional edges. - // let reduced_assignments = sp_phragmen::reduce(&mut assignments); + sp_phragmen::reduce(&mut staked); + // compact encode the assignment. - // let reduced_compact_assignments: CompactAssignments = assignments.into(); - let compact_assignments: CompactAssignments = assignments.into(); + let compact = >::from_staked(staked); - let call: ::Call = Call::submit_election_result(winners, compact_assignments).into(); + let call: ::Call = Call::submit_election_result(winners, compact).into(); // TODO: maybe we want to send from just one of them? we'll see. - T::SubmitTransaction::submit_signed_from(call, current_elected); + let _result = T::SubmitTransaction::submit_signed_from(call, current_elected); } else { frame_support::print("ran phragmen offchain, but None was returned."); } @@ -997,10 +1004,104 @@ decl_module! { fn submit_election_result( origin, winners: Vec, - assignments: CompactAssignments, + compact_assignments: CompactAssignments, ) { - let who = ensure_signed(origin)?; - // TODO: nothing for now, needs encoding. + let _who = ensure_signed(origin)?; + + // convert into staked. + let staked_assignments = compact_assignments.into_staked::< + _, + _, + T::CurrencyToVote, + >(Self::slashable_balance_of); + + // build the support map thereof in order to evaluate. + // OPTIMIZATION: we could merge this with the `staked_assignments.iter()` below and + // iterate only once. + let (supports, num_error) = sp_phragmen::build_support_map::, T::AccountId>( + &winners, + &staked_assignments, + ); + + ensure!(num_error == 0, "bogus edge pointed to a non-winner in assignments."); + + // score the result. Go further only if it is better than what we already have. + let submitted_score = sp_phragmen::evaluate_support(&supports); + if let Some(ElectionResult::> { + score, + .. + }) = Self::queued_elected() { + // if the local score is better in any of the three parameters. + if + submitted_score.iter().enumerate().take(2).any(|(i, s)| score[i] > *s) || + score[2] < submitted_score[2] + { + Err(Error::::WeakPhragmenResult)? + } + } + + // Either the result is better than the one on chain, or we don't have an any on chain. + // do the sanity check. Each claimed edge must exist. Also, each claimed winner must + // have a support associated. + + // check all winners being actual validators. + for w in winners.iter() { + ensure!( + Self::bonded(&w).is_some(), + "presented winner is not a validator candidate", + ) + } + + // check all nominators being bonded, and actually including the claimed vote. + for StakedAssignment { who, distribution } in staked_assignments.iter() { + let maybe_nomination = Self::nominators(&who); + ensure!( + maybe_nomination.is_some(), + "presented nominator is invalid", + ); + let nomination = maybe_nomination.expect("value is checked to be 'Some'"); + ensure!( + distribution.into_iter().all(|(v, _)| nomination.targets.iter().find(|t| *t == v).is_some()), + "assignment contains an edge from non-nominator", + ); + } + + // Endlich alles Ok. Exposures and store the result. + let to_balance = |e: ExtendedBalance| + >>::convert(e); + let mut slot_stake: BalanceOf = Bounded::max_value(); + + let exposures = supports.into_iter().map(|(validator, support)| { + let mut others = Vec::new(); + let mut own: BalanceOf = Zero::zero(); + let mut total: BalanceOf = Zero::zero(); + support.voters + .into_iter() + .map(|(target, weight)| (target, to_balance(weight))) + .for_each(|(nominator, stake)| { + if nominator == validator { + own = own.saturating_add(stake); + } else { + others.push(IndividualExposure { who: nominator, value: stake }); + } + total = total.saturating_add(stake); + }); + let exposure = Exposure { own, others, total }; + + if exposure.total < slot_stake { + slot_stake = exposure.total; + } + (validator, exposure) + }).collect::)>>(); + + >::put(ElectionResult { + compute: ElectionCompute::Submitted, + elected_stashes: winners, + score: submitted_score, + exposures, + slot_stake, + + }); } /// Take the origin account as a stash and lock up `value` of its balance. `controller` will @@ -1636,9 +1737,9 @@ impl Module { if let Some(ElectionResult::> { elected_stashes, exposures, - era, slot_stake, compute, + .. // TODO: this means that we are never storing the score of a winning chain. It this okay? }) = Self::try_do_phragmen() { // Clear Stakers. for v in Self::current_elected().iter() { @@ -1692,8 +1793,7 @@ impl Module { /// /// No storage item is updated. fn do_phragmen_with_post_processing(compute: ElectionCompute) -> Option>> { - let era = Self::current_era(); - if let Some(phragmen_result) = Self::do_phragmen(compute) { + if let Some(phragmen_result) = Self::do_phragmen() { let elected_stashes = phragmen_result.winners.iter() .map(|(s, _)| s.clone()) .collect::>(); @@ -1701,13 +1801,10 @@ impl Module { let staked_assignments: Vec> = assignments .into_iter() - .map(|a| a.into_staked::<_, T::CurrencyToVote, _>(Self::slashable_balance_of)) + .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::slashable_balance_of)) .collect(); - let to_votes = |b: BalanceOf| - , u64>>::convert(b) as ExtendedBalance; - - let supports = sp_phragmen::build_support_map::, T::AccountId>( + let (supports, _) = sp_phragmen::build_support_map::, T::AccountId>( &elected_stashes, &staked_assignments, ); @@ -1717,24 +1814,25 @@ impl Module { >::remove(v); } + let score = sp_phragmen::evaluate_support(&supports); let to_balance = |e: ExtendedBalance| >>::convert(e); // collect exposures and slot stake. let mut slot_stake = BalanceOf::::max_value(); - let exposures = supports.into_iter().map(|(voter, support)| { + let exposures = supports.into_iter().map(|(validator, support)| { // build `struct exposure` from `support` let mut others = Vec::new(); let mut own: BalanceOf = Zero::zero(); let mut total: BalanceOf = Zero::zero(); support.voters .into_iter() - .map(|(target, weight)| (target, to_balance(weight))) - .for_each(|(target, stake)| { - if target == voter { + .map(|(nominator, weight)| (nominator, to_balance(weight))) + .for_each(|(nominator, stake)| { + if nominator == validator { own = own.saturating_add(stake); } else { - others.push(IndividualExposure { who: target, value: stake }); + others.push(IndividualExposure { who: nominator, value: stake }); } total = total.saturating_add(stake); }); @@ -1751,7 +1849,7 @@ impl Module { if exposure.total < slot_stake { slot_stake = exposure.total; } - (voter, exposure) + (validator, exposure) }).collect::)>>(); // In order to keep the property required by `n_session_ending` @@ -1762,8 +1860,8 @@ impl Module { elected_stashes, slot_stake, exposures, - era, compute, + score, }) } else { // There were not enough candidates for even our minimal level of functionality. @@ -1780,7 +1878,7 @@ impl Module { /// weights are returned. /// /// No storage item is updated. - fn do_phragmen(compute: ElectionCompute) -> Option> { + fn do_phragmen() -> Option> { let mut all_nominators: Vec<(T::AccountId, Vec)> = Vec::new(); let all_validator_candidates_iter = >::enumerate(); let all_validators = all_validator_candidates_iter.map(|(who, _pref)| { diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index d3e580fa9faba..3a89b8467cd18 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -33,7 +33,7 @@ use frame_support::{ weights::Weight, }; use frame_system::offchain::{ - TransactionSubmitter, SignAndSubmitTransaction, Signer, CreateTransaction, + TransactionSubmitter, Signer, CreateTransaction, }; use crate::{ EraIndex, GenesisConfig, Module, Trait, StakerStatus, ValidatorPrefs, RewardDestination, @@ -146,7 +146,6 @@ impl_outer_dispatch! { mod staking { pub use super::super::*; - use frame_support::impl_outer_event; } use pallet_balances as balances; use pallet_session as session; @@ -291,14 +290,11 @@ impl Trait for Test { type SubmitTransaction = SubmitTransaction; } -pub mod dummy_sr25519 { +mod dummy_sr25519 { mod app_sr25519 { use sp_application_crypto::{app_crypto, key_types::DUMMY, sr25519}; app_crypto!(sr25519, DUMMY); } - - #[cfg(feature = "std")] - pub type AuthorityPair = app_sr25519::Pair; pub type AuthoritySignature = app_sr25519::Signature; pub type AuthorityId = app_sr25519::Public; @@ -313,7 +309,7 @@ impl CreateTransaction for Test { type Public = dummy_sr25519::AuthorityId; fn create_transaction>( call: Call, - public: Self::Public, + _public: Self::Public, account: AccountId, _index: AccountIndex, ) -> Option<(::Call, ::SignaturePayload)> { diff --git a/primitives/phragmen/Cargo.toml b/primitives/phragmen/Cargo.toml index 3e7ac09c95cf0..90575bc48bfbe 100644 --- a/primitives/phragmen/Cargo.toml +++ b/primitives/phragmen/Cargo.toml @@ -11,10 +11,10 @@ sp-runtime = { version = "2.0.0", default-features = false, path = "../../primit sp-phragmen-compact = { path = "./compact"} [dev-dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } sp-io ={ version = "2.0.0", path = "../../primitives/io" } -rand = "0.7.2" -codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +rand = "0.7.3" [features] default = ["std"] diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index 770c5983d6706..e862fe5678930 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -278,7 +278,7 @@ fn convert_impl_for_staked_assignment( let name = field_name_for(1); quote!( for (who, target) in self.#name { - let all_stake = C::convert(max_of(&who)); + let all_stake = C::convert(max_of(&who)) as ExtendedBalance; assignments.push(StakedAssignment { who, distribution: vec![(target, all_stake)], @@ -290,7 +290,7 @@ fn convert_impl_for_staked_assignment( let name = field_name_for(2); quote!( for (who, (t1, w1), t2) in self.#name { - let all_stake = C::convert(max_of(&who)); + let all_stake = C::convert(max_of(&who)) as ExtendedBalance; let w2 = all_stake.saturating_sub(w1); assignments.push( StakedAssignment { who, @@ -307,7 +307,7 @@ fn convert_impl_for_staked_assignment( quote!( for (who, inners, t_last) in self.#name { let mut sum = u128::min_value(); - let all_stake = C::convert(max_of(&who)); + let all_stake = C::convert(max_of(&who)) as ExtendedBalance; let mut inners_parsed = inners .into_iter() .map(|(ref c, w)| { @@ -330,11 +330,11 @@ fn convert_impl_for_staked_assignment( impl<#account_type: codec::Codec + Default + Clone> #ident<#account_type, u128> { - fn into_compact_staked(self, max_of: FM) + fn into_staked(self, max_of: FM) -> Vec> where for<'r> FM: Fn(&'r #account_type) -> Balance, - C: Convert + C: Convert { let mut assignments: Vec> = Default::default(); #into_impl_single diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 93b7be16b72ba..b8ab3f9cd2667 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -115,7 +115,7 @@ pub struct Assignment { } impl Assignment { - pub fn into_staked(self, stake_of: FS) -> StakedAssignment + pub fn into_staked(self, stake_of: FS) -> StakedAssignment where C: Convert, for<'r> FS: Fn(&'r AccountId) -> Balance, @@ -401,16 +401,20 @@ pub fn elect( /// }, /// } /// ``` +/// The second returned flag indicates the number of edges who corresponded to an actual winner from +/// the given winner set. A value in this place larger than 0 indicates a potentially faulty +/// assignment. pub fn build_support_map( - elected_stashes: &Vec, + winners: &Vec, assignments: &Vec>, -) -> SupportMap where +) -> (SupportMap, u32) where AccountId: Default + Ord + Member, Balance: Default + Copy + SimpleArithmetic, { + let mut errors = 0; // Initialize the support of each candidate. let mut supports = >::new(); - elected_stashes + winners .iter() .for_each(|e| { supports.insert(e.clone(), Default::default()); }); @@ -420,10 +424,37 @@ pub fn build_support_map( if let Some(support) = supports.get_mut(c) { support.total = support.total.saturating_add(*weight_extended); support.voters.push((who.clone(), *weight_extended)); + } else { + errors = errors.saturating_add(1); } } } - supports + (supports, errors) +} + +/// Evaluate a phragmen result, given the 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**. +/// +/// O(E) +pub fn evaluate_support( + support: &SupportMap, +) -> [ExtendedBalance; 3] { + let mut min_support = ExtendedBalance::max_value(); + let mut sum: ExtendedBalance = Zero::zero(); + // TODO: this will probably saturate but using big num makes it even slower. We'll have to see. + let mut sum_squared: ExtendedBalance = Zero::zero(); + for (_, support) in support.iter() { + sum += 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] } /// Returns all combinations of size two in the collection `input` with no repetition. @@ -481,13 +512,16 @@ pub fn trailing_common(t1: &[T], t2: &[T]) -> usize { common } -// TODO: maybe replace with BTreeMap if we want to support this in no_std? -type Map = std::collections::HashMap<(A, A), A>; +// TODO: this whole reduce stuff must go into a crate. Phragmen itself is no_std. This should use std. +// TODO: Using hashMap is more efficient here but that requires a Hash impl for AccountId. We can +// make this by constraining the impl to just polkadot account id type, instead of the opaque +// substrate version which is not `Hash`. +use std::collections::{btree_map::{Entry::*}}; +type Map = BTreeMap<(A, A), A>; -fn reduce_4( +fn reduce_4( assignments: &mut Vec>, ) -> u32 { - use std::collections::{hash_map::{Entry::*}}; let mut combination_map: Map = Map::new(); let mut num_changed: u32 = Zero::zero(); @@ -640,16 +674,14 @@ fn reduce_4( +fn reduce_all( assignments: &mut Vec>, ) -> u32 { - dbg!(&assignments); - use std::collections::{HashMap}; let mut num_changed: u32 = Zero::zero(); // ----------------- Phase 2: remove any other cycle use node::{Node, NodeRef, NodeRole}; - let mut tree: HashMap> = HashMap::new(); + let mut tree: BTreeMap> = BTreeMap::new(); // TODO: unify this terminology: we only have VOTER -> TARGET. no candidate. no staking terms. // a flat iterator of (voter, candidate) over all pairs of votes. Similar to reduce_4, we loop @@ -876,7 +908,7 @@ fn reduce_all( assignments: &mut Vec>, ) -> u32 where { diff --git a/primitives/phragmen/src/mock.rs b/primitives/phragmen/src/mock.rs index 40060d129da93..894c400138d85 100644 --- a/primitives/phragmen/src/mock.rs +++ b/primitives/phragmen/src/mock.rs @@ -413,8 +413,8 @@ pub fn assert_assignments_equal( ass1: &Vec>, ass2: &Vec>, ) { - let support_1 = build_support_map::(winners, ass1); - let support_2 = build_support_map::(winners, ass2); + let (support_1, _) = build_support_map::(winners, ass1); + let (support_2, _) = build_support_map::(winners, ass2); for (who, support) in support_1.iter() { assert_eq!(support.total, support_2.get(who).unwrap().total); diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index 6e1f5d4312037..0116f8fd5eec8 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -578,11 +578,11 @@ fn basic_from_and_into_compact_works_staked_assignments() { } ); - let max_of_fn = |a: &AccountId| -> Balance { 100u128 }; + let max_of_fn = |_: &AccountId| -> Balance { 100u128 }; let max_of: Box Balance> = Box::new(max_of_fn); assert_eq!( - compacted.into_compact_staked::(&max_of), + compacted.into_staked::(&max_of), assignments ); } @@ -836,7 +836,7 @@ fn self_votes_should_be_kept() { .map(|a| a.into_staked::<_, TestCurrencyToVote, _>(&stake_of)) .collect(); - let mut supports = build_support_map::< + let (mut supports, _) = build_support_map::< Balance, AccountId, >( From 35a78722c243a85fd507d8258c706d1500f85519 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 15 Jan 2020 17:30:09 +0100 Subject: [PATCH 012/106] Move reduce into crate. Document stuff --- Cargo.lock | 11 +- Cargo.toml | 1 + primitives/phragmen/compact/Cargo.toml | 3 +- primitives/phragmen/compact/src/lib.rs | 61 +- primitives/phragmen/reduce/Cargo.toml | 11 + primitives/phragmen/reduce/src/lib.rs | 757 +++++++++++++++++++ primitives/phragmen/{ => reduce}/src/node.rs | 0 primitives/phragmen/src/lib.rs | 473 +----------- primitives/phragmen/src/mock.rs | 30 - primitives/phragmen/src/tests.rs | 210 +---- 10 files changed, 841 insertions(+), 716 deletions(-) create mode 100644 primitives/phragmen/reduce/Cargo.toml create mode 100644 primitives/phragmen/reduce/src/lib.rs rename primitives/phragmen/{ => reduce}/src/node.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index cebe6ec28a593..73692c3e9b9fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6511,13 +6511,20 @@ dependencies = [ name = "sp-phragmen-compact" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime 2.0.0", "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "sp-phragmen-reduce" +version = "2.0.0" +dependencies = [ + "sp-phragmen 2.0.0", + "sp-runtime 2.0.0", +] + [[package]] name = "sp-rpc" version = "2.0.0" diff --git a/Cargo.toml b/Cargo.toml index a9af079d90e31..d6857e0c954fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,6 +120,7 @@ members = [ "primitives/panic-handler", "primitives/phragmen", "primitives/phragmen/compact", + "primitives/phragmen/reduce", "primitives/rpc", "primitives/runtime-interface", "primitives/runtime-interface/proc-macro", diff --git a/primitives/phragmen/compact/Cargo.toml b/primitives/phragmen/compact/Cargo.toml index 6625cd053dbd9..cb4ae5c61ae3d 100644 --- a/primitives/phragmen/compact/Cargo.toml +++ b/primitives/phragmen/compact/Cargo.toml @@ -8,8 +8,7 @@ edition = "2018" proc-macro = true [dependencies] -codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } -sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } syn = { version = "1.0.7", features = ["full", "visit"] } quote = "1.0" proc-macro2 = "1.0.6" +proc-macro-crate = "0.1.4" diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index e862fe5678930..0a32286e8682d 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -20,8 +20,10 @@ extern crate proc_macro; use proc_macro::TokenStream; use proc_macro2::{TokenStream as TokenStream2, Span, Ident}; +use proc_macro_crate::crate_name; use quote::quote; use syn::parse::{Parse, ParseStream}; +// use codec::{Encode, Decode}; const PREFIX: &'static str = "votes"; @@ -59,6 +61,25 @@ fn field_name_for(n: usize) -> Ident { /// from the rest. /// /// An example expansion of length 16 is as follows: +/// +/// struct TestCompact { +/// votes1: Vec<(AccountId, AccountId)>, +/// votes2: Vec<(AccountId, (AccountId, W), AccountId)>, +/// votes3: Vec<(AccountId, [(AccountId, W); 2usize], AccountId)>, +/// votes4: Vec<(AccountId, [(AccountId, W); 3usize], AccountId)>, +/// votes5: Vec<(AccountId, [(AccountId, W); 4usize], AccountId)>, +/// votes6: Vec<(AccountId, [(AccountId, W); 5usize], AccountId)>, +/// votes7: Vec<(AccountId, [(AccountId, W); 6usize], AccountId)>, +/// votes8: Vec<(AccountId, [(AccountId, W); 7usize], AccountId)>, +/// votes9: Vec<(AccountId, [(AccountId, W); 8usize], AccountId)>, +/// votes10: Vec<(AccountId, [(AccountId, W); 9usize], AccountId)>, +/// votes11: Vec<(AccountId, [(AccountId, W); 10usize], AccountId)>, +/// votes12: Vec<(AccountId, [(AccountId, W); 11usize], AccountId)>, +/// votes13: Vec<(AccountId, [(AccountId, W); 12usize], AccountId)>, +/// votes14: Vec<(AccountId, [(AccountId, W); 13usize], AccountId)>, +/// votes15: Vec<(AccountId, [(AccountId, W); 14usize], AccountId)>, +/// votes16: Vec<(AccountId, [(AccountId, W); 15usize], AccountId)>, +/// } #[proc_macro] pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { let CompactSolutionDef { @@ -73,6 +94,8 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { panic!("cannot build compact solution struct with capacity less than 2."); } + let imports = imports(); + let singles = { let name = field_name_for(1); quote!(#name: Vec<(#account_type, #account_type)>,) @@ -94,8 +117,8 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { }).collect::(); let compact_def = quote! ( - // TODO: clean imports: how to deal with codec? - #[derive(Default, PartialEq, Eq, Clone, sp_runtime::RuntimeDebug, codec::Encode, codec::Decode)] + #imports + #[derive(Default, PartialEq, Eq, Clone, _sp_runtime::RuntimeDebug, _codec::Encode, _codec::Decode)] #vis struct #ident<#account_type, #weight> { #singles #doubles @@ -103,12 +126,12 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { } ); - // TODO: we can remove this entirely and make the staked impl a bit more generic. A perbill's max value is always ::one. let from_into_impl_assignment = convert_impl_for_assignment( ident.clone(), account_type.clone(), count, ); + let from_into_impl_staked_assignment = convert_impl_for_staked_assignment( ident, account_type, @@ -123,6 +146,28 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { ).into() } +fn imports() -> TokenStream2 { + let runtime_imports = match crate_name("sp-runtime") { + Ok(sp_runtime) => { + let ident = syn::Ident::new(&sp_runtime, Span::call_site()); + quote!( extern crate #ident as _sp_runtime; ) + }, + Err(e) => syn::Error::new(Span::call_site(), &e).to_compile_error(), + }; + let codec_imports = match crate_name("parity-scale-codec") { + Ok(codec) => { + let ident = syn::Ident::new(&codec, Span::call_site()); + quote!( extern crate #ident as _codec; ) + }, + Err(e) => syn::Error::new(Span::call_site(), &e).to_compile_error(), + }; + + quote!( + #runtime_imports + #codec_imports + ) +} + fn convert_impl_for_assignment( ident: syn::Ident, account_type: TokenStream2, @@ -163,7 +208,7 @@ fn convert_impl_for_assignment( #from_impl_double #from_impl_rest _ => { - sp_runtime::print("assignment length too big. ignoring"); + _sp_runtime::print("assignment length too big. ignoring"); } } }); @@ -278,7 +323,7 @@ fn convert_impl_for_staked_assignment( let name = field_name_for(1); quote!( for (who, target) in self.#name { - let all_stake = C::convert(max_of(&who)) as ExtendedBalance; + let all_stake = C::convert(max_of(&who)) as u128; assignments.push(StakedAssignment { who, distribution: vec![(target, all_stake)], @@ -290,7 +335,7 @@ fn convert_impl_for_staked_assignment( let name = field_name_for(2); quote!( for (who, (t1, w1), t2) in self.#name { - let all_stake = C::convert(max_of(&who)) as ExtendedBalance; + let all_stake = C::convert(max_of(&who)) as u128; let w2 = all_stake.saturating_sub(w1); assignments.push( StakedAssignment { who, @@ -307,7 +352,7 @@ fn convert_impl_for_staked_assignment( quote!( for (who, inners, t_last) in self.#name { let mut sum = u128::min_value(); - let all_stake = C::convert(max_of(&who)) as ExtendedBalance; + let all_stake = C::convert(max_of(&who)) as u128; let mut inners_parsed = inners .into_iter() .map(|(ref c, w)| { @@ -352,7 +397,7 @@ fn convert_impl_for_staked_assignment( #from_impl_double #from_impl_rest _ => { - sp_runtime::print("staked assignment length too big. ignoring"); + _sp_runtime::print("staked assignment length too big. ignoring"); } } }); diff --git a/primitives/phragmen/reduce/Cargo.toml b/primitives/phragmen/reduce/Cargo.toml new file mode 100644 index 0000000000000..f147c23b743a2 --- /dev/null +++ b/primitives/phragmen/reduce/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "sp-phragmen-reduce" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } +sp-phragmen = { path = "../" } + +[dev-dependencies] diff --git a/primitives/phragmen/reduce/src/lib.rs b/primitives/phragmen/reduce/src/lib.rs new file mode 100644 index 0000000000000..487a90198d96f --- /dev/null +++ b/primitives/phragmen/reduce/src/lib.rs @@ -0,0 +1,757 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Rust implementation of the Phragmén reduce algorithm. This can be used by any off chain +//! application to reduce cycles from the edge assignment, which will result in smaller size. +//! +//! ### Notions +//! - `m`: size of the committee to elect. +//! - `k`: maximum allowed votes (16 as of this writing). +//! - `nv ∈ E` means that nominator `n ∈ N` supports the election of candidate `v ∈ V`. +//! - A valid solution consists of a tuple `(S, W)` , where `S ⊆ V` is a committee of m validators, +//! and `W : E → R ≥ 0` is an edge weight vector which describes how the budget of each nominator +//! n is fractionally assigned to n 's elected neighbors. +//! - `E_w := { e ∈ E : w_e > 0 }`. +//! +//! ### Algorithm overview +//! +//! > We consider the input edge weight vector `w` as a directed flow over `E_w` , where the flow in +//! > each edge is directed from the nominator to the validator. We build `w′` from `w` by removing +//! > **circulations** to cancel out the flow over as many edges as possible, while preserving flow +//! > demands over all vertices and without reverting the flow direction over any edge. As long as +//! > there is a cycle, we can remove an additional circulation and eliminate at least one new edge +//! > from `E_w′` . This shows that the algorithm always progresses and will eventually finish with +//! > an acyclic edge support. We keep a data structure that represents a forest of rooted trees, +//! > which is initialized as a collection of singletons – one per vertex – and to which edges in +//! > `E_w` are added one by one, causing the trees to merge. Whenever a new edge creates a cycle, +//! > we detect it and destroy it by removing a circulation. We also run a pre-computation which is +//! > designed to detect and remove cycles of length four exclusively. This pre-computation is +//! > optional, and if we skip it then the running time becomes `O (|E_w| ⋅ m), so the +//! > pre-computation makes sense only if `m >> k` and `|E_w| >> m^2`. +//! > +//! +//! ### Resources: +//! +//! 1. https://hackmd.io/JOn9x98iS0e0DPWQ87zGWg?view + +use std::collections::{btree_map::{Entry::*}}; +use std::collections::BTreeMap; + +use sp_runtime::traits::{Zero, Bounded}; +use sp_phragmen::{ExtendedBalance, StakedAssignment}; + +mod node; + +/// Map type used for caching. Can be easily swapped with HashMap. +type Map = BTreeMap<(A, A), A>; + +/// Returns all combinations of size two in the collection `input` with no repetition. +fn combinations_2(input: &[T]) -> Vec<(T, T)> { + let n = input.len(); + if n < 2 { + return Default::default() + } + + let mut comb = Vec::with_capacity(n * (n-1) / 2); + for i in 0..n { + for j in i+1..n { + comb.push((input[i].clone(), input[j].clone())) + } + } + comb +} + +/// Returns the count of trailing common elements in a slice. +fn trailing_common(t1: &[T], t2: &[T]) -> usize { + let mut t1_pointer = t1.len() - 1; + let mut t2_pointer = t2.len() - 1; + let mut common = 0usize; + + while t1[t1_pointer] == t2[t2_pointer] { + common += 1; + if t1_pointer == 0 || t2_pointer == 0 { + break; + } + t1_pointer -= 1; + t2_pointer -= 1; + } + + common +} + +/// Reduce only redundant edges with cycle length of 4. +/// +/// O(|E_w| ⋅ k). +fn reduce_4( + assignments: &mut Vec>, +) -> u32 { + + let mut combination_map: Map = Map::new(); + let mut num_changed: u32 = Zero::zero(); + + // NOTE: we have to use the old fashioned style loops here with manual indexing. Borrowing + // assignments will not work since then there is NO way to mutate it inside. + for i in 0..assignments.len() { + let who = assignments[i].who.clone(); + // immutable copy -- needed for further read operations. TODO: As an optimization at the + // expense of readability, we can remove this. + let distribution = &assignments[i].distribution.clone(); + + // all combinations for this particular voter + let candidate_combinations = combinations_2( + &distribution.iter().map(|(t, _p)| t.clone()).collect::>(), + ); + + for (v1, v2) in candidate_combinations { + match combination_map.entry((v1.clone(), v2.clone())) { + Vacant(entry) => { + entry.insert(who.clone()); + }, + Occupied(mut entry) => { + let other_who = entry.get_mut(); + println!("Occupied {:?} -> ({:?} {:?}) other: {:?}", &who, &v1, &v2, &other_who); + + // check if other_who voted for the same pair v1, v2. + let maybe_other_assignments = assignments.iter().find(|a| a.who == *other_who); + if maybe_other_assignments.is_none() { + // TODO: test for this path? + // This combination is not a cycle. + continue; + } + let other_assignment = maybe_other_assignments.expect("value is checked to be 'Some'"); + + // Collect potential cycle votes + let mut other_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); + other_assignment.distribution.iter().for_each(|(t, w)| { + if *t == v1 || *t == v2 { other_cycle_votes.push((t.clone(), *w)); } + }); + + // This is not a cycle. Replace and continue. + let other_votes_count = other_cycle_votes.len(); + // TODO: this might need testing. Some duplicate can cause this and this + // function should reject them. + debug_assert!(other_votes_count <= 2); + + if other_votes_count < 2 { + // Not a cycle. Replace and move on. + // TODO test fro this path?? + *other_who = who.clone(); + continue; + } else { + println!("And it is a cycle! "); + // This is a cycle. + let mut who_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); + distribution.iter().for_each(|(t, w)| { + if *t == v1 || *t == v2 { who_cycle_votes.push((t.clone(), *w)); } + }); + + if who_cycle_votes.len() != 2 { continue; } + + // Align the targets similarly. This helps with the circulation below. + if other_cycle_votes[0].0 != who_cycle_votes[0].0 { + other_cycle_votes.swap(0, 1); + } + + debug_assert_eq!(who_cycle_votes[0].0, other_cycle_votes[0].0); + debug_assert_eq!(who_cycle_votes[1].0, other_cycle_votes[1].0); + + // Find min + let mut min_value: ExtendedBalance = Bounded::max_value(); + let mut min_index: usize = 0; + let cycle = who_cycle_votes + .iter() + .chain(other_cycle_votes.iter()) + .enumerate() + .map(|(index, (t, w))| { + if *w <= min_value { min_value = *w; min_index = index; } + (t.clone(), *w) + }).collect::>(); + dbg!(&cycle, &min_value); + + // min was in the first part of the chained iters + let mut increase_indices: Vec = Vec::new(); + let mut decrease_indices: Vec = Vec::new(); + decrease_indices.push(min_index); + if min_index < 2 { + // min_index == 0 => sibling_index <- 1 + // min_index == 1 => sibling_index <- 0 + let sibling_index = 1 - min_index; + increase_indices.push(sibling_index); + // valid because the two chained sections of `cycle` are aligned; + // index [0, 2] are both voting for v1 or both v2. Same goes for [1, 3]. + decrease_indices.push(sibling_index + 2); + increase_indices.push(min_index + 2); + } else { + // min_index == 2 => sibling_index <- 3 + // min_index == 3 => sibling_index <- 2 + let sibling_index = 3 - min_index % 2; + increase_indices.push(sibling_index); + // valid because the two chained sections of `cycle` are aligned; + // index [0, 2] are both voting for v1 or both v2. Same goes for [1, 3]. + decrease_indices.push(sibling_index - 2); + increase_indices.push(min_index - 2); + } + + dbg!(&increase_indices, &decrease_indices); + + // apply changes + increase_indices.into_iter().for_each(|i| { + assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { + ass.distribution + .iter_mut() + .position(|(t, _)| *t == cycle[i].0) + .map(|idx| { + let next_value = ass.distribution[idx].1.saturating_add(min_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); + }); + decrease_indices.into_iter().for_each(|i| { + assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { + ass.distribution + .iter_mut() + .position(|(t, _)| *t == cycle[i].0) + .map(|idx| { + let next_value = ass.distribution[idx].1.saturating_sub(min_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + num_changed += 1; + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); + }); + } + } + } + } + } + + num_changed +} + +/// Reduce all redundant edges from the edge weight graph. +/// +/// O(|Ew| ⋅ m) +fn reduce_all( + assignments: &mut Vec>, +) -> u32 { + let mut num_changed: u32 = Zero::zero(); + + // ----------------- Phase 2: remove any other cycle + use node::{Node, NodeRef, NodeRole}; + let mut tree: BTreeMap> = BTreeMap::new(); + + // a flat iterator of (voter, target) over all pairs of votes. Similar to reduce_4, we loop + // without borrowing. + for i in 0..assignments.len() { + let voter = assignments[i].who.clone(); + + for j in 0..assignments[i].distribution.len() { + let (target, _) = assignments[i].distribution[j].clone(); + + let voter_node = tree.entry(voter.clone()) + .or_insert(Node::new(voter.clone(), NodeRole::Voter).into_ref()).clone(); + let target_node = tree.entry(target.clone()) + .or_insert(Node::new(target.clone(), NodeRole::Target).into_ref()).clone(); + + if !voter_node.borrow().has_parent() { + Node::set_parent_of(&voter_node, &target_node); + continue; + } + if !target_node.borrow().has_parent() { + Node::set_parent_of(&target_node, &voter_node); + continue; + } + + let (voter_root, voter_root_path) = Node::root(&voter_node); + let (target_root, target_root_path) = Node::root(&target_node); + + if voter_root != target_root { + // swap + // TODO: test case for this path + if voter_root_path.len() <= target_root_path.len() { + // iterate from last to beginning, skipping the first one. This asserts that + // indexing is always correct. + voter_root_path + .iter() + .skip(1) + .rev() + .enumerate() + .map(|(index, r)| (voter_root_path.len() - index - 1, r)) + .for_each(|(index, r)| { + let index = voter_root_path.len() - index; + Node::set_parent_of(r, &voter_root_path[index-1]) + }); + debug_assert_eq!(voter_root_path[0], voter_node); + Node::set_parent_of(&voter_node, &target_node); + } else { + target_root_path + .iter() + .skip(1) + .rev() + .enumerate() + .map(|(index, r)| (target_root_path.len() - index - 1, r)) + .for_each(|(index, r)| { + let index = target_root_path.len() - index; + Node::set_parent_of(r, &target_root_path[index-1]) + }); + debug_assert_eq!(target_root_path[0], target_node); + Node::set_parent_of(&target_node, &voter_node); + } + } else { + debug_assert_eq!(target_root_path.last().unwrap(), voter_root_path.last().unwrap()); + + // find common and cycle. + let common_count = trailing_common(&voter_root_path, &target_root_path); + + // because roots are the same. TODO: replace with a bail-out + debug_assert!(common_count > 0); + + // cycle part of each path will be `path[path.len() - common_count - 1 : 0]` + // NOTE: the order of chaining is important! it is always build from [target, ..., + // voter] + // TODO: check borrows panic! + let cycle = + target_root_path.iter().take(target_root_path.len() - common_count + 1).cloned() + .chain(voter_root_path.iter().take(voter_root_path.len() - common_count).rev().cloned()) + .collect::>>(); + + + // TODO: a cycle struct that gives you min + to which chunk it belonged. + // find minimum of cycle. + let mut min_value: ExtendedBalance = Bounded::max_value(); + // Note that this can only ever point to a target, not a voter. + let mut min_who: AccountId = Default::default(); + let mut min_index = 0usize; + // 1 -> next // 0 -> prev TOOD: I have some ideas of fixing this. + let mut min_direction = 0u32; + // helpers + let next_index = |i| { if i < (cycle.len() - 1) { i + 1 } else { 0 } }; + let prev_index = |i| { if i > 0 { i - 1 } else { cycle.len() - 1 } }; + for i in 0..cycle.len() { + if cycle[i].borrow().role == NodeRole::Voter { + // NOTE: sadly way too many clones since I don't want to make AccountId: Copy + let current = cycle[i].borrow().who.clone(); + let next = cycle[next_index(i)].borrow().who.clone(); + let prev = cycle[prev_index(i)].borrow().who.clone(); + assignments.iter().find(|a| a.who == current).map(|ass| { + ass.distribution.iter().find(|d| d.0 == next).map(|(_, w)| { + if *w < min_value { + min_value = *w; + min_who = next.clone(); + min_index = i; + min_direction = 1; + } + }) + }); + assignments.iter().find(|a| a.who == current).map(|ass| { + ass.distribution.iter().find(|d| d.0 == prev).map(|(_, w)| { + if *w < min_value { + min_value = *w; + min_who = prev.clone(); + min_index = i; + min_direction = 0; + } + }) + }); + } + } + // if the min edge is in the voter's sub-chain. + let target_chunk = target_root_path.len() - common_count; + let min_chain_in_voter = (min_index + min_direction as usize) >= target_chunk; + + dbg!(min_value, min_index, &min_who, min_direction); + + // walk over the cycle and update the weights + // TODO: or at least unify and merge the process of adding this flow in both places. and surely add dedicated tests for this supply demand circulation math + // if the circulation begins with an addition. It is assumed that a cycle always starts with a target. + let start_operation_add = ((min_index % 2) + min_direction as usize) % 2 == 1; + for i in 0..cycle.len() { + let current = cycle[i].borrow(); + if current.role == NodeRole::Voter { + let prev = cycle[prev_index(i)].borrow(); + + assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { + ass.distribution.iter_mut().position(|(t, _)| *t == prev.who).map(|idx| { + let next_value = if i % 2 == 0 { + if start_operation_add { + ass.distribution[idx].1.saturating_add(min_value) + } else { + ass.distribution[idx].1.saturating_sub(min_value) + } + } else { + if start_operation_add { + ass.distribution[idx].1.saturating_sub(min_value) + } else { + ass.distribution[idx].1.saturating_add(min_value) + } + }; + // println!("Next value for edge {:?} -> {:?} is {}", current.who, prev.who, next_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + num_changed += 1; + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); + + let next = cycle[next_index(i)].borrow(); + + assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { + ass.distribution.iter_mut().position(|(t, _)| *t == next.who).map(|idx| { + let next_value = if i % 2 == 0 { + if start_operation_add { + ass.distribution[idx].1.saturating_sub(min_value) + } else { + ass.distribution[idx].1.saturating_add(min_value) + } + } else { + if start_operation_add { + ass.distribution[idx].1.saturating_add(min_value) + } else { + ass.distribution[idx].1.saturating_sub(min_value) + } + }; + // println!("Next value for edge {:?} -> {:?} is {}", current.who, next.who, next_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + num_changed += 1; + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); + } + }; + + // don't do anything if the edge removed itself + if min_index == (cycle.len() - 1) && min_direction == 1 { continue; } + + // TODO: this is most likely buggy + // re-org otherwise. + if min_chain_in_voter { + // NOTE: safe; voter_root_path is always bigger than 1 element. + for i in 0..voter_root_path.len()-1 { + let next = voter_root_path[i + 1].clone(); + if next.borrow().who == min_who { + break; + } + Node::set_parent_of(&voter_root_path[i + 1], &voter_root_path[i]); + } + Node::set_parent_of(&voter_node, &target_node); + } else { + // NOTE: safe; target_root_path is always bigger than 1 element. + for i in 0..target_root_path.len()-1 { + if target_root_path[i].borrow().who == min_who { + break; + } + Node::set_parent_of(&target_root_path[i + 1], &target_root_path[i]); + } + Node::set_parent_of(&target_node, &voter_node); + } + + + + } + } + } + + num_changed +} + +/// Reduce the given [`PhragmenResult`]. This removes redundant edges from without changing the +/// overall backing of any of the elected candidates. +/// +/// O(min{ |Ew| ⋅ k + m3 , |Ew| ⋅ m }) +pub fn reduce< + AccountId: Clone + Eq + Default + Ord + std::fmt::Debug, +>( + assignments: &mut Vec>, +) -> u32 where { + let mut num_changed = reduce_4(assignments); + num_changed += reduce_all(assignments); + num_changed +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_phragmen::build_support_map; + + type AccountId = u64; + type Balance = u128; + + #[allow(dead_code)] // to be used with fuzzing + pub fn assert_assignments_equal( + winners: &Vec, + ass1: &Vec>, + ass2: &Vec>, + ) { + let (support_1, _) = build_support_map::(winners, ass1); + let (support_2, _) = build_support_map::(winners, ass2); + + for (who, support) in support_1.iter() { + assert_eq!(support.total, support_2.get(who).unwrap().total); + assert_eq!(support.voters, support_2.get(who).unwrap().voters); + + } + } + + #[allow(dead_code)] // to be used with fuzzing + pub fn reduce_and_compare( + assignment: &Vec>, + winners: &Vec, + ) { + let mut altered_assignment = assignment.clone(); + crate::reduce(&mut altered_assignment); + assert_assignments_equal( + winners, + &assignment, + &altered_assignment, + ); + } + + #[test] + fn trailing_common_works() { + assert_eq!( + trailing_common(&vec![1u8, 2, 3, 4, 5], &vec![7u8, 8, 4, 5]), + 2, + ); + assert_eq!( + trailing_common(&vec![1u8, 2], &vec![7u8, 8]), + 0, + ); + assert_eq!( + trailing_common(&vec![1u8, 2, 3, 4, 5], &vec![3u8, 4, 5]), + 3, + ); + } + + #[test] + fn basic_reduce_4_cycle_works() { + use super::*; + + let assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 25), + (20, 75), + ], + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 50), + (20, 50), + ], + }, + ]; + + let mut new_assignments = assignments.clone(); + let num_reduced = reduce_4(&mut new_assignments); + + assert_eq!(num_reduced, 1); + assert_eq!( + new_assignments, + vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (20, 100), + ], + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 75), + (20, 25), + ], + }, + ], + ); + } + + #[test] + fn basic_reduce_all_cycles_works() { + let mut assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![(10, 10)] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 15), + (40, 15) + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (20, 10), + (30, 10), + (40, 20), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 20), + (30, 10), + (40, 20), + ], + }, + ]; + + assert_eq!(3, reduce_all(&mut assignments)); + + assert_eq!( + assignments, + vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 10), + ] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 30), + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (40, 40), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 15), + (30, 20), + (40, 15), + ], + }, + ], + ) + } + + #[test] + fn basic_reduce_works() { + let mut assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![(10, 10)] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 15), + (40, 15) + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (20, 10), + (30, 10), + (40, 20), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 20), + (30, 10), + (40, 20), + ], + }, + ]; + + assert_eq!(3, reduce(&mut assignments)); + + assert_eq!( + assignments, + vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 10), + ] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 30), + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (40, 40), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 15), + (30, 20), + (40, 15), + ], + }, + ], + ) + } + +} diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/reduce/src/node.rs similarity index 100% rename from primitives/phragmen/src/node.rs rename to primitives/phragmen/reduce/src/node.rs diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index b8ab3f9cd2667..1e05f3224d2cd 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -42,8 +42,6 @@ mod mock; #[cfg(test)] mod tests; -mod node; - /// A type in which performing operations on balances and stakes of candidates and voters are safe. /// /// This module's functions expect a `Convert` type to convert all balances to u64. Hence, u128 is @@ -105,6 +103,7 @@ pub struct PhragmenResult { pub assignments: Vec> } +/// A voter's stake assignment among a set of targets, represented as ratios. #[derive(RuntimeDebug, Clone)] #[cfg_attr(feature = "std", derive(PartialEq, Eq))] pub struct Assignment { @@ -133,6 +132,8 @@ impl Assignment { } } +/// A voter's stake assignment among a set of targets, represented as absolute values in the scale +/// of [`ExtendedBalance`]. #[derive(RuntimeDebug, Clone)] #[cfg_attr(feature = "std", derive(PartialEq, Eq))] pub struct StakedAssignment { @@ -187,7 +188,6 @@ pub fn elect( AccountId: Default + Ord + Member, Balance: Default + Copy + SimpleArithmetic, for<'r> FS: Fn(&'r AccountId) -> Balance, - // TODO: btw now we can remove the backward convert! C: Convert + Convert, { let to_votes = |b: Balance| >::convert(b) as ExtendedBalance; @@ -323,7 +323,7 @@ pub fn elect( n.load.n(), ).unwrap_or(Bounded::max_value()) } else { - // defensive only. Both edge and nominator loads are built from + // defensive only. Both edge and voter loads are built from // scores, hence MUST have the same denominator. Zero::zero() } @@ -338,7 +338,7 @@ pub fn elect( } if assignment.distribution.len() > 0 { - // To ensure an assertion indicating: no stake from the nominator going to waste, + // To ensure an assertion indicating: no stake from the voter going to waste, // we add a minimal post-processing to equally assign all of the leftover stake ratios. let vote_count = assignment.distribution.len() as u32; let len = assignment.distribution.len(); @@ -358,7 +358,7 @@ pub fn elect( } } - // `remainder` is set to be less than maximum votes of a nominator (currently 16). + // `remainder` is set to be less than maximum votes of a voter (currently 16). // safe to cast it to usize. let remainder = diff - diff_per_vote * vote_count; for i in 0..remainder as usize { @@ -445,6 +445,7 @@ pub fn evaluate_support( let mut min_support = ExtendedBalance::max_value(); let mut sum: ExtendedBalance = Zero::zero(); // TODO: this will probably saturate but using big num makes it even slower. We'll have to see. + // This must run on chain.. let mut sum_squared: ExtendedBalance = Zero::zero(); for (_, support) in support.iter() { sum += support.total; @@ -457,466 +458,6 @@ pub fn evaluate_support( [min_support, sum, sum_squared] } -/// Returns all combinations of size two in the collection `input` with no repetition. -fn combinations_2(input: &[T]) -> Vec<(T, T)> { - let n = input.len(); - if n < 2 { - return Default::default() - } - - let mut comb = Vec::with_capacity(n * (n-1) / 2); - for i in 0..n { - for j in i+1..n { - comb.push((input[i].clone(), input[j].clone())) - } - } - comb -} - -/// Returns the count of trailing common elements in a slice. -/// TODO: this need not to be in the public interface. -/// ```rust -/// use sp_phragmen::trailing_common; -/// -/// fn main() { -/// assert_eq!( -/// trailing_common(&vec![1u8, 2, 3, 4, 5], &vec![7u8, 8, 4, 5]), -/// 2, -/// ); -/// -/// assert_eq!( -/// trailing_common(&vec![1u8, 2], &vec![7u8, 8]), -/// 0, -/// ); -/// -/// assert_eq!( -/// trailing_common(&vec![1u8, 2, 3, 4, 5], &vec![3u8, 4, 5]), -/// 3, -/// ) -/// } -/// ``` -pub fn trailing_common(t1: &[T], t2: &[T]) -> usize { - let mut t1_pointer = t1.len() - 1; - let mut t2_pointer = t2.len() - 1; - let mut common = 0usize; - - while t1[t1_pointer] == t2[t2_pointer] { - common += 1; - if t1_pointer == 0 || t2_pointer == 0 { - break; - } - t1_pointer -= 1; - t2_pointer -= 1; - } - - common -} - -// TODO: this whole reduce stuff must go into a crate. Phragmen itself is no_std. This should use std. -// TODO: Using hashMap is more efficient here but that requires a Hash impl for AccountId. We can -// make this by constraining the impl to just polkadot account id type, instead of the opaque -// substrate version which is not `Hash`. -use std::collections::{btree_map::{Entry::*}}; -type Map = BTreeMap<(A, A), A>; - -fn reduce_4( - assignments: &mut Vec>, -) -> u32 { - - let mut combination_map: Map = Map::new(); - let mut num_changed: u32 = Zero::zero(); - - // NOTE: we have to use the old fashioned style loops here with manual indexing. Borrowing - // assignments will not work since then there is NO way to mutate it inside. - for i in 0..assignments.len() { - let who = assignments[i].who.clone(); - // immutable copy -- needed for further read operations. TODO: As an optimization at the - // expense of readability, we can remove this. - let distribution = &assignments[i].distribution.clone(); - - // all combinations for this particular voter - let candidate_combinations = combinations_2( - &distribution.iter().map(|(t, _p)| t.clone()).collect::>(), - ); - - for (v1, v2) in candidate_combinations { - match combination_map.entry((v1.clone(), v2.clone())) { - Vacant(entry) => { - entry.insert(who.clone()); - }, - Occupied(mut entry) => { - let other_who = entry.get_mut(); - println!("Occupied {:?} -> ({:?} {:?}) other: {:?}", &who, &v1, &v2, &other_who); - - // check if other_who voted for the same pair v1, v2. - let maybe_other_assignments = assignments.iter().find(|a| a.who == *other_who); - if maybe_other_assignments.is_none() { - // TODO: test for this path? - // This combination is not a cycle. - continue; - } - let other_assignment = maybe_other_assignments.expect("value is checked to be 'Some'"); - - // Collect potential cycle votes - let mut other_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); - other_assignment.distribution.iter().for_each(|(t, w)| { - if *t == v1 || *t == v2 { other_cycle_votes.push((t.clone(), *w)); } - }); - - // This is not a cycle. Replace and continue. - let other_votes_count = other_cycle_votes.len(); - // TODO: this might need testing. Some duplicate can cause this and this - // function should reject them. - debug_assert!(other_votes_count <= 2); - - if other_votes_count < 2 { - // Not a cycle. Replace and move on. - // TODO test fro this path?? - *other_who = who.clone(); - continue; - } else { - println!("And it is a cycle! "); - // This is a cycle. - let mut who_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); - distribution.iter().for_each(|(t, w)| { - if *t == v1 || *t == v2 { who_cycle_votes.push((t.clone(), *w)); } - }); - - if who_cycle_votes.len() != 2 { continue; } - - // Align the targets similarly. This helps with the circulation below. - if other_cycle_votes[0].0 != who_cycle_votes[0].0 { - other_cycle_votes.swap(0, 1); - } - - // TODO: remove later. - debug_assert_eq!(who_cycle_votes[0].0, other_cycle_votes[0].0); - debug_assert_eq!(who_cycle_votes[1].0, other_cycle_votes[1].0); - - // Find min - let mut min_value: ExtendedBalance = Bounded::max_value(); - let mut min_index: usize = 0; - let cycle = who_cycle_votes - .iter() - .chain(other_cycle_votes.iter()) - .enumerate() - .map(|(index, (t, w))| { - if *w <= min_value { min_value = *w; min_index = index; } - (t.clone(), *w) - }).collect::>(); - dbg!(&cycle, &min_value); - - // min was in the first part of the chained iters - let mut increase_indices: Vec = Vec::new(); - let mut decrease_indices: Vec = Vec::new(); - decrease_indices.push(min_index); - if min_index < 2 { - // min_index == 0 => sibling_index <- 1 - // min_index == 1 => sibling_index <- 0 - let sibling_index = 1 - min_index; - increase_indices.push(sibling_index); - // valid because the two chained sections of `cycle` are aligned; - // index [0, 2] are both voting for v1 or both v2. Same goes for [1, 3]. - decrease_indices.push(sibling_index + 2); - increase_indices.push(min_index + 2); - } else { - // min_index == 2 => sibling_index <- 3 - // min_index == 3 => sibling_index <- 2 - let sibling_index = 3 - min_index % 2; - increase_indices.push(sibling_index); - // valid because the two chained sections of `cycle` are aligned; - // index [0, 2] are both voting for v1 or both v2. Same goes for [1, 3]. - decrease_indices.push(sibling_index - 2); - increase_indices.push(min_index - 2); - } - - dbg!(&increase_indices, &decrease_indices); - - // apply changes - increase_indices.into_iter().for_each(|i| { - assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { - ass.distribution - .iter_mut() - .position(|(t, _)| *t == cycle[i].0) - .map(|idx| { - let next_value = ass.distribution[idx].1.saturating_add(min_value); - if next_value.is_zero() { - ass.distribution.remove(idx); - } else { - ass.distribution[idx].1 = next_value; - } - }); - }); - }); - decrease_indices.into_iter().for_each(|i| { - assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { - ass.distribution - .iter_mut() - .position(|(t, _)| *t == cycle[i].0) - .map(|idx| { - let next_value = ass.distribution[idx].1.saturating_sub(min_value); - if next_value.is_zero() { - ass.distribution.remove(idx); - num_changed += 1; - } else { - ass.distribution[idx].1 = next_value; - } - }); - }); - }); - } - } - } - } - } - - println!("DoNE {:?}", assignments); - num_changed -} - -fn reduce_all( - assignments: &mut Vec>, -) -> u32 { - let mut num_changed: u32 = Zero::zero(); - - // ----------------- Phase 2: remove any other cycle - use node::{Node, NodeRef, NodeRole}; - let mut tree: BTreeMap> = BTreeMap::new(); - - // TODO: unify this terminology: we only have VOTER -> TARGET. no candidate. no staking terms. - // a flat iterator of (voter, candidate) over all pairs of votes. Similar to reduce_4, we loop - // without borrowing. - for i in 0..assignments.len() { - let voter = assignments[i].who.clone(); - - for j in 0..assignments[i].distribution.len() { - let (target, _) = assignments[i].distribution[j].clone(); - - let voter_node = tree.entry(voter.clone()) - .or_insert(Node::new(voter.clone(), NodeRole::Voter).into_ref()).clone(); - let target_node = tree.entry(target.clone()) - .or_insert(Node::new(target.clone(), NodeRole::Target).into_ref()).clone(); - - if !voter_node.borrow().has_parent() { - Node::set_parent_of(&voter_node, &target_node); - continue; - } - if !target_node.borrow().has_parent() { - Node::set_parent_of(&target_node, &voter_node); - continue; - } - - let (voter_root, voter_root_path) = Node::root(&voter_node); - let (target_root, target_root_path) = Node::root(&target_node); - - if voter_root != target_root { - // swap - // TODO: test case for this path - if voter_root_path.len() <= target_root_path.len() { - // iterate from last to beginning, skipping the first one. This asserts that - // indexing is always correct. - voter_root_path - .iter() - .skip(1) - .rev() - .enumerate() - .map(|(index, r)| (voter_root_path.len() - index - 1, r)) - .for_each(|(index, r)| { - let index = voter_root_path.len() - index; - Node::set_parent_of(r, &voter_root_path[index-1]) - }); - debug_assert_eq!(voter_root_path[0], voter_node); - Node::set_parent_of(&voter_node, &target_node); - } else { - target_root_path - .iter() - .skip(1) - .rev() - .enumerate() - .map(|(index, r)| (target_root_path.len() - index - 1, r)) - .for_each(|(index, r)| { - let index = target_root_path.len() - index; - Node::set_parent_of(r, &target_root_path[index-1]) - }); - debug_assert_eq!(target_root_path[0], target_node); - Node::set_parent_of(&target_node, &voter_node); - } - } else { - debug_assert_eq!(target_root_path.last().unwrap(), voter_root_path.last().unwrap()); - - // find common and cycle. - let common_count = trailing_common(&voter_root_path, &target_root_path); - - // because roots are the same. TODO: replace with a bail-out - debug_assert!(common_count > 0); - - // cycle part of each path will be `path[path.len() - common_count - 1 : 0]` - // NOTE: the order of chaining is important! it is always build from [target, ..., - // voter] - // TODO: check borrows panic! - let cycle = - target_root_path.iter().take(target_root_path.len() - common_count + 1).cloned() - .chain(voter_root_path.iter().take(voter_root_path.len() - common_count).rev().cloned()) - .collect::>>(); - - - // TODO: a cycle struct that gives you min + to which chunk it belonged. - // find minimum of cycle. - let mut min_value: ExtendedBalance = Bounded::max_value(); - // Note that this can only ever point to a target, not a voter. - let mut min_who: AccountId = Default::default(); - let mut min_index = 0usize; - // 1 -> next // 0 -> prev TOOD: I have some ideas of fixing this. - let mut min_direction = 0u32; - // helpers - let next_index = |i| { if i < (cycle.len() - 1) { i + 1 } else { 0 } }; - let prev_index = |i| { if i > 0 { i - 1 } else { cycle.len() - 1 } }; - for i in 0..cycle.len() { - if cycle[i].borrow().role == NodeRole::Voter { - // NOTE: sadly way too many clones since I don't want to make AccountId: Copy - let current = cycle[i].borrow().who.clone(); - let next = cycle[next_index(i)].borrow().who.clone(); - let prev = cycle[prev_index(i)].borrow().who.clone(); - assignments.iter().find(|a| a.who == current).map(|ass| { - ass.distribution.iter().find(|d| d.0 == next).map(|(_, w)| { - if *w < min_value { - min_value = *w; - min_who = next.clone(); - min_index = i; - min_direction = 1; - } - }) - }); - assignments.iter().find(|a| a.who == current).map(|ass| { - ass.distribution.iter().find(|d| d.0 == prev).map(|(_, w)| { - if *w < min_value { - min_value = *w; - min_who = prev.clone(); - min_index = i; - min_direction = 0; - } - }) - }); - } - } - // if the min edge is in the voter's sub-chain. - let target_chunk = target_root_path.len() - common_count; - let min_chain_in_voter = (min_index + min_direction as usize) >= target_chunk; - - dbg!(min_value, min_index, &min_who, min_direction); - - // walk over the cycle and update the weights - // TODO: or at least unify and merge the process of adding this flow in both places. and surely add dedicated tests for this supply demand circulation math - // if the circulation begins with an addition. It is assumed that a cycle always starts with a target. - let start_operation_add = ((min_index % 2) + min_direction as usize) % 2 == 1; - for i in 0..cycle.len() { - let current = cycle[i].borrow(); - if current.role == NodeRole::Voter { - let prev = cycle[prev_index(i)].borrow(); - - assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { - ass.distribution.iter_mut().position(|(t, _)| *t == prev.who).map(|idx| { - let next_value = if i % 2 == 0 { - if start_operation_add { - ass.distribution[idx].1.saturating_add(min_value) - } else { - ass.distribution[idx].1.saturating_sub(min_value) - } - } else { - if start_operation_add { - ass.distribution[idx].1.saturating_sub(min_value) - } else { - ass.distribution[idx].1.saturating_add(min_value) - } - }; - // println!("Next value for edge {:?} -> {:?} is {}", current.who, prev.who, next_value); - if next_value.is_zero() { - ass.distribution.remove(idx); - num_changed += 1; - } else { - ass.distribution[idx].1 = next_value; - } - }); - }); - - let next = cycle[next_index(i)].borrow(); - - assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { - ass.distribution.iter_mut().position(|(t, _)| *t == next.who).map(|idx| { - let next_value = if i % 2 == 0 { - if start_operation_add { - ass.distribution[idx].1.saturating_sub(min_value) - } else { - ass.distribution[idx].1.saturating_add(min_value) - } - } else { - if start_operation_add { - ass.distribution[idx].1.saturating_add(min_value) - } else { - ass.distribution[idx].1.saturating_sub(min_value) - } - }; - // println!("Next value for edge {:?} -> {:?} is {}", current.who, next.who, next_value); - if next_value.is_zero() { - ass.distribution.remove(idx); - num_changed += 1; - } else { - ass.distribution[idx].1 = next_value; - } - }); - }); - } - }; - - // don't do anything if the edge removed itself - if min_index == (cycle.len() - 1) && min_direction == 1 { continue; } - - // TODO: this is most likely buggy - // re-org otherwise. - if min_chain_in_voter { - // NOTE: safe; voter_root_path is always bigger than 1 element. - for i in 0..voter_root_path.len()-1 { - let next = voter_root_path[i + 1].clone(); - if next.borrow().who == min_who { - break; - } - Node::set_parent_of(&voter_root_path[i + 1], &voter_root_path[i]); - } - Node::set_parent_of(&voter_node, &target_node); - } else { - // NOTE: safe; target_root_path is always bigger than 1 element. - for i in 0..target_root_path.len()-1 { - if target_root_path[i].borrow().who == min_who { - break; - } - Node::set_parent_of(&target_root_path[i + 1], &target_root_path[i]); - } - Node::set_parent_of(&target_node, &voter_node); - } - - - - } - } - } - - num_changed -} - -/// Reduce the given [`PhragmenResult`]. This removes redundant edges from without changing the -/// overall backing of any of the elected candidates. -/// -/// TODO: add complexity to all functions. -pub fn reduce< - AccountId: Clone + Eq + Default + Ord + std::fmt::Debug, ->( - assignments: &mut Vec>, -) -> u32 where { - let mut num_changed = reduce_4(assignments); - num_changed += reduce_all(assignments); - num_changed -} - /// Performs equalize post-processing to the output of the election algorithm. This happens in /// rounds. The number of rounds and the maximum diff-per-round tolerance can be tuned through input /// parameters. diff --git a/primitives/phragmen/src/mock.rs b/primitives/phragmen/src/mock.rs index 894c400138d85..8da79cdda4bea 100644 --- a/primitives/phragmen/src/mock.rs +++ b/primitives/phragmen/src/mock.rs @@ -406,33 +406,3 @@ pub(crate) fn build_support_map_float( } supports } - -#[allow(dead_code)] // to be used with fuzzing -pub fn assert_assignments_equal( - winners: &Vec, - ass1: &Vec>, - ass2: &Vec>, -) { - let (support_1, _) = build_support_map::(winners, ass1); - let (support_2, _) = build_support_map::(winners, ass2); - - for (who, support) in support_1.iter() { - assert_eq!(support.total, support_2.get(who).unwrap().total); - assert_eq!(support.voters, support_2.get(who).unwrap().voters); - - } -} - -#[allow(dead_code)] // to be used with fuzzing -pub fn reduce_and_compare( - assignment: &Vec>, - winners: &Vec, -) { - let mut altered_assignment = assignment.clone(); - crate::reduce(&mut altered_assignment); - assert_assignments_equal( - winners, - &assignment, - &altered_assignment, - ); -} diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index 18ef411dda8e2..ecc6a8c5e4354 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -20,8 +20,8 @@ use crate::mock::*; use crate::{ - elect, reduce, reduce_4, reduce_all, build_support_map, equalize, - Support, StakedAssignment, Assignment, PhragmenResult, ExtendedBalance, + elect, equalize, + Support, StakedAssignment, Assignment, PhragmenResult, }; use substrate_test_utils::assert_eq_uvec; use sp_runtime::{Perbill, Saturating, traits::Convert}; @@ -587,212 +587,6 @@ fn basic_from_and_into_compact_works_staked_assignments() { ); } - -#[test] -fn basic_reduce_4_cycle_works() { - let assignments = vec![ - StakedAssignment { - who: 1, - distribution: vec![ - (10, 25), - (20, 75), - ], - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 50), - (20, 50), - ], - }, - ]; - - let mut new_assignments = assignments.clone(); - let num_reduced = reduce_4(&mut new_assignments); - - assert_eq!(num_reduced, 1); - assert_eq!( - new_assignments, - vec![ - StakedAssignment { - who: 1, - distribution: vec![ - (20, 100), - ], - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 75), - (20, 25), - ], - }, - ], - ); -} - -#[test] -fn basic_reduce_all_cycles_works() { - let mut assignments = vec![ - StakedAssignment { - who: 1, - distribution: vec![(10, 10)] - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], - }, - StakedAssignment { - who: 3, - distribution: vec![ - (20, 15), - (40, 15) - ], - }, - StakedAssignment { - who: 4, distribution: - vec![ - (20, 10), - (30, 10), - (40, 20), - ] - }, - StakedAssignment { - who: 5, - distribution: vec![ - (20, 20), - (30, 10), - (40, 20), - ], - }, - ]; - - assert_eq!(3, reduce_all(&mut assignments)); - - assert_eq!( - assignments, - vec![ - StakedAssignment { - who: 1, - distribution: vec![ - (10, 10), - ] - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], - }, - StakedAssignment { - who: 3, - distribution: vec![ - (20, 30), - ], - }, - StakedAssignment { - who: 4, distribution: - vec![ - (40, 40), - ] - }, - StakedAssignment { - who: 5, - distribution: vec![ - (20, 15), - (30, 20), - (40, 15), - ], - }, - ], - ) -} - -#[test] -fn basic_reduce_works() { - let mut assignments = vec![ - StakedAssignment { - who: 1, - distribution: vec![(10, 10)] - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], - }, - StakedAssignment { - who: 3, - distribution: vec![ - (20, 15), - (40, 15) - ], - }, - StakedAssignment { - who: 4, distribution: - vec![ - (20, 10), - (30, 10), - (40, 20), - ] - }, - StakedAssignment { - who: 5, - distribution: vec![ - (20, 20), - (30, 10), - (40, 20), - ], - }, - ]; - - assert_eq!(3, reduce(&mut assignments)); - - assert_eq!( - assignments, - vec![ - StakedAssignment { - who: 1, - distribution: vec![ - (10, 10), - ] - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], - }, - StakedAssignment { - who: 3, - distribution: vec![ - (20, 30), - ], - }, - StakedAssignment { - who: 4, distribution: - vec![ - (40, 40), - ] - }, - StakedAssignment { - who: 5, - distribution: vec![ - (20, 15), - (30, 20), - (40, 15), - ], - }, - ], - ) -} - #[test] fn self_votes_should_be_kept() { let candidates = vec![5, 10, 20, 30]; From 05406463d4c2201dcffefda47baf957d019e785a Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 16 Jan 2020 09:28:26 +0100 Subject: [PATCH 013/106] move reduce into no_std --- Cargo.lock | 8 - Cargo.toml | 1 - frame/babe/src/mock.rs | 3 +- frame/staking/src/lib.rs | 2 +- primitives/phragmen/reduce/Cargo.toml | 11 - primitives/phragmen/reduce/src/lib.rs | 757 ------------------------- primitives/phragmen/reduce/src/node.rs | 134 ----- primitives/phragmen/src/lib.rs | 3 + primitives/phragmen/src/mock.rs | 5 +- primitives/phragmen/src/tests.rs | 2 +- 10 files changed, 8 insertions(+), 918 deletions(-) delete mode 100644 primitives/phragmen/reduce/Cargo.toml delete mode 100644 primitives/phragmen/reduce/src/lib.rs delete mode 100644 primitives/phragmen/reduce/src/node.rs diff --git a/Cargo.lock b/Cargo.lock index 73692c3e9b9fa..2f165f53f4c23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6517,14 +6517,6 @@ dependencies = [ "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "sp-phragmen-reduce" -version = "2.0.0" -dependencies = [ - "sp-phragmen 2.0.0", - "sp-runtime 2.0.0", -] - [[package]] name = "sp-rpc" version = "2.0.0" diff --git a/Cargo.toml b/Cargo.toml index d6857e0c954fd..a9af079d90e31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,7 +120,6 @@ members = [ "primitives/panic-handler", "primitives/phragmen", "primitives/phragmen/compact", - "primitives/phragmen/reduce", "primitives/rpc", "primitives/runtime-interface", "primitives/runtime-interface/proc-macro", diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index f9b49640aed31..4563b53df0957 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -26,6 +26,7 @@ use sp_runtime::{ traits::{IdentityLookup, OnInitialize}, }; use sp_version::RuntimeVersion; +use frame_system::InitKind; use frame_support::{impl_outer_origin, parameter_types, StorageValue, weights::Weight}; use sp_io; use sp_core::{H256, Blake2Hasher}; @@ -110,7 +111,7 @@ pub fn new_test_ext(authorities: Vec) -> sp_io::TestExternalit pub fn go_to_block(n: u64, s: u64) { let pre_digest = make_pre_digest(0, s, [1; 32], [0xff; 64]); - System::initialize(&n, &Default::default(), &Default::default(), &pre_digest); + System::initialize(&n, &Default::default(), &Default::default(), &pre_digest, InitKind::Full); System::set_block_number(n); if s > 1 { CurrentSlot::put(s); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 3214e331a78c2..1cb72b808923c 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -974,7 +974,7 @@ decl_module! { .collect(); // reduce the assignments. This will remove some additional edges. - sp_phragmen::reduce(&mut staked); + sp_phragmen::reduce::reduce(&mut staked); // compact encode the assignment. let compact = >::from_staked(staked); diff --git a/primitives/phragmen/reduce/Cargo.toml b/primitives/phragmen/reduce/Cargo.toml deleted file mode 100644 index f147c23b743a2..0000000000000 --- a/primitives/phragmen/reduce/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "sp-phragmen-reduce" -version = "2.0.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } -sp-phragmen = { path = "../" } - -[dev-dependencies] diff --git a/primitives/phragmen/reduce/src/lib.rs b/primitives/phragmen/reduce/src/lib.rs deleted file mode 100644 index 487a90198d96f..0000000000000 --- a/primitives/phragmen/reduce/src/lib.rs +++ /dev/null @@ -1,757 +0,0 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Rust implementation of the Phragmén reduce algorithm. This can be used by any off chain -//! application to reduce cycles from the edge assignment, which will result in smaller size. -//! -//! ### Notions -//! - `m`: size of the committee to elect. -//! - `k`: maximum allowed votes (16 as of this writing). -//! - `nv ∈ E` means that nominator `n ∈ N` supports the election of candidate `v ∈ V`. -//! - A valid solution consists of a tuple `(S, W)` , where `S ⊆ V` is a committee of m validators, -//! and `W : E → R ≥ 0` is an edge weight vector which describes how the budget of each nominator -//! n is fractionally assigned to n 's elected neighbors. -//! - `E_w := { e ∈ E : w_e > 0 }`. -//! -//! ### Algorithm overview -//! -//! > We consider the input edge weight vector `w` as a directed flow over `E_w` , where the flow in -//! > each edge is directed from the nominator to the validator. We build `w′` from `w` by removing -//! > **circulations** to cancel out the flow over as many edges as possible, while preserving flow -//! > demands over all vertices and without reverting the flow direction over any edge. As long as -//! > there is a cycle, we can remove an additional circulation and eliminate at least one new edge -//! > from `E_w′` . This shows that the algorithm always progresses and will eventually finish with -//! > an acyclic edge support. We keep a data structure that represents a forest of rooted trees, -//! > which is initialized as a collection of singletons – one per vertex – and to which edges in -//! > `E_w` are added one by one, causing the trees to merge. Whenever a new edge creates a cycle, -//! > we detect it and destroy it by removing a circulation. We also run a pre-computation which is -//! > designed to detect and remove cycles of length four exclusively. This pre-computation is -//! > optional, and if we skip it then the running time becomes `O (|E_w| ⋅ m), so the -//! > pre-computation makes sense only if `m >> k` and `|E_w| >> m^2`. -//! > -//! -//! ### Resources: -//! -//! 1. https://hackmd.io/JOn9x98iS0e0DPWQ87zGWg?view - -use std::collections::{btree_map::{Entry::*}}; -use std::collections::BTreeMap; - -use sp_runtime::traits::{Zero, Bounded}; -use sp_phragmen::{ExtendedBalance, StakedAssignment}; - -mod node; - -/// Map type used for caching. Can be easily swapped with HashMap. -type Map = BTreeMap<(A, A), A>; - -/// Returns all combinations of size two in the collection `input` with no repetition. -fn combinations_2(input: &[T]) -> Vec<(T, T)> { - let n = input.len(); - if n < 2 { - return Default::default() - } - - let mut comb = Vec::with_capacity(n * (n-1) / 2); - for i in 0..n { - for j in i+1..n { - comb.push((input[i].clone(), input[j].clone())) - } - } - comb -} - -/// Returns the count of trailing common elements in a slice. -fn trailing_common(t1: &[T], t2: &[T]) -> usize { - let mut t1_pointer = t1.len() - 1; - let mut t2_pointer = t2.len() - 1; - let mut common = 0usize; - - while t1[t1_pointer] == t2[t2_pointer] { - common += 1; - if t1_pointer == 0 || t2_pointer == 0 { - break; - } - t1_pointer -= 1; - t2_pointer -= 1; - } - - common -} - -/// Reduce only redundant edges with cycle length of 4. -/// -/// O(|E_w| ⋅ k). -fn reduce_4( - assignments: &mut Vec>, -) -> u32 { - - let mut combination_map: Map = Map::new(); - let mut num_changed: u32 = Zero::zero(); - - // NOTE: we have to use the old fashioned style loops here with manual indexing. Borrowing - // assignments will not work since then there is NO way to mutate it inside. - for i in 0..assignments.len() { - let who = assignments[i].who.clone(); - // immutable copy -- needed for further read operations. TODO: As an optimization at the - // expense of readability, we can remove this. - let distribution = &assignments[i].distribution.clone(); - - // all combinations for this particular voter - let candidate_combinations = combinations_2( - &distribution.iter().map(|(t, _p)| t.clone()).collect::>(), - ); - - for (v1, v2) in candidate_combinations { - match combination_map.entry((v1.clone(), v2.clone())) { - Vacant(entry) => { - entry.insert(who.clone()); - }, - Occupied(mut entry) => { - let other_who = entry.get_mut(); - println!("Occupied {:?} -> ({:?} {:?}) other: {:?}", &who, &v1, &v2, &other_who); - - // check if other_who voted for the same pair v1, v2. - let maybe_other_assignments = assignments.iter().find(|a| a.who == *other_who); - if maybe_other_assignments.is_none() { - // TODO: test for this path? - // This combination is not a cycle. - continue; - } - let other_assignment = maybe_other_assignments.expect("value is checked to be 'Some'"); - - // Collect potential cycle votes - let mut other_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); - other_assignment.distribution.iter().for_each(|(t, w)| { - if *t == v1 || *t == v2 { other_cycle_votes.push((t.clone(), *w)); } - }); - - // This is not a cycle. Replace and continue. - let other_votes_count = other_cycle_votes.len(); - // TODO: this might need testing. Some duplicate can cause this and this - // function should reject them. - debug_assert!(other_votes_count <= 2); - - if other_votes_count < 2 { - // Not a cycle. Replace and move on. - // TODO test fro this path?? - *other_who = who.clone(); - continue; - } else { - println!("And it is a cycle! "); - // This is a cycle. - let mut who_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); - distribution.iter().for_each(|(t, w)| { - if *t == v1 || *t == v2 { who_cycle_votes.push((t.clone(), *w)); } - }); - - if who_cycle_votes.len() != 2 { continue; } - - // Align the targets similarly. This helps with the circulation below. - if other_cycle_votes[0].0 != who_cycle_votes[0].0 { - other_cycle_votes.swap(0, 1); - } - - debug_assert_eq!(who_cycle_votes[0].0, other_cycle_votes[0].0); - debug_assert_eq!(who_cycle_votes[1].0, other_cycle_votes[1].0); - - // Find min - let mut min_value: ExtendedBalance = Bounded::max_value(); - let mut min_index: usize = 0; - let cycle = who_cycle_votes - .iter() - .chain(other_cycle_votes.iter()) - .enumerate() - .map(|(index, (t, w))| { - if *w <= min_value { min_value = *w; min_index = index; } - (t.clone(), *w) - }).collect::>(); - dbg!(&cycle, &min_value); - - // min was in the first part of the chained iters - let mut increase_indices: Vec = Vec::new(); - let mut decrease_indices: Vec = Vec::new(); - decrease_indices.push(min_index); - if min_index < 2 { - // min_index == 0 => sibling_index <- 1 - // min_index == 1 => sibling_index <- 0 - let sibling_index = 1 - min_index; - increase_indices.push(sibling_index); - // valid because the two chained sections of `cycle` are aligned; - // index [0, 2] are both voting for v1 or both v2. Same goes for [1, 3]. - decrease_indices.push(sibling_index + 2); - increase_indices.push(min_index + 2); - } else { - // min_index == 2 => sibling_index <- 3 - // min_index == 3 => sibling_index <- 2 - let sibling_index = 3 - min_index % 2; - increase_indices.push(sibling_index); - // valid because the two chained sections of `cycle` are aligned; - // index [0, 2] are both voting for v1 or both v2. Same goes for [1, 3]. - decrease_indices.push(sibling_index - 2); - increase_indices.push(min_index - 2); - } - - dbg!(&increase_indices, &decrease_indices); - - // apply changes - increase_indices.into_iter().for_each(|i| { - assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { - ass.distribution - .iter_mut() - .position(|(t, _)| *t == cycle[i].0) - .map(|idx| { - let next_value = ass.distribution[idx].1.saturating_add(min_value); - if next_value.is_zero() { - ass.distribution.remove(idx); - } else { - ass.distribution[idx].1 = next_value; - } - }); - }); - }); - decrease_indices.into_iter().for_each(|i| { - assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { - ass.distribution - .iter_mut() - .position(|(t, _)| *t == cycle[i].0) - .map(|idx| { - let next_value = ass.distribution[idx].1.saturating_sub(min_value); - if next_value.is_zero() { - ass.distribution.remove(idx); - num_changed += 1; - } else { - ass.distribution[idx].1 = next_value; - } - }); - }); - }); - } - } - } - } - } - - num_changed -} - -/// Reduce all redundant edges from the edge weight graph. -/// -/// O(|Ew| ⋅ m) -fn reduce_all( - assignments: &mut Vec>, -) -> u32 { - let mut num_changed: u32 = Zero::zero(); - - // ----------------- Phase 2: remove any other cycle - use node::{Node, NodeRef, NodeRole}; - let mut tree: BTreeMap> = BTreeMap::new(); - - // a flat iterator of (voter, target) over all pairs of votes. Similar to reduce_4, we loop - // without borrowing. - for i in 0..assignments.len() { - let voter = assignments[i].who.clone(); - - for j in 0..assignments[i].distribution.len() { - let (target, _) = assignments[i].distribution[j].clone(); - - let voter_node = tree.entry(voter.clone()) - .or_insert(Node::new(voter.clone(), NodeRole::Voter).into_ref()).clone(); - let target_node = tree.entry(target.clone()) - .or_insert(Node::new(target.clone(), NodeRole::Target).into_ref()).clone(); - - if !voter_node.borrow().has_parent() { - Node::set_parent_of(&voter_node, &target_node); - continue; - } - if !target_node.borrow().has_parent() { - Node::set_parent_of(&target_node, &voter_node); - continue; - } - - let (voter_root, voter_root_path) = Node::root(&voter_node); - let (target_root, target_root_path) = Node::root(&target_node); - - if voter_root != target_root { - // swap - // TODO: test case for this path - if voter_root_path.len() <= target_root_path.len() { - // iterate from last to beginning, skipping the first one. This asserts that - // indexing is always correct. - voter_root_path - .iter() - .skip(1) - .rev() - .enumerate() - .map(|(index, r)| (voter_root_path.len() - index - 1, r)) - .for_each(|(index, r)| { - let index = voter_root_path.len() - index; - Node::set_parent_of(r, &voter_root_path[index-1]) - }); - debug_assert_eq!(voter_root_path[0], voter_node); - Node::set_parent_of(&voter_node, &target_node); - } else { - target_root_path - .iter() - .skip(1) - .rev() - .enumerate() - .map(|(index, r)| (target_root_path.len() - index - 1, r)) - .for_each(|(index, r)| { - let index = target_root_path.len() - index; - Node::set_parent_of(r, &target_root_path[index-1]) - }); - debug_assert_eq!(target_root_path[0], target_node); - Node::set_parent_of(&target_node, &voter_node); - } - } else { - debug_assert_eq!(target_root_path.last().unwrap(), voter_root_path.last().unwrap()); - - // find common and cycle. - let common_count = trailing_common(&voter_root_path, &target_root_path); - - // because roots are the same. TODO: replace with a bail-out - debug_assert!(common_count > 0); - - // cycle part of each path will be `path[path.len() - common_count - 1 : 0]` - // NOTE: the order of chaining is important! it is always build from [target, ..., - // voter] - // TODO: check borrows panic! - let cycle = - target_root_path.iter().take(target_root_path.len() - common_count + 1).cloned() - .chain(voter_root_path.iter().take(voter_root_path.len() - common_count).rev().cloned()) - .collect::>>(); - - - // TODO: a cycle struct that gives you min + to which chunk it belonged. - // find minimum of cycle. - let mut min_value: ExtendedBalance = Bounded::max_value(); - // Note that this can only ever point to a target, not a voter. - let mut min_who: AccountId = Default::default(); - let mut min_index = 0usize; - // 1 -> next // 0 -> prev TOOD: I have some ideas of fixing this. - let mut min_direction = 0u32; - // helpers - let next_index = |i| { if i < (cycle.len() - 1) { i + 1 } else { 0 } }; - let prev_index = |i| { if i > 0 { i - 1 } else { cycle.len() - 1 } }; - for i in 0..cycle.len() { - if cycle[i].borrow().role == NodeRole::Voter { - // NOTE: sadly way too many clones since I don't want to make AccountId: Copy - let current = cycle[i].borrow().who.clone(); - let next = cycle[next_index(i)].borrow().who.clone(); - let prev = cycle[prev_index(i)].borrow().who.clone(); - assignments.iter().find(|a| a.who == current).map(|ass| { - ass.distribution.iter().find(|d| d.0 == next).map(|(_, w)| { - if *w < min_value { - min_value = *w; - min_who = next.clone(); - min_index = i; - min_direction = 1; - } - }) - }); - assignments.iter().find(|a| a.who == current).map(|ass| { - ass.distribution.iter().find(|d| d.0 == prev).map(|(_, w)| { - if *w < min_value { - min_value = *w; - min_who = prev.clone(); - min_index = i; - min_direction = 0; - } - }) - }); - } - } - // if the min edge is in the voter's sub-chain. - let target_chunk = target_root_path.len() - common_count; - let min_chain_in_voter = (min_index + min_direction as usize) >= target_chunk; - - dbg!(min_value, min_index, &min_who, min_direction); - - // walk over the cycle and update the weights - // TODO: or at least unify and merge the process of adding this flow in both places. and surely add dedicated tests for this supply demand circulation math - // if the circulation begins with an addition. It is assumed that a cycle always starts with a target. - let start_operation_add = ((min_index % 2) + min_direction as usize) % 2 == 1; - for i in 0..cycle.len() { - let current = cycle[i].borrow(); - if current.role == NodeRole::Voter { - let prev = cycle[prev_index(i)].borrow(); - - assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { - ass.distribution.iter_mut().position(|(t, _)| *t == prev.who).map(|idx| { - let next_value = if i % 2 == 0 { - if start_operation_add { - ass.distribution[idx].1.saturating_add(min_value) - } else { - ass.distribution[idx].1.saturating_sub(min_value) - } - } else { - if start_operation_add { - ass.distribution[idx].1.saturating_sub(min_value) - } else { - ass.distribution[idx].1.saturating_add(min_value) - } - }; - // println!("Next value for edge {:?} -> {:?} is {}", current.who, prev.who, next_value); - if next_value.is_zero() { - ass.distribution.remove(idx); - num_changed += 1; - } else { - ass.distribution[idx].1 = next_value; - } - }); - }); - - let next = cycle[next_index(i)].borrow(); - - assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { - ass.distribution.iter_mut().position(|(t, _)| *t == next.who).map(|idx| { - let next_value = if i % 2 == 0 { - if start_operation_add { - ass.distribution[idx].1.saturating_sub(min_value) - } else { - ass.distribution[idx].1.saturating_add(min_value) - } - } else { - if start_operation_add { - ass.distribution[idx].1.saturating_add(min_value) - } else { - ass.distribution[idx].1.saturating_sub(min_value) - } - }; - // println!("Next value for edge {:?} -> {:?} is {}", current.who, next.who, next_value); - if next_value.is_zero() { - ass.distribution.remove(idx); - num_changed += 1; - } else { - ass.distribution[idx].1 = next_value; - } - }); - }); - } - }; - - // don't do anything if the edge removed itself - if min_index == (cycle.len() - 1) && min_direction == 1 { continue; } - - // TODO: this is most likely buggy - // re-org otherwise. - if min_chain_in_voter { - // NOTE: safe; voter_root_path is always bigger than 1 element. - for i in 0..voter_root_path.len()-1 { - let next = voter_root_path[i + 1].clone(); - if next.borrow().who == min_who { - break; - } - Node::set_parent_of(&voter_root_path[i + 1], &voter_root_path[i]); - } - Node::set_parent_of(&voter_node, &target_node); - } else { - // NOTE: safe; target_root_path is always bigger than 1 element. - for i in 0..target_root_path.len()-1 { - if target_root_path[i].borrow().who == min_who { - break; - } - Node::set_parent_of(&target_root_path[i + 1], &target_root_path[i]); - } - Node::set_parent_of(&target_node, &voter_node); - } - - - - } - } - } - - num_changed -} - -/// Reduce the given [`PhragmenResult`]. This removes redundant edges from without changing the -/// overall backing of any of the elected candidates. -/// -/// O(min{ |Ew| ⋅ k + m3 , |Ew| ⋅ m }) -pub fn reduce< - AccountId: Clone + Eq + Default + Ord + std::fmt::Debug, ->( - assignments: &mut Vec>, -) -> u32 where { - let mut num_changed = reduce_4(assignments); - num_changed += reduce_all(assignments); - num_changed -} - -#[cfg(test)] -mod tests { - use super::*; - use sp_phragmen::build_support_map; - - type AccountId = u64; - type Balance = u128; - - #[allow(dead_code)] // to be used with fuzzing - pub fn assert_assignments_equal( - winners: &Vec, - ass1: &Vec>, - ass2: &Vec>, - ) { - let (support_1, _) = build_support_map::(winners, ass1); - let (support_2, _) = build_support_map::(winners, ass2); - - for (who, support) in support_1.iter() { - assert_eq!(support.total, support_2.get(who).unwrap().total); - assert_eq!(support.voters, support_2.get(who).unwrap().voters); - - } - } - - #[allow(dead_code)] // to be used with fuzzing - pub fn reduce_and_compare( - assignment: &Vec>, - winners: &Vec, - ) { - let mut altered_assignment = assignment.clone(); - crate::reduce(&mut altered_assignment); - assert_assignments_equal( - winners, - &assignment, - &altered_assignment, - ); - } - - #[test] - fn trailing_common_works() { - assert_eq!( - trailing_common(&vec![1u8, 2, 3, 4, 5], &vec![7u8, 8, 4, 5]), - 2, - ); - assert_eq!( - trailing_common(&vec![1u8, 2], &vec![7u8, 8]), - 0, - ); - assert_eq!( - trailing_common(&vec![1u8, 2, 3, 4, 5], &vec![3u8, 4, 5]), - 3, - ); - } - - #[test] - fn basic_reduce_4_cycle_works() { - use super::*; - - let assignments = vec![ - StakedAssignment { - who: 1, - distribution: vec![ - (10, 25), - (20, 75), - ], - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 50), - (20, 50), - ], - }, - ]; - - let mut new_assignments = assignments.clone(); - let num_reduced = reduce_4(&mut new_assignments); - - assert_eq!(num_reduced, 1); - assert_eq!( - new_assignments, - vec![ - StakedAssignment { - who: 1, - distribution: vec![ - (20, 100), - ], - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 75), - (20, 25), - ], - }, - ], - ); - } - - #[test] - fn basic_reduce_all_cycles_works() { - let mut assignments = vec![ - StakedAssignment { - who: 1, - distribution: vec![(10, 10)] - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], - }, - StakedAssignment { - who: 3, - distribution: vec![ - (20, 15), - (40, 15) - ], - }, - StakedAssignment { - who: 4, distribution: - vec![ - (20, 10), - (30, 10), - (40, 20), - ] - }, - StakedAssignment { - who: 5, - distribution: vec![ - (20, 20), - (30, 10), - (40, 20), - ], - }, - ]; - - assert_eq!(3, reduce_all(&mut assignments)); - - assert_eq!( - assignments, - vec![ - StakedAssignment { - who: 1, - distribution: vec![ - (10, 10), - ] - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], - }, - StakedAssignment { - who: 3, - distribution: vec![ - (20, 30), - ], - }, - StakedAssignment { - who: 4, distribution: - vec![ - (40, 40), - ] - }, - StakedAssignment { - who: 5, - distribution: vec![ - (20, 15), - (30, 20), - (40, 15), - ], - }, - ], - ) - } - - #[test] - fn basic_reduce_works() { - let mut assignments = vec![ - StakedAssignment { - who: 1, - distribution: vec![(10, 10)] - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], - }, - StakedAssignment { - who: 3, - distribution: vec![ - (20, 15), - (40, 15) - ], - }, - StakedAssignment { - who: 4, distribution: - vec![ - (20, 10), - (30, 10), - (40, 20), - ] - }, - StakedAssignment { - who: 5, - distribution: vec![ - (20, 20), - (30, 10), - (40, 20), - ], - }, - ]; - - assert_eq!(3, reduce(&mut assignments)); - - assert_eq!( - assignments, - vec![ - StakedAssignment { - who: 1, - distribution: vec![ - (10, 10), - ] - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], - }, - StakedAssignment { - who: 3, - distribution: vec![ - (20, 30), - ], - }, - StakedAssignment { - who: 4, distribution: - vec![ - (40, 40), - ] - }, - StakedAssignment { - who: 5, - distribution: vec![ - (20, 15), - (30, 20), - (40, 15), - ], - }, - ], - ) - } - -} diff --git a/primitives/phragmen/reduce/src/node.rs b/primitives/phragmen/reduce/src/node.rs deleted file mode 100644 index beadd8a16a70b..0000000000000 --- a/primitives/phragmen/reduce/src/node.rs +++ /dev/null @@ -1,134 +0,0 @@ -use std::cell::RefCell; -use std::rc::Rc; - -#[derive(PartialEq, Eq, std::fmt::Debug)] -pub(crate) enum NodeRole { - Voter, - Target, -} - -pub(crate) type NodeRef = Rc>>; - -#[derive(PartialEq, Eq)] -pub(crate) struct Node { - pub(crate) who: A, - pub(crate) role: NodeRole, - pub(crate) parent: Option>, -} - -use std::fmt; -impl fmt::Debug for Node { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({:?} [--> {:?})]", self.who, self.parent.as_ref().map(|p| p.borrow().who.clone())) - } -} - -impl Node { - pub fn new(who: A, role: NodeRole) -> Node { - Self { - who, - role, - parent: None, - } - } - - pub fn has_parent(&self) -> bool { - self.parent.is_some() - } - - pub fn set_parent_of(target: &NodeRef, parent: &NodeRef) { - target.borrow_mut().parent = Some(parent.clone()); - } - - pub fn root(start: &NodeRef) -> (NodeRef, Vec>) { - let mut parent_path: Vec> = Vec::new(); - parent_path.push(start.clone()); - - let mut current = parent_path[0].clone(); - while let Some(ref next_parent) = current.clone().borrow().parent { - parent_path.push(next_parent.clone()); - current = next_parent.clone(); - } - - (current, parent_path) - } - - pub fn into_ref(self) -> NodeRef { - Rc::from(RefCell::from(self)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn basic_create_works() { - let node = Node::new(10u32, NodeRole::Target); - assert_eq!(node, Node { who: 10u32, parent: None, role: NodeRole::Target }); - } - - #[test] - fn set_parent_works() { - let a = Node::new(10u32, NodeRole::Target).into_ref(); - let b = Node::new(20u32, NodeRole::Target).into_ref(); - - assert_eq!(a.borrow().parent, None); - Node::set_parent_of(&a, &b); - assert_eq!(*a.borrow().parent.as_ref().unwrap(), b); - } - - #[test] - fn get_root_singular() { - let a = Node::new(1u32, NodeRole::Target).into_ref(); - assert_eq!(Node::root(&a), (a.clone(), vec![a.clone()])); - } - - #[test] - fn get_root_works() { - // D <-- A <-- B <-- C - // \ - // <-- E - let d = Node::new(1u32, NodeRole::Target).into_ref(); - let a = Node::new(1u32, NodeRole::Target).into_ref(); - let b = Node::new(1u32, NodeRole::Target).into_ref(); - let c = Node::new(1u32, NodeRole::Target).into_ref(); - let e = Node::new(1u32, NodeRole::Target).into_ref(); - let f = Node::new(1u32, NodeRole::Target).into_ref(); - - Node::set_parent_of(&c, &b); - Node::set_parent_of(&b, &a); - Node::set_parent_of(&e, &a); - Node::set_parent_of(&a, &d); - - assert_eq!( - Node::root(&e), - (d.clone(), vec![e.clone(), a.clone(), d.clone()]), - ); - - assert_eq!( - Node::root(&a), - (d.clone(), vec![a.clone(), d.clone()]), - ); - - assert_eq!( - Node::root(&c), - (d.clone(), vec![c.clone(), b.clone(), a.clone(), d.clone()]), - ); - - // D A <-- B <-- C - // F <-- / \ - // <-- E - Node::set_parent_of(&a, &f); - - assert_eq!( - Node::root(&a), - (f.clone(), vec![a.clone(), f.clone()]), - ); - - assert_eq!( - Node::root(&c), - (d.clone(), vec![c.clone(), b.clone(), a.clone(), f.clone()]), - ); - } -} diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 1e05f3224d2cd..5497d09bbcf9f 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -42,6 +42,9 @@ mod mock; #[cfg(test)] mod tests; +mod node; +pub mod reduce; + /// A type in which performing operations on balances and stakes of candidates and voters are safe. /// /// This module's functions expect a `Convert` type to convert all balances to u64. Hence, u128 is diff --git a/primitives/phragmen/src/mock.rs b/primitives/phragmen/src/mock.rs index 8da79cdda4bea..8d9217372056a 100644 --- a/primitives/phragmen/src/mock.rs +++ b/primitives/phragmen/src/mock.rs @@ -18,10 +18,7 @@ #![cfg(test)] -use crate::{ - elect, build_support_map, - PhragmenResult, Assignment, StakedAssignment, -}; +use crate::{elect, PhragmenResult, Assignment}; use sp_runtime::{ assert_eq_error_rate, Perbill, traits::{Convert, Member, SaturatedConversion} diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index ecc6a8c5e4354..bb11ff125878a 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -20,7 +20,7 @@ use crate::mock::*; use crate::{ - elect, equalize, + elect, equalize, build_support_map, Support, StakedAssignment, Assignment, PhragmenResult, }; use substrate_test_utils::assert_eq_uvec; From da66da28e02e9839c1ebe7bd54cf2329d7869089 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 16 Jan 2020 09:28:35 +0100 Subject: [PATCH 014/106] Add files --- primitives/phragmen/src/node.rs | 134 ++++++ primitives/phragmen/src/reduce.rs | 755 ++++++++++++++++++++++++++++++ 2 files changed, 889 insertions(+) create mode 100644 primitives/phragmen/src/node.rs create mode 100644 primitives/phragmen/src/reduce.rs diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs new file mode 100644 index 0000000000000..beadd8a16a70b --- /dev/null +++ b/primitives/phragmen/src/node.rs @@ -0,0 +1,134 @@ +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(PartialEq, Eq, std::fmt::Debug)] +pub(crate) enum NodeRole { + Voter, + Target, +} + +pub(crate) type NodeRef = Rc>>; + +#[derive(PartialEq, Eq)] +pub(crate) struct Node { + pub(crate) who: A, + pub(crate) role: NodeRole, + pub(crate) parent: Option>, +} + +use std::fmt; +impl fmt::Debug for Node { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({:?} [--> {:?})]", self.who, self.parent.as_ref().map(|p| p.borrow().who.clone())) + } +} + +impl Node { + pub fn new(who: A, role: NodeRole) -> Node { + Self { + who, + role, + parent: None, + } + } + + pub fn has_parent(&self) -> bool { + self.parent.is_some() + } + + pub fn set_parent_of(target: &NodeRef, parent: &NodeRef) { + target.borrow_mut().parent = Some(parent.clone()); + } + + pub fn root(start: &NodeRef) -> (NodeRef, Vec>) { + let mut parent_path: Vec> = Vec::new(); + parent_path.push(start.clone()); + + let mut current = parent_path[0].clone(); + while let Some(ref next_parent) = current.clone().borrow().parent { + parent_path.push(next_parent.clone()); + current = next_parent.clone(); + } + + (current, parent_path) + } + + pub fn into_ref(self) -> NodeRef { + Rc::from(RefCell::from(self)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic_create_works() { + let node = Node::new(10u32, NodeRole::Target); + assert_eq!(node, Node { who: 10u32, parent: None, role: NodeRole::Target }); + } + + #[test] + fn set_parent_works() { + let a = Node::new(10u32, NodeRole::Target).into_ref(); + let b = Node::new(20u32, NodeRole::Target).into_ref(); + + assert_eq!(a.borrow().parent, None); + Node::set_parent_of(&a, &b); + assert_eq!(*a.borrow().parent.as_ref().unwrap(), b); + } + + #[test] + fn get_root_singular() { + let a = Node::new(1u32, NodeRole::Target).into_ref(); + assert_eq!(Node::root(&a), (a.clone(), vec![a.clone()])); + } + + #[test] + fn get_root_works() { + // D <-- A <-- B <-- C + // \ + // <-- E + let d = Node::new(1u32, NodeRole::Target).into_ref(); + let a = Node::new(1u32, NodeRole::Target).into_ref(); + let b = Node::new(1u32, NodeRole::Target).into_ref(); + let c = Node::new(1u32, NodeRole::Target).into_ref(); + let e = Node::new(1u32, NodeRole::Target).into_ref(); + let f = Node::new(1u32, NodeRole::Target).into_ref(); + + Node::set_parent_of(&c, &b); + Node::set_parent_of(&b, &a); + Node::set_parent_of(&e, &a); + Node::set_parent_of(&a, &d); + + assert_eq!( + Node::root(&e), + (d.clone(), vec![e.clone(), a.clone(), d.clone()]), + ); + + assert_eq!( + Node::root(&a), + (d.clone(), vec![a.clone(), d.clone()]), + ); + + assert_eq!( + Node::root(&c), + (d.clone(), vec![c.clone(), b.clone(), a.clone(), d.clone()]), + ); + + // D A <-- B <-- C + // F <-- / \ + // <-- E + Node::set_parent_of(&a, &f); + + assert_eq!( + Node::root(&a), + (f.clone(), vec![a.clone(), f.clone()]), + ); + + assert_eq!( + Node::root(&c), + (d.clone(), vec![c.clone(), b.clone(), a.clone(), f.clone()]), + ); + } +} diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs new file mode 100644 index 0000000000000..e0ece9b098724 --- /dev/null +++ b/primitives/phragmen/src/reduce.rs @@ -0,0 +1,755 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Rust implementation of the Phragmén reduce algorithm. This can be used by any off chain +//! application to reduce cycles from the edge assignment, which will result in smaller size. +//! +//! ### Notions +//! - `m`: size of the committee to elect. +//! - `k`: maximum allowed votes (16 as of this writing). +//! - `nv ∈ E` means that nominator `n ∈ N` supports the election of candidate `v ∈ V`. +//! - A valid solution consists of a tuple `(S, W)` , where `S ⊆ V` is a committee of m validators, +//! and `W : E → R ≥ 0` is an edge weight vector which describes how the budget of each nominator +//! n is fractionally assigned to n 's elected neighbors. +//! - `E_w := { e ∈ E : w_e > 0 }`. +//! +//! ### Algorithm overview +//! +//! > We consider the input edge weight vector `w` as a directed flow over `E_w` , where the flow in +//! > each edge is directed from the nominator to the validator. We build `w′` from `w` by removing +//! > **circulations** to cancel out the flow over as many edges as possible, while preserving flow +//! > demands over all vertices and without reverting the flow direction over any edge. As long as +//! > there is a cycle, we can remove an additional circulation and eliminate at least one new edge +//! > from `E_w′` . This shows that the algorithm always progresses and will eventually finish with +//! > an acyclic edge support. We keep a data structure that represents a forest of rooted trees, +//! > which is initialized as a collection of singletons – one per vertex – and to which edges in +//! > `E_w` are added one by one, causing the trees to merge. Whenever a new edge creates a cycle, +//! > we detect it and destroy it by removing a circulation. We also run a pre-computation which is +//! > designed to detect and remove cycles of length four exclusively. This pre-computation is +//! > optional, and if we skip it then the running time becomes `O (|E_w| ⋅ m), so the +//! > pre-computation makes sense only if `m >> k` and `|E_w| >> m^2`. +//! > +//! +//! ### Resources: +//! +//! 1. https://hackmd.io/JOn9x98iS0e0DPWQ87zGWg?view + +use sp_std::collections::{btree_map::{Entry::*, BTreeMap}}; + +use crate::node::{Node, NodeRef, NodeRole}; + +use sp_runtime::traits::{Zero, Bounded}; +use crate::{ExtendedBalance, StakedAssignment}; + +/// Map type used for caching. Can be easily swapped with HashMap. +type Map = BTreeMap<(A, A), A>; + +/// Returns all combinations of size two in the collection `input` with no repetition. +fn combinations_2(input: &[T]) -> Vec<(T, T)> { + let n = input.len(); + if n < 2 { + return Default::default() + } + + let mut comb = Vec::with_capacity(n * (n-1) / 2); + for i in 0..n { + for j in i+1..n { + comb.push((input[i].clone(), input[j].clone())) + } + } + comb +} + +/// Returns the count of trailing common elements in a slice. +fn trailing_common(t1: &[T], t2: &[T]) -> usize { + let mut t1_pointer = t1.len() - 1; + let mut t2_pointer = t2.len() - 1; + let mut common = 0usize; + + while t1[t1_pointer] == t2[t2_pointer] { + common += 1; + if t1_pointer == 0 || t2_pointer == 0 { + break; + } + t1_pointer -= 1; + t2_pointer -= 1; + } + + common +} + +/// Reduce only redundant edges with cycle length of 4. +/// +/// O(|E_w| ⋅ k). +fn reduce_4( + assignments: &mut Vec>, +) -> u32 { + + let mut combination_map: Map = Map::new(); + let mut num_changed: u32 = Zero::zero(); + + // NOTE: we have to use the old fashioned style loops here with manual indexing. Borrowing + // assignments will not work since then there is NO way to mutate it inside. + for i in 0..assignments.len() { + let who = assignments[i].who.clone(); + // immutable copy -- needed for further read operations. TODO: As an optimization at the + // expense of readability, we can remove this. + let distribution = &assignments[i].distribution.clone(); + + // all combinations for this particular voter + let candidate_combinations = combinations_2( + &distribution.iter().map(|(t, _p)| t.clone()).collect::>(), + ); + + for (v1, v2) in candidate_combinations { + match combination_map.entry((v1.clone(), v2.clone())) { + Vacant(entry) => { + entry.insert(who.clone()); + }, + Occupied(mut entry) => { + let other_who = entry.get_mut(); + println!("Occupied {:?} -> ({:?} {:?}) other: {:?}", &who, &v1, &v2, &other_who); + + // check if other_who voted for the same pair v1, v2. + let maybe_other_assignments = assignments.iter().find(|a| a.who == *other_who); + if maybe_other_assignments.is_none() { + // TODO: test for this path? + // This combination is not a cycle. + continue; + } + let other_assignment = maybe_other_assignments.expect("value is checked to be 'Some'"); + + // Collect potential cycle votes + let mut other_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); + other_assignment.distribution.iter().for_each(|(t, w)| { + if *t == v1 || *t == v2 { other_cycle_votes.push((t.clone(), *w)); } + }); + + // This is not a cycle. Replace and continue. + let other_votes_count = other_cycle_votes.len(); + // TODO: this might need testing. Some duplicate can cause this and this + // function should reject them. + debug_assert!(other_votes_count <= 2); + + if other_votes_count < 2 { + // Not a cycle. Replace and move on. + // TODO test fro this path?? + *other_who = who.clone(); + continue; + } else { + println!("And it is a cycle! "); + // This is a cycle. + let mut who_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); + distribution.iter().for_each(|(t, w)| { + if *t == v1 || *t == v2 { who_cycle_votes.push((t.clone(), *w)); } + }); + + if who_cycle_votes.len() != 2 { continue; } + + // Align the targets similarly. This helps with the circulation below. + if other_cycle_votes[0].0 != who_cycle_votes[0].0 { + other_cycle_votes.swap(0, 1); + } + + debug_assert_eq!(who_cycle_votes[0].0, other_cycle_votes[0].0); + debug_assert_eq!(who_cycle_votes[1].0, other_cycle_votes[1].0); + + // Find min + let mut min_value: ExtendedBalance = Bounded::max_value(); + let mut min_index: usize = 0; + let cycle = who_cycle_votes + .iter() + .chain(other_cycle_votes.iter()) + .enumerate() + .map(|(index, (t, w))| { + if *w <= min_value { min_value = *w; min_index = index; } + (t.clone(), *w) + }).collect::>(); + dbg!(&cycle, &min_value); + + // min was in the first part of the chained iters + let mut increase_indices: Vec = Vec::new(); + let mut decrease_indices: Vec = Vec::new(); + decrease_indices.push(min_index); + if min_index < 2 { + // min_index == 0 => sibling_index <- 1 + // min_index == 1 => sibling_index <- 0 + let sibling_index = 1 - min_index; + increase_indices.push(sibling_index); + // valid because the two chained sections of `cycle` are aligned; + // index [0, 2] are both voting for v1 or both v2. Same goes for [1, 3]. + decrease_indices.push(sibling_index + 2); + increase_indices.push(min_index + 2); + } else { + // min_index == 2 => sibling_index <- 3 + // min_index == 3 => sibling_index <- 2 + let sibling_index = 3 - min_index % 2; + increase_indices.push(sibling_index); + // valid because the two chained sections of `cycle` are aligned; + // index [0, 2] are both voting for v1 or both v2. Same goes for [1, 3]. + decrease_indices.push(sibling_index - 2); + increase_indices.push(min_index - 2); + } + + dbg!(&increase_indices, &decrease_indices); + + // apply changes + increase_indices.into_iter().for_each(|i| { + assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { + ass.distribution + .iter_mut() + .position(|(t, _)| *t == cycle[i].0) + .map(|idx| { + let next_value = ass.distribution[idx].1.saturating_add(min_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); + }); + decrease_indices.into_iter().for_each(|i| { + assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { + ass.distribution + .iter_mut() + .position(|(t, _)| *t == cycle[i].0) + .map(|idx| { + let next_value = ass.distribution[idx].1.saturating_sub(min_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + num_changed += 1; + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); + }); + } + } + } + } + } + + num_changed +} + +/// Reduce all redundant edges from the edge weight graph. +/// +/// O(|Ew| ⋅ m) +fn reduce_all( + assignments: &mut Vec>, +) -> u32 { + let mut num_changed: u32 = Zero::zero(); + + // ----------------- Phase 2: remove any other cycle + let mut tree: BTreeMap> = BTreeMap::new(); + + // a flat iterator of (voter, target) over all pairs of votes. Similar to reduce_4, we loop + // without borrowing. + for i in 0..assignments.len() { + let voter = assignments[i].who.clone(); + + for j in 0..assignments[i].distribution.len() { + let (target, _) = assignments[i].distribution[j].clone(); + + let voter_node = tree.entry(voter.clone()) + .or_insert(Node::new(voter.clone(), NodeRole::Voter).into_ref()).clone(); + let target_node = tree.entry(target.clone()) + .or_insert(Node::new(target.clone(), NodeRole::Target).into_ref()).clone(); + + if !voter_node.borrow().has_parent() { + Node::set_parent_of(&voter_node, &target_node); + continue; + } + if !target_node.borrow().has_parent() { + Node::set_parent_of(&target_node, &voter_node); + continue; + } + + let (voter_root, voter_root_path) = Node::root(&voter_node); + let (target_root, target_root_path) = Node::root(&target_node); + + if voter_root != target_root { + // swap + // TODO: test case for this path + if voter_root_path.len() <= target_root_path.len() { + // iterate from last to beginning, skipping the first one. This asserts that + // indexing is always correct. + voter_root_path + .iter() + .skip(1) + .rev() + .enumerate() + .map(|(index, r)| (voter_root_path.len() - index - 1, r)) + .for_each(|(index, r)| { + let index = voter_root_path.len() - index; + Node::set_parent_of(r, &voter_root_path[index-1]) + }); + debug_assert_eq!(voter_root_path[0], voter_node); + Node::set_parent_of(&voter_node, &target_node); + } else { + target_root_path + .iter() + .skip(1) + .rev() + .enumerate() + .map(|(index, r)| (target_root_path.len() - index - 1, r)) + .for_each(|(index, r)| { + let index = target_root_path.len() - index; + Node::set_parent_of(r, &target_root_path[index-1]) + }); + debug_assert_eq!(target_root_path[0], target_node); + Node::set_parent_of(&target_node, &voter_node); + } + } else { + debug_assert_eq!(target_root_path.last().unwrap(), voter_root_path.last().unwrap()); + + // find common and cycle. + let common_count = trailing_common(&voter_root_path, &target_root_path); + + // because roots are the same. TODO: replace with a bail-out + debug_assert!(common_count > 0); + + // cycle part of each path will be `path[path.len() - common_count - 1 : 0]` + // NOTE: the order of chaining is important! it is always build from [target, ..., + // voter] + // TODO: check borrows panic! + let cycle = + target_root_path.iter().take(target_root_path.len() - common_count + 1).cloned() + .chain(voter_root_path.iter().take(voter_root_path.len() - common_count).rev().cloned()) + .collect::>>(); + + + // TODO: a cycle struct that gives you min + to which chunk it belonged. + // find minimum of cycle. + let mut min_value: ExtendedBalance = Bounded::max_value(); + // Note that this can only ever point to a target, not a voter. + let mut min_who: AccountId = Default::default(); + let mut min_index = 0usize; + // 1 -> next // 0 -> prev TOOD: I have some ideas of fixing this. + let mut min_direction = 0u32; + // helpers + let next_index = |i| { if i < (cycle.len() - 1) { i + 1 } else { 0 } }; + let prev_index = |i| { if i > 0 { i - 1 } else { cycle.len() - 1 } }; + for i in 0..cycle.len() { + if cycle[i].borrow().role == NodeRole::Voter { + // NOTE: sadly way too many clones since I don't want to make AccountId: Copy + let current = cycle[i].borrow().who.clone(); + let next = cycle[next_index(i)].borrow().who.clone(); + let prev = cycle[prev_index(i)].borrow().who.clone(); + assignments.iter().find(|a| a.who == current).map(|ass| { + ass.distribution.iter().find(|d| d.0 == next).map(|(_, w)| { + if *w < min_value { + min_value = *w; + min_who = next.clone(); + min_index = i; + min_direction = 1; + } + }) + }); + assignments.iter().find(|a| a.who == current).map(|ass| { + ass.distribution.iter().find(|d| d.0 == prev).map(|(_, w)| { + if *w < min_value { + min_value = *w; + min_who = prev.clone(); + min_index = i; + min_direction = 0; + } + }) + }); + } + } + // if the min edge is in the voter's sub-chain. + let target_chunk = target_root_path.len() - common_count; + let min_chain_in_voter = (min_index + min_direction as usize) >= target_chunk; + + dbg!(min_value, min_index, &min_who, min_direction); + + // walk over the cycle and update the weights + // TODO: or at least unify and merge the process of adding this flow in both places. and surely add dedicated tests for this supply demand circulation math + // if the circulation begins with an addition. It is assumed that a cycle always starts with a target. + let start_operation_add = ((min_index % 2) + min_direction as usize) % 2 == 1; + for i in 0..cycle.len() { + let current = cycle[i].borrow(); + if current.role == NodeRole::Voter { + let prev = cycle[prev_index(i)].borrow(); + + assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { + ass.distribution.iter_mut().position(|(t, _)| *t == prev.who).map(|idx| { + let next_value = if i % 2 == 0 { + if start_operation_add { + ass.distribution[idx].1.saturating_add(min_value) + } else { + ass.distribution[idx].1.saturating_sub(min_value) + } + } else { + if start_operation_add { + ass.distribution[idx].1.saturating_sub(min_value) + } else { + ass.distribution[idx].1.saturating_add(min_value) + } + }; + // println!("Next value for edge {:?} -> {:?} is {}", current.who, prev.who, next_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + num_changed += 1; + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); + + let next = cycle[next_index(i)].borrow(); + + assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { + ass.distribution.iter_mut().position(|(t, _)| *t == next.who).map(|idx| { + let next_value = if i % 2 == 0 { + if start_operation_add { + ass.distribution[idx].1.saturating_sub(min_value) + } else { + ass.distribution[idx].1.saturating_add(min_value) + } + } else { + if start_operation_add { + ass.distribution[idx].1.saturating_add(min_value) + } else { + ass.distribution[idx].1.saturating_sub(min_value) + } + }; + // println!("Next value for edge {:?} -> {:?} is {}", current.who, next.who, next_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + num_changed += 1; + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); + } + }; + + // don't do anything if the edge removed itself + if min_index == (cycle.len() - 1) && min_direction == 1 { continue; } + + // TODO: this is most likely buggy + // re-org otherwise. + if min_chain_in_voter { + // NOTE: safe; voter_root_path is always bigger than 1 element. + for i in 0..voter_root_path.len()-1 { + let next = voter_root_path[i + 1].clone(); + if next.borrow().who == min_who { + break; + } + Node::set_parent_of(&voter_root_path[i + 1], &voter_root_path[i]); + } + Node::set_parent_of(&voter_node, &target_node); + } else { + // NOTE: safe; target_root_path is always bigger than 1 element. + for i in 0..target_root_path.len()-1 { + if target_root_path[i].borrow().who == min_who { + break; + } + Node::set_parent_of(&target_root_path[i + 1], &target_root_path[i]); + } + Node::set_parent_of(&target_node, &voter_node); + } + + + + } + } + } + + num_changed +} + +/// Reduce the given [`PhragmenResult`]. This removes redundant edges from without changing the +/// overall backing of any of the elected candidates. +/// +/// O(min{ |Ew| ⋅ k + m3 , |Ew| ⋅ m }) +pub fn reduce< + AccountId: Clone + Eq + Default + Ord + std::fmt::Debug, +>( + assignments: &mut Vec>, +) -> u32 where { + let mut num_changed = reduce_4(assignments); + num_changed += reduce_all(assignments); + num_changed +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::build_support_map; + + type AccountId = u64; + type Balance = u128; + + #[allow(dead_code)] // to be used with fuzzing + pub fn assert_assignments_equal( + winners: &Vec, + ass1: &Vec>, + ass2: &Vec>, + ) { + let (support_1, _) = build_support_map::(winners, ass1); + let (support_2, _) = build_support_map::(winners, ass2); + + for (who, support) in support_1.iter() { + assert_eq!(support.total, support_2.get(who).unwrap().total); + assert_eq!(support.voters, support_2.get(who).unwrap().voters); + + } + } + + #[allow(dead_code)] // to be used with fuzzing + pub fn reduce_and_compare( + assignment: &Vec>, + winners: &Vec, + ) { + let mut altered_assignment = assignment.clone(); + reduce(&mut altered_assignment); + assert_assignments_equal( + winners, + &assignment, + &altered_assignment, + ); + } + + #[test] + fn trailing_common_works() { + assert_eq!( + trailing_common(&vec![1u8, 2, 3, 4, 5], &vec![7u8, 8, 4, 5]), + 2, + ); + assert_eq!( + trailing_common(&vec![1u8, 2], &vec![7u8, 8]), + 0, + ); + assert_eq!( + trailing_common(&vec![1u8, 2, 3, 4, 5], &vec![3u8, 4, 5]), + 3, + ); + } + + #[test] + fn basic_reduce_4_cycle_works() { + use super::*; + + let assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 25), + (20, 75), + ], + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 50), + (20, 50), + ], + }, + ]; + + let mut new_assignments = assignments.clone(); + let num_reduced = reduce_4(&mut new_assignments); + + assert_eq!(num_reduced, 1); + assert_eq!( + new_assignments, + vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (20, 100), + ], + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 75), + (20, 25), + ], + }, + ], + ); + } + + #[test] + fn basic_reduce_all_cycles_works() { + let mut assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![(10, 10)] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 15), + (40, 15) + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (20, 10), + (30, 10), + (40, 20), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 20), + (30, 10), + (40, 20), + ], + }, + ]; + + assert_eq!(3, reduce_all(&mut assignments)); + + assert_eq!( + assignments, + vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 10), + ] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 30), + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (40, 40), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 15), + (30, 20), + (40, 15), + ], + }, + ], + ) + } + + #[test] + fn basic_reduce_works() { + let mut assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![(10, 10)] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 15), + (40, 15) + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (20, 10), + (30, 10), + (40, 20), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 20), + (30, 10), + (40, 20), + ], + }, + ]; + + assert_eq!(3, reduce(&mut assignments)); + + assert_eq!( + assignments, + vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 10), + ] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 30), + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (40, 40), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 15), + (30, 20), + (40, 15), + ], + }, + ], + ) + } + +} From d95010de70bb29c5a9792b7edff46ae27ed4cf7b Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 16 Jan 2020 09:40:50 +0100 Subject: [PATCH 015/106] Remove other std deps. Runtime compiles --- primitives/phragmen/src/node.rs | 12 ++++++------ primitives/phragmen/src/reduce.rs | 18 +++++++----------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index beadd8a16a70b..9855d641fa194 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -1,7 +1,7 @@ -use std::cell::RefCell; -use std::rc::Rc; +use sp_std::{cell::RefCell, rc::Rc, prelude::*}; +use sp_runtime::RuntimeDebug; -#[derive(PartialEq, Eq, std::fmt::Debug)] +#[derive(PartialEq, Eq, RuntimeDebug)] pub(crate) enum NodeRole { Voter, Target, @@ -16,9 +16,9 @@ pub(crate) struct Node { pub(crate) parent: Option>, } -use std::fmt; -impl fmt::Debug for Node { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +#[cfg(feature = "std")] +impl sp_std::fmt::Debug for Node { + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { write!(f, "({:?} [--> {:?})]", self.who, self.parent.as_ref().map(|p| p.borrow().who.clone())) } } diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index e0ece9b098724..50d027192180a 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -47,7 +47,10 @@ //! //! 1. https://hackmd.io/JOn9x98iS0e0DPWQ87zGWg?view -use sp_std::collections::{btree_map::{Entry::*, BTreeMap}}; +use sp_std::{ + prelude::*, + collections::{btree_map::{Entry::*, BTreeMap}}, +}; use crate::node::{Node, NodeRef, NodeRole}; @@ -121,7 +124,6 @@ fn reduce_4( }, Occupied(mut entry) => { let other_who = entry.get_mut(); - println!("Occupied {:?} -> ({:?} {:?}) other: {:?}", &who, &v1, &v2, &other_who); // check if other_who voted for the same pair v1, v2. let maybe_other_assignments = assignments.iter().find(|a| a.who == *other_who); @@ -150,7 +152,6 @@ fn reduce_4( *other_who = who.clone(); continue; } else { - println!("And it is a cycle! "); // This is a cycle. let mut who_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); distribution.iter().for_each(|(t, w)| { @@ -178,7 +179,6 @@ fn reduce_4( if *w <= min_value { min_value = *w; min_index = index; } (t.clone(), *w) }).collect::>(); - dbg!(&cycle, &min_value); // min was in the first part of the chained iters let mut increase_indices: Vec = Vec::new(); @@ -204,8 +204,6 @@ fn reduce_4( increase_indices.push(min_index - 2); } - dbg!(&increase_indices, &decrease_indices); - // apply changes increase_indices.into_iter().for_each(|i| { assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { @@ -377,8 +375,6 @@ fn reduce_all( let target_chunk = target_root_path.len() - common_count; let min_chain_in_voter = (min_index + min_direction as usize) >= target_chunk; - dbg!(min_value, min_index, &min_who, min_direction); - // walk over the cycle and update the weights // TODO: or at least unify and merge the process of adding this flow in both places. and surely add dedicated tests for this supply demand circulation math // if the circulation begins with an addition. It is assumed that a cycle always starts with a target. @@ -403,7 +399,6 @@ fn reduce_all( ass.distribution[idx].1.saturating_add(min_value) } }; - // println!("Next value for edge {:?} -> {:?} is {}", current.who, prev.who, next_value); if next_value.is_zero() { ass.distribution.remove(idx); num_changed += 1; @@ -430,7 +425,6 @@ fn reduce_all( ass.distribution[idx].1.saturating_sub(min_value) } }; - // println!("Next value for edge {:?} -> {:?} is {}", current.who, next.who, next_value); if next_value.is_zero() { ass.distribution.remove(idx); num_changed += 1; @@ -480,9 +474,11 @@ fn reduce_all( /// Reduce the given [`PhragmenResult`]. This removes redundant edges from without changing the /// overall backing of any of the elected candidates. /// +/// Returns the number of edges removed. +/// /// O(min{ |Ew| ⋅ k + m3 , |Ew| ⋅ m }) pub fn reduce< - AccountId: Clone + Eq + Default + Ord + std::fmt::Debug, + AccountId: Clone + Eq + Default + Ord + sp_std::fmt::Debug, >( assignments: &mut Vec>, ) -> u32 where { From 3f2336701bcd64cca63a28c0b75219f41620b4fc Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 20 Jan 2020 11:36:23 +0100 Subject: [PATCH 016/106] Seemingly it is al stable; cycle implemented but not integrated. --- frame/staking/src/lib.rs | 2 + primitives/arithmetic/fuzzer/Cargo.lock | 4 +- primitives/arithmetic/fuzzer/Cargo.toml | 2 +- primitives/phragmen/src/lib.rs | 1 + primitives/phragmen/src/node.rs | 84 ++++- primitives/phragmen/src/reduce.rs | 426 +++++++++++++++--------- 6 files changed, 352 insertions(+), 167 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 1cb72b808923c..2b5a3cdb0fd39 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -583,6 +583,8 @@ pub enum OffchainElectionStatus { Received, } +impl OffchainElectionStatus + impl Default for OffchainElectionStatus { fn default() -> Self { Self::None diff --git a/primitives/arithmetic/fuzzer/Cargo.lock b/primitives/arithmetic/fuzzer/Cargo.lock index de8a764f1f9b1..a0e4345e7c52d 100644 --- a/primitives/arithmetic/fuzzer/Cargo.lock +++ b/primitives/arithmetic/fuzzer/Cargo.lock @@ -75,7 +75,6 @@ dependencies = [ [[package]] name = "honggfuzz" version = "0.5.45" -source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -300,7 +299,7 @@ dependencies = [ name = "sp-arithmetic-fuzzer" version = "2.0.0" dependencies = [ - "honggfuzz 0.5.45 (registry+https://github.com/rust-lang/crates.io-index)", + "honggfuzz 0.5.45", "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -410,7 +409,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" "checksum fixed-hash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72fe7539e2c5692c6989f2f9c0457e42f1e5768f96b85c87d273574670ae459f" "checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" -"checksum honggfuzz 0.5.45 (registry+https://github.com/rust-lang/crates.io-index)" = "24c27b4aa3049d6d10d8e33d52c9d03ca9aec18f8a449b246f8c4a5b0c10fb34" "checksum impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" "checksum integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea155abb3ba6f382a75f1418988c05fe82959ed9ce727de427f9cfd425b0c903" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" diff --git a/primitives/arithmetic/fuzzer/Cargo.toml b/primitives/arithmetic/fuzzer/Cargo.toml index e8568db3707fb..d819606db48e9 100644 --- a/primitives/arithmetic/fuzzer/Cargo.toml +++ b/primitives/arithmetic/fuzzer/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] sp-arithmetic = { version = "2.0.0", path = ".." } -honggfuzz = "0.5" +honggfuzz = { path = "../../../../honggfuzz-rs" } primitive-types = "0.6" num-bigint = "0.2" num-traits = "0.2" diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 5497d09bbcf9f..4dfd1f87af728 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -43,6 +43,7 @@ mod mock; mod tests; mod node; +mod cycle; pub mod reduce; /// A type in which performing operations on balances and stakes of candidates and voters are safe. diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index 9855d641fa194..bb83be5054f51 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -1,29 +1,39 @@ use sp_std::{cell::RefCell, rc::Rc, prelude::*}; use sp_runtime::RuntimeDebug; -#[derive(PartialEq, Eq, RuntimeDebug)] +#[derive(PartialEq, Eq, Clone, RuntimeDebug)] pub(crate) enum NodeRole { Voter, Target, } -pub(crate) type NodeRef = Rc>>; +pub(crate) type RefCellOf = Rc>; +pub(crate) type NodeRef = RefCellOf>; -#[derive(PartialEq, Eq)] +#[derive(Clone)] pub(crate) struct Node { + /// Assumed to be unique. pub(crate) who: A, pub(crate) role: NodeRole, pub(crate) parent: Option>, } +impl PartialEq for Node { + fn eq(&self, other: &Node) -> bool { + self.who == other.who + } +} + +impl Eq for Node {} + #[cfg(feature = "std")] impl sp_std::fmt::Debug for Node { fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - write!(f, "({:?} [--> {:?})]", self.who, self.parent.as_ref().map(|p| p.borrow().who.clone())) + write!(f, "({:?} --> {:?})", self.who, self.parent.as_ref().map(|p| p.borrow().who.clone())) } } -impl Node { +impl Node { pub fn new(who: A, role: NodeRole) -> Node { Self { who, @@ -36,16 +46,29 @@ impl Node { self.parent.is_some() } + pub fn is_parent_of(who: &NodeRef, other: &NodeRef) -> bool { + if who.borrow().parent.is_none() { + return false; + } + who.borrow().parent.as_ref() == Some(other) + } + + pub fn remove_parent(who: &NodeRef) { + who.borrow_mut().parent = None; + } + pub fn set_parent_of(target: &NodeRef, parent: &NodeRef) { target.borrow_mut().parent = Some(parent.clone()); } pub fn root(start: &NodeRef) -> (NodeRef, Vec>) { let mut parent_path: Vec> = Vec::new(); + let initial = start.clone(); parent_path.push(start.clone()); + let mut current = start.clone(); - let mut current = parent_path[0].clone(); while let Some(ref next_parent) = current.clone().borrow().parent { + if initial == next_parent.clone() { break; } parent_path.push(next_parent.clone()); current = next_parent.clone(); } @@ -53,6 +76,10 @@ impl Node { (current, parent_path) } + pub fn parent(&self) -> Option { + self.parent.as_ref().map(|r| r.borrow().who.clone()) + } + pub fn into_ref(self) -> NodeRef { Rc::from(RefCell::from(self)) } @@ -89,12 +116,12 @@ mod tests { // D <-- A <-- B <-- C // \ // <-- E - let d = Node::new(1u32, NodeRole::Target).into_ref(); let a = Node::new(1u32, NodeRole::Target).into_ref(); - let b = Node::new(1u32, NodeRole::Target).into_ref(); - let c = Node::new(1u32, NodeRole::Target).into_ref(); - let e = Node::new(1u32, NodeRole::Target).into_ref(); - let f = Node::new(1u32, NodeRole::Target).into_ref(); + let b = Node::new(2u32, NodeRole::Target).into_ref(); + let c = Node::new(3u32, NodeRole::Target).into_ref(); + let d = Node::new(4u32, NodeRole::Target).into_ref(); + let e = Node::new(5u32, NodeRole::Target).into_ref(); + let f = Node::new(6u32, NodeRole::Target).into_ref(); Node::set_parent_of(&c, &b); Node::set_parent_of(&b, &a); @@ -128,7 +155,40 @@ mod tests { assert_eq!( Node::root(&c), - (d.clone(), vec![c.clone(), b.clone(), a.clone(), f.clone()]), + (f.clone(), vec![c.clone(), b.clone(), a.clone(), f.clone()]), ); } + + #[test] + fn get_root_on_cycle() { + // A ---> B + // | | + // <---- C + let a = Node::new(1u32, NodeRole::Target).into_ref(); + let b = Node::new(2u32, NodeRole::Target).into_ref(); + let c = Node::new(3u32, NodeRole::Target).into_ref(); + + Node::set_parent_of(&a, &b); + Node::set_parent_of(&b, &c); + Node::set_parent_of(&c, &a); + + let (root, path) = Node::root(&a); + assert_eq!(root, c); + assert_eq!(path.clone(), vec![a.clone(), b.clone(), c.clone()]); + } + + #[test] + fn node_cmp_stack_overflows_on_non_unique_elements() { + // To make sure we don't stack overflow on duplicate who. This needs manual impl of + // PartialEq. + let a = Node::new(1u32, NodeRole::Target).into_ref(); + let b = Node::new(1u32, NodeRole::Target).into_ref(); + let c = Node::new(1u32, NodeRole::Target).into_ref(); + + Node::set_parent_of(&a, &b); + Node::set_parent_of(&b, &c); + Node::set_parent_of(&c, &a); + + Node::root(&a); + } } diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index 50d027192180a..32e6c19fa3563 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -76,24 +76,6 @@ fn combinations_2(input: &[T]) -> Vec<(T, T)> { comb } -/// Returns the count of trailing common elements in a slice. -fn trailing_common(t1: &[T], t2: &[T]) -> usize { - let mut t1_pointer = t1.len() - 1; - let mut t2_pointer = t2.len() - 1; - let mut common = 0usize; - - while t1[t1_pointer] == t2[t2_pointer] { - common += 1; - if t1_pointer == 0 || t2_pointer == 0 { - break; - } - t1_pointer -= 1; - t2_pointer -= 1; - } - - common -} - /// Reduce only redundant edges with cycle length of 4. /// /// O(|E_w| ⋅ k). @@ -106,15 +88,12 @@ fn reduce_4( // NOTE: we have to use the old fashioned style loops here with manual indexing. Borrowing // assignments will not work since then there is NO way to mutate it inside. - for i in 0..assignments.len() { - let who = assignments[i].who.clone(); - // immutable copy -- needed for further read operations. TODO: As an optimization at the - // expense of readability, we can remove this. - let distribution = &assignments[i].distribution.clone(); + for assignment_index in 0..assignments.len() { + let who = assignments[assignment_index].who.clone(); // all combinations for this particular voter let candidate_combinations = combinations_2( - &distribution.iter().map(|(t, _p)| t.clone()).collect::>(), + &assignments[assignment_index].distribution.iter().map(|(t, _p)| t.clone()).collect::>(), ); for (v1, v2) in candidate_combinations { @@ -125,6 +104,16 @@ fn reduce_4( Occupied(mut entry) => { let other_who = entry.get_mut(); + // double check if who is still voting for this pair. If not, it means that this + // pair is no longer valid and must have been removed in previous rounds. + if assignments[assignment_index].distribution + .iter() + .filter(|(t, _)| *t == v1 || *t == v2) + .count() != 2 + { + continue; + } + // check if other_who voted for the same pair v1, v2. let maybe_other_assignments = assignments.iter().find(|a| a.who == *other_who); if maybe_other_assignments.is_none() { @@ -142,19 +131,17 @@ fn reduce_4( // This is not a cycle. Replace and continue. let other_votes_count = other_cycle_votes.len(); - // TODO: this might need testing. Some duplicate can cause this and this - // function should reject them. + // TODO Duplicates will fuck us up here. debug_assert!(other_votes_count <= 2); if other_votes_count < 2 { // Not a cycle. Replace and move on. - // TODO test fro this path?? *other_who = who.clone(); continue; } else { // This is a cycle. let mut who_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); - distribution.iter().for_each(|(t, w)| { + assignments[assignment_index].distribution.iter().for_each(|(t, w)| { if *t == v1 || *t == v2 { who_cycle_votes.push((t.clone(), *w)); } }); @@ -205,30 +192,30 @@ fn reduce_4( } // apply changes + let mut remove_indices: Vec = Vec::with_capacity(1); increase_indices.into_iter().for_each(|i| { - assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { + let voter = if i < 2 { who.clone() } else { other_who.clone() }; + assignments.iter_mut().filter(|a| a.who == voter).for_each(|ass| { ass.distribution .iter_mut() .position(|(t, _)| *t == cycle[i].0) .map(|idx| { let next_value = ass.distribution[idx].1.saturating_add(min_value); - if next_value.is_zero() { - ass.distribution.remove(idx); - } else { - ass.distribution[idx].1 = next_value; - } + ass.distribution[idx].1 = next_value; }); + }); }); - }); - decrease_indices.into_iter().for_each(|i| { - assignments.iter_mut().filter(|a| a.who == if i < 2 { who.clone() } else { other_who.clone() }).for_each(|ass| { - ass.distribution + decrease_indices.into_iter().for_each(|i| { + let voter = if i < 2 { who.clone() } else { other_who.clone() }; + assignments.iter_mut().filter(|a| a.who == voter).for_each(|ass| { + ass.distribution .iter_mut() .position(|(t, _)| *t == cycle[i].0) .map(|idx| { let next_value = ass.distribution[idx].1.saturating_sub(min_value); if next_value.is_zero() { ass.distribution.remove(idx); + remove_indices.push(i); num_changed += 1; } else { ass.distribution[idx].1 = next_value; @@ -236,6 +223,17 @@ fn reduce_4( }); }); }); + + // remove either one of them. + let who_removed = remove_indices.iter().find(|i| **i < 2usize).is_some(); + let other_removed = remove_indices.into_iter().find(|i| *i >= 2usize).is_some(); + + match (who_removed, other_removed) { + (false, true) => { *other_who = who.clone(); }, + (true, false) => {}, // nothing, other_who can stay there. + (true, true) => { entry.remove(); }, // remove and don't replace + _ => { debug_assert!(false, "this should be unreachable"); }, + } } } } @@ -245,6 +243,33 @@ fn reduce_4( num_changed } +/// Merges two parent roots as described by the reduce algorithm. +fn merge(voter_root_path: Vec>, target_root_path: Vec>) { + if voter_root_path.len() <= target_root_path.len() { + // iterate from last to beginning, skipping the first one. This asserts that + // indexing is always correct. + voter_root_path + .iter() + .take(voter_root_path.len() - 1) // take all except for last. + .enumerate() + .map(|(i, n)| (n, voter_root_path[i+1].clone())) + .for_each(|(voter, next)| { + Node::set_parent_of(&next, &voter) + }); + Node::set_parent_of(&voter_root_path[0], &target_root_path[0]); + } else { + target_root_path + .iter() + .take(target_root_path.len() - 1) // take all except for last. + .enumerate() + .map(|(i, n)| (n, target_root_path[i+1].clone())) + .for_each(|(target, next)| { + Node::set_parent_of(&next, &target) + }); + Node::set_parent_of(&target_root_path[0], &voter_root_path[0]); + } +} + /// Reduce all redundant edges from the edge weight graph. /// /// O(|Ew| ⋅ m) @@ -258,66 +283,64 @@ fn reduce_all( // a flat iterator of (voter, target) over all pairs of votes. Similar to reduce_4, we loop // without borrowing. - for i in 0..assignments.len() { - let voter = assignments[i].who.clone(); + for assignment_index in 0..assignments.len() { + let voter = assignments[assignment_index].who.clone(); + + for dist_index in 0..assignments[assignment_index].distribution.len() { + // A distribution could have been removed. We don't know for sure. Hence, we check. + let maybe_dist = assignments[assignment_index].distribution.get(dist_index); + if maybe_dist.is_none() { + // The rest of this loop is moot. + break; + } - for j in 0..assignments[i].distribution.len() { - let (target, _) = assignments[i].distribution[j].clone(); + let (target, _) = maybe_dist.expect("Value checked to be some").clone(); + println!("+++ {:?} -> {:?} , TREE = {:?}", &voter, &target, &tree); + + // store if they existed already. + let voter_exists = tree.contains_key(&voter); + let target_exists = tree.contains_key(&target); + + // create both let voter_node = tree.entry(voter.clone()) .or_insert(Node::new(voter.clone(), NodeRole::Voter).into_ref()).clone(); let target_node = tree.entry(target.clone()) .or_insert(Node::new(target.clone(), NodeRole::Target).into_ref()).clone(); - if !voter_node.borrow().has_parent() { - Node::set_parent_of(&voter_node, &target_node); - continue; - } - if !target_node.borrow().has_parent() { - Node::set_parent_of(&target_node, &voter_node); - continue; - } + // TODO a simple test for this. The pic I sent to alfonso + match (voter_exists, target_exists) { + (false, false) => { + Node::set_parent_of(&target_node, &voter_node); + continue; + }, + (false, true) => { + Node::set_parent_of(&voter_node, &target_node); + continue; + }, + (true, false) => { + Node::set_parent_of(&target_node, &voter_node); + continue; + } + (true, true) => { /* don't continue and execute the rest */ } + }; + + dbg!(assignment_index, dist_index); let (voter_root, voter_root_path) = Node::root(&voter_node); let (target_root, target_root_path) = Node::root(&target_node); + dbg!("got roots"); if voter_root != target_root { + dbg!("merging"); // swap // TODO: test case for this path - if voter_root_path.len() <= target_root_path.len() { - // iterate from last to beginning, skipping the first one. This asserts that - // indexing is always correct. - voter_root_path - .iter() - .skip(1) - .rev() - .enumerate() - .map(|(index, r)| (voter_root_path.len() - index - 1, r)) - .for_each(|(index, r)| { - let index = voter_root_path.len() - index; - Node::set_parent_of(r, &voter_root_path[index-1]) - }); - debug_assert_eq!(voter_root_path[0], voter_node); - Node::set_parent_of(&voter_node, &target_node); - } else { - target_root_path - .iter() - .skip(1) - .rev() - .enumerate() - .map(|(index, r)| (target_root_path.len() - index - 1, r)) - .for_each(|(index, r)| { - let index = target_root_path.len() - index; - Node::set_parent_of(r, &target_root_path[index-1]) - }); - debug_assert_eq!(target_root_path[0], target_node); - Node::set_parent_of(&target_node, &voter_node); - } + merge(voter_root_path, target_root_path); } else { debug_assert_eq!(target_root_path.last().unwrap(), voter_root_path.last().unwrap()); // find common and cycle. - let common_count = trailing_common(&voter_root_path, &target_root_path); + let common_count = crate::cycle::trailing_common(&voter_root_path, &target_root_path); // because roots are the same. TODO: replace with a bail-out debug_assert!(common_count > 0); @@ -335,10 +358,12 @@ fn reduce_all( // TODO: a cycle struct that gives you min + to which chunk it belonged. // find minimum of cycle. let mut min_value: ExtendedBalance = Bounded::max_value(); - // Note that this can only ever point to a target, not a voter. - let mut min_who: AccountId = Default::default(); + // The voter and the target pair that create the min edge. + let mut min_target: AccountId = Default::default(); + let mut min_voter: AccountId = Default::default(); + // The index of the min in opaque cycle list. let mut min_index = 0usize; - // 1 -> next // 0 -> prev TOOD: I have some ideas of fixing this. + // 1 -> next // 0 -> prev TODO: I have some ideas of fixing this. let mut min_direction = 0u32; // helpers let next_index = |i| { if i < (cycle.len() - 1) { i + 1 } else { 0 } }; @@ -353,7 +378,8 @@ fn reduce_all( ass.distribution.iter().find(|d| d.0 == next).map(|(_, w)| { if *w < min_value { min_value = *w; - min_who = next.clone(); + min_target = next.clone(); + min_voter = current.clone(); min_index = i; min_direction = 1; } @@ -363,7 +389,8 @@ fn reduce_all( ass.distribution.iter().find(|d| d.0 == prev).map(|(_, w)| { if *w < min_value { min_value = *w; - min_who = prev.clone(); + min_target = prev.clone(); + min_voter = current.clone(); min_index = i; min_direction = 0; } @@ -371,19 +398,28 @@ fn reduce_all( }); } } + // TODO: this is tricky and needs a test. // if the min edge is in the voter's sub-chain. let target_chunk = target_root_path.len() - common_count; - let min_chain_in_voter = (min_index + min_direction as usize) >= target_chunk; + // [target, ..., X, Y, ... voter] + let min_chain_in_voter = (min_index + min_direction as usize) > target_chunk; + + println!("#### cycle = {:?}", cycle.iter().map(|e| e.borrow().who.clone()).collect::>()); + println!("target_root_path = {:?}", target_root_path); + println!("voter_root_path = {:?}", voter_root_path); + dbg!(min_index, min_direction); + dbg!(&min_target); // walk over the cycle and update the weights // TODO: or at least unify and merge the process of adding this flow in both places. and surely add dedicated tests for this supply demand circulation math // if the circulation begins with an addition. It is assumed that a cycle always starts with a target. let start_operation_add = ((min_index % 2) + min_direction as usize) % 2 == 1; + let mut additional_removed = Vec::new(); for i in 0..cycle.len() { let current = cycle[i].borrow(); if current.role == NodeRole::Voter { - let prev = cycle[prev_index(i)].borrow(); + let prev = cycle[prev_index(i)].borrow(); assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { ass.distribution.iter_mut().position(|(t, _)| *t == prev.who).map(|idx| { let next_value = if i % 2 == 0 { @@ -399,9 +435,14 @@ fn reduce_all( ass.distribution[idx].1.saturating_add(min_value) } }; + println!("next value of {:?} -> {:?} is {:?}", ¤t.who, &prev.who, next_value); if next_value.is_zero() { ass.distribution.remove(idx); num_changed += 1; + // only add if this is not the min itself. + if !(i == min_index && min_direction == 0) { + additional_removed.push((cycle[i].clone(), cycle[prev_index(i)].clone())); + } } else { ass.distribution[idx].1 = next_value; } @@ -409,7 +450,6 @@ fn reduce_all( }); let next = cycle[next_index(i)].borrow(); - assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { ass.distribution.iter_mut().position(|(t, _)| *t == next.who).map(|idx| { let next_value = if i % 2 == 0 { @@ -425,9 +465,13 @@ fn reduce_all( ass.distribution[idx].1.saturating_sub(min_value) } }; + println!("next value of {:?} -> {:?} is {:?}", ¤t.who, &next.who, next_value); if next_value.is_zero() { ass.distribution.remove(idx); num_changed += 1; + if !(i == min_index && min_direction == 1) { + additional_removed.push((cycle[i].clone(), cycle[next_index(i)].clone())); + } } else { ass.distribution[idx].1 = next_value; } @@ -436,33 +480,50 @@ fn reduce_all( } }; - // don't do anything if the edge removed itself - if min_index == (cycle.len() - 1) && min_direction == 1 { continue; } + dbg!(&additional_removed); + + // don't do anything if the edge removed itself. This is always the first and last element + let should_reorg = !(min_index == (cycle.len() - 1) && min_direction == 1); // TODO: this is most likely buggy - // re-org otherwise. - if min_chain_in_voter { - // NOTE: safe; voter_root_path is always bigger than 1 element. - for i in 0..voter_root_path.len()-1 { - let next = voter_root_path[i + 1].clone(); - if next.borrow().who == min_who { - break; + // re-org. + if should_reorg { + dbg!(min_chain_in_voter); + let min_edge = vec![min_voter, min_target]; + dbg!(&min_edge); + if min_chain_in_voter { + // NOTE: safe; voter_root_path is always bigger than 1 element. + for i in 0..voter_root_path.len()-1 { + let current = voter_root_path[i].clone().borrow().who.clone(); + let next = voter_root_path[i + 1].clone().borrow().who.clone(); + if min_edge.contains(¤t) && min_edge.contains(&next) { + break; + } + Node::set_parent_of(&voter_root_path[i + 1], &voter_root_path[i]); } - Node::set_parent_of(&voter_root_path[i + 1], &voter_root_path[i]); - } - Node::set_parent_of(&voter_node, &target_node); - } else { - // NOTE: safe; target_root_path is always bigger than 1 element. - for i in 0..target_root_path.len()-1 { - if target_root_path[i].borrow().who == min_who { - break; + Node::set_parent_of(&voter_node, &target_node); + } else { + // NOTE: safe; target_root_path is always bigger than 1 element. + for i in 0..target_root_path.len()-1 { + let current = target_root_path[i].clone().borrow().who.clone(); + let next = target_root_path[i + 1].clone().borrow().who.clone(); + if min_edge.contains(¤t) && min_edge.contains(&next) { + break; + } + Node::set_parent_of(&target_root_path[i + 1], &target_root_path[i]); } - Node::set_parent_of(&target_root_path[i + 1], &target_root_path[i]); + Node::set_parent_of(&target_node, &voter_node); } - Node::set_parent_of(&target_node, &voter_node); } - + // remove every other node which has collapsed to zero + for (r1, r2) in additional_removed { + if Node::is_parent_of(&r1, &r2) { + Node::remove_parent(&r1); + } else if Node::is_parent_of(&r2, &r1) { + Node::remove_parent(&r2); + } + } } } @@ -495,50 +556,31 @@ mod tests { type AccountId = u64; type Balance = u128; - #[allow(dead_code)] // to be used with fuzzing - pub fn assert_assignments_equal( - winners: &Vec, - ass1: &Vec>, - ass2: &Vec>, - ) { - let (support_1, _) = build_support_map::(winners, ass1); - let (support_2, _) = build_support_map::(winners, ass2); - - for (who, support) in support_1.iter() { - assert_eq!(support.total, support_2.get(who).unwrap().total); - assert_eq!(support.voters, support_2.get(who).unwrap().voters); - - } - } - - #[allow(dead_code)] // to be used with fuzzing - pub fn reduce_and_compare( - assignment: &Vec>, - winners: &Vec, - ) { - let mut altered_assignment = assignment.clone(); - reduce(&mut altered_assignment); - assert_assignments_equal( - winners, - &assignment, - &altered_assignment, - ); - } - #[test] - fn trailing_common_works() { - assert_eq!( - trailing_common(&vec![1u8, 2, 3, 4, 5], &vec![7u8, 8, 4, 5]), - 2, - ); - assert_eq!( - trailing_common(&vec![1u8, 2], &vec![7u8, 8]), - 0, - ); - assert_eq!( - trailing_common(&vec![1u8, 2, 3, 4, 5], &vec![3u8, 4, 5]), - 3, - ); + fn merging_works() { + // D <-- A <-- B <-- C + // + // F <-- E + let d = Node::new(1u32, NodeRole::Target).into_ref(); + let a = Node::new(2u32, NodeRole::Target).into_ref(); + let b = Node::new(3u32, NodeRole::Target).into_ref(); + let c = Node::new(4u32, NodeRole::Target).into_ref(); + let e = Node::new(5u32, NodeRole::Target).into_ref(); + let f = Node::new(6u32, NodeRole::Target).into_ref(); + + Node::set_parent_of(&c, &b); + Node::set_parent_of(&b, &a); + Node::set_parent_of(&a, &d); + Node::set_parent_of(&e, &f); + + let path1 = vec![c.clone(), b.clone(), a.clone(), d.clone()]; + let path2 = vec![e.clone(), f.clone()]; + + merge(path1, path2); + // D <-- A <-- B <-- C + // | + // F --> E --> --> + assert_eq!(e.borrow().parent(), Some(4u32)); // c } #[test] @@ -748,4 +790,86 @@ mod tests { ) } + #[test] + fn reduce_3_common_votes_same_weight() { + let mut assignments = vec![ + StakedAssignment { + who: 4, + distribution: vec![ + ( + 1000000, + 100, + ), + ( + 1000002, + 100, + ), + ( + 1000004, + 100, + ), + ], + }, + StakedAssignment { + who: 5, + distribution: vec![ + ( + 1000000, + 100, + ), + ( + 1000002, + 100, + ), + ( + 1000004, + 100, + ), + ], + }, + ]; + + let winners = vec![ + 1000000, + 1000002, + 1000004, + ]; + + reduce_4(&mut assignments); + + assert_eq!( + assignments, + vec![ + StakedAssignment { + who: 4, + distribution: vec![ + ( + 1000000, + 200, + ), + ( + 1000004, + 100, + ), + ], + }, + StakedAssignment { + who: 5, + distribution: vec![ + ( + 1000002, + 200, + ), + ( + 1000004, + 100, + ), + ], + }, + ], + ) + + + + } } From 3cb47fbe1e1c7ee0464a9af11891846f1a20c11e Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 21 Jan 2020 10:04:48 +0100 Subject: [PATCH 017/106] Add fuzzing code. --- primitives/phragmen/fuzzer/.gitignore | 2 + primitives/phragmen/fuzzer/Cargo.lock | 1637 ++++++++++++++++++++++ primitives/phragmen/fuzzer/Cargo.toml | 16 + primitives/phragmen/fuzzer/src/reduce.rs | 133 ++ primitives/phragmen/src/lib.rs | 1 - 5 files changed, 1788 insertions(+), 1 deletion(-) create mode 100644 primitives/phragmen/fuzzer/.gitignore create mode 100644 primitives/phragmen/fuzzer/Cargo.lock create mode 100644 primitives/phragmen/fuzzer/Cargo.toml create mode 100644 primitives/phragmen/fuzzer/src/reduce.rs diff --git a/primitives/phragmen/fuzzer/.gitignore b/primitives/phragmen/fuzzer/.gitignore new file mode 100644 index 0000000000000..3ebcb104d4a50 --- /dev/null +++ b/primitives/phragmen/fuzzer/.gitignore @@ -0,0 +1,2 @@ +hfuzz_target +hfuzz_workspace diff --git a/primitives/phragmen/fuzzer/Cargo.lock b/primitives/phragmen/fuzzer/Cargo.lock new file mode 100644 index 0000000000000..16fb9e72d4445 --- /dev/null +++ b/primitives/phragmen/fuzzer/Cargo.lock @@ -0,0 +1,1637 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ahash" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aho-corasick" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "arbitrary" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayref" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base58" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitvec" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byte-slice-cast" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "c2-chacha" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clear_on_drop" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "const-random" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "const-random-macro 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "const-random-macro" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "curve25519-dalek" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "curve25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "derive_more" +version = "0.99.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.0-pre.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "environmental" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "failure" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fixed-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hashbrown" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hashbrown" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hex" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hmac" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hmac-drbg" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "honggfuzz" +version = "0.5.45" +dependencies = [ + "arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "impl-codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "impl-serde" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libsecp256k1" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lock_api" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lock_api" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "malloc_size_of_derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memory-db" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memory_units" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "merlin" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-bigint" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-integer" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-rational" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "once_cell" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "parity-scale-codec" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-slice-cast 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec-derive 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-util-mem" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-wasm" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "parking_lot" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "paste" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "paste-impl" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pbkdf2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "primitive-types" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fixed-hash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "regex" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc-hex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "schnorrkel" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "smallvec" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "smallvec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "sp-application-crypto" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-io 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-arithmetic" +version = "2.0.0" +dependencies = [ + "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-debug-derive 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-core" +version = "2.0.0" +dependencies = [ + "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-debug-derive 2.0.0", + "sp-externalities 2.0.0", + "sp-runtime-interface 2.0.0", + "sp-std 2.0.0", + "sp-storage 2.0.0", + "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-debug-derive" +version = "2.0.0" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-externalities" +version = "2.0.0" +dependencies = [ + "environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-std 2.0.0", + "sp-storage 2.0.0", +] + +[[package]] +name = "sp-inherents" +version = "2.0.0" +dependencies = [ + "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-io" +version = "2.0.0" +dependencies = [ + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-externalities 2.0.0", + "sp-runtime-interface 2.0.0", + "sp-state-machine 2.0.0", + "sp-std 2.0.0", + "sp-trie 2.0.0", +] + +[[package]] +name = "sp-panic-handler" +version = "2.0.0" +dependencies = [ + "backtrace 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-phragmen" +version = "2.0.0" +dependencies = [ + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-phragmen-compact 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-phragmen-compact" +version = "2.0.0" +dependencies = [ + "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-phragmen-fuzzer" +version = "2.0.0" +dependencies = [ + "honggfuzz 0.5.45", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-phragmen 2.0.0", +] + +[[package]] +name = "sp-runtime" +version = "2.0.0" +dependencies = [ + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-application-crypto 2.0.0", + "sp-arithmetic 2.0.0", + "sp-core 2.0.0", + "sp-inherents 2.0.0", + "sp-io 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-runtime-interface" +version = "2.0.0" +dependencies = [ + "environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 2.0.0", + "sp-runtime-interface-proc-macro 2.0.0", + "sp-std 2.0.0", + "sp-wasm-interface 2.0.0", + "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "2.0.0" +dependencies = [ + "Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-state-machine" +version = "2.0.0" +dependencies = [ + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-externalities 2.0.0", + "sp-panic-handler 2.0.0", + "sp-trie 2.0.0", + "trie-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-std" +version = "2.0.0" + +[[package]] +name = "sp-storage" +version = "2.0.0" +dependencies = [ + "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-debug-derive 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-trie" +version = "2.0.0" +dependencies = [ + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memory-db 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-std 2.0.0", + "trie-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-wasm-interface" +version = "2.0.0" +dependencies = [ + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "substrate-bip39" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "subtle" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tiny-bip39" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "toml" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "trie-db" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "trie-root" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "twox-hash" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "uint" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasmi" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmi-validation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "zeroize" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "zeroize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zeroize_derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +"checksum ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3" +"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" +"checksum arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" +"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" +"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +"checksum backtrace 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b4b1549d804b6c73f4817df2ba073709e96e426f12987127c48e6745568c350b" +"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" +"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" +"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +"checksum byte-slice-cast 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f6209f3b2c1edea170002e016d5ead6903d3bb0a846477f53bbeb614967a52a9" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7b641a8c9867e341f3295564203b1c250eb8ce6cb6126e007941f78c4d2ed7fe" +"checksum const-random-macro 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c750ec12b83377637110d5a57f5ae08e895b06c4b16e2bdbf1a94ef717428c59" +"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +"checksum curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d" +"checksum curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" +"checksum derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2159be042979966de68315bce7034bb000c775f22e3e834e1c52ff78f041cae8" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)" = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" +"checksum environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "516aa8d7a71cb00a1c4146f0798549b93d083d4f189b3ced8f3de6b8f11ee6c4" +"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum fixed-hash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72fe7539e2c5692c6989f2f9c0457e42f1e5768f96b85c87d273574670ae459f" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" +"checksum hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" +"checksum hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" +"checksum hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" +"checksum hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e" +"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" +"checksum hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" +"checksum impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" +"checksum impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8" +"checksum impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" +"checksum integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea155abb3ba6f382a75f1418988c05fe82959ed9ce727de427f9cfd425b0c903" +"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" +"checksum libsecp256k1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "df6edf84fd62aad1c93932b39324eaeda3912c1d26bc18dfaee6293848e49a50" +"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" +"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e37c5d4cd9473c5f4c9c111f033f15d4df9bd378fdf615944e360a4f55a05f0b" +"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" +"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +"checksum memory-db 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "828bdf600636e90c56652689f7c3823ae2072104e4b0b5e83ea984f592f12ab9" +"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" +"checksum merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0942b357c1b4d0dc43ba724674ec89c3218e6ca2b3e8269e7cb53bcecd2f6e" +"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +"checksum num-bigint 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f115de20ad793e857f76da2563ff4a09fbcfd6fe93cca0c5d996ab5f3ee38d" +"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +"checksum num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" +"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +"checksum once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507" +"checksum parity-scale-codec-derive 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42af752f59119656fa3cb31e8852ed24e895b968c0bdb41847da7f0cea6d155f" +"checksum parity-util-mem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8174d85e62c4d615fddd1ef67966bdc5757528891d0742f15b131ad04667b3f9" +"checksum parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" +"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" +"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" +"checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" +"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +"checksum primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0253db64c26d8b4e7896dd2063b516d2a1b9e0a5da26b5b78335f236d1e9522" +"checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" +"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" +"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" +"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" +"checksum regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4b39bd9b0b087684013a792c59e3e07a46a01d2322518d8a1104641a0b1be0" +"checksum serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "ca13fc1a832f793322228923fbb3aba9f3f44444898f835d31ad1b74fa0a2bf8" +"checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" +"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +"checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" +"checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +"checksum substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3be511be555a3633e71739a79e4ddff6a6aaa6579fa6114182a51d72c3eb93c5" +"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" +"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" +"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92" +"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +"checksum tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c5676413eaeb1ea35300a0224416f57abc3bd251657e0fafc12c47ff98c060" +"checksum tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" +"checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf" +"checksum trie-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "191fda5d0106f3ed35a8c6875428b213e15c516e48129cc263dd7ad16e9a665f" +"checksum trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0b779f7c1c8fe9276365d9d5be5c4b5adeacf545117bb3f64c974305789c5c0b" +"checksum twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bf617d864d25af3587aa745529f7aaa541066c876d57e050c0d0c85c61c92aff" +"checksum wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea78c597064ba73596099281e2f4cfc019075122a65cdda3205af94f0b264d93" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" +"checksum zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" +"checksum zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" diff --git a/primitives/phragmen/fuzzer/Cargo.toml b/primitives/phragmen/fuzzer/Cargo.toml new file mode 100644 index 0000000000000..8ef3518154f4a --- /dev/null +++ b/primitives/phragmen/fuzzer/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "sp-phragmen-fuzzer" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +sp-phragmen = { version = "2.0.0", path = ".." } +honggfuzz = { path = "../../../../honggfuzz-rs" } +rand = "0.7.3" + +[workspace] + +[[bin]] +name = "reduce" +path = "src/reduce.rs" \ No newline at end of file diff --git a/primitives/phragmen/fuzzer/src/reduce.rs b/primitives/phragmen/fuzzer/src/reduce.rs new file mode 100644 index 0000000000000..c996340e8249f --- /dev/null +++ b/primitives/phragmen/fuzzer/src/reduce.rs @@ -0,0 +1,133 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! # Running +//! +//! Run with `cargo hfuzz run reduce`. `honggfuzz`. +//! +//! # Debugging a panic +//! +//! Once a panic is found, it can be debugged with +//! `cargo hfuzz run-debug reduce hfuzz_workspace/reduce/*.fuzz`. + +use honggfuzz::fuzz; +use sp_phragmen::{StakedAssignment, ExtendedBalance, build_support_map, reduce::reduce}; +use rand::{self, Rng}; + +type Balance = u128; +type AccountId = u64; + +/// Or any other token type. +const KSM: Balance = 1_000_000_000_000; + +fn main() { + // let (assignments, winners) = generate_random_phragmen_assignment( + // 10, + // 10, + // 5, + // 1, + // ); + // reduce_and_compare(&assignments, &winners); + + loop { + fuzz!(|_data: _| { + let (assignments, winners) = generate_random_phragmen_assignment( + rr(100, 500), + rr(100, 200), + 10, + 6, + ); + reduce_and_compare(&assignments, &winners); + }); + } +} + +fn generate_random_phragmen_assignment( + voter_count: usize, + target_count: usize, + avg_edge_per_voter: usize, + edge_per_voter_var: usize, +) -> (Vec>, Vec) { + // random in range of (a, b) + let rr_128 = |a: u128, b: u128| -> u128 { rand::thread_rng().gen_range(a, b) }; + + // prefix to distinguish the voter and target account ranges. + let target_prefix = 1_000_000_000; + assert!(voter_count < target_prefix); + + let mut assignments = Vec::with_capacity(voter_count as usize); + let mut winners: Vec = Vec::new(); + + let all_targets = (target_prefix..=(target_prefix + target_count)) + .map(|a| a as AccountId) + .collect::>(); + + (1..=voter_count).for_each(|acc| { + let mut targets_to_chose_from = all_targets.clone(); + let targets_to_chose = rr( + avg_edge_per_voter - edge_per_voter_var, + avg_edge_per_voter + edge_per_voter_var, + ); + + let distribution = (0..targets_to_chose).map(|_| { + let target = targets_to_chose_from.remove(rr(0, targets_to_chose_from.len())); + if winners.iter().find(|w| **w == target).is_none() { + winners.push(target.clone()); + } + (target, rr_128(KSM, 100 * KSM)) + }).collect::>(); + + assignments.push(StakedAssignment { + who: (acc as AccountId), + distribution, + }); + }); + + (assignments, winners) +} + +fn assert_assignments_equal( + winners: &Vec, + ass1: &Vec>, + ass2: &Vec>, +) { + let (support_1, _) = build_support_map::(winners, ass1); + let (support_2, _) = build_support_map::(winners, ass2); + + for (who, support) in support_1.iter() { + assert_eq!(support.total, support_2.get(who).unwrap().total); + // TODO: assert on length as well. + // Why am I so stupid... Only the total need to be the same. + // assert_eq!(support.voters, support_2.get(who).unwrap().voters); + } +} + +fn reduce_and_compare( + assignment: &Vec>, + winners: &Vec, +) { + let mut altered_assignment = assignment.clone(); + reduce(&mut altered_assignment); + assert_assignments_equal( + winners, + &assignment, + &altered_assignment, + ); +} + +fn rr(a: usize, b: usize) -> usize { + rand::thread_rng().gen_range(a, b) +} diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 4dfd1f87af728..5497d09bbcf9f 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -43,7 +43,6 @@ mod mock; mod tests; mod node; -mod cycle; pub mod reduce; /// A type in which performing operations on balances and stakes of candidates and voters are safe. From a21e90b669e10d89bbd42bfe3d79a1f7c7e2f168 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 21 Jan 2020 10:25:49 +0100 Subject: [PATCH 018/106] Cleanup reduce a bit more. --- primitives/phragmen/fuzzer/src/reduce.rs | 19 ++---- primitives/phragmen/src/reduce.rs | 77 ++++++++++++------------ 2 files changed, 45 insertions(+), 51 deletions(-) diff --git a/primitives/phragmen/fuzzer/src/reduce.rs b/primitives/phragmen/fuzzer/src/reduce.rs index c996340e8249f..3c422f8935834 100644 --- a/primitives/phragmen/fuzzer/src/reduce.rs +++ b/primitives/phragmen/fuzzer/src/reduce.rs @@ -34,19 +34,11 @@ type AccountId = u64; const KSM: Balance = 1_000_000_000_000; fn main() { - // let (assignments, winners) = generate_random_phragmen_assignment( - // 10, - // 10, - // 5, - // 1, - // ); - // reduce_and_compare(&assignments, &winners); - loop { fuzz!(|_data: _| { let (assignments, winners) = generate_random_phragmen_assignment( - rr(100, 500), - rr(100, 200), + rr(100, 1000), + rr(100, 2000), 10, 6, ); @@ -104,14 +96,12 @@ fn assert_assignments_equal( ass1: &Vec>, ass2: &Vec>, ) { + let (support_1, _) = build_support_map::(winners, ass1); let (support_2, _) = build_support_map::(winners, ass2); for (who, support) in support_1.iter() { assert_eq!(support.total, support_2.get(who).unwrap().total); - // TODO: assert on length as well. - // Why am I so stupid... Only the total need to be the same. - // assert_eq!(support.voters, support_2.get(who).unwrap().voters); } } @@ -120,7 +110,8 @@ fn reduce_and_compare( winners: &Vec, ) { let mut altered_assignment = assignment.clone(); - reduce(&mut altered_assignment); + let num_changed = reduce(&mut altered_assignment); + // TODO: give a report of how many edges were removed on average. assert_assignments_equal( winners, &assignment, diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index 32e6c19fa3563..3969eabdc8e1b 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -76,6 +76,24 @@ fn combinations_2(input: &[T]) -> Vec<(T, T)> { comb } +/// Returns the count of trailing common elements in a slice. +pub(crate) fn trailing_common(t1: &[T], t2: &[T]) -> usize { + let mut t1_pointer = t1.len() - 1; + let mut t2_pointer = t2.len() - 1; + let mut common = 0usize; + + while t1[t1_pointer] == t2[t2_pointer] { + common += 1; + if t1_pointer == 0 || t2_pointer == 0 { + break; + } + t1_pointer -= 1; + t2_pointer -= 1; + } + + common +} + /// Reduce only redundant edges with cycle length of 4. /// /// O(|E_w| ⋅ k). @@ -117,7 +135,6 @@ fn reduce_4( // check if other_who voted for the same pair v1, v2. let maybe_other_assignments = assignments.iter().find(|a| a.who == *other_who); if maybe_other_assignments.is_none() { - // TODO: test for this path? // This combination is not a cycle. continue; } @@ -277,10 +294,14 @@ fn reduce_all( assignments: &mut Vec>, ) -> u32 { let mut num_changed: u32 = Zero::zero(); - - // ----------------- Phase 2: remove any other cycle let mut tree: BTreeMap> = BTreeMap::new(); + // NOTE: This code can heavily use an index cache. Looking up a pair of (voter, target) in the + // assignments happens numerous times and and we can save time. For now it is written as such + // because abstracting some of this code into a function/closure is super hard due to borrow + // checks (and most likely needs unsafe code at the end). For now I will keep it as it and + // refactor later. + // a flat iterator of (voter, target) over all pairs of votes. Similar to reduce_4, we loop // without borrowing. for assignment_index in 0..assignments.len() { @@ -293,22 +314,21 @@ fn reduce_all( // The rest of this loop is moot. break; } - let (target, _) = maybe_dist.expect("Value checked to be some").clone(); - println!("+++ {:?} -> {:?} , TREE = {:?}", &voter, &target, &tree); - // store if they existed already. let voter_exists = tree.contains_key(&voter); let target_exists = tree.contains_key(&target); - // create both + // create both. let voter_node = tree.entry(voter.clone()) .or_insert(Node::new(voter.clone(), NodeRole::Voter).into_ref()).clone(); let target_node = tree.entry(target.clone()) .or_insert(Node::new(target.clone(), NodeRole::Target).into_ref()).clone(); - // TODO a simple test for this. The pic I sent to alfonso + // If one exists but the other one doesn't, or if both does not, then set the existing + // one as the parent of the non-existing one and move on. Else, continue with the rest + // of the code. match (voter_exists, target_exists) { (false, false) => { Node::set_parent_of(&target_node, &voter_node); @@ -325,37 +345,30 @@ fn reduce_all( (true, true) => { /* don't continue and execute the rest */ } }; - dbg!(assignment_index, dist_index); - let (voter_root, voter_root_path) = Node::root(&voter_node); let (target_root, target_root_path) = Node::root(&target_node); - dbg!("got roots"); if voter_root != target_root { - dbg!("merging"); // swap - // TODO: test case for this path merge(voter_root_path, target_root_path); } else { - debug_assert_eq!(target_root_path.last().unwrap(), voter_root_path.last().unwrap()); - // find common and cycle. - let common_count = crate::cycle::trailing_common(&voter_root_path, &target_root_path); + let common_count = trailing_common(&voter_root_path, &target_root_path); - // because roots are the same. TODO: replace with a bail-out + // because roots are the same. + debug_assert_eq!(target_root_path.last().unwrap(), voter_root_path.last().unwrap()); debug_assert!(common_count > 0); // cycle part of each path will be `path[path.len() - common_count - 1 : 0]` // NOTE: the order of chaining is important! it is always build from [target, ..., // voter] - // TODO: check borrows panic! let cycle = target_root_path.iter().take(target_root_path.len() - common_count + 1).cloned() .chain(voter_root_path.iter().take(voter_root_path.len() - common_count).rev().cloned()) .collect::>>(); + // a cycle's length shall always be multiple of two. + debug_assert_eq!(cycle.len() % 2, 0); - - // TODO: a cycle struct that gives you min + to which chunk it belonged. // find minimum of cycle. let mut min_value: ExtendedBalance = Bounded::max_value(); // The voter and the target pair that create the min edge. @@ -363,7 +376,7 @@ fn reduce_all( let mut min_voter: AccountId = Default::default(); // The index of the min in opaque cycle list. let mut min_index = 0usize; - // 1 -> next // 0 -> prev TODO: I have some ideas of fixing this. + // 1 -> next // 0 -> prev let mut min_direction = 0u32; // helpers let next_index = |i| { if i < (cycle.len() - 1) { i + 1 } else { 0 } }; @@ -398,27 +411,18 @@ fn reduce_all( }); } } - // TODO: this is tricky and needs a test. + // if the min edge is in the voter's sub-chain. - let target_chunk = target_root_path.len() - common_count; // [target, ..., X, Y, ... voter] + let target_chunk = target_root_path.len() - common_count; let min_chain_in_voter = (min_index + min_direction as usize) > target_chunk; - println!("#### cycle = {:?}", cycle.iter().map(|e| e.borrow().who.clone()).collect::>()); - println!("target_root_path = {:?}", target_root_path); - println!("voter_root_path = {:?}", voter_root_path); - dbg!(min_index, min_direction); - dbg!(&min_target); - // walk over the cycle and update the weights - // TODO: or at least unify and merge the process of adding this flow in both places. and surely add dedicated tests for this supply demand circulation math - // if the circulation begins with an addition. It is assumed that a cycle always starts with a target. let start_operation_add = ((min_index % 2) + min_direction as usize) % 2 == 1; let mut additional_removed = Vec::new(); for i in 0..cycle.len() { let current = cycle[i].borrow(); if current.role == NodeRole::Voter { - let prev = cycle[prev_index(i)].borrow(); assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { ass.distribution.iter_mut().position(|(t, _)| *t == prev.who).map(|idx| { @@ -435,7 +439,7 @@ fn reduce_all( ass.distribution[idx].1.saturating_add(min_value) } }; - println!("next value of {:?} -> {:?} is {:?}", ¤t.who, &prev.who, next_value); + if next_value.is_zero() { ass.distribution.remove(idx); num_changed += 1; @@ -465,7 +469,7 @@ fn reduce_all( ass.distribution[idx].1.saturating_sub(min_value) } }; - println!("next value of {:?} -> {:?} is {:?}", ¤t.who, &next.who, next_value); + if next_value.is_zero() { ass.distribution.remove(idx); num_changed += 1; @@ -480,12 +484,11 @@ fn reduce_all( } }; - dbg!(&additional_removed); - // don't do anything if the edge removed itself. This is always the first and last element + // don't do anything if the edge removed itself. This is always the first and last + // element let should_reorg = !(min_index == (cycle.len() - 1) && min_direction == 1); - // TODO: this is most likely buggy // re-org. if should_reorg { dbg!(min_chain_in_voter); From df7676b15157b2b9c7f060a0569b97c4c1dddc93 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 22 Jan 2020 16:46:36 +0100 Subject: [PATCH 019/106] =?UTF-8?q?a=20metric=20ton=20of=20tests=20for=20s?= =?UTF-8?q?taking;=20wip=20=F0=9F=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frame/staking/src/lib.rs | 290 ++++++++++----- frame/staking/src/mock.rs | 207 +++++++++-- frame/staking/src/tests.rs | 458 +++++++++++++++++++----- primitives/core/src/offchain/testing.rs | 2 +- primitives/phragmen/src/lib.rs | 9 +- primitives/phragmen/src/node.rs | 92 +++-- primitives/phragmen/src/reduce.rs | 159 +++++--- 7 files changed, 937 insertions(+), 280 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 2b5a3cdb0fd39..2e5b52c7f913e 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -255,10 +255,10 @@ mod slashing; pub mod inflation; -use sp_std::{prelude::*, result}; +use sp_std::{prelude::*, result, cmp::Ordering}; use codec::{HasCompact, Encode, Decode}; use frame_support::{ - decl_module, decl_event, decl_storage, ensure, decl_error, + decl_module, decl_event, decl_storage, ensure, decl_error, debug, weights::SimpleDispatchInfo, traits::{ Currency, OnFreeBalanceZero, LockIdentifier, LockableCurrency, @@ -573,19 +573,15 @@ pub struct ElectionResult { /// The status of the upcoming (offchain) election. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] -pub enum OffchainElectionStatus { - /// Nothing has happened yet. An offchain worker might be triggered now. +pub enum ElectionStatus { + /// Nothing has and will happen for now. We don't have a solution for next era, and submission + /// window is also not open. None, - /// An offchain worker as been triggered but no result has been observed yet. No further - /// offchain workers shall be dispatched now. - Triggered(BlockNumber), - /// A result has been returned and should be used in the upcoming era. - Received, + /// The submission window has been open since the contained block number. + Open(BlockNumber), } -impl OffchainElectionStatus - -impl Default for OffchainElectionStatus { +impl Default for ElectionStatus { fn default() -> Self { Self::None } @@ -774,7 +770,7 @@ decl_storage! { pub QueuedElected get(fn queued_elected): Option>>; /// Flag to control the execution of the offchain election. - pub ElectionStatus get(fn election_status): OffchainElectionStatus; + pub EraElectionStatus get(fn era_election_status): ElectionStatus; /// The current era index. pub CurrentEra get(fn current_era) config(): EraIndex; @@ -910,8 +906,20 @@ decl_error! { NoMoreChunks, /// Can not rebond without unlocking chunks. NoUnlockChunk, + /// The submitted result is received out of the open window. + PhragmenEarlySubmission, /// The submitted result is not as good as the one stored on chain. - WeakPhragmenResult + PhragmenWeakSubmission, + /// The submitted result has unknown edges that are not among the presented winners. + PhragmenBogusEdge, + /// One of the submitted winners is not an active candidate on chain. + PhragmenBogusWinner, + /// One of the submitted nominators is not an active nominator on chain. + PhragmenBogusNominator, + /// One of the submitted nominators stake distribution does not add up to their ledger. + PhragmenBogusNominatorStake, + /// One of the submitted nominators has an edge to which they have not voted on chain. + PhragmenBogusNomination, } } @@ -927,71 +935,86 @@ decl_module! { fn deposit_event() = default; + /// Does the following: + /// + /// 1. potential storage migration + /// 2. sets `ElectionStatus` to `Triggered(now)` where `now` is the block number at which + /// the election window has opened. The offchain worker, if applicable, will execute at the + /// end of the current block. `submit_election_solution` will accept solutions from this block + /// until the end of the era. fn on_initialize(now: T::BlockNumber) { Self::ensure_storage_upgraded(); if // if we don't have any ongoing offchain compute. - Self::election_status() == OffchainElectionStatus::None && + Self::era_election_status() == ElectionStatus::None && // and an era is about to be changed. Self::is_current_session_final() { - // TODO: maybe we can be naive like im-online and just assume block == slot? let next_session_change = T::NextSessionChange::predict_next_session_change(now); if let Some(remaining) = next_session_change.checked_sub(&now) { if remaining <= T::ElectionLookahead::get() && !remaining.is_zero() { // Set the flag to make sure we don't waste any compute here in the same era // after we have triggered the offline compute. - >::put( - OffchainElectionStatus::::Triggered(now) + >::put( + ElectionStatus::::Open(now) + ); + debug::native::info!( + target: "staking", + "detected a good block to trigger offchain election. Submission will \ + be allowed from the next block.", ); - frame_support::print("detected a good block to trigger offchain worker."); } - } else { - frame_support::print("predicted next authority set change to be in the past."); } } } fn offchain_worker(now: T::BlockNumber) { - // TODO: add runtime logging. - if sp_io::offchain::is_validator() { - if Self::election_status() == OffchainElectionStatus::::Triggered(now) { - // get all local keys that are among the current elected stakers. - // TODO: well this is outdated, neh? - let current_elected = Self::current_elected(); - if T::SubmitTransaction::can_sign_with(Some(current_elected.clone())) { - // We have at least some local key which corresponds to an elected - // validator. run phragmen - if let Some(sp_phragmen::PhragmenResult { - winners, - assignments, - }) = Self::do_phragmen() { - let winners = winners.into_iter().map(|(w, _)| w).collect(); - - // convert into staked. This is needed to be able to reduce. - let mut staked: Vec> = assignments - .into_iter() - .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::slashable_balance_of)) - .collect(); - - // reduce the assignments. This will remove some additional edges. - sp_phragmen::reduce::reduce(&mut staked); - - // compact encode the assignment. - let compact = >::from_staked(staked); - - let call: ::Call = Call::submit_election_result(winners, compact).into(); - // TODO: maybe we want to send from just one of them? we'll see. - let _result = T::SubmitTransaction::submit_signed_from(call, current_elected); - } else { - frame_support::print("ran phragmen offchain, but None was returned."); - } + // consult and make sure this is okay. + // debug::RuntimeLogger::init(); + // TODO: make sure that we don't need that is_validator here. + if Self::era_election_status() == ElectionStatus::::Open(now) { + // TODO: well this is outdated, neh? need to get the correct one from session. + let current_elected = Self::current_elected(); + + // Check if current node can sign with any of the keys which correspond to a + // validator. This basically says: proceed if the node is a validator. + if T::SubmitTransaction::can_sign_with(Some(current_elected.clone())) { + // We have at least some local key which corresponds to an elected + // validator. run phragmen + if let Some(sp_phragmen::PhragmenResult { + winners, + assignments, + }) = Self::do_phragmen() { + let winners = winners.into_iter().map(|(w, _)| w).collect(); + + // convert into staked. This is needed to be able to reduce. + let mut staked: Vec> = assignments + .into_iter() + .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::slashable_balance_of)) + .collect(); + + // reduce the assignments. This will remove some additional edges. + sp_phragmen::reduce(&mut staked); + + // compact encode the assignment. + let compact = >::from_staked(staked); + + let call: ::Call = Call::submit_election_solution(winners, compact).into(); + // TODO: maybe we want to send from just one of them? we'll see. + let _result = T::SubmitTransaction::submit_signed_from(call, current_elected); + dbg!(_result); } else { - frame_support::print("Have no key to sign this with"); + debug::native::warn!( + target: "staking", + "Ran offchain phragmen but none was returned", + ); } } else { - frame_support::print("validator did not start offchain election."); + debug::native::warn!( + target: "staking", + "Have no key to sign this with", + ); } } } @@ -1003,13 +1026,59 @@ decl_module! { } } - fn submit_election_result( + /// Submit a phragmen result to the chain. If the solution: + /// 1. is valid + /// 2. has a better score than a potentially existing solution on chain + /// + /// it will replace it. + /// + /// A solution consists of two pieces of data: + /// 1. `winners`: a flat vector of all the winners of the round. + /// 2. `assignments`: the compact version of an assignment vector that encodes the edge + /// weights. + /// + /// Both of which may be computed using the [`phragmen`], or any other algorithm. + /// + /// A solution is valid if + /// 1. All the presented winners are actually on-chain candidates. + /// 2. All the presented edges contain: + /// - a voter who is an on-chain nominator. + /// - a target who is an on-chain candidate. + /// - that target must actually be among the nominations of the voter. + /// + /// A solutions score is consisted of 3 parameters: + /// 1. `min { support.total }` for each support of a winner. This value should be maximized. + /// 2. `sum { support.total }` for each support of a winner. This value should be minimized. + /// 3. `sum { support.total^2 }` for each support of a winner. This value should be minimized + /// (to ensure less variance) + /// + /// # + /// major steps: + /// - decode from compact: E(E) + /// - build_support_map: O(E) + /// - evaluate_support: E(E) + /// - 1 read which decodes a `ElectionResult` from storage. + /// - 1 read which decodes a `ElectionStatus` from storage. + /// - `2*W` reads each of which decodes an `AccountId` and a `ValidatorPrefs` to ensure + /// winner veracity. + /// - N reads from `Bonded`, `Validators` and `Nominators` to ensure nominator veracity. N + /// is the number of nominators. + /// - O(N * E') to ensure nomination veracity. (E' is the average voter count per nominator + /// and it is less than `MAX_NOMINATIONS`) + /// # + fn submit_election_solution( origin, winners: Vec, compact_assignments: CompactAssignments, ) { let _who = ensure_signed(origin)?; + // discard early solutions + match Self::era_election_status() { + ElectionStatus::None => Err(Error::::PhragmenEarlySubmission)?, + ElectionStatus::Open(_) => { /* Open and no solutions received. Allowed. */ }, + } + // convert into staked. let staked_assignments = compact_assignments.into_staked::< _, @@ -1025,7 +1094,7 @@ decl_module! { &staked_assignments, ); - ensure!(num_error == 0, "bogus edge pointed to a non-winner in assignments."); + ensure!(num_error == 0, Error::::PhragmenBogusEdge); // score the result. Go further only if it is better than what we already have. let submitted_score = sp_phragmen::evaluate_support(&supports); @@ -1033,13 +1102,12 @@ decl_module! { score, .. }) = Self::queued_elected() { + // OPTIMIZATION: we can read first the score and then the rest to do less decoding. // if the local score is better in any of the three parameters. - if - submitted_score.iter().enumerate().take(2).any(|(i, s)| score[i] > *s) || - score[2] < submitted_score[2] - { - Err(Error::::WeakPhragmenResult)? - } + ensure!( + Self::is_score_better(score, submitted_score), + Error::::PhragmenWeakSubmission, + ) } // Either the result is better than the one on chain, or we don't have an any on chain. @@ -1049,24 +1117,61 @@ decl_module! { // check all winners being actual validators. for w in winners.iter() { ensure!( - Self::bonded(&w).is_some(), - "presented winner is not a validator candidate", + >::exists(&w), + Error::::PhragmenBogusWinner, ) } - // check all nominators being bonded, and actually including the claimed vote. + // check all nominators being bonded, and actually including the claimed vote, and + // summing up to their ledger stake. for StakedAssignment { who, distribution } in staked_assignments.iter() { + let maybe_bonded = Self::bonded(&who); + let is_validator = >::exists(&who); let maybe_nomination = Self::nominators(&who); + + // it must be bonded. ensure!( - maybe_nomination.is_some(), - "presented nominator is invalid", + maybe_bonded.is_some(), + Error::::PhragmenBogusNominator ); - let nomination = maybe_nomination.expect("value is checked to be 'Some'"); + + // it must be either of ensure!( - distribution.into_iter().all(|(v, _)| nomination.targets.iter().find(|t| *t == v).is_some()), - "assignment contains an edge from non-nominator", + maybe_nomination.is_some() ^ is_validator, + Error::::PhragmenBogusNominator ); + + let ctrl = maybe_bonded.expect("value is checked to be 'Some'; qed"); + let ledger = Self::ledger(ctrl).ok_or(Error::::NotController)?; + + if !is_validator { + // a normal vote + let nomination = maybe_nomination.expect( + "exactly one of maybe_validator and maybe_nomination is true. \ + is_validator is false; maybe_nomination is some; qed" + ); + let mut total_stake: ExtendedBalance = Zero::zero(); + ensure!( + distribution.into_iter().all(|(v, w)| { + total_stake += w; + nomination.targets.iter().find(|t| *t == v).is_some() + }), + Error::::PhragmenBogusNomination, + ); + + let active_extended_stake = , u64>>::convert(ledger.active) as u128; + ensure!(total_stake == active_extended_stake, Error::::PhragmenBogusNominatorStake); + } else { + // a self vote + ensure!(distribution.len() == 1, Error::::PhragmenBogusNomination); + } } + // Note that we don't need to check again ^^ if a particular target in a nomination was + // actually a candidate. we don't explicitly check this but instead do it indirectly. + // build_support_map tells if any target was not in the winners. Also, we check that all + // the winners were actually candidates. Hence, all of the claimed votes are actually + // voting for valid candidates. All we have to check is if they actually come from the + // claimed nominator or not. // Endlich alles Ok. Exposures and store the result. let to_balance = |e: ExtendedBalance| @@ -1096,13 +1201,17 @@ decl_module! { (validator, exposure) }).collect::)>>(); + debug::native::info!( + target: "staking", + "A better solution has been validated and stored on chain.", + ); + >::put(ElectionResult { compute: ElectionCompute::Submitted, elected_stashes: winners, score: submitted_score, exposures, slot_stake, - }); } @@ -1529,6 +1638,25 @@ impl Module { era_length >= session_per_era } + /// Compares two sets of phragmen scores based on desirability and returns true if `that` is + /// better `this`. + /// + /// Evaluation is done in a lexicographic manner. + fn is_score_better(this: [ExtendedBalance; 3], that: [ExtendedBalance; 3]) -> bool { + match that + .iter() + .enumerate() + .map(|(i, e)| e.cmp(&this[i])) + .collect::>() + .as_slice() + { + [Ordering::Greater, _, _] => true, + [Ordering::Equal, Ordering::Greater, _] => true, + [Ordering::Equal, Ordering::Equal, Ordering::Less] => true, + _ => false + } + } + // MUTABLES (DANGEROUS) /// Update the ledger for a controller. This will also update the stash lock. The lock will @@ -1743,6 +1871,9 @@ impl Module { compute, .. // TODO: this means that we are never storing the score of a winning chain. It this okay? }) = Self::try_do_phragmen() { + // We have chosen the new validator set. Submission is no longer allowed. + >::put(ElectionStatus::None); + // Clear Stakers. for v in Self::current_elected().iter() { >::remove(v); @@ -1775,17 +1906,8 @@ impl Module { /// No storage item is updated. fn try_do_phragmen() -> Option>> { // a phragmen result from either a stored submission or locally executed one. - let somehow_phragmen_results = >::take().map(|offchain_result| { - debug_assert!( - Self::election_status() == OffchainElectionStatus::Received, - "offchain result exist but should not.", - ); - - offchain_result - }).or_else(|| Self::do_phragmen_with_post_processing(ElectionCompute::OnChain)); - - // Either way, kill the flag. No more submitted solutions until further notice. - >::kill(); + let somehow_phragmen_results = >::take() + .or_else(|| Self::do_phragmen_with_post_processing(ElectionCompute::OnChain)); somehow_phragmen_results } diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 3a89b8467cd18..7b1793cac77be 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -21,6 +21,7 @@ use sp_runtime::{Perbill, KeyTypeId}; use sp_runtime::curve::PiecewiseLinear; use sp_runtime::traits::{ IdentityLookup, Convert, OpaqueKeys, OnInitialize, SaturatedConversion, Extrinsic as ExtrinsicT, + Zero, }; use sp_runtime::testing::{Header, UintAuthorityId, TestXt}; use sp_staking::{SessionIndex, offence::{OffenceDetails, OnOffenceHandler}}; @@ -35,16 +36,14 @@ use frame_support::{ use frame_system::offchain::{ TransactionSubmitter, Signer, CreateTransaction, }; -use crate::{ - EraIndex, GenesisConfig, Module, Trait, StakerStatus, ValidatorPrefs, RewardDestination, - Nominators, inflation -}; +use crate::*; +use sp_phragmen::{StakedAssignment, reduce, evaluate_support, build_support_map, ExtendedBalance}; /// The AccountId alias in this test module. -type AccountId = u64; -type AccountIndex = u64; -type BlockNumber = u64; -type Balance = u64; +pub(crate) type AccountId = u64; +pub(crate) type AccountIndex = u64; +pub(crate) type BlockNumber = u64; +pub(crate) type Balance = u64; /// Simple structure that exposes how u64 currency can be represented as... u64. pub struct CurrencyToVoteHandler; @@ -62,6 +61,7 @@ thread_local! { static SLASH_DEFER_DURATION: RefCell = RefCell::new(0); static ELECTION_LOOKAHEAD: RefCell = RefCell::new(0); static PERIOD: RefCell = RefCell::new(1); + static LOCAL_KEY_ACCOUNT: RefCell = RefCell::new(10); } pub struct TestSessionHandler; @@ -290,17 +290,22 @@ impl Trait for Test { type SubmitTransaction = SubmitTransaction; } -mod dummy_sr25519 { +pub(crate) mod dummy_sr25519 { + use super::LOCAL_KEY_ACCOUNT; + mod app_sr25519 { use sp_application_crypto::{app_crypto, key_types::DUMMY, sr25519}; app_crypto!(sr25519, DUMMY); } pub type AuthoritySignature = app_sr25519::Signature; pub type AuthorityId = app_sr25519::Public; + pub type AuthorityPair = app_sr25519::Pair; impl sp_runtime::traits::IdentifyAccount for AuthorityId { type AccountId = u64; - fn into_account(self) -> Self::AccountId { 11u64 } + fn into_account(self) -> Self::AccountId { + LOCAL_KEY_ACCOUNT.with(|v| *v.borrow()) + } } } @@ -325,7 +330,7 @@ pub struct ExtBuilder { session_length: BlockNumber, election_lookahead: BlockNumber, session_per_era: SessionIndex, - existential_deposit: u64, + existential_deposit: Balance, validator_pool: bool, nominate: bool, validator_count: u32, @@ -333,7 +338,9 @@ pub struct ExtBuilder { slash_defer_duration: EraIndex, fair: bool, num_validators: Option, - invulnerables: Vec, + invulnerables: Vec, + has_stakers: bool, + local_key_account: AccountId } impl Default for ExtBuilder { @@ -351,6 +358,8 @@ impl Default for ExtBuilder { fair: true, num_validators: None, invulnerables: vec![], + has_stakers: true, + local_key_account: 10, } } } @@ -404,12 +413,21 @@ impl ExtBuilder { self.session_length = length; self } + pub fn has_stakers(mut self, has: bool) -> Self { + self.has_stakers = has; + self + } + pub fn local_key_account(mut self, key: AccountId) -> Self { + self.local_key_account = key; + self + } pub fn set_associated_constants(&self) { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); SLASH_DEFER_DURATION.with(|v| *v.borrow_mut() = self.slash_defer_duration); SESSION_PER_ERA.with(|v| *v.borrow_mut() = self.session_per_era); ELECTION_LOOKAHEAD.with(|v| *v.borrow_mut() = self.election_lookahead); PERIOD.with(|v| *v.borrow_mut() = self.session_length); + LOCAL_KEY_ACCOUNT.with(|v| *v.borrow_mut() = self.local_key_account); } pub fn build(self) -> sp_io::TestExternalities { self.set_associated_constants(); @@ -457,15 +475,19 @@ impl ExtBuilder { let nominated = if self.nominate { vec![11, 21] } else { vec![] }; let _ = GenesisConfig::{ current_era: 0, - stakers: vec![ - // (stash, controller, staked_amount, status) - (11, 10, balance_factor * 1000, StakerStatus::::Validator), - (21, 20, stake_21, StakerStatus::::Validator), - (31, 30, stake_31, StakerStatus::::Validator), - (41, 40, balance_factor * 1000, status_41), - // nominator - (101, 100, balance_factor * 500, StakerStatus::::Nominator(nominated)) - ], + stakers: if self.has_stakers { + vec![ + // (stash, controller, staked_amount, status) + (11, 10, balance_factor * 1000, StakerStatus::::Validator), + (21, 20, stake_21, StakerStatus::::Validator), + (31, 30, stake_31, StakerStatus::::Validator), + (41, 40, balance_factor * 1000, status_41), + // nominator + (101, 100, balance_factor * 500, StakerStatus::::Nominator(nominated)) + ] + } else { + vec![] + }, validator_count: self.validator_count, minimum_validator_count: self.minimum_validator_count, invulnerables: self.invulnerables, @@ -522,7 +544,8 @@ pub fn check_nominator_exposure(stash: u64) { .map(|v| Staking::stakers(v)) .for_each(|e| e.others.iter() .filter(|i| i.who == stash) - .for_each(|i| sum += i.value)); + .for_each(|i| sum += i.value) + ); let nominator_stake = Staking::slashable_balance_of(&stash); // a nominator cannot over-spend. assert!( @@ -543,20 +566,16 @@ pub fn assert_ledger_consistent(stash: u64) { assert_eq!(real_total, ledger.total); } -pub fn bond_validator(acc: u64, val: u64) { - // a = controller - // a + 1 = stash - let _ = Balances::make_free_balance_be(&(acc + 1), val); - assert_ok!(Staking::bond(Origin::signed(acc + 1), acc, val, RewardDestination::Controller)); - assert_ok!(Staking::validate(Origin::signed(acc), ValidatorPrefs::default())); +pub fn bond_validator(stash: u64, ctrl: u64, val: u64) { + let _ = Balances::make_free_balance_be(&stash, val); + assert_ok!(Staking::bond(Origin::signed(stash), ctrl, val, RewardDestination::Controller)); + assert_ok!(Staking::validate(Origin::signed(ctrl), ValidatorPrefs::default())); } -pub fn bond_nominator(acc: u64, val: u64, target: Vec) { - // a = controller - // a + 1 = stash - let _ = Balances::make_free_balance_be(&(acc + 1), val); - assert_ok!(Staking::bond(Origin::signed(acc + 1), acc, val, RewardDestination::Controller)); - assert_ok!(Staking::nominate(Origin::signed(acc), target)); +pub fn bond_nominator(stash: u64, ctrl: u64, val: u64, target: Vec) { + let _ = Balances::make_free_balance_be(&stash, val); + assert_ok!(Staking::bond(Origin::signed(stash), ctrl, val, RewardDestination::Controller)); + assert_ok!(Staking::nominate(Origin::signed(ctrl), target)); } pub fn run_to_block(n: BlockNumber) { @@ -640,3 +659,123 @@ pub fn on_offence_now( let now = Staking::current_era(); on_offence_in_era(offenders, slash_fraction, now) } + +// winners will be chosen by simply their unweighted total backing stake. Nominator stake is +// distributed evenly. +pub fn horrible_phragmen_with_post_processing(do_reduce: bool) + -> (CompactAssignments, Vec) +{ + use std::collections::BTreeMap; + + let mut backing_stake_of: BTreeMap = BTreeMap::new(); + + // self stake + >::enumerate().for_each(|(who, _p)| + *backing_stake_of.entry(who).or_insert(Zero::zero()) += + Staking::slashable_balance_of(&who) + ); + + // add nominator stuff + >::enumerate().for_each(|(who, nomination)| + nomination.targets.iter().for_each(|v| + *backing_stake_of.entry(*v).or_insert(Zero::zero()) += + Staking::slashable_balance_of(&who) + ) + ); + + // elect winners + let mut sorted: Vec = backing_stake_of.keys().cloned().collect(); + sorted.sort_by_key(|x| backing_stake_of.get(x).unwrap()); + let winners: Vec = sorted.iter() + .cloned() + .take(Staking::validator_count() as usize) + .collect(); + + // create assignments + let mut assignments: Vec> = Vec::new(); + >::enumerate().for_each(|(who, nomination)| { + let mut dist: Vec<(AccountId, ExtendedBalance)> = Vec::new(); + nomination.targets.iter().for_each(|v| + if winners.iter().find(|w| *w == v).is_some() { + dist.push((*v, ExtendedBalance::zero())); + } + ); + + if dist.len() == 0 { return; } + + // assign real stakes. just split the stake. + let stake = Staking::slashable_balance_of(&who) as ExtendedBalance; + let mut sum: ExtendedBalance = Zero::zero(); + let dist_len = dist.len(); + { + dist.iter_mut().for_each(|(_, w)| { + let partial = stake / (dist_len as ExtendedBalance); + *w = partial; + sum += partial; + }); + } + + // assign the leftover to last. + { + let leftover = stake - sum; + let last = dist.last_mut().unwrap(); + last.1 += leftover; + } + + assignments.push(StakedAssignment { who, distribution: dist }); + }); + + // Ensure that this result is worse than seq-phragmen. Otherwise, it should not have been used + // for testing. + { + let (better_compact, better_winners) = do_phragmen_with_post_processing(true); + let better_assignments = better_compact.into_staked::< + _, + _, + CurrencyToVoteHandler, + >(Staking::slashable_balance_of); + + let support = build_support_map::(&winners, &assignments).0; + let better_support = build_support_map::(&better_winners, &better_assignments).0; + + let score = evaluate_support(&support); + let better_score = evaluate_support(&better_support); + + assert!(Staking::is_score_better(score, better_score)); + } + + if do_reduce { reduce(&mut assignments); } + let compact = >::from_staked(assignments); + + (compact, winners) +} + +pub fn do_phragmen_with_post_processing(do_reduce: bool) + -> (CompactAssignments, Vec) +{ + // run phragmen on the default stuff. + let sp_phragmen::PhragmenResult { + winners, + assignments, + } = Staking::do_phragmen().unwrap(); + let winners = winners.into_iter().map(|(w, _)| w).collect(); + + let mut staked: Vec> = assignments + .into_iter() + .map(|a| a.into_staked::<_, _, CurrencyToVoteHandler>(Staking::slashable_balance_of)) + .collect(); + + if do_reduce { reduce(&mut staked); } + + let compact = >::from_staked(staked); + + (compact, winners) +} + +#[macro_export] +macro_rules! assert_session_era { + ($session:expr, $era:expr) => { + assert_eq!(Session::current_index(), $session); + assert_eq!(Staking::current_era(), $era); + } +} diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index dc7b5e1e39a0d..887dbbae1b50d 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -1787,17 +1787,17 @@ fn phragmen_linear_worse_case_equalize() { .fair(true) .build() .execute_with(|| { - bond_validator(50, 1000); - bond_validator(60, 1000); - bond_validator(70, 1000); - - bond_nominator(2, 2000, vec![11]); - bond_nominator(4, 1000, vec![11, 21]); - bond_nominator(6, 1000, vec![21, 31]); - bond_nominator(8, 1000, vec![31, 41]); - bond_nominator(110, 1000, vec![41, 51]); - bond_nominator(120, 1000, vec![51, 61]); - bond_nominator(130, 1000, vec![61, 71]); + bond_validator(51, 50, 1000); + bond_validator(61, 60, 1000); + bond_validator(71, 70, 1000); + + bond_nominator(3, 2, 2000, vec![11]); + bond_nominator(5, 4, 1000, vec![11, 21]); + bond_nominator(7, 6, 1000, vec![21, 31]); + bond_nominator(9, 8, 1000, vec![31, 41]); + bond_nominator(111, 110, 1000, vec![41, 51]); + bond_nominator(121, 120, 1000, vec![51, 61]); + bond_nominator(131, 130, 1000, vec![61, 71]); for i in &[10, 20, 30, 40, 50, 60, 70] { assert_ok!(Staking::set_payee(Origin::signed(*i), RewardDestination::Controller)); @@ -1850,11 +1850,11 @@ fn phragmen_should_not_overflow_validators() { let _ = Staking::chill(Origin::signed(10)); let _ = Staking::chill(Origin::signed(20)); - bond_validator(2, u64::max_value()); - bond_validator(4, u64::max_value()); + bond_validator(3, 2, u64::max_value()); + bond_validator(5, 4, u64::max_value()); - bond_nominator(6, u64::max_value() / 2, vec![3, 5]); - bond_nominator(8, u64::max_value() / 2, vec![3, 5]); + bond_nominator(7, 6, u64::max_value() / 2, vec![3, 5]); + bond_nominator(9, 8, u64::max_value() / 2, vec![3, 5]); start_era(1); @@ -1873,11 +1873,11 @@ fn phragmen_should_not_overflow_nominators() { let _ = Staking::chill(Origin::signed(10)); let _ = Staking::chill(Origin::signed(20)); - bond_validator(2, u64::max_value() / 2); - bond_validator(4, u64::max_value() / 2); + bond_validator(3, 2, u64::max_value() / 2); + bond_validator(5, 4, u64::max_value() / 2); - bond_nominator(6, u64::max_value(), vec![3, 5]); - bond_nominator(8, u64::max_value(), vec![3, 5]); + bond_nominator(7, 6, u64::max_value(), vec![3, 5]); + bond_nominator(9, 8, u64::max_value(), vec![3, 5]); start_era(1); @@ -1892,11 +1892,11 @@ fn phragmen_should_not_overflow_nominators() { #[test] fn phragmen_should_not_overflow_ultimate() { ExtBuilder::default().nominate(false).build().execute_with(|| { - bond_validator(2, u64::max_value()); - bond_validator(4, u64::max_value()); + bond_validator(3, 2, u64::max_value()); + bond_validator(5, 4, u64::max_value()); - bond_nominator(6, u64::max_value(), vec![3, 5]); - bond_nominator(8, u64::max_value(), vec![3, 5]); + bond_nominator(7, 6, u64::max_value(), vec![3, 5]); + bond_nominator(9, 8, u64::max_value(), vec![3, 5]); start_era(1); @@ -2797,74 +2797,368 @@ fn version_initialized() { }); } -#[test] -fn offchain_election_flag_is_triggered() { - ExtBuilder::default() - .session_per_era(5) - .session_length(10) - .election_lookahead(3) - .build() - .execute_with( - || { - - run_to_block(10); - assert_eq!(System::block_number(), 10); - assert_eq!(Session::current_index(), 1); - assert_eq!(Staking::current_era(), 0); - assert_eq!(Staking::election_status(), OffchainElectionStatus::None); - - run_to_block(20); - assert_eq!(Session::current_index(), 2); +mod offchain_phragmen { + use crate::*; + use mock::*; + use frame_support::{assert_ok, assert_noop}; + use substrate_test_utils::assert_eq_uvec; + use sp_runtime::{traits::OffchainWorker} ; + use sp_core::offchain::{ + OffchainExt, + TransactionPoolExt, + testing::{TestOffchainExt, TestTransactionPoolExt}, + }; + use sp_core::traits::KeystoreExt; + use sp_core::testing::KeyStore; + use sp_application_crypto::AppKey; + + type KeyT = dummy_sr25519::AuthorityId; + + const PHRASE: &str = "news slush supreme milk chapter athlete soap sausage put clutch what kitten"; + + /// setup a new set of validators and nominator storage items independent of the parent mock + /// file. This produces a edge graph that can be reduced. + fn build_offchain_phragmen_test_ext() { + for i in (10..=40).step_by(10) { + bond_validator(i, i + 1000, 100); + } - run_to_block(40); - assert_eq!(Session::current_index(), 4); - assert_eq!(System::block_number(), 40); - assert_eq!(Staking::current_era(), 0); - assert_eq!(Staking::election_status(), OffchainElectionStatus::None); + let mut voter = 1; + bond_nominator(voter, 1000 + voter, 100, vec![10]); + voter = 2; + bond_nominator(voter, 1000 + voter, 100, vec![10, 20]); + voter = 3; + bond_nominator(voter, 1000 + voter, 100, vec![20, 40]); + voter = 4; + bond_nominator(voter, 1000 + voter, 100, vec![20, 30, 40]); + voter = 5; + bond_nominator(voter, 1000 + voter, 100, vec![20, 30, 40]); + } + + #[test] + fn score_comparison_is_lexicographical() { + // only better in the fist parameter, worse in the other two ✅ + assert_eq!( + Staking::is_score_better([10, 20, 30], [12, 10, 35]), + true, + ); - run_to_block(46); - assert_eq!(Staking::election_status(), OffchainElectionStatus::None); - run_to_block(47); - assert_eq!(Staking::election_status(), OffchainElectionStatus::Triggered(47)); + // worse in the first, better in the other two ❌ + assert_eq!( + Staking::is_score_better([10, 20, 30], [9, 30, 10]), + false, + ); - run_to_block(50); - assert_eq!(Staking::election_status(), OffchainElectionStatus::None); - }) -} + // equal in the first, the second one dictates. + assert_eq!( + Staking::is_score_better([10, 20, 30], [10, 25, 40]), + true, + ); -#[test] -fn election_on_chain_fallback_works() { - ExtBuilder::default().build().execute_with(|| { - start_session(1); - start_session(2); - assert_eq!(Staking::election_status(), OffchainElectionStatus::None); - // some election must have happened by now + // equal in the first two, the last one dictates. assert_eq!( - System::events()[4].event, - MetaEvent::staking(RawEvent::StakingElection(ElectionCompute::OnChain)), + Staking::is_score_better([10, 20, 30], [10, 20, 40]), + false, ); - }) -} + } -#[test] -fn is_current_session_final_works() { - ExtBuilder::default().session_per_era(3).build().execute_with(|| { - start_era(1); - assert_eq!(Session::current_index(), 4); - assert_eq!(Staking::current_era(), 1); - assert_eq!(Staking::is_current_session_final(), false); + #[test] + fn is_current_session_final_works() { + ExtBuilder::default().session_per_era(3).build().execute_with(|| { + start_era(1); + assert_eq!(Session::current_index(), 4); + assert_eq!(Staking::current_era(), 1); + assert_eq!(Staking::is_current_session_final(), false); - start_session(4); - assert_eq!(Session::current_index(), 5); - assert_eq!(Staking::current_era(), 1); - assert_eq!(Staking::is_current_session_final(), true); + start_session(4); + assert_eq!(Session::current_index(), 5); + assert_eq!(Staking::current_era(), 1); + assert_eq!(Staking::is_current_session_final(), true); - start_session(5); - assert_eq!(Session::current_index(), 6); - // era changed. - assert_eq!(Staking::current_era(), 2); - assert_eq!(Staking::is_current_session_final(), false); - }) + start_session(5); + assert_eq!(Session::current_index(), 6); + // era changed. + assert_eq!(Staking::current_era(), 2); + assert_eq!(Staking::is_current_session_final(), false); + }) + } + + #[test] + fn offchain_election_flag_is_triggered() { + ExtBuilder::default() + .session_per_era(5) + .session_length(10) + .election_lookahead(3) + .build() + .execute_with( + || { + run_to_block(10); + assert_session_era!(1, 0); + assert_eq!(Staking::era_election_status(), ElectionStatus::None); + + run_to_block(18); + assert_session_era!(1, 0); + assert_eq!(Staking::era_election_status(), ElectionStatus::None); + + run_to_block(40); + assert_session_era!(4, 0); + assert_eq!(Staking::era_election_status(), ElectionStatus::None); + + run_to_block(46); + assert_eq!(Staking::era_election_status(), ElectionStatus::None); + run_to_block(47); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(47)); + + run_to_block(49); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(47)); + + run_to_block(50); + assert_eq!(Staking::era_election_status(), ElectionStatus::None); + }) + } + + #[test] + fn election_on_chain_fallback_works() { + ExtBuilder::default().build().execute_with(|| { + start_session(1); + start_session(2); + assert_eq!(Staking::era_election_status(), ElectionStatus::None); + // some election must have happened by now. + assert_eq!( + System::events()[4].event, + MetaEvent::staking(RawEvent::StakingElection(ElectionCompute::OnChain)), + ); + }) + } + + #[test] + fn offchain_result_can_be_submitted() { + // should check that we have a new validator set normally, + // event says that it comes from offchain. + ExtBuilder::default() + .session_per_era(3) + .session_length(5) + .election_lookahead(3) + .build() + .execute_with( + || { + run_to_block(12); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); + + let (compact, winners) = do_phragmen_with_post_processing(true); + assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact)); + + let queued_result = Staking::queued_elected().unwrap(); + assert_eq!(queued_result.compute, ElectionCompute::Submitted); + + run_to_block(15); + assert_eq!(Staking::era_election_status(), ElectionStatus::None); + + assert_eq!( + System::events()[3].event, + MetaEvent::staking(RawEvent::StakingElection(ElectionCompute::Submitted)), + ); + }) + } + + #[test] + fn offchain_result_can_be_submitted_later() { + // same as `offchain_result_can_be_submitted` but at a later block. + ExtBuilder::default() + .session_per_era(3) + .session_length(5) + .election_lookahead(3) + .build() + .execute_with( + || { + run_to_block(14); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); + + let (compact, winners) = do_phragmen_with_post_processing(true); + assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact)); + + let queued_result = Staking::queued_elected().unwrap(); + assert_eq!(queued_result.compute, ElectionCompute::Submitted); + + run_to_block(15); + assert_eq!(Staking::era_election_status(), ElectionStatus::None); + + assert_eq!( + System::events()[3].event, + MetaEvent::staking(RawEvent::StakingElection(ElectionCompute::Submitted)), + ); + }) + } + + #[test] + fn early_solution_submission_is_rejected() { + // should check that we have a new validator set normally, + // event says that it comes from offchain. + ExtBuilder::default() + .session_per_era(3) + .session_length(5) + .election_lookahead(3) + .build() + .execute_with( + || { + + run_to_block(11); + // submission is allowed + assert_eq!(Staking::era_election_status(), ElectionStatus::None); + let (compact, winners) = do_phragmen_with_post_processing(true); + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact), + Error::::PhragmenEarlySubmission, + ); + }) + } + + #[test] + fn weak_solution_is_rejected() { + // A solution which is weaker than what we currently have on-chain is rejected. + ExtBuilder::default() + .session_per_era(3) + .session_length(5) + .election_lookahead(3) + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + // a good solution + let (compact, winners) = do_phragmen_with_post_processing(true); + assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact)); + + // a bad solution + let (compact, winners) = horrible_phragmen_with_post_processing(false); + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact), + Error::::PhragmenWeakSubmission, + ); + }) + } + + #[test] + fn better_solution_is_accepted() { + // A solution which is better than what we currently have on-chain is accepted. + ExtBuilder::default() + .session_per_era(3) + .session_length(5) + .election_lookahead(3) + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + // a meeeeh solution + let (compact, winners) = horrible_phragmen_with_post_processing(false); + assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact)); + + // a better solution + let (compact, winners) = do_phragmen_with_post_processing(true); + assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact)); + }) + } + + #[test] + fn offchain_worker_runs_when_window_open() { + // at the end of the first finalized block with ElectionStatus::open(_), it should execute. + let mut ext = ExtBuilder::default() + .session_per_era(3) + .session_length(5) + .election_lookahead(3) + .validator_count(4) + .local_key_account(11) + .build(); + let (offchain, _state) = TestOffchainExt::new(); + let (pool, state) = TestTransactionPoolExt::new(); + let keystore = KeyStore::new(); + keystore.write().sr25519_generate_new(KeyT::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); + ext.register_extension(OffchainExt::new(offchain)); + ext.register_extension(TransactionPoolExt::new(pool)); + ext.register_extension(KeystoreExt(keystore)); + + ext.execute_with(||{ + run_to_block(12); + + // local key 11 is in the elected set. TODO: check with Guillaume + assert_eq_uvec!(Staking::current_elected(), vec![11, 21, 31]); + assert_eq!(state.read().transactions.len(), 0); + Staking::offchain_worker(12); + assert_eq!(state.read().transactions.len(), 1); + + let encoded = state.read().transactions[0].clone(); + let extrinsic = Extrinsic::decode(&mut &*encoded).unwrap(); + + let author = extrinsic.0.unwrap().0; + assert_eq!(author, 11); + + let call = extrinsic.1; + match call { + mock::Call::Staking(crate::Call::submit_election_solution(_, _)) => {}, + _ => panic!("wrong call submitted"), + }; + }) + } + + #[test] + fn offchain_submits_unsigned_transaction_if_validator() { + unimplemented!(); + } + + #[test] + fn invalid_phragmen_result_extra_winner() { + // if some bogus winner is not a candidate, with proper support. + unimplemented!(); + } + + #[test] + fn invalid_phragmen_result_unsupported_winner() { + // if some bogus winner is presented who does not have support. This should be caught in build_support_map + unimplemented!(); + } + + #[test] + fn invalid_phragmen_result_extra_edge() { + // an edge that does not point to any of the winners. This should be caught in build_support_map + unimplemented!(); + } + + #[test] + fn invalid_phragmen_result_non_nominating_voter() { + // A voter who is not a nominator + unimplemented!(); + } + + #[test] + fn invalid_phragmen_result_invalid_target() { + // A valid voter who voted for someone who is not a candidate. This should be caught in build_support_map + unimplemented!(); + } + + #[test] + fn invalid_phragmen_result_over_stake() { + // A valid voter who's total distributed stake is more than what they bond + unimplemented!(); + } + + #[test] + fn invalid_phragmen_result_under_stake() { + // A valid voter who's total distributed stake is less than what they bond + unimplemented!(); + } + + #[test] + fn invalid_phragmen_result_invalid_target_stealing() { + // A valid voter who voted for someone who is a candidate, but is actually not nominated by this nominator. + unimplemented!(); + } } #[test] diff --git a/primitives/core/src/offchain/testing.rs b/primitives/core/src/offchain/testing.rs index 82438dd6f8519..d0da697666b2e 100644 --- a/primitives/core/src/offchain/testing.rs +++ b/primitives/core/src/offchain/testing.rs @@ -144,7 +144,7 @@ impl TestOffchainExt { impl offchain::Externalities for TestOffchainExt { fn is_validator(&self) -> bool { - unimplemented!("not needed in tests so far") + true } fn network_state(&self) -> Result { diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 5497d09bbcf9f..59c294feece80 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -43,7 +43,10 @@ mod mock; mod tests; mod node; -pub mod reduce; +mod reduce; + +// re-export reduce stuff +pub use reduce::reduce; /// A type in which performing operations on balances and stakes of candidates and voters are safe. /// @@ -407,6 +410,8 @@ pub fn elect( /// The second returned flag indicates the number of edges who corresponded to an actual winner from /// the given winner set. A value in this place larger than 0 indicates a potentially faulty /// assignment. +/// +/// `O(E)` where `E` is the total number of edges. pub fn build_support_map( winners: &Vec, assignments: &Vec>, @@ -441,7 +446,7 @@ pub fn build_support_map( /// - Sum of all supports. This value must be **maximized**. /// - Sum of all supports squared. This value must be **minimized**. /// -/// O(E) +/// `O(E)` where `E` is the total number of edges. pub fn evaluate_support( support: &SupportMap, ) -> [ExtendedBalance; 3] { diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index bb83be5054f51..f60dd60f09083 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -1,7 +1,7 @@ use sp_std::{cell::RefCell, rc::Rc, prelude::*}; use sp_runtime::RuntimeDebug; -#[derive(PartialEq, Eq, Clone, RuntimeDebug)] +#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, RuntimeDebug)] pub(crate) enum NodeRole { Voter, Target, @@ -10,17 +10,43 @@ pub(crate) enum NodeRole { pub(crate) type RefCellOf = Rc>; pub(crate) type NodeRef = RefCellOf>; +#[derive(PartialOrd, Ord, Clone)] +pub(crate) struct NodeId { + /// Assumed to be unique. + pub who: A, + pub role: NodeRole, +} + +impl NodeId { + pub fn from(who: A, role: NodeRole) -> Self { + Self { who, role } + } +} + +impl PartialEq for NodeId { + fn eq(&self, other: &NodeId) -> bool { + self.who == other.who && self.role == other.role + } +} + +#[cfg(feature = "std")] +impl sp_std::fmt::Debug for NodeId { + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + write!(f, "Node({:?}, {:?})", self.who, if self.role == NodeRole::Voter { "V" } else { "T" }) + } +} + +impl Eq for NodeId {} + #[derive(Clone)] pub(crate) struct Node { - /// Assumed to be unique. - pub(crate) who: A, - pub(crate) role: NodeRole, + pub(crate) id: NodeId, pub(crate) parent: Option>, } impl PartialEq for Node { fn eq(&self, other: &Node) -> bool { - self.who == other.who + self.id == other.id } } @@ -29,21 +55,13 @@ impl Eq for Node {} #[cfg(feature = "std")] impl sp_std::fmt::Debug for Node { fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - write!(f, "({:?} --> {:?})", self.who, self.parent.as_ref().map(|p| p.borrow().who.clone())) + write!(f, "({:?} --> {:?})", self.id, self.parent.as_ref().map(|p| p.borrow().id.clone())) } } impl Node { - pub fn new(who: A, role: NodeRole) -> Node { - Self { - who, - role, - parent: None, - } - } - - pub fn has_parent(&self) -> bool { - self.parent.is_some() + pub fn new(id: NodeId) -> Node { + Self { id, parent: None } } pub fn is_parent_of(who: &NodeRef, other: &NodeRef) -> bool { @@ -76,10 +94,6 @@ impl Node { (current, parent_path) } - pub fn parent(&self) -> Option { - self.parent.as_ref().map(|r| r.borrow().who.clone()) - } - pub fn into_ref(self) -> NodeRef { Rc::from(RefCell::from(self)) } @@ -89,16 +103,20 @@ impl Node { mod tests { use super::*; + fn id(i: u32) -> NodeId { + NodeId::from(i, NodeRole::Target) + } + #[test] fn basic_create_works() { - let node = Node::new(10u32, NodeRole::Target); - assert_eq!(node, Node { who: 10u32, parent: None, role: NodeRole::Target }); + let node = Node::new(id(10)); + assert_eq!(node, Node { id: NodeId { who: 10, role: NodeRole::Target }, parent: None }); } #[test] fn set_parent_works() { - let a = Node::new(10u32, NodeRole::Target).into_ref(); - let b = Node::new(20u32, NodeRole::Target).into_ref(); + let a = Node::new(id(10)).into_ref(); + let b = Node::new(id(20)).into_ref(); assert_eq!(a.borrow().parent, None); Node::set_parent_of(&a, &b); @@ -107,7 +125,7 @@ mod tests { #[test] fn get_root_singular() { - let a = Node::new(1u32, NodeRole::Target).into_ref(); + let a = Node::new(id(1)).into_ref(); assert_eq!(Node::root(&a), (a.clone(), vec![a.clone()])); } @@ -116,12 +134,12 @@ mod tests { // D <-- A <-- B <-- C // \ // <-- E - let a = Node::new(1u32, NodeRole::Target).into_ref(); - let b = Node::new(2u32, NodeRole::Target).into_ref(); - let c = Node::new(3u32, NodeRole::Target).into_ref(); - let d = Node::new(4u32, NodeRole::Target).into_ref(); - let e = Node::new(5u32, NodeRole::Target).into_ref(); - let f = Node::new(6u32, NodeRole::Target).into_ref(); + let a = Node::new(id(1)).into_ref(); + let b = Node::new(id(2)).into_ref(); + let c = Node::new(id(3)).into_ref(); + let d = Node::new(id(4)).into_ref(); + let e = Node::new(id(5)).into_ref(); + let f = Node::new(id(6)).into_ref(); Node::set_parent_of(&c, &b); Node::set_parent_of(&b, &a); @@ -164,9 +182,9 @@ mod tests { // A ---> B // | | // <---- C - let a = Node::new(1u32, NodeRole::Target).into_ref(); - let b = Node::new(2u32, NodeRole::Target).into_ref(); - let c = Node::new(3u32, NodeRole::Target).into_ref(); + let a = Node::new(id(1)).into_ref(); + let b = Node::new(id(2)).into_ref(); + let c = Node::new(id(3)).into_ref(); Node::set_parent_of(&a, &b); Node::set_parent_of(&b, &c); @@ -181,9 +199,9 @@ mod tests { fn node_cmp_stack_overflows_on_non_unique_elements() { // To make sure we don't stack overflow on duplicate who. This needs manual impl of // PartialEq. - let a = Node::new(1u32, NodeRole::Target).into_ref(); - let b = Node::new(1u32, NodeRole::Target).into_ref(); - let c = Node::new(1u32, NodeRole::Target).into_ref(); + let a = Node::new(id(1)).into_ref(); + let b = Node::new(id(2)).into_ref(); + let c = Node::new(id(3)).into_ref(); Node::set_parent_of(&a, &b); Node::set_parent_of(&b, &c); diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index 3969eabdc8e1b..1b543b2d30d20 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -47,12 +47,14 @@ //! //! 1. https://hackmd.io/JOn9x98iS0e0DPWQ87zGWg?view +// TODO: should be able to handle self votes. + Fuzzer should randomly create them + use sp_std::{ prelude::*, collections::{btree_map::{Entry::*, BTreeMap}}, }; -use crate::node::{Node, NodeRef, NodeRole}; +use crate::node::{Node, NodeRef, NodeRole, NodeId}; use sp_runtime::traits::{Zero, Bounded}; use crate::{ExtendedBalance, StakedAssignment}; @@ -294,7 +296,7 @@ fn reduce_all( assignments: &mut Vec>, ) -> u32 { let mut num_changed: u32 = Zero::zero(); - let mut tree: BTreeMap> = BTreeMap::new(); + let mut tree: BTreeMap, NodeRef> = BTreeMap::new(); // NOTE: This code can heavily use an index cache. Looking up a pair of (voter, target) in the // assignments happens numerous times and and we can save time. For now it is written as such @@ -317,14 +319,16 @@ fn reduce_all( let (target, _) = maybe_dist.expect("Value checked to be some").clone(); // store if they existed already. - let voter_exists = tree.contains_key(&voter); - let target_exists = tree.contains_key(&target); + let voter_id = NodeId::from(voter.clone(), NodeRole::Voter); + let target_id = NodeId::from(target.clone(), NodeRole::Target); + let voter_exists = tree.contains_key(&voter_id); + let target_exists = tree.contains_key(&target_id); // create both. - let voter_node = tree.entry(voter.clone()) - .or_insert(Node::new(voter.clone(), NodeRole::Voter).into_ref()).clone(); - let target_node = tree.entry(target.clone()) - .or_insert(Node::new(target.clone(), NodeRole::Target).into_ref()).clone(); + let voter_node = tree.entry(voter_id.clone()) + .or_insert(Node::new(voter_id).into_ref()).clone(); + let target_node = tree.entry(target_id.clone()) + .or_insert(Node::new(target_id).into_ref()).clone(); // If one exists but the other one doesn't, or if both does not, then set the existing // one as the parent of the non-existing one and move on. Else, continue with the rest @@ -382,11 +386,11 @@ fn reduce_all( let next_index = |i| { if i < (cycle.len() - 1) { i + 1 } else { 0 } }; let prev_index = |i| { if i > 0 { i - 1 } else { cycle.len() - 1 } }; for i in 0..cycle.len() { - if cycle[i].borrow().role == NodeRole::Voter { + if cycle[i].borrow().id.role == NodeRole::Voter { // NOTE: sadly way too many clones since I don't want to make AccountId: Copy - let current = cycle[i].borrow().who.clone(); - let next = cycle[next_index(i)].borrow().who.clone(); - let prev = cycle[prev_index(i)].borrow().who.clone(); + let current = cycle[i].borrow().id.who.clone(); + let next = cycle[next_index(i)].borrow().id.who.clone(); + let prev = cycle[prev_index(i)].borrow().id.who.clone(); assignments.iter().find(|a| a.who == current).map(|ass| { ass.distribution.iter().find(|d| d.0 == next).map(|(_, w)| { if *w < min_value { @@ -422,10 +426,10 @@ fn reduce_all( let mut additional_removed = Vec::new(); for i in 0..cycle.len() { let current = cycle[i].borrow(); - if current.role == NodeRole::Voter { + if current.id.role == NodeRole::Voter { let prev = cycle[prev_index(i)].borrow(); - assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { - ass.distribution.iter_mut().position(|(t, _)| *t == prev.who).map(|idx| { + assignments.iter_mut().filter(|a| a.who == current.id.who).for_each(|ass| { + ass.distribution.iter_mut().position(|(t, _)| *t == prev.id.who).map(|idx| { let next_value = if i % 2 == 0 { if start_operation_add { ass.distribution[idx].1.saturating_add(min_value) @@ -454,8 +458,8 @@ fn reduce_all( }); let next = cycle[next_index(i)].borrow(); - assignments.iter_mut().filter(|a| a.who == current.who).for_each(|ass| { - ass.distribution.iter_mut().position(|(t, _)| *t == next.who).map(|idx| { + assignments.iter_mut().filter(|a| a.who == current.id.who).for_each(|ass| { + ass.distribution.iter_mut().position(|(t, _)| *t == next.id.who).map(|idx| { let next_value = if i % 2 == 0 { if start_operation_add { ass.distribution[idx].1.saturating_sub(min_value) @@ -491,14 +495,12 @@ fn reduce_all( // re-org. if should_reorg { - dbg!(min_chain_in_voter); let min_edge = vec![min_voter, min_target]; - dbg!(&min_edge); if min_chain_in_voter { // NOTE: safe; voter_root_path is always bigger than 1 element. for i in 0..voter_root_path.len()-1 { - let current = voter_root_path[i].clone().borrow().who.clone(); - let next = voter_root_path[i + 1].clone().borrow().who.clone(); + let current = voter_root_path[i].clone().borrow().id.who.clone(); + let next = voter_root_path[i + 1].clone().borrow().id.who.clone(); if min_edge.contains(¤t) && min_edge.contains(&next) { break; } @@ -508,8 +510,8 @@ fn reduce_all( } else { // NOTE: safe; target_root_path is always bigger than 1 element. for i in 0..target_root_path.len()-1 { - let current = target_root_path[i].clone().borrow().who.clone(); - let next = target_root_path[i + 1].clone().borrow().who.clone(); + let current = target_root_path[i].clone().borrow().id.who.clone(); + let next = target_root_path[i + 1].clone().borrow().id.who.clone(); if min_edge.contains(¤t) && min_edge.contains(&next) { break; } @@ -554,22 +556,18 @@ pub fn reduce< #[cfg(test)] mod tests { use super::*; - use crate::build_support_map; - - type AccountId = u64; - type Balance = u128; #[test] fn merging_works() { // D <-- A <-- B <-- C // // F <-- E - let d = Node::new(1u32, NodeRole::Target).into_ref(); - let a = Node::new(2u32, NodeRole::Target).into_ref(); - let b = Node::new(3u32, NodeRole::Target).into_ref(); - let c = Node::new(4u32, NodeRole::Target).into_ref(); - let e = Node::new(5u32, NodeRole::Target).into_ref(); - let f = Node::new(6u32, NodeRole::Target).into_ref(); + let d = Node::new(NodeId::from(1, NodeRole::Target)).into_ref(); + let a = Node::new(NodeId::from(2, NodeRole::Target)).into_ref(); + let b = Node::new(NodeId::from(3, NodeRole::Target)).into_ref(); + let c = Node::new(NodeId::from(4, NodeRole::Target)).into_ref(); + let e = Node::new(NodeId::from(5, NodeRole::Target)).into_ref(); + let f = Node::new(NodeId::from(6, NodeRole::Target)).into_ref(); Node::set_parent_of(&c, &b); Node::set_parent_of(&b, &a); @@ -583,7 +581,7 @@ mod tests { // D <-- A <-- B <-- C // | // F --> E --> --> - assert_eq!(e.borrow().parent(), Some(4u32)); // c + assert_eq!(e.borrow().clone().parent.unwrap().borrow().id.who, 4u32); // c } #[test] @@ -793,6 +791,93 @@ mod tests { ) } + #[test] + fn should_deal_with_self_vote() { + let mut assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![(10, 10)] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 15), + (40, 15) + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (20, 10), + (30, 10), + (40, 20), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 20), + (30, 10), + (40, 20), + ], + }, + // self vote from 10 and 20 to itself. + StakedAssignment { who: 10, distribution: vec![(10, 100)] }, + StakedAssignment { who: 20, distribution: vec![(20, 200)] }, + ]; + + assert_eq!(3, reduce(&mut assignments)); + + assert_eq!( + assignments, + vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 10), + ] + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + (20, 30), + ], + }, + StakedAssignment { + who: 4, distribution: + vec![ + (40, 40), + ] + }, + StakedAssignment { + who: 5, + distribution: vec![ + (20, 15), + (30, 20), + (40, 15), + ], + }, + // should stay untouched. + StakedAssignment { who: 10, distribution: vec![(10, 100)] }, + StakedAssignment { who: 20, distribution: vec![(20, 200)] }, + ], + ) + } + #[test] fn reduce_3_common_votes_same_weight() { let mut assignments = vec![ @@ -832,12 +917,6 @@ mod tests { }, ]; - let winners = vec![ - 1000000, - 1000002, - 1000004, - ]; - reduce_4(&mut assignments); assert_eq!( From 889b18cc46386d185ba6946be1e873398ed02240 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 23 Jan 2020 10:28:55 +0100 Subject: [PATCH 020/106] Implement a lot more of the tests. --- Cargo.lock | 1 + frame/staking/Cargo.toml | 1 + frame/staking/src/lib.rs | 26 ++- frame/staking/src/mock.rs | 6 + frame/staking/src/tests.rs | 321 +++++++++++++++++++++++++++++++------ 5 files changed, 300 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc991a3c35171..0d722ca224d62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3960,6 +3960,7 @@ dependencies = [ "pallet-staking-reward-curve 2.0.0", "pallet-timestamp 2.0.0", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "sp-application-crypto 2.0.0", "sp-core 2.0.0", diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 33cad328e6c00..979f074d70a3b 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -26,6 +26,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" } sp-application-crypto = { version = "2.0.0", path = "../../primitives/application-crypto" } +parking_lot = { version = "0.9.0" } [features] migrate = [] diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 963da9fede881..de5fa276901a4 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -265,7 +265,7 @@ use frame_support::{ WithdrawReasons, OnUnbalanced, Imbalance, Get, Time, PredictNextSessionChange, } }; -use pallet_session::{historical, SelectInitialValidators}; +use pallet_session::historical; use sp_runtime::{ Perbill, RuntimeDebug, curve::PiecewiseLinear, @@ -919,6 +919,8 @@ decl_error! { PhragmenBogusNominatorStake, /// One of the submitted nominators has an edge to which they have not voted on chain. PhragmenBogusNomination, + /// A self vote must only be originated from a validator to ONLY themselves. + PhragmenBogusSelfVote, } } @@ -1142,6 +1144,8 @@ decl_module! { let ctrl = maybe_bonded.expect("value is checked to be 'Some'; qed"); let ledger = Self::ledger(ctrl).ok_or(Error::::NotController)?; + let active_extended_stake = + , u64>>::convert(ledger.active) as u128; if !is_validator { // a normal vote @@ -1157,12 +1161,20 @@ decl_module! { }), Error::::PhragmenBogusNomination, ); - - let active_extended_stake = , u64>>::convert(ledger.active) as u128; - ensure!(total_stake == active_extended_stake, Error::::PhragmenBogusNominatorStake); + ensure!( + total_stake == active_extended_stake, + Error::::PhragmenBogusNominatorStake + ); } else { // a self vote - ensure!(distribution.len() == 1, Error::::PhragmenBogusNomination); + ensure!(distribution.len() == 1, Error::::PhragmenBogusSelfVote); + ensure!(distribution[0].0 == *who, Error::::PhragmenBogusSelfVote); + // defensive only. A compact assignment of length one does NOT encode the stake + // and it is actually read from chain. Hence, this must always be correct. + ensure!( + distribution[0].1 == active_extended_stake, + Error::::PhragmenBogusSelfVote, + ); } } // Note that we don't need to check again ^^ if a particular target in a nomination was @@ -1756,7 +1768,7 @@ impl Module { // available yet. CurrentEraStartSessionIndex::put(0); BondedEras::mutate(|bonded| bonded.push((0, 0))); - Self::select_validators().1 + Self::select_and_update_validators() } /// The era has changed - enact new staking set. @@ -2022,7 +2034,7 @@ impl Module { targets.retain(|stash| { ::SlashingSpans::get(&stash).map_or( true, - |spans| submitted_in >= spans.last_start(), + |spans| submitted_in >= spans.last_nonzero_slash(), ) }); diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index c1780b5a2aee9..ec33a0edf63ff 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -420,6 +420,12 @@ impl ExtBuilder { self.local_key_account = key; self } + pub fn offchain_phragmen_ext(self) -> Self { + self.session_per_era(3) + .session_length(5) + .election_lookahead(3) + .local_key_account(11) + } pub fn set_associated_constants(&self) { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); SLASH_DEFER_DURATION.with(|v| *v.borrow_mut() = self.slash_defer_duration); diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 8cea8a0fb0b77..d4a86163e0f77 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2688,11 +2688,14 @@ mod offchain_phragmen { use sp_core::offchain::{ OffchainExt, TransactionPoolExt, - testing::{TestOffchainExt, TestTransactionPoolExt}, + testing::{TestOffchainExt, TestTransactionPoolExt, PoolState}, }; + use sp_io::TestExternalities; use sp_core::traits::KeystoreExt; use sp_core::testing::KeyStore; use sp_application_crypto::AppKey; + use std::sync::Arc; + use parking_lot::RwLock; type KeyT = dummy_sr25519::AuthorityId; @@ -2717,6 +2720,53 @@ mod offchain_phragmen { bond_nominator(voter, 1000 + voter, 100, vec![20, 30, 40]); } + fn offchainify(ext: &mut TestExternalities) -> Arc> { + let (offchain, _state) = TestOffchainExt::new(); + let (pool, state) = TestTransactionPoolExt::new(); + let keystore = KeyStore::new(); + keystore.write().sr25519_generate_new(KeyT::ID, Some(&format!("{}/staking1", PHRASE))).unwrap(); + ext.register_extension(OffchainExt::new(offchain)); + ext.register_extension(TransactionPoolExt::new(pool)); + ext.register_extension(KeystoreExt(keystore)); + state + } + + #[test] + fn compact_assignment_into_staked_overflow() { + ExtBuilder::default().build().execute_with(|| { + // everyone's stake is 10. + let stake_of = |_who: &AccountId| -> Balance { 10 }; + + // a single vote cannot be faked. + let malicious_assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![(10, 5), (20, 8), (30, 5)] + } + ]; + + let compact = >::from_staked(malicious_assignments); + + assert_eq!(compact.votes3[0], (1, [(10, 5), (20, 8)], 30)); + + // converting this back will yield no stake for 0 + let fixed_assignments = compact.into_staked::<_, _, CurrencyToVoteHandler>(&stake_of); + + assert_eq!( + fixed_assignments, + vec![ + StakedAssignment { + who: 1, + distribution: vec![(10, 5), (20, 8), (30, 0)], + } + ] + ); + + // Note: this is still wrong and sums to 13, but submitting it will yield an error + // anyway. + }) + } + #[test] fn score_comparison_is_lexicographical() { // only better in the fist parameter, worse in the other two ✅ @@ -2818,9 +2868,7 @@ mod offchain_phragmen { // should check that we have a new validator set normally, // event says that it comes from offchain. ExtBuilder::default() - .session_per_era(3) - .session_length(5) - .election_lookahead(3) + .offchain_phragmen_ext() .build() .execute_with( || { @@ -2847,9 +2895,7 @@ mod offchain_phragmen { fn offchain_result_can_be_submitted_later() { // same as `offchain_result_can_be_submitted` but at a later block. ExtBuilder::default() - .session_per_era(3) - .session_length(5) - .election_lookahead(3) + .offchain_phragmen_ext() .build() .execute_with( || { @@ -2877,9 +2923,7 @@ mod offchain_phragmen { // should check that we have a new validator set normally, // event says that it comes from offchain. ExtBuilder::default() - .session_per_era(3) - .session_length(5) - .election_lookahead(3) + .offchain_phragmen_ext() .build() .execute_with( || { @@ -2899,11 +2943,9 @@ mod offchain_phragmen { fn weak_solution_is_rejected() { // A solution which is weaker than what we currently have on-chain is rejected. ExtBuilder::default() - .session_per_era(3) - .session_length(5) - .election_lookahead(3) - .validator_count(4) + .offchain_phragmen_ext() .has_stakers(false) + .validator_count(4) .build() .execute_with( || { @@ -2927,9 +2969,7 @@ mod offchain_phragmen { fn better_solution_is_accepted() { // A solution which is better than what we currently have on-chain is accepted. ExtBuilder::default() - .session_per_era(3) - .session_length(5) - .election_lookahead(3) + .offchain_phragmen_ext() .validator_count(4) .has_stakers(false) .build() @@ -2952,24 +2992,14 @@ mod offchain_phragmen { fn offchain_worker_runs_when_window_open() { // at the end of the first finalized block with ElectionStatus::open(_), it should execute. let mut ext = ExtBuilder::default() - .session_per_era(3) - .session_length(5) - .election_lookahead(3) + .offchain_phragmen_ext() .validator_count(4) - .local_key_account(11) .build(); - let (offchain, _state) = TestOffchainExt::new(); - let (pool, state) = TestTransactionPoolExt::new(); - let keystore = KeyStore::new(); - keystore.write().sr25519_generate_new(KeyT::ID, Some(&format!("{}/hunter1", PHRASE))).unwrap(); - ext.register_extension(OffchainExt::new(offchain)); - ext.register_extension(TransactionPoolExt::new(pool)); - ext.register_extension(KeystoreExt(keystore)); - + let state = offchainify(&mut ext); ext.execute_with(||{ run_to_block(12); - // local key 11 is in the elected set. TODO: check with Guillaume + // local key 11 is in the elected set. assert_eq_uvec!(Staking::current_elected(), vec![11, 21, 31]); assert_eq!(state.read().transactions.len(), 0); Staking::offchain_worker(12); @@ -2995,51 +3025,246 @@ mod offchain_phragmen { } #[test] - fn invalid_phragmen_result_extra_winner() { - // if some bogus winner is not a candidate, with proper support. - unimplemented!(); + fn invalid_phragmen_result_build_support_extra_edge() { + // an edge that does not point to any of the winners. This should be caught in + // build_support_map. All the targets presented must be one of the presented winners. + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + let (mut compact, winners) = do_phragmen_with_post_processing(false); + assert_eq_uvec!(winners, vec![10, 20, 30, 40]); + + // inject a correct nominator voting for a correct, but non-winner validator. + compact.votes1.push((1, 999)); + + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact), + Error::::PhragmenBogusEdge, + ); + }) } #[test] - fn invalid_phragmen_result_unsupported_winner() { - // if some bogus winner is presented who does not have support. This should be caught in build_support_map - unimplemented!(); + fn invalid_phragmen_result_build_support_extra_winner() { + // if some bogus winner is not a candidate. This and + // `invalid_phragmen_result_build_support_extra_edge` together ensure that all targets are + // actually validators with correct intention. + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + let (compact, mut winners) = do_phragmen_with_post_processing(false); + winners.push(999); + + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact), + Error::::PhragmenBogusWinner, + ); + }) } #[test] - fn invalid_phragmen_result_extra_edge() { - // an edge that does not point to any of the winners. This should be caught in build_support_map - unimplemented!(); + fn invalid_phragmen_result_invalid_target() { + // A valid voter who voted for someone who is not a candidate. Fake candidate is injected as + // winner. Hence, build support map will not catch this. But the check for all winners being + // validators will. + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + // bond a malicious nominator + bond_nominator(666, 667, 1, vec![10]); + + let sp_phragmen::PhragmenResult { + winners, + assignments, + } = Staking::do_phragmen().unwrap(); + let mut winners: Vec = winners.into_iter().map(|(w, _)| w).collect(); + + let mut staked: Vec> = assignments + .into_iter() + .map(|a| a.into_staked::<_, _, CurrencyToVoteHandler>(Staking::slashable_balance_of)) + .collect(); + + staked.push(sp_phragmen::StakedAssignment { who: 666, distribution: vec![(999, 1)] }); + winners.push(999); + + sp_phragmen::reduce(&mut staked); + let compact = >::from_staked(staked); + + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact), + Error::::PhragmenBogusWinner, + ); + }) } + #[test] fn invalid_phragmen_result_non_nominating_voter() { // A voter who is not a nominator - unimplemented!(); + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + let (mut compact, winners) = do_phragmen_with_post_processing(false); + + // inject a wrong nominator voting for a valid validator. + compact.votes1.push((999, 10)); + + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners.clone(), compact.clone()), + Error::::PhragmenBogusNominator, + ); + + // even when it is bonded but not nominating. + let _ = Balances::make_free_balance_be(&999, 10); + assert_ok!(Staking::bond(Origin::signed(999), 998, 10, Default::default())); + + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact), + Error::::PhragmenBogusNominator, + ); + }) } #[test] - fn invalid_phragmen_result_invalid_target() { - // A valid voter who voted for someone who is not a candidate. This should be caught in build_support_map - unimplemented!(); + fn invalid_phragmen_result_wrong_self_vote() { + // A self vote for someone else. + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + let (mut compact, winners) = do_phragmen_with_post_processing(false); + + // mutate a self vote to target someone else. + compact.votes1.iter_mut().find(|x| x.0 == 10).map(|(_voter, target)| *target = 20); + dbg!(&compact); + + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact), + Error::::PhragmenBogusSelfVote, + ); + }) + } + + #[test] + fn invalid_phragmen_result_wrong_self_vote_2() { + // A self validator voting for someone else next to self vote. + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + let (mut compact, winners) = do_phragmen_with_post_processing(false); + + // mutate a self vote to target someone else. + compact.votes1.retain(|x| x.0 != 10); + + // add 10 as a double vote + compact.votes2.push((10, (10, 5), 20)); + + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact), + Error::::PhragmenBogusSelfVote, + ); + }) } #[test] fn invalid_phragmen_result_over_stake() { // A valid voter who's total distributed stake is more than what they bond - unimplemented!(); + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + let (mut compact, winners) = do_phragmen_with_post_processing(false); + + // 3 has: (3, (20, 50), 40) which means evenly distributed between 20 and 40 (50 each). + compact.votes2.iter_mut().find(|x| x.0 == 3).map(|(_who, v1, _v2)| v1.1 = 120); + + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact), + Error::::PhragmenBogusNominatorStake, + ); + }) } #[test] fn invalid_phragmen_result_under_stake() { - // A valid voter who's total distributed stake is less than what they bond - unimplemented!(); + // at the time of this writing, we cannot under stake someone. The compact assignment works + // in a way that some of the stakes are presented by the submitter, and the last one is read + // from chain by subtracting the rest from total. This test is only here as a demonstration. } #[test] fn invalid_phragmen_result_invalid_target_stealing() { - // A valid voter who voted for someone who is a candidate, but is actually not nominated by this nominator. - unimplemented!(); + // A valid voter who voted for someone who is a candidate, but is actually not nominated by + // this nominator. + // A valid voter who's total distributed stake is more than what they bond + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + let (mut compact, winners) = do_phragmen_with_post_processing(false); + + // 3 has: (3, (20, 50), 40). We add a fake vote to 30. + compact.votes2.retain(|x| x.0 != 3); + compact.votes3.push((3, [(20, 50), (40, 30)], 30)); + + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact), + Error::::PhragmenBogusNomination, + ); + }) } } From 50f5399c4bad05e40efe91bcce90af29edb753ea Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 23 Jan 2020 17:14:30 +0100 Subject: [PATCH 021/106] wip getting the unsigned stuff to work --- Cargo.lock | 2 +- frame/staking/Cargo.toml | 4 +- frame/staking/src/lib.rs | 123 +++++++++++++++++++++++++++++++---- frame/system/src/offchain.rs | 19 +++++- 4 files changed, 129 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d722ca224d62..e00851eb56d26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3960,7 +3960,7 @@ dependencies = [ "pallet-staking-reward-curve 2.0.0", "pallet-timestamp 2.0.0", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "sp-application-crypto 2.0.0", "sp-core 2.0.0", diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 979f074d70a3b..5db2767a13e90 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -18,6 +18,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", features = ["historical"], path = "../session", default-features = false } pallet-authorship = { version = "2.0.0", default-features = false, path = "../authorship" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } [dev-dependencies] sp-core = { version = "2.0.0", path = "../../primitives/core" } @@ -25,8 +26,7 @@ pallet-balances = { version = "2.0.0", path = "../balances" } 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" } -sp-application-crypto = { version = "2.0.0", path = "../../primitives/application-crypto" } -parking_lot = { version = "0.9.0" } +parking_lot = { version = "0.10.0" } [features] migrate = [] diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index de5fa276901a4..597480a7312e5 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -258,7 +258,7 @@ pub mod inflation; use sp_std::{prelude::*, result, cmp::Ordering}; use codec::{HasCompact, Encode, Decode}; use frame_support::{ - decl_module, decl_event, decl_storage, ensure, decl_error, debug, + decl_module, decl_event, decl_storage, ensure, decl_error, debug, Parameter, weights::SimpleDispatchInfo, traits::{ Currency, OnFreeBalanceZero, LockIdentifier, LockableCurrency, @@ -271,8 +271,11 @@ use sp_runtime::{ curve::PiecewiseLinear, traits::{ Convert, Zero, One, StaticLookup, CheckedSub, Saturating, Bounded, SaturatedConversion, - SimpleArithmetic, EnsureOrigin, - } + SimpleArithmetic, EnsureOrigin, Member, + }, + transaction_validity::{ + TransactionValidity, ValidTransaction, InvalidTransaction, TransactionPriority, + }, }; use sp_staking::{ SessionIndex, @@ -281,9 +284,10 @@ use sp_staking::{ #[cfg(feature = "std")] use sp_runtime::{Serialize, Deserialize}; use frame_system::{ - self as system, ensure_signed, ensure_root, - offchain::{SignAndSubmitTransaction, SubmitSignedTransaction}, + self as system, ensure_signed, ensure_root, ensure_none, + offchain::{SubmitSignedTransaction, CreateTransaction, SubmitUnsignedTransaction, PublicOf, SignerOf, SignatureOf, SignAndSubmitTransaction, Signer}, }; +use sp_application_crypto::RuntimeAppPublic; use sp_phragmen::{ExtendedBalance, Assignment, StakedAssignment}; @@ -701,7 +705,13 @@ pub trait Trait: frame_system::Trait { type Call: From> + Clone; /// A transaction submitter. - type SubmitTransaction: SignAndSubmitTransaction::Call> + SubmitSignedTransaction::Call>; + type SubmitTransaction: + SubmitSignedTransaction::Call> + + SubmitUnsignedTransaction::Call> + + SignAndSubmitTransaction::Call>; + + /// The key type. Must be the same as given to `SubmitTransaction`. TODO: we can probably extract it from it + type AuthorityId: Member + Parameter + RuntimeAppPublic + Default + Ord; } /// Mode of era-forcing. @@ -975,20 +985,18 @@ decl_module! { // debug::RuntimeLogger::init(); // TODO: make sure that we don't need that is_validator here. if Self::era_election_status() == ElectionStatus::::Open(now) { + // TODO: well this is outdated, neh? need to get the correct one from session. let current_elected = Self::current_elected(); // Check if current node can sign with any of the keys which correspond to a // validator. This basically says: proceed if the node is a validator. if T::SubmitTransaction::can_sign_with(Some(current_elected.clone())) { - // We have at least some local key which corresponds to an elected - // validator. run phragmen if let Some(sp_phragmen::PhragmenResult { winners, assignments, }) = Self::do_phragmen() { - let winners = winners.into_iter().map(|(w, _)| w).collect(); - + let winners: Vec = winners.into_iter().map(|(w, _)| w).collect(); // convert into staked. This is needed to be able to reduce. let mut staked: Vec> = assignments .into_iter() @@ -1001,10 +1009,37 @@ decl_module! { // compact encode the assignment. let compact = >::from_staked(staked); - let call: ::Call = Call::submit_election_solution(winners, compact).into(); - // TODO: maybe we want to send from just one of them? we'll see. - let _result = T::SubmitTransaction::submit_signed_from(call, current_elected); - dbg!(_result); + #[cfg(feature = "signed")] + { + // TODO: maybe we want to send from just one of them? we'll see. + let call: ::Call = Call::submit_election_solution(winners, compact).into(); + let _result = T::SubmitTransaction::submit_signed_from(call, current_elected); + dbg!(_result); + } + #[cfg(not(feature = "signed"))] + { + // TODO: this call is really not needed, we can do it manually instead + // of `can_sign_with`. TODO: we could use some error handling here. + let maybe_signing_key = T::SubmitTransaction::find_local_keys(Some(current_elected)); + if maybe_signing_key.len() > 0 { + let signing_key = maybe_signing_key[0].1; + let signature_payload = (winners.clone(), compact.clone()).encode(); + let maybe_signature = ::Call, T::SubmitTransaction>>::sign(signing_key, &signature_payload); + if let Some(signature) = maybe_signature { + let call: ::Call = Call::submit_election_solution_unsigned( + winners, + compact, + signature, + ).into(); + let _result = T::SubmitTransaction::submit_unsigned(call); + } + } else { + debug::native::warn!( + target: "staking", + "[unsigned] Have no key to sign this with", + ); + } + } } else { debug::native::warn!( target: "staking", @@ -1226,6 +1261,17 @@ decl_module! { }); } + /// Unsigned version of `submit_election_solution` + fn submit_election_solution_unsigned( + origin, + winners: Vec, + compact_assignments: CompactAssignments, + signature: SignatureOf::Call, T::SubmitTransaction>, + ) { + ensure_none(origin)?; + + } + /// Take the origin account as a stash and lock up `value` of its balance. `controller` will /// be the account that controls it. /// @@ -2299,3 +2345,52 @@ impl ReportOffence } } } + +// TODO: do we need BoundToRuntimeAppPublic stuff? +#[allow(deprecated)] +impl frame_support::unsigned::ValidateUnsigned for Module { + type Call = Call; + + fn validate_unsigned(call: &Self::Call) -> TransactionValidity { + Ok(TransactionValidity::Ok(ValidTransaction::default())) + // if let Call::heartbeat(heartbeat, signature) = call { + // if >::is_online(heartbeat.authority_index) { + // // we already received a heartbeat for this authority + // return InvalidTransaction::Stale.into(); + // } + + // // check if session index from heartbeat is recent + // let current_session = >::current_index(); + // if heartbeat.session_index != current_session { + // return InvalidTransaction::Stale.into(); + // } + + // // verify that the incoming (unverified) pubkey is actually an authority id + // let keys = Keys::::get(); + // let authority_id = match keys.get(heartbeat.authority_index as usize) { + // Some(id) => id, + // None => return InvalidTransaction::BadProof.into(), + // }; + + // // check signature (this is expensive so we do it last). + // let signature_valid = heartbeat.using_encoded(|encoded_heartbeat| { + // authority_id.verify(&encoded_heartbeat, &signature) + // }); + + // if !signature_valid { + // return InvalidTransaction::BadProof.into(); + // } + + // Ok(ValidTransaction { + // priority: TransactionPriority::max_value(), + // requires: vec![], + // provides: vec![(current_session, authority_id).encode()], + // longevity: TryInto::::try_into(T::SessionDuration::get() / 2.into()).unwrap_or(64_u64), + // propagate: true, + // }) + // } else { + // InvalidTransaction::Call.into() + // } + } +} + diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 0ccd60a320078..ad57973376582 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -31,7 +31,7 @@ pub trait CreateTransaction { /// A `Public` key representing a particular `AccountId`. type Public: IdentifyAccount + Clone; /// A `Signature` generated by the `Signer`. - type Signature; + type Signature: codec::Encode + codec::Decode; /// Attempt to create signed extrinsic data that encodes call from given account. /// @@ -95,12 +95,27 @@ impl Signer for TAnyAppPubl } /// Retrieves a public key type for given `SignAndSubmitTransaction`. -pub type PublicOf = < +pub type PublicOf = +< >::CreateTransaction as CreateTransaction>::Extrinsic> >::Public; +pub type SignerOf = +< + >::SignAndSubmit + as + SignAndSubmitTransaction +>::Signer; + +pub type SignatureOf = +< + >::CreateTransaction + as + CreateTransaction>::Extrinsic> +>::Signature; + /// A trait to sign and submit transactions in off-chain calls. /// /// NOTE: Most likely you should not implement this trait yourself. From 2bbdec44cfc2654e59f93a32f91b242bbd538e05 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 24 Jan 2020 10:24:14 +0100 Subject: [PATCH 022/106] A bit gleanup for unsigned debug --- frame/staking/src/lib.rs | 421 +++++++++++++++++------------------ frame/system/src/offchain.rs | 7 +- 2 files changed, 210 insertions(+), 218 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 597480a7312e5..5a5ad1fe91b6d 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -981,11 +981,7 @@ decl_module! { } fn offchain_worker(now: T::BlockNumber) { - // consult and make sure this is okay. - // debug::RuntimeLogger::init(); - // TODO: make sure that we don't need that is_validator here. if Self::era_election_status() == ElectionStatus::::Open(now) { - // TODO: well this is outdated, neh? need to get the correct one from session. let current_elected = Self::current_elected(); @@ -1002,10 +998,8 @@ decl_module! { .into_iter() .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::slashable_balance_of)) .collect(); - // reduce the assignments. This will remove some additional edges. sp_phragmen::reduce(&mut staked); - // compact encode the assignment. let compact = >::from_staked(staked); @@ -1020,25 +1014,23 @@ decl_module! { { // TODO: this call is really not needed, we can do it manually instead // of `can_sign_with`. TODO: we could use some error handling here. - let maybe_signing_key = T::SubmitTransaction::find_local_keys(Some(current_elected)); - if maybe_signing_key.len() > 0 { - let signing_key = maybe_signing_key[0].1; - let signature_payload = (winners.clone(), compact.clone()).encode(); - let maybe_signature = ::Call, T::SubmitTransaction>>::sign(signing_key, &signature_payload); - if let Some(signature) = maybe_signature { + let local_keys = T::SubmitTransaction::find_all_local_keys(); + // loop for at least one account in the validators to sign with. + local_keys + .into_iter() + .enumerate() + .find(|(_, (acc, _))| current_elected.contains(&acc)) + .map(|(index, (_, pubkey))| { + let signature_payload = (winners.clone(), compact.clone()).encode(); + let signature = ::Call, T::SubmitTransaction>>::sign(pubkey, &signature_payload).unwrap(); let call: ::Call = Call::submit_election_solution_unsigned( winners, compact, + index as u32, signature, ).into(); let _result = T::SubmitTransaction::submit_unsigned(call); - } - } else { - debug::native::warn!( - target: "staking", - "[unsigned] Have no key to sign this with", - ); - } + }); } } else { debug::native::warn!( @@ -1085,8 +1077,8 @@ decl_module! { /// A solutions score is consisted of 3 parameters: /// 1. `min { support.total }` for each support of a winner. This value should be maximized. /// 2. `sum { support.total }` for each support of a winner. This value should be minimized. - /// 3. `sum { support.total^2 }` for each support of a winner. This value should be minimized - /// (to ensure less variance) + /// 3. `sum { support.total^2 }` for each support of a winner. This value should be + /// minimized (to ensure less variance) /// /// # /// major steps: @@ -1108,157 +1100,7 @@ decl_module! { compact_assignments: CompactAssignments, ) { let _who = ensure_signed(origin)?; - - // discard early solutions - match Self::era_election_status() { - ElectionStatus::None => Err(Error::::PhragmenEarlySubmission)?, - ElectionStatus::Open(_) => { /* Open and no solutions received. Allowed. */ }, - } - - // convert into staked. - let staked_assignments = compact_assignments.into_staked::< - _, - _, - T::CurrencyToVote, - >(Self::slashable_balance_of); - - // build the support map thereof in order to evaluate. - // OPTIMIZATION: we could merge this with the `staked_assignments.iter()` below and - // iterate only once. - let (supports, num_error) = sp_phragmen::build_support_map::, T::AccountId>( - &winners, - &staked_assignments, - ); - - ensure!(num_error == 0, Error::::PhragmenBogusEdge); - - // score the result. Go further only if it is better than what we already have. - let submitted_score = sp_phragmen::evaluate_support(&supports); - if let Some(ElectionResult::> { - score, - .. - }) = Self::queued_elected() { - // OPTIMIZATION: we can read first the score and then the rest to do less decoding. - // if the local score is better in any of the three parameters. - ensure!( - Self::is_score_better(score, submitted_score), - Error::::PhragmenWeakSubmission, - ) - } - - // Either the result is better than the one on chain, or we don't have an any on chain. - // do the sanity check. Each claimed edge must exist. Also, each claimed winner must - // have a support associated. - - // check all winners being actual validators. - for w in winners.iter() { - ensure!( - >::exists(&w), - Error::::PhragmenBogusWinner, - ) - } - - // check all nominators being bonded, and actually including the claimed vote, and - // summing up to their ledger stake. - for StakedAssignment { who, distribution } in staked_assignments.iter() { - let maybe_bonded = Self::bonded(&who); - let is_validator = >::exists(&who); - let maybe_nomination = Self::nominators(&who); - - // it must be bonded. - ensure!( - maybe_bonded.is_some(), - Error::::PhragmenBogusNominator - ); - - // it must be either of - ensure!( - maybe_nomination.is_some() ^ is_validator, - Error::::PhragmenBogusNominator - ); - - let ctrl = maybe_bonded.expect("value is checked to be 'Some'; qed"); - let ledger = Self::ledger(ctrl).ok_or(Error::::NotController)?; - let active_extended_stake = - , u64>>::convert(ledger.active) as u128; - - if !is_validator { - // a normal vote - let nomination = maybe_nomination.expect( - "exactly one of maybe_validator and maybe_nomination is true. \ - is_validator is false; maybe_nomination is some; qed" - ); - let mut total_stake: ExtendedBalance = Zero::zero(); - ensure!( - distribution.into_iter().all(|(v, w)| { - total_stake += w; - nomination.targets.iter().find(|t| *t == v).is_some() - }), - Error::::PhragmenBogusNomination, - ); - ensure!( - total_stake == active_extended_stake, - Error::::PhragmenBogusNominatorStake - ); - } else { - // a self vote - ensure!(distribution.len() == 1, Error::::PhragmenBogusSelfVote); - ensure!(distribution[0].0 == *who, Error::::PhragmenBogusSelfVote); - // defensive only. A compact assignment of length one does NOT encode the stake - // and it is actually read from chain. Hence, this must always be correct. - ensure!( - distribution[0].1 == active_extended_stake, - Error::::PhragmenBogusSelfVote, - ); - } - } - // Note that we don't need to check again ^^ if a particular target in a nomination was - // actually a candidate. we don't explicitly check this but instead do it indirectly. - // build_support_map tells if any target was not in the winners. Also, we check that all - // the winners were actually candidates. Hence, all of the claimed votes are actually - // voting for valid candidates. All we have to check is if they actually come from the - // claimed nominator or not. - - // Endlich alles Ok. Exposures and store the result. - let to_balance = |e: ExtendedBalance| - >>::convert(e); - let mut slot_stake: BalanceOf = Bounded::max_value(); - - let exposures = supports.into_iter().map(|(validator, support)| { - let mut others = Vec::new(); - let mut own: BalanceOf = Zero::zero(); - let mut total: BalanceOf = Zero::zero(); - support.voters - .into_iter() - .map(|(target, weight)| (target, to_balance(weight))) - .for_each(|(nominator, stake)| { - if nominator == validator { - own = own.saturating_add(stake); - } else { - others.push(IndividualExposure { who: nominator, value: stake }); - } - total = total.saturating_add(stake); - }); - let exposure = Exposure { own, others, total }; - - if exposure.total < slot_stake { - slot_stake = exposure.total; - } - (validator, exposure) - }).collect::)>>(); - - debug::native::info!( - target: "staking", - "A better solution has been validated and stored on chain.", - ); - - >::put(ElectionResult { - compute: ElectionCompute::Submitted, - elected_stashes: winners, - score: submitted_score, - exposures, - slot_stake, - }); + Self::check_and_replace_solution(winners, compact_assignments)? } /// Unsigned version of `submit_election_solution` @@ -1266,10 +1108,12 @@ decl_module! { origin, winners: Vec, compact_assignments: CompactAssignments, - signature: SignatureOf::Call, T::SubmitTransaction>, + validator_index: u32, + // already checked. + _signature: SignatureOf::Call, T::SubmitTransaction>, ) { ensure_none(origin)?; - + Self::check_and_replace_solution(winners, compact_assignments)? } /// Take the origin account as a stash and lock up `value` of its balance. `controller` will @@ -1284,8 +1128,8 @@ decl_module! { /// - O(1). /// - Three extra DB entries. /// - /// NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned unless - /// the `origin` falls below _existential deposit_ and gets removed as dust. + /// NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned + /// unless the `origin` falls below _existential deposit_ and gets removed as dust. /// # #[weight = SimpleDispatchInfo::FixedNormal(500_000)] fn bond(origin, @@ -1817,6 +1661,166 @@ impl Module { Self::select_and_update_validators() } + /// Checks a given solution and if correct and improved, writes it on chain as the queued result + /// of the next round. This may be called by both a signed and an unsigned transaction. + fn check_and_replace_solution( + winners: Vec, + compact_assignments: CompactAssignments, + ) -> Result<(), Error> { + // discard early solutions + match Self::era_election_status() { + ElectionStatus::None => Err(Error::::PhragmenEarlySubmission)?, + ElectionStatus::Open(_) => { /* Open and no solutions received. Allowed. */ }, + } + + // convert into staked. + let staked_assignments = compact_assignments.into_staked::< + _, + _, + T::CurrencyToVote, + >(Self::slashable_balance_of); + + // build the support map thereof in order to evaluate. + // OPTIMIZATION: we could merge this with the `staked_assignments.iter()` below and + // iterate only once. + let (supports, num_error) = sp_phragmen::build_support_map::, T::AccountId>( + &winners, + &staked_assignments, + ); + + ensure!(num_error == 0, Error::::PhragmenBogusEdge); + + // score the result. Go further only if it is better than what we already have. + let submitted_score = sp_phragmen::evaluate_support(&supports); + if let Some(ElectionResult::> { + score, + .. + }) = Self::queued_elected() { + // OPTIMIZATION: we can read first the score and then the rest to do less decoding. + // if the local score is better in any of the three parameters. + ensure!( + Self::is_score_better(score, submitted_score), + Error::::PhragmenWeakSubmission, + ) + } + + // Either the result is better than the one on chain, or we don't have an any on chain. + // do the sanity check. Each claimed edge must exist. Also, each claimed winner must + // have a support associated. + + // check all winners being actual validators. + for w in winners.iter() { + ensure!( + >::exists(&w), + Error::::PhragmenBogusWinner, + ) + } + + // check all nominators being bonded, and actually including the claimed vote, and + // summing up to their ledger stake. + for StakedAssignment { who, distribution } in staked_assignments.iter() { + let maybe_bonded = Self::bonded(&who); + let is_validator = >::exists(&who); + let maybe_nomination = Self::nominators(&who); + + // it must be bonded. + ensure!( + maybe_bonded.is_some(), + Error::::PhragmenBogusNominator + ); + + // it must be either of + ensure!( + maybe_nomination.is_some() ^ is_validator, + Error::::PhragmenBogusNominator + ); + + let ctrl = maybe_bonded.expect("value is checked to be 'Some'; qed"); + let ledger = Self::ledger(ctrl).ok_or(Error::::NotController)?; + let active_extended_stake = + , u64>>::convert(ledger.active) as u128; + + if !is_validator { + // a normal vote + let nomination = maybe_nomination.expect( + "exactly one of maybe_validator and maybe_nomination is true. \ + is_validator is false; maybe_nomination is some; qed" + ); + let mut total_stake: ExtendedBalance = Zero::zero(); + ensure!( + distribution.into_iter().all(|(v, w)| { + total_stake += w; + nomination.targets.iter().find(|t| *t == v).is_some() + }), + Error::::PhragmenBogusNomination, + ); + ensure!( + total_stake == active_extended_stake, + Error::::PhragmenBogusNominatorStake + ); + } else { + // a self vote + ensure!(distribution.len() == 1, Error::::PhragmenBogusSelfVote); + ensure!(distribution[0].0 == *who, Error::::PhragmenBogusSelfVote); + // defensive only. A compact assignment of length one does NOT encode the stake + // and it is actually read from chain. Hence, this must always be correct. + ensure!( + distribution[0].1 == active_extended_stake, + Error::::PhragmenBogusSelfVote, + ); + } + } + // Note that we don't need to check again ^^ if a particular target in a nomination was + // actually a candidate. we don't explicitly check this but instead do it indirectly. + // build_support_map tells if any target was not in the winners. Also, we check that all + // the winners were actually candidates. Hence, all of the claimed votes are actually + // voting for valid candidates. All we have to check is if they actually come from the + // claimed nominator or not. + + // Endlich alles Ok. Exposures and store the result. + let to_balance = |e: ExtendedBalance| + >>::convert(e); + let mut slot_stake: BalanceOf = Bounded::max_value(); + + let exposures = supports.into_iter().map(|(validator, support)| { + let mut others = Vec::new(); + let mut own: BalanceOf = Zero::zero(); + let mut total: BalanceOf = Zero::zero(); + support.voters + .into_iter() + .map(|(target, weight)| (target, to_balance(weight))) + .for_each(|(nominator, stake)| { + if nominator == validator { + own = own.saturating_add(stake); + } else { + others.push(IndividualExposure { who: nominator, value: stake }); + } + total = total.saturating_add(stake); + }); + let exposure = Exposure { own, others, total }; + + if exposure.total < slot_stake { + slot_stake = exposure.total; + } + (validator, exposure) + }).collect::)>>(); + + debug::native::info!( + target: "staking", + "A better solution has been validated and stored on chain.", + ); + + >::put(ElectionResult { + compute: ElectionCompute::Submitted, + elected_stashes: winners, + score: submitted_score, + exposures, + slot_stake, + }); + + Ok(()) + } + /// The era has changed - enact new staking set. /// /// NOTE: This always happens immediately before a session change to ensure that new validators @@ -2352,45 +2356,32 @@ impl frame_support::unsigned::ValidateUnsigned for Module { type Call = Call; fn validate_unsigned(call: &Self::Call) -> TransactionValidity { - Ok(TransactionValidity::Ok(ValidTransaction::default())) - // if let Call::heartbeat(heartbeat, signature) = call { - // if >::is_online(heartbeat.authority_index) { - // // we already received a heartbeat for this authority - // return InvalidTransaction::Stale.into(); - // } - - // // check if session index from heartbeat is recent - // let current_session = >::current_index(); - // if heartbeat.session_index != current_session { - // return InvalidTransaction::Stale.into(); - // } - - // // verify that the incoming (unverified) pubkey is actually an authority id - // let keys = Keys::::get(); - // let authority_id = match keys.get(heartbeat.authority_index as usize) { - // Some(id) => id, - // None => return InvalidTransaction::BadProof.into(), - // }; - - // // check signature (this is expensive so we do it last). - // let signature_valid = heartbeat.using_encoded(|encoded_heartbeat| { - // authority_id.verify(&encoded_heartbeat, &signature) - // }); - - // if !signature_valid { - // return InvalidTransaction::BadProof.into(); - // } - - // Ok(ValidTransaction { - // priority: TransactionPriority::max_value(), - // requires: vec![], - // provides: vec![(current_session, authority_id).encode()], - // longevity: TryInto::::try_into(T::SessionDuration::get() / 2.into()).unwrap_or(64_u64), - // propagate: true, - // }) - // } else { - // InvalidTransaction::Call.into() - // } + if let Call::submit_election_solution_unsigned(winners, compact, validator_index, signature) = call { + // TODO: since unsigned is only for validators, and it is not increasing the block + // weight and fee etc. maybe we should only accept an unsigned solution when we don't + // have ANY solutions. Otherwise each block will get a LOT of these. + + // check signature + // let payload = (winners, compact); + // let signature_valid = payload.using_encoded(|encoded_payload| { + // authority_id.verify(&encoded_heartbeat, &signature) + // }); + + // if !signature_valid { + // return InvalidTransaction::BadProof.into(); + // } + + // Ok(ValidTransaction { + // priority: TransactionPriority::max_value(), + // requires: vec![], + // provides: vec![(current_session, authority_id).encode()], + // longevity: TryInto::::try_into(T::SessionDuration::get() / 2.into()).unwrap_or(64_u64), + // propagate: true, + // }) + TransactionValidity::Ok(ValidTransaction::default()) + } else { + InvalidTransaction::Call.into() + } } } diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index ad57973376582..41cce3d940ad2 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -16,9 +16,10 @@ //! Module helpers for off-chain calls. -use codec::Encode; +use codec::{Encode, Decode}; use sp_std::convert::TryInto; use sp_std::prelude::Vec; +use sp_std::fmt::Debug; use sp_runtime::app_crypto::{RuntimeAppPublic, AppPublic, AppSignature}; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount}; use frame_support::debug; @@ -31,7 +32,7 @@ pub trait CreateTransaction { /// A `Public` key representing a particular `AccountId`. type Public: IdentifyAccount + Clone; /// A `Signature` generated by the `Signer`. - type Signature: codec::Encode + codec::Decode; + type Signature: Encode + Decode + Clone + Debug + Eq + PartialEq + Default; //TODO: SignatureOf Gives you this. /// Attempt to create signed extrinsic data that encodes call from given account. /// @@ -170,7 +171,7 @@ pub trait SignAndSubmitTransaction { /// you should use. pub trait SubmitUnsignedTransaction { /// Unchecked extrinsic type. - type Extrinsic: ExtrinsicT + codec::Encode; + type Extrinsic: ExtrinsicT + Encode; /// Submit given call to the transaction pool as unsigned transaction. /// From c9cfcb8337ed7ff76aa7a0dcf22ebca10bfbee58 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 24 Jan 2020 11:50:32 +0100 Subject: [PATCH 023/106] Clean and finalize compact code. --- Cargo.lock | 2 +- frame/staking/Cargo.toml | 1 - primitives/phragmen/Cargo.toml | 1 + primitives/phragmen/compact/src/lib.rs | 196 +++++++++------- primitives/phragmen/src/lib.rs | 3 + primitives/phragmen/src/tests.rs | 295 +++++++++++++------------ 6 files changed, 277 insertions(+), 221 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e00851eb56d26..e3a6bbe75b05a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3967,7 +3967,6 @@ dependencies = [ "sp-io 2.0.0", "sp-keyring 2.0.0", "sp-phragmen 2.0.0", - "sp-phragmen-compact 2.0.0", "sp-runtime 2.0.0", "sp-staking 2.0.0", "sp-std 2.0.0", @@ -6512,6 +6511,7 @@ dependencies = [ "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "sp-io 2.0.0", + "sp-phragmen 2.0.0", "sp-phragmen-compact 2.0.0", "sp-runtime 2.0.0", "sp-std 2.0.0", diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 5db2767a13e90..ac51e2aa73efd 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -10,7 +10,6 @@ codec = { package = "parity-scale-codec", version = "1.0.0", default-features = sp-keyring = { version = "2.0.0", optional = true, path = "../../primitives/keyring" } sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } sp-phragmen = { version = "2.0.0", default-features = false, path = "../../primitives/phragmen" } -sp-phragmen-compact = { version = "2.0.0", path = "../../primitives/phragmen/compact" } sp-io ={ path = "../../primitives/io", default-features = false } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } sp-staking = { version = "2.0.0", default-features = false, path = "../../primitives/staking" } diff --git a/primitives/phragmen/Cargo.toml b/primitives/phragmen/Cargo.toml index 90575bc48bfbe..fa1e7757bb624 100644 --- a/primitives/phragmen/Cargo.toml +++ b/primitives/phragmen/Cargo.toml @@ -15,6 +15,7 @@ codec = { package = "parity-scale-codec", version = "1.0.0", default-features = substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } sp-io ={ version = "2.0.0", path = "../../primitives/io" } rand = "0.7.3" +sp-phragmen = { path = "." } [features] default = ["std"] diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index 0a32286e8682d..c9be33f2ab809 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -22,46 +22,31 @@ use proc_macro::TokenStream; use proc_macro2::{TokenStream as TokenStream2, Span, Ident}; use proc_macro_crate::crate_name; use quote::quote; -use syn::parse::{Parse, ParseStream}; -// use codec::{Encode, Decode}; +use syn::{GenericArgument, Type, parse::{Parse, ParseStream, Result}}; +// prefix used for struct fields in compact. const PREFIX: &'static str = "votes"; -struct CompactSolutionDef { - vis: syn::Visibility, - ident: syn::Ident, - count: usize, -} - -impl Parse for CompactSolutionDef { - fn parse(input: ParseStream) -> syn::Result { - let vis: syn::Visibility = input.parse()?; - let ident: syn::Ident = input.parse()?; - let _ = ::parse(input)?; - let count_literal: syn::LitInt = input.parse()?; - let count = count_literal.base10_parse::()?; - Ok(Self { vis, ident, count } ) - } -} - -fn field_name_for(n: usize) -> Ident { - Ident::new(&format!("{}{}", PREFIX, n), Span::call_site()) -} - /// Generates a struct to store the phragmen assignments in a compact way. The struct can only store /// distributions up to the given input count. The given count must be greater than 2. /// +/// ```rust +/// // generate a struct with account Identifier u32 and edge weight u128, with maximum supported +/// // edge per voter of 32. +/// generate_compact_solution_type(TestCompact, 32) +/// ``` +/// /// The generated structure creates one key for each possible count of distributions from 1 up to -/// the given length. A normal distribution is a tuple of `(candidate, weight)` where `candidate` is -/// a generic `AccountId` and `weight` is an encodable `T`. Typically, the weight can refer to -/// either the ratio of the voter's support or its absolute value. The following rules hold -/// regarding the compact representation: +/// the given length. A normal distribution is a tuple of `(candidate, weight)`. Typically, the +/// weight can refer to either the ratio of the voter's support or its absolute value. The following +/// rules hold regarding the compact representation: /// - For single distribution, no weight is stored. The weight is known to be 100%. /// - For all the rest, the weight if the last distribution is omitted. This value can be computed /// from the rest. /// /// An example expansion of length 16 is as follows: /// +/// ```rust /// struct TestCompact { /// votes1: Vec<(AccountId, AccountId)>, /// votes2: Vec<(AccountId, (AccountId, W), AccountId)>, @@ -79,7 +64,8 @@ fn field_name_for(n: usize) -> Ident { /// votes14: Vec<(AccountId, [(AccountId, W); 13usize], AccountId)>, /// votes15: Vec<(AccountId, [(AccountId, W); 14usize], AccountId)>, /// votes16: Vec<(AccountId, [(AccountId, W); 15usize], AccountId)>, -/// } +/// } +/// ``` #[proc_macro] pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { let CompactSolutionDef { @@ -87,90 +73,123 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { ident, count, } = syn::parse_macro_input!(item as CompactSolutionDef); - let account_type = quote!(AccountId); - let weight = quote!(W); + let account_type = GenericArgument::Type(Type::Verbatim(quote!(AccountId))); + let weight_type = GenericArgument::Type(Type::Verbatim(quote!(weight))); + + let imports = imports().unwrap_or_else(|e| e.to_compile_error()); + + let compact_def = struct_def( + vis, + ident.clone(), + count, + account_type.clone(), + weight_type, + ).unwrap_or_else(|e| e.to_compile_error()); + + let from_into_impl_assignment = convert_impl_for_assignment( + ident.clone(), + account_type.clone(), + count, + ); + + let from_into_impl_staked_assignment = convert_impl_for_staked_assignment( + ident, + account_type, + count, + ); + + quote!( + #imports + #compact_def + #from_into_impl_assignment + #from_into_impl_staked_assignment + ).into() +} + +fn struct_def( + vis: syn::Visibility, + ident: syn::Ident, + count: usize, + account_type: GenericArgument, + weight_type: GenericArgument, +) -> Result { if count <= 2 { - panic!("cannot build compact solution struct with capacity less than 2."); + Err(syn::Error::new( + Span::call_site(), + "cannot build compact solution struct with capacity less than 2." + ))? } - let imports = imports(); - let singles = { let name = field_name_for(1); quote!(#name: Vec<(#account_type, #account_type)>,) }; + let doubles = { let name = field_name_for(2); - quote!(#name: Vec<(#account_type, (#account_type, #weight), #account_type)>,) + quote!(#name: Vec<(#account_type, (#account_type, #weight_type), #account_type)>,) }; + let rest = (3..=count).map(|c| { let field_name = field_name_for(c); let array_len = c - 1; quote!( #field_name: Vec<( #account_type, - [(#account_type, #weight); #array_len], + [(#account_type, #weight_type); #array_len], #account_type )>, ) }).collect::(); let compact_def = quote! ( - #imports + /// A struct to encode a `Vec` or `Vec` of the phragmen module + /// in a compact way. #[derive(Default, PartialEq, Eq, Clone, _sp_runtime::RuntimeDebug, _codec::Encode, _codec::Decode)] - #vis struct #ident<#account_type, #weight> { + #vis struct #ident<#account_type, #weight_type> { #singles #doubles #rest } ); - let from_into_impl_assignment = convert_impl_for_assignment( - ident.clone(), - account_type.clone(), - count, - ); - - let from_into_impl_staked_assignment = convert_impl_for_staked_assignment( - ident, - account_type, - count, - ); - - - quote!( - #compact_def - #from_into_impl_assignment - #from_into_impl_staked_assignment - ).into() + Ok(compact_def) } -fn imports() -> TokenStream2 { +fn imports() -> Result { let runtime_imports = match crate_name("sp-runtime") { Ok(sp_runtime) => { let ident = syn::Ident::new(&sp_runtime, Span::call_site()); quote!( extern crate #ident as _sp_runtime; ) }, - Err(e) => syn::Error::new(Span::call_site(), &e).to_compile_error(), + Err(e) => return Err(syn::Error::new(Span::call_site(), &e)), }; let codec_imports = match crate_name("parity-scale-codec") { Ok(codec) => { let ident = syn::Ident::new(&codec, Span::call_site()); quote!( extern crate #ident as _codec; ) }, - Err(e) => syn::Error::new(Span::call_site(), &e).to_compile_error(), + Err(e) => return Err(syn::Error::new(Span::call_site(), &e)), + }; + let sp_phragmen_imports = match crate_name("sp-phragmen") { + Ok(sp_phragmen) => { + let ident = syn::Ident::new(&sp_phragmen, Span::call_site()); + quote!( extern crate #ident as _phragmen; ) + } + Err(e) => return Err(syn::Error::new(Span::call_site(), &e)), }; - quote!( + Ok(quote!( #runtime_imports #codec_imports - ) + #sp_phragmen_imports + )) } fn convert_impl_for_assignment( ident: syn::Ident, - account_type: TokenStream2, + account_type: GenericArgument, count: usize ) -> TokenStream2 { let from_impl_single = { @@ -194,7 +213,7 @@ fn convert_impl_for_assignment( }).collect::(); let from_impl = quote!( - impl<#account_type: codec::Codec + Default + Clone> + impl<#account_type: _codec::Codec + Default + Clone> From>> for #ident<#account_type, Perbill> { @@ -232,7 +251,7 @@ fn convert_impl_for_assignment( let name = field_name_for(2); quote!( for (who, (t1, p1), t2) in self.#name { - let p2 = Perbill::one().saturating_sub(p1); + let p2 = _sp_runtime::traits::Saturating::saturating_sub(Perbill::one(), p1); assignments.push( Assignment { who, distribution: vec![ @@ -251,11 +270,11 @@ fn convert_impl_for_assignment( let mut inners_parsed = inners .into_iter() .map(|(ref c, p)| { - sum = sum.saturating_add(*p); + sum = _sp_runtime::traits::Saturating::saturating_add(sum, *p); (c.clone(), *p) }).collect::>(); - let p_last = Perbill::one().saturating_sub(sum); + let p_last = _sp_runtime::traits::Saturating::saturating_sub(Perbill::one(), sum); inners_parsed.push((t_last, p_last)); assignments.push(Assignment { @@ -267,7 +286,7 @@ fn convert_impl_for_assignment( }).collect::(); let into_impl = quote!( - impl<#account_type: codec::Codec + Default + Clone> + impl<#account_type: _codec::Codec + Default + Clone> Into>> for #ident<#account_type, Perbill> { @@ -290,7 +309,7 @@ fn convert_impl_for_assignment( fn convert_impl_for_staked_assignment( ident: syn::Ident, - account_type: TokenStream2, + account_type: GenericArgument, count: usize ) -> TokenStream2 { let from_impl_single = { @@ -324,7 +343,7 @@ fn convert_impl_for_staked_assignment( quote!( for (who, target) in self.#name { let all_stake = C::convert(max_of(&who)) as u128; - assignments.push(StakedAssignment { + assignments.push(_phragmen::StakedAssignment { who, distribution: vec![(target, all_stake)], }) @@ -337,7 +356,7 @@ fn convert_impl_for_staked_assignment( for (who, (t1, w1), t2) in self.#name { let all_stake = C::convert(max_of(&who)) as u128; let w2 = all_stake.saturating_sub(w1); - assignments.push( StakedAssignment { + assignments.push( _phragmen::StakedAssignment { who, distribution: vec![ (t1, w1), @@ -363,7 +382,7 @@ fn convert_impl_for_staked_assignment( let w_last = all_stake.saturating_sub(sum); inners_parsed.push((t_last, w_last)); - assignments.push(StakedAssignment { + assignments.push(_phragmen::StakedAssignment { who, distribution: inners_parsed, }); @@ -372,16 +391,19 @@ fn convert_impl_for_staked_assignment( }).collect::(); let final_impl = quote!( - impl<#account_type: codec::Codec + Default + Clone> + impl<#account_type: _codec::Codec + Default + Clone> #ident<#account_type, u128> { + /// Convert self into `StakedAssignment`. The given function should return the total + /// weight of a voter. It is used to subtract the sum of all the encoded weights to + /// infer the last one. fn into_staked(self, max_of: FM) - -> Vec> + -> Vec<_phragmen::StakedAssignment<#account_type>> where for<'r> FM: Fn(&'r #account_type) -> Balance, - C: Convert + C: _sp_runtime::traits::Convert { - let mut assignments: Vec> = Default::default(); + let mut assignments: Vec<_phragmen::StakedAssignment<#account_type>> = Default::default(); #into_impl_single #into_impl_double #into_impl_rest @@ -389,9 +411,10 @@ fn convert_impl_for_staked_assignment( assignments } - fn from_staked(assignments: Vec>) -> Self { + /// Generate self from a vector of `StakedAssignment`. + fn from_staked(assignments: Vec<_phragmen::StakedAssignment<#account_type>>) -> Self { let mut compact: #ident<#account_type, u128> = Default::default(); - assignments.into_iter().for_each(|StakedAssignment { who, distribution } | { + assignments.into_iter().for_each(|_phragmen::StakedAssignment { who, distribution } | { match distribution.len() { #from_impl_single #from_impl_double @@ -410,3 +433,24 @@ fn convert_impl_for_staked_assignment( #final_impl ) } + +struct CompactSolutionDef { + vis: syn::Visibility, + ident: syn::Ident, + count: usize, +} + +impl Parse for CompactSolutionDef { + fn parse(input: ParseStream) -> syn::Result { + let vis: syn::Visibility = input.parse()?; + let ident: syn::Ident = input.parse()?; + let _ = ::parse(input)?; + let count_literal: syn::LitInt = input.parse()?; + let count = count_literal.base10_parse::()?; + Ok(Self { vis, ident, count } ) + } +} + +fn field_name_for(n: usize) -> Ident { + Ident::new(&format!("{}{}", PREFIX, n), Span::call_site()) +} diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 59c294feece80..4b30cc2935caf 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -48,6 +48,9 @@ mod reduce; // re-export reduce stuff pub use reduce::reduce; +// re-export the compact macro +pub use sp_phragmen_compact::generate_compact_solution_type; + /// A type in which performing operations on balances and stakes of candidates and voters are safe. /// /// This module's functions expect a `Convert` type to convert all balances to u64. Hence, u128 is diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index bb11ff125878a..9a3e39c98db64 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -24,11 +24,7 @@ use crate::{ Support, StakedAssignment, Assignment, PhragmenResult, }; use substrate_test_utils::assert_eq_uvec; -use sp_runtime::{Perbill, Saturating, traits::Convert}; -use codec::{Encode, Decode}; -use sp_phragmen_compact::generate_compact_solution_type; - -generate_compact_solution_type!(TestCompact, 16); +use sp_runtime::Perbill; #[test] fn float_phragmen_poc_works() { @@ -449,144 +445,6 @@ fn minimum_to_elect_is_respected() { assert!(maybe_result.is_none()); } -#[test] -fn compact_struct_is_codec() { - let compact = TestCompact { - votes1: vec![(2, 20), (4, 40)], - votes2: vec![ - (1, (10, Perbill::from_percent(80)), 11), - (5, (50, Perbill::from_percent(85)), 51), - ], - ..Default::default() - }; - - let encoded = compact.encode(); - - assert_eq!( - compact, - Decode::decode(&mut &encoded[..]).unwrap(), - ); -} - -#[test] -fn basic_from_and_into_compact_works_assignments() { - let assignments = vec![ - Assignment { - who: 2u64, - distribution: vec![(20, Perbill::from_percent(100))] - }, - Assignment { - who: 4u64, - distribution: vec![(40, Perbill::from_percent(100))], - }, - Assignment { - who: 1u64, - distribution: vec![ - (10, Perbill::from_percent(80)), - (11, Perbill::from_percent(20)) - ], - }, - Assignment { - who: 5u64, distribution: - vec![ - (50, Perbill::from_percent(85)), - (51, Perbill::from_percent(15)), - ] - }, - Assignment { - who: 3u64, - distribution: vec![ - (30, Perbill::from_percent(50)), - (31, Perbill::from_percent(25)), - (32, Perbill::from_percent(25)), - ], - }, - ]; - - let compacted: TestCompact = assignments.clone().into(); - - assert_eq!( - compacted, - TestCompact { - votes1: vec![(2, 20), (4, 40)], - votes2: vec![ - (1, (10, Perbill::from_percent(80)), 11), - (5, (50, Perbill::from_percent(85)), 51), - ], - votes3: vec![ - (3, [(30, Perbill::from_percent(50)), (31, Perbill::from_percent(25))], 32), - ], - ..Default::default() - } - ); - - assert_eq!( - as Into>>>::into(compacted), - assignments - ); -} - -#[test] -fn basic_from_and_into_compact_works_staked_assignments() { - let assignments = vec![ - StakedAssignment { - who: 2u64, - distribution: vec![(20, 100u128)] - }, - StakedAssignment { - who: 4u64, - distribution: vec![(40, 100)], - }, - StakedAssignment { - who: 1u64, - distribution: vec![ - (10, 80), - (11, 20) - ], - }, - StakedAssignment { - who: 5u64, distribution: - vec![ - (50, 85), - (51, 15), - ] - }, - StakedAssignment { - who: 3u64, - distribution: vec![ - (30, 50), - (31, 25), - (32, 25), - ], - }, - ]; - - let compacted = >::from_staked(assignments.clone()); - - assert_eq!( - compacted, - TestCompact { - votes1: vec![(2, 20), (4, 40)], - votes2: vec![ - (1, (10, 80), 11), - (5, (50, 85), 51), - ], - votes3: vec![ - (3, [(30, 50), (31, 25)], 32), - ], - ..Default::default() - } - ); - - let max_of_fn = |_: &AccountId| -> Balance { 100u128 }; - let max_of: Box Balance> = Box::new(max_of_fn); - - assert_eq!( - compacted.into_staked::(&max_of), - assignments - ); -} - #[test] fn self_votes_should_be_kept() { let candidates = vec![5, 10, 20, 30]; @@ -665,3 +523,154 @@ fn self_votes_should_be_kept() { &Support { total: 20u128, voters: vec![(20u64, 20u128)] }, ); } + +mod compact { + use crate::generate_compact_solution_type; + // these need to come from the same dev-dependency `sp-phragmen`, not from the crate. + use sp_phragmen::{Assignment, StakedAssignment}; + + use super::{AccountId, Balance}; + use sp_runtime::Perbill; + use codec::{Decode, Encode}; + use crate::mock::{TestCurrencyToVote}; + + generate_compact_solution_type!(TestCompact, 16); + + #[test] + fn compact_struct_is_codec() { + let compact = TestCompact { + votes1: vec![(2, 20), (4, 40)], + votes2: vec![ + (1, (10, Perbill::from_percent(80)), 11), + (5, (50, Perbill::from_percent(85)), 51), + ], + ..Default::default() + }; + + let encoded = compact.encode(); + + assert_eq!( + compact, + Decode::decode(&mut &encoded[..]).unwrap(), + ); + } + + #[test] + fn basic_from_and_into_compact_works_assignments() { + let assignments = vec![ + Assignment { + who: 2u64, + distribution: vec![(20, Perbill::from_percent(100))] + }, + Assignment { + who: 4u64, + distribution: vec![(40, Perbill::from_percent(100))], + }, + Assignment { + who: 1u64, + distribution: vec![ + (10, Perbill::from_percent(80)), + (11, Perbill::from_percent(20)) + ], + }, + Assignment { + who: 5u64, distribution: + vec![ + (50, Perbill::from_percent(85)), + (51, Perbill::from_percent(15)), + ] + }, + Assignment { + who: 3u64, + distribution: vec![ + (30, Perbill::from_percent(50)), + (31, Perbill::from_percent(25)), + (32, Perbill::from_percent(25)), + ], + }, + ]; + + let compacted: TestCompact = assignments.clone().into(); + + assert_eq!( + compacted, + TestCompact { + votes1: vec![(2, 20), (4, 40)], + votes2: vec![ + (1, (10, Perbill::from_percent(80)), 11), + (5, (50, Perbill::from_percent(85)), 51), + ], + votes3: vec![ + (3, [(30, Perbill::from_percent(50)), (31, Perbill::from_percent(25))], 32), + ], + ..Default::default() + } + ); + + assert_eq!( + as Into>>>::into(compacted), + assignments + ); + } + + #[test] + fn basic_from_and_into_compact_works_staked_assignments() { + let assignments = vec![ + StakedAssignment { + who: 2u64, + distribution: vec![(20, 100u128)] + }, + StakedAssignment { + who: 4u64, + distribution: vec![(40, 100)], + }, + StakedAssignment { + who: 1u64, + distribution: vec![ + (10, 80), + (11, 20) + ], + }, + StakedAssignment { + who: 5u64, distribution: + vec![ + (50, 85), + (51, 15), + ] + }, + StakedAssignment { + who: 3u64, + distribution: vec![ + (30, 50), + (31, 25), + (32, 25), + ], + }, + ]; + + let compacted = >::from_staked(assignments.clone()); + + assert_eq!( + compacted, + TestCompact { + votes1: vec![(2, 20), (4, 40)], + votes2: vec![ + (1, (10, 80), 11), + (5, (50, 85), 51), + ], + votes3: vec![ + (3, [(30, 50), (31, 25)], 32), + ], + ..Default::default() + } + ); + + let max_of_fn = |_: &AccountId| -> Balance { 100u128 }; + let max_of: Box Balance> = Box::new(max_of_fn); + + assert_eq!( + compacted.into_staked::(&max_of), + assignments + ); + } +} From 3633a77bba7a553fda658b8261605a4efbda2bb1 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 27 Jan 2020 09:38:24 +0100 Subject: [PATCH 024/106] Document reduce. --- frame/staking/src/lib.rs | 34 +-- frame/system/src/offchain.rs | 4 +- primitives/phragmen/fuzzer/Cargo.lock | 276 ++++++++++------------- primitives/phragmen/fuzzer/src/reduce.rs | 6 +- primitives/phragmen/src/lib.rs | 8 +- primitives/phragmen/src/node.rs | 51 ++++- primitives/phragmen/src/reduce.rs | 276 ++++++++++++++++------- 7 files changed, 392 insertions(+), 263 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 5a5ad1fe91b6d..e83ec8bb3d310 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -285,11 +285,14 @@ use sp_staking::{ use sp_runtime::{Serialize, Deserialize}; use frame_system::{ self as system, ensure_signed, ensure_root, ensure_none, - offchain::{SubmitSignedTransaction, CreateTransaction, SubmitUnsignedTransaction, PublicOf, SignerOf, SignatureOf, SignAndSubmitTransaction, Signer}, + offchain::{SubmitSignedTransaction, SubmitUnsignedTransaction, SignerOf, SignatureOf, Signer}, }; use sp_application_crypto::RuntimeAppPublic; -use sp_phragmen::{ExtendedBalance, Assignment, StakedAssignment}; +use sp_phragmen::{ + ExtendedBalance, Assignment, StakedAssignment, + generate_compact_solution_type, +}; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; // ------------- IMPORTANT NOTE: must be the same as `generate_compact_solution_type`. @@ -592,7 +595,7 @@ impl Default for ElectionStatus { } // ------------- IMPORTANT NOTE: must be the same as `MAX_NOMINATIONS`. -sp_phragmen_compact::generate_compact_solution_type!(pub CompactAssignments, 16); +generate_compact_solution_type!(pub CompactAssignments, 16); pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -707,11 +710,7 @@ pub trait Trait: frame_system::Trait { /// A transaction submitter. type SubmitTransaction: SubmitSignedTransaction::Call> + - SubmitUnsignedTransaction::Call> + - SignAndSubmitTransaction::Call>; - - /// The key type. Must be the same as given to `SubmitTransaction`. TODO: we can probably extract it from it - type AuthorityId: Member + Parameter + RuntimeAppPublic + Default + Ord; + SubmitUnsignedTransaction::Call>; } /// Mode of era-forcing. @@ -1013,7 +1012,8 @@ decl_module! { #[cfg(not(feature = "signed"))] { // TODO: this call is really not needed, we can do it manually instead - // of `can_sign_with`. TODO: we could use some error handling here. + // of `can_sign_with`. + // TODO: we could use some error handling here. let local_keys = T::SubmitTransaction::find_all_local_keys(); // loop for at least one account in the validators to sign with. local_keys @@ -1021,7 +1021,7 @@ decl_module! { .enumerate() .find(|(_, (acc, _))| current_elected.contains(&acc)) .map(|(index, (_, pubkey))| { - let signature_payload = (winners.clone(), compact.clone()).encode(); + let signature_payload = (winners.clone(), compact.clone(), index as u32).encode(); let signature = ::Call, T::SubmitTransaction>>::sign(pubkey, &signature_payload).unwrap(); let call: ::Call = Call::submit_election_solution_unsigned( winners, @@ -1110,7 +1110,7 @@ decl_module! { compact_assignments: CompactAssignments, validator_index: u32, // already checked. - _signature: SignatureOf::Call, T::SubmitTransaction>, + _signature: SignatureOf::Call, ::Call>>::SignAndSubmit>, ) { ensure_none(origin)?; Self::check_and_replace_solution(winners, compact_assignments)? @@ -2362,10 +2362,14 @@ impl frame_support::unsigned::ValidateUnsigned for Module { // have ANY solutions. Otherwise each block will get a LOT of these. // check signature - // let payload = (winners, compact); - // let signature_valid = payload.using_encoded(|encoded_payload| { - // authority_id.verify(&encoded_heartbeat, &signature) - // }); + let payload = (winners, compact, validator_index); + let validator_id = Self::current_elected().get(*validator_index as usize).unwrap(); + + + let signature_valid = payload.using_encoded(|encoded_payload| { + // validator_id need to be converted to a key type. + validator_id.verify(&payload, &signature) + }); // if !signature_valid { // return InvalidTransaction::BadProof.into(); diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 41cce3d940ad2..daa79d21be532 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -30,9 +30,9 @@ use frame_support::debug; /// to submit `SignedTransaction`s` to the pool from off-chain code. pub trait CreateTransaction { /// A `Public` key representing a particular `AccountId`. - type Public: IdentifyAccount + Clone; + type Public: IdentifyAccount + Clone + RuntimeAppPublic; /// A `Signature` generated by the `Signer`. - type Signature: Encode + Decode + Clone + Debug + Eq + PartialEq + Default; //TODO: SignatureOf Gives you this. + type Signature: Encode + Decode + Clone + Debug + Eq + PartialEq + Default; /// Attempt to create signed extrinsic data that encodes call from given account. /// diff --git a/primitives/phragmen/fuzzer/Cargo.lock b/primitives/phragmen/fuzzer/Cargo.lock index 16fb9e72d4445..0d4ea2559140d 100644 --- a/primitives/phragmen/fuzzer/Cargo.lock +++ b/primitives/phragmen/fuzzer/Cargo.lock @@ -14,7 +14,7 @@ name = "ahash" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -75,7 +75,7 @@ version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -123,7 +123,7 @@ dependencies = [ [[package]] name = "byte-slice-cast" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -172,20 +172,20 @@ dependencies = [ [[package]] name = "const-random" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "const-random-macro 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "const-random-macro" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -236,9 +236,9 @@ name = "derive_more" version = "0.99.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -279,9 +279,9 @@ name = "failure_derive" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -292,13 +292,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "fixed-hash" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -317,12 +317,12 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -402,7 +402,15 @@ name = "impl-serde" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "impl-serde" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -410,14 +418,14 @@ name = "impl-trait-for-tuples" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "integer-sqrt" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -432,7 +440,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.65" +version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -479,8 +487,8 @@ name = "malloc_size_of_derive" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -499,13 +507,13 @@ name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memory-db" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -593,20 +601,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-slice-cast 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec-derive 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-slice-cast 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parity-scale-codec-derive" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -648,7 +656,7 @@ name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -662,7 +670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -684,9 +692,9 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -705,12 +713,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "primitive-types" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "fixed-hash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-serde 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -719,7 +727,7 @@ name = "proc-macro-crate" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -727,41 +735,25 @@ name = "proc-macro-hack" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro2" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -770,7 +762,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -787,8 +779,8 @@ name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -830,7 +822,7 @@ name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -862,7 +854,7 @@ name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -874,7 +866,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -933,7 +925,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rustc-hex" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -985,20 +977,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1030,7 +1022,7 @@ name = "sp-application-crypto" version = "2.0.0" dependencies = [ "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0", "sp-io 2.0.0", "sp-std 2.0.0", @@ -1040,10 +1032,10 @@ dependencies = [ name = "sp-arithmetic" version = "2.0.0" dependencies = [ - "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "integer-sqrt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-debug-derive 2.0.0", "sp-std 2.0.0", ] @@ -1066,15 +1058,15 @@ dependencies = [ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "sp-debug-derive 2.0.0", - "sp-externalities 2.0.0", + "sp-externalities 0.8.0", "sp-runtime-interface 2.0.0", "sp-std 2.0.0", "sp-storage 2.0.0", @@ -1090,14 +1082,14 @@ dependencies = [ name = "sp-debug-derive" version = "2.0.0" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-externalities" -version = "2.0.0" +version = "0.8.0" dependencies = [ "environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "sp-std 2.0.0", @@ -1124,9 +1116,9 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0", - "sp-externalities 2.0.0", + "sp-externalities 0.8.0", "sp-runtime-interface 2.0.0", - "sp-state-machine 2.0.0", + "sp-state-machine 0.8.0", "sp-std 2.0.0", "sp-trie 2.0.0", ] @@ -1143,7 +1135,7 @@ dependencies = [ name = "sp-phragmen" version = "2.0.0" dependencies = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-phragmen-compact 2.0.0", "sp-runtime 2.0.0", "sp-std 2.0.0", @@ -1154,9 +1146,9 @@ name = "sp-phragmen-compact" version = "2.0.0" dependencies = [ "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1177,7 +1169,7 @@ dependencies = [ "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-application-crypto 2.0.0", "sp-arithmetic 2.0.0", "sp-core 2.0.0", @@ -1190,10 +1182,9 @@ dependencies = [ name = "sp-runtime-interface" version = "2.0.0" dependencies = [ - "environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 2.0.0", + "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.8.0", "sp-runtime-interface-proc-macro 2.0.0", "sp-std 2.0.0", "sp-wasm-interface 2.0.0", @@ -1206,14 +1197,14 @@ version = "2.0.0" dependencies = [ "Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-state-machine" -version = "2.0.0" +version = "0.8.0" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1222,10 +1213,10 @@ dependencies = [ "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0", - "sp-externalities 2.0.0", + "sp-externalities 0.8.0", "sp-panic-handler 2.0.0", "sp-trie 2.0.0", - "trie-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-db 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)", "trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1238,7 +1229,7 @@ name = "sp-storage" version = "2.0.0" dependencies = [ "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-debug-derive 2.0.0", "sp-std 2.0.0", ] @@ -1248,11 +1239,11 @@ name = "sp-trie" version = "2.0.0" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memory-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0", "sp-std 2.0.0", - "trie-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-db 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)", "trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1292,20 +1283,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "1.0.8" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1315,9 +1296,9 @@ name = "synstructure" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1353,21 +1334,22 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trie-db" -version = "0.18.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1384,7 +1366,7 @@ name = "twox-hash" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1399,15 +1381,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "unicode-xid" version = "0.2.0" @@ -1415,7 +1392,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasi" -version = "0.7.0" +version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1423,7 +1400,7 @@ name = "wasmi" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1476,9 +1453,9 @@ name = "zeroize_derive" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1500,7 +1477,7 @@ dependencies = [ "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -"checksum byte-slice-cast 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f6209f3b2c1edea170002e016d5ead6903d3bb0a846477f53bbeb614967a52a9" +"checksum byte-slice-cast 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" @@ -1508,8 +1485,8 @@ dependencies = [ "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7b641a8c9867e341f3295564203b1c250eb8ce6cb6126e007941f78c4d2ed7fe" -"checksum const-random-macro 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c750ec12b83377637110d5a57f5ae08e895b06c4b16e2bdbf1a94ef717428c59" +"checksum const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" +"checksum const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" "checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" "checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" "checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" @@ -1522,10 +1499,10 @@ dependencies = [ "checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" "checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fixed-hash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72fe7539e2c5692c6989f2f9c0457e42f1e5768f96b85c87d273574670ae459f" +"checksum fixed-hash 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3367952ceb191f4ab95dd5685dc163ac539e36202f9fcfd0cb22f9f9c542fefc" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" +"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" "checksum hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" "checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" @@ -1535,11 +1512,12 @@ dependencies = [ "checksum hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" "checksum impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" "checksum impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8" +"checksum impl-serde 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bbe9ea9b182f0fb1cabbd61f4ff9b7b7b9197955e95a7e4c27de5055eb29ff8" "checksum impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" -"checksum integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea155abb3ba6f382a75f1418988c05fe82959ed9ce727de427f9cfd425b0c903" +"checksum integer-sqrt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f65877bf7d44897a473350b1046277941cee20b263397e90869c50b6e766088b" "checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" +"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum libsecp256k1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "df6edf84fd62aad1c93932b39324eaeda3912c1d26bc18dfaee6293848e49a50" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" @@ -1548,7 +1526,7 @@ dependencies = [ "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -"checksum memory-db 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "828bdf600636e90c56652689f7c3823ae2072104e4b0b5e83ea984f592f12ab9" +"checksum memory-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "881736a0f68a6fae1b596bb066c5bd16d7b3ed645a4dd8ffaefd02f585abaf71" "checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" "checksum merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0942b357c1b4d0dc43ba724674ec89c3218e6ca2b3e8269e7cb53bcecd2f6e" "checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" @@ -1559,7 +1537,7 @@ dependencies = [ "checksum once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507" -"checksum parity-scale-codec-derive 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42af752f59119656fa3cb31e8852ed24e895b968c0bdb41847da7f0cea6d155f" +"checksum parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34e513ff3e406f3ede6796dcdc83d0b32ffb86668cea1ccf7363118abeb00476" "checksum parity-util-mem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8174d85e62c4d615fddd1ef67966bdc5757528891d0742f15b131ad04667b3f9" "checksum parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" @@ -1570,12 +1548,10 @@ dependencies = [ "checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" "checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0253db64c26d8b4e7896dd2063b516d2a1b9e0a5da26b5b78335f236d1e9522" +"checksum primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e4336f4f5d5524fa60bcbd6fe626f9223d8142a50e7053e979acdf0da41ab975" "checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" "checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" -"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" @@ -1596,15 +1572,15 @@ dependencies = [ "checksum regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" "checksum regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -"checksum rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8" +"checksum rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4b39bd9b0b087684013a792c59e3e07a46a01d2322518d8a1104641a0b1be0" -"checksum serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "ca13fc1a832f793322228923fbb3aba9f3f44444898f835d31ad1b74fa0a2bf8" +"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" "checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" "checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" "checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" @@ -1612,21 +1588,19 @@ dependencies = [ "checksum substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3be511be555a3633e71739a79e4ddff6a6aaa6579fa6114182a51d72c3eb93c5" "checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" "checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" -"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92" +"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" "checksum tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c5676413eaeb1ea35300a0224416f57abc3bd251657e0fafc12c47ff98c060" "checksum tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" -"checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf" -"checksum trie-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "191fda5d0106f3ed35a8c6875428b213e15c516e48129cc263dd7ad16e9a665f" +"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +"checksum trie-db 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d747ae5b6f078df7e46477fcc7df66df9eb4f27a031cf4a7c890a8dd03d8e6" "checksum trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0b779f7c1c8fe9276365d9d5be5c4b5adeacf545117bb3f64c974305789c5c0b" "checksum twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" "checksum wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bf617d864d25af3587aa745529f7aaa541066c876d57e050c0d0c85c61c92aff" "checksum wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea78c597064ba73596099281e2f4cfc019075122a65cdda3205af94f0b264d93" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" diff --git a/primitives/phragmen/fuzzer/src/reduce.rs b/primitives/phragmen/fuzzer/src/reduce.rs index 3c422f8935834..8f0a0cb1978c3 100644 --- a/primitives/phragmen/fuzzer/src/reduce.rs +++ b/primitives/phragmen/fuzzer/src/reduce.rs @@ -24,7 +24,7 @@ //! `cargo hfuzz run-debug reduce hfuzz_workspace/reduce/*.fuzz`. use honggfuzz::fuzz; -use sp_phragmen::{StakedAssignment, ExtendedBalance, build_support_map, reduce::reduce}; +use sp_phragmen::{StakedAssignment, ExtendedBalance, build_support_map, reduce}; use rand::{self, Rng}; type Balance = u128; @@ -42,7 +42,8 @@ fn main() { 10, 6, ); - reduce_and_compare(&assignments, &winners); + reduce_and_compare(&assignments, &winners); + dbg!("Spme"); }); } } @@ -111,7 +112,6 @@ fn reduce_and_compare( ) { let mut altered_assignment = assignment.clone(); let num_changed = reduce(&mut altered_assignment); - // TODO: give a report of how many edges were removed on average. assert_assignments_equal( winners, &assignment, diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 4b30cc2935caf..77a8a6c41c9de 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -33,7 +33,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::{prelude::*, collections::btree_map::BTreeMap}; +use sp_std::{prelude::*, collections::btree_map::BTreeMap, fmt::Debug}; use sp_runtime::{helpers_128bit::multiply_by_rational, Perbill, Rational128, RuntimeDebug}; use sp_runtime::traits::{Zero, Convert, Member, SimpleArithmetic, Saturating, Bounded}; @@ -51,6 +51,12 @@ pub use reduce::reduce; // re-export the compact macro pub use sp_phragmen_compact::generate_compact_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 {} + +impl IdentifierT for T {} + /// A type in which performing operations on balances and stakes of candidates and voters are safe. /// /// This module's functions expect a `Convert` type to convert all balances to u64. Hence, u128 is diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index f60dd60f09083..c1993ef439d42 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -1,23 +1,49 @@ -use sp_std::{cell::RefCell, rc::Rc, prelude::*}; +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! (very) Basic implementation of a graph node used in the reduce algorithm. + use sp_runtime::RuntimeDebug; +use sp_std::cell::RefCell; +use sp_std::rc::Rc; +/// The role that a node can accept. #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, RuntimeDebug)] pub(crate) enum NodeRole { + /// A voter. This is synonym to a nominator in a staking context. Voter, + /// A target. This is synonym to a candidate/validator in a staking context. Target, } pub(crate) type RefCellOf = Rc>; pub(crate) type NodeRef = RefCellOf>; +/// Identifier of a node. This is particularly handy to have a proper `PartialEq` implementation. +/// Otherwise, self votes wouldn't have been indistinguishable. #[derive(PartialOrd, Ord, Clone)] pub(crate) struct NodeId { - /// Assumed to be unique. + /// An account-like identifier representing the node. pub who: A, + /// The role of the node. pub role: NodeRole, } impl NodeId { + /// Create a new [`NodeId`]. pub fn from(who: A, role: NodeRole) -> Self { Self { who, role } } @@ -29,6 +55,8 @@ impl PartialEq for NodeId { } } +impl Eq for NodeId {} + #[cfg(feature = "std")] impl sp_std::fmt::Debug for NodeId { fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { @@ -36,11 +64,12 @@ impl sp_std::fmt::Debug for NodeId { } } -impl Eq for NodeId {} - +/// A one-way graph note. This can only store a pointer to its parent. #[derive(Clone)] pub(crate) struct Node { + /// The identifier of the note. pub(crate) id: NodeId, + /// The parent pointer. pub(crate) parent: Option>, } @@ -60,10 +89,12 @@ impl sp_std::fmt::Debug for Node { } impl Node { + /// Create a new [`Node`] pub fn new(id: NodeId) -> Node { Self { id, parent: None } } + /// Returns true if `other` is the parent of `who`. pub fn is_parent_of(who: &NodeRef, other: &NodeRef) -> bool { if who.borrow().parent.is_none() { return false; @@ -71,14 +102,20 @@ impl Node { who.borrow().parent.as_ref() == Some(other) } + /// Removes the parent of `who`. pub fn remove_parent(who: &NodeRef) { who.borrow_mut().parent = None; } - pub fn set_parent_of(target: &NodeRef, parent: &NodeRef) { - target.borrow_mut().parent = Some(parent.clone()); + /// Sets `who`'s parent to be `parent`. + pub fn set_parent_of(who: &NodeRef, parent: &NodeRef) { + who.borrow_mut().parent = Some(parent.clone()); } + /// Finds the root of `start`. It return a tuple of `(root, root_vec)` where `root_vec` is the + /// vector of Nodes leading to the root. Hence the first element is the start itself and the + /// last one is the root. As convenient, the root itself is also returned as the first element + /// of the tuple. pub fn root(start: &NodeRef) -> (NodeRef, Vec>) { let mut parent_path: Vec> = Vec::new(); let initial = start.clone(); @@ -94,6 +131,8 @@ impl Node { (current, parent_path) } + /// Consumes self and wraps it in a `Rc>`. This type can be used as the pointer type + /// to a parent node. pub fn into_ref(self) -> NodeRef { Rc::from(RefCell::from(self)) } diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index 1b543b2d30d20..e72fc83a358a6 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -53,13 +53,12 @@ use sp_std::{ prelude::*, collections::{btree_map::{Entry::*, BTreeMap}}, }; - +use sp_runtime::traits::{Zero, Bounded}; use crate::node::{Node, NodeRef, NodeRole, NodeId}; +use crate::{ExtendedBalance, StakedAssignment, IdentifierT}; -use sp_runtime::traits::{Zero, Bounded}; -use crate::{ExtendedBalance, StakedAssignment}; -/// Map type used for caching. Can be easily swapped with HashMap. +/// Map type used for reduce_4. Can be easily swapped with HashMap. type Map = BTreeMap<(A, A), A>; /// Returns all combinations of size two in the collection `input` with no repetition. @@ -78,7 +77,7 @@ fn combinations_2(input: &[T]) -> Vec<(T, T)> { comb } -/// Returns the count of trailing common elements in a slice. +/// Returns the count of trailing common elements in two slices. pub(crate) fn trailing_common(t1: &[T], t2: &[T]) -> usize { let mut t1_pointer = t1.len() - 1; let mut t2_pointer = t2.len() - 1; @@ -96,25 +95,63 @@ pub(crate) fn trailing_common(t1: &[T], t2: &[T]) -> usize { common } +/// Merges two parent roots as described by the reduce algorithm. +fn merge( + voter_root_path: Vec>, + target_root_path: Vec> +) { + if voter_root_path.len() <= target_root_path.len() { + // iterate from last to beginning, skipping the first one. This asserts that + // indexing is always correct. + voter_root_path + .iter() + .take(voter_root_path.len() - 1) // take all except for last. + .enumerate() + .map(|(i, n)| (n, voter_root_path[i+1].clone())) + .for_each(|(voter, next)| { + Node::set_parent_of(&next, &voter) + }); + Node::set_parent_of(&voter_root_path[0], &target_root_path[0]); + } else { + target_root_path + .iter() + .take(target_root_path.len() - 1) // take all except for last. + .enumerate() + .map(|(i, n)| (n, target_root_path[i+1].clone())) + .for_each(|(target, next)| { + Node::set_parent_of(&next, &target) + }); + Node::set_parent_of(&target_root_path[0], &voter_root_path[0]); + } +} + /// Reduce only redundant edges with cycle length of 4. /// +/// Returns the number of edges removed. +/// +/// It is strictly assumed that the `who` attribute of all provided assignments are unique. The +/// result will most likely be corrupt otherwise. +/// /// O(|E_w| ⋅ k). -fn reduce_4( - assignments: &mut Vec>, +fn reduce_4( + assignments: &mut Vec>, ) -> u32 { - let mut combination_map: Map = Map::new(); + let mut combination_map: Map = Map::new(); let mut num_changed: u32 = Zero::zero(); - // NOTE: we have to use the old fashioned style loops here with manual indexing. Borrowing - // assignments will not work since then there is NO way to mutate it inside. + // we have to use the old fashioned loops here with manual indexing. Borrowing assignments will + // not work since then there is NO way to mutate it inside. for assignment_index in 0..assignments.len() { let who = assignments[assignment_index].who.clone(); // all combinations for this particular voter - let candidate_combinations = combinations_2( - &assignments[assignment_index].distribution.iter().map(|(t, _p)| t.clone()).collect::>(), - ); + let distribution_ids = &assignments[assignment_index] + .distribution + .iter() + .map(|(t, _p)| t.clone()) + .collect::>(); + let candidate_combinations = combinations_2(distribution_ids); for (v1, v2) in candidate_combinations { match combination_map.entry((v1.clone(), v2.clone())) { @@ -125,7 +162,10 @@ fn reduce_4( let other_who = entry.get_mut(); // double check if who is still voting for this pair. If not, it means that this - // pair is no longer valid and must have been removed in previous rounds. + // pair is no longer valid and must have been removed in previous rounds. The + // reason for this is subtle; candidate_combinations is created once while the + // inner loop might remove some edges. Note that if count() > 2, the we have + // duplicates. if assignments[assignment_index].distribution .iter() .filter(|(t, _)| *t == v1 || *t == v2) @@ -137,29 +177,34 @@ fn reduce_4( // check if other_who voted for the same pair v1, v2. let maybe_other_assignments = assignments.iter().find(|a| a.who == *other_who); if maybe_other_assignments.is_none() { - // This combination is not a cycle. continue; } - let other_assignment = maybe_other_assignments.expect("value is checked to be 'Some'"); + let other_assignment = maybe_other_assignments + .expect("value is checked to be 'Some'"); // Collect potential cycle votes - let mut other_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); - other_assignment.distribution.iter().for_each(|(t, w)| { - if *t == v1 || *t == v2 { other_cycle_votes.push((t.clone(), *w)); } - }); + let mut other_cycle_votes = other_assignment.distribution + .iter() + .filter_map(|(t, w)| { + if *t == v1 || *t == v2 { Some((t.clone(), *w)) } + else { None } + }) + .collect::>(); - // This is not a cycle. Replace and continue. let other_votes_count = other_cycle_votes.len(); - // TODO Duplicates will fuck us up here. + + // If the length is more than 2, then we have identified duplicates. For now, we + // just skip. Later on we can early exit and stop processing this data since it + // is corrupt anyhow. debug_assert!(other_votes_count <= 2); if other_votes_count < 2 { - // Not a cycle. Replace and move on. + // This is not a cycle. Replace and continue. *other_who = who.clone(); continue; - } else { + } else if other_votes_count == 2 { // This is a cycle. - let mut who_cycle_votes: Vec<(AccountId, ExtendedBalance)> = Vec::with_capacity(2); + let mut who_cycle_votes: Vec<(A, ExtendedBalance)> = Vec::with_capacity(2); assignments[assignment_index].distribution.iter().for_each(|(t, w)| { if *t == v1 || *t == v2 { who_cycle_votes.push((t.clone(), *w)); } }); @@ -171,9 +216,6 @@ fn reduce_4( other_cycle_votes.swap(0, 1); } - debug_assert_eq!(who_cycle_votes[0].0, other_cycle_votes[0].0); - debug_assert_eq!(who_cycle_votes[1].0, other_cycle_votes[1].0); - // Find min let mut min_value: ExtendedBalance = Bounded::max_value(); let mut min_index: usize = 0; @@ -184,7 +226,7 @@ fn reduce_4( .map(|(index, (t, w))| { if *w <= min_value { min_value = *w; min_index = index; } (t.clone(), *w) - }).collect::>(); + }).collect::>(); // min was in the first part of the chained iters let mut increase_indices: Vec = Vec::new(); @@ -214,6 +256,9 @@ fn reduce_4( let mut remove_indices: Vec = Vec::with_capacity(1); increase_indices.into_iter().for_each(|i| { let voter = if i < 2 { who.clone() } else { other_who.clone() }; + // Note: so this is pretty ambiguous. We should only look for one + // assignment that meets this criteria and if we find multiple then that + // is a corrupt input. Same goes for the next block. assignments.iter_mut().filter(|a| a.who == voter).for_each(|ass| { ass.distribution .iter_mut() @@ -222,24 +267,24 @@ fn reduce_4( let next_value = ass.distribution[idx].1.saturating_add(min_value); ass.distribution[idx].1 = next_value; }); - }); }); - decrease_indices.into_iter().for_each(|i| { - let voter = if i < 2 { who.clone() } else { other_who.clone() }; - assignments.iter_mut().filter(|a| a.who == voter).for_each(|ass| { - ass.distribution - .iter_mut() - .position(|(t, _)| *t == cycle[i].0) - .map(|idx| { - let next_value = ass.distribution[idx].1.saturating_sub(min_value); - if next_value.is_zero() { - ass.distribution.remove(idx); - remove_indices.push(i); - num_changed += 1; - } else { - ass.distribution[idx].1 = next_value; - } - }); + }); + decrease_indices.into_iter().for_each(|i| { + let voter = if i < 2 { who.clone() } else { other_who.clone() }; + assignments.iter_mut().filter(|a| a.who == voter).for_each(|ass| { + ass.distribution + .iter_mut() + .position(|(t, _)| *t == cycle[i].0) + .map(|idx| { + let next_value = ass.distribution[idx].1.saturating_sub(min_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + remove_indices.push(i); + num_changed += 1; + } else { + ass.distribution[idx].1 = next_value; + } + }); }); }); @@ -251,7 +296,10 @@ fn reduce_4( (false, true) => { *other_who = who.clone(); }, (true, false) => {}, // nothing, other_who can stay there. (true, true) => { entry.remove(); }, // remove and don't replace - _ => { debug_assert!(false, "this should be unreachable"); }, + (false, false) => { + // Neither of the edges was removed? impossible. + debug_assert!(false, "Duplicate voter (or other corrupt input)."); + }, } } } @@ -262,41 +310,19 @@ fn reduce_4( num_changed } -/// Merges two parent roots as described by the reduce algorithm. -fn merge(voter_root_path: Vec>, target_root_path: Vec>) { - if voter_root_path.len() <= target_root_path.len() { - // iterate from last to beginning, skipping the first one. This asserts that - // indexing is always correct. - voter_root_path - .iter() - .take(voter_root_path.len() - 1) // take all except for last. - .enumerate() - .map(|(i, n)| (n, voter_root_path[i+1].clone())) - .for_each(|(voter, next)| { - Node::set_parent_of(&next, &voter) - }); - Node::set_parent_of(&voter_root_path[0], &target_root_path[0]); - } else { - target_root_path - .iter() - .take(target_root_path.len() - 1) // take all except for last. - .enumerate() - .map(|(i, n)| (n, target_root_path[i+1].clone())) - .for_each(|(target, next)| { - Node::set_parent_of(&next, &target) - }); - Node::set_parent_of(&target_root_path[0], &voter_root_path[0]); - } -} - /// Reduce all redundant edges from the edge weight graph. /// +/// Returns the number of edges removed. +/// +/// It is strictly assumed that the `who` attribute of all provided assignments are unique. The +/// result will most likely be corrupt otherwise. +/// /// O(|Ew| ⋅ m) -fn reduce_all( - assignments: &mut Vec>, +fn reduce_all( + assignments: &mut Vec>, ) -> u32 { let mut num_changed: u32 = Zero::zero(); - let mut tree: BTreeMap, NodeRef> = BTreeMap::new(); + let mut tree: BTreeMap, NodeRef> = BTreeMap::new(); // NOTE: This code can heavily use an index cache. Looking up a pair of (voter, target) in the // assignments happens numerous times and and we can save time. For now it is written as such @@ -369,15 +395,15 @@ fn reduce_all( let cycle = target_root_path.iter().take(target_root_path.len() - common_count + 1).cloned() .chain(voter_root_path.iter().take(voter_root_path.len() - common_count).rev().cloned()) - .collect::>>(); + .collect::>>(); // a cycle's length shall always be multiple of two. debug_assert_eq!(cycle.len() % 2, 0); // find minimum of cycle. let mut min_value: ExtendedBalance = Bounded::max_value(); // The voter and the target pair that create the min edge. - let mut min_target: AccountId = Default::default(); - let mut min_voter: AccountId = Default::default(); + let mut min_target: A = Default::default(); + let mut min_voter: A = Default::default(); // The index of the min in opaque cycle list. let mut min_index = 0usize; // 1 -> next // 0 -> prev @@ -387,7 +413,7 @@ fn reduce_all( let prev_index = |i| { if i > 0 { i - 1 } else { cycle.len() - 1 } }; for i in 0..cycle.len() { if cycle[i].borrow().id.role == NodeRole::Voter { - // NOTE: sadly way too many clones since I don't want to make AccountId: Copy + // NOTE: sadly way too many clones since I don't want to make A: Copy let current = cycle[i].borrow().id.who.clone(); let next = cycle[next_index(i)].borrow().id.who.clone(); let prev = cycle[prev_index(i)].borrow().id.who.clone(); @@ -542,11 +568,13 @@ fn reduce_all( /// /// Returns the number of edges removed. /// +/// It is strictly assumed that the `who` attribute of all provided assignments are unique. The +/// result will most likely be corrupt otherwise. Furthermore, if the _distribution vector_ contains +/// duplicate ids, only the first instance is ever updates. +/// /// O(min{ |Ew| ⋅ k + m3 , |Ew| ⋅ m }) -pub fn reduce< - AccountId: Clone + Eq + Default + Ord + sp_std::fmt::Debug, ->( - assignments: &mut Vec>, +pub fn reduce( + assignments: &mut Vec>, ) -> u32 where { let mut num_changed = reduce_4(assignments); num_changed += reduce_all(assignments); @@ -954,4 +982,82 @@ mod tests { } + + #[test] + #[should_panic] + fn should_deal_with_duplicates_voter() { + let mut assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 10), + (20, 10) + ] + }, + StakedAssignment { + who: 1, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 15) + ], + }, + ]; + + reduce(&mut assignments); + } + + #[test] + fn should_deal_with_duplicates_target() { + let mut assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 15), + (20, 5), + ], + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 15), + (20, 15), + // duplicate + (10, 1), + // duplicate + (20, 1), + ], + }, + ]; + + reduce(&mut assignments); + + assert_eq!( + assignments, + vec![ + StakedAssignment { + who: 1, + distribution: vec![ + (10, 20), + ], + }, + StakedAssignment { + who: 2, + distribution: vec![ + (10, 10), + (20, 20), + // duplicate votes are silently ignored. + (10, 1), + (20, 1), + ], + }, + ], + ) + } } From 686d263829d58a9eb6f57dfd5ee717130775a0ed Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 27 Jan 2020 14:36:51 +0100 Subject: [PATCH 025/106] Still problems with signing --- Cargo.lock | 2 +- frame/staking/Cargo.toml | 2 +- frame/staking/src/lib.rs | 155 ++++-------- frame/staking/src/mock.rs | 323 ++++++++++++++++--------- frame/staking/src/offchain_election.rs | 141 +++++++++++ frame/staking/src/tests.rs | 8 +- frame/system/src/offchain.rs | 12 +- frame/transaction-payment/src/lib.rs | 10 +- 8 files changed, 411 insertions(+), 242 deletions(-) create mode 100644 frame/staking/src/offchain_election.rs diff --git a/Cargo.lock b/Cargo.lock index e3a6bbe75b05a..18d21cf44866d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3960,7 +3960,7 @@ dependencies = [ "pallet-staking-reward-curve 2.0.0", "pallet-timestamp 2.0.0", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "sp-application-crypto 2.0.0", "sp-core 2.0.0", diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index ac51e2aa73efd..a75a8994735a2 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -25,7 +25,7 @@ pallet-balances = { version = "2.0.0", path = "../balances" } 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" } -parking_lot = { version = "0.10.0" } +parking_lot = { version = "0.9.0" } [features] migrate = [] diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index e83ec8bb3d310..82d1df7fbb123 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -252,13 +252,14 @@ mod mock; mod tests; mod migration; mod slashing; +mod offchain_election; pub mod inflation; -use sp_std::{prelude::*, result, cmp::Ordering}; +use sp_std::{prelude::*, result, convert::TryInto}; use codec::{HasCompact, Encode, Decode}; use frame_support::{ - decl_module, decl_event, decl_storage, ensure, decl_error, debug, Parameter, + decl_module, decl_event, decl_storage, ensure, decl_error, debug, weights::SimpleDispatchInfo, traits::{ Currency, OnFreeBalanceZero, LockIdentifier, LockableCurrency, @@ -271,10 +272,10 @@ use sp_runtime::{ curve::PiecewiseLinear, traits::{ Convert, Zero, One, StaticLookup, CheckedSub, Saturating, Bounded, SaturatedConversion, - SimpleArithmetic, EnsureOrigin, Member, + SimpleArithmetic, EnsureOrigin, Verify, }, transaction_validity::{ - TransactionValidity, ValidTransaction, InvalidTransaction, TransactionPriority, + TransactionValidityError, TransactionValidity, ValidTransaction, InvalidTransaction, TransactionPriority, }, }; use sp_staking::{ @@ -285,9 +286,8 @@ use sp_staking::{ use sp_runtime::{Serialize, Deserialize}; use frame_system::{ self as system, ensure_signed, ensure_root, ensure_none, - offchain::{SubmitSignedTransaction, SubmitUnsignedTransaction, SignerOf, SignatureOf, Signer}, + offchain::{SubmitSignedTransaction, SubmitUnsignedTransaction, SignAndSubmitTransaction}, }; -use sp_application_crypto::RuntimeAppPublic; use sp_phragmen::{ ExtendedBalance, Assignment, StakedAssignment, @@ -709,8 +709,11 @@ pub trait Trait: frame_system::Trait { /// A transaction submitter. type SubmitTransaction: + SignAndSubmitTransaction::Call> + SubmitSignedTransaction::Call> + SubmitUnsignedTransaction::Call>; + + type KeyType: sp_runtime::RuntimeAppPublic + sp_runtime::traits::Member + frame_support::Parameter; } /// Mode of era-forcing. @@ -733,7 +736,6 @@ impl Default for Forcing { decl_storage! { trait Store for Module as Staking { - /// The ideal number of staking participants. pub ValidatorCount get(fn validator_count) config(): u32; /// Minimum number of staking participants before emergency conditions are imposed. @@ -949,9 +951,9 @@ decl_module! { /// /// 1. potential storage migration /// 2. sets `ElectionStatus` to `Triggered(now)` where `now` is the block number at which - /// the election window has opened. The offchain worker, if applicable, will execute at the - /// end of the current block. `submit_election_solution` will accept solutions from this block - /// until the end of the era. + /// the election window has opened. The offchain worker, if applicable, will execute at + /// the end of the current block. `submit_election_solution` will accept solutions from + /// this block until the end of the era. fn on_initialize(now: T::BlockNumber) { Self::ensure_storage_upgraded(); if @@ -979,71 +981,17 @@ decl_module! { } } + /// Check if the current block number is the one at which the election window has been set + /// to open. If so, it runs the offchain worker code. fn offchain_worker(now: T::BlockNumber) { + // runs only once. if Self::era_election_status() == ElectionStatus::::Open(now) { - // TODO: well this is outdated, neh? need to get the correct one from session. - let current_elected = Self::current_elected(); - - // Check if current node can sign with any of the keys which correspond to a - // validator. This basically says: proceed if the node is a validator. - if T::SubmitTransaction::can_sign_with(Some(current_elected.clone())) { - if let Some(sp_phragmen::PhragmenResult { - winners, - assignments, - }) = Self::do_phragmen() { - let winners: Vec = winners.into_iter().map(|(w, _)| w).collect(); - // convert into staked. This is needed to be able to reduce. - let mut staked: Vec> = assignments - .into_iter() - .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::slashable_balance_of)) - .collect(); - // reduce the assignments. This will remove some additional edges. - sp_phragmen::reduce(&mut staked); - // compact encode the assignment. - let compact = >::from_staked(staked); - - #[cfg(feature = "signed")] - { - // TODO: maybe we want to send from just one of them? we'll see. - let call: ::Call = Call::submit_election_solution(winners, compact).into(); - let _result = T::SubmitTransaction::submit_signed_from(call, current_elected); - dbg!(_result); - } - #[cfg(not(feature = "signed"))] - { - // TODO: this call is really not needed, we can do it manually instead - // of `can_sign_with`. - // TODO: we could use some error handling here. - let local_keys = T::SubmitTransaction::find_all_local_keys(); - // loop for at least one account in the validators to sign with. - local_keys - .into_iter() - .enumerate() - .find(|(_, (acc, _))| current_elected.contains(&acc)) - .map(|(index, (_, pubkey))| { - let signature_payload = (winners.clone(), compact.clone(), index as u32).encode(); - let signature = ::Call, T::SubmitTransaction>>::sign(pubkey, &signature_payload).unwrap(); - let call: ::Call = Call::submit_election_solution_unsigned( - winners, - compact, - index as u32, - signature, - ).into(); - let _result = T::SubmitTransaction::submit_unsigned(call); - }); - } - } else { - debug::native::warn!( - target: "staking", - "Ran offchain phragmen but none was returned", - ); - } - } else { + let _ = offchain_election::compute_offchain_election::().map_err(|e| debug::native::warn!( target: "staking", - "Have no key to sign this with", - ); - } + "{:?}", e + ) + ); } } @@ -1108,11 +1056,12 @@ decl_module! { origin, winners: Vec, compact_assignments: CompactAssignments, - validator_index: u32, - // already checked. - _signature: SignatureOf::Call, ::Call>>::SignAndSubmit>, + // already used and checked. + _validator_index: u32, + _signature: offchain_election::SignatureOf, ) { ensure_none(origin)?; + Encode::encode(&_signature); Self::check_and_replace_solution(winners, compact_assignments)? } @@ -1539,25 +1488,6 @@ impl Module { era_length >= session_per_era } - /// Compares two sets of phragmen scores based on desirability and returns true if `that` is - /// better `this`. - /// - /// Evaluation is done in a lexicographic manner. - fn is_score_better(this: [ExtendedBalance; 3], that: [ExtendedBalance; 3]) -> bool { - match that - .iter() - .enumerate() - .map(|(i, e)| e.cmp(&this[i])) - .collect::>() - .as_slice() - { - [Ordering::Greater, _, _] => true, - [Ordering::Equal, Ordering::Greater, _] => true, - [Ordering::Equal, Ordering::Equal, Ordering::Less] => true, - _ => false - } - } - // MUTABLES (DANGEROUS) /// Update the ledger for a controller. This will also update the stash lock. The lock will @@ -1699,7 +1629,7 @@ impl Module { // OPTIMIZATION: we can read first the score and then the rest to do less decoding. // if the local score is better in any of the three parameters. ensure!( - Self::is_score_better(score, submitted_score), + offchain_election::is_score_better(score, submitted_score), Error::::PhragmenWeakSubmission, ) } @@ -2352,9 +2282,11 @@ impl ReportOffence // TODO: do we need BoundToRuntimeAppPublic stuff? #[allow(deprecated)] -impl frame_support::unsigned::ValidateUnsigned for Module { +impl frame_support::unsigned::ValidateUnsigned for Module +where + offchain_election::SignatureOf: Verify>, +{ type Call = Call; - fn validate_unsigned(call: &Self::Call) -> TransactionValidity { if let Call::submit_election_solution_unsigned(winners, compact, validator_index, signature) = call { // TODO: since unsigned is only for validators, and it is not increasing the block @@ -2363,26 +2295,25 @@ impl frame_support::unsigned::ValidateUnsigned for Module { // check signature let payload = (winners, compact, validator_index); - let validator_id = Self::current_elected().get(*validator_index as usize).unwrap(); - + let current_validators = T::SessionInterface::validators(); + let validator_id = current_validators.get(*validator_index as usize) + .ok_or(TransactionValidityError::Invalid(InvalidTransaction::Custom(0u8).into()))?; let signature_valid = payload.using_encoded(|encoded_payload| { - // validator_id need to be converted to a key type. - validator_id.verify(&payload, &signature) + signature.verify(encoded_payload, &validator_id) }); - // if !signature_valid { - // return InvalidTransaction::BadProof.into(); - // } - - // Ok(ValidTransaction { - // priority: TransactionPriority::max_value(), - // requires: vec![], - // provides: vec![(current_session, authority_id).encode()], - // longevity: TryInto::::try_into(T::SessionDuration::get() / 2.into()).unwrap_or(64_u64), - // propagate: true, - // }) - TransactionValidity::Ok(ValidTransaction::default()) + if !signature_valid { + return InvalidTransaction::BadProof.into(); + } + + Ok(ValidTransaction { + priority: TransactionPriority::max_value(), + requires: vec![], + provides: vec![], + longevity: TryInto::::try_into(T::ElectionLookahead::get()).unwrap_or(150_u64), + propagate: true, + }) } else { InvalidTransaction::Call.into() } diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index ec33a0edf63ff..c32bf66cebc30 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -16,28 +16,29 @@ //! Test utilities -use std::{collections::HashSet, cell::RefCell}; -use sp_runtime::{Perbill, KeyTypeId}; -use sp_runtime::curve::PiecewiseLinear; -use sp_runtime::traits::{ - IdentityLookup, Convert, OpaqueKeys, OnInitialize, SaturatedConversion, Extrinsic as ExtrinsicT, - Zero, -}; -use sp_runtime::testing::{Header, UintAuthorityId, TestXt}; -use sp_staking::{SessionIndex, offence::{OffenceDetails, OnOffenceHandler}}; -use sp_core::{H256, crypto::key_types}; -use sp_io; +use crate::*; use frame_support::{ - assert_ok, impl_outer_origin, parameter_types, impl_outer_dispatch, impl_outer_event, - StorageLinkedMap, StorageValue, - traits::{Currency, Get, FindAuthor, PredictNextSessionChange}, + assert_ok, impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types, + traits::{Currency, FindAuthor, Get, PredictNextSessionChange}, weights::Weight, + StorageLinkedMap, StorageValue, }; -use frame_system::offchain::{ - TransactionSubmitter, Signer, CreateTransaction, +use frame_system::offchain::{CreateTransaction, Signer, TransactionSubmitter}; +use sp_core::{crypto::key_types, H256}; +use sp_io; +use sp_phragmen::{build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment}; +use sp_runtime::curve::PiecewiseLinear; +use sp_runtime::testing::{Header, TestXt, UintAuthorityId}; +use sp_runtime::traits::{ + Convert, Extrinsic as ExtrinsicT, IdentityLookup, OnInitialize, OpaqueKeys, + SaturatedConversion, Zero, }; -use crate::*; -use sp_phragmen::{StakedAssignment, reduce, evaluate_support, build_support_map, ExtendedBalance}; +use sp_runtime::{KeyTypeId, Perbill}; +use sp_staking::{ + offence::{OffenceDetails, OnOffenceHandler}, + SessionIndex, +}; +use std::{cell::RefCell, collections::HashSet}; /// The AccountId alias in this test module. pub(crate) type AccountId = u64; @@ -48,10 +49,14 @@ pub(crate) type Balance = u64; /// Simple structure that exposes how u64 currency can be represented as... u64. pub struct CurrencyToVoteHandler; impl Convert for CurrencyToVoteHandler { - fn convert(x: u64) -> u64 { x } + fn convert(x: u64) -> u64 { + x + } } impl Convert for CurrencyToVoteHandler { - fn convert(x: u128) -> u64 { x.saturated_into() } + fn convert(x: u128) -> u64 { + x.saturated_into() + } } thread_local! { @@ -75,9 +80,12 @@ impl pallet_session::SessionHandler for TestSessionHandler { validators: &[(AccountId, Ks)], _queued_validators: &[(AccountId, Ks)], ) { - SESSION.with(|x| - *x.borrow_mut() = (validators.iter().map(|x| x.0.clone()).collect(), HashSet::new()) - ); + SESSION.with(|x| { + *x.borrow_mut() = ( + validators.iter().map(|x| x.0.clone()).collect(), + HashSet::new(), + ) + }); } fn on_disabled(validator_index: usize) { @@ -134,7 +142,7 @@ impl Get for SlashDeferDuration { } } -impl_outer_origin!{ +impl_outer_origin! { pub enum Origin for Test where system = frame_system {} } @@ -147,9 +155,9 @@ impl_outer_dispatch! { mod staking { pub use super::super::*; } +use frame_system as system; use pallet_balances as balances; use pallet_session as session; -use frame_system as system; impl_outer_event! { pub enum MetaEvent for Test { @@ -159,7 +167,8 @@ impl_outer_event! { pub struct PeriodicSessionChange

(sp_std::marker::PhantomData

); impl

PredictNextSessionChange for PeriodicSessionChange

- where P: Get, +where + P: Get, { fn predict_next_session_change(now: BlockNumber) -> BlockNumber { let period = P::get(); @@ -172,7 +181,8 @@ impl

PredictNextSessionChange for PeriodicSessionChange

pub struct Author11; impl FindAuthor for Author11 { fn find_author<'a, I>(_digests: I) -> Option - where I: 'a + IntoIterator + where + I: 'a + IntoIterator, { Some(11) } @@ -291,11 +301,13 @@ impl Trait for Test { pub(crate) mod dummy_sr25519 { use super::LOCAL_KEY_ACCOUNT; + use sp_runtime::traits::{IdentifyAccount, Verify, Lazy}; mod app_sr25519 { use sp_application_crypto::{app_crypto, key_types::DUMMY, sr25519}; app_crypto!(sr25519, DUMMY); } + pub type AuthoritySignature = app_sr25519::Signature; pub type AuthorityId = app_sr25519::Public; pub type AuthorityPair = app_sr25519::Pair; @@ -306,6 +318,18 @@ pub(crate) mod dummy_sr25519 { LOCAL_KEY_ACCOUNT.with(|v| *v.borrow()) } } + + impl Verify for AuthoritySignature { + type Signer = AuthorityId; + + fn verify>( + &self, + msg: L, + signer: &::AccountId, + ) -> bool { + LOCAL_KEY_ACCOUNT.with(|v| *v.borrow() == *signer) + } + } } impl CreateTransaction for Test { @@ -316,7 +340,10 @@ impl CreateTransaction for Test { _public: Self::Public, account: AccountId, _index: AccountIndex, - ) -> Option<(::Call, ::SignaturePayload)> { + ) -> Option<( + ::Call, + ::SignaturePayload, + )> { let extra = (); Some((call, (account, extra))) } @@ -339,7 +366,7 @@ pub struct ExtBuilder { num_validators: Option, invulnerables: Vec, has_stakers: bool, - local_key_account: AccountId + local_key_account: AccountId, } impl Default for ExtBuilder { @@ -436,59 +463,72 @@ impl ExtBuilder { } pub fn build(self) -> sp_io::TestExternalities { self.set_associated_constants(); - let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); - let balance_factor = if self.existential_deposit > 0 { - 256 - } else { - 1 - }; + let mut storage = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + let balance_factor = if self.existential_deposit > 0 { 256 } else { 1 }; let num_validators = self.num_validators.unwrap_or(self.validator_count); let validators = (0..num_validators) .map(|x| ((x + 1) * 10 + 1) as u64) .collect::>(); - let _ = pallet_balances::GenesisConfig::{ + let _ = pallet_balances::GenesisConfig:: { balances: vec![ - (1, 10 * balance_factor), - (2, 20 * balance_factor), - (3, 300 * balance_factor), - (4, 400 * balance_factor), - (10, balance_factor), - (11, balance_factor * 1000), - (20, balance_factor), - (21, balance_factor * 2000), - (30, balance_factor), - (31, balance_factor * 2000), - (40, balance_factor), - (41, balance_factor * 2000), - (100, 2000 * balance_factor), - (101, 2000 * balance_factor), - // This allow us to have a total_payout different from 0. - (999, 1_000_000_000_000), + (1, 10 * balance_factor), + (2, 20 * balance_factor), + (3, 300 * balance_factor), + (4, 400 * balance_factor), + (10, balance_factor), + (11, balance_factor * 1000), + (20, balance_factor), + (21, balance_factor * 2000), + (30, balance_factor), + (31, balance_factor * 2000), + (40, balance_factor), + (41, balance_factor * 2000), + (100, 2000 * balance_factor), + (101, 2000 * balance_factor), + // This allow us to have a total_payout different from 0. + (999, 1_000_000_000_000), ], vesting: vec![], - }.assimilate_storage(&mut storage); + } + .assimilate_storage(&mut storage); let stake_21 = if self.fair { 1000 } else { 2000 }; - let stake_31 = if self.validator_pool { balance_factor * 1000 } else { 1 }; + let stake_31 = if self.validator_pool { + balance_factor * 1000 + } else { + 1 + }; let status_41 = if self.validator_pool { StakerStatus::::Validator } else { StakerStatus::::Idle }; let nominated = if self.nominate { vec![11, 21] } else { vec![] }; - let _ = GenesisConfig::{ + let _ = GenesisConfig:: { current_era: 0, stakers: if self.has_stakers { vec![ // (stash, controller, staked_amount, status) - (11, 10, balance_factor * 1000, StakerStatus::::Validator), + ( + 11, + 10, + balance_factor * 1000, + StakerStatus::::Validator, + ), (21, 20, stake_21, StakerStatus::::Validator), (31, 30, stake_31, StakerStatus::::Validator), (41, 40, balance_factor * 1000, status_41), // nominator - (101, 100, balance_factor * 500, StakerStatus::::Nominator(nominated)) + ( + 101, + 100, + balance_factor * 500, + StakerStatus::::Nominator(nominated), + ), ] } else { vec![] @@ -498,18 +538,21 @@ impl ExtBuilder { invulnerables: self.invulnerables, slash_reward_fraction: Perbill::from_percent(10), ..Default::default() - }.assimilate_storage(&mut storage); + } + .assimilate_storage(&mut storage); let _ = pallet_session::GenesisConfig:: { - keys: validators.iter().map(|x| (*x, UintAuthorityId(*x))).collect(), - }.assimilate_storage(&mut storage); + keys: validators + .iter() + .map(|x| (*x, UintAuthorityId(*x))) + .collect(), + } + .assimilate_storage(&mut storage); let mut ext = sp_io::TestExternalities::from(storage); ext.execute_with(|| { let validators = Session::validators(); - SESSION.with(|x| - *x.borrow_mut() = (validators.clone(), HashSet::new()) - ); + SESSION.with(|x| *x.borrow_mut() = (validators.clone(), HashSet::new())); }); ext } @@ -522,7 +565,9 @@ pub type Timestamp = pallet_timestamp::Module; pub type Staking = Module; pub fn check_exposure_all() { - Staking::current_elected().into_iter().for_each(|acc| check_exposure(acc)); + Staking::current_elected() + .into_iter() + .for_each(|acc| check_exposure(acc)); } pub fn check_nominator_all() { @@ -534,8 +579,11 @@ pub fn check_exposure(stash: u64) { assert_is_stash(stash); let expo = Staking::stakers(&stash); assert_eq!( - expo.total as u128, expo.own as u128 + expo.others.iter().map(|e| e.value as u128).sum::(), - "wrong total exposure for {:?}: {:?}", stash, expo, + expo.total as u128, + expo.own as u128 + expo.others.iter().map(|e| e.value as u128).sum::(), + "wrong total exposure for {:?}: {:?}", + stash, + expo, ); } @@ -547,15 +595,20 @@ pub fn check_nominator_exposure(stash: u64) { Staking::current_elected() .iter() .map(|v| Staking::stakers(v)) - .for_each(|e| e.others.iter() - .filter(|i| i.who == stash) - .for_each(|i| sum += i.value) - ); + .for_each(|e| { + e.others + .iter() + .filter(|i| i.who == stash) + .for_each(|i| sum += i.value) + }); let nominator_stake = Staking::slashable_balance_of(&stash); // a nominator cannot over-spend. assert!( nominator_stake >= sum, - "failed: Nominator({}) stake({}) >= sum divided({})", stash, nominator_stake, sum, + "failed: Nominator({}) stake({}) >= sum divided({})", + stash, + nominator_stake, + sum, ); } @@ -567,24 +620,40 @@ pub fn assert_ledger_consistent(stash: u64) { assert_is_stash(stash); let ledger = Staking::ledger(stash - 1).unwrap(); - let real_total: Balance = ledger.unlocking.iter().fold(ledger.active, |a, c| a + c.value); + let real_total: Balance = ledger + .unlocking + .iter() + .fold(ledger.active, |a, c| a + c.value); assert_eq!(real_total, ledger.total); } pub fn bond_validator(stash: u64, ctrl: u64, val: u64) { let _ = Balances::make_free_balance_be(&stash, val); - assert_ok!(Staking::bond(Origin::signed(stash), ctrl, val, RewardDestination::Controller)); - assert_ok!(Staking::validate(Origin::signed(ctrl), ValidatorPrefs::default())); + assert_ok!(Staking::bond( + Origin::signed(stash), + ctrl, + val, + RewardDestination::Controller + )); + assert_ok!(Staking::validate( + Origin::signed(ctrl), + ValidatorPrefs::default() + )); } pub fn bond_nominator(stash: u64, ctrl: u64, val: u64, target: Vec) { let _ = Balances::make_free_balance_be(&stash, val); - assert_ok!(Staking::bond(Origin::signed(stash), ctrl, val, RewardDestination::Controller)); + assert_ok!(Staking::bond( + Origin::signed(stash), + ctrl, + val, + RewardDestination::Controller + )); assert_ok!(Staking::nominate(Origin::signed(ctrl), target)); } pub fn run_to_block(n: BlockNumber) { - for b in System::block_number()+1..=n { + for b in System::block_number() + 1..=n { System::set_block_number(b); Session::on_initialize(b); Staking::on_initialize(b); @@ -620,11 +689,13 @@ pub fn current_total_payout_for_duration(duration: u64) -> u64 { >::slot_stake() * 2, Balances::total_issuance(), duration, - ).0 + ) + .0 } pub fn reward_all_elected() { - let rewards = >::current_elected().iter() + let rewards = >::current_elected() + .iter() .map(|v| (*v, 1)) .collect::>(); @@ -632,11 +703,17 @@ pub fn reward_all_elected() { } pub fn validator_controllers() -> Vec { - Session::validators().into_iter().map(|s| Staking::bonded(&s).expect("no controller for validator")).collect() + Session::validators() + .into_iter() + .map(|s| Staking::bonded(&s).expect("no controller for validator")) + .collect() } pub fn on_offence_in_era( - offenders: &[OffenceDetails>], + offenders: &[OffenceDetails< + AccountId, + pallet_session::historical::IdentificationTuple, + >], slash_fraction: &[Perbill], era: EraIndex, ) { @@ -644,21 +721,28 @@ pub fn on_offence_in_era( for &(bonded_era, start_session) in bonded_eras.iter() { if bonded_era == era { Staking::on_offence(offenders, slash_fraction, start_session); - return + return; } else if bonded_era > era { - break + break; } } if Staking::current_era() == era { - Staking::on_offence(offenders, slash_fraction, Staking::current_era_start_session_index()); + Staking::on_offence( + offenders, + slash_fraction, + Staking::current_era_start_session_index(), + ); } else { panic!("cannot slash in era {}", era); } } pub fn on_offence_now( - offenders: &[OffenceDetails>], + offenders: &[OffenceDetails< + AccountId, + pallet_session::historical::IdentificationTuple, + >], slash_fraction: &[Perbill], ) { let now = Staking::current_era(); @@ -667,31 +751,34 @@ pub fn on_offence_now( // winners will be chosen by simply their unweighted total backing stake. Nominator stake is // distributed evenly. -pub fn horrible_phragmen_with_post_processing(do_reduce: bool) - -> (CompactAssignments, Vec) -{ +pub fn horrible_phragmen_with_post_processing( + do_reduce: bool, +) -> ( + CompactAssignments, + Vec, +) { use std::collections::BTreeMap; let mut backing_stake_of: BTreeMap = BTreeMap::new(); // self stake - >::enumerate().for_each(|(who, _p)| - *backing_stake_of.entry(who).or_insert(Zero::zero()) += - Staking::slashable_balance_of(&who) - ); + >::enumerate().for_each(|(who, _p)| { + *backing_stake_of.entry(who).or_insert(Zero::zero()) += Staking::slashable_balance_of(&who) + }); // add nominator stuff - >::enumerate().for_each(|(who, nomination)| - nomination.targets.iter().for_each(|v| + >::enumerate().for_each(|(who, nomination)| { + nomination.targets.iter().for_each(|v| { *backing_stake_of.entry(*v).or_insert(Zero::zero()) += Staking::slashable_balance_of(&who) - ) - ); + }) + }); // elect winners let mut sorted: Vec = backing_stake_of.keys().cloned().collect(); sorted.sort_by_key(|x| backing_stake_of.get(x).unwrap()); - let winners: Vec = sorted.iter() + let winners: Vec = sorted + .iter() .cloned() .take(Staking::validator_count() as usize) .collect(); @@ -700,13 +787,15 @@ pub fn horrible_phragmen_with_post_processing(do_reduce: bool) let mut assignments: Vec> = Vec::new(); >::enumerate().for_each(|(who, nomination)| { let mut dist: Vec<(AccountId, ExtendedBalance)> = Vec::new(); - nomination.targets.iter().for_each(|v| + nomination.targets.iter().for_each(|v| { if winners.iter().find(|w| *w == v).is_some() { dist.push((*v, ExtendedBalance::zero())); } - ); + }); - if dist.len() == 0 { return; } + if dist.len() == 0 { + return; + } // assign real stakes. just split the stake. let stake = Staking::slashable_balance_of(&who) as ExtendedBalance; @@ -727,37 +816,43 @@ pub fn horrible_phragmen_with_post_processing(do_reduce: bool) last.1 += leftover; } - assignments.push(StakedAssignment { who, distribution: dist }); + assignments.push(StakedAssignment { + who, + distribution: dist, + }); }); // Ensure that this result is worse than seq-phragmen. Otherwise, it should not have been used // for testing. { let (better_compact, better_winners) = do_phragmen_with_post_processing(true); - let better_assignments = better_compact.into_staked::< - _, - _, - CurrencyToVoteHandler, - >(Staking::slashable_balance_of); + let better_assignments = better_compact + .into_staked::<_, _, CurrencyToVoteHandler>(Staking::slashable_balance_of); let support = build_support_map::(&winners, &assignments).0; - let better_support = build_support_map::(&better_winners, &better_assignments).0; + let better_support = + build_support_map::(&better_winners, &better_assignments).0; let score = evaluate_support(&support); let better_score = evaluate_support(&better_support); - assert!(Staking::is_score_better(score, better_score)); + assert!(offchain_election::is_score_better(score, better_score)); } - if do_reduce { reduce(&mut assignments); } + if do_reduce { + reduce(&mut assignments); + } let compact = >::from_staked(assignments); (compact, winners) } -pub fn do_phragmen_with_post_processing(do_reduce: bool) - -> (CompactAssignments, Vec) -{ +pub fn do_phragmen_with_post_processing( + do_reduce: bool, +) -> ( + CompactAssignments, + Vec, +) { // run phragmen on the default stuff. let sp_phragmen::PhragmenResult { winners, @@ -770,7 +865,9 @@ pub fn do_phragmen_with_post_processing(do_reduce: bool) .map(|a| a.into_staked::<_, _, CurrencyToVoteHandler>(Staking::slashable_balance_of)) .collect(); - if do_reduce { reduce(&mut staked); } + if do_reduce { + reduce(&mut staked); + } let compact = >::from_staked(staked); @@ -782,5 +879,5 @@ macro_rules! assert_session_era { ($session:expr, $era:expr) => { assert_eq!(Session::current_index(), $session); assert_eq!(Staking::current_era(), $era); - } + }; } diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs new file mode 100644 index 0000000000000..aca0ad2d0e342 --- /dev/null +++ b/frame/staking/src/offchain_election.rs @@ -0,0 +1,141 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Helpers for offchain worker election. + +use crate::{Call, CompactAssignments, Module, SessionInterface, Trait}; +use codec::Encode; +use frame_system::offchain::{Signer, SubmitSignedTransaction, SubmitUnsignedTransaction}; +use sp_phragmen::{reduce, ExtendedBalance, PhragmenResult, StakedAssignment}; +use sp_std::cmp::Ordering; +use sp_runtime::RuntimeAppPublic; + +pub(crate) type SignatureOf = frame_system::offchain::SignatureOf +< + T, + ::Call, + < + ::SubmitTransaction as SubmitSignedTransaction::Call> + >::SignAndSubmit, +>; +pub(crate) type SignerOf = frame_system::offchain::SignerOf +< + T, + ::Call, + ::SubmitTransaction, +>; + +pub(crate) type PublicOf = frame_system::offchain::PublicOf +< + T, + ::Call, + ::SubmitTransaction, +>; + +#[derive(Debug)] +pub(crate) enum OffchainElectionError { + /// No signing key has been found on the current node that maps to a validators. This node + /// should not run the offchain election code. + NoSigningKey, + /// Phragmen election returned None. This means less candidate that minimum number of needed + /// validators were present. The chain is in trouble and not much that we can do about it. + FailedElection, +} + +/// Compares two sets of phragmen scores based on desirability and returns true if `that` is +/// better `this`. +/// +/// Evaluation is done in a lexicographic manner. +pub(super) fn is_score_better(this: [ExtendedBalance; 3], that: [ExtendedBalance; 3]) -> bool { + match that + .iter() + .enumerate() + .map(|(i, e)| e.cmp(&this[i])) + .collect::>() + .as_slice() + { + [Ordering::Greater, _, _] => true, + [Ordering::Equal, Ordering::Greater, _] => true, + [Ordering::Equal, Ordering::Equal, Ordering::Less] => true, + _ => false, + } +} + +/// The internal logic of the offchain worker of this module. +pub(crate) fn compute_offchain_election() -> Result<(), OffchainElectionError> { + let validators = T::SessionInterface::validators(); + + // Check if current node can sign with any of the keys which correspond to a + // validator. This basically says: proceed if the node is a validator. + if T::SubmitTransaction::can_sign_with(Some(validators.clone())) { + let PhragmenResult { + winners, + assignments, + } = >::do_phragmen().ok_or(OffchainElectionError::FailedElection)?; + + // convert winners into just account ids. + let winners: Vec = winners.into_iter().map(|(w, _)| w).collect(); + + // convert into staked. This is needed to be able to reduce. + let mut staked: Vec> = assignments + .into_iter() + .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(>::slashable_balance_of)) + .collect(); + + // reduce the assignments. This will remove some additional edges. + reduce(&mut staked); + + // compact encode the assignment. + let compact = >::from_staked(staked); + + #[cfg(feature = "signed")] + { + // TODO: maybe we want to send from just one of them? we'll see. + let call: ::Call = Call::submit_election_solution(winners, compact).into(); + let _result = T::SubmitTransaction::submit_signed_from(call, validators); + dbg!(_result); + } + #[cfg(not(feature = "signed"))] + { + // TODO: this call is really not needed, we can do it manually instead + // of `can_sign_with`. + // TODO: we could use some error handling here. + let local_keys = T::SubmitTransaction::find_all_local_keys(); + // loop for at least one account in the validators to sign with. + local_keys + .into_iter() + .enumerate() + .find(|(_, (acc, _))| validators.contains(&acc)) + .map(|(index, (_, pubkey))| { + let signature_payload = + (winners.clone(), compact.clone(), index as u32).encode(); + let signature = >::sign(pubkey, &signature_payload).unwrap(); + let call: ::Call = Call::submit_election_solution_unsigned( + winners, + compact, + index as u32, + signature, + ) + .into(); + let _result = T::SubmitTransaction::submit_unsigned(call); + }); + } + } else { + Err(OffchainElectionError::NoSigningKey)?; + } + + Ok(()) +} diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index d4a86163e0f77..b56761512df4b 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2771,25 +2771,25 @@ mod offchain_phragmen { fn score_comparison_is_lexicographical() { // only better in the fist parameter, worse in the other two ✅ assert_eq!( - Staking::is_score_better([10, 20, 30], [12, 10, 35]), + offchain_election::is_score_better([10, 20, 30], [12, 10, 35]), true, ); // worse in the first, better in the other two ❌ assert_eq!( - Staking::is_score_better([10, 20, 30], [9, 30, 10]), + offchain_election::is_score_better([10, 20, 30], [9, 30, 10]), false, ); // equal in the first, the second one dictates. assert_eq!( - Staking::is_score_better([10, 20, 30], [10, 25, 40]), + offchain_election::is_score_better([10, 20, 30], [10, 25, 40]), true, ); // equal in the first two, the last one dictates. assert_eq!( - Staking::is_score_better([10, 20, 30], [10, 20, 40]), + offchain_election::is_score_better([10, 20, 30], [10, 20, 40]), false, ); } diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index daa79d21be532..623f18c101945 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -30,7 +30,7 @@ use frame_support::debug; /// to submit `SignedTransaction`s` to the pool from off-chain code. pub trait CreateTransaction { /// A `Public` key representing a particular `AccountId`. - type Public: IdentifyAccount + Clone + RuntimeAppPublic; + type Public: IdentifyAccount + Clone; /// A `Signature` generated by the `Signer`. type Signature: Encode + Decode + Clone + Debug + Eq + PartialEq + Default; @@ -125,7 +125,7 @@ pub type SignatureOf = /// you should use. pub trait SignAndSubmitTransaction { /// Unchecked extrinsic type. - type Extrinsic: ExtrinsicT + codec::Encode; + type Extrinsic: ExtrinsicT + Encode; /// A runtime-specific type to produce signed data for the extrinsic. type CreateTransaction: CreateTransaction; @@ -133,7 +133,7 @@ pub trait SignAndSubmitTransaction { /// A type used to sign transactions created using `CreateTransaction`. type Signer: Signer< PublicOf, - >::Signature, + SignatureOf, >; /// Sign given call and submit it to the transaction pool. @@ -306,7 +306,7 @@ impl SignAndSubmitTransaction for TransactionSubmitte T: crate::Trait, C: CreateTransaction, S: Signer<>::Public, >::Signature>, - E: ExtrinsicT + codec::Encode, + E: ExtrinsicT + Encode, { type Extrinsic = E; type CreateTransaction = C; @@ -316,7 +316,7 @@ impl SignAndSubmitTransaction for TransactionSubmitte /// A blanket implementation to use the same submitter for unsigned transactions as well. impl SubmitUnsignedTransaction for TransactionSubmitter where T: crate::Trait, - E: ExtrinsicT + codec::Encode, + E: ExtrinsicT + Encode, { type Extrinsic = E; } @@ -325,7 +325,7 @@ impl SubmitUnsignedTransaction for TransactionSubmitt impl SubmitSignedTransaction for TransactionSubmitter where T: crate::Trait, C: CreateTransaction, - E: ExtrinsicT + codec::Encode, + E: ExtrinsicT + Encode, S: Signer<>::Public, >::Signature>, // Make sure we can unwrap the app crypto key. S: RuntimeAppPublic + AppPublic + Into<::Generic>, diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index 00dbf6bc66b73..ca40fcc4d6c5c 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -104,11 +104,6 @@ impl Module { /// /// All dispatchables must be annotated with weight and will have some fee info. This function /// always returns. - // NOTE: we can actually make it understand `ChargeTransactionPayment`, but would be some hassle - // for sure. We have to make it aware of the index of `ChargeTransactionPayment` in `Extra`. - // Alternatively, we could actually execute the tx's per-dispatch and record the balance of the - // sender before and after the pipeline.. but this is way too much hassle for a very very little - // potential gain in the future. pub fn query_info( unchecked_extrinsic: Extrinsic, len: u32, @@ -117,6 +112,11 @@ impl Module { T: Send + Sync, BalanceOf: Send + Sync, { + // NOTE: we can actually make it understand `ChargeTransactionPayment`, but would be some + // hassle for sure. We have to make it aware of the index of `ChargeTransactionPayment` in + // `Extra`. Alternatively, we could actually execute the tx's per-dispatch and record the + // balance of the sender before and after the pipeline.. but this is way too much hassle for + // a very very little potential gain in the future. let dispatch_info = ::get_dispatch_info(&unchecked_extrinsic); let partial_fee = >::compute_fee(len, dispatch_info, 0u32.into()); From 295cdd7779bc99309da8df698a0adf0021c02e14 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 27 Jan 2020 17:18:02 +0100 Subject: [PATCH 026/106] =?UTF-8?q?We=20officaly=20duct=20taped=20the=20tr?= =?UTF-8?q?ansaction=20submission=20stuff.=20=F0=9F=A4=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frame/staking/src/lib.rs | 42 +++++++++++++----------- frame/staking/src/mock.rs | 1 + frame/staking/src/offchain_election.rs | 45 ++++++++++++++------------ frame/staking/src/tests.rs | 39 ++++++++++++++++++++-- frame/system/src/offchain.rs | 19 ++--------- 5 files changed, 87 insertions(+), 59 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 82d1df7fbb123..424792fea8fdd 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -713,7 +713,9 @@ pub trait Trait: frame_system::Trait { SubmitSignedTransaction::Call> + SubmitUnsignedTransaction::Call>; - type KeyType: sp_runtime::RuntimeAppPublic + sp_runtime::traits::Member + frame_support::Parameter; + type KeyType: + std::convert::From<<<<::SubmitTransaction as frame_system::offchain::SubmitSignedTransaction::Call>>::SignAndSubmit as frame_system::offchain::SignAndSubmitTransaction::Call>>::CreateTransaction as frame_system::offchain::CreateTransaction::SubmitTransaction as frame_system::offchain::SubmitSignedTransaction::Call>>::SignAndSubmit as frame_system::offchain::SignAndSubmitTransaction::Call>>::Extrinsic>>::Public> + + sp_runtime::RuntimeAppPublic + sp_runtime::traits::Member + frame_support::Parameter; } /// Mode of era-forcing. @@ -1058,7 +1060,7 @@ decl_module! { compact_assignments: CompactAssignments, // already used and checked. _validator_index: u32, - _signature: offchain_election::SignatureOf, + _signature: ::Signature, ) { ensure_none(origin)?; Encode::encode(&_signature); @@ -2282,30 +2284,32 @@ impl ReportOffence // TODO: do we need BoundToRuntimeAppPublic stuff? #[allow(deprecated)] -impl frame_support::unsigned::ValidateUnsigned for Module -where - offchain_election::SignatureOf: Verify>, -{ +impl frame_support::unsigned::ValidateUnsigned for Module { type Call = Call; fn validate_unsigned(call: &Self::Call) -> TransactionValidity { - if let Call::submit_election_solution_unsigned(winners, compact, validator_index, signature) = call { + if let Call::submit_election_solution_unsigned( + winners, + compact, + validator_index, + signature, + ) = call { // TODO: since unsigned is only for validators, and it is not increasing the block // weight and fee etc. maybe we should only accept an unsigned solution when we don't // have ANY solutions. Otherwise each block will get a LOT of these. // check signature - let payload = (winners, compact, validator_index); - let current_validators = T::SessionInterface::validators(); - let validator_id = current_validators.get(*validator_index as usize) - .ok_or(TransactionValidityError::Invalid(InvalidTransaction::Custom(0u8).into()))?; - - let signature_valid = payload.using_encoded(|encoded_payload| { - signature.verify(encoded_payload, &validator_id) - }); - - if !signature_valid { - return InvalidTransaction::BadProof.into(); - } + // let payload = (winners, compact, validator_index); + // let current_validators = T::SessionInterface::validators(); + // let validator_id = current_validators.get(*validator_index as usize) + // .ok_or(TransactionValidityError::Invalid(InvalidTransaction::Custom(0u8).into()))?; + + // let signature_valid = payload.using_encoded(|encoded_payload| { + // signature.verify(encoded_payload, &validator_id) + // }); + + // if !signature_valid { + // return InvalidTransaction::BadProof.into(); + // } Ok(ValidTransaction { priority: TransactionPriority::max_value(), diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index c32bf66cebc30..6252bd4e81bfd 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -297,6 +297,7 @@ impl Trait for Test { type ElectionLookahead = ElectionLookahead; type Call = Call; type SubmitTransaction = SubmitTransaction; + type KeyType = dummy_sr25519::AuthorityId; } pub(crate) mod dummy_sr25519 { diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index aca0ad2d0e342..103cdd85eca48 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -18,32 +18,30 @@ use crate::{Call, CompactAssignments, Module, SessionInterface, Trait}; use codec::Encode; -use frame_system::offchain::{Signer, SubmitSignedTransaction, SubmitUnsignedTransaction}; +use frame_system::offchain::{ + Signer, SubmitSignedTransaction, SubmitUnsignedTransaction, SignAndSubmitTransaction, + CreateTransaction, +}; use sp_phragmen::{reduce, ExtendedBalance, PhragmenResult, StakedAssignment}; use sp_std::cmp::Ordering; use sp_runtime::RuntimeAppPublic; -pub(crate) type SignatureOf = frame_system::offchain::SignatureOf +type SignAndSubmitOf = < - T, - ::Call, - < - ::SubmitTransaction as SubmitSignedTransaction::Call> - >::SignAndSubmit, ->; -pub(crate) type SignerOf = frame_system::offchain::SignerOf -< - T, - ::Call, - ::SubmitTransaction, ->; + ::SubmitTransaction + as + SubmitSignedTransaction::Call> +>::SignAndSubmit; -pub(crate) type PublicOf = frame_system::offchain::PublicOf +pub(crate) type PublicOf = < - T, - ::Call, - ::SubmitTransaction, ->; + as SignAndSubmitTransaction::Call>>::CreateTransaction + as + CreateTransaction< + T, + as SignAndSubmitTransaction::Call>>::Extrinsic + > +>::Public; #[derive(Debug)] pub(crate) enum OffchainElectionError { @@ -75,7 +73,10 @@ pub(super) fn is_score_better(this: [ExtendedBalance; 3], that: [ExtendedBalance } /// The internal logic of the offchain worker of this module. -pub(crate) fn compute_offchain_election() -> Result<(), OffchainElectionError> { +pub(crate) fn compute_offchain_election() -> Result<(), OffchainElectionError> +where + ::KeyType: From> +{ let validators = T::SessionInterface::validators(); // Check if current node can sign with any of the keys which correspond to a @@ -122,7 +123,8 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti .map(|(index, (_, pubkey))| { let signature_payload = (winners.clone(), compact.clone(), index as u32).encode(); - let signature = >::sign(pubkey, &signature_payload).unwrap(); + let pubkey: T::KeyType = pubkey.into(); + let signature = pubkey.sign(&signature_payload).unwrap(); let call: ::Call = Call::submit_election_solution_unsigned( winners, compact, @@ -131,6 +133,7 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti ) .into(); let _result = T::SubmitTransaction::submit_unsigned(call); + dbg!(_result); }); } } else { diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index b56761512df4b..52409dbfc2840 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2989,7 +2989,8 @@ mod offchain_phragmen { } #[test] - fn offchain_worker_runs_when_window_open() { + #[cfg(feature = "signed")] + fn offchain_worker_runs_when_window_open_signed() { // at the end of the first finalized block with ElectionStatus::open(_), it should execute. let mut ext = ExtBuilder::default() .offchain_phragmen_ext() @@ -3016,12 +3017,44 @@ mod offchain_phragmen { mock::Call::Staking(crate::Call::submit_election_solution(_, _)) => {}, _ => panic!("wrong call submitted"), }; + + // TODO: dispatch the call + }) + } + + #[test] + #[cfg(not(feature = "signed"))] + fn offchain_worker_runs_when_window_open_unsigned() { + // at the end of the first finalized block with ElectionStatus::open(_), it should execute. + let mut ext = ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .build(); + let state = offchainify(&mut ext); + ext.execute_with(||{ + run_to_block(12); + + // local key 11 is in the elected set. + assert_eq_uvec!(Staking::current_elected(), vec![11, 21, 31]); + assert_eq!(state.read().transactions.len(), 0); + Staking::offchain_worker(12); + assert_eq!(state.read().transactions.len(), 1); + + let encoded = state.read().transactions[0].clone(); + // WTF just happened? I changed Extrinsic::decode() to this and now it works? + let extrinsic: Extrinsic = Decode::decode(&mut &*encoded).unwrap(); + + let call = extrinsic.1; + match call { + mock::Call::Staking(crate::Call::submit_election_solution_unsigned(_, _, _, _)) => {}, + _ => panic!("wrong call submitted"), + }; }) } #[test] - fn offchain_submits_unsigned_transaction_if_validator() { - unimplemented!(); + fn validate_unsigned_works() { + unimplemented!() } #[test] diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 623f18c101945..51e3cc1a59421 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -103,20 +103,6 @@ pub type PublicOf = CreateTransaction>::Extrinsic> >::Public; -pub type SignerOf = -< - >::SignAndSubmit - as - SignAndSubmitTransaction ->::Signer; - -pub type SignatureOf = -< - >::CreateTransaction - as - CreateTransaction>::Extrinsic> ->::Signature; - /// A trait to sign and submit transactions in off-chain calls. /// /// NOTE: Most likely you should not implement this trait yourself. @@ -133,7 +119,7 @@ pub trait SignAndSubmitTransaction { /// A type used to sign transactions created using `CreateTransaction`. type Signer: Signer< PublicOf, - SignatureOf, + >::Signature, >; /// Sign given call and submit it to the transaction pool. @@ -179,7 +165,8 @@ pub trait SubmitUnsignedTransaction { /// and `Err` if transaction was rejected from the pool. fn submit_unsigned(call: impl Into) -> Result<(), ()> { let xt = Self::Extrinsic::new(call.into(), None).ok_or(())?; - sp_io::offchain::submit_transaction(xt.encode()) + let encoded_xt = xt.encode(); + sp_io::offchain::submit_transaction(encoded_xt) } } From 57b2b4ecf9a1ad749a48ead861b81fb7dc3f251c Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 28 Jan 2020 14:02:46 +0100 Subject: [PATCH 027/106] Deadlock with keys again --- bin/node/runtime/src/lib.rs | 10 +++- frame/session/src/lib.rs | 3 +- frame/staking/src/lib.rs | 71 +++++++++++++++++--------- frame/staking/src/mock.rs | 46 ++++++++++++----- frame/staking/src/offchain_election.rs | 22 ++++---- frame/staking/src/tests.rs | 36 +++++++++---- primitives/phragmen/src/node.rs | 2 +- primitives/phragmen/src/reduce.rs | 8 +-- primitives/runtime/src/traits.rs | 2 +- 9 files changed, 134 insertions(+), 66 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index d47362bc7f31f..48750aa599ccc 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -269,8 +269,11 @@ parameter_types! { pub const BondingDuration: pallet_staking::EraIndex = 24 * 28; pub const SlashDeferDuration: pallet_staking::EraIndex = 24 * 7; // 1/4 the bonding duration. pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; + pub const ElectionLookahead: BlockNumber = 150; } +struct StakingTransactionSubmitter; + impl pallet_staking::Trait for Runtime { type Currency = Balances; type Time = Timestamp; @@ -286,6 +289,11 @@ impl pallet_staking::Trait for Runtime { type SlashCancelOrigin = pallet_collective::EnsureProportionAtLeast<_3, _4, AccountId, CouncilCollective>; type SessionInterface = Self; type RewardCurve = RewardCurve; + type NextSessionChange = Babe; + type ElectionLookahead = ElectionLookahead; + type Call = Call; + type SubmitTransaction = TransactionSubmitterOf; + type KeyType = pallet_babe::AuthorityId; } parameter_types! { @@ -455,9 +463,9 @@ parameter_types! { } impl pallet_im_online::Trait for Runtime { - type AuthorityId = ImOnlineId; type Call = Call; type Event = Event; + type AuthorityId = ImOnlineId; type SubmitTransaction = TransactionSubmitterOf; type ReportUnresponsiveness = Offences; type SessionDuration = SessionDuration; diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index 098b5330779ef..37a23bae610ad 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -413,7 +413,6 @@ decl_storage! { >::load_keys(&who).is_none(), "genesis config contained duplicate validator {:?}", who, ); - >::do_set_keys(&who, keys) .expect("genesis config must not contain duplicates; qed"); } @@ -671,7 +670,7 @@ impl Module { } } - fn load_keys(v: &T::ValidatorId) -> Option { + pub fn load_keys(v: &T::ValidatorId) -> Option { >::get(DEDUP_KEY_PREFIX, v) } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 424792fea8fdd..b1cba105e1057 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -256,7 +256,7 @@ mod offchain_election; pub mod inflation; -use sp_std::{prelude::*, result, convert::TryInto}; +use sp_std::{prelude::*, result, convert::{TryInto, From}}; use codec::{HasCompact, Encode, Decode}; use frame_support::{ decl_module, decl_event, decl_storage, ensure, decl_error, debug, @@ -268,11 +268,11 @@ use frame_support::{ }; use pallet_session::historical; use sp_runtime::{ - Perbill, RuntimeDebug, + Perbill, RuntimeDebug, RuntimeAppPublic, curve::PiecewiseLinear, traits::{ Convert, Zero, One, StaticLookup, CheckedSub, Saturating, Bounded, SaturatedConversion, - SimpleArithmetic, EnsureOrigin, Verify, + SimpleArithmetic, EnsureOrigin }, transaction_validity::{ TransactionValidityError, TransactionValidity, ValidTransaction, InvalidTransaction, TransactionPriority, @@ -617,6 +617,8 @@ pub trait SessionInterface: frame_system::Trait { fn disable_validator(validator: &AccountId) -> Result; /// Get the validators from session. fn validators() -> Vec; + /// Get the validators and their corresponding keys from session. + fn keys() -> Vec<(AccountId, Option)>; /// Prune historical session tries up to but not including the given index. fn prune_historical_up_to(up_to: SessionIndex); /// Current session index. @@ -631,7 +633,7 @@ impl SessionInterface<::AccountId> for T whe >, T::SessionHandler: pallet_session::SessionHandler<::AccountId>, T::SessionManager: pallet_session::SessionManager<::AccountId>, - T::ValidatorIdOf: Convert<::AccountId, Option<::AccountId>> + T::ValidatorIdOf: Convert<::AccountId, Option<::AccountId>>, { fn disable_validator(validator: &::AccountId) -> Result { >::disable(validator) @@ -641,6 +643,25 @@ impl SessionInterface<::AccountId> for T whe >::validators() } + fn keys() + -> Vec<(::AccountId, Option)> + { + use sp_runtime::traits::OpaqueKeys; + // dbg!(::Keys::key_ids()); + // dbg!(::ID); + Self::validators().iter().map(|v| { + // let opkeys = >::load_keys(&v).unwrap(); + // println!("Loaded stuff {:?} {:?}", + // opkeys.get::(::ID), + // v + // ); + let maybe_key = >::load_keys(&v) + .and_then(|opk| opk.get(KeyT::ID)); + (v.clone(), maybe_key) + }) + .collect::::AccountId, Option)>>() + } + fn prune_historical_up_to(up_to: SessionIndex) { >::prune_up_to(up_to); } @@ -713,9 +734,8 @@ pub trait Trait: frame_system::Trait { SubmitSignedTransaction::Call> + SubmitUnsignedTransaction::Call>; - type KeyType: - std::convert::From<<<<::SubmitTransaction as frame_system::offchain::SubmitSignedTransaction::Call>>::SignAndSubmit as frame_system::offchain::SignAndSubmitTransaction::Call>>::CreateTransaction as frame_system::offchain::CreateTransaction::SubmitTransaction as frame_system::offchain::SubmitSignedTransaction::Call>>::SignAndSubmit as frame_system::offchain::SignAndSubmitTransaction::Call>>::Extrinsic>>::Public> + - sp_runtime::RuntimeAppPublic + sp_runtime::traits::Member + frame_support::Parameter; + type KeyType: From> + + RuntimeAppPublic + sp_runtime::traits::Member + frame_support::Parameter; } /// Mode of era-forcing. @@ -988,12 +1008,12 @@ decl_module! { fn offchain_worker(now: T::BlockNumber) { // runs only once. if Self::era_election_status() == ElectionStatus::::Open(now) { - let _ = offchain_election::compute_offchain_election::().map_err(|e| + offchain_election::compute_offchain_election::().map_err(|e| { debug::native::warn!( target: "staking", "{:?}", e - ) - ); + ); + }); } } @@ -1060,7 +1080,7 @@ decl_module! { compact_assignments: CompactAssignments, // already used and checked. _validator_index: u32, - _signature: ::Signature, + _signature: ::Signature, ) { ensure_none(origin)?; Encode::encode(&_signature); @@ -2298,18 +2318,23 @@ impl frame_support::unsigned::ValidateUnsigned for Module { // have ANY solutions. Otherwise each block will get a LOT of these. // check signature - // let payload = (winners, compact, validator_index); - // let current_validators = T::SessionInterface::validators(); - // let validator_id = current_validators.get(*validator_index as usize) - // .ok_or(TransactionValidityError::Invalid(InvalidTransaction::Custom(0u8).into()))?; - - // let signature_valid = payload.using_encoded(|encoded_payload| { - // signature.verify(encoded_payload, &validator_id) - // }); - - // if !signature_valid { - // return InvalidTransaction::BadProof.into(); - // } + let payload = (winners, compact, validator_index); + let current_validators = T::SessionInterface::keys::(); + let (validator_id, validator_key) = current_validators.get(*validator_index as usize) + .ok_or(TransactionValidityError::Invalid(InvalidTransaction::Custom(0u8).into()))?; + + // dbg!(&validator_id, &validator_key); + // unwrap the key if it exists. + let validator_key = validator_key.as_ref() + .ok_or(TransactionValidityError::Invalid(InvalidTransaction::Custom(1u8).into()))?; + + let signature_valid = payload.using_encoded(|encoded_payload| { + validator_key.verify(&encoded_payload, &signature) + }); + + if !signature_valid { + return InvalidTransaction::BadProof.into(); + } Ok(ValidTransaction { priority: TransactionPriority::max_value(), diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 6252bd4e81bfd..2b16983b1ec29 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -71,7 +71,8 @@ thread_local! { pub struct TestSessionHandler; impl pallet_session::SessionHandler for TestSessionHandler { - const KEY_TYPE_IDS: &'static [KeyTypeId] = &[key_types::DUMMY]; + // EVEN if no tests break, I must have broken something here... TODO + const KEY_TYPE_IDS: &'static [KeyTypeId] = &[dummy_sr25519::AuthorityId::ID]; fn on_genesis_session(_validators: &[(AccountId, Ks)]) {} @@ -236,9 +237,24 @@ parameter_types! { pub const UncleGenerations: u64 = 0; pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(25); } + +pub struct Babe; +impl sp_runtime::BoundToRuntimeAppPublic for Babe { + type Public = dummy_sr25519::AuthorityId; +} + +// insert one dummy key and one key as a representative of the babe key which we will use in reality +// in here. +sp_runtime::impl_opaque_keys! { + pub struct SessionKeys { + // pub foo: UintAuthorityId, + pub babe: Babe, + } +} + impl pallet_session::Trait for Test { type SessionManager = pallet_session::historical::NoteHistoricalRoot; - type Keys = UintAuthorityId; + type Keys = SessionKeys; type ShouldEndSession = pallet_session::PeriodicSessions; type SessionHandler = TestSessionHandler; type Event = MetaEvent; @@ -301,8 +317,7 @@ impl Trait for Test { } pub(crate) mod dummy_sr25519 { - use super::LOCAL_KEY_ACCOUNT; - use sp_runtime::traits::{IdentifyAccount, Verify, Lazy}; + use super::{LOCAL_KEY_ACCOUNT, AccountId}; mod app_sr25519 { use sp_application_crypto::{app_crypto, key_types::DUMMY, sr25519}; @@ -320,16 +335,13 @@ pub(crate) mod dummy_sr25519 { } } - impl Verify for AuthoritySignature { - type Signer = AuthorityId; + pub fn dummy_key_for(x: AccountId) -> AuthorityId { + use sp_core::Pair; + let mut raw_key = [0u8; 32]; + raw_key[0] = x as u8; + let generic_key = sp_core::sr25519::Pair::from_seed(&raw_key).public(); + generic_key.into() - fn verify>( - &self, - msg: L, - signer: &::AccountId, - ) -> bool { - LOCAL_KEY_ACCOUNT.with(|v| *v.borrow() == *signer) - } } } @@ -545,7 +557,13 @@ impl ExtBuilder { let _ = pallet_session::GenesisConfig:: { keys: validators .iter() - .map(|x| (*x, UintAuthorityId(*x))) + .map(|x| ( + *x, + SessionKeys { + // foo: UintAuthorityId(*x), + babe: dummy_sr25519::dummy_key_for(*x), + } + )) .collect(), } .assimilate_storage(&mut storage); diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 103cdd85eca48..23b8f288f6061 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -23,7 +23,7 @@ use frame_system::offchain::{ CreateTransaction, }; use sp_phragmen::{reduce, ExtendedBalance, PhragmenResult, StakedAssignment}; -use sp_std::cmp::Ordering; +use sp_std::{prelude::*, cmp::Ordering}; use sp_runtime::RuntimeAppPublic; type SignAndSubmitOf = @@ -73,10 +73,7 @@ pub(super) fn is_score_better(this: [ExtendedBalance; 3], that: [ExtendedBalance } /// The internal logic of the offchain worker of this module. -pub(crate) fn compute_offchain_election() -> Result<(), OffchainElectionError> -where - ::KeyType: From> -{ +pub(crate) fn compute_offchain_election() -> Result<(), OffchainElectionError> { let validators = T::SessionInterface::validators(); // Check if current node can sign with any of the keys which correspond to a @@ -107,7 +104,7 @@ where // TODO: maybe we want to send from just one of them? we'll see. let call: ::Call = Call::submit_election_solution(winners, compact).into(); let _result = T::SubmitTransaction::submit_signed_from(call, validators); - dbg!(_result); + // dbg!(_result); } #[cfg(not(feature = "signed"))] { @@ -118,12 +115,17 @@ where // loop for at least one account in the validators to sign with. local_keys .into_iter() - .enumerate() - .find(|(_, (acc, _))| validators.contains(&acc)) - .map(|(index, (_, pubkey))| { + .find_map(|(acc, pubkey)| + validators + .iter() + .position(|a| *a == acc) + .map(|idx| (idx, (acc, pubkey))) + ).map(|(index, (_acc, pubkey))| { + // println!("Signing with {:?} {:?} {:?}", &_acc, &validators, index); let signature_payload = (winners.clone(), compact.clone(), index as u32).encode(); let pubkey: T::KeyType = pubkey.into(); + // dbg!(&pubkey); let signature = pubkey.sign(&signature_payload).unwrap(); let call: ::Call = Call::submit_election_solution_unsigned( winners, @@ -133,7 +135,7 @@ where ) .into(); let _result = T::SubmitTransaction::submit_unsigned(call); - dbg!(_result); + // dbg!(_result); }); } } else { diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 52409dbfc2840..2c3620ea9967d 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2684,7 +2684,7 @@ mod offchain_phragmen { use mock::*; use frame_support::{assert_ok, assert_noop}; use substrate_test_utils::assert_eq_uvec; - use sp_runtime::{traits::OffchainWorker} ; + use sp_runtime::traits::{OffchainWorker, ValidateUnsigned} ; use sp_core::offchain::{ OffchainExt, TransactionPoolExt, @@ -2697,9 +2697,8 @@ mod offchain_phragmen { use std::sync::Arc; use parking_lot::RwLock; - type KeyT = dummy_sr25519::AuthorityId; + type DummyT = dummy_sr25519::AuthorityId; - const PHRASE: &str = "news slush supreme milk chapter athlete soap sausage put clutch what kitten"; /// setup a new set of validators and nominator storage items independent of the parent mock /// file. This produces a edge graph that can be reduced. @@ -2724,7 +2723,12 @@ mod offchain_phragmen { let (offchain, _state) = TestOffchainExt::new(); let (pool, state) = TestTransactionPoolExt::new(); let keystore = KeyStore::new(); - keystore.write().sr25519_generate_new(KeyT::ID, Some(&format!("{}/staking1", PHRASE))).unwrap(); + // keystore.write().sr25519_generate_new(::ID, Some(&format!("{}/staking1", PHRASE))).unwrap(); + keystore.write().insert_unknown( + ::ID, + "news slush supreme milk chapter athlete soap sausage put clutch what kitten/staking1", + dummy_sr25519::dummy_key_for(11).as_ref(), + ); ext.register_extension(OffchainExt::new(offchain)); ext.register_extension(TransactionPoolExt::new(pool)); ext.register_extension(KeystoreExt(keystore)); @@ -3024,6 +3028,7 @@ mod offchain_phragmen { #[test] #[cfg(not(feature = "signed"))] + #[allow(deprecated)] fn offchain_worker_runs_when_window_open_unsigned() { // at the end of the first finalized block with ElectionStatus::open(_), it should execute. let mut ext = ExtBuilder::default() @@ -3045,16 +3050,25 @@ mod offchain_phragmen { let extrinsic: Extrinsic = Decode::decode(&mut &*encoded).unwrap(); let call = extrinsic.1; - match call { - mock::Call::Staking(crate::Call::submit_election_solution_unsigned(_, _, _, _)) => {}, + let inner = match call { + mock::Call::Staking(inner) => { inner }, _ => panic!("wrong call submitted"), }; - }) - } - #[test] - fn validate_unsigned_works() { - unimplemented!() + // pass this call to ValidateUnsigned + assert_eq!( + ::validate_unsigned(&inner), + TransactionValidity::Ok(ValidTransaction { + priority: TransactionPriority::max_value(), + requires: vec![], + provides: vec![], + longevity: TryInto::::try_into( + ::ElectionLookahead::get() + ).unwrap_or(150_u64), + propagate: true, + }) + ) + }) } #[test] diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index c1993ef439d42..4b7dc85d029a7 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -17,7 +17,7 @@ //! (very) Basic implementation of a graph node used in the reduce algorithm. use sp_runtime::RuntimeDebug; -use sp_std::cell::RefCell; +use sp_std::{prelude::*, cell::RefCell}; use sp_std::rc::Rc; /// The role that a node can accept. diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index e72fc83a358a6..430789bc9f48b 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -386,6 +386,7 @@ fn reduce_all( let common_count = trailing_common(&voter_root_path, &target_root_path); // because roots are the same. + #[cfg(feature = "std")] debug_assert_eq!(target_root_path.last().unwrap(), voter_root_path.last().unwrap()); debug_assert!(common_count > 0); @@ -393,10 +394,11 @@ fn reduce_all( // NOTE: the order of chaining is important! it is always build from [target, ..., // voter] let cycle = - target_root_path.iter().take(target_root_path.len() - common_count + 1).cloned() - .chain(voter_root_path.iter().take(voter_root_path.len() - common_count).rev().cloned()) - .collect::>>(); + target_root_path.iter().take(target_root_path.len() - common_count + 1).cloned() + .chain(voter_root_path.iter().take(voter_root_path.len() - common_count).rev().cloned()) + .collect::>>(); // a cycle's length shall always be multiple of two. + #[cfg(feature = "std")] debug_assert_eq!(cycle.len() % 2, 0); // find minimum of cycle. diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 2547ce1072185..3bc7b376d9aa7 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -949,7 +949,7 @@ pub trait OpaqueKeys: Clone { fn key_ids() -> &'static [crate::KeyTypeId]; /// Get the raw bytes of key with key-type ID `i`. fn get_raw(&self, i: super::KeyTypeId) -> &[u8]; - /// Get the decoded key with index `i`. + /// Get the decoded key with key-type ID `i`. fn get(&self, i: super::KeyTypeId) -> Option { T::decode(&mut self.get_raw(i)).ok() } From 6a33fc109ec0d6e92e400983e395d067f6c0a0a7 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 28 Jan 2020 15:19:31 +0100 Subject: [PATCH 028/106] Runtime builds --- frame/staking/Cargo.toml | 2 +- frame/staking/src/lib.rs | 15 +-- frame/staking/src/mock.rs | 8 +- frame/staking/src/offchain_election.rs | 121 +++++++++++-------------- frame/staking/src/tests.rs | 4 +- 5 files changed, 66 insertions(+), 84 deletions(-) diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index a75a8994735a2..a31c8269e1a2c 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -20,7 +20,7 @@ pallet-authorship = { version = "2.0.0", default-features = false, path = "../au sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } [dev-dependencies] -sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-core = { version = "2.0.0", path = "../../primitives/core", features = ["full_crypto"] } pallet-balances = { version = "2.0.0", path = "../balances" } pallet-timestamp = { version = "2.0.0", path = "../timestamp" } pallet-staking-reward-curve = { version = "2.0.0", path = "../staking/reward-curve" } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index b1cba105e1057..4eba550df7c8a 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -259,7 +259,7 @@ pub mod inflation; use sp_std::{prelude::*, result, convert::{TryInto, From}}; use codec::{HasCompact, Encode, Decode}; use frame_support::{ - decl_module, decl_event, decl_storage, ensure, decl_error, debug, + decl_module, decl_event, decl_storage, ensure, decl_error, debug, Parameter, weights::SimpleDispatchInfo, traits::{ Currency, OnFreeBalanceZero, LockIdentifier, LockableCurrency, @@ -272,7 +272,7 @@ use sp_runtime::{ curve::PiecewiseLinear, traits::{ Convert, Zero, One, StaticLookup, CheckedSub, Saturating, Bounded, SaturatedConversion, - SimpleArithmetic, EnsureOrigin + SimpleArithmetic, EnsureOrigin, Member, OpaqueKeys }, transaction_validity::{ TransactionValidityError, TransactionValidity, ValidTransaction, InvalidTransaction, TransactionPriority, @@ -646,15 +646,7 @@ impl SessionInterface<::AccountId> for T whe fn keys() -> Vec<(::AccountId, Option)> { - use sp_runtime::traits::OpaqueKeys; - // dbg!(::Keys::key_ids()); - // dbg!(::ID); Self::validators().iter().map(|v| { - // let opkeys = >::load_keys(&v).unwrap(); - // println!("Loaded stuff {:?} {:?}", - // opkeys.get::(::ID), - // v - // ); let maybe_key = >::load_keys(&v) .and_then(|opk| opk.get(KeyT::ID)); (v.clone(), maybe_key) @@ -734,8 +726,7 @@ pub trait Trait: frame_system::Trait { SubmitSignedTransaction::Call> + SubmitUnsignedTransaction::Call>; - type KeyType: From> + - RuntimeAppPublic + sp_runtime::traits::Member + frame_support::Parameter; + type KeyType: RuntimeAppPublic + Member + Parameter; } /// Mode of era-forcing. diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 2b16983b1ec29..4955bf669445b 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -46,6 +46,8 @@ pub(crate) type AccountIndex = u64; pub(crate) type BlockNumber = u64; pub(crate) type Balance = u64; +pub const PHRASE: &str = "news slush supreme milk chapter athlete soap sausage put clutch what kitten"; + /// Simple structure that exposes how u64 currency can be represented as... u64. pub struct CurrencyToVoteHandler; impl Convert for CurrencyToVoteHandler { @@ -339,9 +341,11 @@ pub(crate) mod dummy_sr25519 { use sp_core::Pair; let mut raw_key = [0u8; 32]; raw_key[0] = x as u8; - let generic_key = sp_core::sr25519::Pair::from_seed(&raw_key).public(); + let generic_key = sp_core::sr25519::Pair::from_string( + &format!("{}/staking{}", super::PHRASE, x), + None, + ).unwrap().public(); generic_key.into() - } } diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 23b8f288f6061..e85a5b97396fd 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -74,73 +74,60 @@ pub(super) fn is_score_better(this: [ExtendedBalance; 3], that: [ExtendedBalance /// The internal logic of the offchain worker of this module. pub(crate) fn compute_offchain_election() -> Result<(), OffchainElectionError> { - let validators = T::SessionInterface::validators(); - - // Check if current node can sign with any of the keys which correspond to a - // validator. This basically says: proceed if the node is a validator. - if T::SubmitTransaction::can_sign_with(Some(validators.clone())) { - let PhragmenResult { - winners, - assignments, - } = >::do_phragmen().ok_or(OffchainElectionError::FailedElection)?; - - // convert winners into just account ids. - let winners: Vec = winners.into_iter().map(|(w, _)| w).collect(); - - // convert into staked. This is needed to be able to reduce. - let mut staked: Vec> = assignments - .into_iter() - .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(>::slashable_balance_of)) - .collect(); - - // reduce the assignments. This will remove some additional edges. - reduce(&mut staked); - - // compact encode the assignment. - let compact = >::from_staked(staked); - - #[cfg(feature = "signed")] - { - // TODO: maybe we want to send from just one of them? we'll see. - let call: ::Call = Call::submit_election_solution(winners, compact).into(); - let _result = T::SubmitTransaction::submit_signed_from(call, validators); - // dbg!(_result); - } - #[cfg(not(feature = "signed"))] - { - // TODO: this call is really not needed, we can do it manually instead - // of `can_sign_with`. - // TODO: we could use some error handling here. - let local_keys = T::SubmitTransaction::find_all_local_keys(); - // loop for at least one account in the validators to sign with. - local_keys + let validator_keys = T::SessionInterface::keys::(); + let local_keys = T::KeyType::all(); + + if let Some((index, pubkey)) = local_keys + .into_iter() + .enumerate() + .find(|(_, k)| + validator_keys.iter().find(|(_acc, maybe_vk)| + maybe_vk.as_ref().map(|vk| vk == k).unwrap_or(false) + ).is_some() + ) { + // k is a local key who is also among the validators. + let PhragmenResult { + winners, + assignments, + } = >::do_phragmen().ok_or(OffchainElectionError::FailedElection)?; + + // convert winners into just account ids. + let winners: Vec = winners.into_iter().map(|(w, _)| w).collect(); + + // convert into staked. This is needed to be able to reduce. + let mut staked: Vec> = assignments .into_iter() - .find_map(|(acc, pubkey)| - validators - .iter() - .position(|a| *a == acc) - .map(|idx| (idx, (acc, pubkey))) - ).map(|(index, (_acc, pubkey))| { - // println!("Signing with {:?} {:?} {:?}", &_acc, &validators, index); - let signature_payload = - (winners.clone(), compact.clone(), index as u32).encode(); - let pubkey: T::KeyType = pubkey.into(); - // dbg!(&pubkey); - let signature = pubkey.sign(&signature_payload).unwrap(); - let call: ::Call = Call::submit_election_solution_unsigned( - winners, - compact, - index as u32, - signature, - ) - .into(); - let _result = T::SubmitTransaction::submit_unsigned(call); - // dbg!(_result); - }); + .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(>::slashable_balance_of)) + .collect(); + + // reduce the assignments. This will remove some additional edges. + reduce(&mut staked); + + // compact encode the assignment. + let compact = >::from_staked(staked); + + #[cfg(feature = "signed")] + { + unimplemented!(); + } + + #[cfg(not(feature = "signed"))] + { + let signature_payload = + (winners.clone(), compact.clone(), index as u32).encode(); + let signature = pubkey.sign(&signature_payload).unwrap(); + let call: ::Call = Call::submit_election_solution_unsigned( + winners, + compact, + index as u32, + signature, + ) + .into(); + let _result = T::SubmitTransaction::submit_unsigned(call); + } + + Ok(()) + } else { + Err(OffchainElectionError::NoSigningKey) } - } else { - Err(OffchainElectionError::NoSigningKey)?; - } - - Ok(()) } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 2c3620ea9967d..c0bb83a99a263 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2723,10 +2723,10 @@ mod offchain_phragmen { let (offchain, _state) = TestOffchainExt::new(); let (pool, state) = TestTransactionPoolExt::new(); let keystore = KeyStore::new(); - // keystore.write().sr25519_generate_new(::ID, Some(&format!("{}/staking1", PHRASE))).unwrap(); + keystore.write().insert_unknown( ::ID, - "news slush supreme milk chapter athlete soap sausage put clutch what kitten/staking1", + &format!("{}/staking{}", mock::PHRASE, 11), dummy_sr25519::dummy_key_for(11).as_ref(), ); ext.register_extension(OffchainExt::new(offchain)); From 4e951afb4e43b35f60e1944526322a96d43eefe8 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 28 Jan 2020 15:46:33 +0100 Subject: [PATCH 029/106] =?UTF-8?q?Unsigned=20test=20works=20=F0=9F=99=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frame/staking/src/lib.rs | 1 - frame/staking/src/offchain_election.rs | 35 ++++++++------------------ 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 4eba550df7c8a..8935ebcaa3b67 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -2314,7 +2314,6 @@ impl frame_support::unsigned::ValidateUnsigned for Module { let (validator_id, validator_key) = current_validators.get(*validator_index as usize) .ok_or(TransactionValidityError::Invalid(InvalidTransaction::Custom(0u8).into()))?; - // dbg!(&validator_id, &validator_key); // unwrap the key if it exists. let validator_key = validator_key.as_ref() .ok_or(TransactionValidityError::Invalid(InvalidTransaction::Custom(1u8).into()))?; diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index e85a5b97396fd..66ea0880c8861 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -19,30 +19,12 @@ use crate::{Call, CompactAssignments, Module, SessionInterface, Trait}; use codec::Encode; use frame_system::offchain::{ - Signer, SubmitSignedTransaction, SubmitUnsignedTransaction, SignAndSubmitTransaction, - CreateTransaction, + SubmitSignedTransaction, SubmitUnsignedTransaction, SignAndSubmitTransaction, CreateTransaction, }; use sp_phragmen::{reduce, ExtendedBalance, PhragmenResult, StakedAssignment}; use sp_std::{prelude::*, cmp::Ordering}; use sp_runtime::RuntimeAppPublic; -type SignAndSubmitOf = -< - ::SubmitTransaction - as - SubmitSignedTransaction::Call> ->::SignAndSubmit; - -pub(crate) type PublicOf = -< - as SignAndSubmitTransaction::Call>>::CreateTransaction - as - CreateTransaction< - T, - as SignAndSubmitTransaction::Call>>::Extrinsic - > ->::Public; - #[derive(Debug)] pub(crate) enum OffchainElectionError { /// No signing key has been found on the current node that maps to a validators. This node @@ -77,13 +59,16 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti let validator_keys = T::SessionInterface::keys::(); let local_keys = T::KeyType::all(); - if let Some((index, pubkey)) = local_keys + if let Some((index, ref pubkey)) = local_keys .into_iter() - .enumerate() - .find(|(_, k)| - validator_keys.iter().find(|(_acc, maybe_vk)| - maybe_vk.as_ref().map(|vk| vk == k).unwrap_or(false) - ).is_some() + .find_map(|k| + validator_keys + .iter() + .enumerate() + .find_map(|(index, (_acc, maybe_vk))| + maybe_vk.as_ref() + .and_then(|vk| if *vk == k { Some((index, vk)) } else { None }) + ) ) { // k is a local key who is also among the validators. let PhragmenResult { From 5ab3cf0bde959b955d100ff0e16890e83ec2cfe7 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 28 Jan 2020 17:57:38 +0100 Subject: [PATCH 030/106] Some cleanups --- bin/node/runtime/src/lib.rs | 4 +- frame/babe/src/lib.rs | 8 +-- frame/staking/src/lib.rs | 26 ++++---- frame/staking/src/mock.rs | 82 +++++++----------------- frame/staking/src/offchain_election.rs | 4 +- frame/staking/src/tests.rs | 9 ++- frame/support/src/traits.rs | 8 +-- frame/system/src/offchain.rs | 2 +- primitives/phragmen/fuzzer/src/reduce.rs | 2 +- primitives/phragmen/src/node.rs | 2 +- primitives/phragmen/src/reduce.rs | 2 +- 11 files changed, 54 insertions(+), 95 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 48750aa599ccc..07cc80d2aa5d7 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -272,8 +272,6 @@ parameter_types! { pub const ElectionLookahead: BlockNumber = 150; } -struct StakingTransactionSubmitter; - impl pallet_staking::Trait for Runtime { type Currency = Balances; type Time = Timestamp; @@ -466,7 +464,7 @@ impl pallet_im_online::Trait for Runtime { type Call = Call; type Event = Event; type AuthorityId = ImOnlineId; - type SubmitTransaction = TransactionSubmitterOf; + type SubmitTransaction = TransactionSubmitterOf; type ReportUnresponsiveness = Offences; type SessionDuration = SessionDuration; } diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index b2aa7766efe2d..be82f875e0b4a 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -320,7 +320,7 @@ impl Module { /// -------------- 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. - pub fn next_epoch_change(now: T::BlockNumber) -> T::BlockNumber { + pub fn next_expected_epoch_change(now: T::BlockNumber) -> T::BlockNumber { let next_slot = Self::current_epoch_start().saturating_add(T::EpochDuration::get()); let slots_remaining = next_slot .checked_sub(CurrentSlot::get()) @@ -487,9 +487,9 @@ impl OnTimestampSet for Module { fn on_timestamp_set(_moment: T::Moment) { } } -impl frame_support::traits::PredictNextSessionChange for Module { - fn predict_next_session_change(now: T::BlockNumber) -> T::BlockNumber { - Self::next_epoch_change(now) +impl frame_support::traits::EstimateNextSessionChange for Module { + fn estimate_next_session_change(now: T::BlockNumber) -> T::BlockNumber { + Self::next_expected_epoch_change(now) } } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 8935ebcaa3b67..802c4e3c0a953 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -263,7 +263,7 @@ use frame_support::{ weights::SimpleDispatchInfo, traits::{ Currency, OnFreeBalanceZero, LockIdentifier, LockableCurrency, - WithdrawReasons, OnUnbalanced, Imbalance, Get, Time, PredictNextSessionChange, + WithdrawReasons, OnUnbalanced, Imbalance, Get, Time, EstimateNextSessionChange, } }; use pallet_session::historical; @@ -286,7 +286,7 @@ use sp_staking::{ use sp_runtime::{Serialize, Deserialize}; use frame_system::{ self as system, ensure_signed, ensure_root, ensure_none, - offchain::{SubmitSignedTransaction, SubmitUnsignedTransaction, SignAndSubmitTransaction}, + offchain::SubmitUnsignedTransaction, }; use sp_phragmen::{ @@ -618,7 +618,7 @@ pub trait SessionInterface: frame_system::Trait { /// Get the validators from session. fn validators() -> Vec; /// Get the validators and their corresponding keys from session. - fn keys() -> Vec<(AccountId, Option)>; + fn keys() -> Vec<(AccountId, Option)>; /// Prune historical session tries up to but not including the given index. fn prune_historical_up_to(up_to: SessionIndex); /// Current session index. @@ -643,7 +643,7 @@ impl SessionInterface<::AccountId> for T whe >::validators() } - fn keys() + fn keys() -> Vec<(::AccountId, Option)> { Self::validators().iter().map(|v| { @@ -710,22 +710,21 @@ pub trait Trait: frame_system::Trait { type RewardCurve: Get<&'static PiecewiseLinear<'static>>; /// Something that can predict the next session change. - type NextSessionChange: PredictNextSessionChange; + type NextSessionChange: EstimateNextSessionChange; - /// How many blocks ahead of the epoch do we try to run the phragmen offchain? Setting this to + /// How many blocks ahead of the era do we try to run the phragmen offchain? Setting this to /// zero will disable the offchain compute and only on-chain seq-phragmen will be used. type ElectionLookahead: Get; /// The overarching call type. - // TODO: This is needed just to bound it to `From>`. Otherwise could have `Self as system` type Call: From> + Clone; /// A transaction submitter. - type SubmitTransaction: - SignAndSubmitTransaction::Call> + - SubmitSignedTransaction::Call> + - SubmitUnsignedTransaction::Call>; + type SubmitTransaction: SubmitUnsignedTransaction::Call>; + /// Key type used to sign and verify transaction. + // TODO: this cen be fetched from `SubmitTransaction` as well, but for now both me and @todr + // gave up. type KeyType: RuntimeAppPublic + Member + Parameter; } @@ -976,7 +975,7 @@ decl_module! { Self::is_current_session_final() { let next_session_change = - T::NextSessionChange::predict_next_session_change(now); + T::NextSessionChange::estimate_next_session_change(now); if let Some(remaining) = next_session_change.checked_sub(&now) { if remaining <= T::ElectionLookahead::get() && !remaining.is_zero() { // Set the flag to make sure we don't waste any compute here in the same era @@ -2293,7 +2292,6 @@ impl ReportOffence } } -// TODO: do we need BoundToRuntimeAppPublic stuff? #[allow(deprecated)] impl frame_support::unsigned::ValidateUnsigned for Module { type Call = Call; @@ -2311,7 +2309,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { // check signature let payload = (winners, compact, validator_index); let current_validators = T::SessionInterface::keys::(); - let (validator_id, validator_key) = current_validators.get(*validator_index as usize) + let (_, validator_key) = current_validators.get(*validator_index as usize) .ok_or(TransactionValidityError::Invalid(InvalidTransaction::Custom(0u8).into()))?; // unwrap the key if it exists. diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 4955bf669445b..68b34ecfb0def 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -19,12 +19,12 @@ use crate::*; use frame_support::{ assert_ok, impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types, - traits::{Currency, FindAuthor, Get, PredictNextSessionChange}, + traits::{Currency, FindAuthor, Get, EstimateNextSessionChange}, weights::Weight, StorageLinkedMap, StorageValue, }; use frame_system::offchain::{CreateTransaction, Signer, TransactionSubmitter}; -use sp_core::{crypto::key_types, H256}; +use sp_core::H256; use sp_io; use sp_phragmen::{build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment}; use sp_runtime::curve::PiecewiseLinear; @@ -169,11 +169,11 @@ impl_outer_event! { } pub struct PeriodicSessionChange

(sp_std::marker::PhantomData

); -impl

PredictNextSessionChange for PeriodicSessionChange

+impl

EstimateNextSessionChange for PeriodicSessionChange

where P: Get, { - fn predict_next_session_change(now: BlockNumber) -> BlockNumber { + fn estimate_next_session_change(now: BlockNumber) -> BlockNumber { let period = P::get(); let excess = now % period; now - excess + period @@ -184,13 +184,13 @@ where pub struct Author11; impl FindAuthor for Author11 { fn find_author<'a, I>(_digests: I) -> Option - where - I: 'a + IntoIterator, + where I: 'a + IntoIterator, { Some(11) } } +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, Eq, PartialEq, Debug)] pub struct Test; @@ -328,7 +328,6 @@ pub(crate) mod dummy_sr25519 { pub type AuthoritySignature = app_sr25519::Signature; pub type AuthorityId = app_sr25519::Public; - pub type AuthorityPair = app_sr25519::Pair; impl sp_runtime::traits::IdentifyAccount for AuthorityId { type AccountId = u64; @@ -510,8 +509,7 @@ impl ExtBuilder { (999, 1_000_000_000_000), ], vesting: vec![], - } - .assimilate_storage(&mut storage); + }.assimilate_storage(&mut storage); let stake_21 = if self.fair { 1000 } else { 2000 }; let stake_31 = if self.validator_pool { @@ -530,22 +528,12 @@ impl ExtBuilder { stakers: if self.has_stakers { vec![ // (stash, controller, staked_amount, status) - ( - 11, - 10, - balance_factor * 1000, - StakerStatus::::Validator, - ), + (11, 10, balance_factor * 1000, StakerStatus::::Validator), (21, 20, stake_21, StakerStatus::::Validator), (31, 30, stake_31, StakerStatus::::Validator), (41, 40, balance_factor * 1000, status_41), // nominator - ( - 101, - 100, - balance_factor * 500, - StakerStatus::::Nominator(nominated), - ), + (101, 100, balance_factor * 500, StakerStatus::::Nominator(nominated)), ] } else { vec![] @@ -559,18 +547,14 @@ impl ExtBuilder { .assimilate_storage(&mut storage); let _ = pallet_session::GenesisConfig:: { - keys: validators - .iter() - .map(|x| ( - *x, - SessionKeys { - // foo: UintAuthorityId(*x), - babe: dummy_sr25519::dummy_key_for(*x), - } - )) - .collect(), - } - .assimilate_storage(&mut storage); + keys: validators.iter().map(|x| ( + *x, + SessionKeys { + // foo: UintAuthorityId(*x), + babe: dummy_sr25519::dummy_key_for(*x), + } + )).collect(), + }.assimilate_storage(&mut storage); let mut ext = sp_io::TestExternalities::from(storage); ext.execute_with(|| { @@ -588,9 +572,7 @@ pub type Timestamp = pallet_timestamp::Module; pub type Staking = Module; pub fn check_exposure_all() { - Staking::current_elected() - .into_iter() - .for_each(|acc| check_exposure(acc)); + Staking::current_elected().into_iter().for_each(|acc| check_exposure(acc)); } pub fn check_nominator_all() { @@ -618,12 +600,7 @@ pub fn check_nominator_exposure(stash: u64) { Staking::current_elected() .iter() .map(|v| Staking::stakers(v)) - .for_each(|e| { - e.others - .iter() - .filter(|i| i.who == stash) - .for_each(|i| sum += i.value) - }); + .for_each(|e| e.others.iter().filter(|i| i.who == stash).for_each(|i| sum += i.value)); let nominator_stake = Staking::slashable_balance_of(&stash); // a nominator cannot over-spend. assert!( @@ -643,10 +620,7 @@ pub fn assert_ledger_consistent(stash: u64) { assert_is_stash(stash); let ledger = Staking::ledger(stash - 1).unwrap(); - let real_total: Balance = ledger - .unlocking - .iter() - .fold(ledger.active, |a, c| a + c.value); + let real_total: Balance = ledger.unlocking.iter().fold(ledger.active, |a, c| a + c.value); assert_eq!(real_total, ledger.total); } @@ -689,7 +663,7 @@ pub fn advance_session() { } pub fn start_session(session_index: SessionIndex) { - // Compensate for session delay TODO: double check this. + // Compensate for session delay. let session_index = session_index + 1; for i in Session::current_index()..session_index { System::set_block_number((i + 1).into()); @@ -712,8 +686,7 @@ pub fn current_total_payout_for_duration(duration: u64) -> u64 { >::slot_stake() * 2, Balances::total_issuance(), duration, - ) - .0 + ).0 } pub fn reward_all_elected() { @@ -751,21 +724,14 @@ pub fn on_offence_in_era( } if Staking::current_era() == era { - Staking::on_offence( - offenders, - slash_fraction, - Staking::current_era_start_session_index(), - ); + Staking::on_offence(offenders, slash_fraction, Staking::current_era_start_session_index()); } else { panic!("cannot slash in era {}", era); } } pub fn on_offence_now( - offenders: &[OffenceDetails< - AccountId, - pallet_session::historical::IdentificationTuple, - >], + offenders: &[OffenceDetails>], slash_fraction: &[Perbill], ) { let now = Staking::current_era(); diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 66ea0880c8861..4800c2a3c98d9 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -18,9 +18,7 @@ use crate::{Call, CompactAssignments, Module, SessionInterface, Trait}; use codec::Encode; -use frame_system::offchain::{ - SubmitSignedTransaction, SubmitUnsignedTransaction, SignAndSubmitTransaction, CreateTransaction, -}; +use frame_system::offchain::{SubmitUnsignedTransaction}; use sp_phragmen::{reduce, ExtendedBalance, PhragmenResult, StakedAssignment}; use sp_std::{prelude::*, cmp::Ordering}; use sp_runtime::RuntimeAppPublic; diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index c0bb83a99a263..3f8a796ef2cc0 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2684,7 +2684,7 @@ mod offchain_phragmen { use mock::*; use frame_support::{assert_ok, assert_noop}; use substrate_test_utils::assert_eq_uvec; - use sp_runtime::traits::{OffchainWorker, ValidateUnsigned} ; + use sp_runtime::traits::OffchainWorker; use sp_core::offchain::{ OffchainExt, TransactionPoolExt, @@ -2693,7 +2693,6 @@ mod offchain_phragmen { use sp_io::TestExternalities; use sp_core::traits::KeystoreExt; use sp_core::testing::KeyStore; - use sp_application_crypto::AppKey; use std::sync::Arc; use parking_lot::RwLock; @@ -2724,11 +2723,11 @@ mod offchain_phragmen { let (pool, state) = TestTransactionPoolExt::new(); let keystore = KeyStore::new(); - keystore.write().insert_unknown( + let _ = keystore.write().insert_unknown( ::ID, &format!("{}/staking{}", mock::PHRASE, 11), dummy_sr25519::dummy_key_for(11).as_ref(), - ); + ).unwrap(); ext.register_extension(OffchainExt::new(offchain)); ext.register_extension(TransactionPoolExt::new(pool)); ext.register_extension(KeystoreExt(keystore)); @@ -3057,7 +3056,7 @@ mod offchain_phragmen { // pass this call to ValidateUnsigned assert_eq!( - ::validate_unsigned(&inner), + ::validate_unsigned(&inner), TransactionValidity::Ok(ValidTransaction { priority: TransactionPriority::max_value(), requires: vec![], diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 90606f3b50970..74f50dc7d050c 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -29,13 +29,13 @@ use sp_runtime::{ use crate::dispatch::Parameter; /// Something that can predict at which block number the next era change will happen. -pub trait PredictNextSessionChange { +pub trait EstimateNextSessionChange { /// Return the block number at which the next era change will happen. - fn predict_next_session_change(now: BlockNumber) -> BlockNumber; + fn estimate_next_session_change(now: BlockNumber) -> BlockNumber; } -impl PredictNextSessionChange for () { - fn predict_next_session_change(_: BlockNumber) -> BlockNumber { +impl EstimateNextSessionChange for () { + fn estimate_next_session_change(_: BlockNumber) -> BlockNumber { // practically never Bounded::max_value() } diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 51e3cc1a59421..6b791570a3c61 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -32,7 +32,7 @@ pub trait CreateTransaction { /// A `Public` key representing a particular `AccountId`. type Public: IdentifyAccount + Clone; /// A `Signature` generated by the `Signer`. - type Signature: Encode + Decode + Clone + Debug + Eq + PartialEq + Default; + type Signature; /// Attempt to create signed extrinsic data that encodes call from given account. /// diff --git a/primitives/phragmen/fuzzer/src/reduce.rs b/primitives/phragmen/fuzzer/src/reduce.rs index 8f0a0cb1978c3..09294544c1b73 100644 --- a/primitives/phragmen/fuzzer/src/reduce.rs +++ b/primitives/phragmen/fuzzer/src/reduce.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// Copyright 2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index 4b7dc85d029a7..19b442e24711b 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// Copyright 2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index 430789bc9f48b..e585a014c92a2 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// Copyright 2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify From 31fd60e46a40ddaa6e8f74b3bee4343970ebd205 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 29 Jan 2020 11:44:12 +0100 Subject: [PATCH 031/106] Make all the tests compile and stuff --- bin/node/executor/tests/submit_transaction.rs | 4 +- bin/node/runtime/src/lib.rs | 84 ++++++++++--------- frame/authority-discovery/src/lib.rs | 1 - frame/babe/src/mock.rs | 2 +- frame/babe/src/tests.rs | 2 +- frame/staking/src/lib.rs | 15 ++-- frame/staking/src/offchain_election.rs | 6 +- frame/system/src/offchain.rs | 3 +- 8 files changed, 59 insertions(+), 58 deletions(-) diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index 9e91ffc76b38f..2f4694c03f882 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -15,7 +15,7 @@ // along with Substrate. If not, see . use node_runtime::{ - Call, Executive, Indices, Runtime, SubmitTransaction, UncheckedExtrinsic, + Call, Executive, Indices, Runtime, TransactionSubmitterOf, UncheckedExtrinsic, }; use sp_application_crypto::AppKey; use sp_core::testing::KeyStore; @@ -31,6 +31,8 @@ use codec::Decode; pub mod common; use self::common::*; +type SubmitTransaction = TransactionSubmitterOf; + #[test] fn should_submit_unsigned_transaction() { let mut t = new_test_ext(COMPACT_CODE, false); diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e1582d31d09be..35bf302ce7200 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -71,8 +71,51 @@ use constants::{time::*, currency::*}; #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +/// A transaction submitter with the given key type. pub type TransactionSubmitterOf = TransactionSubmitter; +/// Submits transaction with the node's public and signature type. Adheres to the signed extension +/// format of the chain. +impl frame_system::offchain::CreateTransaction for Runtime { + type Public = ::Signer; + type Signature = Signature; + + fn create_transaction>( + call: Call, + public: Self::Public, + account: AccountId, + index: Index, + ) -> Option<(Call, ::SignaturePayload)> { + // take the biggest period possible. + let period = BlockHashCount::get() + .checked_next_power_of_two() + .map(|c| c / 2) + .unwrap_or(2) as u64; + let current_block = System::block_number() + .saturated_into::() + // The `System::block_number` is initialized with `n+1`, + // so the actual block number is `n`. + .saturating_sub(1); + let tip = 0; + let extra: SignedExtra = ( + frame_system::CheckVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(generic::Era::mortal(period, current_block)), + frame_system::CheckNonce::::from(index), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(tip), + Default::default(), + ); + let raw_payload = SignedPayload::new(call, extra).map_err(|e| { + debug::warn!("Unable to create signed payload: {:?}", e); + }).ok()?; + let signature = TSigner::sign(public, &raw_payload)?; + let address = Indices::unlookup(account); + let (call, extra, _) = raw_payload.deconstruct(); + Some((call, (address, signature, extra))) + } +} + /// Runtime version. pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node"), @@ -456,7 +499,6 @@ impl pallet_sudo::Trait for Runtime { } parameter_types! { - // assume 1 slot == 1 block pub const SessionDuration: BlockNumber = EPOCH_DURATION_IN_SLOTS as _; } @@ -511,46 +553,6 @@ impl pallet_identity::Trait for Runtime { type ForceOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; } -impl frame_system::offchain::CreateTransaction for Runtime { - type Public = ::Signer; - type Signature = Signature; - - fn create_transaction>( - call: Call, - public: Self::Public, - account: AccountId, - index: Index, - ) -> Option<(Call, ::SignaturePayload)> { - // take the biggest period possible. - let period = BlockHashCount::get() - .checked_next_power_of_two() - .map(|c| c / 2) - .unwrap_or(2) as u64; - let current_block = System::block_number() - .saturated_into::() - // The `System::block_number` is initialized with `n+1`, - // so the actual block number is `n`. - .saturating_sub(1); - let tip = 0; - let extra: SignedExtra = ( - frame_system::CheckVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(generic::Era::mortal(period, current_block)), - frame_system::CheckNonce::::from(index), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - Default::default(), - ); - let raw_payload = SignedPayload::new(call, extra).map_err(|e| { - debug::warn!("Unable to create signed payload: {:?}", e); - }).ok()?; - let signature = TSigner::sign(public, &raw_payload)?; - let address = Indices::unlookup(account); - let (call, extra, _) = raw_payload.deconstruct(); - Some((call, (address, signature, extra))) - } -} - parameter_types! { pub const ConfigDepositBase: Balance = 5 * DOLLARS; pub const FriendDepositFactor: Balance = 50 * CENTS; diff --git a/frame/authority-discovery/src/lib.rs b/frame/authority-discovery/src/lib.rs index c427043397075..22ea3d3bbafbb 100644 --- a/frame/authority-discovery/src/lib.rs +++ b/frame/authority-discovery/src/lib.rs @@ -103,7 +103,6 @@ mod tests { use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; type AuthorityDiscovery = Module; - type SessionIndex = u32; #[derive(Clone, Eq, PartialEq)] pub struct Test; diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index bb2371788557b..7f60e2b1a6efc 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -82,7 +82,7 @@ impl pallet_session::Trait for Test { type ValidatorId = ::AccountId; type ShouldEndSession = Babe; type SessionHandler = (Babe,); - type OnSessionEnding = (); + type SessionManager = (); type ValidatorIdOf = (); type Keys = MockSessionKeys; type DisabledValidatorsThreshold = DisabledValidatorsThreshold; diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index e2fc226f511e4..6128155be9e4c 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -132,6 +132,6 @@ fn can_predict_next_epoch_change() { // next epoch change will be at assert_eq!(Babe::current_epoch_start(), 9); // next change will be 12, 2 slots from now - assert_eq!(Babe::next_epoch_change(System::block_number()), 5 + 2); + assert_eq!(Babe::next_expected_epoch_change(System::block_number()), 5 + 2); }) } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index a52b2e486763a..fbf8f12a65dd4 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -300,6 +300,9 @@ const MAX_NOMINATIONS: usize = 16; const MAX_UNLOCKING_CHUNKS: usize = 32; const STAKING_ID: LockIdentifier = *b"staking "; +// ------------- IMPORTANT NOTE: must be the same as `MAX_NOMINATIONS`. +generate_compact_solution_type!(pub CompactAssignments, 16); + /// Counter for the number of eras that have passed. pub type EraIndex = u32; @@ -581,8 +584,7 @@ pub struct ElectionResult { /// The status of the upcoming (offchain) election. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub enum ElectionStatus { - /// Nothing has and will happen for now. We don't have a solution for next era, and submission - /// window is also not open. + /// Nothing has and will happen for now. submission window is not open. None, /// The submission window has been open since the contained block number. Open(BlockNumber), @@ -594,9 +596,6 @@ impl Default for ElectionStatus { } } -// ------------- IMPORTANT NOTE: must be the same as `MAX_NOMINATIONS`. -generate_compact_solution_type!(pub CompactAssignments, 16); - pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type PositiveImbalanceOf = @@ -723,7 +722,7 @@ pub trait Trait: frame_system::Trait { type SubmitTransaction: SubmitUnsignedTransaction::Call>; /// Key type used to sign and verify transaction. - // TODO: this cen be fetched from `SubmitTransaction` as well, but for now both me and @todr + // TODO: this can be fetched from `SubmitTransaction` as well, but for now both me and @todr // gave up. type KeyType: RuntimeAppPublic + Member + Parameter; } @@ -1006,7 +1005,7 @@ decl_module! { fn offchain_worker(now: T::BlockNumber) { // runs only once. if Self::era_election_status() == ElectionStatus::::Open(now) { - offchain_election::compute_offchain_election::().map_err(|e| { + let _ = offchain_election::compute_offchain_election::().map_err(|e| { debug::native::warn!( target: "staking", "{:?}", e @@ -1727,7 +1726,7 @@ impl Module { // voting for valid candidates. All we have to check is if they actually come from the // claimed nominator or not. - // Endlich alles Ok. Exposures and store the result. + // At last, alles Ok. Exposures and store the result. let to_balance = |e: ExtendedBalance| >>::convert(e); let mut slot_stake: BalanceOf = Bounded::max_value(); diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 4800c2a3c98d9..d38952475fcb9 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -21,9 +21,9 @@ use codec::Encode; use frame_system::offchain::{SubmitUnsignedTransaction}; use sp_phragmen::{reduce, ExtendedBalance, PhragmenResult, StakedAssignment}; use sp_std::{prelude::*, cmp::Ordering}; -use sp_runtime::RuntimeAppPublic; +use sp_runtime::{RuntimeAppPublic, RuntimeDebug}; -#[derive(Debug)] +#[derive(RuntimeDebug)] pub(crate) enum OffchainElectionError { /// No signing key has been found on the current node that maps to a validators. This node /// should not run the offchain election code. @@ -37,7 +37,7 @@ pub(crate) enum OffchainElectionError { /// better `this`. /// /// Evaluation is done in a lexicographic manner. -pub(super) fn is_score_better(this: [ExtendedBalance; 3], that: [ExtendedBalance; 3]) -> bool { +pub(crate) fn is_score_better(this: [ExtendedBalance; 3], that: [ExtendedBalance; 3]) -> bool { match that .iter() .enumerate() diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index 6b791570a3c61..54b84e5eb28d1 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -16,10 +16,9 @@ //! Module helpers for off-chain calls. -use codec::{Encode, Decode}; +use codec::Encode; use sp_std::convert::TryInto; use sp_std::prelude::Vec; -use sp_std::fmt::Debug; use sp_runtime::app_crypto::{RuntimeAppPublic, AppPublic, AppSignature}; use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount}; use frame_support::debug; From 2623d9883f09b083756e695a7b731b6cbf4d31e6 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 3 Feb 2020 08:58:04 +0100 Subject: [PATCH 032/106] Minor cleanup --- bin/node/runtime/src/lib.rs | 2 +- frame/staking/src/lib.rs | 63 ++++++++++++++++--------- primitives/phragmen/Cargo.toml | 4 +- primitives/phragmen/benches/phragmen.rs | 1 + primitives/phragmen/compact/src/lib.rs | 36 +++++++------- primitives/phragmen/src/lib.rs | 10 ++-- primitives/phragmen/src/node.rs | 30 +++++++++++- primitives/phragmen/src/reduce.rs | 30 ++++++------ 8 files changed, 114 insertions(+), 62 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 35bf302ce7200..132cc18ea7667 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -610,7 +610,7 @@ construct_runtime!( Indices: pallet_indices, Balances: pallet_balances, TransactionPayment: pallet_transaction_payment::{Module, Storage}, - Staking: pallet_staking, + Staking: pallet_staking::{Module, Call, Storage, Event, ValidateUnsigned, Config}, Session: pallet_session::{Module, Call, Storage, Event, Config}, Democracy: pallet_democracy::{Module, Call, Storage, Config, Event}, Council: pallet_collective::::{Module, Call, Storage, Origin, Event, Config}, diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index fbf8f12a65dd4..824178b1c552e 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -251,9 +251,9 @@ mod mock; #[cfg(test)] mod tests; mod migration; -mod slashing; -mod offchain_election; +pub mod slashing; +pub mod offchain_election; pub mod inflation; use sp_std::{prelude::*, result, convert::{TryInto, From}}; @@ -289,10 +289,7 @@ use frame_system::{ offchain::SubmitUnsignedTransaction, }; -use sp_phragmen::{ - ExtendedBalance, Assignment, StakedAssignment, - generate_compact_solution_type, -}; +use sp_phragmen::{ExtendedBalance, StakedAssignment, generate_compact_solution_type}; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; // ------------- IMPORTANT NOTE: must be the same as `generate_compact_solution_type`. @@ -562,8 +559,10 @@ pub struct UnappliedSlash { pub enum ElectionCompute { /// Result was forcefully computed on chain at the end of the session. OnChain, - /// Result was submitted and accepted to the chain. - Submitted, + /// Result was submitted and accepted to the chain via a signed transaction. + Signed, + /// Result was submitted by an authority (probably via an unsigned transaction) + Authority, } /// The result of an election round. @@ -992,8 +991,9 @@ decl_module! { ); debug::native::info!( target: "staking", - "detected a good block to trigger offchain election. Submission will \ + "detected a good block ({}) to trigger offchain election. Submission will \ be allowed from the next block.", + now, ); } } @@ -1003,12 +1003,14 @@ decl_module! { /// Check if the current block number is the one at which the election window has been set /// to open. If so, it runs the offchain worker code. fn offchain_worker(now: T::BlockNumber) { + debug::RuntimeLogger::init(); // runs only once. if Self::era_election_status() == ElectionStatus::::Open(now) { let _ = offchain_election::compute_offchain_election::().map_err(|e| { debug::native::warn!( target: "staking", - "{:?}", e + "Error in phragmen offchain worker call: {:?}", + e, ); }); } @@ -1061,16 +1063,22 @@ decl_module! { /// - O(N * E') to ensure nomination veracity. (E' is the average voter count per nominator /// and it is less than `MAX_NOMINATIONS`) /// # + #[weight = SimpleDispatchInfo::FixedNormal(10_000_000)] fn submit_election_solution( origin, winners: Vec, compact_assignments: CompactAssignments, ) { let _who = ensure_signed(origin)?; - Self::check_and_replace_solution(winners, compact_assignments)? + Self::check_and_replace_solution( + winners, + compact_assignments, + ElectionCompute::Signed + )? } - /// Unsigned version of `submit_election_solution` + /// Unsigned version of `submit_election_solution`. Will only be accepted from those who are + /// in the current validator set. fn submit_election_solution_unsigned( origin, winners: Vec, @@ -1081,7 +1089,11 @@ decl_module! { ) { ensure_none(origin)?; Encode::encode(&_signature); - Self::check_and_replace_solution(winners, compact_assignments)? + Self::check_and_replace_solution( + winners, + compact_assignments, + ElectionCompute::Authority + )? } /// Take the origin account as a stash and lock up `value` of its balance. `controller` will @@ -1615,6 +1627,7 @@ impl Module { fn check_and_replace_solution( winners: Vec, compact_assignments: CompactAssignments, + compute: ElectionCompute, ) -> Result<(), Error> { // discard early solutions match Self::era_election_status() { @@ -1760,9 +1773,9 @@ impl Module { ); >::put(ElectionResult { - compute: ElectionCompute::Submitted, elected_stashes: winners, score: submitted_score, + compute, exposures, slot_stake, }); @@ -1918,10 +1931,9 @@ impl Module { /// No storage item is updated. fn try_do_phragmen() -> Option>> { // a phragmen result from either a stored submission or locally executed one. - let somehow_phragmen_results = >::take() - .or_else(|| Self::do_phragmen_with_post_processing(ElectionCompute::OnChain)); - - somehow_phragmen_results + >::take().or_else(|| + Self::do_phragmen_with_post_processing(ElectionCompute::OnChain) + ) } /// Execute phragmen and return the new results. The edge weights are processed into support @@ -2309,9 +2321,15 @@ impl frame_support::unsigned::ValidateUnsigned for Module { validator_index, signature, ) = call { - // TODO: since unsigned is only for validators, and it is not increasing the block - // weight and fee etc. maybe we should only accept an unsigned solution when we don't - // have ANY solutions. Otherwise each block will get a LOT of these. + if let Some(queued_elected) = Self::queued_elected() { + if queued_elected.compute == ElectionCompute::Authority { + debug::native::debug!( + target: "staking", + "rejecting unsigned transaction because we already have one from an authority" + ); + return InvalidTransaction::Future.into(); + } + } // check signature let payload = (winners, compact, validator_index); @@ -2334,7 +2352,8 @@ impl frame_support::unsigned::ValidateUnsigned for Module { Ok(ValidTransaction { priority: TransactionPriority::max_value(), requires: vec![], - provides: vec![], + // TODO: what is a good value for this? + provides: vec![(Self::current_era(), validator_key).encode()], longevity: TryInto::::try_into(T::ElectionLookahead::get()).unwrap_or(150_u64), propagate: true, }) diff --git a/primitives/phragmen/Cargo.toml b/primitives/phragmen/Cargo.toml index fa1e7757bb624..6ccdc94b08389 100644 --- a/primitives/phragmen/Cargo.toml +++ b/primitives/phragmen/Cargo.toml @@ -5,13 +5,13 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0", optional = true, default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true, features = ["derive"] } sp-std = { version = "2.0.0", default-features = false, path = "../std" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } sp-phragmen-compact = { path = "./compact"} [dev-dependencies] -codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } sp-io ={ version = "2.0.0", path = "../../primitives/io" } rand = "0.7.3" @@ -19,7 +19,9 @@ sp-phragmen = { path = "." } [features] default = ["std"] +bench = [] std = [ + "codec/std", "serde", "sp-std/std", "sp-runtime/std", diff --git a/primitives/phragmen/benches/phragmen.rs b/primitives/phragmen/benches/phragmen.rs index 7bebda564e435..4d2aa2cabdce4 100644 --- a/primitives/phragmen/benches/phragmen.rs +++ b/primitives/phragmen/benches/phragmen.rs @@ -16,6 +16,7 @@ //! Note that execution times will not be accurate in an absolute scale, since //! - Everything is executed in the context of `TestExternalities` //! - Everything is executed in native environment. + #![cfg(feature = "bench")] #![feature(test)] diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index c9be33f2ab809..9c499dbfaaa15 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -144,7 +144,7 @@ fn struct_def( }).collect::(); let compact_def = quote! ( - /// A struct to encode a `Vec` or `Vec` of the phragmen module + /// A struct to encode a `Vec` or `Vec<_phragmen::Assignment>` of the phragmen module /// in a compact way. #[derive(Default, PartialEq, Eq, Clone, _sp_runtime::RuntimeDebug, _codec::Encode, _codec::Decode)] #vis struct #ident<#account_type, #weight_type> { @@ -214,14 +214,14 @@ fn convert_impl_for_assignment( let from_impl = quote!( impl<#account_type: _codec::Codec + Default + Clone> - From>> - for #ident<#account_type, Perbill> + From>> + for #ident<#account_type, _sp_runtime::Perbill> { fn from( - assignments: Vec>, + assignments: Vec<_phragmen::Assignment<#account_type>>, ) -> Self { - let mut compact: #ident<#account_type, Perbill> = Default::default(); - assignments.into_iter().for_each(|Assignment { who, distribution } | { + let mut compact: #ident<#account_type, _sp_runtime::Perbill> = Default::default(); + assignments.into_iter().for_each(|_phragmen::Assignment { who, distribution } | { match distribution.len() { #from_impl_single #from_impl_double @@ -240,9 +240,9 @@ fn convert_impl_for_assignment( let name = field_name_for(1); quote!( for (who, target) in self.#name { - assignments.push(Assignment { + assignments.push(_phragmen::Assignment { who, - distribution: vec![(target, Perbill::one())], + distribution: vec![(target, _sp_runtime::Perbill::one())], }) } ) @@ -251,8 +251,8 @@ fn convert_impl_for_assignment( let name = field_name_for(2); quote!( for (who, (t1, p1), t2) in self.#name { - let p2 = _sp_runtime::traits::Saturating::saturating_sub(Perbill::one(), p1); - assignments.push( Assignment { + let p2 = _sp_runtime::traits::Saturating::saturating_sub(_sp_runtime::Perbill::one(), p1); + assignments.push( _phragmen::Assignment { who, distribution: vec![ (t1, p1), @@ -266,18 +266,18 @@ fn convert_impl_for_assignment( let name = field_name_for(c); quote!( for (who, inners, t_last) in self.#name { - let mut sum = Perbill::zero(); + let mut sum = _sp_runtime::Perbill::zero(); let mut inners_parsed = inners .into_iter() .map(|(ref c, p)| { sum = _sp_runtime::traits::Saturating::saturating_add(sum, *p); (c.clone(), *p) - }).collect::>(); + }).collect::>(); - let p_last = _sp_runtime::traits::Saturating::saturating_sub(Perbill::one(), sum); + let p_last = _sp_runtime::traits::Saturating::saturating_sub(_sp_runtime::Perbill::one(), sum); inners_parsed.push((t_last, p_last)); - assignments.push(Assignment { + assignments.push(_phragmen::Assignment { who, distribution: inners_parsed, }); @@ -287,11 +287,11 @@ fn convert_impl_for_assignment( let into_impl = quote!( impl<#account_type: _codec::Codec + Default + Clone> - Into>> - for #ident<#account_type, Perbill> + Into>> + for #ident<#account_type, _sp_runtime::Perbill> { - fn into(self) -> Vec> { - let mut assignments: Vec> = Default::default(); + fn into(self) -> Vec<_phragmen::Assignment<#account_type>> { + let mut assignments: Vec<_phragmen::Assignment<#account_type>> = Default::default(); #into_impl_single #into_impl_double #into_impl_rest diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 77a8a6c41c9de..117a8c6de8356 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -41,6 +41,10 @@ use sp_runtime::traits::{Zero, Convert, Member, SimpleArithmetic, Saturating, Bo mod mock; #[cfg(test)] mod tests; +#[cfg(feature = "std")] +use serde::{Serialize, Deserialize}; +#[cfg(feature = "std")] +use codec::{Encode, Decode}; mod node; mod reduce; @@ -120,7 +124,7 @@ pub struct PhragmenResult { /// A voter's stake assignment among a set of targets, represented as ratios. #[derive(RuntimeDebug, Clone)] -#[cfg_attr(feature = "std", derive(PartialEq, Eq))] +#[cfg_attr(feature = "std", derive(PartialEq, Eq, Encode, Decode))] pub struct Assignment { /// Voter's identifier pub who: AccountId, @@ -150,7 +154,7 @@ impl Assignment { /// A voter's stake assignment among a set of targets, represented as absolute values in the scale /// of [`ExtendedBalance`]. #[derive(RuntimeDebug, Clone)] -#[cfg_attr(feature = "std", derive(PartialEq, Eq))] +#[cfg_attr(feature = "std", derive(PartialEq, Eq, Encode, Decode))] pub struct StakedAssignment { /// Voter's identifier pub who: AccountId, @@ -166,7 +170,7 @@ pub struct StakedAssignment { /// This, at the current version, resembles the `Exposure` defined in the staking SRML module, yet /// they do not necessarily have to be the same. #[derive(Default, RuntimeDebug)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize, Eq, PartialEq))] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Eq, PartialEq))] pub struct Support { /// Total support. pub total: ExtendedBalance, diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index 19b442e24711b..3a0e5dab7eb89 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -116,16 +116,24 @@ impl Node { /// vector of Nodes leading to the root. Hence the first element is the start itself and the /// last one is the root. As convenient, the root itself is also returned as the first element /// of the tuple. + /// + /// This function detects cycles and breaks as soon a duplicate node is visited, returning the + /// cycle up to but not including the duplicate node. + /// + /// If you are certain that no cycles exist, you can use [`root_unchecked`]. pub fn root(start: &NodeRef) -> (NodeRef, Vec>) { let mut parent_path: Vec> = Vec::new(); - let initial = start.clone(); + let mut visited: Vec> = Vec::new(); + parent_path.push(start.clone()); + visited.push(start.clone()); let mut current = start.clone(); while let Some(ref next_parent) = current.clone().borrow().parent { - if initial == next_parent.clone() { break; } + if visited.contains(next_parent) { break; } parent_path.push(next_parent.clone()); current = next_parent.clone(); + visited.push(current.clone()); } (current, parent_path) @@ -234,6 +242,24 @@ mod tests { assert_eq!(path.clone(), vec![a.clone(), b.clone(), c.clone()]); } + #[test] + fn get_root_on_cycle_2() { + // A ---> B + // | | | + // - C + let a = Node::new(id(1)).into_ref(); + let b = Node::new(id(2)).into_ref(); + let c = Node::new(id(3)).into_ref(); + + Node::set_parent_of(&a, &b); + Node::set_parent_of(&b, &c); + Node::set_parent_of(&c, &b); + + let (root, path) = Node::root(&a); + assert_eq!(root, c); + assert_eq!(path.clone(), vec![a.clone(), b.clone(), c.clone()]); + } + #[test] fn node_cmp_stack_overflows_on_non_unique_elements() { // To make sure we don't stack overflow on duplicate who. This needs manual impl of diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index e585a014c92a2..73637e1cdc476 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -273,18 +273,18 @@ fn reduce_4( let voter = if i < 2 { who.clone() } else { other_who.clone() }; assignments.iter_mut().filter(|a| a.who == voter).for_each(|ass| { ass.distribution - .iter_mut() - .position(|(t, _)| *t == cycle[i].0) - .map(|idx| { - let next_value = ass.distribution[idx].1.saturating_sub(min_value); - if next_value.is_zero() { - ass.distribution.remove(idx); - remove_indices.push(i); - num_changed += 1; - } else { - ass.distribution[idx].1 = next_value; - } - }); + .iter_mut() + .position(|(t, _)| *t == cycle[i].0) + .map(|idx| { + let next_value = ass.distribution[idx].1.saturating_sub(min_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + remove_indices.push(i); + num_changed += 1; + } else { + ass.distribution[idx].1 = next_value; + } + }); }); }); @@ -394,9 +394,9 @@ fn reduce_all( // NOTE: the order of chaining is important! it is always build from [target, ..., // voter] let cycle = - target_root_path.iter().take(target_root_path.len() - common_count + 1).cloned() - .chain(voter_root_path.iter().take(voter_root_path.len() - common_count).rev().cloned()) - .collect::>>(); + target_root_path.iter().take(target_root_path.len() - common_count + 1).cloned() + .chain(voter_root_path.iter().take(voter_root_path.len() - common_count).rev().cloned()) + .collect::>>(); // a cycle's length shall always be multiple of two. #[cfg(feature = "std")] debug_assert_eq!(cycle.len() % 2, 0); From 2ed0a1d90664f704c716c09531fb28f3ff39d8b3 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 3 Feb 2020 09:05:40 +0100 Subject: [PATCH 033/106] fix more merge stuff --- Cargo.lock | 4 ++-- frame/executive/src/lib.rs | 2 +- frame/staking/src/lib.rs | 7 +------ frame/staking/src/tests.rs | 8 ++++---- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be72ffd5ce109..2634a25c22a7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6608,9 +6608,9 @@ name = "sp-phragmen-compact" version = "2.0.0" dependencies = [ "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 936da70211c42..93db1418561ec 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -357,7 +357,7 @@ mod tests { use sp_core::H256; use sp_runtime::{ generic::Era, Perbill, DispatchError, testing::{Digest, Header, Block}, - traits::{Bounded, Header as HeaderT, BlakeTwo256, IdentityLookup, ConvertInto}, + traits::{Header as HeaderT, BlakeTwo256, IdentityLookup, ConvertInto}, transaction_validity::{InvalidTransaction, UnknownTransaction, TransactionValidityError}, }; use frame_support::{ diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index ce38e3c9076a3..8e0894f45ceb6 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -263,7 +263,7 @@ use frame_support::{ weights::SimpleDispatchInfo, traits::{ Currency, LockIdentifier, LockableCurrency, WithdrawReasons, OnUnbalanced, Imbalance, Get, - Time, EstimateNextSessionChange, + Time, EstimateNextSessionChange, OnReapAccount, } }; use pallet_session::historical; @@ -289,12 +289,7 @@ use frame_system::{ offchain::SubmitUnsignedTransaction, }; -<<<<<<< HEAD use sp_phragmen::{ExtendedBalance, StakedAssignment, generate_compact_solution_type}; -======= -use sp_phragmen::ExtendedBalance; -use frame_support::traits::OnReapAccount; ->>>>>>> de2ffd937db78093efaa58d7cc08f5599a8f4728 const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; // ------------- IMPORTANT NOTE: must be the same as `generate_compact_solution_type`. diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index f0204365ce819..5d6fb85ddd16a 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2882,14 +2882,14 @@ mod offchain_phragmen { assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact)); let queued_result = Staking::queued_elected().unwrap(); - assert_eq!(queued_result.compute, ElectionCompute::Submitted); + assert_eq!(queued_result.compute, ElectionCompute::Signed); run_to_block(15); assert_eq!(Staking::era_election_status(), ElectionStatus::None); assert_eq!( System::events()[3].event, - MetaEvent::staking(RawEvent::StakingElection(ElectionCompute::Submitted)), + MetaEvent::staking(RawEvent::StakingElection(ElectionCompute::Signed)), ); }) } @@ -2909,14 +2909,14 @@ mod offchain_phragmen { assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact)); let queued_result = Staking::queued_elected().unwrap(); - assert_eq!(queued_result.compute, ElectionCompute::Submitted); + assert_eq!(queued_result.compute, ElectionCompute::Signed); run_to_block(15); assert_eq!(Staking::era_election_status(), ElectionStatus::None); assert_eq!( System::events()[3].event, - MetaEvent::staking(RawEvent::StakingElection(ElectionCompute::Submitted)), + MetaEvent::staking(RawEvent::StakingElection(ElectionCompute::Signed)), ); }) } From 6bef2d944fbcf6c670b50d2b4fee6714bf35c2c8 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 5 Feb 2020 18:30:23 +0100 Subject: [PATCH 034/106] Most tests work again. --- frame/staking/src/lib.rs | 179 +++++++++++++++---------- frame/staking/src/mock.rs | 32 +++-- frame/staking/src/offchain_election.rs | 14 +- frame/staking/src/tests.rs | 175 ++++++++++++++---------- primitives/phragmen/compact/src/lib.rs | 49 +++++-- primitives/phragmen/src/lib.rs | 26 +++- primitives/phragmen/src/tests.rs | 107 ++++++++++++--- 7 files changed, 394 insertions(+), 188 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 8e0894f45ceb6..7e66359e1daa1 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -576,8 +576,6 @@ pub struct ElectionResult { elected_stashes: Vec, /// Flat list of new exposures, to be updated in the [`Exposure`] storage. exposures: Vec<(AccountId, Exposure)>, - /// The score of the result based on [`sp_phragmen::evaluate_support`]. - score: [ExtendedBalance; 3], } /// The status of the upcoming (offchain) election. @@ -595,6 +593,8 @@ impl Default for ElectionStatus { } } +pub type ElectionScore = [ExtendedBalance; 3]; + pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type PositiveImbalanceOf = @@ -721,8 +721,6 @@ pub trait Trait: frame_system::Trait { type SubmitTransaction: SubmitUnsignedTransaction::Call>; /// Key type used to sign and verify transaction. - // TODO: this can be fetched from `SubmitTransaction` as well, but for now both me and @todr - // gave up. type KeyType: RuntimeAppPublic + Member + Parameter; } @@ -793,6 +791,9 @@ decl_storage! { /// is executed. pub QueuedElected get(fn queued_elected): Option>>; + /// The score of the current [`QueuedElected`]. + pub QueuedScore get(fn queued_score): Option; + /// Flag to control the execution of the offchain election. pub EraElectionStatus get(fn era_election_status): ElectionStatus; @@ -950,6 +951,8 @@ decl_error! { PhragmenBogusNomination, /// A self vote must only be originated from a validator to ONLY themselves. PhragmenBogusSelfVote, + /// The claimed score does not match with the one computed from the data. + PhragmenBogusScore, } } @@ -991,8 +994,7 @@ decl_module! { ); debug::native::info!( target: "staking", - "detected a good block ({}) to trigger offchain election. Submission will \ - be allowed from the next block.", + "Election window is Open({:?})", now, ); } @@ -1006,13 +1008,13 @@ decl_module! { debug::RuntimeLogger::init(); // runs only once. if Self::era_election_status() == ElectionStatus::::Open(now) { - let _ = offchain_election::compute_offchain_election::().map_err(|e| { + if let Err(e) = offchain_election::compute_offchain_election::() { debug::native::warn!( target: "staking", "Error in phragmen offchain worker call: {:?}", e, ); - }); + }; } } @@ -1024,75 +1026,100 @@ decl_module! { } /// Submit a phragmen result to the chain. If the solution: + /// /// 1. is valid /// 2. has a better score than a potentially existing solution on chain /// /// it will replace it. /// /// A solution consists of two pieces of data: + /// /// 1. `winners`: a flat vector of all the winners of the round. /// 2. `assignments`: the compact version of an assignment vector that encodes the edge /// weights. /// /// Both of which may be computed using the [`phragmen`], or any other algorithm. /// - /// A solution is valid if + /// Additionally, the submitter must provide: + /// + /// 1. The score that they claim their solution has. + /// 2. The block number at which the solution is computed. + /// + /// A solution is valid if: + /// /// 1. All the presented winners are actually on-chain candidates. /// 2. All the presented edges contain: /// - a voter who is an on-chain nominator. - /// - a target who is an on-chain candidate. + /// - a target who is an on-chain candidate and among the presented winners. /// - that target must actually be among the nominations of the voter. + /// 3. For all the presented nominators, their total stake must actually add up to their + /// staked amount on chain. This does not need to be calculated. Converting compact -> + /// staked will assure it. + /// 4. The solution must have been submitted when [`ElectionStatus`] is `Open`. + /// 5. The claimed score is valid, and, if an on-chain solution exists, it is better than + /// the on on-chain, as defined by [`is_score_better`]. /// /// A solutions score is consisted of 3 parameters: + /// /// 1. `min { support.total }` for each support of a winner. This value should be maximized. /// 2. `sum { support.total }` for each support of a winner. This value should be minimized. /// 3. `sum { support.total^2 }` for each support of a winner. This value should be /// minimized (to ensure less variance) /// /// # - /// major steps: - /// - decode from compact: E(E) + /// E: number of edges. + /// m: size of winner committee. + /// n: number of nominators. + /// d: edge degree aka MAX_NOMINATIONS = 16 + /// + /// major steps (all done in `check_and_replace_solution`): + /// + /// - 1 read. `ElectionScore`. O(1). + /// - `m` calls `Validators::exists()` for validator veracity. + /// + /// - decode from compact and convert into assignments: O(E) TODO: if our reduce is correct, this will be at most `n + m` /// - build_support_map: O(E) - /// - evaluate_support: E(E) - /// - 1 read which decodes a `ElectionResult` from storage. - /// - 1 read which decodes a `ElectionStatus` from storage. - /// - `2*W` reads each of which decodes an `AccountId` and a `ValidatorPrefs` to ensure - /// winner veracity. - /// - N reads from `Bonded`, `Validators` and `Nominators` to ensure nominator veracity. N - /// is the number of nominators. - /// - O(N * E') to ensure nomination veracity. (E' is the average voter count per nominator - /// and it is less than `MAX_NOMINATIONS`) + /// - evaluate_support: O(E) + /// + /// - `n` reads from [`Bonded`], [`Ledger`] and [`Nominators`], and `n` calls to + /// `Validators::exists()` to ensure nominator veracity. + /// + /// - O(N * d) to ensure vote veracity. /// # #[weight = SimpleDispatchInfo::FixedNormal(10_000_000)] fn submit_election_solution( origin, winners: Vec, compact_assignments: CompactAssignments, + score: ElectionScore, ) { let _who = ensure_signed(origin)?; Self::check_and_replace_solution( winners, compact_assignments, - ElectionCompute::Signed + ElectionCompute::Signed, + score, )? } /// Unsigned version of `submit_election_solution`. Will only be accepted from those who are /// in the current validator set. + // TODO: weight should be noted. fn submit_election_solution_unsigned( origin, winners: Vec, compact_assignments: CompactAssignments, - // already used and checked. + score: ElectionScore, + // already used and checked in ValidateUnsigned. _validator_index: u32, _signature: ::Signature, ) { ensure_none(origin)?; - Encode::encode(&_signature); Self::check_and_replace_solution( winners, compact_assignments, - ElectionCompute::Authority + ElectionCompute::Authority, + score, )? } @@ -1627,6 +1654,8 @@ impl Module { winners: Vec, compact_assignments: CompactAssignments, compute: ElectionCompute, + claimed_score: ElectionScore, + // at: T::BlockNumber, ) -> Result<(), Error> { // discard early solutions match Self::era_election_status() { @@ -1634,48 +1663,43 @@ impl Module { ElectionStatus::Open(_) => { /* Open and no solutions received. Allowed. */ }, } + // assume the given score is valid. Is it better than what we have on-chain, if we have any? + if let Some(queued_score) = Self::queued_score() { + ensure!( + offchain_election::is_score_better(queued_score, claimed_score), + Error::::PhragmenWeakSubmission, + ) + } + + // check if all winners were legit; this is rather cheap. + for w in winners.iter() { + ensure!( + >::exists(&w), + Error::::PhragmenBogusWinner, + ) + } + // convert into staked. let staked_assignments = compact_assignments.into_staked::< _, _, T::CurrencyToVote, - >(Self::slashable_balance_of); + >(Self::slashable_balance_of).map_err(|_| Error::::PhragmenBogusNominatorStake)?; // build the support map thereof in order to evaluate. // OPTIMIZATION: we could merge this with the `staked_assignments.iter()` below and // iterate only once. - let (supports, num_error) = sp_phragmen::build_support_map::, T::AccountId>( + let (supports, num_error) = sp_phragmen::build_support_map::( &winners, &staked_assignments, ); - + // This technically checks that all targets in all nominators were among the winners. ensure!(num_error == 0, Error::::PhragmenBogusEdge); - // score the result. Go further only if it is better than what we already have. - let submitted_score = sp_phragmen::evaluate_support(&supports); - if let Some(ElectionResult::> { - score, - .. - }) = Self::queued_elected() { - // OPTIMIZATION: we can read first the score and then the rest to do less decoding. - // if the local score is better in any of the three parameters. - ensure!( - offchain_election::is_score_better(score, submitted_score), - Error::::PhragmenWeakSubmission, - ) - } - - // Either the result is better than the one on chain, or we don't have an any on chain. - // do the sanity check. Each claimed edge must exist. Also, each claimed winner must - // have a support associated. - // check all winners being actual validators. - for w in winners.iter() { - ensure!( - >::exists(&w), - Error::::PhragmenBogusWinner, - ) - } + // Check if the score is the same as the claimed one. + let submitted_score = sp_phragmen::evaluate_support(&supports); + ensure!(submitted_score == claimed_score, Error::::PhragmenBogusScore); // check all nominators being bonded, and actually including the claimed vote, and // summing up to their ledger stake. @@ -1707,18 +1731,14 @@ impl Module { "exactly one of maybe_validator and maybe_nomination is true. \ is_validator is false; maybe_nomination is some; qed" ); - let mut total_stake: ExtendedBalance = Zero::zero(); + // NOTE: we don't really have to check here if the sum of all edges are teh + // nominator budged. By definition, compact -> staked ensures this. ensure!( - distribution.into_iter().all(|(v, w)| { - total_stake += w; - nomination.targets.iter().find(|t| *t == v).is_some() + distribution.into_iter().all(|(t, _)| { + nomination.targets.iter().find(|&tt| tt == t).is_some() }), Error::::PhragmenBogusNomination, ); - ensure!( - total_stake == active_extended_stake, - Error::::PhragmenBogusNominatorStake - ); } else { // a self vote ensure!(distribution.len() == 1, Error::::PhragmenBogusSelfVote); @@ -1773,11 +1793,11 @@ impl Module { >::put(ElectionResult { elected_stashes: winners, - score: submitted_score, compute, exposures, slot_stake, }); + QueuedScore::put(submitted_score); Ok(()) } @@ -1882,18 +1902,22 @@ impl Module { } /// Runs [`try_do_phragmen`] and updates the following storage items: - /// - [`Stakers`] - /// - [`SlotStake`] - /// - [`CurrentElected] + /// - [`Stakers`]: with the new staker set. + /// - [`SlotStake`]: with the new slot stake. + /// - [`CurrentElected`]: with the new elected set. + /// - [`EraElectionStatus`]: with `None` + /// + /// Internally, [`QueuedElected`] and [`QueuedScore`] are also consumed. + /// + /// If the election has been successful, It passes the new set upwards. /// - /// If the election has been successful. It passes the new set upwards. + /// This should only be called at the end of an era. fn select_and_update_validators() -> Option> { if let Some(ElectionResult::> { elected_stashes, exposures, slot_stake, compute, - .. // TODO: this means that we are never storing the score of a winning chain. It this okay? }) = Self::try_do_phragmen() { // We have chosen the new validator set. Submission is no longer allowed. >::put(ElectionStatus::None); @@ -1927,12 +1951,18 @@ impl Module { /// Select a new validator set from the assembled stakers and their role preferences. It tries /// first to peek into [`QueuedElected`]. Otherwise, it runs a new phragmen. /// - /// No storage item is updated. + /// If [`QueuedElected`] and [`QueuedScore`] exists, they are both removed. No further storage + /// is updated. fn try_do_phragmen() -> Option>> { // a phragmen result from either a stored submission or locally executed one. - >::take().or_else(|| + let next_result = >::take().or_else(|| Self::do_phragmen_with_post_processing(ElectionCompute::OnChain) - ) + ); + + // either way, kill this + QueuedScore::kill(); + + next_result } /// Execute phragmen and return the new results. The edge weights are processed into support @@ -1948,10 +1978,10 @@ impl Module { let staked_assignments: Vec> = assignments .into_iter() - .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::slashable_balance_of)) + .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::slashable_balance_of, true)) .collect(); - let (supports, _) = sp_phragmen::build_support_map::, T::AccountId>( + let (supports, _) = sp_phragmen::build_support_map::( &elected_stashes, &staked_assignments, ); @@ -1961,7 +1991,6 @@ impl Module { >::remove(v); } - let score = sp_phragmen::evaluate_support(&supports); let to_balance = |e: ExtendedBalance| >>::convert(e); @@ -2008,7 +2037,6 @@ impl Module { slot_stake, exposures, compute, - score, }) } else { // There were not enough candidates for even our minimal level of functionality. @@ -2317,9 +2345,12 @@ impl frame_support::unsigned::ValidateUnsigned for Module { if let Call::submit_election_solution_unsigned( winners, compact, + score, validator_index, signature, ) = call { + // TODO: Double check if this is a good idea. I have to make sure that the offchain code + // is the same for everyone. if let Some(queued_elected) = Self::queued_elected() { if queued_elected.compute == ElectionCompute::Authority { debug::native::debug!( @@ -2331,7 +2362,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { } // check signature - let payload = (winners, compact, validator_index); + let payload = (winners, compact, score, validator_index); let current_validators = T::SessionInterface::keys::(); let (_, validator_key) = current_validators.get(*validator_index as usize) .ok_or(TransactionValidityError::Invalid(InvalidTransaction::Custom(0u8).into()))?; diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 21069b6338385..3121b7373025b 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -741,6 +741,7 @@ pub fn horrible_phragmen_with_post_processing( ) -> ( CompactAssignments, Vec, + ElectionScore, ) { use std::collections::BTreeMap; @@ -809,34 +810,33 @@ pub fn horrible_phragmen_with_post_processing( // Ensure that this result is worse than seq-phragmen. Otherwise, it should not have been used // for testing. - { - let (better_compact, better_winners) = do_phragmen_with_post_processing(true); - let better_assignments = better_compact - .into_staked::<_, _, CurrencyToVoteHandler>(Staking::slashable_balance_of); - - let support = build_support_map::(&winners, &assignments).0; - let better_support = - build_support_map::(&better_winners, &better_assignments).0; + let score = { + let (_, _, better_score) = do_phragmen_with_post_processing(true, |_| {}); + let support = build_support_map::(&winners, &assignments).0; let score = evaluate_support(&support); - let better_score = evaluate_support(&better_support); assert!(offchain_election::is_score_better(score, better_score)); - } + + score + }; if do_reduce { reduce(&mut assignments); } + let compact = >::from_staked(assignments); - (compact, winners) + (compact, winners, score) } pub fn do_phragmen_with_post_processing( do_reduce: bool, + tweak: impl FnOnce(&mut Vec>), ) -> ( CompactAssignments, Vec, + ElectionScore, ) { // run phragmen on the default stuff. let sp_phragmen::PhragmenResult { @@ -847,16 +847,22 @@ pub fn do_phragmen_with_post_processing( let mut staked: Vec> = assignments .into_iter() - .map(|a| a.into_staked::<_, _, CurrencyToVoteHandler>(Staking::slashable_balance_of)) + .map(|a| a.into_staked::<_, _, CurrencyToVoteHandler>(Staking::slashable_balance_of, true)) .collect(); + // apply custom tweaks. awesome for testing. + tweak(&mut staked); + if do_reduce { reduce(&mut staked); } + let (support_map, _) = build_support_map::(&winners, &staked); + let score = evaluate_support::(&support_map); + let compact = >::from_staked(staked); - (compact, winners) + (compact, winners, score) } #[macro_export] diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index d38952475fcb9..f6faa027bd623 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -37,6 +37,8 @@ pub(crate) enum OffchainElectionError { /// better `this`. /// /// Evaluation is done in a lexicographic manner. +/// +/// Note that the third component should be minimized. pub(crate) fn is_score_better(this: [ExtendedBalance; 3], that: [ExtendedBalance; 3]) -> bool { match that .iter() @@ -80,12 +82,19 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti // convert into staked. This is needed to be able to reduce. let mut staked: Vec> = assignments .into_iter() - .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(>::slashable_balance_of)) + .map(|a| a.into_staked::<_, _, T::CurrencyToVote>( + >::slashable_balance_of, + true, + )) .collect(); // reduce the assignments. This will remove some additional edges. reduce(&mut staked); + // get support and score. + let (support, _) = sp_phragmen::build_support_map::(&winners, &staked); + let score = sp_phragmen::evaluate_support(&support); + // compact encode the assignment. let compact = >::from_staked(staked); @@ -97,11 +106,12 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti #[cfg(not(feature = "signed"))] { let signature_payload = - (winners.clone(), compact.clone(), index as u32).encode(); + (winners.clone(), compact.clone(), score, index as u32).encode(); let signature = pubkey.sign(&signature_payload).unwrap(); let call: ::Call = Call::submit_election_solution_unsigned( winners, compact, + score, index as u32, signature, ) diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 5d6fb85ddd16a..6aaea5d7fd512 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2748,25 +2748,16 @@ mod offchain_phragmen { } ]; - let compact = >::from_staked(malicious_assignments); + let compact = >::from_staked( + malicious_assignments, + ); assert_eq!(compact.votes3[0], (1, [(10, 5), (20, 8)], 30)); - // converting this back will yield no stake for 0 - let fixed_assignments = compact.into_staked::<_, _, CurrencyToVoteHandler>(&stake_of); - - assert_eq!( - fixed_assignments, - vec![ - StakedAssignment { - who: 1, - distribution: vec![(10, 5), (20, 8), (30, 0)], - } - ] + // converting this back will yield error. This must be provided by the compact lib code. + assert!( + compact.into_staked::<_, _, CurrencyToVoteHandler>(&stake_of).is_err() ); - - // Note: this is still wrong and sums to 13, but submitting it will yield an error - // anyway. }) } @@ -2878,8 +2869,13 @@ mod offchain_phragmen { run_to_block(12); assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); - let (compact, winners) = do_phragmen_with_post_processing(true); - assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact)); + let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + assert_ok!(Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + )); let queued_result = Staking::queued_elected().unwrap(); assert_eq!(queued_result.compute, ElectionCompute::Signed); @@ -2905,8 +2901,15 @@ mod offchain_phragmen { run_to_block(14); assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); - let (compact, winners) = do_phragmen_with_post_processing(true); - assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact)); + let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + assert_ok!( + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + ) + ); let queued_result = Staking::queued_elected().unwrap(); assert_eq!(queued_result.compute, ElectionCompute::Signed); @@ -2934,9 +2937,9 @@ mod offchain_phragmen { run_to_block(11); // submission is allowed assert_eq!(Staking::era_election_status(), ElectionStatus::None); - let (compact, winners) = do_phragmen_with_post_processing(true); + let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact), + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenEarlySubmission, ); }) @@ -2956,13 +2959,13 @@ mod offchain_phragmen { run_to_block(12); // a good solution - let (compact, winners) = do_phragmen_with_post_processing(true); - assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact)); + let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); // a bad solution - let (compact, winners) = horrible_phragmen_with_post_processing(false); + let (compact, winners, score) = horrible_phragmen_with_post_processing(false); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact), + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenWeakSubmission, ); }) @@ -2982,12 +2985,12 @@ mod offchain_phragmen { run_to_block(12); // a meeeeh solution - let (compact, winners) = horrible_phragmen_with_post_processing(false); - assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact)); + let (compact, winners, score) = horrible_phragmen_with_post_processing(false); + assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); // a better solution - let (compact, winners) = do_phragmen_with_post_processing(true); - assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact)); + let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); }) } @@ -3017,11 +3020,9 @@ mod offchain_phragmen { let call = extrinsic.1; match call { - mock::Call::Staking(crate::Call::submit_election_solution(_, _)) => {}, + mock::Call::Staking(crate::Call::submit_election_solution(_, _, _)) => {}, _ => panic!("wrong call submitted"), }; - - // TODO: dispatch the call }) } @@ -3051,16 +3052,16 @@ mod offchain_phragmen { let call = extrinsic.1; let inner = match call { mock::Call::Staking(inner) => { inner }, - _ => panic!("wrong call submitted"), }; // pass this call to ValidateUnsigned + let signing_key = dummy_sr25519::dummy_key_for(11); assert_eq!( ::validate_unsigned(&inner), TransactionValidity::Ok(ValidTransaction { priority: TransactionPriority::max_value(), requires: vec![], - provides: vec![], + provides: vec![(Staking::current_era(), signing_key).encode()], longevity: TryInto::::try_into( ::ElectionLookahead::get() ).unwrap_or(150_u64), @@ -3084,14 +3085,14 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (mut compact, winners) = do_phragmen_with_post_processing(false); + let (mut compact, winners, scores) = do_phragmen_with_post_processing(true, |_| {}); assert_eq_uvec!(winners, vec![10, 20, 30, 40]); // inject a correct nominator voting for a correct, but non-winner validator. compact.votes1.push((1, 999)); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact), + Staking::submit_election_solution(Origin::signed(10), winners, compact, scores), Error::::PhragmenBogusEdge, ); }) @@ -3112,11 +3113,11 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (compact, mut winners) = do_phragmen_with_post_processing(false); + let (compact, mut winners, score) = do_phragmen_with_post_processing(true, |_| {}); winners.push(999); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact), + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenBogusWinner, ); }) @@ -3148,9 +3149,12 @@ mod offchain_phragmen { let mut staked: Vec> = assignments .into_iter() - .map(|a| a.into_staked::<_, _, CurrencyToVoteHandler>(Staking::slashable_balance_of)) + .map(|a| a.into_staked::<_, _, CurrencyToVoteHandler>(Staking::slashable_balance_of, true)) .collect(); + let (support, _) = sp_phragmen::build_support_map::(&winners, &staked); + let score = sp_phragmen::evaluate_support::(&support); + staked.push(sp_phragmen::StakedAssignment { who: 666, distribution: vec![(999, 1)] }); winners.push(999); @@ -3158,7 +3162,7 @@ mod offchain_phragmen { let compact = >::from_staked(staked); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact), + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenBogusWinner, ); }) @@ -3178,14 +3182,20 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (mut compact, winners) = do_phragmen_with_post_processing(false); - - // inject a wrong nominator voting for a valid validator. - compact.votes1.push((999, 10)); + let (compact, winners, score) = do_phragmen_with_post_processing(false, |a| { + // inject a wrong nominator voting for a valid validator. + a.push(StakedAssignment:: { who: 999, distribution: vec![(10, 10)]}); + }); + // This will cause a score mismatch since 99 doesn't have any money. assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners.clone(), compact.clone()), - Error::::PhragmenBogusNominator, + Staking::submit_election_solution( + Origin::signed(10), + winners.clone(), + compact.clone(), + score, + ), + Error::::PhragmenBogusScore, // TODO: we can catch this in PhragmenBogusNominatorStake. ); // even when it is bonded but not nominating. @@ -3193,7 +3203,7 @@ mod offchain_phragmen { assert_ok!(Staking::bond(Origin::signed(999), 998, 10, Default::default())); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact), + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenBogusNominator, ); }) @@ -3212,14 +3222,16 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (mut compact, winners) = do_phragmen_with_post_processing(false); - - // mutate a self vote to target someone else. - compact.votes1.iter_mut().find(|x| x.0 == 10).map(|(_voter, target)| *target = 20); - dbg!(&compact); + let (compact, winners, score) = do_phragmen_with_post_processing(true, |a| { + // mutate a self vote to target someone else. That someone else is still among the + // winners + a.iter_mut().find(|x| x.who == 10).map(|x| + x.distribution.iter_mut().find(|y| y.0 == 10).map(|y| y.0 = 20) + ); + }); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact), + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenBogusSelfVote, ); }) @@ -3238,16 +3250,16 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (mut compact, winners) = do_phragmen_with_post_processing(false); - - // mutate a self vote to target someone else. - compact.votes1.retain(|x| x.0 != 10); - - // add 10 as a double vote - compact.votes2.push((10, (10, 5), 20)); + let (compact, winners, score) = do_phragmen_with_post_processing(true, |a| { + // Remove the self vote. + a.retain(|x| x.who != 10); + // add is as a new double vote + a.push(StakedAssignment { who: 10, distribution: vec![(10, 50), (20, 50)]}); + }); + // This raises score issue. assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact), + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenBogusSelfVote, ); }) @@ -3266,13 +3278,13 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (mut compact, winners) = do_phragmen_with_post_processing(false); + let (mut compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); // 3 has: (3, (20, 50), 40) which means evenly distributed between 20 and 40 (50 each). compact.votes2.iter_mut().find(|x| x.0 == 3).map(|(_who, v1, _v2)| v1.1 = 120); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact), + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenBogusNominatorStake, ); }) @@ -3300,18 +3312,43 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (mut compact, winners) = do_phragmen_with_post_processing(false); - - // 3 has: (3, (20, 50), 40). We add a fake vote to 30. - compact.votes2.retain(|x| x.0 != 3); - compact.votes3.push((3, [(20, 50), (40, 30)], 30)); + let (compact, winners, score) = do_phragmen_with_post_processing(false, |a| { + // 3 has: (3, (20, 50), 40). We add a fake vote to 30. The stake sum is still + // correctly 100. + a.iter_mut().find(|x| x.who == 3).map(|x| { + x.distribution = vec![(20, 50), (40, 30), (30, 20)] + }); + }); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact), + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenBogusNomination, ); }) } + + #[test] + fn invalid_phragmen_result_wrong_score() { + // A valid voter who's total distributed stake is more than what they bond + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + let (compact, winners, mut score) = do_phragmen_with_post_processing(true, |_| {}); + score[0] += 1; + + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusScore, + ); + }) + } } #[test] diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index 9c499dbfaaa15..b0592b84bb9b8 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -27,6 +27,8 @@ use syn::{GenericArgument, Type, parse::{Parse, ParseStream, Result}}; // prefix used for struct fields in compact. const PREFIX: &'static str = "votes"; +// TODO: what to do if additions overflow in into staked. + /// Generates a struct to store the phragmen assignments in a compact way. The struct can only store /// distributions up to the given input count. The given count must be greater than 2. /// @@ -179,11 +181,19 @@ fn imports() -> Result { } Err(e) => return Err(syn::Error::new(Span::call_site(), &e)), }; + let sp_std_imports = match crate_name("sp-std") { + Ok(sp_std) => { + let ident = syn::Ident::new(&sp_std, Span::call_site()); + quote!( extern crate #ident as _sp_std; ) + } + Err(e) => return Err(syn::Error::new(Span::call_site(), &e)), + }; Ok(quote!( #runtime_imports #codec_imports #sp_phragmen_imports + #sp_std_imports )) } @@ -251,6 +261,10 @@ fn convert_impl_for_assignment( let name = field_name_for(2); quote!( for (who, (t1, p1), t2) in self.#name { + if p1 >= _sp_runtime::Perbill::one() { + return Err(_phragmen::Error::CompactStakeOverflow); + } + // defensive only. Since perbill doesn't have `Sub`. let p2 = _sp_runtime::traits::Saturating::saturating_sub(_sp_runtime::Perbill::one(), p1); assignments.push( _phragmen::Assignment { who, @@ -268,12 +282,16 @@ fn convert_impl_for_assignment( for (who, inners, t_last) in self.#name { let mut sum = _sp_runtime::Perbill::zero(); let mut inners_parsed = inners - .into_iter() + .iter() .map(|(ref c, p)| { sum = _sp_runtime::traits::Saturating::saturating_add(sum, *p); (c.clone(), *p) }).collect::>(); + if sum >= _sp_runtime::Perbill::one() { + return Err(_phragmen::Error::CompactStakeOverflow); + } + // defensive only. Since perbill doesn't have `Sub`. let p_last = _sp_runtime::traits::Saturating::saturating_sub(_sp_runtime::Perbill::one(), sum); inners_parsed.push((t_last, p_last)); @@ -287,16 +305,18 @@ fn convert_impl_for_assignment( let into_impl = quote!( impl<#account_type: _codec::Codec + Default + Clone> - Into>> + _sp_std::convert::TryInto>> for #ident<#account_type, _sp_runtime::Perbill> { - fn into(self) -> Vec<_phragmen::Assignment<#account_type>> { + type Error = _phragmen::Error; + + fn try_into(self) -> Result>, Self::Error> { let mut assignments: Vec<_phragmen::Assignment<#account_type>> = Default::default(); #into_impl_single #into_impl_double #into_impl_rest - assignments + Ok(assignments) } } ); @@ -355,7 +375,13 @@ fn convert_impl_for_staked_assignment( quote!( for (who, (t1, w1), t2) in self.#name { let all_stake = C::convert(max_of(&who)) as u128; - let w2 = all_stake.saturating_sub(w1); + + if w1 >= all_stake { + return Err(_phragmen::Error::CompactStakeOverflow); + } + + // w2 is ensured to be positive. + let w2 = all_stake - w1; assignments.push( _phragmen::StakedAssignment { who, distribution: vec![ @@ -373,13 +399,18 @@ fn convert_impl_for_staked_assignment( let mut sum = u128::min_value(); let all_stake = C::convert(max_of(&who)) as u128; let mut inners_parsed = inners - .into_iter() + .iter() .map(|(ref c, w)| { sum = sum.saturating_add(*w); (c.clone(), *w) }).collect::>(); - let w_last = all_stake.saturating_sub(sum); + if sum >= all_stake { + return Err(_phragmen::Error::CompactStakeOverflow); + } + // w_last is proved to be positive. + let w_last = all_stake - sum; + inners_parsed.push((t_last, w_last)); assignments.push(_phragmen::StakedAssignment { @@ -398,7 +429,7 @@ fn convert_impl_for_staked_assignment( /// weight of a voter. It is used to subtract the sum of all the encoded weights to /// infer the last one. fn into_staked(self, max_of: FM) - -> Vec<_phragmen::StakedAssignment<#account_type>> + -> Result>, _phragmen::Error> where for<'r> FM: Fn(&'r #account_type) -> Balance, C: _sp_runtime::traits::Convert @@ -408,7 +439,7 @@ fn convert_impl_for_staked_assignment( #into_impl_double #into_impl_rest - assignments + Ok(assignments) } /// Generate self from a vector of `StakedAssignment`. diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 117a8c6de8356..7cb36f9c149f2 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -59,6 +59,14 @@ pub use sp_phragmen_compact::generate_compact_solution_type; // substrate's account id. pub trait IdentifierT: Clone + Eq + Default + Ord + Debug {} +/// The errors that might occur in the this crate and compact. +#[derive(Debug, Eq, PartialEq)] +pub enum Error { + /// While going from compact to staked, the stake of all the edges has gone above the + /// total and the last stake cannot be assigned. + CompactStakeOverflow +} + impl IdentifierT for T {} /// A type in which performing operations on balances and stakes of candidates and voters are safe. @@ -133,17 +141,28 @@ pub struct Assignment { } impl Assignment { - pub fn into_staked(self, stake_of: FS) -> StakedAssignment + pub fn into_staked(self, stake_of: FS, fill: bool) -> StakedAssignment where C: Convert, for<'r> FS: Fn(&'r AccountId) -> Balance, { let stake = C::convert(stake_of(&self.who)) as ExtendedBalance; - let distribution = self.distribution.into_iter().map(|(target, p)| { + let mut sum: ExtendedBalance = Bounded::min_value(); + let mut distribution = self.distribution.into_iter().map(|(target, p)| { let distribution_stake = p * stake; + // defensive only. We assume that balance cannot exceed extended balance. + sum = sum.saturating_add(distribution_stake); (target, distribution_stake) }).collect::>(); + if fill { + // if leftover is zero it is effectless. + if let Some(leftover) = stake.checked_sub(sum) { + if let Some(last) = distribution.last_mut() { + last.1 += leftover; + } + } + } StakedAssignment { who: self.who, distribution, @@ -425,12 +444,11 @@ pub fn elect( /// assignment. /// /// `O(E)` where `E` is the total number of edges. -pub fn build_support_map( +pub fn build_support_map( winners: &Vec, assignments: &Vec>, ) -> (SupportMap, u32) where AccountId: Default + Ord + Member, - Balance: Default + Copy + SimpleArithmetic, { let mut errors = 0; // Initialize the support of each candidate. diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index 9a3e39c98db64..ed08e00e35323 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -20,8 +20,7 @@ use crate::mock::*; use crate::{ - elect, equalize, build_support_map, - Support, StakedAssignment, Assignment, PhragmenResult, + elect, equalize, build_support_map, Support, StakedAssignment, Assignment, PhragmenResult, }; use substrate_test_utils::assert_eq_uvec; use sp_runtime::Perbill; @@ -485,16 +484,13 @@ fn self_votes_should_be_kept() { let staked_assignments: Vec> = result.assignments .into_iter() - .map(|a| a.into_staked::<_, _, TestCurrencyToVote>(&stake_of)) + .map(|a| a.into_staked::<_, _, TestCurrencyToVote>(&stake_of, true)) .collect(); - let (mut supports, _) = build_support_map::< - Balance, - AccountId, - >( - &result.winners.into_iter().map(|(who, _)| who).collect(), - &staked_assignments, - ); + let (mut supports, _) = build_support_map::( + &result.winners.into_iter().map(|(who, _)| who).collect(), + &staked_assignments, + ); assert_eq!(supports.get(&5u64), None); assert_eq!( @@ -525,14 +521,14 @@ fn self_votes_should_be_kept() { } mod compact { + use codec::{Decode, Encode}; use crate::generate_compact_solution_type; - // these need to come from the same dev-dependency `sp-phragmen`, not from the crate. - use sp_phragmen::{Assignment, StakedAssignment}; - + use crate::mock::{TestCurrencyToVote}; use super::{AccountId, Balance}; + // these need to come from the same dev-dependency `sp-phragmen`, not from the crate. + use sp_phragmen::{Assignment, StakedAssignment, Error as PhragmenError}; + use sp_std::convert::TryInto; use sp_runtime::Perbill; - use codec::{Decode, Encode}; - use crate::mock::{TestCurrencyToVote}; generate_compact_solution_type!(TestCompact, 16); @@ -608,7 +604,7 @@ mod compact { ); assert_eq!( - as Into>>>::into(compacted), + as TryInto>>>::try_into(compacted).unwrap(), assignments ); } @@ -669,8 +665,85 @@ mod compact { let max_of: Box Balance> = Box::new(max_of_fn); assert_eq!( - compacted.into_staked::(&max_of), + compacted.into_staked::(&max_of).unwrap(), assignments ); } + + #[test] + fn compact_into_stake_must_report_budget_overflow() { + // The last edge which is computed from the rest should ALWAYS be positive. + // in votes2 + let compact = TestCompact:: { + votes1: Default::default(), + votes2: vec![(1, (10, 10), 20)], + ..Default::default() + }; + + let max_of_fn = |_: &AccountId| -> Balance { 5 }; + let max_of: Box Balance> = Box::new(max_of_fn); + + assert_eq!( + compact.into_staked::(&max_of).unwrap_err(), + PhragmenError::CompactStakeOverflow, + ); + + // in votes3 onwards + let compact = TestCompact:: { + votes1: Default::default(), + votes2: Default::default(), + votes3: vec![(1, [(10, 7), (20, 8)], 30)], + ..Default::default() + }; + + assert_eq!( + compact.into_staked::(&max_of).unwrap_err(), + PhragmenError::CompactStakeOverflow, + ); + + // Also if equal + let compact = TestCompact:: { + votes1: Default::default(), + votes2: Default::default(), + // 5 is total, we cannot leave none for 30 here. + votes3: vec![(1, [(10, 3), (20, 2)], 30)], + ..Default::default() + }; + + assert_eq!( + compact.into_staked::(&max_of).unwrap_err(), + PhragmenError::CompactStakeOverflow, + ); + } + + + #[test] + fn compact_into_stake_must_report_ratio_overflow() { + // in votes2 + let compact = TestCompact:: { + votes1: Default::default(), + votes2: vec![(1, (10, Perbill::from_percent(100)), 20)], + ..Default::default() + }; + + assert_eq!( + as TryInto>>>::try_into(compact) + .unwrap_err(), + PhragmenError::CompactStakeOverflow, + ); + + // in votes3 onwards + let compact = TestCompact:: { + votes1: Default::default(), + votes2: Default::default(), + votes3: vec![(1, [(10, Perbill::from_percent(70)), (20, Perbill::from_percent(80))], 30)], + ..Default::default() + }; + + assert_eq!( + as TryInto>>>::try_into(compact) + .unwrap_err(), + PhragmenError::CompactStakeOverflow, + ); + } } From 201c11cf1685ca81008e83285e16d298cee8e643 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 6 Feb 2020 07:49:24 +0100 Subject: [PATCH 035/106] a very nasty bug in reduce --- primitives/phragmen/fuzzer/Cargo.lock | 4 + primitives/phragmen/fuzzer/src/reduce.rs | 37 +++++-- primitives/phragmen/src/reduce.rs | 124 ++++++++++++++++++++++- 3 files changed, 154 insertions(+), 11 deletions(-) diff --git a/primitives/phragmen/fuzzer/Cargo.lock b/primitives/phragmen/fuzzer/Cargo.lock index 0d4ea2559140d..591320a0033df 100644 --- a/primitives/phragmen/fuzzer/Cargo.lock +++ b/primitives/phragmen/fuzzer/Cargo.lock @@ -1121,6 +1121,7 @@ dependencies = [ "sp-state-machine 0.8.0", "sp-std 2.0.0", "sp-trie 2.0.0", + "sp-wasm-interface 2.0.0", ] [[package]] @@ -1135,6 +1136,7 @@ dependencies = [ name = "sp-phragmen" version = "2.0.0" dependencies = [ + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-phragmen-compact 2.0.0", "sp-runtime 2.0.0", @@ -1252,6 +1254,8 @@ name = "sp-wasm-interface" version = "2.0.0" dependencies = [ "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-std 2.0.0", "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/primitives/phragmen/fuzzer/src/reduce.rs b/primitives/phragmen/fuzzer/src/reduce.rs index 09294544c1b73..ca7ad921d0caa 100644 --- a/primitives/phragmen/fuzzer/src/reduce.rs +++ b/primitives/phragmen/fuzzer/src/reduce.rs @@ -43,7 +43,6 @@ fn main() { 6, ); reduce_and_compare(&assignments, &winners); - dbg!("Spme"); }); } } @@ -58,29 +57,30 @@ fn generate_random_phragmen_assignment( let rr_128 = |a: u128, b: u128| -> u128 { rand::thread_rng().gen_range(a, b) }; // prefix to distinguish the voter and target account ranges. - let target_prefix = 1_000_000_000; + let target_prefix = 1_000_000; + // let target_prefix = 1000; assert!(voter_count < target_prefix); let mut assignments = Vec::with_capacity(voter_count as usize); let mut winners: Vec = Vec::new(); - let all_targets = (target_prefix..=(target_prefix + target_count)) + let all_targets = (target_prefix..(target_prefix + target_count)) .map(|a| a as AccountId) .collect::>(); (1..=voter_count).for_each(|acc| { let mut targets_to_chose_from = all_targets.clone(); - let targets_to_chose = rr( + let targets_to_chose = if edge_per_voter_var > 0 { rr( avg_edge_per_voter - edge_per_voter_var, avg_edge_per_voter + edge_per_voter_var, - ); + ) } else { avg_edge_per_voter }; let distribution = (0..targets_to_chose).map(|_| { let target = targets_to_chose_from.remove(rr(0, targets_to_chose_from.len())); if winners.iter().find(|w| **w == target).is_none() { winners.push(target.clone()); } - (target, rr_128(KSM, 100 * KSM)) + (target, rr_128(1 * KSM, 100 * KSM)) }).collect::>(); assignments.push(StakedAssignment { @@ -98,8 +98,8 @@ fn assert_assignments_equal( ass2: &Vec>, ) { - let (support_1, _) = build_support_map::(winners, ass1); - let (support_2, _) = build_support_map::(winners, ass2); + let (support_1, _) = build_support_map::(winners, ass1); + let (support_2, _) = build_support_map::(winners, ass2); for (who, support) in support_1.iter() { assert_eq!(support.total, support_2.get(who).unwrap().total); @@ -111,7 +111,22 @@ fn reduce_and_compare( winners: &Vec, ) { let mut altered_assignment = assignment.clone(); + let n = assignment.len() as u32; + let m = winners.len() as u32; + + let edges_before = assignment_len(&assignment); let num_changed = reduce(&mut altered_assignment); + let edges_after = edges_before - num_changed; + + assert!( + edges_after <= m + n, + "reduce bound not satisfied. n = {}, m = {}, edges after reduce = {} (removed {})", + n, + m, + edges_after, + num_changed, + ); + assert_assignments_equal( winners, &assignment, @@ -119,6 +134,12 @@ fn reduce_and_compare( ); } +fn assignment_len(assignments: &[StakedAssignment]) -> u32 { + let mut counter = 0; + assignments.iter().for_each(|x| x.distribution.iter().for_each(|_| counter += 1)); + counter +} + fn rr(a: usize, b: usize) -> usize { rand::thread_rng().gen_range(a, b) } diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index 73637e1cdc476..d6a9ac3b5a53d 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -335,7 +335,8 @@ fn reduce_all( for assignment_index in 0..assignments.len() { let voter = assignments[assignment_index].who.clone(); - for dist_index in 0..assignments[assignment_index].distribution.len() { + let mut dist_index = 0; + loop { // A distribution could have been removed. We don't know for sure. Hence, we check. let maybe_dist = assignments[assignment_index].distribution.get(dist_index); if maybe_dist.is_none() { @@ -362,14 +363,17 @@ fn reduce_all( match (voter_exists, target_exists) { (false, false) => { Node::set_parent_of(&target_node, &voter_node); + dist_index += 1; continue; }, (false, true) => { Node::set_parent_of(&voter_node, &target_node); + dist_index += 1; continue; }, (true, false) => { Node::set_parent_of(&target_node, &voter_node); + dist_index += 1; continue; } (true, true) => { /* don't continue and execute the rest */ } @@ -381,6 +385,7 @@ fn reduce_all( if voter_root != target_root { // swap merge(voter_root_path, target_root_path); + dist_index += 1; } else { // find common and cycle. let common_count = trailing_common(&voter_root_path, &target_root_path); @@ -401,6 +406,7 @@ fn reduce_all( #[cfg(feature = "std")] debug_assert_eq!(cycle.len() % 2, 0); + // find minimum of cycle. let mut min_value: ExtendedBalance = Bounded::max_value(); // The voter and the target pair that create the min edge. @@ -450,13 +456,14 @@ fn reduce_all( let min_chain_in_voter = (min_index + min_direction as usize) > target_chunk; // walk over the cycle and update the weights + let mut should_inc_counter = true; let start_operation_add = ((min_index % 2) + min_direction as usize) % 2 == 1; let mut additional_removed = Vec::new(); for i in 0..cycle.len() { let current = cycle[i].borrow(); if current.id.role == NodeRole::Voter { let prev = cycle[prev_index(i)].borrow(); - assignments.iter_mut().filter(|a| a.who == current.id.who).for_each(|ass| { + assignments.iter_mut().enumerate().filter(|(_, a)| a.who == current.id.who).for_each(|(target_ass_index, ass)| { ass.distribution.iter_mut().position(|(t, _)| *t == prev.id.who).map(|idx| { let next_value = if i % 2 == 0 { if start_operation_add { @@ -473,6 +480,9 @@ fn reduce_all( }; if next_value.is_zero() { + // if the removed edge is from the current assignment, dis_index + // should NOT be increased. + if target_ass_index == assignment_index { should_inc_counter = false } ass.distribution.remove(idx); num_changed += 1; // only add if this is not the min itself. @@ -486,7 +496,7 @@ fn reduce_all( }); let next = cycle[next_index(i)].borrow(); - assignments.iter_mut().filter(|a| a.who == current.id.who).for_each(|ass| { + assignments.iter_mut().enumerate().filter(|(_, a)| a.who == current.id.who).for_each(|(target_ass_index, ass)| { ass.distribution.iter_mut().position(|(t, _)| *t == next.id.who).map(|idx| { let next_value = if i % 2 == 0 { if start_operation_add { @@ -503,6 +513,9 @@ fn reduce_all( }; if next_value.is_zero() { + // if the removed edge is from the current assignment, dis_index + // should NOT be increased. + if target_ass_index == assignment_index { should_inc_counter = false } ass.distribution.remove(idx); num_changed += 1; if !(i == min_index && min_direction == 1) { @@ -558,6 +571,8 @@ fn reduce_all( } } + // increment the counter if needed. + if should_inc_counter { dist_index += 1; } } } } @@ -1062,4 +1077,107 @@ mod tests { ], ) } + + #[test] + fn bound_should_be_kept() { + let mut assignments = vec![ + StakedAssignment { + who: 1, + distribution: vec![ + ( + 103, + 72, + ), + ( + 101, + 53, + ), + ( + 100, + 83, + ), + ( + 102, + 38, + ), + ], + }, + StakedAssignment { + who: 2, + distribution: vec![ + ( + 103, + 18, + ), + ( + 101, + 36, + ), + ( + 102, + 54, + ), + ( + 100, + 94, + ), + ], + }, + StakedAssignment { + who: 3, + distribution: vec![ + ( + 100, + 96, + ), + ( + 101, + 35, + ), + ( + 102, + 52, + ), + ( + 103, + 69, + ), + ], + }, + StakedAssignment { + who: 4, + distribution: vec![ + ( + 102, + 34, + ), + ( + 100, + 47, + ), + ( + 103, + 91, + ), + ( + 101, + 73, + ), + ], + }, + ]; + + let winners = vec![ + 103, + 101, + 100, + 102, + ]; + + let n = 4; + let m = winners.len() as u32; + let num_reduced = reduce_all(&mut assignments); + assert!(16 - num_reduced <= n + m); + + } } From eef09e5f4e6706065ec0a2ffef3a05a8450ba31d Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 6 Feb 2020 09:11:58 +0100 Subject: [PATCH 036/106] Fix all integrations --- frame/elections-phragmen/src/lib.rs | 7 ++----- frame/staking/src/lib.rs | 27 ++++++++++++++------------- frame/staking/src/mock.rs | 8 +++++--- primitives/phragmen/src/lib.rs | 10 +++++++++- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index fb600236633d0..06e76a7c51ef5 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -666,13 +666,10 @@ impl Module { let staked_assignments: Vec> = phragmen_result.assignments .into_iter() - .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::locked_stake_of)) + .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::locked_stake_of, true)) .collect(); - let (support_map, _) = build_support_map::, T::AccountId>( - &new_set, - &staked_assignments, - ); + let (support_map, _) = build_support_map::(&new_set, &staked_assignments); let to_balance = |e: ExtendedBalance| >>::convert(e); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 7e66359e1daa1..f856336d095e6 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -289,7 +289,10 @@ use frame_system::{ offchain::SubmitUnsignedTransaction, }; -use sp_phragmen::{ExtendedBalance, StakedAssignment, generate_compact_solution_type}; +use sp_phragmen::{ + ExtendedBalance, StakedAssignment, PhragmenScore, PhragmenResult, + generate_compact_solution_type, build_support_map, evaluate_support, elect, +}; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; // ------------- IMPORTANT NOTE: must be the same as `generate_compact_solution_type`. @@ -593,8 +596,6 @@ impl Default for ElectionStatus { } } -pub type ElectionScore = [ExtendedBalance; 3]; - pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type PositiveImbalanceOf = @@ -792,7 +793,7 @@ decl_storage! { pub QueuedElected get(fn queued_elected): Option>>; /// The score of the current [`QueuedElected`]. - pub QueuedScore get(fn queued_score): Option; + pub QueuedScore get(fn queued_score): Option; /// Flag to control the execution of the offchain election. pub EraElectionStatus get(fn era_election_status): ElectionStatus; @@ -1074,7 +1075,7 @@ decl_module! { /// /// major steps (all done in `check_and_replace_solution`): /// - /// - 1 read. `ElectionScore`. O(1). + /// - 1 read. `PhragmenScore`. O(1). /// - `m` calls `Validators::exists()` for validator veracity. /// /// - decode from compact and convert into assignments: O(E) TODO: if our reduce is correct, this will be at most `n + m` @@ -1091,7 +1092,7 @@ decl_module! { origin, winners: Vec, compact_assignments: CompactAssignments, - score: ElectionScore, + score: PhragmenScore, ) { let _who = ensure_signed(origin)?; Self::check_and_replace_solution( @@ -1109,7 +1110,7 @@ decl_module! { origin, winners: Vec, compact_assignments: CompactAssignments, - score: ElectionScore, + score: PhragmenScore, // already used and checked in ValidateUnsigned. _validator_index: u32, _signature: ::Signature, @@ -1654,7 +1655,7 @@ impl Module { winners: Vec, compact_assignments: CompactAssignments, compute: ElectionCompute, - claimed_score: ElectionScore, + claimed_score: PhragmenScore, // at: T::BlockNumber, ) -> Result<(), Error> { // discard early solutions @@ -1689,7 +1690,7 @@ impl Module { // build the support map thereof in order to evaluate. // OPTIMIZATION: we could merge this with the `staked_assignments.iter()` below and // iterate only once. - let (supports, num_error) = sp_phragmen::build_support_map::( + let (supports, num_error) = build_support_map::( &winners, &staked_assignments, ); @@ -1698,7 +1699,7 @@ impl Module { // Check if the score is the same as the claimed one. - let submitted_score = sp_phragmen::evaluate_support(&supports); + let submitted_score = evaluate_support(&supports); ensure!(submitted_score == claimed_score, Error::::PhragmenBogusScore); // check all nominators being bonded, and actually including the claimed vote, and @@ -1981,7 +1982,7 @@ impl Module { .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::slashable_balance_of, true)) .collect(); - let (supports, _) = sp_phragmen::build_support_map::( + let (supports, _) = build_support_map::( &elected_stashes, &staked_assignments, ); @@ -2053,7 +2054,7 @@ impl Module { /// weights are returned. /// /// No storage item is updated. - fn do_phragmen() -> Option> { + fn do_phragmen() -> Option> { let mut all_nominators: Vec<(T::AccountId, Vec)> = Vec::new(); let all_validator_candidates_iter = >::enumerate(); let all_validators = all_validator_candidates_iter.map(|(who, _pref)| { @@ -2080,7 +2081,7 @@ impl Module { }); all_nominators.extend(nominator_votes); - sp_phragmen::elect::<_, _, _, T::CurrencyToVote>( + elect::<_, _, _, T::CurrencyToVote>( Self::validator_count() as usize, Self::minimum_validator_count().max(1) as usize, all_validators, diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 3121b7373025b..1a992bd2cb079 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -26,7 +26,9 @@ use frame_support::{ use frame_system::offchain::{CreateTransaction, Signer, TransactionSubmitter}; use sp_core::H256; use sp_io; -use sp_phragmen::{build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment}; +use sp_phragmen::{ + build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment, PhragmenScore, +}; use sp_runtime::curve::PiecewiseLinear; use sp_runtime::testing::{Header, TestXt, UintAuthorityId}; use sp_runtime::traits::{ @@ -741,7 +743,7 @@ pub fn horrible_phragmen_with_post_processing( ) -> ( CompactAssignments, Vec, - ElectionScore, + PhragmenScore, ) { use std::collections::BTreeMap; @@ -836,7 +838,7 @@ pub fn do_phragmen_with_post_processing( ) -> ( CompactAssignments, Vec, - ElectionScore, + PhragmenScore, ) { // run phragmen on the default stuff. let sp_phragmen::PhragmenResult { diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 7cb36f9c149f2..f6bf85ce6d2c1 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -77,6 +77,9 @@ impl IdentifierT for T {} /// Balance types converted to `ExtendedBalance` are referred to as `Votes`. pub type ExtendedBalance = u128; +/// The score of an assignment. This can be computed from the support map via [`evaluate_support`]. +pub type PhragmenScore = [ExtendedBalance; 3]; + /// 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 /// bigger than u64::max_value() is needed. For maximum accuracy we simply use u128; @@ -141,6 +144,11 @@ pub struct Assignment { } impl Assignment { + /// Convert from a ratio assignment into one with absolute values aka. [`StakedAssignment`]. + /// + /// It needs `stake_of` to know how the total budget of each voter. If `fill` is set to true, + /// it ensures that all the potential rounding errors are compensated and the distribution's sum + /// is exactly equal to the total budget. pub fn into_staked(self, stake_of: FS, fill: bool) -> StakedAssignment where C: Convert, @@ -480,7 +488,7 @@ pub fn build_support_map( /// `O(E)` where `E` is the total number of edges. pub fn evaluate_support( support: &SupportMap, -) -> [ExtendedBalance; 3] { +) -> PhragmenScore { let mut min_support = ExtendedBalance::max_value(); let mut sum: ExtendedBalance = Zero::zero(); // TODO: this will probably saturate but using big num makes it even slower. We'll have to see. From 51fc8658da287db3b4a2814280ae07bb4058676f Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 6 Feb 2020 14:54:44 +0100 Subject: [PATCH 037/106] Fix more todos --- Cargo.lock | 1 + frame/staking/Cargo.toml | 1 + frame/staking/src/lib.rs | 5 ++--- frame/staking/src/mock.rs | 15 +++++++++------ frame/staking/src/tests.rs | 12 ++++++++++++ 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2634a25c22a7f..e0a413aec36b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3988,6 +3988,7 @@ dependencies = [ name = "pallet-staking" version = "2.0.0" dependencies = [ + "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "frame-support 2.0.0", "frame-system 2.0.0", "pallet-authorship 2.0.0", diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index c75c602e6a0ae..1d40825bc801a 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -27,6 +27,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" } parking_lot = { version = "0.9.0" } +env_logger = "0.7.1" [features] migrate = [] diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index f856336d095e6..a3dc19db8c4e7 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -617,7 +617,7 @@ pub trait SessionInterface: frame_system::Trait { /// Get the validators from session. fn validators() -> Vec; /// Get the validators and their corresponding keys from session. - fn keys() -> Vec<(AccountId, Option)>; + fn keys() -> Vec<(AccountId, Option)>; /// Prune historical session tries up to but not including the given index. fn prune_historical_up_to(up_to: SessionIndex); /// Current session index. @@ -642,7 +642,7 @@ impl SessionInterface<::AccountId> for T whe >::validators() } - fn keys() + fn keys() -> Vec<(::AccountId, Option)> { Self::validators().iter().map(|v| { @@ -1839,7 +1839,6 @@ impl Module { } } - // assert!(total_imbalance.peek() == total_payout) let total_payout = total_imbalance.peek(); let rest = max_payout.saturating_sub(total_payout); diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 1a992bd2cb079..467637e1d61ee 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -75,8 +75,10 @@ thread_local! { pub struct TestSessionHandler; impl pallet_session::SessionHandler for TestSessionHandler { - // EVEN if no tests break, I must have broken something here... TODO - const KEY_TYPE_IDS: &'static [KeyTypeId] = &[dummy_sr25519::AuthorityId::ID]; + const KEY_TYPE_IDS: &'static [KeyTypeId] = &[ + ::ID, + dummy_sr25519::AuthorityId::ID, + ]; fn on_genesis_session(_validators: &[(AccountId, Ks)]) {} @@ -248,7 +250,7 @@ impl sp_runtime::BoundToRuntimeAppPublic for Babe { // in here. sp_runtime::impl_opaque_keys! { pub struct SessionKeys { - // pub foo: UintAuthorityId, + pub foo: UintAuthorityId, pub babe: Babe, } } @@ -321,8 +323,8 @@ pub(crate) mod dummy_sr25519 { use super::{LOCAL_KEY_ACCOUNT, AccountId}; mod app_sr25519 { - use sp_application_crypto::{app_crypto, key_types::DUMMY, sr25519}; - app_crypto!(sr25519, DUMMY); + use sp_application_crypto::{app_crypto, KeyTypeId, sr25519}; + app_crypto!(sr25519, KeyTypeId(*b"ebab")); } pub type AuthoritySignature = app_sr25519::Signature; @@ -477,6 +479,7 @@ impl ExtBuilder { LOCAL_KEY_ACCOUNT.with(|v| *v.borrow_mut() = self.local_key_account); } pub fn build(self) -> sp_io::TestExternalities { + env_logger::init(); self.set_associated_constants(); let mut storage = frame_system::GenesisConfig::default() .build_storage::() @@ -548,7 +551,7 @@ impl ExtBuilder { keys: validators.iter().map(|x| ( *x, SessionKeys { - // foo: UintAuthorityId(*x), + foo: UintAuthorityId(*x), babe: dummy_sr25519::dummy_key_for(*x), } )).collect(), diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 6aaea5d7fd512..712774363a0ab 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -607,6 +607,7 @@ fn nominators_also_get_slashed() { assert_eq!(Balances::total_balance(&2), initial_balance - nominator_slash); check_exposure_all(); check_nominator_all(); + // Because slashing happened. assert!(is_disabled(10)); }); @@ -2728,6 +2729,12 @@ mod offchain_phragmen { &format!("{}/staking{}", mock::PHRASE, 11), dummy_sr25519::dummy_key_for(11).as_ref(), ).unwrap(); + frame_support::debug::native::debug!( + target: "staking", + "generated key for account {}: {:?}", + 11, + dummy_sr25519::dummy_key_for(11), + ); ext.register_extension(OffchainExt::new(offchain)); ext.register_extension(TransactionPoolExt::new(pool)); ext.register_extension(KeystoreExt(keystore)); @@ -3349,6 +3356,11 @@ mod offchain_phragmen { ); }) } + + #[test] + fn session_keys_work() { + unimplemented!(); + } } #[test] From aa6186ca5326dca53c373522b322ad4e425f35c5 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 11 Feb 2020 15:34:04 +0100 Subject: [PATCH 038/106] Revamp everything and everything --- bin/node/cli/src/factory_impl.rs | 3 +- bin/node/cli/src/service.rs | 3 +- bin/node/runtime/src/lib.rs | 4 +- bin/node/testing/src/keyring.rs | 1 + bin/utils/subkey/src/main.rs | 2 + frame/elections-phragmen/src/lib.rs | 7 +- frame/staking/src/lib.rs | 421 ++++++++++++++++++------- frame/staking/src/mock.rs | 94 +++++- frame/staking/src/offchain_election.rs | 127 ++++++-- frame/staking/src/tests.rs | 391 +++++++++++++++-------- primitives/phragmen/compact/src/lib.rs | 332 +++++++++++-------- primitives/phragmen/src/lib.rs | 51 ++- primitives/phragmen/src/tests.rs | 272 ++++++++++++---- 13 files changed, 1214 insertions(+), 494 deletions(-) diff --git a/bin/node/cli/src/factory_impl.rs b/bin/node/cli/src/factory_impl.rs index a1c5a5f4e050f..fed8452bacdf9 100644 --- a/bin/node/cli/src/factory_impl.rs +++ b/bin/node/cli/src/factory_impl.rs @@ -63,6 +63,7 @@ impl FactoryState { frame_system::CheckWeight::new(), pallet_transaction_payment::ChargeTransactionPayment::from(0), Default::default(), + Default::default(), ) } } @@ -153,7 +154,7 @@ impl RuntimeAdapter for FactoryState { (*amount).into() ) ) - }, key, (version, genesis_hash.clone(), prior_block_hash.clone(), (), (), (), ())) + }, key, (version, genesis_hash.clone(), prior_block_hash.clone(), (), (), (), (), ())) } fn inherent_extrinsics(&self) -> InherentData { diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index c462a60836a22..ffd61a47b91f7 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -589,11 +589,12 @@ mod tests { check_weight, payment, Default::default(), + Default::default(), ); let raw_payload = SignedPayload::from_raw( function, extra, - (version, genesis_hash, genesis_hash, (), (), (), ()) + (version, genesis_hash, genesis_hash, (), (), (), (), ()) ); let signature = raw_payload.using_encoded(|payload| { signer.sign(payload) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index bb5854b754462..fd1d61838cdfc 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -56,7 +56,7 @@ pub use pallet_timestamp::Call as TimestampCall; pub use pallet_balances::Call as BalancesCall; pub use pallet_contracts::Gas; pub use frame_support::StorageValue; -pub use pallet_staking::StakerStatus; +pub use pallet_staking::{StakerStatus, LockStakingStatus}; /// Implementations of some helper traits passed into runtime modules as associated types. pub mod impls; @@ -104,6 +104,7 @@ impl frame_system::offchain::CreateTransaction for frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), Default::default(), + Default::default(), ); let raw_payload = SignedPayload::new(call, extra).map_err(|e| { debug::warn!("Unable to create signed payload: {:?}", e); @@ -655,6 +656,7 @@ pub type SignedExtra = ( frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, pallet_contracts::CheckBlockGasLimit, + pallet_staking::LockStakingStatus, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; diff --git a/bin/node/testing/src/keyring.rs b/bin/node/testing/src/keyring.rs index 6b0d06875d692..5fa1e48b03218 100644 --- a/bin/node/testing/src/keyring.rs +++ b/bin/node/testing/src/keyring.rs @@ -75,6 +75,7 @@ pub fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra { frame_system::CheckWeight::new(), pallet_transaction_payment::ChargeTransactionPayment::from(extra_fee), Default::default(), + Default::default(), ) } diff --git a/bin/utils/subkey/src/main.rs b/bin/utils/subkey/src/main.rs index c2fd7e28c2548..67288330088dc 100644 --- a/bin/utils/subkey/src/main.rs +++ b/bin/utils/subkey/src/main.rs @@ -595,6 +595,7 @@ fn create_extrinsic( frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(f), Default::default(), + Default::default(), ) }; let raw_payload = SignedPayload::from_raw( @@ -608,6 +609,7 @@ fn create_extrinsic( (), (), (), + (), ), ); let signature = raw_payload.using_encoded(|payload| signer.sign(payload)).into_runtime(); diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 06e76a7c51ef5..ee68639088f17 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -666,7 +666,12 @@ impl Module { let staked_assignments: Vec> = phragmen_result.assignments .into_iter() - .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::locked_stake_of, true)) + .map(|a| { + let stake = , u64>>::convert( + Self::locked_stake_of(&a.who) + ) as ExtendedBalance; + a.into_staked(stake, true) + }) .collect(); let (support_map, _) = build_support_map::(&new_set, &staked_assignments); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index a3dc19db8c4e7..88fbd37b53ab3 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -72,8 +72,8 @@ //! There are three possible roles that any staked account pair can be in: `Validator`, `Nominator` //! and `Idle` (defined in [`StakerStatus`](./enum.StakerStatus.html)). There are three //! corresponding instructions to change between roles, namely: -//! [`validate`](./enum.Call.html#variant.validate), [`nominate`](./enum.Call.html#variant.nominate), -//! and [`chill`](./enum.Call.html#variant.chill). +//! [`validate`](./enum.Call.html#variant.validate), +//! [`nominate`](./enum.Call.html#variant.nominate), and [`chill`](./enum.Call.html#variant.chill). //! //! #### Validating //! @@ -181,8 +181,8 @@ //! [`reward_by_indices`](./enum.Call.html#variant.reward_by_indices). //! //! [`Module`](./struct.Module.html) implements -//! [`pallet_authorship::EventHandler`](../pallet_authorship/trait.EventHandler.html) to add reward points -//! to block producer and block producer of referenced uncles. +//! [`pallet_authorship::EventHandler`](../pallet_authorship/trait.EventHandler.html) to add reward +//! points to block producer and block producer of referenced uncles. //! //! The validator and its nominator split their reward as following: //! @@ -240,8 +240,8 @@ //! ## Related Modules //! //! - [Balances](../pallet_balances/index.html): Used to manage values at stake. -//! - [Session](../pallet_session/index.html): Used to manage sessions. Also, a list of new validators -//! is stored in the Session module's `Validators` at the end of each era. +//! - [Session](../pallet_session/index.html): Used to manage sessions. Also, a list of new +//! validators is stored in the Session module's `Validators` at the end of each era. #![recursion_limit="128"] #![cfg_attr(not(feature = "std"), no_std)] @@ -261,6 +261,7 @@ use codec::{HasCompact, Encode, Decode}; use frame_support::{ decl_module, decl_event, decl_storage, ensure, decl_error, debug, Parameter, weights::SimpleDispatchInfo, + dispatch::IsSubType, traits::{ Currency, LockIdentifier, LockableCurrency, WithdrawReasons, OnUnbalanced, Imbalance, Get, Time, EstimateNextSessionChange, OnReapAccount, @@ -272,10 +273,11 @@ use sp_runtime::{ curve::PiecewiseLinear, traits::{ Convert, Zero, One, StaticLookup, CheckedSub, Saturating, Bounded, SaturatedConversion, - SimpleArithmetic, EnsureOrigin, Member, OpaqueKeys + SimpleArithmetic, EnsureOrigin, Member, OpaqueKeys, SignedExtension, }, transaction_validity::{ - TransactionValidityError, TransactionValidity, ValidTransaction, InvalidTransaction, TransactionPriority, + TransactionValidityError, TransactionValidity, ValidTransaction, InvalidTransaction, + TransactionPriority, }, }; use sp_staking::{ @@ -288,21 +290,28 @@ use frame_system::{ self as system, ensure_signed, ensure_root, ensure_none, offchain::SubmitUnsignedTransaction, }; - use sp_phragmen::{ - ExtendedBalance, StakedAssignment, PhragmenScore, PhragmenResult, - generate_compact_solution_type, build_support_map, evaluate_support, elect, + ExtendedBalance, StakedAssignment, Assignment, PhragmenScore, PhragmenResult, + build_support_map, evaluate_support, elect, generate_compact_solution_type, }; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; -// ------------- IMPORTANT NOTE: must be the same as `generate_compact_solution_type`. -const MAX_NOMINATIONS: usize = 16; +pub(crate) const MAX_NOMINATIONS: usize = 16; const MAX_UNLOCKING_CHUNKS: usize = 32; const STAKING_ID: LockIdentifier = *b"staking "; -// ------------- IMPORTANT NOTE: must be the same as `MAX_NOMINATIONS`. +// indexable by 2 bytes +pub(crate) const MAX_VALIDATORS: u32 = ValidatorIndex::max_value() as u32; +pub(crate) const MAX_NOMINATORS: u32 = NominatorIndex::max_value(); + +// TODO: get rid of 16 generate_compact_solution_type!(pub CompactAssignments, 16); +/// Data type used to index nominators in the compact type +pub type NominatorIndex = u32; +/// Data type used to index validators in the compact type. +pub type ValidatorIndex = u16; + /// Counter for the number of eras that have passed. pub type EraIndex = u32; @@ -598,6 +607,13 @@ impl Default for ElectionStatus { pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +pub type CompactOf = CompactAssignments< + NominatorIndex, + ValidatorIndex, + Perbill, + ::AccountId, +>; + type PositiveImbalanceOf = <::Currency as Currency<::AccountId>>::PositiveImbalance; type NegativeImbalanceOf = @@ -617,7 +633,7 @@ pub trait SessionInterface: frame_system::Trait { /// Get the validators from session. fn validators() -> Vec; /// Get the validators and their corresponding keys from session. - fn keys() -> Vec<(AccountId, Option)>; + fn keys() -> Vec<(AccountId, Option)>; /// Prune historical session tries up to but not including the given index. fn prune_historical_up_to(up_to: SessionIndex); /// Current session index. @@ -642,7 +658,7 @@ impl SessionInterface<::AccountId> for T whe >::validators() } - fn keys() + fn keys() -> Vec<(::AccountId, Option)> { Self::validators().iter().map(|v| { @@ -716,7 +732,7 @@ pub trait Trait: frame_system::Trait { type ElectionLookahead: Get; /// The overarching call type. - type Call: From> + Clone; + type Call: From> + IsSubType, Self> + Clone; /// A transaction submitter. type SubmitTransaction: SubmitUnsignedTransaction::Call>; @@ -784,6 +800,14 @@ decl_storage! { pub Stakers get(fn stakers): map hasher(blake2_256) T::AccountId => Exposure>; + /// Snapshot of validators at the beginning of the current election window. This should only + /// have a value when [`EraElectionStatus`] == `ElectionStatus::Open(_)`. + 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(_)`. + pub SnapshotNominators get(fn snapshot_nominators): Option>; + /// The currently elected validator set keyed by stash account ID. pub CurrentElected get(fn current_elected): Vec; @@ -946,14 +970,18 @@ decl_error! { PhragmenBogusWinner, /// One of the submitted nominators is not an active nominator on chain. PhragmenBogusNominator, - /// One of the submitted nominators stake distribution does not add up to their ledger. - PhragmenBogusNominatorStake, /// One of the submitted nominators has an edge to which they have not voted on chain. PhragmenBogusNomination, /// A self vote must only be originated from a validator to ONLY themselves. PhragmenBogusSelfVote, /// The claimed score does not match with the one computed from the data. PhragmenBogusScore, + /// Error while building the assignment type from the compact. + PhragmenBogusCompact, + /// Incorrect number of winners were presented. + PhragmenBogusWinnerCount, + /// The snapshot data of the current window is missing. + SnapshotUnavailable, } } @@ -988,16 +1016,26 @@ decl_module! { T::NextSessionChange::estimate_next_session_change(now); if let Some(remaining) = next_session_change.checked_sub(&now) { if remaining <= T::ElectionLookahead::get() && !remaining.is_zero() { - // Set the flag to make sure we don't waste any compute here in the same era - // after we have triggered the offline compute. - >::put( - ElectionStatus::::Open(now) - ); - debug::native::info!( - target: "staking", - "Election window is Open({:?})", - now, - ); + // create snapshot. + if Self::create_stakers_snapshot() { + // Set the flag to make sure we don't waste any compute here in the same era + // after we have triggered the offline compute. + >::put( + ElectionStatus::::Open(now) + ); + debug::native::info!( + target: "staking", + "Election window is Open({:?}). Snapshot created", + now, + ); + } else { + debug::native::warn!( + target: "staking", + "Failed to create snapshot at {:?}. Election window will remain closed.", + now, + ); + } + } } } @@ -1007,8 +1045,11 @@ decl_module! { /// to open. If so, it runs the offchain worker code. fn offchain_worker(now: T::BlockNumber) { debug::RuntimeLogger::init(); - // runs only once. - if Self::era_election_status() == ElectionStatus::::Open(now) { + + let offchain_status = offchain_election::set_check_offchain_execution_status::(now); + let window_open = Self::era_election_status() == ElectionStatus::::Open(now); + + if window_open && offchain_status.is_ok() { if let Err(e) = offchain_election::compute_offchain_election::() { debug::native::warn!( target: "staking", @@ -1016,6 +1057,14 @@ decl_module! { e, ); }; + } else if window_open { + if let Err(why) = offchain_status { + debug::native::debug!( + target: "staking", + "skipping offchain call in open election window due to [{}]", + why, + ); + } } } @@ -1031,7 +1080,7 @@ decl_module! { /// 1. is valid /// 2. has a better score than a potentially existing solution on chain /// - /// it will replace it. + /// then, it will be put on chain. /// /// A solution consists of two pieces of data: /// @@ -1043,22 +1092,24 @@ decl_module! { /// /// Additionally, the submitter must provide: /// - /// 1. The score that they claim their solution has. - /// 2. The block number at which the solution is computed. + /// - The score that they claim their solution has. + /// + /// Both validators and nominators will be represented by indices in the solution. The + /// indices should respect the corresponding types ([`ValidatorIndex`] and + /// [`NominatorIndex`]). Moreover, they should be valid when used to index into + /// [`SnapshotValidators`] and [`SnapshotNominators`]. Any invalid index will cause the + /// solution to be rejected. /// /// A solution is valid if: /// - /// 1. All the presented winners are actually on-chain candidates. - /// 2. All the presented edges contain: - /// - a voter who is an on-chain nominator. - /// - a target who is an on-chain candidate and among the presented winners. - /// - that target must actually be among the nominations of the voter. - /// 3. For all the presented nominators, their total stake must actually add up to their - /// staked amount on chain. This does not need to be calculated. Converting compact -> - /// staked will assure it. - /// 4. The solution must have been submitted when [`ElectionStatus`] is `Open`. - /// 5. The claimed score is valid, and, if an on-chain solution exists, it is better than - /// the on on-chain, as defined by [`is_score_better`]. + /// 0. It is submitted when [`EraElectionStatus`] is `Open`. + /// 1. Its claimed score is equal to the score computed on-chain. + /// 2. Presents the correct number of winners. + /// 3. All indexes must be value according to the snapshot vectors. All edge values must + /// also be correct and should not overflow the granularity of the ratio type (i.e. 256 + /// or billion). + /// 4. For each edge, all targets are actually nominated by the voter. + /// 5. Has correct self-votes. /// /// A solutions score is consisted of 3 parameters: /// @@ -1073,12 +1124,16 @@ decl_module! { /// n: number of nominators. /// d: edge degree aka MAX_NOMINATIONS = 16 /// + /// NOTE: given a solution which is reduced, we can enable a new check the ensure `|E| < n + + /// m`. + /// /// major steps (all done in `check_and_replace_solution`): /// + /// TODO: update this one last time at the end. /// - 1 read. `PhragmenScore`. O(1). /// - `m` calls `Validators::exists()` for validator veracity. /// - /// - decode from compact and convert into assignments: O(E) TODO: if our reduce is correct, this will be at most `n + m` + /// - decode from compact and convert into assignments: O(E) /// - build_support_map: O(E) /// - evaluate_support: O(E) /// @@ -1090,8 +1145,8 @@ decl_module! { #[weight = SimpleDispatchInfo::FixedNormal(10_000_000)] fn submit_election_solution( origin, - winners: Vec, - compact_assignments: CompactAssignments, + winners: Vec, + compact_assignments: CompactOf, score: PhragmenScore, ) { let _who = ensure_signed(origin)?; @@ -1108,8 +1163,8 @@ decl_module! { // TODO: weight should be noted. fn submit_election_solution_unsigned( origin, - winners: Vec, - compact_assignments: CompactAssignments, + winners: Vec, + compact_assignments: CompactOf, score: PhragmenScore, // already used and checked in ValidateUnsigned. _validator_index: u32, @@ -1547,6 +1602,42 @@ impl Module { era_length >= session_per_era } + /// Dump the list list of validators and nominators into vectors and keep them on-chain. + /// + /// This data is used to efficiently evaluate election results. + fn create_stakers_snapshot() -> bool { + let validators = >::enumerate().map(|(v, _)| v).collect::>(); + let mut nominators = >::enumerate().map(|(n, _)| n).collect::>(); + + // all validators nominate themselves; + nominators.extend(validators.clone()); + + let num_validators = validators.len() as u32; + let num_nominators = nominators.len() as u32; + if num_validators > MAX_VALIDATORS || num_nominators > MAX_NOMINATORS { + debug::native::warn!( + target: "staking", + "Snapshot size too big [{} <> {}][{} <> {}].", + num_validators, + MAX_VALIDATORS, + num_nominators, + MAX_NOMINATORS, + ); + false + } else { + >::put(validators); + >::put(nominators); + true + } + + } + + /// Clears both snapshots of stakers. + fn kill_stakers_snapshot() { + >::kill(); + >::kill(); + } + // MUTABLES (DANGEROUS) /// Update the ledger for a controller. This will also update the stash lock. The lock will @@ -1652,8 +1743,8 @@ impl Module { /// Checks a given solution and if correct and improved, writes it on chain as the queued result /// of the next round. This may be called by both a signed and an unsigned transaction. fn check_and_replace_solution( - winners: Vec, - compact_assignments: CompactAssignments, + winners: Vec, + compact_assignments: CompactOf, compute: ElectionCompute, claimed_score: PhragmenScore, // at: T::BlockNumber, @@ -1672,59 +1763,66 @@ impl Module { ) } - // check if all winners were legit; this is rather cheap. - for w in winners.iter() { - ensure!( - >::exists(&w), - Error::::PhragmenBogusWinner, - ) - } - - // convert into staked. - let staked_assignments = compact_assignments.into_staked::< - _, - _, - T::CurrencyToVote, - >(Self::slashable_balance_of).map_err(|_| Error::::PhragmenBogusNominatorStake)?; - - // build the support map thereof in order to evaluate. - // OPTIMIZATION: we could merge this with the `staked_assignments.iter()` below and - // iterate only once. - let (supports, num_error) = build_support_map::( - &winners, - &staked_assignments, - ); - // This technically checks that all targets in all nominators were among the winners. - ensure!(num_error == 0, Error::::PhragmenBogusEdge); - + // Check that the number of presented winners is sane. Most often we have more candidates + // that we need. Then it should be Self::validator_count(). Else it should be all the + // candidates. + let snapshot_length = >::decode_len() + .map_err(|_| Error::::SnapshotUnavailable)?; + let desired_winners = Self::validator_count().min(snapshot_length as u32); + ensure!(winners.len() as u32 == desired_winners, Error::::PhragmenBogusWinnerCount); + + // decode snapshot validators. + let snapshot_validators = Self::snapshot_validators() + .ok_or(Error::::SnapshotUnavailable)?; + + // check if all winners were legit; this is rather cheap. Replace with accountId. + let winners = winners.into_iter().map(|widx| { + snapshot_validators.get(widx as usize).cloned().ok_or(Error::::PhragmenBogusWinner) + }).collect::, Error>>()?; + + // decode the rest of the snapshot. + let snapshot_nominators = >::snapshot_nominators() + .ok_or(Error::::SnapshotUnavailable)?; + + // helpers + let nominator_at = |i: NominatorIndex| -> Option { + // NOTE: we assume both `ValidatorIndex` and `NominatorIndex` are smaller than usize. + snapshot_nominators.get(i as usize).cloned() + }; + let validator_at = |i: ValidatorIndex| -> Option { + // NOTE: we assume both `ValidatorIndex` and `NominatorIndex` are smaller than usize. + snapshot_validators.get(i as usize).cloned() + }; + let slashable_balance = |w: &T::AccountId| -> ExtendedBalance { + , u64>>::convert( + Self::slashable_balance_of(w) + ) as ExtendedBalance + }; - // Check if the score is the same as the claimed one. - let submitted_score = evaluate_support(&supports); - ensure!(submitted_score == claimed_score, Error::::PhragmenBogusScore); + // un-compact. + let assignments = compact_assignments.into_assignment( + nominator_at, + validator_at, + ).map_err(|_| Error::::PhragmenBogusCompact)?; - // check all nominators being bonded, and actually including the claimed vote, and - // summing up to their ledger stake. - for StakedAssignment { who, distribution } in staked_assignments.iter() { - let maybe_bonded = Self::bonded(&who); + // check all nominators actually including the claimed vote. Also check correct self votes. + // Note that we assume all validators and nominators in `assignments` are properly bonded, + // because they are coming from the snapshot via a given index. + for Assignment { who, distribution } in assignments.iter() { let is_validator = >::exists(&who); let maybe_nomination = Self::nominators(&who); - // it must be bonded. - ensure!( - maybe_bonded.is_some(), - Error::::PhragmenBogusNominator - ); - - // it must be either of - ensure!( - maybe_nomination.is_some() ^ is_validator, - Error::::PhragmenBogusNominator - ); - - let ctrl = maybe_bonded.expect("value is checked to be 'Some'; qed"); - let ledger = Self::ledger(ctrl).ok_or(Error::::NotController)?; - let active_extended_stake = - , u64>>::convert(ledger.active) as u128; + if !(maybe_nomination.is_some() ^ is_validator) { + // 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. + debug::native::error!( + target: "staking", + "detected an error in the staking locking and snapshot." + ); + // abort. + return Err(Error::::PhragmenBogusNominator); + } if !is_validator { // a normal vote @@ -1732,7 +1830,7 @@ impl Module { "exactly one of maybe_validator and maybe_nomination is true. \ is_validator is false; maybe_nomination is some; qed" ); - // NOTE: we don't really have to check here if the sum of all edges are teh + // NOTE: we don't really have to check here if the sum of all edges are the // nominator budged. By definition, compact -> staked ensures this. ensure!( distribution.into_iter().all(|(t, _)| { @@ -1744,20 +1842,37 @@ impl Module { // a self vote ensure!(distribution.len() == 1, Error::::PhragmenBogusSelfVote); ensure!(distribution[0].0 == *who, Error::::PhragmenBogusSelfVote); - // defensive only. A compact assignment of length one does NOT encode the stake - // and it is actually read from chain. Hence, this must always be correct. + // defensive only. A compact assignment of length one does NOT encode the weight and + // it is always created to be 100%. ensure!( - distribution[0].1 == active_extended_stake, + distribution[0].1 == Perbill::one(), Error::::PhragmenBogusSelfVote, ); } } - // Note that we don't need to check again ^^ if a particular target in a nomination was - // actually a candidate. we don't explicitly check this but instead do it indirectly. - // build_support_map tells if any target was not in the winners. Also, we check that all - // the winners were actually candidates. Hence, all of the claimed votes are actually - // voting for valid candidates. All we have to check is if they actually come from the - // claimed nominator or not. + + // convert into staked assignments. + let staked_assignments: Vec> = assignments + .into_iter() + .map(|a| { + let stake = slashable_balance(&a.who); + a.into_staked(stake, true) + }) + .collect(); + + // build the support map thereof in order to evaluate. + // OPTIMIZATION: loop to create the staked assignments but it would bloat the code. Okay for + // now as it does not add to the complexity order. + let (supports, num_error) = build_support_map::( + &winners, + &staked_assignments, + ); + // This technically checks that all targets in all nominators were among the winners. + ensure!(num_error == 0, Error::::PhragmenBogusEdge); + + // Check if the score is the same as the claimed one. + let submitted_score = evaluate_support(&supports); + ensure!(submitted_score == claimed_score, Error::::PhragmenBogusScore); // At last, alles Ok. Exposures and store the result. let to_balance = |e: ExtendedBalance| @@ -1922,6 +2037,9 @@ impl Module { // We have chosen the new validator set. Submission is no longer allowed. >::put(ElectionStatus::None); + // kill the snapshots. + Self::kill_stakers_snapshot(); + // Clear Stakers. for v in Self::current_elected().iter() { >::remove(v); @@ -1978,7 +2096,12 @@ impl Module { let staked_assignments: Vec> = assignments .into_iter() - .map(|a| a.into_staked::<_, _, T::CurrencyToVote>(Self::slashable_balance_of, true)) + .map(|a| { + let stake = , u64>>::convert( + Self::slashable_balance_of(&a.who) + ) as ExtendedBalance; + a.into_staked(stake, true) + }) .collect(); let (supports, _) = build_support_map::( @@ -2338,6 +2461,72 @@ impl ReportOffence } } +/// Disallows any transactions that change the election result to be submitted after the election +/// window is open. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct LockStakingStatus(sp_std::marker::PhantomData); + +impl sp_std::fmt::Debug for LockStakingStatus { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + write!(f, "LockStakingStatus<{:?}>", self.0) + } + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + Ok(()) + } +} + +impl LockStakingStatus { + /// Create new `LockStakingStatus`. + pub fn new() -> Self { + Self(sp_std::marker::PhantomData) + } +} + +impl Default for LockStakingStatus { + fn default() -> Self { + Self::new() + } +} + +impl SignedExtension for LockStakingStatus { + const IDENTIFIER: &'static str = "LockStakingStatus"; + type AccountId = T::AccountId; + type Call = ::Call; + type AdditionalSigned = (); + type DispatchInfo = frame_support::weights::DispatchInfo; + type Pre = (); + + fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) } + + fn validate( + &self, + _who: &Self::AccountId, + call: &Self::Call, + _info: Self::DispatchInfo, + _len: usize, + ) -> TransactionValidity { + // TODO: maybe do this via manual ensure!()? I don't see why it would be _better_ though. + if let Some(inner_call) = call.is_sub_type() { + match inner_call { + Call::::bond(..) | + Call::::validate(..) | + Call::::nominate(..) | + Call::::chill(..) | + Call::::unbond(..) | + Call::::rebond(..) | + Call::::bond_extra(..) => { + return Err(InvalidTransaction::Stale.into()); + } + _ => { /* no problem */ } + } + } + + Ok(Default::default()) + } +} + #[allow(deprecated)] impl frame_support::unsigned::ValidateUnsigned for Module { type Call = Call; @@ -2349,15 +2538,13 @@ impl frame_support::unsigned::ValidateUnsigned for Module { validator_index, signature, ) = call { - // TODO: Double check if this is a good idea. I have to make sure that the offchain code - // is the same for everyone. - if let Some(queued_elected) = Self::queued_elected() { - if queued_elected.compute == ElectionCompute::Authority { + if let Some(queued_score) = Self::queued_score() { + if !offchain_election::is_score_better(queued_score, *score) { debug::native::debug!( target: "staking", - "rejecting unsigned transaction because we already have one from an authority" + "rejecting unsigned transaction because the claimed score is not good enough." ); - return InvalidTransaction::Future.into(); + return InvalidTransaction::Custom(1u8).into(); } } diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 467637e1d61ee..1506442963a70 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -28,6 +28,7 @@ use sp_core::H256; use sp_io; use sp_phragmen::{ build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment, PhragmenScore, + Assignment, }; use sp_runtime::curve::PiecewiseLinear; use sp_runtime::testing::{Header, TestXt, UintAuthorityId}; @@ -70,7 +71,7 @@ thread_local! { static SLASH_DEFER_DURATION: RefCell = RefCell::new(0); static ELECTION_LOOKAHEAD: RefCell = RefCell::new(0); static PERIOD: RefCell = RefCell::new(1); - static LOCAL_KEY_ACCOUNT: RefCell = RefCell::new(10); + pub(crate) static LOCAL_KEY_ACCOUNT: RefCell = RefCell::new(10); } pub struct TestSessionHandler; @@ -479,7 +480,7 @@ impl ExtBuilder { LOCAL_KEY_ACCOUNT.with(|v| *v.borrow_mut() = self.local_key_account); } pub fn build(self) -> sp_io::TestExternalities { - env_logger::init(); + let _ = env_logger::try_init(); self.set_associated_constants(); let mut storage = frame_system::GenesisConfig::default() .build_storage::() @@ -739,13 +740,34 @@ pub fn on_offence_now( on_offence_in_era(offenders, slash_fraction, now) } +/// convert a vector of staked assignments into ratio +pub fn assignment_to_staked(assignments: Vec>) -> Vec> { + assignments + .into_iter() + .map(|a| { + let stake = >::convert( + Staking::slashable_balance_of(&a.who) + ) as ExtendedBalance; + a.into_staked(stake, true) + }) + .collect() +} + +/// convert a vector of assignments into staked +pub fn staked_to_assignment(staked: Vec>) -> Vec> { + staked + .into_iter() + .map(|sa| sa.into_assignment(true)) + .collect() +} + // winners will be chosen by simply their unweighted total backing stake. Nominator stake is // distributed evenly. pub fn horrible_phragmen_with_post_processing( do_reduce: bool, ) -> ( - CompactAssignments, - Vec, + CompactOf, + Vec, PhragmenScore, ) { use std::collections::BTreeMap; @@ -775,7 +797,7 @@ pub fn horrible_phragmen_with_post_processing( .collect(); // create assignments - let mut assignments: Vec> = Vec::new(); + let mut staked_assignment: Vec> = Vec::new(); >::enumerate().for_each(|(who, nomination)| { let mut dist: Vec<(AccountId, ExtendedBalance)> = Vec::new(); nomination.targets.iter().for_each(|v| { @@ -807,7 +829,7 @@ pub fn horrible_phragmen_with_post_processing( last.1 += leftover; } - assignments.push(StakedAssignment { + staked_assignment.push(StakedAssignment { who, distribution: dist, }); @@ -818,7 +840,7 @@ pub fn horrible_phragmen_with_post_processing( let score = { let (_, _, better_score) = do_phragmen_with_post_processing(true, |_| {}); - let support = build_support_map::(&winners, &assignments).0; + let support = build_support_map::(&winners, &staked_assignment).0; let score = evaluate_support(&support); assert!(offchain_election::is_score_better(score, better_score)); @@ -827,10 +849,29 @@ pub fn horrible_phragmen_with_post_processing( }; if do_reduce { - reduce(&mut assignments); + reduce(&mut staked_assignment); } - let compact = >::from_staked(assignments); + let snapshot_validators = Staking::snapshot_validators().unwrap(); + let snapshot_nominators = Staking::snapshot_nominators().unwrap(); + let nominator_index = |a: &AccountId| -> Option { + snapshot_nominators.iter().position(|x| x == a).map(|i| i as NominatorIndex) + }; + let validator_index = |a: &AccountId| -> Option { + snapshot_validators.iter().position(|x| x == a).map(|i| i as ValidatorIndex) + }; + + // convert back to ratio assignment. This takes less space. + let assignments_reduced = staked_to_assignment(staked_assignment); + + let compact = >::from_assignment( + assignments_reduced, + nominator_index, + validator_index, + ).unwrap(); + + // winner ids to index + let winners = winners.into_iter().map(|w| validator_index(&w).unwrap()).collect::>(); (compact, winners, score) } @@ -839,8 +880,8 @@ pub fn do_phragmen_with_post_processing( do_reduce: bool, tweak: impl FnOnce(&mut Vec>), ) -> ( - CompactAssignments, - Vec, + CompactOf, + Vec, PhragmenScore, ) { // run phragmen on the default stuff. @@ -850,10 +891,7 @@ pub fn do_phragmen_with_post_processing( } = Staking::do_phragmen().unwrap(); let winners = winners.into_iter().map(|(w, _)| w).collect(); - let mut staked: Vec> = assignments - .into_iter() - .map(|a| a.into_staked::<_, _, CurrencyToVoteHandler>(Staking::slashable_balance_of, true)) - .collect(); + let mut staked = assignment_to_staked(assignments); // apply custom tweaks. awesome for testing. tweak(&mut staked); @@ -862,10 +900,34 @@ pub fn do_phragmen_with_post_processing( reduce(&mut staked); } + // compute score let (support_map, _) = build_support_map::(&winners, &staked); let score = evaluate_support::(&support_map); - let compact = >::from_staked(staked); + // convert back to ratio assignment. This takes less space. + let snapshot_validators = Staking::snapshot_validators().unwrap(); + let snapshot_nominators = Staking::snapshot_nominators().unwrap(); + let nominator_index = |a: &AccountId| -> Option { + snapshot_nominators.iter().position(|x| x == a).map(|i| i as NominatorIndex) + }; + let validator_index = |a: &AccountId| -> Option { + snapshot_validators.iter().position(|x| x == a).map(|i| i as ValidatorIndex) + }; + + let assignments_reduced: Vec> = staked + .into_iter() + .map(|sa| sa.into_assignment(true)) + .collect(); + + let compact = >::from_assignment( + assignments_reduced, + nominator_index, + validator_index, + ).unwrap(); + + + // winner ids to index + let winners = winners.into_iter().map(|w| validator_index(&w).unwrap()).collect::>(); (compact, winners, score) } diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index f6faa027bd623..74ec5fc87b61a 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -16,12 +16,16 @@ //! Helpers for offchain worker election. -use crate::{Call, CompactAssignments, Module, SessionInterface, Trait}; +use crate::{ + Call, Module, SessionInterface, Trait, BalanceOf, ValidatorIndex, NominatorIndex, CompactOf, +}; use codec::Encode; use frame_system::offchain::{SubmitUnsignedTransaction}; -use sp_phragmen::{reduce, ExtendedBalance, PhragmenResult, StakedAssignment}; -use sp_std::{prelude::*, cmp::Ordering}; +use sp_phragmen::{ reduce, ExtendedBalance, PhragmenResult, StakedAssignment, Assignment}; +use sp_std::{prelude::*, cmp::Ordering, convert::TryInto}; use sp_runtime::{RuntimeAppPublic, RuntimeDebug}; +use sp_runtime::offchain::storage::StorageValueRef; +use sp_runtime::traits::Convert; #[derive(RuntimeDebug)] pub(crate) enum OffchainElectionError { @@ -30,7 +34,42 @@ pub(crate) enum OffchainElectionError { NoSigningKey, /// Phragmen election returned None. This means less candidate that minimum number of needed /// validators were present. The chain is in trouble and not much that we can do about it. - FailedElection, + ElectionFailed, + /// The snapshot data is not available. + SnapshotUnavailable, + /// Failed to create the compact type. + CompactFailed, +} + +pub(crate) const OFFCHAIN_HEAD_DB: &[u8] = b"parity/staking-election/"; +const OFFCHAIN_REPEAT: u32 = 5; + +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>| { + dbg!(maybe_head, now); + 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), + } } /// Compares two sets of phragmen scores based on desirability and returns true if `that` is @@ -70,11 +109,16 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti .and_then(|vk| if *vk == k { Some((index, vk)) } else { None }) ) ) { + // make sure that the snapshot is available. + let snapshot_validators = >::snapshot_validators() + .ok_or(OffchainElectionError::SnapshotUnavailable)?; + let snapshot_nominators = >::snapshot_nominators() + .ok_or(OffchainElectionError::SnapshotUnavailable)?; // k is a local key who is also among the validators. let PhragmenResult { winners, assignments, - } = >::do_phragmen().ok_or(OffchainElectionError::FailedElection)?; + } = >::do_phragmen().ok_or(OffchainElectionError::ElectionFailed)?; // convert winners into just account ids. let winners: Vec = winners.into_iter().map(|(w, _)| w).collect(); @@ -82,10 +126,12 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti // convert into staked. This is needed to be able to reduce. let mut staked: Vec> = assignments .into_iter() - .map(|a| a.into_staked::<_, _, T::CurrencyToVote>( - >::slashable_balance_of, - true, - )) + .map(|a| { + let stake = , u64>>::convert( + >::slashable_balance_of(&a.who) + ) as ExtendedBalance; + a.into_staked(stake, true) + }) .collect(); // reduce the assignments. This will remove some additional edges. @@ -95,29 +141,48 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti let (support, _) = sp_phragmen::build_support_map::(&winners, &staked); let score = sp_phragmen::evaluate_support(&support); - // compact encode the assignment. - let compact = >::from_staked(staked); - - #[cfg(feature = "signed")] - { - unimplemented!(); - } - - #[cfg(not(feature = "signed"))] - { - let signature_payload = - (winners.clone(), compact.clone(), score, index as u32).encode(); - let signature = pubkey.sign(&signature_payload).unwrap(); - let call: ::Call = Call::submit_election_solution_unsigned( - winners, - compact, - score, - index as u32, - signature, + // helpers for building the compact + let nominator_index = |a: &T::AccountId| -> Option { + snapshot_nominators.iter().position(|x| x == a).and_then(|i| + >::try_into(i).ok() + ) + }; + let validator_index = |a: &T::AccountId| -> Option { + snapshot_validators.iter().position(|x| x == a).and_then(|i| + >::try_into(i).ok() ) - .into(); - let _result = T::SubmitTransaction::submit_unsigned(call); - } + }; + + // convert back to ratio assignment. This takes less space. + let assignments_reduced: Vec> = staked + .into_iter() + .map(|sa| sa.into_assignment(true)) + .collect(); + + // compact encode the assignment. + let compact = >::from_assignment( + assignments_reduced, + nominator_index, + validator_index, + ).map_err(|_| OffchainElectionError::CompactFailed)?; + + // convert winners to index as well + let winners = winners.into_iter().map(|w| + validator_index(&w).expect("winners are chosen from the snapshot list; the must have index; qed") + ).collect::>(); + + let signature_payload = + (winners.clone(), compact.clone(), score, index as u32).encode(); + let signature = pubkey.sign(&signature_payload).unwrap(); + let call: ::Call = Call::submit_election_solution_unsigned( + winners, + compact, + score, + index as u32, + signature, + ) + .into(); + let _result = T::SubmitTransaction::submit_unsigned(call); Ok(()) } else { diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 712774363a0ab..b8dc92e80d2b2 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2683,9 +2683,9 @@ fn version_initialized() { mod offchain_phragmen { use crate::*; use mock::*; - use frame_support::{assert_ok, assert_noop}; + use frame_support::{assert_ok, assert_noop, debug}; use substrate_test_utils::assert_eq_uvec; - use sp_runtime::traits::OffchainWorker; + use sp_runtime::{traits::OffchainWorker, Perbill}; use sp_core::offchain::{ OffchainExt, TransactionPoolExt, @@ -2699,6 +2699,9 @@ mod offchain_phragmen { type DummyT = dummy_sr25519::AuthorityId; + fn percent(x: u32) -> Perbill { + Perbill::from_percent(x) + } /// setup a new set of validators and nominator storage items independent of the parent mock /// file. This produces a edge graph that can be reduced. @@ -2724,16 +2727,18 @@ mod offchain_phragmen { let (pool, state) = TestTransactionPoolExt::new(); let keystore = KeyStore::new(); + let account = LOCAL_KEY_ACCOUNT.with(|v| *v.borrow()); + let key = dummy_sr25519::dummy_key_for(account); let _ = keystore.write().insert_unknown( ::ID, - &format!("{}/staking{}", mock::PHRASE, 11), - dummy_sr25519::dummy_key_for(11).as_ref(), + &format!("{}/staking{}", mock::PHRASE, account), + key.as_ref(), ).unwrap(); - frame_support::debug::native::debug!( + debug::native::debug!( target: "staking", "generated key for account {}: {:?}", - 11, - dummy_sr25519::dummy_key_for(11), + account, + key, ); ext.register_extension(OffchainExt::new(offchain)); ext.register_extension(TransactionPoolExt::new(pool)); @@ -2741,33 +2746,6 @@ mod offchain_phragmen { state } - #[test] - fn compact_assignment_into_staked_overflow() { - ExtBuilder::default().build().execute_with(|| { - // everyone's stake is 10. - let stake_of = |_who: &AccountId| -> Balance { 10 }; - - // a single vote cannot be faked. - let malicious_assignments = vec![ - StakedAssignment { - who: 1, - distribution: vec![(10, 5), (20, 8), (30, 5)] - } - ]; - - let compact = >::from_staked( - malicious_assignments, - ); - - assert_eq!(compact.votes3[0], (1, [(10, 5), (20, 8)], 30)); - - // converting this back will yield error. This must be provided by the compact lib code. - assert!( - compact.into_staked::<_, _, CurrencyToVoteHandler>(&stake_of).is_err() - ); - }) - } - #[test] fn score_comparison_is_lexicographical() { // only better in the fist parameter, worse in the other two ✅ @@ -2828,25 +2806,40 @@ mod offchain_phragmen { run_to_block(10); assert_session_era!(1, 0); assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert!(Staking::snapshot_nominators().is_none()); + assert!(Staking::snapshot_validators().is_none()); run_to_block(18); assert_session_era!(1, 0); assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert!(Staking::snapshot_nominators().is_none()); + assert!(Staking::snapshot_validators().is_none()); run_to_block(40); assert_session_era!(4, 0); assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert!(Staking::snapshot_nominators().is_none()); + assert!(Staking::snapshot_validators().is_none()); run_to_block(46); assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert!(Staking::snapshot_nominators().is_none()); + assert!(Staking::snapshot_validators().is_none()); + run_to_block(47); assert_eq!(Staking::era_election_status(), ElectionStatus::Open(47)); + assert!(Staking::snapshot_nominators().is_some()); + assert!(Staking::snapshot_validators().is_some()); run_to_block(49); assert_eq!(Staking::era_election_status(), ElectionStatus::Open(47)); + assert!(Staking::snapshot_nominators().is_some()); + assert!(Staking::snapshot_validators().is_some()); run_to_block(50); assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert!(Staking::snapshot_nominators().is_none()); + assert!(Staking::snapshot_validators().is_none()); }) } @@ -2875,6 +2868,7 @@ mod offchain_phragmen { || { run_to_block(12); assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); + assert!(Staking::snapshot_validators().is_some()); let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); assert_ok!(Staking::submit_election_solution( @@ -2940,11 +2934,15 @@ mod offchain_phragmen { .build() .execute_with( || { - run_to_block(11); - // submission is allowed + // submission is not yet allowed assert_eq!(Staking::era_election_status(), ElectionStatus::None); + + // create all the indices just to build the solution. + Staking::create_stakers_snapshot(); let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + Staking::kill_stakers_snapshot(); + assert_noop!( Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenEarlySubmission, @@ -2970,7 +2968,7 @@ mod offchain_phragmen { assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); // a bad solution - let (compact, winners, score) = horrible_phragmen_with_post_processing(false); + let (compact, winners, score) = horrible_phragmen_with_post_processing(true); assert_noop!( Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenWeakSubmission, @@ -2992,7 +2990,7 @@ mod offchain_phragmen { run_to_block(12); // a meeeeh solution - let (compact, winners, score) = horrible_phragmen_with_post_processing(false); + let (compact, winners, score) = horrible_phragmen_with_post_processing(true); assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); // a better solution @@ -3002,8 +3000,8 @@ mod offchain_phragmen { } #[test] - #[cfg(feature = "signed")] - fn offchain_worker_runs_when_window_open_signed() { + #[allow(deprecated)] + fn offchain_worker_runs_when_window_open_unsigned() { // at the end of the first finalized block with ElectionStatus::open(_), it should execute. let mut ext = ExtBuilder::default() .offchain_phragmen_ext() @@ -3020,24 +3018,33 @@ mod offchain_phragmen { assert_eq!(state.read().transactions.len(), 1); let encoded = state.read().transactions[0].clone(); - let extrinsic = Extrinsic::decode(&mut &*encoded).unwrap(); - - let author = extrinsic.0.unwrap().0; - assert_eq!(author, 11); + let extrinsic: Extrinsic = Decode::decode(&mut &*encoded).unwrap(); let call = extrinsic.1; - match call { - mock::Call::Staking(crate::Call::submit_election_solution(_, _, _)) => {}, - _ => panic!("wrong call submitted"), + let inner = match call { + mock::Call::Staking(inner) => { inner }, }; + + // pass this call to ValidateUnsigned + let signing_key = dummy_sr25519::dummy_key_for(11); + assert_eq!( + ::validate_unsigned(&inner), + TransactionValidity::Ok(ValidTransaction { + priority: TransactionPriority::max_value(), + requires: vec![], + provides: vec![(Staking::current_era(), signing_key).encode()], + longevity: TryInto::::try_into( + ::ElectionLookahead::get() + ).unwrap_or(150_u64), + propagate: true, + }) + ) }) } #[test] - #[cfg(not(feature = "signed"))] #[allow(deprecated)] - fn offchain_worker_runs_when_window_open_unsigned() { - // at the end of the first finalized block with ElectionStatus::open(_), it should execute. + fn mediocre_submission_from_authority_is_early_rejected() { let mut ext = ExtBuilder::default() .offchain_phragmen_ext() .validator_count(4) @@ -3045,15 +3052,17 @@ mod offchain_phragmen { let state = offchainify(&mut ext); ext.execute_with(||{ run_to_block(12); + // put a good solution on-chain + let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + assert_ok!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score) + ); - // local key 11 is in the elected set. - assert_eq_uvec!(Staking::current_elected(), vec![11, 21, 31]); - assert_eq!(state.read().transactions.len(), 0); + // now run the offchain worker in the same chain state. Staking::offchain_worker(12); assert_eq!(state.read().transactions.len(), 1); let encoded = state.read().transactions[0].clone(); - // WTF just happened? I changed Extrinsic::decode() to this and now it works? let extrinsic: Extrinsic = Decode::decode(&mut &*encoded).unwrap(); let call = extrinsic.1; @@ -3062,26 +3071,15 @@ mod offchain_phragmen { }; // pass this call to ValidateUnsigned - let signing_key = dummy_sr25519::dummy_key_for(11); assert_eq!( ::validate_unsigned(&inner), - TransactionValidity::Ok(ValidTransaction { - priority: TransactionPriority::max_value(), - requires: vec![], - provides: vec![(Staking::current_era(), signing_key).encode()], - longevity: TryInto::::try_into( - ::ElectionLookahead::get() - ).unwrap_or(150_u64), - propagate: true, - }) + TransactionValidity::Err(InvalidTransaction::Custom(1u8).into()), ) }) } #[test] - fn invalid_phragmen_result_build_support_extra_edge() { - // an edge that does not point to any of the winners. This should be caught in - // build_support_map. All the targets presented must be one of the presented winners. + fn invalid_phragmen_result_correct_number_of_winners() { ExtBuilder::default() .offchain_phragmen_ext() .validator_count(4) @@ -3092,27 +3090,25 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (mut compact, winners, scores) = do_phragmen_with_post_processing(true, |_| {}); - assert_eq_uvec!(winners, vec![10, 20, 30, 40]); + ValidatorCount::put(3); + let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + ValidatorCount::put(4); - // inject a correct nominator voting for a correct, but non-winner validator. - compact.votes1.push((1, 999)); + assert_eq!(winners.len(), 3); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, scores), - Error::::PhragmenBogusEdge, + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusWinnerCount, ); }) } #[test] - fn invalid_phragmen_result_build_support_extra_winner() { - // if some bogus winner is not a candidate. This and - // `invalid_phragmen_result_build_support_extra_edge` together ensure that all targets are - // actually validators with correct intention. + fn invalid_phragmen_result_correct_number_of_winners_1() { + // if we have too little validators, then the number of candidates is the bound. ExtBuilder::default() .offchain_phragmen_ext() - .validator_count(4) + .validator_count(8) // we simply cannot elect 8 .has_stakers(false) .build() .execute_with( @@ -3120,24 +3116,25 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (compact, mut winners, score) = do_phragmen_with_post_processing(true, |_| {}); - winners.push(999); + ValidatorCount::put(3); + let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + ValidatorCount::put(4); + + assert_eq!(winners.len(), 3); assert_noop!( Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusWinner, + Error::::PhragmenBogusWinnerCount, ); }) } #[test] - fn invalid_phragmen_result_invalid_target() { - // A valid voter who voted for someone who is not a candidate. Fake candidate is injected as - // winner. Hence, build support map will not catch this. But the check for all winners being - // validators will. + fn invalid_phragmen_result_correct_number_of_winners_2() { + // if we have too little validators, then the number of candidates is the bound. ExtBuilder::default() .offchain_phragmen_ext() - .validator_count(4) + .validator_count(8) // we simply cannot elect 8 .has_stakers(false) .build() .execute_with( @@ -3145,40 +3142,76 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - // bond a malicious nominator - bond_nominator(666, 667, 1, vec![10]); + let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); - let sp_phragmen::PhragmenResult { - winners, - assignments, - } = Staking::do_phragmen().unwrap(); - let mut winners: Vec = winners.into_iter().map(|(w, _)| w).collect(); + assert_eq!(winners.len(), 4); - let mut staked: Vec> = assignments - .into_iter() - .map(|a| a.into_staked::<_, _, CurrencyToVoteHandler>(Staking::slashable_balance_of, true)) - .collect(); + // all good. We chose 4 and it works. + assert_ok!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + ); + }) + } - let (support, _) = sp_phragmen::build_support_map::(&winners, &staked); - let score = sp_phragmen::evaluate_support::(&support); + #[test] + fn invalid_phragmen_result_out_of_bound_nominator_index() { + // A nominator index which is simply invalid + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); - staked.push(sp_phragmen::StakedAssignment { who: 666, distribution: vec![(999, 1)] }); - winners.push(999); + assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); + assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); + let (mut compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); - sp_phragmen::reduce(&mut staked); - let compact = >::from_staked(staked); + // index 9 doesn't exist. + compact.votes1.push((9, 2)); + // The error type sadly cannot be more specific now. assert_noop!( Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusWinner, + Error::::PhragmenBogusCompact, ); }) } + #[test] + fn invalid_phragmen_result_out_of_bound_validator_index() { + // A validator index which is out of bound + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); + assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); + let (mut compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + + // index 4 doesn't exist. + compact.votes1.push((3, 4)); + + // The error type sadly cannot be more specific now. + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusCompact, + ); + }) + } #[test] - fn invalid_phragmen_result_non_nominating_voter() { - // A voter who is not a nominator + fn invalid_phragmen_result_out_of_bound_winner_index() { + // A winner index which is simply invalid ExtBuilder::default() .offchain_phragmen_ext() .validator_count(4) @@ -3189,29 +3222,45 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (compact, winners, score) = do_phragmen_with_post_processing(false, |a| { - // inject a wrong nominator voting for a valid validator. - a.push(StakedAssignment:: { who: 999, distribution: vec![(10, 10)]}); - }); + assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); + assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); + let (compact, _, score) = do_phragmen_with_post_processing(true, |_| {}); + + // index 4 doesn't exist. + let winners = vec![0, 1, 2, 4]; - // This will cause a score mismatch since 99 doesn't have any money. assert_noop!( - Staking::submit_election_solution( - Origin::signed(10), - winners.clone(), - compact.clone(), - score, - ), - Error::::PhragmenBogusScore, // TODO: we can catch this in PhragmenBogusNominatorStake. + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusWinner, ); + }) + } - // even when it is bonded but not nominating. - let _ = Balances::make_free_balance_be(&999, 10); - assert_ok!(Staking::bond(Origin::signed(999), 998, 10, Default::default())); + #[test] + fn invalid_phragmen_result_non_winner_validator_index() { + // An edge that points to a correct validator index who is NOT a winner. This is very + // similar to the test that raises `PhragmenBogusNomination`. + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(2) + .has_stakers(false) + .build() + .execute_with( + || { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); + assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); + let (compact, winners, score) = do_phragmen_with_post_processing(true, |a| { + a.iter_mut().find(|x| x.who == 5).map(|x| + x.distribution = vec![(20, 50), (40, 30), (30, 20)] + ); + }); assert_noop!( Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusNominator, + Error::::PhragmenBogusEdge, ); }) } @@ -3274,7 +3323,7 @@ mod offchain_phragmen { #[test] fn invalid_phragmen_result_over_stake() { - // A valid voter who's total distributed stake is more than what they bond + // Someone's edge ratios sums to more than 100%. ExtBuilder::default() .offchain_phragmen_ext() .validator_count(4) @@ -3285,14 +3334,19 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (mut compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + // Note: we don't reduce here to be able to tweak votes3. votes3 will vanish if you + // reduce. + let (mut compact, winners, score) = do_phragmen_with_post_processing(false, |_| {}); - // 3 has: (3, (20, 50), 40) which means evenly distributed between 20 and 40 (50 each). - compact.votes2.iter_mut().find(|x| x.0 == 3).map(|(_who, v1, _v2)| v1.1 = 120); + if let Some(c) = compact.votes3.iter_mut().find(|x| x.0 == 0) { + // by default it should have been (0, [(2, 33%), (1, 33%)], 0) + // now the sum is above 100% + c.1 = [(2, percent(66)), (1, percent(66))]; + } assert_noop!( Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusNominatorStake, + Error::::PhragmenBogusCompact, ); }) } @@ -3301,14 +3355,14 @@ mod offchain_phragmen { fn invalid_phragmen_result_under_stake() { // at the time of this writing, we cannot under stake someone. The compact assignment works // in a way that some of the stakes are presented by the submitter, and the last one is read - // from chain by subtracting the rest from total. This test is only here as a demonstration. + // from chain by subtracting the rest from total. Hence, the sum is always correct. + // This test is only here as a demonstration. } #[test] fn invalid_phragmen_result_invalid_target_stealing() { - // A valid voter who voted for someone who is a candidate, but is actually not nominated by + // A valid voter who voted for someone who is a candidate, but is actually NOT nominated by // this nominator. - // A valid voter who's total distributed stake is more than what they bond ExtBuilder::default() .offchain_phragmen_ext() .validator_count(4) @@ -3320,7 +3374,7 @@ mod offchain_phragmen { run_to_block(12); let (compact, winners, score) = do_phragmen_with_post_processing(false, |a| { - // 3 has: (3, (20, 50), 40). We add a fake vote to 30. The stake sum is still + // 3 only voted for 20 and 40. We add a fake vote to 30. The stake sum is still // correctly 100. a.iter_mut().find(|x| x.who == 3).map(|x| { x.distribution = vec![(20, 50), (40, 30), (30, 20)] @@ -3358,8 +3412,79 @@ mod offchain_phragmen { } #[test] - fn session_keys_work() { - unimplemented!(); + fn session_interface_work() { + let mut ext = ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .build(); + let _ = offchainify(&mut ext); + ext.execute_with(|| { + assert_eq_uvec!( + >::keys::(), + >::enumerate() + .map(|(acc, _)| (acc, Some(dummy_sr25519::dummy_key_for(acc)))) + .collect::)>>() + ); + }) + } + + #[test] + fn offchain_storage_is_set() { + let mut ext = ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .build(); + let state = offchainify(&mut ext); + + ext.execute_with(|| { + use offchain_election::OFFCHAIN_HEAD_DB; + use sp_runtime::offchain::storage::StorageValueRef; + + run_to_block(12); + + Staking::offchain_worker(12); + // it works + assert_eq!(state.read().transactions.len(), 1); + + // and it is set + let storage = StorageValueRef::persistent(&OFFCHAIN_HEAD_DB); + assert_eq!(storage.get::().unwrap().unwrap(), 12); + }) + } + + #[test] + fn offchain_storage_prevents_duplicate() { + let mut ext = ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .build(); + let _ = offchainify(&mut ext); + + ext.execute_with(|| { + use offchain_election::OFFCHAIN_HEAD_DB; + use sp_runtime::offchain::storage::StorageValueRef; + let storage = StorageValueRef::persistent(&OFFCHAIN_HEAD_DB); + + // first run -- ok + run_to_block(12); + assert_eq!( + offchain_election::set_check_offchain_execution_status::(12), + Ok(()), + ); + assert_eq!(storage.get::().unwrap().unwrap(), 12); + + // re-execute after the next. not allowed. + assert_eq!( + offchain_election::set_check_offchain_execution_status::(13), + Err("recently executed."), + ); + + // a fork like situation -- re-execute 12. But it won't go through. + assert_eq!( + offchain_election::set_check_offchain_execution_status::(11), + Err("fork."), + ); + }) } } diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index b0592b84bb9b8..e033ed7b7ad06 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -32,8 +32,14 @@ const PREFIX: &'static str = "votes"; /// Generates a struct to store the phragmen assignments in a compact way. The struct can only store /// distributions up to the given input count. The given count must be greater than 2. /// +/// 3 generic types must be given to the type +/// +/// - `V`: identifier/index type of the voter. +/// - `T`: identifier/index type of the target. +/// - `W`: any type used as the edge weight. +/// /// ```rust -/// // generate a struct with account Identifier u32 and edge weight u128, with maximum supported +/// // generate a struct with nominator and edge weight u128, with maximum supported /// // edge per voter of 32. /// generate_compact_solution_type(TestCompact, 32) /// ``` @@ -49,23 +55,23 @@ const PREFIX: &'static str = "votes"; /// An example expansion of length 16 is as follows: /// /// ```rust -/// struct TestCompact { -/// votes1: Vec<(AccountId, AccountId)>, -/// votes2: Vec<(AccountId, (AccountId, W), AccountId)>, -/// votes3: Vec<(AccountId, [(AccountId, W); 2usize], AccountId)>, -/// votes4: Vec<(AccountId, [(AccountId, W); 3usize], AccountId)>, -/// votes5: Vec<(AccountId, [(AccountId, W); 4usize], AccountId)>, -/// votes6: Vec<(AccountId, [(AccountId, W); 5usize], AccountId)>, -/// votes7: Vec<(AccountId, [(AccountId, W); 6usize], AccountId)>, -/// votes8: Vec<(AccountId, [(AccountId, W); 7usize], AccountId)>, -/// votes9: Vec<(AccountId, [(AccountId, W); 8usize], AccountId)>, -/// votes10: Vec<(AccountId, [(AccountId, W); 9usize], AccountId)>, -/// votes11: Vec<(AccountId, [(AccountId, W); 10usize], AccountId)>, -/// votes12: Vec<(AccountId, [(AccountId, W); 11usize], AccountId)>, -/// votes13: Vec<(AccountId, [(AccountId, W); 12usize], AccountId)>, -/// votes14: Vec<(AccountId, [(AccountId, W); 13usize], AccountId)>, -/// votes15: Vec<(AccountId, [(AccountId, W); 14usize], AccountId)>, -/// votes16: Vec<(AccountId, [(AccountId, W); 15usize], AccountId)>, +/// struct TestCompact { +/// votes1: Vec<(V, T)>, +/// votes2: Vec<(V, (T, W), T)>, +/// votes3: Vec<(V, [(T, W); 2usize], T)>, +/// votes4: Vec<(V, [(T, W); 3usize], T)>, +/// votes5: Vec<(V, [(T, W); 4usize], T)>, +/// votes6: Vec<(V, [(T, W); 5usize], T)>, +/// votes7: Vec<(V, [(T, W); 6usize], T)>, +/// votes8: Vec<(V, [(T, W); 7usize], T)>, +/// votes9: Vec<(V, [(T, W); 8usize], T)>, +/// votes10: Vec<(V, [(T, W); 9usize], T)>, +/// votes11: Vec<(V, [(T, W); 10usize], T)>, +/// votes12: Vec<(V, [(T, W); 11usize], T)>, +/// votes13: Vec<(V, [(T, W); 12usize], T)>, +/// votes14: Vec<(V, [(T, W); 13usize], T)>, +/// votes15: Vec<(V, [(T, W); 14usize], T)>, +/// votes16: Vec<(V, [(T, W); 15usize], T)>, /// } /// ``` #[proc_macro] @@ -76,8 +82,9 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { count, } = syn::parse_macro_input!(item as CompactSolutionDef); - let account_type = GenericArgument::Type(Type::Verbatim(quote!(AccountId))); - let weight_type = GenericArgument::Type(Type::Verbatim(quote!(weight))); + let voter_type = GenericArgument::Type(Type::Verbatim(quote!(V))); + let target_type = GenericArgument::Type(Type::Verbatim(quote!(T))); + let weight_type = GenericArgument::Type(Type::Verbatim(quote!(W))); let imports = imports().unwrap_or_else(|e| e.to_compile_error()); @@ -85,19 +92,22 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { vis, ident.clone(), count, - account_type.clone(), + voter_type.clone(), + target_type.clone(), weight_type, ).unwrap_or_else(|e| e.to_compile_error()); let from_into_impl_assignment = convert_impl_for_assignment( ident.clone(), - account_type.clone(), + voter_type.clone(), + target_type.clone(), count, ); let from_into_impl_staked_assignment = convert_impl_for_staked_assignment( ident, - account_type, + voter_type, + target_type, count, ); @@ -113,7 +123,8 @@ fn struct_def( vis: syn::Visibility, ident: syn::Ident, count: usize, - account_type: GenericArgument, + voter_type: GenericArgument, + target_type: GenericArgument, weight_type: GenericArgument, ) -> Result { if count <= 2 { @@ -125,12 +136,12 @@ fn struct_def( let singles = { let name = field_name_for(1); - quote!(#name: Vec<(#account_type, #account_type)>,) + quote!(#name: Vec<(#voter_type, #target_type)>,) }; let doubles = { let name = field_name_for(2); - quote!(#name: Vec<(#account_type, (#account_type, #weight_type), #account_type)>,) + quote!(#name: Vec<(#voter_type, (#target_type, #weight_type), #target_type)>,) }; let rest = (3..=count).map(|c| { @@ -138,25 +149,24 @@ fn struct_def( let array_len = c - 1; quote!( #field_name: Vec<( - #account_type, - [(#account_type, #weight_type); #array_len], - #account_type + #voter_type, + [(#target_type, #weight_type); #array_len], + #target_type )>, ) }).collect::(); - let compact_def = quote! ( - /// A struct to encode a `Vec` or `Vec<_phragmen::Assignment>` of the phragmen module + Ok(quote! ( + /// A struct to encode a `Vec` or `Vec` of the phragmen module /// in a compact way. #[derive(Default, PartialEq, Eq, Clone, _sp_runtime::RuntimeDebug, _codec::Encode, _codec::Decode)] - #vis struct #ident<#account_type, #weight_type> { + #vis struct #ident<#voter_type, #target_type, #weight_type, A> { + _marker: sp_std::marker::PhantomData, #singles #doubles #rest } - ); - - Ok(compact_def) + )) } fn imports() -> Result { @@ -199,60 +209,55 @@ fn imports() -> Result { fn convert_impl_for_assignment( ident: syn::Ident, - account_type: GenericArgument, + voter_type: GenericArgument, + target_type: GenericArgument, count: usize ) -> TokenStream2 { let from_impl_single = { let name = field_name_for(1); - quote!(1 => compact.#name.push((who, distribution[0].clone().0)),) + quote!(1 => compact.#name.push( + ( + index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, + index_of_target(&distribution[0].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, + ) + ),) }; let from_impl_double = { let name = field_name_for(2); - quote!(2 => compact.#name.push((who, distribution[0].clone(), distribution[1].clone().0)),) + quote!(2 => compact.#name.push( + ( + index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, + ( + index_of_target(&distribution[0].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, + distribution[0].1, + ), + index_of_target(&distribution[1].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, + ) + ),) }; let from_impl_rest = (3..=count).map(|c| { - let inner = (0..c-1).map(|i| quote!(distribution[#i].clone(),)).collect::(); + let inner = (0..c-1).map(|i| + quote!((index_of_target(&distribution[#i].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, distribution[#i].1),) + ).collect::(); let field_name = field_name_for(c); let last_index = c - 1; - let last = quote!(distribution[#last_index].clone().0); + let last = quote!(index_of_target(&distribution[#last_index].0).ok_or(_phragmen::Error::CompactInvalidIndex)?); quote!( - #c => compact.#field_name.push((who, [#inner], #last)), + #c => compact.#field_name.push((index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, [#inner], #last)), ) }).collect::(); - let from_impl = quote!( - impl<#account_type: _codec::Codec + Default + Clone> - From>> - for #ident<#account_type, _sp_runtime::Perbill> - { - fn from( - assignments: Vec<_phragmen::Assignment<#account_type>>, - ) -> Self { - let mut compact: #ident<#account_type, _sp_runtime::Perbill> = Default::default(); - assignments.into_iter().for_each(|_phragmen::Assignment { who, distribution } | { - match distribution.len() { - #from_impl_single - #from_impl_double - #from_impl_rest - _ => { - _sp_runtime::print("assignment length too big. ignoring"); - } - } - }); - compact - } - } - ); - let into_impl_single = { let name = field_name_for(1); quote!( - for (who, target) in self.#name { + for (voter_index, target_index) in self.#name { assignments.push(_phragmen::Assignment { - who, - distribution: vec![(target, _sp_runtime::Perbill::one())], + who: voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, + distribution: vec![ + (target_at(target_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, _sp_runtime::Perbill::one()) + ], }) } ) @@ -260,17 +265,22 @@ fn convert_impl_for_assignment( let into_impl_double = { let name = field_name_for(2); quote!( - for (who, (t1, p1), t2) in self.#name { + for (voter_index, (t1_idx, p1), t2_idx) in self.#name { if p1 >= _sp_runtime::Perbill::one() { return Err(_phragmen::Error::CompactStakeOverflow); } + // defensive only. Since perbill doesn't have `Sub`. - let p2 = _sp_runtime::traits::Saturating::saturating_sub(_sp_runtime::Perbill::one(), p1); + let p2 = _sp_runtime::traits::Saturating::saturating_sub( + _sp_runtime::Perbill::one(), + p1, + ); + assignments.push( _phragmen::Assignment { - who, + who: voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, distribution: vec![ - (t1, p1), - (t2, p2), + (target_at(t1_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, p1), + (target_at(t2_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, p2), ] }); } @@ -279,39 +289,80 @@ fn convert_impl_for_assignment( let into_impl_rest = (3..=count).map(|c| { let name = field_name_for(c); quote!( - for (who, inners, t_last) in self.#name { + for (voter_index, inners, t_last_idx) in self.#name { let mut sum = _sp_runtime::Perbill::zero(); let mut inners_parsed = inners .iter() - .map(|(ref c, p)| { + .map(|(ref t_idx, p)| { sum = _sp_runtime::traits::Saturating::saturating_add(sum, *p); - (c.clone(), *p) - }).collect::>(); + let target = target_at(*t_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?; + Ok((target, *p)) + }) + .collect::, _phragmen::Error>>()?; if sum >= _sp_runtime::Perbill::one() { return Err(_phragmen::Error::CompactStakeOverflow); } + // defensive only. Since perbill doesn't have `Sub`. - let p_last = _sp_runtime::traits::Saturating::saturating_sub(_sp_runtime::Perbill::one(), sum); - inners_parsed.push((t_last, p_last)); + let p_last = _sp_runtime::traits::Saturating::saturating_sub( + _sp_runtime::Perbill::one(), + sum, + ); + + inners_parsed.push((target_at(t_last_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, p_last)); assignments.push(_phragmen::Assignment { - who, + who: voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, distribution: inners_parsed, }); } ) }).collect::(); - let into_impl = quote!( - impl<#account_type: _codec::Codec + Default + Clone> - _sp_std::convert::TryInto>> - for #ident<#account_type, _sp_runtime::Perbill> + let from_into_impl = quote!( + impl< + #voter_type: _codec::Codec + Default + Copy, + #target_type: _codec::Codec + Default + Copy, + A: _codec::Codec + Default + Clone, + > + #ident<#voter_type, #target_type, _sp_runtime::Perbill, A> { - type Error = _phragmen::Error; + pub fn from_assignment( + assignments: Vec<_phragmen::Assignment>, + index_of_voter: FV, + index_of_target: FT, + ) -> Result + where + for<'r> FV: Fn(&'r A) -> Option<#voter_type>, + for<'r> FT: Fn(&'r A) -> Option<#target_type>, + { + let mut compact: #ident< + #voter_type, + #target_type, + _sp_runtime::Perbill, + A, + > = Default::default(); + + for _phragmen::Assignment { who, distribution } in assignments { + match distribution.len() { + #from_impl_single + #from_impl_double + #from_impl_rest + _ => { + return Err(_phragmen::Error::CompactTargetOverflow); + } + } + }; + Ok(compact) + } - fn try_into(self) -> Result>, Self::Error> { - let mut assignments: Vec<_phragmen::Assignment<#account_type>> = Default::default(); + pub fn into_assignment( + self, + voter_at: impl Fn(#voter_type) -> Option, + target_at: impl Fn(#target_type) -> Option, + ) -> Result>, _phragmen::Error> { + let mut assignments: Vec<_phragmen::Assignment> = Default::default(); #into_impl_single #into_impl_double #into_impl_rest @@ -321,51 +372,60 @@ fn convert_impl_for_assignment( } ); - quote!( - #from_impl - #into_impl - ) + from_into_impl } fn convert_impl_for_staked_assignment( ident: syn::Ident, - account_type: GenericArgument, - count: usize + voter_type: GenericArgument, + target_type: GenericArgument, + count: usize, ) -> TokenStream2 { let from_impl_single = { let name = field_name_for(1); - quote!(1 => compact.#name.push((who, distribution[0].clone().0)),) + quote!(1 => compact.#name.push( + ( + index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, + index_of_target(&distribution[0].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, + ) + ),) }; let from_impl_double = { let name = field_name_for(2); quote!(2 => compact.#name.push( ( - who, - distribution[0].clone(), - distribution[1].clone().0, + index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, + ( + index_of_target(&distribution[0].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, + distribution[0].1, + ), + index_of_target(&distribution[1].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, ) ),) }; let from_impl_rest = (3..=count).map(|c| { - let inner = (0..c-1).map(|i| quote!(distribution[#i].clone(),)).collect::(); + let inner = (0..c-1).map(|i| + quote!((index_of_target(&distribution[#i].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, distribution[#i].1),) + ).collect::(); let field_name = field_name_for(c); let last_index = c - 1; - let last = quote!(distribution[#last_index].clone().0); + let last = quote!(index_of_target(&distribution[#last_index].0).ok_or(_phragmen::Error::CompactInvalidIndex)?); quote!( - #c => compact.#field_name.push((who, [#inner], #last)), + #c => compact.#field_name.push((index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, [#inner], #last)), ) }).collect::(); let into_impl_single = { let name = field_name_for(1); quote!( - for (who, target) in self.#name { - let all_stake = C::convert(max_of(&who)) as u128; + for (voter_index, target_index) in self.#name { + let who = voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?; + let all_stake = max_of(&who); assignments.push(_phragmen::StakedAssignment { who, - distribution: vec![(target, all_stake)], + distribution: vec![(target_at(target_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, all_stake)], }) } ) @@ -373,8 +433,9 @@ fn convert_impl_for_staked_assignment( let into_impl_double = { let name = field_name_for(2); quote!( - for (who, (t1, w1), t2) in self.#name { - let all_stake = C::convert(max_of(&who)) as u128; + for (voter_index, (t1_idx, w1), t2_idx) in self.#name { + let who = voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?; + let all_stake = max_of(&who); if w1 >= all_stake { return Err(_phragmen::Error::CompactStakeOverflow); @@ -385,8 +446,8 @@ fn convert_impl_for_staked_assignment( assignments.push( _phragmen::StakedAssignment { who, distribution: vec![ - (t1, w1), - (t2, w2), + (target_at(t1_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, w1), + (target_at(t2_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, w2), ] }); } @@ -395,15 +456,18 @@ fn convert_impl_for_staked_assignment( let into_impl_rest = (3..=count).map(|c| { let name = field_name_for(c); quote!( - for (who, inners, t_last) in self.#name { + for (voter_index, inners, t_last_idx) in self.#name { + let who = voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?; let mut sum = u128::min_value(); - let all_stake = C::convert(max_of(&who)) as u128; + let all_stake = max_of(&who); + let mut inners_parsed = inners .iter() - .map(|(ref c, w)| { + .map(|(ref t_idx, w)| { sum = sum.saturating_add(*w); - (c.clone(), *w) - }).collect::>(); + let target = target_at(*t_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?; + Ok((target, *w)) + }).collect::, _phragmen::Error>>()?; if sum >= all_stake { return Err(_phragmen::Error::CompactStakeOverflow); @@ -411,7 +475,7 @@ fn convert_impl_for_staked_assignment( // w_last is proved to be positive. let w_last = all_stake - sum; - inners_parsed.push((t_last, w_last)); + inners_parsed.push((target_at(t_last_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, w_last)); assignments.push(_phragmen::StakedAssignment { who, @@ -422,19 +486,27 @@ fn convert_impl_for_staked_assignment( }).collect::(); let final_impl = quote!( - impl<#account_type: _codec::Codec + Default + Clone> - #ident<#account_type, u128> + impl< + #voter_type: _codec::Codec + Default + Copy, + #target_type: _codec::Codec + Default + Copy, + A: _codec::Codec + Default + Clone, + > + #ident<#voter_type, #target_type, u128, A> { /// Convert self into `StakedAssignment`. The given function should return the total /// weight of a voter. It is used to subtract the sum of all the encoded weights to /// infer the last one. - fn into_staked(self, max_of: FM) - -> Result>, _phragmen::Error> + pub fn into_staked( + self, + max_of: FM, + voter_at: impl Fn(#voter_type) -> Option, + target_at: impl Fn(#target_type) -> Option, + ) + -> Result>, _phragmen::Error> where - for<'r> FM: Fn(&'r #account_type) -> Balance, - C: _sp_runtime::traits::Convert + for<'r> FM: Fn(&'r A) -> u128, { - let mut assignments: Vec<_phragmen::StakedAssignment<#account_type>> = Default::default(); + let mut assignments: Vec<_phragmen::StakedAssignment> = Default::default(); #into_impl_single #into_impl_double #into_impl_rest @@ -443,19 +515,27 @@ fn convert_impl_for_staked_assignment( } /// Generate self from a vector of `StakedAssignment`. - fn from_staked(assignments: Vec<_phragmen::StakedAssignment<#account_type>>) -> Self { - let mut compact: #ident<#account_type, u128> = Default::default(); - assignments.into_iter().for_each(|_phragmen::StakedAssignment { who, distribution } | { + pub fn from_staked( + assignments: Vec<_phragmen::StakedAssignment>, + index_of_voter: FV, + index_of_target: FT, + ) -> Result + where + for<'r> FV: Fn(&'r A) -> Option<#voter_type>, + for<'r> FT: Fn(&'r A) -> Option<#target_type>, + { + let mut compact: #ident<#voter_type, #target_type, u128, A> = Default::default(); + for _phragmen::StakedAssignment { who, distribution } in assignments { match distribution.len() { #from_impl_single #from_impl_double #from_impl_rest _ => { - _sp_runtime::print("staked assignment length too big. ignoring"); + return Err(_phragmen::Error::CompactTargetOverflow); } } - }); - compact + }; + Ok(compact) } } ); diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index f6bf85ce6d2c1..98d38d0d0bc6c 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -64,7 +64,19 @@ pub trait IdentifierT: Clone + Eq + Default + Ord + Debug {} pub enum Error { /// While going from compact to staked, the stake of all the edges has gone above the /// total and the last stake cannot be assigned. - CompactStakeOverflow + CompactStakeOverflow, + /// The compact type has a voter who's number of targets is out of bound. + CompactTargetOverflow, + /// One of the index functions returned none. + CompactInvalidIndex, + +} + +#[macro_export] +macro_rules! some { + ($inner:expr) => { + $some.ok_or(_phragmen::CompactInvalidIndex)? + }; } impl IdentifierT for T {} @@ -146,15 +158,11 @@ pub struct Assignment { impl Assignment { /// Convert from a ratio assignment into one with absolute values aka. [`StakedAssignment`]. /// - /// It needs `stake_of` to know how the total budget of each voter. If `fill` is set to true, + /// It needs `stake` which is the total budget of the voter. If `fill` is set to true, /// it ensures that all the potential rounding errors are compensated and the distribution's sum /// is exactly equal to the total budget. - pub fn into_staked(self, stake_of: FS, fill: bool) -> StakedAssignment - where - C: Convert, - for<'r> FS: Fn(&'r AccountId) -> Balance, + pub fn into_staked(self, stake: ExtendedBalance, fill: bool) -> StakedAssignment { - let stake = C::convert(stake_of(&self.who)) as ExtendedBalance; let mut sum: ExtendedBalance = Bounded::min_value(); let mut distribution = self.distribution.into_iter().map(|(target, p)| { let distribution_stake = p * stake; @@ -189,6 +197,35 @@ pub struct StakedAssignment { pub distribution: Vec<(AccountId, ExtendedBalance)>, } +impl StakedAssignment { + /// Converts self into the normal [`Assignment`] type. + pub fn into_assignment(self, fill: bool) -> Assignment { + let accuracy = Perbill::accuracy() as u128; + let mut sum: u128 = Zero::zero(); + + let stake = self.distribution.iter().map(|x| x.1).sum(); + let mut distribution = self.distribution.into_iter().map(|(target, w)| { + let portion = multiply_by_rational(w, accuracy, stake).unwrap_or(Bounded::max_value()); + sum += portion; + let per_thing = Perbill::from_parts(portion as u32); + (target, per_thing) + }).collect::>(); + + if fill { + if let Some(leftover) = accuracy.checked_sub(sum) { + if let Some(last) = distribution.last_mut() { + last.1 = last.1.saturating_add(Perbill::from_parts(leftover as u32)); + } + } + } + + Assignment { + who: self.who, + distribution, + } + } +} + /// A structure to demonstrate the phragmen result from the perspective of the candidate, i.e. how /// much support each candidate is receiving. /// diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index ed08e00e35323..989378e869dc2 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -20,10 +20,11 @@ use crate::mock::*; use crate::{ - elect, equalize, build_support_map, Support, StakedAssignment, Assignment, PhragmenResult, + elect, equalize, build_support_map, + Support, StakedAssignment, Assignment, PhragmenResult, ExtendedBalance, }; use substrate_test_utils::assert_eq_uvec; -use sp_runtime::Perbill; +use sp_runtime::{Perbill, traits::Convert}; #[test] fn float_phragmen_poc_works() { @@ -484,8 +485,10 @@ fn self_votes_should_be_kept() { let staked_assignments: Vec> = result.assignments .into_iter() - .map(|a| a.into_staked::<_, _, TestCurrencyToVote>(&stake_of, true)) - .collect(); + .map(|a| { + let stake = >::convert(stake_of(&a.who)) as ExtendedBalance; + a.into_staked(stake, true) + }).collect(); let (mut supports, _) = build_support_map::( &result.winners.into_iter().map(|(who, _)| who).collect(), @@ -520,22 +523,68 @@ fn self_votes_should_be_kept() { ); } +#[test] +fn assignment_conversion_is_reversible_without_fill() { + let assignment = Assignment { + who: 1, + distribution: vec![ + (20, Perbill::from_percent(33)), + (10, Perbill::from_percent(67)), + ] + }; + + let staked = assignment.into_staked(1243252161234124, true); + dbg!(&staked); + + let sum = staked.distribution[0].1 + staked.distribution[1].1; + dbg!(sum == 1243252161234124); + + unimplemented!(); +} + +#[test] +fn assignment_convert_works() { + let staked = StakedAssignment { + who: 1 as AccountId, + distribution: vec![ + (20, 100 as Balance), + (30, 25), + ], + }; + + let assignment = staked.clone().into_assignment(true); + assert_eq!( + assignment, + Assignment { + who: 1, + distribution: vec![ + (20, Perbill::from_percent(80)), + (30, Perbill::from_percent(20)), + ] + } + ); + + assert_eq!( + assignment.into_staked(125, true), + staked, + ); +} + mod compact { use codec::{Decode, Encode}; use crate::generate_compact_solution_type; - use crate::mock::{TestCurrencyToVote}; use super::{AccountId, Balance}; // these need to come from the same dev-dependency `sp-phragmen`, not from the crate. use sp_phragmen::{Assignment, StakedAssignment, Error as PhragmenError}; - use sp_std::convert::TryInto; + use sp_std::{convert::{TryInto, TryFrom}, fmt::Debug}; use sp_runtime::Perbill; generate_compact_solution_type!(TestCompact, 16); #[test] fn compact_struct_is_codec() { - let compact = TestCompact { - votes1: vec![(2, 20), (4, 40)], + let compact = TestCompact::<_, _, _, u64> { + votes1: vec![(2u64, 20), (4, 40)], votes2: vec![ (1, (10, Perbill::from_percent(80)), 11), (5, (50, Perbill::from_percent(85)), 51), @@ -551,33 +600,58 @@ mod compact { ); } - #[test] - fn basic_from_and_into_compact_works_assignments() { + fn basic_ratio_test_with() where + V: codec::Codec + Copy + Default + PartialEq + Eq + TryInto + TryFrom + From + Debug, + T: codec::Codec + Copy + Default + PartialEq + Eq + TryInto + TryFrom + From + Debug, + >::Error: std::fmt::Debug, + >::Error: std::fmt::Debug, + >::Error: std::fmt::Debug, + >::Error: std::fmt::Debug, + { + let voters = vec![ + 2 as AccountId, + 4, + 1, + 5, + 3, + ]; + let targets = vec![ + 10 as AccountId, + 11, + 20, // 2 + 30, + 31, // 4 + 32, + 40, // 6 + 50, + 51, // 8 + ]; + let assignments = vec![ Assignment { - who: 2u64, - distribution: vec![(20, Perbill::from_percent(100))] + who: 2 as AccountId, + distribution: vec![(20u64, Perbill::from_percent(100))] }, Assignment { - who: 4u64, + who: 4, distribution: vec![(40, Perbill::from_percent(100))], }, Assignment { - who: 1u64, + who: 1, distribution: vec![ (10, Perbill::from_percent(80)), (11, Perbill::from_percent(20)) ], }, Assignment { - who: 5u64, distribution: - vec![ + who: 5, + distribution: vec![ (50, Perbill::from_percent(85)), (51, Perbill::from_percent(15)), ] }, Assignment { - who: 3u64, + who: 3, distribution: vec![ (30, Perbill::from_percent(50)), (31, Perbill::from_percent(25)), @@ -586,56 +660,96 @@ mod compact { }, ]; - let compacted: TestCompact = assignments.clone().into(); + let voter_index = |a: &AccountId| -> Option { + voters.iter().position(|x| x == a).map(TryInto::try_into).unwrap().ok() + }; + let target_index = |a: &AccountId| -> Option { + targets.iter().position(|x| x == a).map(TryInto::try_into).unwrap().ok() + }; + + let compacted = >::from_assignment( + assignments.clone(), + voter_index, + target_index, + ).unwrap(); assert_eq!( compacted, TestCompact { - votes1: vec![(2, 20), (4, 40)], + votes1: vec![(V::from(0u8), T::from(2u8)), (V::from(1u8), T::from(6u8))], votes2: vec![ - (1, (10, Perbill::from_percent(80)), 11), - (5, (50, Perbill::from_percent(85)), 51), + (V::from(2u8), (T::from(0u8), Perbill::from_percent(80)), T::from(1u8)), + (V::from(3u8), (T::from(7u8), Perbill::from_percent(85)), T::from(8u8)), ], votes3: vec![ - (3, [(30, Perbill::from_percent(50)), (31, Perbill::from_percent(25))], 32), + ( + V::from(4), + [(T::from(3u8), Perbill::from_percent(50)), (T::from(4u8), Perbill::from_percent(25))], + T::from(5u8), + ), ], ..Default::default() } ); + let voter_at = |a: V| -> Option { voters.get(>::try_into(a).unwrap()).cloned() }; + let target_at = |a: T| -> Option { targets.get(>::try_into(a).unwrap()).cloned() }; + assert_eq!( - as TryInto>>>::try_into(compacted).unwrap(), - assignments + compacted.into_assignment(voter_at, target_at).unwrap(), + assignments, ); } + #[test] + fn basic_from_and_into_compact_works_assignments() { + basic_ratio_test_with::(); + basic_ratio_test_with::(); + basic_ratio_test_with::(); + } + #[test] fn basic_from_and_into_compact_works_staked_assignments() { + let voters = vec![ + 2 as AccountId, + 4, + 1, + 5, + 3, + ]; + let targets = vec![ + 10 as AccountId, 11, + 20, + 30, 31, 32, + 40, + 50, 51, + ]; + let assignments = vec![ StakedAssignment { - who: 2u64, - distribution: vec![(20, 100u128)] + who: 2 as AccountId, + distribution: vec![(20, 100 as Balance)] }, StakedAssignment { - who: 4u64, + who: 4, distribution: vec![(40, 100)], }, StakedAssignment { - who: 1u64, + who: 1, distribution: vec![ (10, 80), (11, 20) ], }, StakedAssignment { - who: 5u64, distribution: + who: 5, distribution: vec![ (50, 85), (51, 15), ] }, StakedAssignment { - who: 3u64, + who: 3, distribution: vec![ (30, 50), (31, 25), @@ -644,106 +758,144 @@ mod compact { }, ]; - let compacted = >::from_staked(assignments.clone()); + let voter_index = |a: &AccountId| -> Option { + voters.iter().position(|x| x == a).map(TryInto::try_into).unwrap().ok() + }; + let target_index = |a: &AccountId| -> Option { + targets.iter().position(|x| x == a).map(TryInto::try_into).unwrap().ok() + }; + + let compacted = >::from_staked( + assignments.clone(), + voter_index, + target_index, + ).unwrap(); assert_eq!( compacted, TestCompact { - votes1: vec![(2, 20), (4, 40)], + votes1: vec![(0, 2), (1, 6)], votes2: vec![ - (1, (10, 80), 11), - (5, (50, 85), 51), + (2, (0, 80), 1), + (3, (7, 85), 8), ], votes3: vec![ - (3, [(30, 50), (31, 25)], 32), + (4, [(3, 50), (4, 25)], 5), ], ..Default::default() } ); let max_of_fn = |_: &AccountId| -> Balance { 100u128 }; - let max_of: Box Balance> = Box::new(max_of_fn); + let voter_at = |a: u16| -> Option { voters.get(a as usize).cloned() }; + let target_at = |a: u16| -> Option { targets.get(a as usize).cloned() }; assert_eq!( - compacted.into_staked::(&max_of).unwrap(), - assignments + compacted.into_staked( + max_of_fn, + voter_at, + target_at, + ).unwrap(), + assignments, ); } #[test] - fn compact_into_stake_must_report_budget_overflow() { + fn compact_into_stake_must_report_overflow() { // The last edge which is computed from the rest should ALWAYS be positive. // in votes2 - let compact = TestCompact:: { + let compact = TestCompact:: { votes1: Default::default(), - votes2: vec![(1, (10, 10), 20)], + votes2: vec![(0, (1, 10), 2)], ..Default::default() }; - let max_of_fn = |_: &AccountId| -> Balance { 5 }; - let max_of: Box Balance> = Box::new(max_of_fn); + let entity_at = |a: u16| -> Option { Some(a as AccountId) }; + let max_of = |_: &AccountId| -> Balance { 5 }; assert_eq!( - compact.into_staked::(&max_of).unwrap_err(), + compact.into_staked(&max_of, &entity_at, &entity_at).unwrap_err(), PhragmenError::CompactStakeOverflow, ); // in votes3 onwards - let compact = TestCompact:: { + let compact = TestCompact:: { votes1: Default::default(), votes2: Default::default(), - votes3: vec![(1, [(10, 7), (20, 8)], 30)], + votes3: vec![(0, [(1, 7), (2, 8)], 3)], ..Default::default() }; assert_eq!( - compact.into_staked::(&max_of).unwrap_err(), + compact.into_staked(&max_of, &entity_at, &entity_at).unwrap_err(), PhragmenError::CompactStakeOverflow, ); // Also if equal - let compact = TestCompact:: { + let compact = TestCompact:: { votes1: Default::default(), votes2: Default::default(), // 5 is total, we cannot leave none for 30 here. - votes3: vec![(1, [(10, 3), (20, 2)], 30)], + votes3: vec![(0, [(1, 3), (2, 2)], 3)], ..Default::default() }; assert_eq!( - compact.into_staked::(&max_of).unwrap_err(), + compact.into_staked(&max_of, &entity_at, &entity_at).unwrap_err(), PhragmenError::CompactStakeOverflow, ); } - #[test] - fn compact_into_stake_must_report_ratio_overflow() { + fn compact_into_assignment_must_report_overflow() { // in votes2 - let compact = TestCompact:: { + let compact = TestCompact:: { votes1: Default::default(), - votes2: vec![(1, (10, Perbill::from_percent(100)), 20)], + votes2: vec![(0, (1, Perbill::from_percent(100)), 2)], ..Default::default() }; + let entity_at = |a: u16| -> Option { Some(a as AccountId) }; + assert_eq!( - as TryInto>>>::try_into(compact) - .unwrap_err(), + compact.into_assignment(&entity_at, &entity_at).unwrap_err(), PhragmenError::CompactStakeOverflow, ); // in votes3 onwards - let compact = TestCompact:: { + let compact = TestCompact:: { votes1: Default::default(), votes2: Default::default(), - votes3: vec![(1, [(10, Perbill::from_percent(70)), (20, Perbill::from_percent(80))], 30)], + votes3: vec![(0, [(1, Perbill::from_percent(70)), (2, Perbill::from_percent(80))], 3)], ..Default::default() }; assert_eq!( - as TryInto>>>::try_into(compact) - .unwrap_err(), + compact.into_assignment(&entity_at, &entity_at).unwrap_err(), PhragmenError::CompactStakeOverflow, ); } + + #[test] + fn target_count_overflow_is_detected() { + let assignments = vec![ + StakedAssignment { + who: 1 as AccountId, + distribution: (10..30).map(|i| (i as AccountId, i as Balance)).collect::>(), + }, + ]; + + let entity_index = |a: &AccountId| -> Option { Some(*a as u16) }; + + let compacted = >::from_staked( + assignments.clone(), + entity_index, + entity_index, + ); + + assert_eq!( + compacted.unwrap_err(), + PhragmenError::CompactTargetOverflow, + ) + } } From 084a355e134e57742bb831d25e0f210a71526acc Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 11 Feb 2020 16:05:02 +0100 Subject: [PATCH 039/106] Remove bogus test --- primitives/phragmen/src/tests.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index 989378e869dc2..9f3cbe36369aa 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -523,25 +523,6 @@ fn self_votes_should_be_kept() { ); } -#[test] -fn assignment_conversion_is_reversible_without_fill() { - let assignment = Assignment { - who: 1, - distribution: vec![ - (20, Perbill::from_percent(33)), - (10, Perbill::from_percent(67)), - ] - }; - - let staked = assignment.into_staked(1243252161234124, true); - dbg!(&staked); - - let sum = staked.distribution[0].1 + staked.distribution[1].1; - dbg!(sum == 1243252161234124); - - unimplemented!(); -} - #[test] fn assignment_convert_works() { let staked = StakedAssignment { From 817f8aa53384b46d8614547def1de5fd1391c669 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 11 Feb 2020 17:39:02 +0100 Subject: [PATCH 040/106] Some review grumbles. --- Cargo.lock | 1 + frame/staking/Cargo.toml | 2 +- frame/staking/src/lib.rs | 10 +- frame/staking/src/offchain_election.rs | 21 +- primitives/phragmen/Cargo.toml | 6 +- primitives/phragmen/compact/src/assignment.rs | 207 ++++++++++ primitives/phragmen/compact/src/lib.rs | 383 +----------------- primitives/phragmen/compact/src/staked.rs | 206 ++++++++++ primitives/phragmen/src/lib.rs | 7 +- 9 files changed, 465 insertions(+), 378 deletions(-) create mode 100644 primitives/phragmen/compact/src/assignment.rs create mode 100644 primitives/phragmen/compact/src/staked.rs diff --git a/Cargo.lock b/Cargo.lock index bd05cf388928f..3f28d7339d6d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7098,6 +7098,7 @@ dependencies = [ "parity-scale-codec", "rand 0.7.3", "serde", + "sp-core", "sp-io", "sp-phragmen", "sp-phragmen-compact", diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 69d25b95e8435..bb28b46038fd3 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -21,7 +21,7 @@ pallet-authorship = { version = "2.0.0", default-features = false, path = "../au sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } [dev-dependencies] -sp-core = { version = "2.0.0", path = "../../primitives/core", features = ["full_crypto"] } +sp-core = { version = "2.0.0", path = "../../primitives/core" } pallet-balances = { version = "2.0.0", path = "../balances" } pallet-timestamp = { version = "2.0.0", path = "../timestamp" } pallet-staking-reward-curve = { version = "2.0.0", path = "../staking/reward-curve" } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index b308cc4088705..5c1fe263554bf 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -1923,7 +1923,6 @@ impl Module { /// NOTE: This always happens immediately before a session change to ensure that new validators /// get a chance to set their session keys. fn new_era(start_session_index: SessionIndex) -> Option> { - // TODO: further clean this function. Payment stuff can go elsewhere. // Payout let points = CurrentEraPointsEarned::take(); let now = T::Time::now(); @@ -2538,6 +2537,8 @@ impl frame_support::unsigned::ValidateUnsigned for Module { validator_index, signature, ) = call { + use offchain_election::SignaturePayloadOf; + if let Some(queued_score) = Self::queued_score() { if !offchain_election::is_score_better(queued_score, *score) { debug::native::debug!( @@ -2549,7 +2550,12 @@ impl frame_support::unsigned::ValidateUnsigned for Module { } // check signature - let payload = (winners, compact, score, validator_index); + let payload: SignaturePayloadOf = ( + winners, + compact, + score, + validator_index, + ); let current_validators = T::SessionInterface::keys::(); let (_, validator_key) = current_validators.get(*validator_index as usize) .ok_or(TransactionValidityError::Invalid(InvalidTransaction::Custom(0u8).into()))?; diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 1c60145063c85..9b4ec2815cc08 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -21,7 +21,7 @@ use crate::{ }; use codec::Encode; use frame_system::offchain::{SubmitUnsignedTransaction}; -use sp_phragmen::{ reduce, ExtendedBalance, PhragmenResult, StakedAssignment, Assignment}; +use sp_phragmen::{reduce, ExtendedBalance, PhragmenResult, StakedAssignment, Assignment, PhragmenScore}; use sp_std::{prelude::*, cmp::Ordering, convert::TryInto}; use sp_runtime::{RuntimeAppPublic, RuntimeDebug}; use sp_runtime::offchain::storage::StorageValueRef; @@ -32,6 +32,8 @@ pub(crate) enum OffchainElectionError { /// No signing key has been found on the current node that maps to a validators. This node /// should not run the offchain election code. NoSigningKey, + /// Signing operation failed. + SigningFailed, /// Phragmen election returned None. This means less candidate that minimum number of needed /// validators were present. The chain is in trouble and not much that we can do about it. ElectionFailed, @@ -41,6 +43,14 @@ pub(crate) enum OffchainElectionError { CompactFailed, } +/// The type of signature data encoded with the unsigned submission +pub(crate) type SignaturePayloadOf<'a, T> = ( + &'a [ValidatorIndex], + &'a CompactOf, + &'a PhragmenScore, + &'a u32, +); + pub(crate) const OFFCHAIN_HEAD_DB: &[u8] = b"parity/staking-election/"; const OFFCHAIN_REPEAT: u32 = 5; @@ -77,7 +87,7 @@ pub(crate) fn set_check_offchain_execution_status(now: T::BlockNumber) /// Evaluation is done in a lexicographic manner. /// /// Note that the third component should be minimized. -pub(crate) fn is_score_better(this: [ExtendedBalance; 3], that: [ExtendedBalance; 3]) -> bool { +pub(crate) fn is_score_better(this: PhragmenScore, that: PhragmenScore) -> bool { match that .iter() .enumerate() @@ -170,9 +180,10 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti validator_index(&w).expect("winners are chosen from the snapshot list; the must have index; qed") ).collect::>(); - let signature_payload = - (winners.clone(), compact.clone(), score, index as u32).encode(); - let signature = pubkey.sign(&signature_payload).unwrap(); + let signature_payload: SignaturePayloadOf = + (&winners, &compact, &score, &(index as u32)); + let signature = pubkey.sign(&signature_payload.encode()) + .ok_or(OffchainElectionError::SigningFailed)?; let call: ::Call = Call::submit_election_solution_unsigned( winners, compact, diff --git a/primitives/phragmen/Cargo.toml b/primitives/phragmen/Cargo.toml index a73d594fc39be..ade758263d3f7 100644 --- a/primitives/phragmen/Cargo.toml +++ b/primitives/phragmen/Cargo.toml @@ -6,10 +6,11 @@ edition = "2018" license = "GPL-3.0" [dependencies] -codec = { package = "parity-scale-codec", version = "1.0.0", optional = true, default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true, features = ["derive"] } sp-std = { version = "2.0.0", default-features = false, path = "../std" } -sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } +sp-core = { version = "2.0.0", path = "../core", default-features = false } sp-phragmen-compact = { path = "./compact"} [dev-dependencies] @@ -26,4 +27,5 @@ std = [ "serde", "sp-std/std", "sp-runtime/std", + "sp-core/std", ] diff --git a/primitives/phragmen/compact/src/assignment.rs b/primitives/phragmen/compact/src/assignment.rs new file mode 100644 index 0000000000000..b9ede40e14cb9 --- /dev/null +++ b/primitives/phragmen/compact/src/assignment.rs @@ -0,0 +1,207 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Code generation for the ratio assignment type. + +use crate::field_name_for; +use proc_macro2::{TokenStream as TokenStream2}; +use syn::{GenericArgument}; +use quote::quote; + +fn from_impl(count: usize) -> TokenStream2 { + let from_impl_single = { + let name = field_name_for(1); + quote!(1 => compact.#name.push( + ( + index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, + index_of_target(&distribution[0].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, + ) + ),) + }; + + let from_impl_double = { + let name = field_name_for(2); + quote!(2 => compact.#name.push( + ( + index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, + ( + index_of_target(&distribution[0].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, + distribution[0].1, + ), + index_of_target(&distribution[1].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, + ) + ),) + }; + + let from_impl_rest = (3..=count).map(|c| { + let inner = (0..c-1).map(|i| + quote!((index_of_target(&distribution[#i].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, distribution[#i].1),) + ).collect::(); + + let field_name = field_name_for(c); + let last_index = c - 1; + let last = quote!(index_of_target(&distribution[#last_index].0).ok_or(_phragmen::Error::CompactInvalidIndex)?); + + quote!( + #c => compact.#field_name.push((index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, [#inner], #last)), + ) + }).collect::(); + + quote!( + #from_impl_single + #from_impl_double + #from_impl_rest + ) +} + +fn into_impl(count: usize) -> TokenStream2 { + let into_impl_single = { + let name = field_name_for(1); + quote!( + for (voter_index, target_index) in self.#name { + assignments.push(_phragmen::Assignment { + who: voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, + distribution: vec![ + (target_at(target_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, _phragmen::sp_runtime::Perbill::one()) + ], + }) + } + ) + }; + + let into_impl_double = { + let name = field_name_for(2); + quote!( + for (voter_index, (t1_idx, p1), t2_idx) in self.#name { + if p1 >= _phragmen::sp_runtime::Perbill::one() { + return Err(_phragmen::Error::CompactStakeOverflow); + } + + // defensive only. Since perbill doesn't have `Sub`. + let p2 = _phragmen::sp_runtime::traits::Saturating::saturating_sub( + _phragmen::sp_runtime::Perbill::one(), + p1, + ); + + assignments.push( _phragmen::Assignment { + who: voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, + distribution: vec![ + (target_at(t1_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, p1), + (target_at(t2_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, p2), + ] + }); + } + ) + }; + + let into_impl_rest = (3..=count).map(|c| { + let name = field_name_for(c); + quote!( + for (voter_index, inners, t_last_idx) in self.#name { + let mut sum = _phragmen::sp_runtime::Perbill::zero(); + let mut inners_parsed = inners + .iter() + .map(|(ref t_idx, p)| { + sum = _phragmen::sp_runtime::traits::Saturating::saturating_add(sum, *p); + let target = target_at(*t_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?; + Ok((target, *p)) + }) + .collect::, _phragmen::Error>>()?; + + if sum >= _phragmen::sp_runtime::Perbill::one() { + return Err(_phragmen::Error::CompactStakeOverflow); + } + + // defensive only. Since perbill doesn't have `Sub`. + let p_last = _phragmen::sp_runtime::traits::Saturating::saturating_sub( + _phragmen::sp_runtime::Perbill::one(), + sum, + ); + + inners_parsed.push((target_at(t_last_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, p_last)); + + assignments.push(_phragmen::Assignment { + who: voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, + distribution: inners_parsed, + }); + } + ) + }).collect::(); + + quote!( + #into_impl_single + #into_impl_double + #into_impl_rest + ) +} + +pub(crate) fn assignment( + ident: syn::Ident, + voter_type: GenericArgument, + target_type: GenericArgument, + count: usize, +) -> TokenStream2 { + + let from_impl = from_impl(count); + let into_impl = into_impl(count); + + quote!( + impl< + #voter_type: _phragmen::codec::Codec + Default + Copy, + #target_type: _phragmen::codec::Codec + Default + Copy, + A: _phragmen::codec::Codec + Default + Clone, + > + #ident<#voter_type, #target_type, _phragmen::sp_runtime::Perbill, A> + { + pub fn from_assignment( + assignments: Vec<_phragmen::Assignment>, + index_of_voter: FV, + index_of_target: FT, + ) -> Result + where + for<'r> FV: Fn(&'r A) -> Option<#voter_type>, + for<'r> FT: Fn(&'r A) -> Option<#target_type>, + { + let mut compact: #ident< + #voter_type, + #target_type, + _phragmen::sp_runtime::Perbill, + A, + > = Default::default(); + + for _phragmen::Assignment { who, distribution } in assignments { + match distribution.len() { + #from_impl + _ => { + return Err(_phragmen::Error::CompactTargetOverflow); + } + } + }; + Ok(compact) + } + + pub fn into_assignment( + self, + voter_at: impl Fn(#voter_type) -> Option, + target_at: impl Fn(#target_type) -> Option, + ) -> Result>, _phragmen::Error> { + let mut assignments: Vec<_phragmen::Assignment> = Default::default(); + #into_impl + Ok(assignments) + } + } + ) +} diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index e033ed7b7ad06..7b157a6a1b07f 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -24,6 +24,9 @@ use proc_macro_crate::crate_name; use quote::quote; use syn::{GenericArgument, Type, parse::{Parse, ParseStream, Result}}; +mod assignment; +mod staked; + // prefix used for struct fields in compact. const PREFIX: &'static str = "votes"; @@ -97,14 +100,14 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { weight_type, ).unwrap_or_else(|e| e.to_compile_error()); - let from_into_impl_assignment = convert_impl_for_assignment( + let assignment_impls = assignment::assignment( ident.clone(), voter_type.clone(), target_type.clone(), count, ); - let from_into_impl_staked_assignment = convert_impl_for_staked_assignment( + let staked_impls = staked::staked( ident, voter_type, target_type, @@ -114,8 +117,8 @@ pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { quote!( #imports #compact_def - #from_into_impl_assignment - #from_into_impl_staked_assignment + #assignment_impls + #staked_impls ).into() } @@ -159,7 +162,15 @@ fn struct_def( Ok(quote! ( /// A struct to encode a `Vec` or `Vec` of the phragmen module /// in a compact way. - #[derive(Default, PartialEq, Eq, Clone, _sp_runtime::RuntimeDebug, _codec::Encode, _codec::Decode)] + #[derive( + Default, + PartialEq, + Eq, + Clone, + _phragmen::sp_runtime::RuntimeDebug, + _phragmen::codec::Encode, + _phragmen::codec::Decode, + )] #vis struct #ident<#voter_type, #target_type, #weight_type, A> { _marker: sp_std::marker::PhantomData, #singles @@ -170,20 +181,6 @@ fn struct_def( } fn imports() -> Result { - let runtime_imports = match crate_name("sp-runtime") { - Ok(sp_runtime) => { - let ident = syn::Ident::new(&sp_runtime, Span::call_site()); - quote!( extern crate #ident as _sp_runtime; ) - }, - Err(e) => return Err(syn::Error::new(Span::call_site(), &e)), - }; - let codec_imports = match crate_name("parity-scale-codec") { - Ok(codec) => { - let ident = syn::Ident::new(&codec, Span::call_site()); - quote!( extern crate #ident as _codec; ) - }, - Err(e) => return Err(syn::Error::new(Span::call_site(), &e)), - }; let sp_phragmen_imports = match crate_name("sp-phragmen") { Ok(sp_phragmen) => { let ident = syn::Ident::new(&sp_phragmen, Span::call_site()); @@ -191,360 +188,12 @@ fn imports() -> Result { } Err(e) => return Err(syn::Error::new(Span::call_site(), &e)), }; - let sp_std_imports = match crate_name("sp-std") { - Ok(sp_std) => { - let ident = syn::Ident::new(&sp_std, Span::call_site()); - quote!( extern crate #ident as _sp_std; ) - } - Err(e) => return Err(syn::Error::new(Span::call_site(), &e)), - }; Ok(quote!( - #runtime_imports - #codec_imports #sp_phragmen_imports - #sp_std_imports )) } -fn convert_impl_for_assignment( - ident: syn::Ident, - voter_type: GenericArgument, - target_type: GenericArgument, - count: usize -) -> TokenStream2 { - let from_impl_single = { - let name = field_name_for(1); - quote!(1 => compact.#name.push( - ( - index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, - index_of_target(&distribution[0].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, - ) - ),) - }; - let from_impl_double = { - let name = field_name_for(2); - quote!(2 => compact.#name.push( - ( - index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, - ( - index_of_target(&distribution[0].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, - distribution[0].1, - ), - index_of_target(&distribution[1].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, - ) - ),) - }; - let from_impl_rest = (3..=count).map(|c| { - let inner = (0..c-1).map(|i| - quote!((index_of_target(&distribution[#i].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, distribution[#i].1),) - ).collect::(); - - let field_name = field_name_for(c); - let last_index = c - 1; - let last = quote!(index_of_target(&distribution[#last_index].0).ok_or(_phragmen::Error::CompactInvalidIndex)?); - - quote!( - #c => compact.#field_name.push((index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, [#inner], #last)), - ) - }).collect::(); - - let into_impl_single = { - let name = field_name_for(1); - quote!( - for (voter_index, target_index) in self.#name { - assignments.push(_phragmen::Assignment { - who: voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, - distribution: vec![ - (target_at(target_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, _sp_runtime::Perbill::one()) - ], - }) - } - ) - }; - let into_impl_double = { - let name = field_name_for(2); - quote!( - for (voter_index, (t1_idx, p1), t2_idx) in self.#name { - if p1 >= _sp_runtime::Perbill::one() { - return Err(_phragmen::Error::CompactStakeOverflow); - } - - // defensive only. Since perbill doesn't have `Sub`. - let p2 = _sp_runtime::traits::Saturating::saturating_sub( - _sp_runtime::Perbill::one(), - p1, - ); - - assignments.push( _phragmen::Assignment { - who: voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, - distribution: vec![ - (target_at(t1_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, p1), - (target_at(t2_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, p2), - ] - }); - } - ) - }; - let into_impl_rest = (3..=count).map(|c| { - let name = field_name_for(c); - quote!( - for (voter_index, inners, t_last_idx) in self.#name { - let mut sum = _sp_runtime::Perbill::zero(); - let mut inners_parsed = inners - .iter() - .map(|(ref t_idx, p)| { - sum = _sp_runtime::traits::Saturating::saturating_add(sum, *p); - let target = target_at(*t_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?; - Ok((target, *p)) - }) - .collect::, _phragmen::Error>>()?; - - if sum >= _sp_runtime::Perbill::one() { - return Err(_phragmen::Error::CompactStakeOverflow); - } - - // defensive only. Since perbill doesn't have `Sub`. - let p_last = _sp_runtime::traits::Saturating::saturating_sub( - _sp_runtime::Perbill::one(), - sum, - ); - - inners_parsed.push((target_at(t_last_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, p_last)); - - assignments.push(_phragmen::Assignment { - who: voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, - distribution: inners_parsed, - }); - } - ) - }).collect::(); - - let from_into_impl = quote!( - impl< - #voter_type: _codec::Codec + Default + Copy, - #target_type: _codec::Codec + Default + Copy, - A: _codec::Codec + Default + Clone, - > - #ident<#voter_type, #target_type, _sp_runtime::Perbill, A> - { - pub fn from_assignment( - assignments: Vec<_phragmen::Assignment>, - index_of_voter: FV, - index_of_target: FT, - ) -> Result - where - for<'r> FV: Fn(&'r A) -> Option<#voter_type>, - for<'r> FT: Fn(&'r A) -> Option<#target_type>, - { - let mut compact: #ident< - #voter_type, - #target_type, - _sp_runtime::Perbill, - A, - > = Default::default(); - - for _phragmen::Assignment { who, distribution } in assignments { - match distribution.len() { - #from_impl_single - #from_impl_double - #from_impl_rest - _ => { - return Err(_phragmen::Error::CompactTargetOverflow); - } - } - }; - Ok(compact) - } - - pub fn into_assignment( - self, - voter_at: impl Fn(#voter_type) -> Option, - target_at: impl Fn(#target_type) -> Option, - ) -> Result>, _phragmen::Error> { - let mut assignments: Vec<_phragmen::Assignment> = Default::default(); - #into_impl_single - #into_impl_double - #into_impl_rest - - Ok(assignments) - } - } - ); - - from_into_impl -} - -fn convert_impl_for_staked_assignment( - ident: syn::Ident, - voter_type: GenericArgument, - target_type: GenericArgument, - count: usize, -) -> TokenStream2 { - let from_impl_single = { - let name = field_name_for(1); - quote!(1 => compact.#name.push( - ( - index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, - index_of_target(&distribution[0].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, - ) - ),) - }; - let from_impl_double = { - let name = field_name_for(2); - quote!(2 => compact.#name.push( - ( - index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, - ( - index_of_target(&distribution[0].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, - distribution[0].1, - ), - index_of_target(&distribution[1].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, - ) - ),) - }; - let from_impl_rest = (3..=count).map(|c| { - let inner = (0..c-1).map(|i| - quote!((index_of_target(&distribution[#i].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, distribution[#i].1),) - ).collect::(); - - let field_name = field_name_for(c); - let last_index = c - 1; - let last = quote!(index_of_target(&distribution[#last_index].0).ok_or(_phragmen::Error::CompactInvalidIndex)?); - - quote!( - #c => compact.#field_name.push((index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, [#inner], #last)), - ) - }).collect::(); - - let into_impl_single = { - let name = field_name_for(1); - quote!( - for (voter_index, target_index) in self.#name { - let who = voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?; - let all_stake = max_of(&who); - assignments.push(_phragmen::StakedAssignment { - who, - distribution: vec![(target_at(target_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, all_stake)], - }) - } - ) - }; - let into_impl_double = { - let name = field_name_for(2); - quote!( - for (voter_index, (t1_idx, w1), t2_idx) in self.#name { - let who = voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?; - let all_stake = max_of(&who); - - if w1 >= all_stake { - return Err(_phragmen::Error::CompactStakeOverflow); - } - - // w2 is ensured to be positive. - let w2 = all_stake - w1; - assignments.push( _phragmen::StakedAssignment { - who, - distribution: vec![ - (target_at(t1_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, w1), - (target_at(t2_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, w2), - ] - }); - } - ) - }; - let into_impl_rest = (3..=count).map(|c| { - let name = field_name_for(c); - quote!( - for (voter_index, inners, t_last_idx) in self.#name { - let who = voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?; - let mut sum = u128::min_value(); - let all_stake = max_of(&who); - - let mut inners_parsed = inners - .iter() - .map(|(ref t_idx, w)| { - sum = sum.saturating_add(*w); - let target = target_at(*t_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?; - Ok((target, *w)) - }).collect::, _phragmen::Error>>()?; - - if sum >= all_stake { - return Err(_phragmen::Error::CompactStakeOverflow); - } - // w_last is proved to be positive. - let w_last = all_stake - sum; - - inners_parsed.push((target_at(t_last_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, w_last)); - - assignments.push(_phragmen::StakedAssignment { - who, - distribution: inners_parsed, - }); - } - ) - }).collect::(); - - let final_impl = quote!( - impl< - #voter_type: _codec::Codec + Default + Copy, - #target_type: _codec::Codec + Default + Copy, - A: _codec::Codec + Default + Clone, - > - #ident<#voter_type, #target_type, u128, A> - { - /// Convert self into `StakedAssignment`. The given function should return the total - /// weight of a voter. It is used to subtract the sum of all the encoded weights to - /// infer the last one. - pub fn into_staked( - self, - max_of: FM, - voter_at: impl Fn(#voter_type) -> Option, - target_at: impl Fn(#target_type) -> Option, - ) - -> Result>, _phragmen::Error> - where - for<'r> FM: Fn(&'r A) -> u128, - { - let mut assignments: Vec<_phragmen::StakedAssignment> = Default::default(); - #into_impl_single - #into_impl_double - #into_impl_rest - - Ok(assignments) - } - - /// Generate self from a vector of `StakedAssignment`. - pub fn from_staked( - assignments: Vec<_phragmen::StakedAssignment>, - index_of_voter: FV, - index_of_target: FT, - ) -> Result - where - for<'r> FV: Fn(&'r A) -> Option<#voter_type>, - for<'r> FT: Fn(&'r A) -> Option<#target_type>, - { - let mut compact: #ident<#voter_type, #target_type, u128, A> = Default::default(); - for _phragmen::StakedAssignment { who, distribution } in assignments { - match distribution.len() { - #from_impl_single - #from_impl_double - #from_impl_rest - _ => { - return Err(_phragmen::Error::CompactTargetOverflow); - } - } - }; - Ok(compact) - } - } - ); - - quote!( - #final_impl - ) -} - struct CompactSolutionDef { vis: syn::Visibility, ident: syn::Ident, diff --git a/primitives/phragmen/compact/src/staked.rs b/primitives/phragmen/compact/src/staked.rs new file mode 100644 index 0000000000000..3d39466853d2d --- /dev/null +++ b/primitives/phragmen/compact/src/staked.rs @@ -0,0 +1,206 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Code generation for the staked assignment type. + +use crate::field_name_for; +use proc_macro2::{TokenStream as TokenStream2}; +use syn::{GenericArgument}; +use quote::quote; + +fn from_impl(count: usize) -> TokenStream2 { + let from_impl_single = { + let name = field_name_for(1); + quote!(1 => compact.#name.push( + ( + index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, + index_of_target(&distribution[0].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, + ) + ),) + }; + + let from_impl_double = { + let name = field_name_for(2); + quote!(2 => compact.#name.push( + ( + index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, + ( + index_of_target(&distribution[0].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, + distribution[0].1, + ), + index_of_target(&distribution[1].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, + ) + ),) + }; + + let from_impl_rest = (3..=count).map(|c| { + let inner = (0..c-1).map(|i| + quote!((index_of_target(&distribution[#i].0).ok_or(_phragmen::Error::CompactInvalidIndex)?, distribution[#i].1),) + ).collect::(); + + let field_name = field_name_for(c); + let last_index = c - 1; + let last = quote!(index_of_target(&distribution[#last_index].0).ok_or(_phragmen::Error::CompactInvalidIndex)?); + + quote!( + #c => compact.#field_name.push((index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, [#inner], #last)), + ) + }).collect::(); + + quote!( + #from_impl_single + #from_impl_double + #from_impl_rest + ) +} + +fn into_impl(count: usize) -> TokenStream2 { + let into_impl_single = { + let name = field_name_for(1); + quote!( + for (voter_index, target_index) in self.#name { + let who = voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?; + let all_stake = max_of(&who); + assignments.push(_phragmen::StakedAssignment { + who, + distribution: vec![(target_at(target_index).ok_or(_phragmen::Error::CompactInvalidIndex)?, all_stake)], + }) + } + ) + }; + + let into_impl_double = { + let name = field_name_for(2); + quote!( + for (voter_index, (t1_idx, w1), t2_idx) in self.#name { + let who = voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?; + let all_stake = max_of(&who); + + if w1 >= all_stake { + return Err(_phragmen::Error::CompactStakeOverflow); + } + + // w2 is ensured to be positive. + let w2 = all_stake - w1; + assignments.push( _phragmen::StakedAssignment { + who, + distribution: vec![ + (target_at(t1_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, w1), + (target_at(t2_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, w2), + ] + }); + } + ) + }; + + let into_impl_rest = (3..=count).map(|c| { + let name = field_name_for(c); + quote!( + for (voter_index, inners, t_last_idx) in self.#name { + let who = voter_at(voter_index).ok_or(_phragmen::Error::CompactInvalidIndex)?; + let mut sum = u128::min_value(); + let all_stake = max_of(&who); + + let mut inners_parsed = inners + .iter() + .map(|(ref t_idx, w)| { + sum = sum.saturating_add(*w); + let target = target_at(*t_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?; + Ok((target, *w)) + }).collect::, _phragmen::Error>>()?; + + if sum >= all_stake { + return Err(_phragmen::Error::CompactStakeOverflow); + } + // w_last is proved to be positive. + let w_last = all_stake - sum; + + inners_parsed.push((target_at(t_last_idx).ok_or(_phragmen::Error::CompactInvalidIndex)?, w_last)); + + assignments.push(_phragmen::StakedAssignment { + who, + distribution: inners_parsed, + }); + } + ) + }).collect::(); + + quote!( + #into_impl_single + #into_impl_double + #into_impl_rest + ) +} + +pub(crate) fn staked( + ident: syn::Ident, + voter_type: GenericArgument, + target_type: GenericArgument, + count: usize, +) -> TokenStream2 { + + let from_impl = from_impl(count); + let into_impl = into_impl(count); + quote!( + impl< + #voter_type: _phragmen::codec::Codec + Default + Copy, + #target_type: _phragmen::codec::Codec + Default + Copy, + A: _phragmen::codec::Codec + Default + Clone, + > + #ident<#voter_type, #target_type, u128, A> + { + /// Generate self from a vector of `StakedAssignment`. + pub fn from_staked( + assignments: Vec<_phragmen::StakedAssignment>, + index_of_voter: FV, + index_of_target: FT, + ) -> Result + where + for<'r> FV: Fn(&'r A) -> Option<#voter_type>, + for<'r> FT: Fn(&'r A) -> Option<#target_type>, + { + let mut compact: #ident<#voter_type, #target_type, u128, A> = Default::default(); + for _phragmen::StakedAssignment { who, distribution } in assignments { + match distribution.len() { + #from_impl + _ => { + return Err(_phragmen::Error::CompactTargetOverflow); + } + } + }; + Ok(compact) + } + + /// Convert self into `StakedAssignment`. The given function should return the total + /// weight of a voter. It is used to subtract the sum of all the encoded weights to + /// infer the last one. + pub fn into_staked( + self, + max_of: FM, + voter_at: impl Fn(#voter_type) -> Option, + target_at: impl Fn(#target_type) -> Option, + ) + -> Result>, _phragmen::Error> + where + for<'r> FM: Fn(&'r A) -> u128, + { + let mut assignments: Vec<_phragmen::StakedAssignment> = Default::default(); + #into_impl + Ok(assignments) + } + } + ) +} diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 98d38d0d0bc6c..03892d0e544e8 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -52,7 +52,12 @@ mod reduce; // re-export reduce stuff pub use reduce::reduce; -// re-export the compact macro +// re-export the compact macro, with the dependencies of the macro. +#[doc(hidden)] +pub use codec; +#[doc(hidden)] +pub use sp_runtime; + pub use sp_phragmen_compact::generate_compact_solution_type; // an aggregator trait for a generic type of a voter/target identifier. This usually maps to From 02c4e3b2bdc4b740916bfe6c726bf3e25771c17b Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 12 Feb 2020 16:37:35 +0100 Subject: [PATCH 041/106] Some fixes --- frame/staking/src/lib.rs | 10 +++--- frame/staking/src/mock.rs | 2 +- frame/staking/src/offchain_election.rs | 23 +------------ frame/staking/src/tests.rs | 40 +++++------------------ primitives/phragmen/src/lib.rs | 45 +++++++++++++++++--------- primitives/phragmen/src/tests.rs | 29 ++++++++++++++++- 6 files changed, 71 insertions(+), 78 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 24277d7da719d..a20739d91e376 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -277,7 +277,6 @@ use sp_runtime::{ }, transaction_validity::{ TransactionValidityError, TransactionValidity, ValidTransaction, InvalidTransaction, - TransactionPriority, }, }; use sp_staking::{ @@ -292,7 +291,7 @@ use frame_system::{ }; use sp_phragmen::{ ExtendedBalance, StakedAssignment, Assignment, PhragmenScore, PhragmenResult, - build_support_map, evaluate_support, elect, generate_compact_solution_type, + build_support_map, evaluate_support, elect, generate_compact_solution_type, is_score_better, }; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; @@ -1765,7 +1764,7 @@ impl Module { // assume the given score is valid. Is it better than what we have on-chain, if we have any? if let Some(queued_score) = Self::queued_score() { ensure!( - offchain_election::is_score_better(queued_score, claimed_score), + is_score_better(queued_score, claimed_score), Error::::PhragmenWeakSubmission, ) } @@ -2574,7 +2573,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { use offchain_election::SignaturePayloadOf; if let Some(queued_score) = Self::queued_score() { - if !offchain_election::is_score_better(queued_score, *score) { + if !is_score_better(queued_score, *score) { debug::native::debug!( target: "staking", "rejecting unsigned transaction because the claimed score is not good enough." @@ -2605,9 +2604,8 @@ impl frame_support::unsigned::ValidateUnsigned for Module { } Ok(ValidTransaction { - priority: TransactionPriority::max_value(), + priority: score[0].saturated_into(), requires: vec![], - // TODO: what is a good value for this? Also set priority provides: vec![(Self::current_era(), validator_key).encode()], longevity: TryInto::::try_into(T::ElectionLookahead::get()).unwrap_or(150_u64), propagate: true, diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index db963e375ba9e..c5478e1e5f1f9 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -847,7 +847,7 @@ pub fn horrible_phragmen_with_post_processing( let support = build_support_map::(&winners, &staked_assignment).0; let score = evaluate_support(&support); - assert!(offchain_election::is_score_better(score, better_score)); + assert!(sp_phragmen::is_score_better(score, better_score)); score }; diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index b0db19d92cafe..56a7ba62bbd51 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -20,7 +20,7 @@ use crate::{Call, Module, Trait, BalanceOf, ValidatorIndex, NominatorIndex, Comp use codec::Encode; use frame_system::offchain::{SubmitUnsignedTransaction}; use sp_phragmen::{reduce, ExtendedBalance, PhragmenResult, StakedAssignment, Assignment, PhragmenScore}; -use sp_std::{prelude::*, cmp::Ordering, convert::TryInto}; +use sp_std::{prelude::*, convert::TryInto}; use sp_runtime::{RuntimeAppPublic, RuntimeDebug}; use sp_runtime::offchain::storage::StorageValueRef; use sp_runtime::traits::Convert; @@ -79,27 +79,6 @@ pub(crate) fn set_check_offchain_execution_status(now: T::BlockNumber) } } -/// Compares two sets of phragmen scores based on desirability and returns true if `that` is -/// better `this`. -/// -/// Evaluation is done in a lexicographic manner. -/// -/// Note that the third component should be minimized. -pub(crate) fn is_score_better(this: PhragmenScore, that: PhragmenScore) -> bool { - match that - .iter() - .enumerate() - .map(|(i, e)| e.cmp(&this[i])) - .collect::>() - .as_slice() - { - [Ordering::Greater, _, _] => true, - [Ordering::Equal, Ordering::Greater, _] => true, - [Ordering::Equal, Ordering::Equal, Ordering::Less] => true, - _ => false, - } -} - /// The internal logic of the offchain worker of this module. pub(crate) fn compute_offchain_election() -> Result<(), OffchainElectionError> { let keys = >::keys(); diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 0461fa244e47c..29c8e7b5a10eb 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -19,7 +19,10 @@ use super::*; use mock::*; use codec::Encode; -use sp_runtime::{assert_eq_error_rate, traits::{OnInitialize, BadOrigin}}; +use sp_runtime::{ + assert_eq_error_rate, + traits::{OnInitialize, BadOrigin}, +}; use sp_staking::offence::OffenceDetails; use frame_support::{ assert_ok, assert_noop, @@ -2747,33 +2750,6 @@ mod offchain_phragmen { state } - #[test] - fn score_comparison_is_lexicographical() { - // only better in the fist parameter, worse in the other two ✅ - assert_eq!( - offchain_election::is_score_better([10, 20, 30], [12, 10, 35]), - true, - ); - - // worse in the first, better in the other two ❌ - assert_eq!( - offchain_election::is_score_better([10, 20, 30], [9, 30, 10]), - false, - ); - - // equal in the first, the second one dictates. - assert_eq!( - offchain_election::is_score_better([10, 20, 30], [10, 25, 40]), - true, - ); - - // equal in the first two, the last one dictates. - assert_eq!( - offchain_election::is_score_better([10, 20, 30], [10, 20, 40]), - false, - ); - } - #[test] fn is_current_session_final_works() { ExtBuilder::default().session_per_era(3).build().execute_with(|| { @@ -3002,18 +2978,18 @@ mod offchain_phragmen { #[test] #[allow(deprecated)] - fn offchain_worker_runs_when_window_open_unsigned() { + fn offchain_worker_runs_when_window_open() { // at the end of the first finalized block with ElectionStatus::open(_), it should execute. let mut ext = ExtBuilder::default() .offchain_phragmen_ext() - .validator_count(4) + .validator_count(2) .build(); let state = offchainify(&mut ext); ext.execute_with(||{ run_to_block(12); // local key 11 is in the elected set. - assert_eq_uvec!(Staking::current_elected(), vec![11, 21, 31]); + assert_eq_uvec!(Staking::current_elected(), vec![11, 21]); assert_eq!(state.read().transactions.len(), 0); Staking::offchain_worker(12); assert_eq!(state.read().transactions.len(), 1); @@ -3031,7 +3007,7 @@ mod offchain_phragmen { assert_eq!( ::validate_unsigned(&inner), TransactionValidity::Ok(ValidTransaction { - priority: TransactionPriority::max_value(), + priority: 1125, // the proposed slot stake. requires: vec![], provides: vec![(Staking::current_era(), signing_key).encode()], longevity: TryInto::::try_into( diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 03892d0e544e8..25a2100a79298 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -33,7 +33,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::{prelude::*, collections::btree_map::BTreeMap, fmt::Debug}; +use sp_std::{prelude::*, collections::btree_map::BTreeMap, fmt::Debug, cmp::Ordering}; use sp_runtime::{helpers_128bit::multiply_by_rational, Perbill, Rational128, RuntimeDebug}; use sp_runtime::traits::{Zero, Convert, Member, SimpleArithmetic, Saturating, Bounded}; @@ -64,6 +64,8 @@ pub use sp_phragmen_compact::generate_compact_solution_type; // substrate's account id. pub trait IdentifierT: Clone + Eq + Default + Ord + Debug {} +impl IdentifierT for T {} + /// The errors that might occur in the this crate and compact. #[derive(Debug, Eq, PartialEq)] pub enum Error { @@ -74,18 +76,8 @@ pub enum Error { CompactTargetOverflow, /// One of the index functions returned none. CompactInvalidIndex, - -} - -#[macro_export] -macro_rules! some { - ($inner:expr) => { - $some.ok_or(_phragmen::CompactInvalidIndex)? - }; } -impl IdentifierT for T {} - /// A type in which performing operations on balances and stakes of candidates and voters are safe. /// /// This module's functions expect a `Convert` type to convert all balances to u64. Hence, u128 is @@ -547,17 +539,38 @@ pub fn evaluate_support( [min_support, sum, sum_squared] } +/// Compares two sets of phragmen scores based on desirability and returns true if `that` is +/// better `this`. +/// +/// Evaluation is done in a lexicographic manner. +/// +/// Note that the third component should be minimized. +pub fn is_score_better(this: PhragmenScore, that: PhragmenScore) -> bool { + match that + .iter() + .enumerate() + .map(|(i, e)| e.cmp(&this[i])) + .collect::>() + .as_slice() + { + [Ordering::Greater, _, _] => true, + [Ordering::Equal, Ordering::Greater, _] => true, + [Ordering::Equal, Ordering::Equal, Ordering::Less] => true, + _ => false, + } +} + /// Performs equalize post-processing to the output of the election algorithm. This happens in /// rounds. The number of rounds and the maximum diff-per-round tolerance can be tuned through input /// parameters. /// /// No value is returned from the function and the `supports` parameter is updated. /// -/// * `assignments`: exactly the same is the output of phragmen. -/// * `supports`: mutable reference to s `SupportMap`. This parameter is updated. -/// * `tolerance`: maximum difference that can occur before an early quite happens. -/// * `iterations`: maximum number of iterations that will be processed. -/// * `stake_of`: something that can return the stake stake of a particular candidate or voter. +/// `assignments`: exactly the same is the output of phragmen. +/// `supports`: mutable reference to s `SupportMap`. This parameter is updated. +/// `tolerance`: maximum difference that can occur before an early quite happens. +/// `iterations`: maximum number of iterations that will be processed. +/// `stake_of`: something that can return the stake stake of a particular candidate or voter. pub fn equalize( mut assignments: Vec>, supports: &mut SupportMap, diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index 9f3cbe36369aa..342a5b1821f80 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -20,7 +20,7 @@ use crate::mock::*; use crate::{ - elect, equalize, build_support_map, + elect, equalize, build_support_map, is_score_better, Support, StakedAssignment, Assignment, PhragmenResult, ExtendedBalance, }; use substrate_test_utils::assert_eq_uvec; @@ -551,6 +551,33 @@ fn assignment_convert_works() { ); } +#[test] +fn score_comparison_is_lexicographical() { + // only better in the fist parameter, worse in the other two ✅ + assert_eq!( + is_score_better([10, 20, 30], [12, 10, 35]), + true, + ); + + // worse in the first, better in the other two ❌ + assert_eq!( + is_score_better([10, 20, 30], [9, 30, 10]), + false, + ); + + // equal in the first, the second one dictates. + assert_eq!( + is_score_better([10, 20, 30], [10, 25, 40]), + true, + ); + + // equal in the first two, the last one dictates. + assert_eq!( + is_score_better([10, 20, 30], [10, 20, 40]), + false, + ); +} + mod compact { use codec::{Decode, Encode}; use crate::generate_compact_solution_type; From aea7ad2b18697d1e08eab8cff9dbccc1f50db093 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 12 Feb 2020 17:12:28 +0100 Subject: [PATCH 042/106] Fix doc test --- primitives/phragmen/compact/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index 7b157a6a1b07f..ccc1e61be1115 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -41,7 +41,7 @@ const PREFIX: &'static str = "votes"; /// - `T`: identifier/index type of the target. /// - `W`: any type used as the edge weight. /// -/// ```rust +/// ```nocompile /// // generate a struct with nominator and edge weight u128, with maximum supported /// // edge per voter of 32. /// generate_compact_solution_type(TestCompact, 32) @@ -57,7 +57,7 @@ const PREFIX: &'static str = "votes"; /// /// An example expansion of length 16 is as follows: /// -/// ```rust +/// ```nocompile /// struct TestCompact { /// votes1: Vec<(V, T)>, /// votes2: Vec<(V, (T, W), T)>, From e2210cc7d56a0c26936c22766a04349539e1608a Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 13 Feb 2020 09:37:44 +0100 Subject: [PATCH 043/106] loop for submission --- frame/staking/src/offchain_election.rs | 184 +++++++++++++------------ frame/staking/src/tests.rs | 21 +++ 2 files changed, 117 insertions(+), 88 deletions(-) diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 56a7ba62bbd51..864d612d0b384 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -19,6 +19,7 @@ use crate::{Call, Module, Trait, BalanceOf, ValidatorIndex, NominatorIndex, CompactOf}; use codec::Encode; use frame_system::offchain::{SubmitUnsignedTransaction}; +use frame_support::debug; use sp_phragmen::{reduce, ExtendedBalance, PhragmenResult, StakedAssignment, Assignment, PhragmenScore}; use sp_std::{prelude::*, convert::TryInto}; use sp_runtime::{RuntimeAppPublic, RuntimeDebug}; @@ -84,94 +85,101 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti let keys = >::keys(); let local_keys = T::KeyType::all(); - if let Some((index, ref pubkey)) = local_keys - .into_iter() - .find_map(|key| - keys - .iter() - .enumerate() - .find_map(|(index, val_key)| - if *val_key == key { Some((index, val_key)) } else { None } - ) - ) { - // make sure that the snapshot is available. - let snapshot_validators = >::snapshot_validators() - .ok_or(OffchainElectionError::SnapshotUnavailable)?; - let snapshot_nominators = >::snapshot_nominators() - .ok_or(OffchainElectionError::SnapshotUnavailable)?; - // k is a local key who is also among the validators. - let PhragmenResult { - winners, - assignments, - } = >::do_phragmen().ok_or(OffchainElectionError::ElectionFailed)?; - - // convert winners into just account ids. - let winners: Vec = winners.into_iter().map(|(w, _)| w).collect(); - - // convert into staked. This is needed to be able to reduce. - let mut staked: Vec> = assignments - .into_iter() - .map(|a| { - let stake = , u64>>::convert( - >::slashable_balance_of(&a.who) - ) as ExtendedBalance; - a.into_staked(stake, true) - }) - .collect(); - - // reduce the assignments. This will remove some additional edges. - reduce(&mut staked); - - // get support and score. - let (support, _) = sp_phragmen::build_support_map::(&winners, &staked); - let score = sp_phragmen::evaluate_support(&support); - - // helpers for building the compact - let nominator_index = |a: &T::AccountId| -> Option { - snapshot_nominators.iter().position(|x| x == a).and_then(|i| - >::try_into(i).ok() - ) - }; - let validator_index = |a: &T::AccountId| -> Option { - snapshot_validators.iter().position(|x| x == a).and_then(|i| - >::try_into(i).ok() - ) - }; - - // convert back to ratio assignment. This takes less space. - let assignments_reduced: Vec> = staked - .into_iter() - .map(|sa| sa.into_assignment(true)) - .collect(); - - // compact encode the assignment. - let compact = >::from_assignment( - assignments_reduced, - nominator_index, - validator_index, - ).map_err(|_| OffchainElectionError::CompactFailed)?; - - // convert winners to index as well - let winners = winners.into_iter().map(|w| - validator_index(&w).expect("winners are chosen from the snapshot list; the must have index; qed") - ).collect::>(); - - let signature_payload: SignaturePayloadOf = - (&winners, &compact, &score, &(index as u32)); - let signature = pubkey.sign(&signature_payload.encode()) - .ok_or(OffchainElectionError::SigningFailed)?; - let call: ::Call = Call::submit_election_solution_unsigned( - winners, - compact, - score, - index as u32, - signature, + // For each local key is in the stored authority keys, try and submit. Breaks out after first + // successful submission. + for (index, ref pubkey) in local_keys.into_iter().filter_map(|key| + keys.iter().enumerate().find_map(|(index, val_key)| + if *val_key == key { + Some((index, val_key)) + } else { + None + } + ) + ) { + // make sure that the snapshot is available. + let snapshot_validators = >::snapshot_validators() + .ok_or(OffchainElectionError::SnapshotUnavailable)?; + let snapshot_nominators = >::snapshot_nominators() + .ok_or(OffchainElectionError::SnapshotUnavailable)?; + // k is a local key who is also among the validators. + let PhragmenResult { + winners, + assignments, + } = >::do_phragmen().ok_or(OffchainElectionError::ElectionFailed)?; + + // convert winners into just account ids. + let winners: Vec = winners.into_iter().map(|(w, _)| w).collect(); + + // convert into staked. This is needed to be able to reduce. + let mut staked: Vec> = assignments + .into_iter() + .map(|a| { + let stake = , u64>>::convert( + >::slashable_balance_of(&a.who) + ) as ExtendedBalance; + a.into_staked(stake, true) + }) + .collect(); + + // reduce the assignments. This will remove some additional edges. + reduce(&mut staked); + + // get support and score. + let (support, _) = sp_phragmen::build_support_map::(&winners, &staked); + let score = sp_phragmen::evaluate_support(&support); + + // helpers for building the compact + let nominator_index = |a: &T::AccountId| -> Option { + snapshot_nominators.iter().position(|x| x == a).and_then(|i| + >::try_into(i).ok() ) - .into(); - let _result = T::SubmitTransaction::submit_unsigned(call); + }; + let validator_index = |a: &T::AccountId| -> Option { + snapshot_validators.iter().position(|x| x == a).and_then(|i| + >::try_into(i).ok() + ) + }; + + // convert back to ratio assignment. This takes less space. + let assignments_reduced: Vec> = staked + .into_iter() + .map(|sa| sa.into_assignment(true)) + .collect(); + + // compact encode the assignment. + let compact = >::from_assignment( + assignments_reduced, + nominator_index, + validator_index, + ).map_err(|_| OffchainElectionError::CompactFailed)?; + + // convert winners to index as well + let winners = winners.into_iter().map(|w| + validator_index(&w).expect("winners are chosen from the snapshot list; the must have index; qed") + ).collect::>(); + + let signature_payload: SignaturePayloadOf = + (&winners, &compact, &score, &(index as u32)); + let signature = pubkey.sign(&signature_payload.encode()) + .ok_or(OffchainElectionError::SigningFailed)?; + let call: ::Call = Call::submit_election_solution_unsigned( + winners, + compact, + score, + index as u32, + signature, + ).into(); + + let ok = T::SubmitTransaction::submit_unsigned(call).map_err(|_| + debug::native::warn!( + target: "staking", + "failed to submit offchain solution with key {:?}", + pubkey, + ) + ).is_ok(); + if ok { return Ok(()) } + } - Ok(()) - } else { - Err(OffchainElectionError::NoSigningKey) - } + // no key left and no submission. + Err(OffchainElectionError::NoSigningKey) } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 29c8e7b5a10eb..86ce6d342c9b9 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -3055,6 +3055,27 @@ mod offchain_phragmen { }) } + #[test] + fn offchain_wont_run_without_signing_key() { + let mut ext = ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(2) + .local_key_account(5) + .build(); + let state = offchainify(&mut ext); + ext.execute_with(||{ + run_to_block(12); + + // local key 5 is not there. + assert_eq_uvec!(Staking::current_elected(), vec![11, 21]); + + assert_eq!(state.read().transactions.len(), 0); + Staking::offchain_worker(12); + assert_eq!(state.read().transactions.len(), 0); + // Error in phragmen offchain worker call: OffchainElectionError::NoSigningKey + }) + } + #[test] fn invalid_phragmen_result_correct_number_of_winners() { ExtBuilder::default() From ca200339ff2746b6081e2c1ca03636374a0ea1d2 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 13 Feb 2020 11:41:16 +0100 Subject: [PATCH 044/106] Fix cli, keyring etc. --- Cargo.lock | 1 + bin/node/cli/Cargo.toml | 1 + bin/node/cli/src/chain_spec.rs | 34 +++++++++++++++++++++--- bin/node/runtime/src/lib.rs | 1 + bin/node/testing/src/keyring.rs | 1 + bin/utils/chain-spec-builder/src/main.rs | 7 ++++- frame/staking/src/lib.rs | 33 ++++++++++++++--------- frame/staking/src/offchain_election.rs | 6 ++--- 8 files changed, 64 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b85e14727bae7..98e823d6af5ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3360,6 +3360,7 @@ dependencies = [ "pallet-contracts", "pallet-im-online", "pallet-indices", + "pallet-staking", "pallet-timestamp", "pallet-transaction-payment", "parity-scale-codec", diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index dad76ae4bfdad..88ca926011c12 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -75,6 +75,7 @@ pallet-transaction-payment = { version = "2.0.0", path = "../../../frame/transac frame-support = { version = "2.0.0", default-features = false, path = "../../../frame/support" } pallet-im-online = { version = "2.0.0", default-features = false, path = "../../../frame/im-online" } pallet-authority-discovery = { version = "2.0.0", path = "../../../frame/authority-discovery" } +pallet-staking = { version = "2.0.0", path = "../../../frame/staking" } # node-specific dependencies node-runtime = { version = "2.0.0", path = "../runtime" } diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index 868b991480b89..0bdba99572bf5 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -32,6 +32,7 @@ use sc_telemetry::TelemetryEndpoints; use grandpa_primitives::{AuthorityId as GrandpaId}; use sp_consensus_babe::{AuthorityId as BabeId}; use pallet_im_online::sr25519::{AuthorityId as ImOnlineId}; +use pallet_staking::sr25519::{AuthorityId as StakingId}; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_runtime::{Perbill, traits::{Verify, IdentifyAccount}}; @@ -70,8 +71,9 @@ fn session_keys( babe: BabeId, im_online: ImOnlineId, authority_discovery: AuthorityDiscoveryId, + staking: StakingId, ) -> SessionKeys { - SessionKeys { grandpa, babe, im_online, authority_discovery } + SessionKeys { grandpa, babe, im_online, authority_discovery, staking, } } fn staging_testnet_config_genesis() -> GenesisConfig { @@ -81,7 +83,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig { // and // for i in 1 2 3 4 ; do for j in session; do subkey --ed25519 inspect "$secret"//fir//$j//$i; done; done - let initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId)> = vec![( + let initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId, StakingId)> = vec![( // 5Fbsd6WXDGiLTxunqeK5BATNiocfCqu9bS1yArVjCgeBLkVy hex!["9c7a2ee14e565db0c69f78c7b4cd839fbf52b607d867e9e9c5a79042898a0d12"].into(), // 5EnCiV7wSHeNhjW3FSUwiJNkcc2SBkPLn5Nj93FmbLtBjQUq @@ -94,6 +96,8 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(), // 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8 hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(), + // 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8 + hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(), ),( // 5ERawXCzCWkjVq3xz1W5KGNtVx2VdefvZ62Bw1FEuZW4Vny2 hex!["68655684472b743e456907b398d3a44c113f189e56d1bbfd55e889e295dfde78"].into(), @@ -107,6 +111,8 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(), // 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(), + // 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ + hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(), ),( // 5DyVtKWPidondEu8iHZgi6Ffv9yrJJ1NDNLom3X9cTDi98qp hex!["547ff0ab649283a7ae01dbc2eb73932eba2fb09075e9485ff369082a2ff38d65"].into(), @@ -120,6 +126,8 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(), // 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(), + // 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH + hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(), ),( // 5HYZnKWe5FVZQ33ZRJK1rG3WaLMztxWrrNDb1JRwaHHVWyP9 hex!["f26cdb14b5aec7b2789fd5ca80f979cef3761897ae1f37ffb3e154cbcc1c2663"].into(), @@ -133,6 +141,8 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(), // 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(), + // 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x + hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(), )]; // generated with secret: subkey inspect "$secret"/fir @@ -188,6 +198,7 @@ pub fn get_authority_keys_from_seed(seed: &str) -> ( BabeId, ImOnlineId, AuthorityDiscoveryId, + StakingId, ) { ( get_account_id_from_seed::(&format!("{}//stash", seed)), @@ -196,12 +207,21 @@ pub fn get_authority_keys_from_seed(seed: &str) -> ( get_from_seed::(seed), get_from_seed::(seed), get_from_seed::(seed), + get_from_seed::(seed), ) } /// Helper function to create GenesisConfig for testing pub fn testnet_genesis( - initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId)>, + initial_authorities: Vec<( + AccountId, + AccountId, + GrandpaId, + BabeId, + ImOnlineId, + AuthorityDiscoveryId, + StakingId, + )>, root_key: AccountId, endowed_accounts: Option>, enable_println: bool, @@ -245,7 +265,13 @@ pub fn testnet_genesis( }), pallet_session: Some(SessionConfig { keys: initial_authorities.iter().map(|x| { - (x.0.clone(), session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone())) + (x.0.clone(), session_keys( + x.2.clone(), + x.3.clone(), + x.4.clone(), + x.5.clone(), + x.6.clone(), + )) }).collect::>(), }), pallet_staking: Some(StakingConfig { diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index ca3934d5d17e0..b35ec61734fc6 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -272,6 +272,7 @@ impl_opaque_keys! { pub babe: Babe, pub im_online: ImOnline, pub authority_discovery: AuthorityDiscovery, + pub staking: Staking, } } diff --git a/bin/node/testing/src/keyring.rs b/bin/node/testing/src/keyring.rs index 5fa1e48b03218..3154ad719f1e9 100644 --- a/bin/node/testing/src/keyring.rs +++ b/bin/node/testing/src/keyring.rs @@ -62,6 +62,7 @@ pub fn to_session_keys( babe: sr25519_keyring.to_owned().public().into(), im_online: sr25519_keyring.to_owned().public().into(), authority_discovery: sr25519_keyring.to_owned().public().into(), + staking: sr25519_keyring.to_owned().public().into(), } } diff --git a/bin/utils/chain-spec-builder/src/main.rs b/bin/utils/chain-spec-builder/src/main.rs index b1eab2ebe5486..a2faa3857b9b7 100644 --- a/bin/utils/chain-spec-builder/src/main.rs +++ b/bin/utils/chain-spec-builder/src/main.rs @@ -141,7 +141,7 @@ fn generate_authority_keys_and_store( None, ).map_err(|err| err.to_string())?; - let (_, _, grandpa, babe, im_online, authority_discovery) = + let (_, _, grandpa, babe, im_online, authority_discovery, staking) = chain_spec::get_authority_keys_from_seed(seed); let insert_key = |key_type, public| { @@ -171,6 +171,11 @@ fn generate_authority_keys_and_store( sp_core::crypto::key_types::AUTHORITY_DISCOVERY, authority_discovery.as_slice(), )?; + + insert_key( + sp_core::crypto::key_types::STAKING, + staking.as_slice(), + )?; } Ok(()) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index a20739d91e376..d47fde2da2812 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -1051,25 +1051,27 @@ decl_module! { /// to open. If so, it runs the offchain worker code. fn offchain_worker(now: T::BlockNumber) { debug::RuntimeLogger::init(); + use offchain_election::{set_check_offchain_execution_status, compute_offchain_election}; - let offchain_status = offchain_election::set_check_offchain_execution_status::(now); - let window_open = Self::era_election_status() == ElectionStatus::::Open(now); + let window_open = + Self::era_election_status() == ElectionStatus::::Open(now); - if window_open && offchain_status.is_ok() { - if let Err(e) = offchain_election::compute_offchain_election::() { - debug::native::warn!( - target: "staking", - "Error in phragmen offchain worker call: {:?}", - e, - ); - }; - } else if window_open { + if window_open { + let offchain_status = set_check_offchain_execution_status::(now); if let Err(why) = offchain_status { - debug::native::debug!( + debug::native::warn!( target: "staking", "skipping offchain call in open election window due to [{}]", why, ); + } else { + if let Err(e) = compute_offchain_election::() { + debug::native::warn!( + target: "staking", + "Error in phragmen offchain worker call: {:?}", + e, + ); + }; } } } @@ -2065,6 +2067,13 @@ impl Module { // emit event Self::deposit_event(RawEvent::StakingElection(compute)); + debug::native::info!( + target: "staking", + "new validator set of size {:?} has been elected via {:?}", + elected_stashes.len(), + compute, + ); + Some(elected_stashes) } else { None diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 864d612d0b384..4dd0a74025c28 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -170,13 +170,13 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti signature, ).into(); - let ok = T::SubmitTransaction::submit_unsigned(call).map_err(|_| + let ok = T::SubmitTransaction::submit_unsigned(call).map_err(|_| { debug::native::warn!( target: "staking", "failed to submit offchain solution with key {:?}", pubkey, - ) - ).is_ok(); + ); + }).is_ok(); if ok { return Ok(()) } } From 10e97e33a728408da782828b4d45ba7584248f3c Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 14 Feb 2020 09:09:03 +0100 Subject: [PATCH 045/106] some cleanup --- frame/session/src/lib.rs | 2 +- frame/staking/src/lib.rs | 82 ++++++++++++----------- frame/staking/src/mock.rs | 10 +-- frame/staking/src/offchain_election.rs | 13 +++- frame/staking/src/tests.rs | 30 ++++++--- primitives/arithmetic/src/per_things.rs | 4 +- primitives/phragmen/compact/src/lib.rs | 2 - primitives/phragmen/fuzzer/Cargo.lock | 87 +++++++++++++------------ primitives/phragmen/fuzzer/Cargo.toml | 4 +- primitives/phragmen/src/lib.rs | 2 +- primitives/phragmen/src/node.rs | 13 ++-- primitives/phragmen/src/tests.rs | 27 ++++---- 12 files changed, 150 insertions(+), 126 deletions(-) diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index 7536661dc2d01..04487cbf1312e 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -650,7 +650,7 @@ impl Module { } } - pub fn load_keys(v: &T::ValidatorId) -> Option { + fn load_keys(v: &T::ValidatorId) -> Option { >::get(DEDUP_KEY_PREFIX, v) } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 0b44e75975825..87d6815001a6a 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -256,7 +256,7 @@ pub mod slashing; pub mod offchain_election; pub mod inflation; -use sp_std::{prelude::*, result, convert::{TryInto, From}}; +use sp_std::{prelude::*, result, convert::{TryInto, From}, ops}; use codec::{HasCompact, Encode, Decode}; use frame_support::{ decl_module, decl_event, decl_storage, ensure, decl_error, debug, Parameter, @@ -616,14 +616,14 @@ pub struct ElectionResult { #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub enum ElectionStatus { /// Nothing has and will happen for now. submission window is not open. - None, + Close, /// The submission window has been open since the contained block number. Open(BlockNumber), } impl Default for ElectionStatus { fn default() -> Self { - Self::None + Self::Close } } @@ -1020,7 +1020,7 @@ decl_module! { Self::ensure_storage_upgraded(); if // if we don't have any ongoing offchain compute. - Self::era_election_status() == ElectionStatus::None && + Self::era_election_status() == ElectionStatus::Close && // and an era is about to be changed. Self::is_current_session_final() { @@ -1761,11 +1761,10 @@ impl Module { compact_assignments: CompactOf, compute: ElectionCompute, claimed_score: PhragmenScore, - // at: T::BlockNumber, ) -> Result<(), Error> { // discard early solutions match Self::era_election_status() { - ElectionStatus::None => Err(Error::::PhragmenEarlySubmission)?, + ElectionStatus::Close => Err(Error::::PhragmenEarlySubmission)?, ElectionStatus::Open(_) => { /* Open and no solutions received. Allowed. */ }, } @@ -2035,7 +2034,7 @@ impl Module { /// - [`CurrentElected`]: with the new elected set. /// - [`EraElectionStatus`]: with `None` /// - /// Internally, [`QueuedElected`] and [`QueuedScore`] are also consumed. + /// Internally, [`QueuedElected`], snapshots and [`QueuedScore`] are also consumed. /// /// If the election has been successful, It passes the new set upwards. /// @@ -2048,7 +2047,7 @@ impl Module { compute, }) = Self::try_do_phragmen() { // We have chosen the new validator set. Submission is no longer allowed. - >::put(ElectionStatus::None); + >::put(ElectionStatus::Close); // kill the snapshots. Self::kill_stakers_snapshot(); @@ -2066,7 +2065,6 @@ impl Module { // Update slot stake. >::put(&slot_stake); - // Set the new validator set in sessions. // Update current elected. >::put(&elected_stashes); @@ -2108,7 +2106,7 @@ impl Module { /// /// No storage item is updated. fn do_phragmen_with_post_processing< - Accuracy: PerThing + sp_std::ops::Mul, + Accuracy: PerThing + ops::Mul, >( compute: ElectionCompute, ) -> Option>> { @@ -2133,11 +2131,6 @@ impl Module { &staked_assignments, ); - // Clear Stakers. - for v in Self::current_elected().iter() { - >::remove(v); - } - let to_balance = |e: ExtendedBalance| >>::convert(e); @@ -2164,8 +2157,8 @@ impl Module { others, // This might reasonably saturate and we cannot do much about it. The sum of // someone's stake might exceed the balance type if they have the maximum amount - // of balance and receive some support. This is super unlikely to happen, yet - // we simulate it in some tests. + // of balance and receive some support. This is super unlikely to happen, yet we + // simulate it in some tests. total, }; @@ -2175,10 +2168,10 @@ impl Module { (validator, exposure) }).collect::)>>(); - // In order to keep the property required by `n_session_ending` - // that we must return the new validator set even if it's the same as the old, - // as long as any underlying economic conditions have changed, we don't attempt - // to do any optimization where we compare against the prior set. + // In order to keep the property required by `n_session_ending` that we must return the + // new validator set even if it's the same as the old, as long as any underlying + // economic conditions have changed, we don't attempt to do any optimization where we + // compare against the prior set. Some(ElectionResult::> { elected_stashes, slot_stake, @@ -2186,12 +2179,10 @@ impl Module { compute, }) } else { - // There were not enough candidates for even our minimal level of functionality. - // This is bad. - // We should probably disable all functionality except for block production - // and let the chain keep producing blocks until we can decide on a sufficiently - // substantial set. - // TODO: #2494 + // There were not enough candidates for even our minimal level of functionality. This is + // bad. We should probably disable all functionality except for block production and let + // the chain keep producing blocks until we can decide on a sufficiently substantial + // set. TODO: #2494 None } } @@ -2199,6 +2190,8 @@ impl Module { /// Execute phragmen and return the new results. No post-processing is applied and the raw edge /// weights are returned. /// + /// Self votes are added and nominations before the most recent slashing span are reaped. + /// /// No storage item is updated. fn do_phragmen() -> Option> { let mut all_nominators: Vec<(T::AccountId, Vec)> = Vec::new(); @@ -2549,7 +2542,7 @@ impl SignedExtension for LockStakingStatus { type DispatchInfo = frame_support::weights::DispatchInfo; type Pre = (); - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) } + fn additional_signed(&self) -> Result<(), TransactionValidityError> { Ok(()) } fn validate( &self, @@ -2558,19 +2551,20 @@ impl SignedExtension for LockStakingStatus { _info: Self::DispatchInfo, _len: usize, ) -> TransactionValidity { - // TODO: maybe do this via manual ensure!()? I don't see why it would be _better_ though. if let Some(inner_call) = call.is_sub_type() { - match inner_call { - Call::::bond(..) | - Call::::validate(..) | - Call::::nominate(..) | - Call::::chill(..) | - Call::::unbond(..) | - Call::::rebond(..) | - Call::::bond_extra(..) => { - return Err(InvalidTransaction::Stale.into()); + if let ElectionStatus::Open(_) = >::era_election_status() { + match inner_call { + Call::::bond(..) | + Call::::unbond(..) | + Call::::bond_extra(..) | + Call::::rebond(..) | + Call::::validate(..) | + Call::::nominate(..) | + Call::::chill(..) => { + return Err(InvalidTransaction::Stale.into()); + } + _ => { /* no problem */ } } - _ => { /* no problem */ } } } @@ -2591,6 +2585,16 @@ impl frame_support::unsigned::ValidateUnsigned for Module { ) = call { use offchain_election::SignaturePayloadOf; + // discard early solution + if Self::era_election_status() == ElectionStatus::Close { + debug::native::debug!( + target: "staking", + "rejecting unsigned transaction because it is too early." + ); + return InvalidTransaction::Future.into(); + } + + // discard weak solution if let Some(queued_score) = Self::queued_score() { if !is_score_better(queued_score, *score) { debug::native::debug!( diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 1fe5109d14a7d..27f8bfc300fc6 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -40,7 +40,7 @@ use sp_staking::{ offence::{OffenceDetails, OnOffenceHandler}, SessionIndex, }; -use std::{cell::RefCell, collections::HashSet}; +use std::{cell::RefCell, collections::HashSet, ops}; /// The AccountId alias in this test module. pub(crate) type AccountId = u64; @@ -326,7 +326,7 @@ pub(crate) mod dummy_sr25519 { mod app_sr25519 { use sp_application_crypto::{app_crypto, KeyTypeId, sr25519}; - app_crypto!(sr25519, KeyTypeId(*b"ebab")); + app_crypto!(sr25519, KeyTypeId(*b"vali")); } pub type AuthoritySignature = app_sr25519::Signature; @@ -637,7 +637,7 @@ pub fn bond_validator(stash: u64, ctrl: u64, val: u64) { Origin::signed(stash), ctrl, val, - RewardDestination::Controller + RewardDestination::Controller, )); assert_ok!(Staking::validate( Origin::signed(ctrl), @@ -651,7 +651,7 @@ pub fn bond_nominator(stash: u64, ctrl: u64, val: u64, target: Vec) { Origin::signed(stash), ctrl, val, - RewardDestination::Controller + RewardDestination::Controller, )); assert_ok!(Staking::nominate(Origin::signed(ctrl), target)); } @@ -746,7 +746,7 @@ pub fn on_offence_now( } /// convert a vector of staked assignments into ratio -pub fn assignment_to_staked>( +pub fn assignment_to_staked>( assignments: Vec> ) -> Vec> { assignments diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index e2376d23f0c64..7d2f2f4775970 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -57,11 +57,17 @@ pub(crate) type SignaturePayloadOf<'a, T> = ( pub(crate) const OFFCHAIN_HEAD_DB: &[u8] = b"parity/staking-election/"; const OFFCHAIN_REPEAT: u32 = 5; -pub(crate) fn set_check_offchain_execution_status(now: T::BlockNumber) -> Result<(), &'static str> { +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>| { + 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."), @@ -160,7 +166,8 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti // convert winners to index as well let winners = winners.into_iter().map(|w| - validator_index(&w).expect("winners are chosen from the snapshot list; the must have index; qed") + validator_index(&w) + .expect("winners are chosen from the snapshot list; the must have index; qed") ).collect::>(); let signature_payload: SignaturePayloadOf = diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 9aad458987102..93624af9d325b 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2689,7 +2689,7 @@ mod offchain_phragmen { use mock::*; use frame_support::{assert_ok, assert_noop, debug}; use substrate_test_utils::assert_eq_uvec; - use sp_runtime::{traits::OffchainWorker, Perbill}; + use sp_runtime::traits::OffchainWorker; use sp_core::offchain::{ OffchainExt, TransactionPoolExt, @@ -2782,24 +2782,24 @@ mod offchain_phragmen { || { run_to_block(10); assert_session_era!(1, 0); - assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert_eq!(Staking::era_election_status(), ElectionStatus::Close); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); run_to_block(18); assert_session_era!(1, 0); - assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert_eq!(Staking::era_election_status(), ElectionStatus::Close); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); run_to_block(40); assert_session_era!(4, 0); - assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert_eq!(Staking::era_election_status(), ElectionStatus::Close); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); run_to_block(46); - assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert_eq!(Staking::era_election_status(), ElectionStatus::Close); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); @@ -2814,7 +2814,7 @@ mod offchain_phragmen { assert!(Staking::snapshot_validators().is_some()); run_to_block(50); - assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert_eq!(Staking::era_election_status(), ElectionStatus::Close); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); }) @@ -2825,7 +2825,7 @@ mod offchain_phragmen { ExtBuilder::default().build().execute_with(|| { start_session(1); start_session(2); - assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert_eq!(Staking::era_election_status(), ElectionStatus::Close); // some election must have happened by now. assert_eq!( System::events()[4].event, @@ -2834,6 +2834,16 @@ mod offchain_phragmen { }) } + #[test] + fn offchain_wont_work_if_snapshot_fails() { + unimplemented!(); + } + + #[test] + fn staking_is_locked_when_election_window_open() { + unimplemented!(); + } + #[test] fn offchain_result_can_be_submitted() { // should check that we have a new validator set normally, @@ -2859,7 +2869,7 @@ mod offchain_phragmen { assert_eq!(queued_result.compute, ElectionCompute::Signed); run_to_block(15); - assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert_eq!(Staking::era_election_status(), ElectionStatus::Close); assert_eq!( System::events()[3].event, @@ -2893,7 +2903,7 @@ mod offchain_phragmen { assert_eq!(queued_result.compute, ElectionCompute::Signed); run_to_block(15); - assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert_eq!(Staking::era_election_status(), ElectionStatus::Close); assert_eq!( System::events()[3].event, @@ -2913,7 +2923,7 @@ mod offchain_phragmen { || { run_to_block(11); // submission is not yet allowed - assert_eq!(Staking::era_election_status(), ElectionStatus::None); + assert_eq!(Staking::era_election_status(), ElectionStatus::Close); // create all the indices just to build the solution. Staking::create_stakers_snapshot(); diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index 5f1c780732dbf..9c35387bddb48 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -17,7 +17,7 @@ #[cfg(feature = "std")] use serde::{Serialize, Deserialize}; -use sp_std::{ops, prelude::*, convert::TryInto}; +use sp_std::{ops, fmt, prelude::*, convert::TryInto}; use codec::{Encode, Decode, CompactAs}; use crate::{ traits::{SaturatedConversion, UniqueSaturatedInto, Saturating, BaseArithmetic}, @@ -26,7 +26,7 @@ use sp_debug_derive::RuntimeDebug; /// Something that implements a fixed point ration with an arbitrary granularity `X`, as _parts per /// `X`_. -pub trait PerThing: Sized + Saturating + Copy + Default + sp_std::fmt::Debug { +pub trait PerThing: Sized + Saturating + Copy + Default + fmt::Debug { /// The data type used to build this per-thingy. type Inner: BaseArithmetic + Copy; diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index ccc1e61be1115..9a51ddcb33d0a 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -30,8 +30,6 @@ mod staked; // prefix used for struct fields in compact. const PREFIX: &'static str = "votes"; -// TODO: what to do if additions overflow in into staked. - /// Generates a struct to store the phragmen assignments in a compact way. The struct can only store /// distributions up to the given input count. The given count must be greater than 2. /// diff --git a/primitives/phragmen/fuzzer/Cargo.lock b/primitives/phragmen/fuzzer/Cargo.lock index 591320a0033df..921dc910cb966 100644 --- a/primitives/phragmen/fuzzer/Cargo.lock +++ b/primitives/phragmen/fuzzer/Cargo.lock @@ -445,7 +445,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libsecp256k1" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -482,16 +482,6 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "malloc_size_of_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "maybe-uninit" version = "2.0.0" @@ -513,13 +503,13 @@ dependencies = [ [[package]] name = "memory-db" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -619,14 +609,27 @@ dependencies = [ [[package]] name = "parity-util-mem" -version = "0.3.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-util-mem-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parity-wasm" version = "0.41.0" @@ -643,12 +646,11 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -665,15 +667,14 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1053,11 +1054,12 @@ dependencies = [ "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1102,7 +1104,7 @@ version = "2.0.0" dependencies = [ "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0", "sp-std 2.0.0", ] @@ -1112,7 +1114,7 @@ name = "sp-io" version = "2.0.0" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0", @@ -1138,6 +1140,7 @@ version = "2.0.0" dependencies = [ "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", "sp-phragmen-compact 2.0.0", "sp-runtime 2.0.0", "sp-std 2.0.0", @@ -1169,6 +1172,7 @@ dependencies = [ "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1212,14 +1216,14 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0", "sp-externalities 0.8.0", "sp-panic-handler 2.0.0", "sp-trie 2.0.0", - "trie-db 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1241,12 +1245,12 @@ name = "sp-trie" version = "2.0.0" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memory-db 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0", "sp-std 2.0.0", - "trie-db 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1346,20 +1350,19 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.19.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trie-root" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1522,15 +1525,14 @@ dependencies = [ "checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" -"checksum libsecp256k1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "df6edf84fd62aad1c93932b39324eaeda3912c1d26bc18dfaee6293848e49a50" +"checksum libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e37c5d4cd9473c5f4c9c111f033f15d4df9bd378fdf615944e360a4f55a05f0b" "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -"checksum memory-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "881736a0f68a6fae1b596bb066c5bd16d7b3ed645a4dd8ffaefd02f585abaf71" +"checksum memory-db 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "198831fe8722331a395bc199a5d08efbc197497ef354cb4c77b969c02ffc0fc4" "checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" "checksum merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0942b357c1b4d0dc43ba724674ec89c3218e6ca2b3e8269e7cb53bcecd2f6e" "checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" @@ -1542,12 +1544,13 @@ dependencies = [ "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507" "checksum parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34e513ff3e406f3ede6796dcdc83d0b32ffb86668cea1ccf7363118abeb00476" -"checksum parity-util-mem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8174d85e62c4d615fddd1ef67966bdc5757528891d0742f15b131ad04667b3f9" +"checksum parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ef1476e40bf8f5c6776e9600983435821ca86eb9819d74a6207cca69d091406a" +"checksum parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" "checksum parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" +"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" -"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" -"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" "checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" "checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" "checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" @@ -1598,8 +1601,8 @@ dependencies = [ "checksum tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c5676413eaeb1ea35300a0224416f57abc3bd251657e0fafc12c47ff98c060" "checksum tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" "checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" -"checksum trie-db 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d747ae5b6f078df7e46477fcc7df66df9eb4f27a031cf4a7c890a8dd03d8e6" -"checksum trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0b779f7c1c8fe9276365d9d5be5c4b5adeacf545117bb3f64c974305789c5c0b" +"checksum trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de9222c50cc325855621271157c973da27a0dcd26fa06f8edf81020bd2333df0" +"checksum trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "652931506d2c1244d7217a70b99f56718a7b4161b37f04e7cd868072a99f68cd" "checksum twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" diff --git a/primitives/phragmen/fuzzer/Cargo.toml b/primitives/phragmen/fuzzer/Cargo.toml index 8ef3518154f4a..b0bb5671d36a5 100644 --- a/primitives/phragmen/fuzzer/Cargo.toml +++ b/primitives/phragmen/fuzzer/Cargo.toml @@ -6,11 +6,11 @@ edition = "2018" [dependencies] sp-phragmen = { version = "2.0.0", path = ".." } -honggfuzz = { path = "../../../../honggfuzz-rs" } +honggfuzz = "0.5" rand = "0.7.3" [workspace] [[bin]] name = "reduce" -path = "src/reduce.rs" \ No newline at end of file +path = "src/reduce.rs" diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index b377de36222a7..1ef11954ce854 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -534,7 +534,7 @@ pub fn evaluate_support( ) -> PhragmenScore { let mut min_support = ExtendedBalance::max_value(); let mut sum: ExtendedBalance = Zero::zero(); - // TODO: this will probably saturate but using big num makes it even slower. We'll have to see. + // NOTE: this will probably saturate but using big num makes it even slower. We'll have to see. // This must run on chain.. let mut sum_squared: ExtendedBalance = Zero::zero(); for (_, support) in support.iter() { diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index 3a0e5dab7eb89..a41bb1621d4c2 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -17,8 +17,7 @@ //! (very) Basic implementation of a graph node used in the reduce algorithm. use sp_runtime::RuntimeDebug; -use sp_std::{prelude::*, cell::RefCell}; -use sp_std::rc::Rc; +use sp_std::{prelude::*, cell::RefCell, rc::Rc, fmt};; /// The role that a node can accept. #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, RuntimeDebug)] @@ -58,8 +57,8 @@ impl PartialEq for NodeId { impl Eq for NodeId {} #[cfg(feature = "std")] -impl sp_std::fmt::Debug for NodeId { - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { +impl sp_std::fmt::Debug for NodeId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> sp_std::fmt::Result { write!(f, "Node({:?}, {:?})", self.who, if self.role == NodeRole::Voter { "V" } else { "T" }) } } @@ -82,13 +81,13 @@ impl PartialEq for Node { impl Eq for Node {} #[cfg(feature = "std")] -impl sp_std::fmt::Debug for Node { - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { +impl fmt::Debug for Node { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "({:?} --> {:?})", self.id, self.parent.as_ref().map(|p| p.borrow().id.clone())) } } -impl Node { +impl Node { /// Create a new [`Node`] pub fn new(id: NodeId) -> Node { Self { id, parent: None } diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index e14602f44bb48..f9518ef94ee5c 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -26,8 +26,6 @@ use crate::{ use substrate_test_utils::assert_eq_uvec; use sp_runtime::{Perbill, traits::Convert}; -type Output = Perbill; - #[test] fn float_phragmen_poc_works() { let candidates = vec![1, 2, 3]; @@ -83,7 +81,7 @@ fn phragmen_poc_works() { (30, vec![2, 3]), ]; - let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>( + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Perbill>( 2, 2, candidates, @@ -133,6 +131,11 @@ fn phragmen_poc_2_works() { run_and_compare(candidates, voters, stake_of, 2, 2); } +#[test] +fn phragmen_works_with_any_per_thing() { + unimplemented!(); +} + #[test] fn phragmen_poc_3_works() { let candidates = vec![10, 20, 30]; @@ -164,7 +167,7 @@ fn phragmen_accuracy_on_large_scale_only_validators() { (5, (u64::max_value() - 2).into()), ]); - let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>( + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Perbill>( 2, 2, candidates.clone(), @@ -195,7 +198,7 @@ fn phragmen_accuracy_on_large_scale_validators_and_nominators() { (14, u64::max_value().into()), ]); - let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>( + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Perbill>( 2, 2, candidates, @@ -239,7 +242,7 @@ fn phragmen_accuracy_on_small_scale_self_vote() { (30, 1), ]); - let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote, Output>( + let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote, Perbill>( 3, 3, candidates, @@ -270,7 +273,7 @@ fn phragmen_accuracy_on_small_scale_no_self_vote() { (3, 1), ]); - let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote, Output>( + let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote, Perbill>( 3, 3, candidates, @@ -304,7 +307,7 @@ fn phragmen_large_scale_test() { (50, 990000000000000000), ]); - let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>( + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Perbill>( 2, 2, candidates, @@ -331,7 +334,7 @@ fn phragmen_large_scale_test_2() { (50, nom_budget.into()), ]); - let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>( + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Perbill>( 2, 2, candidates, @@ -408,7 +411,7 @@ fn elect_has_no_entry_barrier() { (2, 10), ]); - let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote, Output>( + let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote, Perbill>( 3, 3, candidates, @@ -436,7 +439,7 @@ fn minimum_to_elect_is_respected() { (2, 10), ]); - let maybe_result = elect::<_, _, _, TestCurrencyToVote, Output>( + let maybe_result = elect::<_, _, _, TestCurrencyToVote, Perbill>( 10, 10, candidates, @@ -463,7 +466,7 @@ fn self_votes_should_be_kept() { (1, 8), ]); - let result = elect::<_, _, _, TestCurrencyToVote, Output>( + let result = elect::<_, _, _, TestCurrencyToVote, Perbill>( 2, 2, candidates, From 54a5a35b87c76a5f4b165d1dd79ece9944308923 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 14 Feb 2020 10:16:26 +0100 Subject: [PATCH 046/106] Fix staking tests again --- frame/staking/src/lib.rs | 3 +- frame/staking/src/mock.rs | 8 ++- frame/staking/src/tests.rs | 90 +++++++++++++++++++++++++-------- primitives/phragmen/src/node.rs | 2 +- 4 files changed, 78 insertions(+), 25 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 9ab7d77e8d538..b53c74faf5c29 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -250,7 +250,6 @@ mod mock; #[cfg(test)] mod tests; -mod slashing; pub mod slashing; pub mod offchain_election; @@ -1207,7 +1206,7 @@ decl_module! { fn bond(origin, controller: ::Source, #[compact] value: BalanceOf, - payee: RewardDestination + payee: RewardDestination, ) { let stash = ensure_signed(origin)?; diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index ee3db534faa9f..f4f392e7a31c3 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -161,6 +161,7 @@ impl_outer_dispatch! { } mod staking { + // Re-export needed for `impl_outer_event!`. pub use super::super::*; } use frame_system as system; @@ -169,7 +170,10 @@ use pallet_session as session; impl_outer_event! { pub enum MetaEvent for Test { - staking, balances, session, + system, + balances, + session, + staking, } } @@ -228,7 +232,7 @@ impl frame_system::Trait for Test { } impl pallet_balances::Trait for Test { type Balance = Balance; - type Event = (); + type Event = MetaEvent; type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 522636522c129..c2b6a6e54ea8b 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -18,15 +18,10 @@ use super::*; use mock::*; -<<<<<<< HEAD -use codec::Encode; use sp_runtime::{ assert_eq_error_rate, traits::{OnInitialize, BadOrigin}, }; -======= -use sp_runtime::{assert_eq_error_rate, traits::{OnInitialize, BadOrigin}}; ->>>>>>> 29454c30501ccf3a09fe82b965dd50e1a9e5aa24 use sp_staking::offence::OffenceDetails; use frame_support::{ assert_ok, assert_noop, @@ -2681,13 +2676,6 @@ fn remove_multi_deferred() { }) } -#[test] -fn version_initialized() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(::StorageVersion::get(), crate::migration::CURRENT_VERSION); - }); -} - mod offchain_phragmen { use crate::*; use mock::*; @@ -2832,20 +2820,70 @@ mod offchain_phragmen { assert_eq!(Staking::era_election_status(), ElectionStatus::Close); // some election must have happened by now. assert_eq!( - System::events()[4].event, - MetaEvent::staking(RawEvent::StakingElection(ElectionCompute::OnChain)), + System::events().into_iter().map(|r| r.event).filter_map(|e| { + if let MetaEvent::staking(inner) = e { + Some(inner) + } else { + None + } + }).last().unwrap(), + RawEvent::StakingElection(ElectionCompute::OnChain), ); }) } #[test] + #[ignore] // This takes a few mins fn offchain_wont_work_if_snapshot_fails() { - unimplemented!(); + ExtBuilder::default() + .offchain_phragmen_ext() + .election_lookahead(3) + .build() + .execute_with( + || { + run_to_block(12); + assert!(Staking::snapshot_validators().is_some()); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); + + // nominate more than the limit + let limit: NominatorIndex = ValidatorIndex::max_value() as NominatorIndex + 1; + let ctrl = 1_000_000; + for i in 0..limit { + bond_validator((1000 + i).into(), (1000 + i + ctrl).into(), 100); + } + + run_to_block(27); + assert!(Staking::snapshot_validators().is_none()); + assert_eq!(Staking::era_election_status(), ElectionStatus::Close); + }) } #[test] fn staking_is_locked_when_election_window_open() { - unimplemented!(); + ExtBuilder::default() + .offchain_phragmen_ext() + .election_lookahead(3) + .build() + .execute_with( + || { + run_to_block(12); + assert!(Staking::snapshot_validators().is_some()); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); + + let call = crate::Call::bond(999, 998, Default::default()); + let outer: mock::Call = call.into(); + + let lock_staking: LockStakingStatus = Default::default(); + assert_eq!( + lock_staking.validate( + &10, + &outer, + Default::default(), + Default::default(), + ), + TransactionValidity::Err(InvalidTransaction::Stale.into()), + ) + }) } #[test] @@ -2876,8 +2914,14 @@ mod offchain_phragmen { assert_eq!(Staking::era_election_status(), ElectionStatus::Close); assert_eq!( - System::events()[3].event, - MetaEvent::staking(RawEvent::StakingElection(ElectionCompute::Signed)), + System::events().into_iter().map(|r| r.event).filter_map(|e| { + if let MetaEvent::staking(inner) = e { + Some(inner) + } else { + None + } + }).last().unwrap(), + RawEvent::StakingElection(ElectionCompute::Signed), ); }) } @@ -2910,8 +2954,14 @@ mod offchain_phragmen { assert_eq!(Staking::era_election_status(), ElectionStatus::Close); assert_eq!( - System::events()[3].event, - MetaEvent::staking(RawEvent::StakingElection(ElectionCompute::Signed)), + System::events().into_iter().map(|r| r.event).filter_map(|e| { + if let MetaEvent::staking(inner) = e { + Some(inner) + } else { + None + } + }).last().unwrap(), + RawEvent::StakingElection(ElectionCompute::Signed), ); }) } diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index a41bb1621d4c2..e4061ec750872 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -17,7 +17,7 @@ //! (very) Basic implementation of a graph node used in the reduce algorithm. use sp_runtime::RuntimeDebug; -use sp_std::{prelude::*, cell::RefCell, rc::Rc, fmt};; +use sp_std::{prelude::*, cell::RefCell, rc::Rc, fmt}; /// The role that a node can accept. #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, RuntimeDebug)] From d746fd42a0dcf0cf6049e8129f491d6cf009d1ae Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 18 Feb 2020 16:10:03 +0100 Subject: [PATCH 047/106] fix per-things; bring patches from benchmarking --- frame/elections-phragmen/src/lib.rs | 20 +- frame/staking/src/lib.rs | 8 +- frame/staking/src/mock.rs | 43 +-- frame/staking/src/offchain_election.rs | 155 +++++--- primitives/arithmetic/src/lib.rs | 2 +- primitives/arithmetic/src/per_things.rs | 357 +++++++++++++----- primitives/phragmen/compact/src/assignment.rs | 1 + primitives/phragmen/compact/src/lib.rs | 3 +- primitives/phragmen/compact/src/staked.rs | 1 + primitives/phragmen/src/helpers.rs | 101 +++++ primitives/phragmen/src/lib.rs | 35 +- primitives/phragmen/src/mock.rs | 14 +- primitives/phragmen/src/tests.rs | 98 ++++- primitives/runtime/src/lib.rs | 2 +- 14 files changed, 608 insertions(+), 232 deletions(-) create mode 100644 primitives/phragmen/src/helpers.rs diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 0784008595783..68167808b73ae 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -94,7 +94,7 @@ use frame_support::{ ChangeMembers, OnUnbalanced, WithdrawReason, Contains, BalanceStatus } }; -use sp_phragmen::{build_support_map, ExtendedBalance, StakedAssignment}; +use sp_phragmen::{build_support_map, ExtendedBalance}; use frame_system::{self as system, ensure_signed, ensure_root}; const MODULE_ID: LockIdentifier = *b"phrelect"; @@ -667,15 +667,15 @@ impl Module { .filter_map(|(m, a)| if a.is_zero() { None } else { Some(m) } ) .collect::>(); - let staked_assignments: Vec> = phragmen_result.assignments - .into_iter() - .map(|a| { - let stake = , u64>>::convert( - Self::locked_stake_of(&a.who) - ) as ExtendedBalance; - a.into_staked(stake, true) - }) - .collect(); + let stake_of = |who: &T::AccountId| -> ExtendedBalance { + , u64>>::convert( + Self::locked_stake_of(who) + ) as ExtendedBalance + }; + let staked_assignments = sp_phragmen::assignment_ratio_to_budget( + phragmen_result.assignments, + stake_of, + ); let (support_map, _) = build_support_map::(&new_set, &staked_assignments); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index b53c74faf5c29..e5552f381b785 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -255,7 +255,7 @@ pub mod slashing; pub mod offchain_election; pub mod inflation; -use sp_std::{prelude::*, result, convert::{TryInto, From}, ops}; +use sp_std::{prelude::*, result, convert::{TryInto, From}}; use codec::{HasCompact, Encode, Decode}; use frame_support::{ decl_module, decl_event, decl_storage, ensure, decl_error, debug, Parameter, @@ -2101,10 +2101,12 @@ impl Module { /// /// No storage item is updated. fn do_phragmen_with_post_processing< - Accuracy: PerThing + ops::Mul, + Accuracy: PerThing, >( compute: ElectionCompute, - ) -> Option>> { + ) -> Option>> + where ExtendedBalance: From<::Inner> + { if let Some(phragmen_result) = Self::do_phragmen::() { let elected_stashes = phragmen_result.winners.iter() .map(|(s, _)| s.clone()) diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index f4f392e7a31c3..47aa355fcc960 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -40,7 +40,7 @@ use sp_staking::{ offence::{OffenceDetails, OnOffenceHandler}, SessionIndex, }; -use std::{cell::RefCell, collections::HashSet, ops}; +use std::{cell::RefCell, collections::HashSet}; /// The AccountId alias in this test module. pub(crate) type AccountId = u64; @@ -746,31 +746,6 @@ pub fn on_offence_now( on_offence_in_era(offenders, slash_fraction, now) } -/// convert a vector of staked assignments into ratio -pub fn assignment_to_staked>( - assignments: Vec> -) -> Vec> { - assignments - .into_iter() - .map(|a| { - let stake = >::convert( - Staking::slashable_balance_of(&a.who) - ) as ExtendedBalance; - a.into_staked(stake, true) - }) - .collect() -} - -/// convert a vector of assignments into staked -pub fn staked_to_assignment( - staked: Vec>, -) -> Vec> { - staked - .into_iter() - .map(|sa| sa.into_assignment(true)) - .collect() -} - // winners will be chosen by simply their unweighted total backing stake. Nominator stake is // distributed evenly. pub fn horrible_phragmen_with_post_processing( @@ -872,7 +847,10 @@ pub fn horrible_phragmen_with_post_processing( }; // convert back to ratio assignment. This takes less space. - let assignments_reduced = staked_to_assignment::(staked_assignment); + let assignments_reduced = sp_phragmen::assignment_staked_to_ratio::< + AccountId, + OffchainAccuracy, + >(staked_assignment); let compact = >::from_assignment( assignments_reduced, @@ -899,9 +877,14 @@ pub fn do_phragmen_with_post_processing( winners, assignments, } = Staking::do_phragmen::().unwrap(); - let winners = winners.into_iter().map(|(w, _)| w).collect(); + let winners = winners.into_iter().map(|(w, _)| w).collect::>(); - let mut staked = assignment_to_staked::(assignments); + let stake_of = |who: &AccountId| -> ExtendedBalance { + >::convert( + Staking::slashable_balance_of(&who) + ) as ExtendedBalance + }; + let mut staked = sp_phragmen::assignment_ratio_to_budget(assignments, stake_of); // apply custom tweaks. awesome for testing. tweak(&mut staked); @@ -911,7 +894,7 @@ pub fn do_phragmen_with_post_processing( } // compute score - let (support_map, _) = build_support_map::(&winners, &staked); + let (support_map, _) = build_support_map::(winners.as_ref(), &staked); let score = evaluate_support::(&support_map); // convert back to ratio assignment. This takes less space. diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 7d2f2f4775970..6281e56dd64b4 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -24,11 +24,13 @@ use frame_system::offchain::{SubmitUnsignedTransaction}; use frame_support::debug; use sp_phragmen::{ reduce, ExtendedBalance, PhragmenResult, StakedAssignment, Assignment, PhragmenScore, + build_support_map, evaluate_support, }; use sp_std::{prelude::*, convert::TryInto}; use sp_runtime::{RuntimeAppPublic, RuntimeDebug}; use sp_runtime::offchain::storage::StorageValueRef; use sp_runtime::traits::Convert; +use sp_runtime::PerThing; #[derive(RuntimeDebug)] pub(crate) enum OffchainElectionError { @@ -111,69 +113,30 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti .ok_or(OffchainElectionError::SnapshotUnavailable)?; let snapshot_nominators = >::snapshot_nominators() .ok_or(OffchainElectionError::SnapshotUnavailable)?; - // k is a local key who is also among the validators. + + // compute raw solution. let PhragmenResult { winners, assignments, } = >::do_phragmen::() .ok_or(OffchainElectionError::ElectionFailed)?; - // convert winners into just account ids. - let winners: Vec = winners.into_iter().map(|(w, _)| w).collect(); - - // convert into staked. This is needed to be able to reduce. - let mut staked: Vec> = assignments - .into_iter() - .map(|a| { - let stake = , u64>>::convert( - >::slashable_balance_of(&a.who) - ) as ExtendedBalance; - a.into_staked(stake, true) - }) - .collect(); - - // reduce the assignments. This will remove some additional edges. - reduce(&mut staked); - - // get support and score. - let (support, _) = sp_phragmen::build_support_map::(&winners, &staked); - let score = sp_phragmen::evaluate_support(&support); - - // helpers for building the compact - let nominator_index = |a: &T::AccountId| -> Option { - snapshot_nominators.iter().position(|x| x == a).and_then(|i| - >::try_into(i).ok() - ) - }; - let validator_index = |a: &T::AccountId| -> Option { - snapshot_validators.iter().position(|x| x == a).and_then(|i| - >::try_into(i).ok() - ) - }; - - // convert back to ratio assignment. This takes less space. - let assignments_reduced: Vec> = staked - .into_iter() - .map(|sa| sa.into_assignment(true)) - .collect(); - - // compact encode the assignment. - let compact = >::from_assignment( - assignments_reduced, - nominator_index, - validator_index, - ).map_err(|_| OffchainElectionError::CompactFailed)?; - - // convert winners to index as well - let winners = winners.into_iter().map(|w| - validator_index(&w) - .expect("winners are chosen from the snapshot list; the must have index; qed") - ).collect::>(); + // process and prepare it for submission. + let (winners, compact, score) = prepare_submission::( + snapshot_nominators, + snapshot_validators, + assignments, + winners, + true, + ); + // sign it. let signature_payload: SignaturePayloadOf = (&winners, &compact, &score, &(index as u32)); let signature = pubkey.sign(&signature_payload.encode()) .ok_or(OffchainElectionError::SigningFailed)?; + + // send it. let call: ::Call = Call::submit_election_solution_unsigned( winners, compact, @@ -195,3 +158,91 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti // no key left and no submission. Err(OffchainElectionError::NoSigningKey) } + +/// Takes a phragmen result and the snapshot data and spits out some data that can be submitted to +/// the chain. +/// +/// This does a lot of stuff; read the inline comments. +pub(crate) fn prepare_submission( + snapshot_nominators: Vec, + snapshot_validators: Vec, + assignments: Vec>, + winners: Vec<(T::AccountId, ExtendedBalance)>, + do_reduce: bool, +) -> (Vec, CompactOf, PhragmenScore) +where + ExtendedBalance: From<::Inner>, +{ + // all helper closures + let nominator_index = |a: &T::AccountId| -> Option { + snapshot_nominators.iter().position(|x| x == a).and_then(|i| + >::try_into(i).ok() + ) + }; + let validator_index = |a: &T::AccountId| -> Option { + snapshot_validators.iter().position(|x| x == a).and_then(|i| + >::try_into(i).ok() + ) + }; + let stake_of = |who: &T::AccountId| -> ExtendedBalance { + , u64>>::convert( + >::slashable_balance_of(who) + ) as ExtendedBalance + }; + + // Clean winners. + let winners = winners.into_iter().map(|(w, _)| w).collect::>(); + + // convert into absolute value and to obtain the reduced version. + let mut staked: Vec> = assignments + .into_iter() + .map(|a| { + let stake = stake_of(&a.who); + a.into_staked(stake, true) + }) + .collect(); + + if do_reduce { + reduce(&mut staked); + } + + // Convert back to ratio assignment. This takes less space. + let low_accuracy_assignment: Vec> = staked + .into_iter() + .map(|sa| sa.into_assignment(true)) + .collect(); + + // convert back to staked to compute the score in the receiver's accuracy. This can be done + // nicer, for now we do it as such since this code is not time-critical. This ensure that the + // score _predicted_ here is the same as the one computed on chain and you will not get a + // `PhragmenBogusScore` error. This is totally NOT needed if we don't do reduce. This whole + // accuracy glitch happens because reduce breaks that assumption of scale. + let score = { + let staked: Vec> = low_accuracy_assignment + .iter() + .map(|a| { + let stake = stake_of(&a.who); + a.clone().into_staked(stake, true) + }).collect(); + + let (support_map, _) = build_support_map::( + winners.as_slice(), + staked.as_slice(), + ); + evaluate_support::(&support_map) + }; + + // compact encode the assignment. + let compact = >::from_assignment( + low_accuracy_assignment, + nominator_index, + validator_index, + ).unwrap(); + + // winners to index. + let winners = winners.into_iter().map(|w| + snapshot_validators.iter().position(|v| *v == w).unwrap().try_into().unwrap() + ).collect::>(); + + (winners, compact, score) +} diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs index c2feae00b74ff..a1cc3fa8d7d9d 100644 --- a/primitives/arithmetic/src/lib.rs +++ b/primitives/arithmetic/src/lib.rs @@ -40,5 +40,5 @@ mod fixed64; mod rational128; pub use fixed64::Fixed64; -pub use per_things::{PerThing, Percent, Permill, Perbill, Perquintill}; +pub use per_things::{PerThing, Percent, PerU16, Permill, Perbill, Perquintill}; pub use rational128::Rational128; diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index 9c35387bddb48..9912cafec7a03 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -27,8 +27,12 @@ use sp_debug_derive::RuntimeDebug; /// Something that implements a fixed point ration with an arbitrary granularity `X`, as _parts per /// `X`_. pub trait PerThing: Sized + Saturating + Copy + Default + fmt::Debug { - /// The data type used to build this per-thingy. - type Inner: BaseArithmetic + Copy; +/// The data type used to build this per-thingy. + type Inner: BaseArithmetic + Copy + fmt::Debug; + + /// The data type that is used to store values bigger than the maximum of this type. This must + /// at least be able to store twice the size of `Self::ACCURACY`. + type Upper: BaseArithmetic + Copy + fmt::Debug; /// accuracy of this type const ACCURACY: Self::Inner; @@ -63,8 +67,42 @@ pub trait PerThing: Sized + Saturating + Copy + Default + fmt::Debug { /// The computation of this approximation is performed in the generic type `N`. Given /// `M` as the data type that can hold the maximum value of this per-thing (e.g. u32 for /// perbill), this can only work if `N == M` or `N: From + TryInto`. + /// + /// Note that this always rounds _down_, i.e. + /// + /// ```rust + /// # use sp_arithmetic::{Percent, PerThing}; + /// # fn main () { + /// // 989/100 is technically closer to 99%. + /// assert_eq!( + /// Percent::from_rational_approximation(989, 1000), + /// Percent::from_parts(98), + /// ); + /// # } + /// ``` fn from_rational_approximation(p: N, q: N) -> Self - where N: Clone + Ord + From + TryInto + ops::Div; + where N: + Clone + Ord + From + TryInto + TryInto + + ops::Div; + + /// A mul implementation that always rounds down, whilst the standard `Mul` implementation + /// rounds to the nearest numbers + /// + /// ```rust + /// # use sp_arithmetic::{Percent, PerThing}; + /// # fn main () { + /// // rounds to closest + /// assert_eq!(Percent::from_percent(34) * 10u64, 3); + /// assert_eq!(Percent::from_percent(36) * 10u64, 4); + /// + /// // collapse down + /// assert_eq!(Percent::from_percent(34).mul_collapse(10u64), 3); + /// assert_eq!(Percent::from_percent(36).mul_collapse(10u64), 3); + /// # } + /// ``` + fn mul_collapse(self, b: N) -> N + where N: Clone + From + UniqueSaturatedInto + ops::Rem + + ops::Div + ops::Mul + ops::Add; } macro_rules! implement_per_thing { @@ -78,33 +116,30 @@ macro_rules! implement_per_thing { impl PerThing for $name { type Inner = $type; + type Upper = $upper_type; - /// The accuracy of this type. const ACCURACY: Self::Inner = $max; - /// Nothing. fn zero() -> Self { Self(0) } - /// `true` if this is nothing. fn is_zero(&self) -> bool { self.0 == 0 } - /// Everything. fn one() -> Self { Self($max) } - /// Consume self and deconstruct into a raw numeric type. fn deconstruct(self) -> Self::Inner { self.0 } - /// From an explicitly defined number of parts per maximum of the type. + // needed only for peru16. Since peru16 is the only type in which $max == + // $type::max_value(), rustc is being a smart-a** here by warning that the comparison + // is not needed. + #[allow(unused_comparisons)] fn from_parts(parts: Self::Inner) -> Self { Self([parts, $max][(parts > $max) as usize]) } - /// Converts a percent into `Self`. Equal to `x / 100`. fn from_percent(x: Self::Inner) -> Self { - Self([x, 100][(x > 100) as usize] * ($max / 100)) + Self::from_rational_approximation([x, 100][(x > 100) as usize] as $upper_type, 100) } - /// Return the product of multiplication of this value by itself. fn square(self) -> Self { // both can be safely casted and multiplied. let p: $upper_type = self.0 as $upper_type * self.0 as $upper_type; @@ -112,35 +147,29 @@ macro_rules! implement_per_thing { Self::from_rational_approximation(p, q) } - /// Converts a fraction into `Self`. #[cfg(feature = "std")] fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as Self::Inner) } - /// Approximate the fraction `p/q` into a per-thing fraction. This will never overflow. - /// - /// The computation of this approximation is performed in the generic type `N`. Given - /// `M` as the data type that can hold the maximum value of this per-thing (e.g. u32 for - /// perbill), this can only work if `N == M` or `N: From + TryInto`. fn from_rational_approximation(p: N, q: N) -> Self - where N: Clone + Ord + From + TryInto + ops::Div + where N: Clone + Ord + From + TryInto + TryInto + ops::Div { // q cannot be zero. - let q = q.max((1 as Self::Inner).into()); + let q: N = q.max((1 as Self::Inner).into()); // p should not be bigger than q. - let p = p.min(q.clone()); + let p: N = p.min(q.clone()); - let factor = (q.clone() / $max.into()).max((1 as Self::Inner).into()); + let factor: N = (q.clone() / $max.into()).max((1 as Self::Inner).into()); // q cannot overflow: (q / (q/$max)) < 2 * $max. p < q hence p also cannot overflow. // this implies that Self::Inner must be able to fit 2 * $max. - let q_reduce: Self::Inner = (q / factor.clone()) + let q_reduce: $upper_type = (q / factor.clone()) .try_into() .map_err(|_| "Failed to convert") .expect( "q / (q/$max) < (2 * $max). Macro prevents any type being created that \ does not satisfy this; qed" ); - let p_reduce: Self::Inner = (p / factor.clone()) + let p_reduce: $upper_type = (p / factor.clone()) .try_into() .map_err(|_| "Failed to convert") .expect( @@ -157,13 +186,49 @@ macro_rules! implement_per_thing { $name(part as Self::Inner) } + + fn mul_collapse(self, b: N) -> N + where + N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem + + ops::Div + ops::Mul + ops::Add + { + let maximum: N = $max.into(); + let upper_max: $upper_type = $max.into(); + let part: N = self.0.into(); + + let rem_multiplied_divided = { + let rem = b.clone().rem(maximum.clone()); + + // `rem_sized` is inferior to $max, thus it fits into $type. This is assured by + // a test. + let rem_sized = rem.saturated_into::<$type>(); + + // `self` and `rem_sized` are inferior to $max, thus the product is less than + // $max^2 and fits into $upper_type. This is assured by a test. + let rem_multiplied_upper = rem_sized as $upper_type * self.0 as $upper_type; + + // `rem_multiplied_upper` is less than $max^2 therefore divided by $max it fits + // in $type. remember that $type always fits $max. + let rem_multiplied_divided_sized = + (rem_multiplied_upper / upper_max) as $type; + + // `rem_multiplied_divided_sized` is inferior to b, thus it can be converted + // back to N type + rem_multiplied_divided_sized.into() + }; + + (b / maximum) * part + rem_multiplied_divided + } } - /// Implement const functions impl $name { /// From an explicitly defined number of parts per maximum of the type. /// /// This can be called at compile time. + // needed only for peru16. Since peru16 is the only type in which $max == + // $type::max_value(), rustc is being a smart-a** here by warning that the comparison + // is not needed. + #[allow(unused_comparisons)] pub const fn from_parts(parts: $type) -> Self { Self([parts, $max][(parts > $max) as usize]) } @@ -202,6 +267,16 @@ macro_rules! implement_per_thing { } } + impl crate::traits::Bounded for $name { + fn min_value() -> Self { + ::zero() + } + + fn max_value() -> Self { + ::one() + } + } + impl ops::Div for $name { type Output = Self; @@ -212,9 +287,10 @@ macro_rules! implement_per_thing { } } - /// Overflow-prune multiplication. + /// Non-overflow multiplication. /// /// tailored to be used with a balance type. + /// impl ops::Mul for $name where N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem @@ -241,6 +317,7 @@ macro_rules! implement_per_thing { // in $type. remember that $type always fits $max. let mut rem_multiplied_divided_sized = (rem_multiplied_upper / upper_max) as $type; + // fix a tiny rounding error if rem_multiplied_upper % upper_max > upper_max / 2 { rem_multiplied_divided_sized += 1; @@ -263,12 +340,15 @@ macro_rules! implement_per_thing { #[test] fn macro_expanded_correctly() { - // needed for the `from_percent` to work. - assert!($max >= 100); - assert!($max % 100 == 0); + // needed for the `from_percent` to work. UPDATE: this is no longer needed; yet note + // that tests that use percentage or fractions such as $name::from_fraction(0.2) to + // create values will most likely be inaccurate when used with per_things that are + // not multiples of 100. + // assert!($max >= 100); + // assert!($max % 100 == 0); // needed for `from_rational_approximation` - assert!(2 * $max < <$type>::max_value()); + assert!(2 * ($max as $upper_type) < <$upper_type>::max_value()); assert!(<$upper_type>::from($max) < <$upper_type>::max_value()); // for something like percent they can be the same. @@ -297,7 +377,7 @@ macro_rules! implement_per_thing { (63, 1), (64, 2), (65, 2), - (<$type>::max_value(), <$type>::max_value().encode().len() + 1) + // (<$type>::max_value(), <$type>::max_value().encode().len() + 1) ]; for &(n, l) in &tests { let compact: codec::Compact<$name> = $name(n).into(); @@ -316,33 +396,73 @@ macro_rules! implement_per_thing { assert_eq!($name::zero(), $name::from_parts(Zero::zero())); assert_eq!($name::one(), $name::from_parts($max)); assert_eq!($name::ACCURACY, $max); - assert_eq!($name::from_percent(0), $name::from_parts(Zero::zero())); - assert_eq!($name::from_percent(10), $name::from_parts($max / 10)); - assert_eq!($name::from_percent(100), $name::from_parts($max)); + assert_eq!($name::from_fraction(0.0), $name::from_parts(Zero::zero())); + assert_eq!($name::from_fraction(0.1), $name::from_parts($max / 10)); + assert_eq!($name::from_fraction(1.0), $name::from_parts($max)); + } + + macro_rules! u256ify { + ($val:expr) => { + Into::::into($val) + }; } macro_rules! per_thing_mul_test { ($num_type:tt) => { // multiplication from all sort of from_percent assert_eq!( - $name::from_percent(100) * $num_type::max_value(), + $name::from_fraction(1.0) * $num_type::max_value(), $num_type::max_value() ); - assert_eq_error_rate!( - $name::from_percent(99) * $num_type::max_value(), - ((Into::::into($num_type::max_value()) * 99u32) / 100u32).as_u128() as $num_type, - 1, - ); - assert_eq!( - $name::from_percent(50) * $num_type::max_value(), - $num_type::max_value() / 2, - ); - assert_eq_error_rate!( - $name::from_percent(1) * $num_type::max_value(), - $num_type::max_value() / 100, - 1, - ); - assert_eq!($name::from_percent(0) * $num_type::max_value(), 0); + if $max % 100 == 0 { + assert_eq_error_rate!( + $name::from_percent(99) * $num_type::max_value(), + ((Into::::into($num_type::max_value()) * 99u32) / 100u32).as_u128() as $num_type, + 1, + ); + assert_eq!( + $name::from_fraction(0.5) * $num_type::max_value(), + $num_type::max_value() / 2, + ); + assert_eq_error_rate!( + $name::from_percent(1) * $num_type::max_value(), + $num_type::max_value() / 100, + 1, + ); + } else { + assert_eq!( + $name::from_fraction(0.99) * <$num_type>::max_value(), + ( + ( + u256ify!($name::from_fraction(0.99).0) * + u256ify!(<$num_type>::max_value()) / + u256ify!($max) + ).as_u128() + ) as $num_type, + ); + assert_eq!( + $name::from_fraction(0.50) * <$num_type>::max_value(), + ( + ( + u256ify!($name::from_fraction(0.50).0) * + u256ify!(<$num_type>::max_value()) / + u256ify!($max) + ).as_u128() + ) as $num_type, + ); + assert_eq!( + $name::from_fraction(0.01) * <$num_type>::max_value(), + ( + ( + u256ify!($name::from_fraction(0.01).0) * + u256ify!(<$num_type>::max_value()) / + u256ify!($max) + ).as_u128() + ) as $num_type, + ); + } + + assert_eq!($name::from_fraction(0.0) * $num_type::max_value(), 0); // // multiplication with bounds assert_eq!($name::one() * $num_type::max_value(), $num_type::max_value()); @@ -355,17 +475,20 @@ macro_rules! implement_per_thing { use primitive_types::U256; // accuracy test - assert_eq!($name::from_rational_approximation(1 as $type, 3) * 30 as $type, 10); + assert_eq!( + $name::from_rational_approximation(1 as $type, 3) * 30 as $type, + 10, + ); $(per_thing_mul_test!($test_units);)* } #[test] fn per_thing_mul_rounds_to_nearest_number() { - assert_eq!($name::from_percent(33) * 10u64, 3); - assert_eq!($name::from_percent(34) * 10u64, 3); - assert_eq!($name::from_percent(35) * 10u64, 3); - assert_eq!($name::from_percent(36) * 10u64, 4); + assert_eq!($name::from_fraction(0.33) * 10u64, 3); + assert_eq!($name::from_fraction(0.34) * 10u64, 3); + assert_eq!($name::from_fraction(0.35) * 10u64, 3); + assert_eq!($name::from_fraction(0.36) * 10u64, 4); } #[test] @@ -397,30 +520,31 @@ macro_rules! implement_per_thing { ); assert_eq!( $name::from_rational_approximation(1 as $num_type, 10), - $name::from_percent(10), + $name::from_fraction(0.10), ); assert_eq!( $name::from_rational_approximation(1 as $num_type, 4), - $name::from_percent(25), + $name::from_fraction(0.25), ); assert_eq!( $name::from_rational_approximation(1 as $num_type, 4), $name::from_rational_approximation(2 as $num_type, 8), ); // no accurate anymore but won't overflow. - assert_eq!( + assert_eq_error_rate!( $name::from_rational_approximation( $num_type::max_value() - 1, $num_type::max_value() - ), - $name::one(), + ).0 as $upper_type, + $name::one().0 as $upper_type, + 1, ); assert_eq_error_rate!( $name::from_rational_approximation( $num_type::max_value() / 3, $num_type::max_value() - ).0, - $name::from_parts($max / 3).0, + ).0 as $upper_type, + $name::from_parts($max / 3).0 as $upper_type, 2 ); assert_eq!( @@ -459,67 +583,73 @@ macro_rules! implement_per_thing { ); assert_eq!( $name::from_rational_approximation(3 * max_value / 2, 3 * max_value), - $name::from_percent(50), + $name::from_fraction(0.5), ); $(per_thing_from_rationale_approx_test!($test_units);)* } #[test] fn per_things_mul_operates_in_output_type() { - // assert_eq!($name::from_percent(50) * 100u32, 50u32); - assert_eq!($name::from_percent(50) * 100u64, 50u64); - assert_eq!($name::from_percent(50) * 100u128, 50u128); + // assert_eq!($name::from_fraction(0.5) * 100u32, 50u32); + assert_eq!($name::from_fraction(0.5) * 100u64, 50u64); + assert_eq!($name::from_fraction(0.5) * 100u128, 50u128); } #[test] fn per_thing_saturating_op_works() { - assert_eq!( - $name::from_percent(50).saturating_add($name::from_percent(40)), - $name::from_percent(90) + assert_eq_error_rate!( + $name::from_fraction(0.5).saturating_add($name::from_fraction(0.4)).0 as $upper_type, + $name::from_fraction(0.9).0 as $upper_type, + 2, ); - assert_eq!( - $name::from_percent(50).saturating_add($name::from_percent(50)), - $name::from_percent(100) + assert_eq_error_rate!( + $name::from_fraction(0.5).saturating_add($name::from_fraction(0.5)).0 as $upper_type, + $name::one().0 as $upper_type, + 2, ); assert_eq!( - $name::from_percent(60).saturating_add($name::from_percent(50)), - $name::from_percent(100) + $name::from_fraction(0.6).saturating_add($name::from_fraction(0.5)), + $name::one(), ); - assert_eq!( - $name::from_percent(60).saturating_sub($name::from_percent(50)), - $name::from_percent(10) + assert_eq_error_rate!( + $name::from_fraction(0.6).saturating_sub($name::from_fraction(0.5)).0 as $upper_type, + $name::from_fraction(0.1).0 as $upper_type, + 2, ); assert_eq!( - $name::from_percent(60).saturating_sub($name::from_percent(60)), - $name::from_percent(0) + $name::from_fraction(0.6).saturating_sub($name::from_fraction(0.6)), + $name::from_fraction(0.0), ); assert_eq!( - $name::from_percent(60).saturating_sub($name::from_percent(70)), - $name::from_percent(0) + $name::from_fraction(0.6).saturating_sub($name::from_fraction(0.7)), + $name::from_fraction(0.0), ); - assert_eq!( - $name::from_percent(50).saturating_mul($name::from_percent(50)), - $name::from_percent(25) + assert_eq_error_rate!( + $name::from_fraction(0.5).saturating_mul($name::from_fraction(0.5)).0 as $upper_type, + $name::from_fraction(0.25).0 as $upper_type, + 2, ); - assert_eq!( - $name::from_percent(20).saturating_mul($name::from_percent(20)), - $name::from_percent(4) + assert_eq_error_rate!( + $name::from_fraction(0.2).saturating_mul($name::from_fraction(0.2)).0 as $upper_type, + $name::from_fraction(0.04).0 as $upper_type, + 2, ); - assert_eq!( - $name::from_percent(10).saturating_mul($name::from_percent(10)), - $name::from_percent(1) + assert_eq_error_rate!( + $name::from_fraction(0.1).saturating_mul($name::from_fraction(0.1)).0 as $upper_type, + $name::from_fraction(0.01).0 as $upper_type, + 1, ); } #[test] fn per_thing_square_works() { - assert_eq!($name::from_percent(100).square(), $name::from_percent(100)); - assert_eq!($name::from_percent(50).square(), $name::from_percent(25)); - assert_eq!($name::from_percent(10).square(), $name::from_percent(1)); + assert_eq!($name::from_fraction(1.0).square(), $name::from_fraction(1.0)); + assert_eq!($name::from_fraction(0.5).square(), $name::from_fraction(0.25)); + assert_eq!($name::from_fraction(0.1).square(), $name::from_fraction(0.01)); assert_eq!( - $name::from_percent(2).square(), + $name::from_fraction(0.02).square(), $name::from_parts((4 * <$upper_type>::from($max) / 100 / 100) as $type) ); } @@ -527,22 +657,32 @@ macro_rules! implement_per_thing { #[test] fn per_things_div_works() { // normal - assert_eq!($name::from_percent(10) / $name::from_percent(20), - $name::from_percent(50) + assert_eq_error_rate!( + ($name::from_fraction(0.1) / $name::from_fraction(0.20)).0 as $upper_type, + $name::from_fraction(0.50).0 as $upper_type, + 2, ); - assert_eq!($name::from_percent(10) / $name::from_percent(10), - $name::from_percent(100) + assert_eq_error_rate!( + ($name::from_fraction(0.1) / $name::from_fraction(0.10)).0 as $upper_type, + $name::from_fraction(1.0).0 as $upper_type, + 2, ); - assert_eq!($name::from_percent(10) / $name::from_percent(0), - $name::from_percent(100) + assert_eq_error_rate!( + ($name::from_fraction(0.1) / $name::from_fraction(0.0)).0 as $upper_type, + $name::from_fraction(1.0).0 as $upper_type, + 2, ); // will not overflow - assert_eq!($name::from_percent(10) / $name::from_percent(5), - $name::from_percent(100) + assert_eq_error_rate!( + ($name::from_fraction(0.10) / $name::from_fraction(0.05)).0 as $upper_type, + $name::from_fraction(1.0).0 as $upper_type, + 2, ); - assert_eq!($name::from_percent(100) / $name::from_percent(50), - $name::from_percent(100) + assert_eq_error_rate!( + ($name::from_fraction(1.0) / $name::from_fraction(0.5)).0 as $upper_type, + $name::from_fraction(1.0).0 as $upper_type, + 2, ); } } @@ -558,6 +698,15 @@ implement_per_thing!( u16, "_Percent_", ); +implement_per_thing!( + PerU16, + test_peru16, + [u32, u64, u128], + 65535_u16, + u16, + u32, + "_Parts per 65535_", +); implement_per_thing!( Permill, test_permill, diff --git a/primitives/phragmen/compact/src/assignment.rs b/primitives/phragmen/compact/src/assignment.rs index e6413efe23110..b81c807dec25f 100644 --- a/primitives/phragmen/compact/src/assignment.rs +++ b/primitives/phragmen/compact/src/assignment.rs @@ -187,6 +187,7 @@ pub(crate) fn assignment( for _phragmen::Assignment { who, distribution } in assignments { match distribution.len() { + 0 => continue, #from_impl _ => { return Err(_phragmen::Error::CompactTargetOverflow); diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index 9a51ddcb33d0a..cc3eac63b9e8b 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -158,8 +158,7 @@ fn struct_def( }).collect::(); Ok(quote! ( - /// A struct to encode a `Vec` or `Vec` of the phragmen module - /// in a compact way. + /// A struct to encode a Phragmen assignment in a compact way. #[derive( Default, PartialEq, diff --git a/primitives/phragmen/compact/src/staked.rs b/primitives/phragmen/compact/src/staked.rs index 3d39466853d2d..46b1d6bf7aada 100644 --- a/primitives/phragmen/compact/src/staked.rs +++ b/primitives/phragmen/compact/src/staked.rs @@ -175,6 +175,7 @@ pub(crate) fn staked( let mut compact: #ident<#voter_type, #target_type, u128, A> = Default::default(); for _phragmen::StakedAssignment { who, distribution } in assignments { match distribution.len() { + 0 => continue, #from_impl _ => { return Err(_phragmen::Error::CompactTargetOverflow); diff --git a/primitives/phragmen/src/helpers.rs b/primitives/phragmen/src/helpers.rs new file mode 100644 index 0000000000000..82ffc8a4757db --- /dev/null +++ b/primitives/phragmen/src/helpers.rs @@ -0,0 +1,101 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Helper methods for phragmen. + +use crate::{Assignment, StakedAssignment, ExtendedBalance, IdentifierT}; +use sp_std::prelude::*; +use sp_runtime::PerThing; + +/// Converts a vector of ratio assignments into ones with absolute budget value. +pub fn assignment_ratio_to_budget( + ratio: Vec>, + stake_of: FS, +) -> Vec> + where + for <'r> FS: Fn(&'r A) -> ExtendedBalance, + ExtendedBalance: From<::Inner> +{ + ratio + .into_iter() + .map(|a| { + let who = a.who.clone(); + a.into_staked(stake_of(&who), true) + }) + .collect() +} + +/// Converts a vector of ratio assignments into ones with absolute budget value. +pub fn assignment_staked_to_ratio( + ratio: Vec>, +) -> Vec> where ExtendedBalance: From<::Inner> +{ + ratio + .into_iter() + .map(|a| a.into_assignment(true)) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ExtendedBalance; + use sp_runtime::Perbill; + + #[test] + fn into_staked_works() { + let ratio = vec![ + Assignment { + who: 1u32, + distribution: vec![ + (10u32, Perbill::from_fraction(0.5)), + (20, Perbill::from_fraction(0.5)), + ] + }, + Assignment { + who: 2u32, + distribution: vec![ + (10, Perbill::from_fraction(0.33)), + (20, Perbill::from_fraction(0.67)), + ] + }, + ]; + + let stake_of = |_: &u32| -> ExtendedBalance { 100u128 }; + let staked = assignment_ratio_to_budget(ratio, stake_of); + + assert_eq!( + staked, + vec![ + StakedAssignment { + who: 1u32, + distribution: vec![ + (10u32, 50), + (20, 50), + ] + }, + StakedAssignment { + who: 2u32, + distribution: vec![ + (10u32, 33), + (20, 67), + ] + } + ] + ); + } +} + diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 1ef11954ce854..ef74327ca42a6 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -48,16 +48,21 @@ use codec::{Encode, Decode}; mod node; mod reduce; +mod helpers; -// re-export reduce stuff +// re-export reduce stuff. pub use reduce::reduce; +// re-export the helpers. +pub use helpers::*; + // re-export the compact macro, with the dependencies of the macro. #[doc(hidden)] pub use codec; #[doc(hidden)] pub use sp_runtime; +// re-export the compact solution type. pub use sp_phragmen_compact::generate_compact_solution_type; // an aggregator trait for a generic type of a voter/target identifier. This usually maps to @@ -153,7 +158,7 @@ pub struct Assignment { } impl Assignment - where T: sp_std::ops::Mul + where ExtendedBalance: From<::Inner> { /// Convert from a ratio assignment into one with absolute values aka. [`StakedAssignment`]. /// @@ -164,7 +169,7 @@ impl Assignment { let mut sum: ExtendedBalance = Bounded::min_value(); let mut distribution = self.distribution.into_iter().map(|(target, p)| { - let distribution_stake = p * stake; + let distribution_stake = p.mul_collapse(stake); // defensive only. We assume that balance cannot exceed extended balance. sum = sum.saturating_add(distribution_stake); (target, distribution_stake) @@ -176,6 +181,10 @@ impl Assignment if let Some(last) = distribution.last_mut() { last.1 += leftover; } + } else if let Some(excess) = sum.checked_sub(stake) { + if let Some(last) = distribution.last_mut() { + last.1 -= excess; + } } } StakedAssignment { @@ -196,17 +205,19 @@ pub struct StakedAssignment { pub distribution: Vec<(AccountId, ExtendedBalance)>, } -impl StakedAssignment { +impl StakedAssignment +{ /// Converts self into the normal [`Assignment`] type. - pub fn into_assignment(self, fill: bool) -> Assignment { + pub fn into_assignment(self, fill: bool) -> Assignment + where ExtendedBalance: From<::Inner> + { let accuracy: u128 = T::ACCURACY.saturated_into(); let mut sum: u128 = Zero::zero(); let stake = self.distribution.iter().map(|x| x.1).sum(); let mut distribution = self.distribution.into_iter().map(|(target, w)| { - let portion = multiply_by_rational(w, accuracy, stake).unwrap_or(Bounded::max_value()); - sum += portion; - let per_thing = T::from_parts(portion.saturated_into()); + let per_thing = T::from_rational_approximation(w, stake); + sum += per_thing.clone().deconstruct().saturated_into(); (target, per_thing) }).collect::>(); @@ -215,6 +226,10 @@ impl StakedAssignment { if let Some(last) = distribution.last_mut() { last.1 = last.1.saturating_add(T::from_parts(leftover.saturated_into())); } + } else if let Some(excess) = sum.checked_sub(accuracy) { + if let Some(last) = distribution.last_mut() { + last.1 = last.1.saturating_sub(T::from_parts(excess.saturated_into())); + } } } @@ -496,8 +511,8 @@ pub fn elect( /// /// `O(E)` where `E` is the total number of edges. pub fn build_support_map( - winners: &Vec, - assignments: &Vec>, + winners: &[AccountId], + assignments: &[StakedAssignment], ) -> (SupportMap, u32) where AccountId: Default + Ord + Member, { diff --git a/primitives/phragmen/src/mock.rs b/primitives/phragmen/src/mock.rs index 520a344a5194f..aeac71f1003fa 100644 --- a/primitives/phragmen/src/mock.rs +++ b/primitives/phragmen/src/mock.rs @@ -20,8 +20,8 @@ use crate::{elect, PhragmenResult, Assignment}; use sp_runtime::{ - assert_eq_error_rate, Perbill, PerThing, - traits::{Convert, Member, SaturatedConversion, Zero} + assert_eq_error_rate, PerThing, + traits::{Convert, Member, SaturatedConversion, Zero, One} }; use sp_std::collections::btree_map::BTreeMap; @@ -328,15 +328,15 @@ pub fn check_assignments_sum(assignments: Vec( candidates: Vec, voters: Vec<(AccountId, Vec)>, - stake_of: Box Balance>, + stake_of: &Box Balance>, to_elect: usize, min_to_elect: usize, ) { // run fixed point code. - let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Perbill>( + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>( to_elect, min_to_elect, candidates.clone(), @@ -360,9 +360,9 @@ pub(crate) fn run_and_compare( for (candidate, per_thingy) in distribution { if let Some(float_assignment) = float_assignments.1.iter().find(|x| x.0 == candidate ) { assert_eq_error_rate!( - Perbill::from_fraction(float_assignment.1).deconstruct(), + Output::from_fraction(float_assignment.1).deconstruct(), per_thingy.deconstruct(), - 1, + Output::Inner::one(), ); } else { panic!("candidate mismatch. This should never happen.") diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index f9518ef94ee5c..59a94ad51bafc 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -24,7 +24,7 @@ use crate::{ Support, StakedAssignment, Assignment, PhragmenResult, ExtendedBalance, }; use substrate_test_utils::assert_eq_uvec; -use sp_runtime::{Perbill, traits::Convert}; +use sp_runtime::{Perbill, Permill, Percent, PerU16, traits::Convert}; #[test] fn float_phragmen_poc_works() { @@ -128,12 +128,10 @@ fn phragmen_poc_2_works() { (4, 500), ]); - run_and_compare(candidates, voters, stake_of, 2, 2); -} - -#[test] -fn phragmen_works_with_any_per_thing() { - unimplemented!(); + run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2, 2); + run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2, 2); + run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2, 2); + run_and_compare::(candidates, voters, &stake_of, 2, 2); } #[test] @@ -151,7 +149,10 @@ fn phragmen_poc_3_works() { (4, 1000), ]); - run_and_compare(candidates, voters, stake_of, 2, 2); + run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2, 2); + run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2, 2); + run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2, 2); + run_and_compare::(candidates, voters, &stake_of, 2, 2); } #[test] @@ -396,7 +397,7 @@ fn phragmen_linear_equalize() { (130, 1000), ]); - run_and_compare(candidates, voters, stake_of, 2, 2); + run_and_compare::(candidates, voters, &stake_of, 2, 2); } #[test] @@ -495,8 +496,9 @@ fn self_votes_should_be_kept() { a.into_staked(stake, true) }).collect(); + let winners = result.winners.into_iter().map(|(who, _)| who).collect::>(); let (mut supports, _) = build_support_map::( - &result.winners.into_iter().map(|(who, _)| who).collect(), + winners.as_slice(), &staked_assignments, ); @@ -896,7 +898,7 @@ mod compact { let assignments = vec![ StakedAssignment { who: 1 as AccountId, - distribution: (10..30).map(|i| (i as AccountId, i as Balance)).collect::>(), + distribution: (10..26).map(|i| (i as AccountId, i as Balance)).collect::>(), }, ]; @@ -908,9 +910,81 @@ mod compact { entity_index, ); + assert!(compacted.is_ok()); + + let assignments = vec![ + StakedAssignment { + who: 1 as AccountId, + distribution: (10..27).map(|i| (i as AccountId, i as Balance)).collect::>(), + }, + ]; + + let compacted = >::from_staked( + assignments.clone(), + entity_index, + entity_index, + ); + + assert_eq!( + compacted.unwrap_err(), + PhragmenError::CompactTargetOverflow, + ); + + let assignments = vec![ + Assignment { + who: 1 as AccountId, + distribution: (10..27).map(|i| (i as AccountId, Percent::from_parts(i as u8))).collect::>(), + }, + ]; + + let compacted = >::from_assignment( + assignments.clone(), + entity_index, + entity_index, + ); + assert_eq!( compacted.unwrap_err(), PhragmenError::CompactTargetOverflow, - ) + ); + } + + #[test] + fn zero_target_count_is_ignored() { + let voters = vec![1 as AccountId, 2]; + let targets = vec![10 as AccountId, 11]; + + let assignments = vec![ + StakedAssignment { + who: 1 as AccountId, + distribution: vec![(10, 100 as Balance), (11, 100)] + }, + StakedAssignment { + who: 2, + distribution: vec![], + }, + ]; + + let voter_index = |a: &AccountId| -> Option { + voters.iter().position(|x| x == a).map(TryInto::try_into).unwrap().ok() + }; + let target_index = |a: &AccountId| -> Option { + targets.iter().position(|x| x == a).map(TryInto::try_into).unwrap().ok() + }; + + let compacted = >::from_staked( + assignments.clone(), + voter_index, + target_index, + ).unwrap(); + + assert_eq!( + compacted, + TestCompact { + votes1: Default::default(), + votes2: vec![(0, (0, 100), 1)], + ..Default::default() + } + ); } } diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index cf388a203c70b..4ff614475504e 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -69,7 +69,7 @@ pub use sp_core::RuntimeDebug; /// Re-export top-level arithmetic stuff. pub use sp_arithmetic::{ - Perquintill, Perbill, Permill, Percent, Rational128, Fixed64, PerThing, + Perquintill, Perbill, Permill, Percent, PerU16, Rational128, Fixed64, PerThing, traits::SaturatedConversion, }; /// Re-export 128 bit helpers. From 2f75ea7fd00a65f4db1d1af6f422efacca27f7c6 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 18 Feb 2020 17:11:43 +0100 Subject: [PATCH 048/106] better score prediction --- frame/elections-phragmen/src/lib.rs | 2 +- frame/staking/src/lib.rs | 48 ++++++++++++-------------- frame/staking/src/mock.rs | 29 ++++++++++------ frame/staking/src/offchain_election.rs | 41 ++++++++-------------- frame/staking/src/tests.rs | 21 +++++------ primitives/phragmen/src/helpers.rs | 7 ++-- primitives/phragmen/src/lib.rs | 3 +- 7 files changed, 74 insertions(+), 77 deletions(-) diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 68167808b73ae..b37917be84036 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -672,7 +672,7 @@ impl Module { Self::locked_stake_of(who) ) as ExtendedBalance }; - let staked_assignments = sp_phragmen::assignment_ratio_to_budget( + let staked_assignments = sp_phragmen::assignment_ratio_to_staked( phragmen_result.assignments, stake_of, ); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index e5552f381b785..0caeb28878d10 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -268,7 +268,7 @@ use frame_support::{ }; use pallet_session::historical; use sp_runtime::{ - Perbill, PerThing, Percent, RuntimeDebug, RuntimeAppPublic, + Perbill, PerThing, RuntimeDebug, RuntimeAppPublic, curve::PiecewiseLinear, traits::{ Convert, Zero, One, StaticLookup, CheckedSub, Saturating, Bounded, SaturatedConversion, @@ -289,8 +289,8 @@ use frame_system::{ offchain::SubmitUnsignedTransaction, }; use sp_phragmen::{ - ExtendedBalance, StakedAssignment, Assignment, PhragmenScore, PhragmenResult, - build_support_map, evaluate_support, elect, generate_compact_solution_type, is_score_better, + ExtendedBalance, Assignment, PhragmenScore, PhragmenResult, build_support_map, evaluate_support, + elect, generate_compact_solution_type, is_score_better, }; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; @@ -320,7 +320,7 @@ pub type Points = u32; pub type ChainAccuracy = Perbill; /// Accuracy used for off-chain phragmen. This better be small. -pub type OffchainAccuracy = Percent; +pub type OffchainAccuracy = sp_runtime::PerU16; pub mod sr25519 { mod app_sr25519 { @@ -1598,6 +1598,13 @@ impl Module { Self::bonded(stash).and_then(Self::ledger).map(|l| l.active).unwrap_or_default() } + /// internal impl of [`slashable_balance_of`] that returns [`ExtendedBalance`]. + fn slashable_balance_of_extended(stash: &T::AccountId) -> ExtendedBalance { + , u64>>::convert( + Self::slashable_balance_of(stash) + ) as ExtendedBalance + } + /// returns true if the end of the current session leads to an era change. fn is_current_session_final() -> bool { let current_index = T::SessionInterface::current_index(); @@ -1801,11 +1808,6 @@ impl Module { // NOTE: we assume both `ValidatorIndex` and `NominatorIndex` are smaller than usize. snapshot_validators.get(i as usize).cloned() }; - let slashable_balance = |w: &T::AccountId| -> ExtendedBalance { - , u64>>::convert( - Self::slashable_balance_of(w) - ) as ExtendedBalance - }; // un-compact. let assignments = compact_assignments.into_assignment( @@ -1860,13 +1862,10 @@ impl Module { } // convert into staked assignments. - let staked_assignments: Vec> = assignments - .into_iter() - .map(|a| { - let stake = slashable_balance(&a.who); - a.into_staked(stake, true) - }) - .collect(); + let staked_assignments = sp_phragmen::assignment_ratio_to_staked( + assignments, + Self::slashable_balance_of_extended, + ); // build the support map thereof in order to evaluate. // OPTIMIZATION: loop to create the staked assignments but it would bloat the code. Okay for @@ -2105,7 +2104,9 @@ impl Module { >( compute: ElectionCompute, ) -> Option>> - where ExtendedBalance: From<::Inner> + where + Accuracy: sp_std::ops::Mul, + ExtendedBalance: From<::Inner>, { if let Some(phragmen_result) = Self::do_phragmen::() { let elected_stashes = phragmen_result.winners.iter() @@ -2113,15 +2114,10 @@ impl Module { .collect::>(); let assignments = phragmen_result.assignments; - let staked_assignments: Vec> = assignments - .into_iter() - .map(|a: Assignment| { - let stake = , u64>>::convert( - Self::slashable_balance_of(&a.who) - ) as ExtendedBalance; - a.into_staked(stake, true) - }) - .collect(); + let staked_assignments = sp_phragmen::assignment_ratio_to_staked( + assignments, + Self::slashable_balance_of_extended, + ); let (supports, _) = build_support_map::( &elected_stashes, diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 47aa355fcc960..1ececa74434fb 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -28,7 +28,6 @@ use sp_core::H256; use sp_io; use sp_phragmen::{ build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment, PhragmenScore, - Assignment, }; use sp_runtime::curve::PiecewiseLinear; use sp_runtime::testing::{Header, TestXt, UintAuthorityId}; @@ -820,6 +819,7 @@ pub fn horrible_phragmen_with_post_processing( }); }); + dbg!(&staked_assignment); // Ensure that this result is worse than seq-phragmen. Otherwise, it should not have been used // for testing. let score = { @@ -864,6 +864,8 @@ pub fn horrible_phragmen_with_post_processing( (compact, winners, score) } +// Note: this should always logicall reproduce [`offchain_election::prepare_submission`], yet we +// cannot do it since we want to have `tweak` injected into the process. pub fn do_phragmen_with_post_processing( do_reduce: bool, tweak: impl FnOnce(&mut Vec>), @@ -884,7 +886,7 @@ pub fn do_phragmen_with_post_processing( Staking::slashable_balance_of(&who) ) as ExtendedBalance }; - let mut staked = sp_phragmen::assignment_ratio_to_budget(assignments, stake_of); + let mut staked = sp_phragmen::assignment_ratio_to_staked(assignments, stake_of); // apply custom tweaks. awesome for testing. tweak(&mut staked); @@ -893,10 +895,6 @@ pub fn do_phragmen_with_post_processing( reduce(&mut staked); } - // compute score - let (support_map, _) = build_support_map::(winners.as_ref(), &staked); - let score = evaluate_support::(&support_map); - // convert back to ratio assignment. This takes less space. let snapshot_validators = Staking::snapshot_validators().unwrap(); let snapshot_nominators = Staking::snapshot_nominators().unwrap(); @@ -907,10 +905,21 @@ pub fn do_phragmen_with_post_processing( snapshot_validators.iter().position(|x| x == a).map(|i| i as ValidatorIndex) }; - let assignments_reduced: Vec> = staked - .into_iter() - .map(|sa| sa.into_assignment(true)) - .collect(); + let assignments_reduced = sp_phragmen::assignment_staked_to_ratio(staked); + + // re-compute score by converting, yet again, into staked type + let score = { + let staked = sp_phragmen::assignment_ratio_to_staked( + assignments_reduced.clone(), + Staking::slashable_balance_of_extended, + ); + + let (support_map, _) = build_support_map::( + winners.as_slice(), + staked.as_slice(), + ); + evaluate_support::(&support_map) + }; let compact = >::from_assignment( assignments_reduced, diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 6281e56dd64b4..8faa16630d951 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -17,19 +17,18 @@ //! Helpers for offchain worker election. use crate::{ - Call, Module, Trait, BalanceOf, ValidatorIndex, NominatorIndex, CompactOf, OffchainAccuracy, + Call, Module, Trait, ValidatorIndex, NominatorIndex, CompactOf, OffchainAccuracy, }; use codec::Encode; use frame_system::offchain::{SubmitUnsignedTransaction}; use frame_support::debug; use sp_phragmen::{ - reduce, ExtendedBalance, PhragmenResult, StakedAssignment, Assignment, PhragmenScore, + reduce, ExtendedBalance, PhragmenResult, Assignment, PhragmenScore, build_support_map, evaluate_support, }; use sp_std::{prelude::*, convert::TryInto}; use sp_runtime::{RuntimeAppPublic, RuntimeDebug}; use sp_runtime::offchain::storage::StorageValueRef; -use sp_runtime::traits::Convert; use sp_runtime::PerThing; #[derive(RuntimeDebug)] @@ -184,46 +183,36 @@ where >::try_into(i).ok() ) }; - let stake_of = |who: &T::AccountId| -> ExtendedBalance { - , u64>>::convert( - >::slashable_balance_of(who) - ) as ExtendedBalance - }; // Clean winners. let winners = winners.into_iter().map(|(w, _)| w).collect::>(); // convert into absolute value and to obtain the reduced version. - let mut staked: Vec> = assignments - .into_iter() - .map(|a| { - let stake = stake_of(&a.who); - a.into_staked(stake, true) - }) - .collect(); + let mut staked = sp_phragmen::assignment_ratio_to_staked( + assignments, + >::slashable_balance_of_extended, + ); if do_reduce { reduce(&mut staked); } // Convert back to ratio assignment. This takes less space. - let low_accuracy_assignment: Vec> = staked - .into_iter() - .map(|sa| sa.into_assignment(true)) - .collect(); + let low_accuracy_assignment = sp_phragmen::assignment_staked_to_ratio(staked); // convert back to staked to compute the score in the receiver's accuracy. This can be done // nicer, for now we do it as such since this code is not time-critical. This ensure that the // score _predicted_ here is the same as the one computed on chain and you will not get a // `PhragmenBogusScore` error. This is totally NOT needed if we don't do reduce. This whole - // accuracy glitch happens because reduce breaks that assumption of scale. + // accuracy glitch happens because reduce breaks that assumption of _scale_. The initial + // phragmen results are computed in `OffchainAccuracy` and the initial `staked` assignment set + // is also all multiples of this value. After reduce, this no longer holds. Hence converting to + // ratio thereafter is not trivially reversible. let score = { - let staked: Vec> = low_accuracy_assignment - .iter() - .map(|a| { - let stake = stake_of(&a.who); - a.clone().into_staked(stake, true) - }).collect(); + let staked = sp_phragmen::assignment_ratio_to_staked( + low_accuracy_assignment.clone(), + >::slashable_balance_of_extended, + ); let (support_map, _) = build_support_map::( winners.as_slice(), diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index c2b6a6e54ea8b..484fb469e66ae 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2677,25 +2677,26 @@ fn remove_multi_deferred() { } mod offchain_phragmen { + use std::sync::Arc; + use parking_lot::RwLock; use crate::*; use mock::*; - use frame_support::{assert_ok, assert_noop, debug}; - use substrate_test_utils::assert_eq_uvec; - use sp_runtime::traits::OffchainWorker; + use sp_core::traits::KeystoreExt; + use sp_core::testing::KeyStore; use sp_core::offchain::{ OffchainExt, TransactionPoolExt, testing::{TestOffchainExt, TestTransactionPoolExt, PoolState}, }; use sp_io::TestExternalities; - use sp_core::traits::KeystoreExt; - use sp_core::testing::KeyStore; - use std::sync::Arc; - use parking_lot::RwLock; + use substrate_test_utils::assert_eq_uvec; + use frame_support::{assert_ok, assert_noop, debug}; + use sp_runtime::traits::OffchainWorker; + use sp_phragmen::StakedAssignment; type DummyT = dummy_sr25519::AuthorityId; - fn percent(x: u8) -> OffchainAccuracy { + fn percent(x: u16) -> OffchainAccuracy { OffchainAccuracy::from_percent(x) } @@ -3009,7 +3010,7 @@ mod offchain_phragmen { assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); // a bad solution - let (compact, winners, score) = horrible_phragmen_with_post_processing(true); + let (compact, winners, score) = horrible_phragmen_with_post_processing(false); assert_noop!( Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenWeakSubmission, @@ -3031,7 +3032,7 @@ mod offchain_phragmen { run_to_block(12); // a meeeeh solution - let (compact, winners, score) = horrible_phragmen_with_post_processing(true); + let (compact, winners, score) = horrible_phragmen_with_post_processing(false); assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); // a better solution diff --git a/primitives/phragmen/src/helpers.rs b/primitives/phragmen/src/helpers.rs index 82ffc8a4757db..d3db897027231 100644 --- a/primitives/phragmen/src/helpers.rs +++ b/primitives/phragmen/src/helpers.rs @@ -21,13 +21,14 @@ use sp_std::prelude::*; use sp_runtime::PerThing; /// Converts a vector of ratio assignments into ones with absolute budget value. -pub fn assignment_ratio_to_budget( +pub fn assignment_ratio_to_staked( ratio: Vec>, stake_of: FS, ) -> Vec> where for <'r> FS: Fn(&'r A) -> ExtendedBalance, - ExtendedBalance: From<::Inner> + T: sp_std::ops::Mul, + ExtendedBalance: From<::Inner>, { ratio .into_iter() @@ -75,7 +76,7 @@ mod tests { ]; let stake_of = |_: &u32| -> ExtendedBalance { 100u128 }; - let staked = assignment_ratio_to_budget(ratio, stake_of); + let staked = assignment_ratio_to_staked(ratio, stake_of); assert_eq!( staked, diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index ef74327ca42a6..0b15f4c90f0db 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -166,10 +166,11 @@ impl Assignment /// it ensures that all the potential rounding errors are compensated and the distribution's sum /// is exactly equal to the total budget. pub fn into_staked(self, stake: ExtendedBalance, fill: bool) -> StakedAssignment + where T: sp_std::ops::Mul { let mut sum: ExtendedBalance = Bounded::min_value(); let mut distribution = self.distribution.into_iter().map(|(target, p)| { - let distribution_stake = p.mul_collapse(stake); + let distribution_stake = p * stake; // defensive only. We assume that balance cannot exceed extended balance. sum = sum.saturating_add(distribution_stake); (target, distribution_stake) From 2fa064b0ded2e97393ddcddbadd32bd8c4a027eb Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 19 Feb 2020 14:31:18 +0100 Subject: [PATCH 049/106] Add fuzzer, more patches. --- Cargo.lock | 3 +- frame/staking/Cargo.toml | 13 +- frame/staking/fuzzer/.gitignore | 3 + frame/staking/fuzzer/Cargo.lock | 2277 +++++++++++++++++++ frame/staking/fuzzer/Cargo.toml | 31 + frame/staking/fuzzer/src/mock.rs | 195 ++ frame/staking/fuzzer/src/submit_solution.rs | 135 ++ frame/staking/src/lib.rs | 6 +- frame/staking/src/mock.rs | 1 - frame/staking/src/offchain_election.rs | 2 +- frame/staking/src/testing_utils.rs | 324 +++ primitives/arithmetic/src/per_things.rs | 6 +- primitives/phragmen/src/lib.rs | 57 +- 13 files changed, 3030 insertions(+), 23 deletions(-) create mode 100644 frame/staking/fuzzer/.gitignore create mode 100644 frame/staking/fuzzer/Cargo.lock create mode 100644 frame/staking/fuzzer/Cargo.toml create mode 100644 frame/staking/fuzzer/src/mock.rs create mode 100644 frame/staking/fuzzer/src/submit_solution.rs create mode 100644 frame/staking/src/testing_utils.rs diff --git a/Cargo.lock b/Cargo.lock index 09bbcbe3f2f4d..7958b3f58311a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4360,16 +4360,17 @@ dependencies = [ "frame-system", "pallet-authorship", "pallet-balances", + "pallet-indices", "pallet-session", "pallet-staking-reward-curve", "pallet-timestamp", "parity-scale-codec", "parking_lot 0.10.0", + "rand 0.7.3", "serde", "sp-application-crypto", "sp-core", "sp-io", - "sp-keyring", "sp-phragmen", "sp-runtime", "sp-staking", diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index bb28b46038fd3..49b56771b13da 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -8,7 +8,6 @@ license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } -sp-keyring = { version = "2.0.0", optional = true, path = "../../primitives/keyring" } sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } sp-phragmen = { version = "2.0.0", default-features = false, path = "../../primitives/phragmen" } sp-io ={ path = "../../primitives/io", default-features = false } @@ -20,8 +19,12 @@ pallet-session = { version = "2.0.0", features = ["historical"], path = "../sess pallet-authorship = { version = "2.0.0", default-features = false, path = "../authorship" } sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } +# Optional imports for tesing-utils feature +pallet-indices = { version = "2.0.0", optional = true, path = "../indices" } +sp-core = { version = "2.0.0", optional = true, path = "../../primitives/core" } +rand = { version = "0.7.3", optional = true } + [dev-dependencies] -sp-core = { version = "2.0.0", path = "../../primitives/core" } pallet-balances = { version = "2.0.0", path = "../balances" } pallet-timestamp = { version = "2.0.0", path = "../timestamp" } pallet-staking-reward-curve = { version = "2.0.0", path = "../staking/reward-curve" } @@ -29,12 +32,13 @@ substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } parking_lot = { version = "0.10.0" } env_logger = "0.7.1" + [features] migrate = [] +testing-utils = ["std"] default = ["std"] std = [ "serde", - "sp-keyring", "codec/std", "sp-std/std", "sp-phragmen/std", @@ -43,6 +47,9 @@ std = [ "sp-runtime/std", "sp-staking/std", "pallet-session/std", + "pallet-indices/std", "frame-system/std", "pallet-authorship/std", + "sp-core/std", + "rand/std" ] diff --git a/frame/staking/fuzzer/.gitignore b/frame/staking/fuzzer/.gitignore new file mode 100644 index 0000000000000..bd4ff56b3c310 --- /dev/null +++ b/frame/staking/fuzzer/.gitignore @@ -0,0 +1,3 @@ +hfuzz_target +hfuzz_workspace + diff --git a/frame/staking/fuzzer/Cargo.lock b/frame/staking/fuzzer/Cargo.lock new file mode 100644 index 0000000000000..421f926cb09a6 --- /dev/null +++ b/frame/staking/fuzzer/Cargo.lock @@ -0,0 +1,2277 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ahash" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aho-corasick" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "anyhow" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arbitrary" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base58" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitmask" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitvec" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bumpalo" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byte-slice-cast" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "c2-chacha" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clear_on_drop" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "const-random" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "const-random-macro" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "curve25519-dalek" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "curve25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "derive_more" +version = "0.99.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.0-pre.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "environmental" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "failure" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fixed-hash" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "frame-metadata" +version = "11.0.0" +dependencies = [ + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "frame-support" +version = "2.0.0" +dependencies = [ + "bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "frame-metadata 11.0.0", + "frame-support-procedural 2.0.0", + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-arithmetic 2.0.0", + "sp-core 2.0.0", + "sp-inherents 2.0.0", + "sp-io 2.0.0", + "sp-runtime 2.0.0", + "sp-state-machine 0.8.0", + "sp-std 2.0.0", + "tracing 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "frame-support-procedural" +version = "2.0.0" +dependencies = [ + "frame-support-procedural-tools 2.0.0", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "2.0.0" +dependencies = [ + "frame-support-procedural-tools-derive 2.0.0", + "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "frame-support-procedural-tools-derive" +version = "2.0.0" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "frame-system" +version = "2.0.0" +dependencies = [ + "frame-support 2.0.0", + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-io 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", + "sp-version 2.0.0", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-channel" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-executor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-io" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-sink" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-task" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-util" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hashbrown" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hashbrown" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hmac" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hmac-drbg" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "honggfuzz" +version = "0.5.45" +dependencies = [ + "arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "impl-codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "impl-serde" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "impl-serde" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "js-sys" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libsecp256k1" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lock_api" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lock_api" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memory-db" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memory_units" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "merlin" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "nom" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-integer" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-rational" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "once_cell" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "once_cell" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pallet-authorship" +version = "2.0.0" +dependencies = [ + "frame-support 2.0.0", + "frame-system 2.0.0", + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-authorship 2.0.0", + "sp-core 2.0.0", + "sp-inherents 2.0.0", + "sp-io 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "pallet-balances" +version = "2.0.0" +dependencies = [ + "frame-support 2.0.0", + "frame-system 2.0.0", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-io 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "pallet-indices" +version = "2.0.0" +dependencies = [ + "frame-support 2.0.0", + "frame-system 2.0.0", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-io 2.0.0", + "sp-keyring 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "pallet-session" +version = "2.0.0" +dependencies = [ + "frame-support 2.0.0", + "frame-system 2.0.0", + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pallet-timestamp 2.0.0", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-io 2.0.0", + "sp-runtime 2.0.0", + "sp-staking 2.0.0", + "sp-std 2.0.0", + "sp-trie 2.0.0", +] + +[[package]] +name = "pallet-staking" +version = "2.0.0" +dependencies = [ + "frame-support 2.0.0", + "frame-system 2.0.0", + "pallet-authorship 2.0.0", + "pallet-indices 2.0.0", + "pallet-session 2.0.0", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-application-crypto 2.0.0", + "sp-core 2.0.0", + "sp-io 2.0.0", + "sp-phragmen 2.0.0", + "sp-runtime 2.0.0", + "sp-staking 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "pallet-staking-fuzzer" +version = "2.0.0" +dependencies = [ + "frame-support 2.0.0", + "frame-system 2.0.0", + "honggfuzz 0.5.45", + "pallet-balances 2.0.0", + "pallet-indices 2.0.0", + "pallet-session 2.0.0", + "pallet-staking 2.0.0", + "pallet-staking-reward-curve 2.0.0", + "pallet-timestamp 2.0.0", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-io 2.0.0", + "sp-phragmen 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "pallet-staking-reward-curve" +version = "2.0.0" +dependencies = [ + "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pallet-timestamp" +version = "2.0.0" +dependencies = [ + "frame-support 2.0.0", + "frame-system 2.0.0", + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-inherents 2.0.0", + "sp-io 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", + "sp-timestamp 2.0.0", +] + +[[package]] +name = "parity-scale-codec" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-slice-cast 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-util-mem" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-util-mem-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-wasm" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "parking_lot" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "paste" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "paste-impl" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pbkdf2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pin-utils" +version = "0.1.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "primitive-types" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fixed-hash 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-serde 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-nested" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "regex" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "schnorrkel" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "send_wrapper" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "smallvec" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "sourcefile" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "sp-api" +version = "2.0.0" +dependencies = [ + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-api-proc-macro 2.0.0", + "sp-core 2.0.0", + "sp-runtime 2.0.0", + "sp-state-machine 0.8.0", + "sp-std 2.0.0", + "sp-version 2.0.0", +] + +[[package]] +name = "sp-api-proc-macro" +version = "2.0.0" +dependencies = [ + "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-application-crypto" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-io 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-arithmetic" +version = "2.0.0" +dependencies = [ + "integer-sqrt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-debug-derive 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-authorship" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-inherents 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-core" +version = "2.0.0" +dependencies = [ + "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-debug-derive 2.0.0", + "sp-externalities 0.8.0", + "sp-runtime-interface 2.0.0", + "sp-std 2.0.0", + "sp-storage 2.0.0", + "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-debug-derive" +version = "2.0.0" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-externalities" +version = "0.8.0" +dependencies = [ + "environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-std 2.0.0", + "sp-storage 2.0.0", +] + +[[package]] +name = "sp-inherents" +version = "2.0.0" +dependencies = [ + "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-io" +version = "2.0.0" +dependencies = [ + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-externalities 0.8.0", + "sp-runtime-interface 2.0.0", + "sp-state-machine 0.8.0", + "sp-std 2.0.0", + "sp-trie 2.0.0", + "sp-wasm-interface 2.0.0", +] + +[[package]] +name = "sp-keyring" +version = "2.0.0" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-runtime 2.0.0", + "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-panic-handler" +version = "2.0.0" +dependencies = [ + "backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-phragmen" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-phragmen-compact 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-phragmen-compact" +version = "2.0.0" +dependencies = [ + "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-runtime" +version = "2.0.0" +dependencies = [ + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-application-crypto 2.0.0", + "sp-arithmetic 2.0.0", + "sp-core 2.0.0", + "sp-inherents 2.0.0", + "sp-io 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-runtime-interface" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.8.0", + "sp-runtime-interface-proc-macro 2.0.0", + "sp-std 2.0.0", + "sp-wasm-interface 2.0.0", + "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "2.0.0" +dependencies = [ + "Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-staking" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-runtime 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-state-machine" +version = "0.8.0" +dependencies = [ + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-externalities 0.8.0", + "sp-panic-handler 2.0.0", + "sp-trie 2.0.0", + "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-std" +version = "2.0.0" + +[[package]] +name = "sp-storage" +version = "2.0.0" +dependencies = [ + "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-debug-derive 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-timestamp" +version = "2.0.0" +dependencies = [ + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-api 2.0.0", + "sp-inherents 2.0.0", + "sp-runtime 2.0.0", + "sp-std 2.0.0", + "wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-trie" +version = "2.0.0" +dependencies = [ + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memory-db 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0", + "sp-std 2.0.0", + "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-version" +version = "2.0.0" +dependencies = [ + "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-runtime 2.0.0", + "sp-std 2.0.0", +] + +[[package]] +name = "sp-wasm-interface" +version = "2.0.0" +dependencies = [ + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-std 2.0.0", + "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strum" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "strum_macros" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-bip39" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "subtle" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tiny-bip39" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tracing" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing-attributes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tracing-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "trie-db" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "trie-root" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "twox-hash" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "uint" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasm-bindgen" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro-support 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasm-bindgen-webidl" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-timer" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", + "send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-futures 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmi" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmi-validation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "web-sys" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", + "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "weedle" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "zeroize" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "zeroize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zeroize_derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +"checksum ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3" +"checksum aho-corasick 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811" +"checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" +"checksum arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" +"checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +"checksum backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e4036b9bf40f3cf16aba72a3d65e8a520fc4bafcdc7079aea8f848c58c5b5536" +"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" +"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead" +"checksum bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" +"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +"checksum bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" +"checksum byte-slice-cast 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" +"checksum const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" +"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +"checksum curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d" +"checksum curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" +"checksum derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a806e96c59a76a5ba6e18735b6cf833344671e61e7863f2edb5c518ea2cac95c" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)" = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" +"checksum environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "516aa8d7a71cb00a1c4146f0798549b93d083d4f189b3ced8f3de6b8f11ee6c4" +"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum fixed-hash 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3367952ceb191f4ab95dd5685dc163ac539e36202f9fcfd0cb22f9f9c542fefc" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" +"checksum futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" +"checksum futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" +"checksum futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" +"checksum futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" +"checksum futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" +"checksum futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" +"checksum futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" +"checksum futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +"checksum hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" +"checksum hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" +"checksum hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" +"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +"checksum hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" +"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" +"checksum hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" +"checksum impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" +"checksum impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8" +"checksum impl-serde 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bbe9ea9b182f0fb1cabbd61f4ff9b7b7b9197955e95a7e4c27de5055eb29ff8" +"checksum impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" +"checksum integer-sqrt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f65877bf7d44897a473350b1046277941cee20b263397e90869c50b6e766088b" +"checksum js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "7889c7c36282151f6bf465be4700359318aef36baa951462382eae49e9577cf9" +"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" +"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" +"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +"checksum memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978" +"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +"checksum memory-db 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "198831fe8722331a395bc199a5d08efbc197497ef354cb4c77b969c02ffc0fc4" +"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" +"checksum merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0942b357c1b4d0dc43ba724674ec89c3218e6ca2b3e8269e7cb53bcecd2f6e" +"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +"checksum num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +"checksum num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" +"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +"checksum once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37" +"checksum once_cell 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d584f08c2d717d5c23a6414fc2822b71c651560713e54fa7eace675f758a355e" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507" +"checksum parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34e513ff3e406f3ede6796dcdc83d0b32ffb86668cea1ccf7363118abeb00476" +"checksum parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ef1476e40bf8f5c6776e9600983435821ca86eb9819d74a6207cca69d091406a" +"checksum parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" +"checksum parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" +"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" +"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" +"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" +"checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" +"checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" +"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" +"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +"checksum primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e4336f4f5d5524fa60bcbd6fe626f9223d8142a50e7053e979acdf0da41ab975" +"checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" +"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" +"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" +"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" +"checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" +"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +"checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" +"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" +"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" +"checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +"checksum strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22" +"checksum strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" +"checksum substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3be511be555a3633e71739a79e4ddff6a6aaa6579fa6114182a51d72c3eb93c5" +"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" +"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" +"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +"checksum tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c5676413eaeb1ea35300a0224416f57abc3bd251657e0fafc12c47ff98c060" +"checksum tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" +"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +"checksum tracing 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e213bd24252abeb86a0b7060e02df677d367ce6cb772cef17e9214b8390a8d3" +"checksum tracing-attributes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04cfd395def5a60236e187e1ff905cb55668a59f29928dec05e6e1b1fd2ac1f3" +"checksum tracing-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "13a46f11e372b8bd4b4398ea54353412fdd7fd42a8370c7e543e218cf7661978" +"checksum trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de9222c50cc325855621271157c973da27a0dcd26fa06f8edf81020bd2333df0" +"checksum trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "652931506d2c1244d7217a70b99f56718a7b4161b37f04e7cd868072a99f68cd" +"checksum twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" +"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +"checksum wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c" +"checksum wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45" +"checksum wasm-bindgen-futures 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8bbdd49e3e28b40dec6a9ba8d17798245ce32b019513a845369c641b275135d9" +"checksum wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "574094772ce6921576fb6f2e3f7497b8a76273b6db092be18fc48a082de09dc3" +"checksum wasm-bindgen-macro-support 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "e85031354f25eaebe78bb7db1c3d86140312a911a106b2e29f9cc440ce3e7668" +"checksum wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e7e61fc929f4c0dddb748b102ebf9f632e2b8d739f2016542b4de2965a9601" +"checksum wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "ef012a0d93fc0432df126a8eaf547b2dce25a8ce9212e1d3cbeef5c11157975d" +"checksum wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "324c5e65a08699c9c4334ba136597ab22b85dccd4b65dd1e36ccf8f723a95b54" +"checksum wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bf617d864d25af3587aa745529f7aaa541066c876d57e050c0d0c85c61c92aff" +"checksum wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea78c597064ba73596099281e2f4cfc019075122a65cdda3205af94f0b264d93" +"checksum web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf97caf6aa8c2b1dac90faf0db529d9d63c93846cca4911856f78a83cebf53b" +"checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" +"checksum zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" +"checksum zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" diff --git a/frame/staking/fuzzer/Cargo.toml b/frame/staking/fuzzer/Cargo.toml new file mode 100644 index 0000000000000..995ff03951c9a --- /dev/null +++ b/frame/staking/fuzzer/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "pallet-staking-fuzzer" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +pallet-staking = { version = "2.0.0", path = "..", features = ["testing-utils"] } +pallet-staking-reward-curve = { version = "2.0.0", path = "../reward-curve" } +pallet-session = { version = "2.0.0", path = "../../session" } +pallet-indices = { version = "2.0.0", path = "../../indices" } +pallet-balances = { version = "2.0.0", path = "../../balances" } +pallet-timestamp = { version = "2.0.0", path = "../../timestamp" } +frame-system = { version = "2.0.0", path = "../../system" } +frame-support = { version = "2.0.0", path = "../../support" } +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-phragmen = { version = "2.0.0", path = "../../../primitives/phragmen" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +# honggfuzz = "0.5" +honggfuzz = { path = "../../../../honggfuzz-rs"} +rand = "0.7.3" + +[workspace] + +[[bin]] +name = "submit_solution" +path = "src/submit_solution.rs" + diff --git a/frame/staking/fuzzer/src/mock.rs b/frame/staking/fuzzer/src/mock.rs new file mode 100644 index 0000000000000..ff9970fb2b360 --- /dev/null +++ b/frame/staking/fuzzer/src/mock.rs @@ -0,0 +1,195 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Mock file for staking fuzzing. + +use sp_runtime::traits::{Convert, SaturatedConversion}; +use frame_support::{impl_outer_origin, impl_outer_dispatch, parameter_types}; +use frame_support::traits::{Get, EstimateNextSessionChange}; + +type AccountId = u64; +type AccountIndex = u32; +type BlockNumber = u64; +type Balance = u64; + +type System = frame_system::Module; +type Balances = pallet_balances::Module; +type Staking = pallet_staking::Module; +type Indices = pallet_indices::Module; + +impl_outer_origin! { + pub enum Origin for Test where system = frame_system {} +} + +impl_outer_dispatch! { + pub enum Call for Test where origin: Origin { + staking::Staking, + } +} + +pub struct CurrencyToVoteHandler; +impl Convert for CurrencyToVoteHandler { + fn convert(x: u64) -> u64 { + x + } +} +impl Convert for CurrencyToVoteHandler { + fn convert(x: u128) -> u64 { + x.saturated_into() + } +} + +pub struct PeriodicSessionChange

(sp_std::marker::PhantomData

); +impl

EstimateNextSessionChange for PeriodicSessionChange

+where + P: Get, +{ + fn estimate_next_session_change(now: BlockNumber) -> BlockNumber { + let period = P::get(); + let excess = now % period; + now - excess + period + } +} + +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct Test; + +impl frame_system::Trait for Test { + type Origin = Origin; + type Index = AccountIndex; + type BlockNumber = BlockNumber; + type Call = Call; + type Hash = sp_core::H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = Indices; + type Header = sp_runtime::testing::Header; + type Event = (); + type BlockHashCount = (); + type MaximumBlockWeight = (); + type AvailableBlockRatio = (); + type MaximumBlockLength = (); + type Version = (); + type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = (Balances, Staking); +} +parameter_types! { + pub const ExistentialDeposit: Balance = 10; +} +impl pallet_balances::Trait for Test { + type Balance = Balance; + type Event = (); + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; +} +impl pallet_indices::Trait for Test { + type AccountIndex = AccountIndex; + type Event = (); + type Currency = Balances; + type Deposit = (); +} +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} +impl pallet_timestamp::Trait for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; +} +impl pallet_session::historical::Trait for Test { + type FullIdentification = pallet_staking::Exposure; + type FullIdentificationOf = pallet_staking::ExposureOf; +} + +sp_runtime::impl_opaque_keys! { + pub struct SessionKeys { + pub foo: sp_runtime::testing::UintAuthorityId, + } +} + +pub struct TestSessionHandler; +impl pallet_session::SessionHandler for TestSessionHandler { + const KEY_TYPE_IDS: &'static [sp_runtime::KeyTypeId] = &[]; + + fn on_genesis_session(_validators: &[(AccountId, Ks)]) {} + + fn on_new_session( + _: bool, + _: &[(AccountId, Ks)], + _: &[(AccountId, Ks)], + ) {} + + fn on_disabled(_: usize) {} +} + +impl pallet_session::Trait for Test { + type SessionManager = pallet_session::historical::NoteHistoricalRoot; + type Keys = SessionKeys; + type ShouldEndSession = pallet_session::PeriodicSessions<(), ()>; + type SessionHandler = TestSessionHandler; + type Event = (); + type ValidatorId = AccountId; + type ValidatorIdOf = pallet_staking::StashOf; + type DisabledValidatorsThreshold = (); +} +pallet_staking_reward_curve::build! { + const I_NPOS: sp_runtime::curve::PiecewiseLinear<'static> = curve!( + min_inflation: 0_025_000, + max_inflation: 0_100_000, + ideal_stake: 0_500_000, + falloff: 0_050_000, + max_piece_count: 40, + test_precision: 0_005_000, + ); +} +parameter_types! { + pub const RewardCurve: &'static sp_runtime::curve::PiecewiseLinear<'static> = &I_NPOS; +} + +pub type Extrinsic = sp_runtime::testing::TestXt; +type SubmitTransaction = frame_system::offchain::TransactionSubmitter< + sp_runtime::testing::UintAuthorityId, + Test, + Extrinsic, +>; + +impl pallet_staking::Trait for Test { + type Currency = Balances; + type Time = pallet_timestamp::Module; + type CurrencyToVote = CurrencyToVoteHandler; + type RewardRemainder = (); + type Event = (); + type Slash = (); + type Reward = (); + type SessionsPerEra = (); + type SlashDeferDuration = (); + type SlashCancelOrigin = frame_system::EnsureRoot; + type BondingDuration = (); + type SessionInterface = Self; + type RewardCurve = RewardCurve; + type NextSessionChange = PeriodicSessionChange<()>; + type ElectionLookahead = (); + type Call = Call; + type SubmitTransaction = SubmitTransaction; + type KeyType = sp_runtime::testing::UintAuthorityId; +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::default().build_storage::().unwrap().into() +} diff --git a/frame/staking/fuzzer/src/submit_solution.rs b/frame/staking/fuzzer/src/submit_solution.rs new file mode 100644 index 0000000000000..60343a00229c7 --- /dev/null +++ b/frame/staking/fuzzer/src/submit_solution.rs @@ -0,0 +1,135 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Fuzzing for staking pallet. +//! +//! # Running +//! +//! Run with `cargo hfuzz run submit_solution`. `honggfuzz`. +//! +//! # Debugging a panic +//! +//! Once a panic is found, it can be debugged with +//! `cargo hfuzz run-debug submit_solution hfuzz_workspace/reduce/*.fuzz`. + +use honggfuzz::fuzz; + +use mock::Test; +use pallet_staking::testing_utils::{ + self, USER, get_seq_phragmen_solution, get_weak_solution, setup_chain_stakers, + set_validator_count, signed_account, +}; +use frame_support::assert_ok; +use sp_runtime::{traits::Dispatchable, DispatchError}; + +mod mock; + +#[repr(u32)] +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +enum Mode { + /// Initial submission. This will be rather cheap + InitialSubmission, + /// A better submission that will replace the previous ones. This is the most expensive. + StrongerSubmission, + /// A weak submission that will be rejected. This will be rather cheap. + WeakerSubmission, +} + +fn main() { + loop { + fuzz!(|_data: _| { + let mut ext = mock::new_test_ext(); + let mode: Mode = unsafe { std::mem::transmute(testing_utils::random(0, 2)) }; + let num_validators = testing_utils::random(100, 1000); + let num_nominators = testing_utils::random(100, 20_000); + let edge_per_voter = 16; + let do_reduce = true; + let to_elect = testing_utils::random(100, num_validators); + + println!("++ instance with params {} / {} / {} / {:?} / {}", + num_nominators, + num_validators, + edge_per_voter, + mode, + to_elect, + ); + + ext.execute_with(|| { + // initial setup + set_validator_count::(to_elect); + setup_chain_stakers::( + num_validators, + num_nominators, + edge_per_voter, + ); + + // stuff to submit + let (winners, compact, score) = match mode { + Mode::InitialSubmission => { + /* No need to setup anything */ + get_seq_phragmen_solution::(do_reduce) + }, + Mode::StrongerSubmission => { + let (winners, compact, score) = get_weak_solution::(false); + assert_ok!( + >::submit_election_solution( + signed_account::(USER), + winners, + compact, + score, + ) + ); + get_seq_phragmen_solution::(do_reduce) + }, + Mode::WeakerSubmission => { + let (winners, compact, score) = get_seq_phragmen_solution::(do_reduce); + assert_ok!( + >::submit_election_solution( + signed_account::(USER), + winners, + compact, + score, + ) + ); + get_weak_solution::(false) + } + }; + + // must have chosen correct number of winners. + assert_eq!(winners.len() as u32, >::validator_count()); + + let call = pallet_staking::Call::::submit_election_solution( + winners, + compact, + score, + ); + let caller = signed_account::(USER); + + // actually submit + match mode { + Mode::WeakerSubmission => { + assert_eq!( + call.dispatch(caller.into()).unwrap_err(), + DispatchError::Module { index: 0, error: 11, message: Some("PhragmenWeakSubmission") }, + ); + }, + _ => assert_ok!(call.dispatch(caller.into())), + }; + }) + }); + } +} diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 0caeb28878d10..e76a5d8411065 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -250,6 +250,8 @@ mod mock; #[cfg(test)] mod tests; +#[cfg(feature = "testing-utils")] +pub mod testing_utils; pub mod slashing; pub mod offchain_election; @@ -1151,7 +1153,7 @@ decl_module! { /// - O(N * d) to ensure vote veracity. /// # #[weight = SimpleDispatchInfo::FixedNormal(10_000_000)] - fn submit_election_solution( + pub fn submit_election_solution( origin, winners: Vec, compact_assignments: CompactOf, @@ -1169,7 +1171,7 @@ decl_module! { /// Unsigned version of `submit_election_solution`. Will only be accepted from those who are /// in the current validator set. // TODO: weight should be noted. - fn submit_election_solution_unsigned( + pub fn submit_election_solution_unsigned( origin, winners: Vec, compact_assignments: CompactOf, diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 1ececa74434fb..27e05600435fc 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -819,7 +819,6 @@ pub fn horrible_phragmen_with_post_processing( }); }); - dbg!(&staked_assignment); // Ensure that this result is worse than seq-phragmen. Otherwise, it should not have been used // for testing. let score = { diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 8faa16630d951..62b15cab6abef 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -162,7 +162,7 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti /// the chain. /// /// This does a lot of stuff; read the inline comments. -pub(crate) fn prepare_submission( +pub fn prepare_submission( snapshot_nominators: Vec, snapshot_validators: Vec, assignments: Vec>, diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs new file mode 100644 index 0000000000000..1c9dbdafc43f3 --- /dev/null +++ b/frame/staking/src/testing_utils.rs @@ -0,0 +1,324 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Testing utils for staking. Needs the `testing-utils` feature to be enabled. +//! +//! Note that these helpers should NOT be used with the actual crate tests, but are rather designed +//! for when the module is being externally tested (i.e. fuzzing, benchmarking, e2e tests). Enabling +//! this feature in the current crate's Cargo.toml will leak the all of this into a normal release +//! build. Just don't do it. + +use rand::Rng; +use codec::{Encode, Decode}; +use sp_core::hashing::blake2_256; +use sp_phragmen::{reduce, evaluate_support, build_support_map, Assignment, StakedAssignment, PhragmenScore}; +use frame_system::RawOrigin; +use frame_support::{assert_ok}; +use pallet_indices::address::Address; +use crate::*; + +const CTRL_PREFIX: u32 = 1000; +const NOMINATOR_PREFIX: u32 = 1_000_000; + +/// A dummy suer. +pub const USER: u32 = 999_999_999; + +/// Address type of the `T` +pub type AddressOf = Address<::AccountId, u32>; + +/// Random number in the range `[a, b]`. +pub fn random(a: u32, b: u32) -> u32 { + rand::thread_rng().gen_range(a, b) +} + +pub fn set_validator_count(to_elect: u32) { + ValidatorCount::put(to_elect); + MinimumValidatorCount::put(to_elect/2); + >::put(ElectionStatus::Open(T::BlockNumber::from(1u32))); +} + +/// Build an account with the given index. +pub fn account(index: u32) -> T::AccountId { + let entropy = (b"benchmark/staking", index).using_encoded(blake2_256); + T::AccountId::decode(&mut &entropy[..]).unwrap_or_default() +} + +/// Build an address given Index +pub fn address(index: u32) -> AddressOf { + pallet_indices::address::Address::Id(account::(index)) +} + +/// Generate signed origin from `who`. +pub fn signed(who: T::AccountId) -> T::Origin { + RawOrigin::Signed(who).into() +} + +/// Generate signed origin from `index`. +pub fn signed_account(index: u32) -> T::Origin { + signed::(account::(index)) +} + +/// Bond a validator. +pub fn bond_validator(stash: T::AccountId, ctrl: u32, val: BalanceOf) + where T::Lookup: StaticLookup> +{ + let _ = T::Currency::make_free_balance_be(&stash, val); + assert_ok!(>::bond( + signed::(stash), + address::(ctrl), + val, + RewardDestination::Controller + )); + assert_ok!(>::validate( + signed_account::(ctrl), + ValidatorPrefs::default() + )); +} + +pub fn bond_nominator( + stash: T::AccountId, + ctrl: u32, + val: BalanceOf, + target: Vec> +) where T::Lookup: StaticLookup> { + + let _ = T::Currency::make_free_balance_be(&stash, val); + assert_ok!(>::bond( + signed::(stash), + address::(ctrl), + val, + RewardDestination::Controller + )); + assert_ok!(>::nominate(signed_account::(ctrl), target)); +} + +/// Bond `nun_validators` validators and `num_nominator` nominators with `edge_per_voter` random +/// votes per nominator. +pub fn setup_chain_stakers( + num_validators: u32, + num_voters: u32, + edge_per_voter: u32, +) where T::Lookup: StaticLookup> { + (0..num_validators).for_each(|i| { + bond_validator::( + account::(i), + i + CTRL_PREFIX, + >::from(random(1, 1000)) * T::Currency::minimum_balance(), + ); + }); + + (0..num_voters).for_each(|i| { + let mut targets: Vec> = Vec::with_capacity(edge_per_voter as usize); + let mut all_targets = (0..num_validators).map(|t| address::(t)).collect::>(); + assert!(num_validators >= edge_per_voter); + (0..edge_per_voter).for_each(|_| { + let target = all_targets.remove(random(0, all_targets.len() as u32 - 1) as usize); + targets.push(target); + }); + bond_nominator::( + account::(i + NOMINATOR_PREFIX), + i + NOMINATOR_PREFIX + CTRL_PREFIX, + >::from(random(1, 1000)) * T::Currency::minimum_balance(), + targets, + ); + }); + + + >::create_stakers_snapshot(); +} + +/// Build a _really bad_ but acceptable solution for election. This should always yield a solution +/// which has a less score than the seq-phragmen. +pub fn get_weak_solution(do_reduce: bool) +-> (Vec, CompactOf, PhragmenScore) { + use sp_std::collections::btree_map::BTreeMap; + let mut backing_stake_of: BTreeMap> = BTreeMap::new(); + + // self stake + >::enumerate().for_each(|(who, _p)| { + *backing_stake_of.entry(who.clone()).or_insert(Zero::zero()) += + >::slashable_balance_of(&who) + }); + + // add nominator stuff + >::enumerate().for_each(|(who, nomination)| { + nomination.targets.into_iter().for_each(|v| { + *backing_stake_of.entry(v).or_insert(Zero::zero()) += + >::slashable_balance_of(&who) + }) + }); + + + // elect winners + let mut sorted: Vec = backing_stake_of.keys().cloned().collect(); + sorted.sort_by_key(|x| backing_stake_of.get(x).unwrap()); + let winners: Vec = sorted + .iter() + .cloned() + .take(>::validator_count() as usize) + .collect(); + + let mut staked_assignments: Vec> = Vec::new(); + >::enumerate().for_each(|(who, nomination)| { + let mut dist: Vec<(T::AccountId, ExtendedBalance)> = Vec::new(); + nomination.targets.into_iter().for_each(|v| { + if winners.iter().find(|&w| *w == v).is_some() { + dist.push((v, ExtendedBalance::zero())); + } + }); + + if dist.len() == 0 { + return; + } + + // assign real stakes. just split the stake. + let stake = , u64>>::convert( + >::slashable_balance_of(&who), + ) as ExtendedBalance; + + let mut sum: ExtendedBalance = Zero::zero(); + let dist_len = dist.len() as ExtendedBalance; + + // assign main portion + // only take the first half into account. This should highly imbalance stuff, which is good. + dist + .iter_mut() + .take( if dist_len > 1 { (dist_len as usize) / 2 } else { 1 } ) + .for_each(|(_, w)| + { + let partial = stake / dist_len; + *w = partial; + sum += partial; + }); + + // assign the leftover to last. + let leftover = stake - sum; + let last = dist.last_mut().unwrap(); + last.1 += leftover; + + staked_assignments.push(StakedAssignment { + who, + distribution: dist, + }); + }); + + // add self support to winners. + winners.iter().for_each(|w| staked_assignments.push(StakedAssignment { + who: w.clone(), + distribution: vec![( + w.clone(), + , u64>>::convert( + >::slashable_balance_of(&w) + ) as ExtendedBalance, + )] + })); + + if do_reduce { + reduce(&mut staked_assignments); + } + + // helpers for building the compact + let snapshot_validators = >::snapshot_validators().unwrap(); + let snapshot_nominators = >::snapshot_nominators().unwrap(); + + let nominator_index = |a: &T::AccountId| -> Option { + snapshot_nominators.iter().position(|x| x == a).and_then(|i| + >::try_into(i).ok() + ) + }; + let validator_index = |a: &T::AccountId| -> Option { + snapshot_validators.iter().position(|x| x == a).and_then(|i| + >::try_into(i).ok() + ) + }; + let stake_of = |who: &T::AccountId| -> ExtendedBalance { + , u64>>::convert( + >::slashable_balance_of(who) + ) as ExtendedBalance + }; + + // convert back to ratio assignment. This takes less space. + let low_accuracy_assignment: Vec> = staked_assignments + .into_iter() + .map(|sa| sa.into_assignment(true)) + .collect(); + + // re-calculate score based on what the chain will decode. + let score = { + let staked: Vec> = low_accuracy_assignment + .iter() + .map(|a| { + let stake = stake_of(&a.who); + a.clone().into_staked(stake, true) + }).collect(); + + let (support_map, _) = build_support_map::( + winners.as_slice(), + staked.as_slice(), + ); + evaluate_support::(&support_map) + }; + + + // compact encode the assignment. + let compact = >::from_assignment( + low_accuracy_assignment, + nominator_index, + validator_index, + ).unwrap(); + + // winners to index. + let winners = winners.into_iter().map(|w| + snapshot_validators.iter().position(|v| *v == w).unwrap().try_into().unwrap() + ).collect::>(); + + (winners, compact, score) +} + +pub fn get_seq_phragmen_solution(do_reduce: bool) +-> (Vec, CompactOf, PhragmenScore) { + let sp_phragmen::PhragmenResult { + winners, + assignments, + } = >::do_phragmen::().unwrap(); + + let snapshot_validators = >::snapshot_validators().unwrap(); + let snapshot_nominators = >::snapshot_nominators().unwrap(); + + offchain_election::prepare_submission::( + snapshot_nominators, + snapshot_validators, + assignments, + winners, + do_reduce, + ) +} + +pub fn clean() { + >::enumerate().for_each(|(k, _)| { + let ctrl = >::bonded(&k).unwrap(); + >::remove(&k); + >::remove(&k); + >::remove(&k); + >::remove(&ctrl); + }); + >::enumerate().for_each(|(k, _)| >::remove(k)); + >::remove_all(); + >::remove_all(); + >::remove_all(); + >::kill(); + QueuedScore::kill(); +} diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index 9912cafec7a03..4859b15ab866e 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -20,13 +20,15 @@ use serde::{Serialize, Deserialize}; use sp_std::{ops, fmt, prelude::*, convert::TryInto}; use codec::{Encode, Decode, CompactAs}; use crate::{ - traits::{SaturatedConversion, UniqueSaturatedInto, Saturating, BaseArithmetic}, + traits::{SaturatedConversion, UniqueSaturatedInto, Saturating, BaseArithmetic, Bounded}, }; use sp_debug_derive::RuntimeDebug; /// Something that implements a fixed point ration with an arbitrary granularity `X`, as _parts per /// `X`_. -pub trait PerThing: Sized + Saturating + Copy + Default + fmt::Debug { +pub trait PerThing: + Sized + Saturating + Copy + Default + Eq + PartialEq + Ord + PartialOrd + Bounded + fmt::Debug +{ /// The data type used to build this per-thingy. type Inner: BaseArithmetic + Copy + fmt::Debug; diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 0b15f4c90f0db..c8431aeaf9edd 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -163,24 +163,38 @@ 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 ensures that all the potential rounding errors are compensated and the distribution's sum - /// is exactly equal to the total budget. + /// it _tries_ to ensure that all the potential rounding errors are compensated and the + /// distribution's sum is exactly equal to the total budget, by adding or subtracting the + /// remainder from the last distribution. + /// + /// If an edge ratio is [`Bounded::max_value()`], it is dropped. This edge can never mean + /// anything useful. pub fn into_staked(self, stake: ExtendedBalance, fill: bool) -> StakedAssignment where T: sp_std::ops::Mul { let mut sum: ExtendedBalance = Bounded::min_value(); - let mut distribution = self.distribution.into_iter().map(|(target, p)| { - let distribution_stake = p * stake; - // defensive only. We assume that balance cannot exceed extended balance. - sum = sum.saturating_add(distribution_stake); - (target, distribution_stake) + let mut distribution = self.distribution.into_iter().filter_map(|(target, p)| { + // if this ratio is zero, then skip it. + if p == Bounded::min_value() { + None + } else { + let distribution_stake = p * stake; + // defensive only. We assume that balance cannot exceed extended balance. + sum = sum.saturating_add(distribution_stake); + Some((target, distribution_stake)) + } }).collect::>(); if fill { - // if leftover is zero it is effectless. + // NOTE: we can do this better. + // https://revs.runtime-revolution.com/getting-100-with-rounded-percentages-273ffa70252b if let Some(leftover) = stake.checked_sub(sum) { if let Some(last) = distribution.last_mut() { - last.1 += leftover; + last.1 = last.1.saturating_add(leftover); + } + } else if let Some(excess) = sum.checked_sub(stake) { + if let Some(last) = distribution.last_mut() { + last.1 = last.1.saturating_sub(excess); } } else if let Some(excess) = sum.checked_sub(stake) { if let Some(last) = distribution.last_mut() { @@ -188,6 +202,7 @@ impl Assignment } } } + StakedAssignment { who: self.who, distribution, @@ -209,17 +224,33 @@ 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 + /// potentially get used to create a compact type; a compact type requires sum of ratios to be + /// less than 100% upon un-compacting. + /// TODO: isolate this process into something like `normalise_assignments` which makes sure all + /// is okay. + /// + /// If an edge stake is so small that it cannot be represented in `T`, it is ignored. This edge + /// can never be re-created and does not mean anything useful anymore. pub fn into_assignment(self, fill: bool) -> Assignment where ExtendedBalance: From<::Inner> { let accuracy: u128 = T::ACCURACY.saturated_into(); let mut sum: u128 = Zero::zero(); - let stake = self.distribution.iter().map(|x| x.1).sum(); - let mut distribution = self.distribution.into_iter().map(|(target, w)| { + let mut distribution = self.distribution.into_iter().filter_map(|(target, w)| { let per_thing = T::from_rational_approximation(w, stake); - sum += per_thing.clone().deconstruct().saturated_into(); - (target, per_thing) + if per_thing == Bounded::min_value() { + None + } else { + sum += per_thing.clone().deconstruct().saturated_into(); + Some((target, per_thing)) + } }).collect::>(); if fill { From 0afbde3490e52bb64fa72c504563c1ac8ebd631f Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 19 Feb 2020 17:50:28 +0100 Subject: [PATCH 050/106] Some fixes --- bin/node/cli/src/chain_spec.rs | 2 +- frame/staking/src/lib.rs | 49 ++++++++++++++++--------- frame/staking/src/testing_utils.rs | 2 +- primitives/arithmetic/src/per_things.rs | 2 +- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index a4f944e4ad478..2d94cfedfc2b9 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -73,7 +73,7 @@ fn session_keys( authority_discovery: AuthorityDiscoveryId, staking: StakingId, ) -> SessionKeys { - SessionKeys { grandpa, babe, im_online, authority_discovery, staking, } + SessionKeys { grandpa, babe, im_online, authority_discovery, staking } } fn staging_testnet_config_genesis() -> GenesisConfig { diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index e76a5d8411065..bd752e7fc5bbf 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -330,9 +330,10 @@ pub mod sr25519 { app_crypto!(sr25519, STAKING); } - /// Staking key-pair for signing. - #[cfg(feature = "std")] - pub type AuthorityPair = app_sr25519::Pair; + sp_application_crypto::with_pair! { + /// A staking keypair using sr25519 as its crypto. + pub type AuthorityPair = app_sr25519::Pair; + } /// Staking signature using sr25519 as its crypto. pub type AuthoritySignature = app_sr25519::Signature; @@ -1133,26 +1134,31 @@ decl_module! { /// m: size of winner committee. /// n: number of nominators. /// d: edge degree aka MAX_NOMINATIONS = 16 + /// v: number of on-chain validator candidates. /// /// NOTE: given a solution which is reduced, we can enable a new check the ensure `|E| < n + /// m`. /// /// major steps (all done in `check_and_replace_solution`): /// - /// TODO: update this one last time at the end. - /// - 1 read. `PhragmenScore`. O(1). - /// - `m` calls `Validators::contains_key()` for validator veracity. - /// - /// - decode from compact and convert into assignments: O(E) - /// - build_support_map: O(E) - /// - evaluate_support: O(E) - /// - /// - `n` reads from [`Bonded`], [`Ledger`] and [`Nominators`], and `n` calls to - /// `Validators::contains_key()` to ensure nominator veracity. + /// - Storage: O(1) read `ElectionStatus`. + /// - Storage: O(1) read `PhragmenScore`. + /// - Storage: O(1) read `ValidatorCount`. + /// - Storage: O(1) length read from `SnapshotValidators`. + /// - Storage: O(v) reads of `AccountId`. + /// - Memory: O(m) iterations to map winner index to validator id. + /// - Storage: O(n) reads `AccountId`. + /// - Memory: O(n + m) reads to map index to `AccountId` for un-compact. + /// - Storage: O(e) accountid reads from `Nomination` to read correct nominations. + /// - Storage: O(e) calls into `slashable_balance_of_extended` to convert ratio to staked. + /// - Memory: build_support_map. O(e). + /// - Memory: evaluate_support: O(E). + /// - Storage: O(e) writes to `QueuedElected`. + /// - Storage: O(1) write to `QueuedScore` /// - /// - O(N * d) to ensure vote veracity. + /// The weight of this call is 1/10th of the blocks total weight. /// # - #[weight = SimpleDispatchInfo::FixedNormal(10_000_000)] + #[weight = SimpleDispatchInfo::FixedNormal(100_000_000)] pub fn submit_election_solution( origin, winners: Vec, @@ -1170,7 +1176,7 @@ decl_module! { /// Unsigned version of `submit_election_solution`. Will only be accepted from those who are /// in the current validator set. - // TODO: weight should be noted. + #[weight = SimpleDispatchInfo::FixedNormal(100_000_000)] pub fn submit_election_solution_unsigned( origin, winners: Vec, @@ -1619,7 +1625,7 @@ impl Module { era_length >= session_per_era } - /// Dump the list list of validators and nominators into vectors and keep them on-chain. + /// Dump the list of validators and nominators into vectors and keep them on-chain. /// /// This data is used to efficiently evaluate election results. fn create_stakers_snapshot() -> bool { @@ -1815,7 +1821,14 @@ impl Module { let assignments = compact_assignments.into_assignment( nominator_at, validator_at, - ).map_err(|_| Error::::PhragmenBogusCompact)?; + ).map_err(|e| { + debug::native::warn!( + target: "staking", + "un-compacting solution failed due to {:?}", + e, + ); + Error::::PhragmenBogusCompact + })?; // check all nominators actually including the claimed vote. Also check correct self votes. // Note that we assume all validators and nominators in `assignments` are properly bonded, diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index 1c9dbdafc43f3..f3f83e5352410 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -1,4 +1,4 @@ -// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// Copyright 2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index 4859b15ab866e..a548e9192f4ea 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -29,7 +29,7 @@ use sp_debug_derive::RuntimeDebug; pub trait PerThing: Sized + Saturating + Copy + Default + Eq + PartialEq + Ord + PartialOrd + Bounded + fmt::Debug { -/// The data type used to build this per-thingy. + /// The data type used to build this per-thingy. type Inner: BaseArithmetic + Copy + fmt::Debug; /// The data type that is used to store values bigger than the maximum of this type. This must From b8efef43ca8a1401f3833b355a7e9b76d294c93d Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 20 Feb 2020 09:56:36 +0100 Subject: [PATCH 051/106] More docs --- frame/staking/fuzzer/Cargo.lock | 94 +++++++-------------- frame/staking/fuzzer/src/mock.rs | 4 - frame/staking/fuzzer/src/submit_solution.rs | 18 ++-- frame/staking/src/testing_utils.rs | 4 + primitives/phragmen/fuzzer/Cargo.lock | 68 ++++++--------- primitives/phragmen/fuzzer/Cargo.toml | 3 +- 6 files changed, 72 insertions(+), 119 deletions(-) diff --git a/frame/staking/fuzzer/Cargo.lock b/frame/staking/fuzzer/Cargo.lock index 421f926cb09a6..f784cd078a3a4 100644 --- a/frame/staking/fuzzer/Cargo.lock +++ b/frame/staking/fuzzer/Cargo.lock @@ -317,6 +317,16 @@ dependencies = [ "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "frame-benchmarking" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-api 2.0.0", + "sp-runtime-interface 2.0.0", + "sp-std 2.0.0", +] + [[package]] name = "frame-metadata" version = "11.0.0" @@ -336,7 +346,7 @@ dependencies = [ "frame-support-procedural 2.0.0", "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", @@ -513,15 +523,6 @@ dependencies = [ "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "hashbrown" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "hashbrown" version = "0.6.3" @@ -649,14 +650,6 @@ dependencies = [ "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "lock_api" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "lock_api" version = "0.3.3" @@ -773,17 +766,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "0.1.8" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "once_cell" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "opaque-debug" version = "0.2.3" @@ -809,6 +797,7 @@ dependencies = [ name = "pallet-balances" version = "2.0.0" dependencies = [ + "frame-benchmarking 2.0.0", "frame-support 2.0.0", "frame-system 2.0.0", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -907,6 +896,7 @@ dependencies = [ name = "pallet-timestamp" version = "2.0.0" dependencies = [ + "frame-benchmarking 2.0.0", "frame-support 2.0.0", "frame-system 2.0.0", "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -970,15 +960,6 @@ name = "parity-wasm" version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "parking_lot" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "parking_lot" version = "0.9.0" @@ -998,18 +979,6 @@ dependencies = [ "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "parking_lot_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "parking_lot_core" version = "0.6.2" @@ -1293,6 +1262,11 @@ name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -1322,11 +1296,6 @@ dependencies = [ "zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "scopeguard" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "scopeguard" version = "1.1.0" @@ -1471,7 +1440,7 @@ dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-serde 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1492,7 +1461,7 @@ dependencies = [ "sp-std 2.0.0", "sp-storage 2.0.0", "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1792,15 +1761,15 @@ dependencies = [ [[package]] name = "tiny-bip39" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2143,7 +2112,6 @@ dependencies = [ "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" "checksum hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" -"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" "checksum hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" @@ -2159,7 +2127,6 @@ dependencies = [ "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" -"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" @@ -2174,8 +2141,7 @@ dependencies = [ "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" "checksum num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -"checksum once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37" -"checksum once_cell 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d584f08c2d717d5c23a6414fc2822b71c651560713e54fa7eace675f758a355e" +"checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507" "checksum parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34e513ff3e406f3ede6796dcdc83d0b32ffb86668cea1ccf7363118abeb00476" @@ -2183,9 +2149,7 @@ dependencies = [ "checksum parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" "checksum parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" "checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" -"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" "checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" @@ -2218,10 +2182,10 @@ dependencies = [ "checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" "checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" "checksum rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" -"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" @@ -2242,7 +2206,7 @@ dependencies = [ "checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -"checksum tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c5676413eaeb1ea35300a0224416f57abc3bd251657e0fafc12c47ff98c060" +"checksum tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1cd1fb03fe8e07d17cd851a624a9fff74642a997b67fbd1ccd77533241640d92" "checksum tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" "checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" "checksum tracing 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e213bd24252abeb86a0b7060e02df677d367ce6cb772cef17e9214b8390a8d3" diff --git a/frame/staking/fuzzer/src/mock.rs b/frame/staking/fuzzer/src/mock.rs index ff9970fb2b360..523088c24eb00 100644 --- a/frame/staking/fuzzer/src/mock.rs +++ b/frame/staking/fuzzer/src/mock.rs @@ -189,7 +189,3 @@ impl pallet_staking::Trait for Test { type SubmitTransaction = SubmitTransaction; type KeyType = sp_runtime::testing::UintAuthorityId; } - -pub fn new_test_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::default().build_storage::().unwrap().into() -} diff --git a/frame/staking/fuzzer/src/submit_solution.rs b/frame/staking/fuzzer/src/submit_solution.rs index 60343a00229c7..e10e0c2a61ecd 100644 --- a/frame/staking/fuzzer/src/submit_solution.rs +++ b/frame/staking/fuzzer/src/submit_solution.rs @@ -23,7 +23,7 @@ //! # Debugging a panic //! //! Once a panic is found, it can be debugged with -//! `cargo hfuzz run-debug submit_solution hfuzz_workspace/reduce/*.fuzz`. +//! `cargo hfuzz run-debug submit_solution hfuzz_workspace/submit_solution/*.fuzz`. use honggfuzz::fuzz; @@ -49,15 +49,18 @@ enum Mode { WeakerSubmission, } +pub fn new_test_ext() -> Result { + frame_system::GenesisConfig::default().build_storage::().map(Into::into) +} + fn main() { loop { - fuzz!(|_data: _| { - let mut ext = mock::new_test_ext(); + fuzz!(|do_reduce: bool| { + let ext = new_test_ext(); let mode: Mode = unsafe { std::mem::transmute(testing_utils::random(0, 2)) }; - let num_validators = testing_utils::random(100, 1000); - let num_nominators = testing_utils::random(100, 20_000); + let num_validators = testing_utils::random(100, 2_000); + let num_nominators = testing_utils::random(100, 2_000); let edge_per_voter = 16; - let do_reduce = true; let to_elect = testing_utils::random(100, num_validators); println!("++ instance with params {} / {} / {} / {:?} / {}", @@ -68,7 +71,7 @@ fn main() { to_elect, ); - ext.execute_with(|| { + ext.unwrap_or_default().execute_with(|| { // initial setup set_validator_count::(to_elect); setup_chain_stakers::( @@ -112,6 +115,7 @@ fn main() { // must have chosen correct number of winners. assert_eq!(winners.len() as u32, >::validator_count()); + // final call and origin let call = pallet_staking::Call::::submit_election_solution( winners, compact, diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index f3f83e5352410..c7ae18431e324 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -44,6 +44,7 @@ pub fn random(a: u32, b: u32) -> u32 { rand::thread_rng().gen_range(a, b) } +/// Set the desired validator count, with related storage items. pub fn set_validator_count(to_elect: u32) { ValidatorCount::put(to_elect); MinimumValidatorCount::put(to_elect/2); @@ -288,6 +289,8 @@ pub fn get_weak_solution(do_reduce: bool) (winners, compact, score) } +/// Create a solution for seq-phragmen. This uses the same internal function as used by the offchain +/// worker code. pub fn get_seq_phragmen_solution(do_reduce: bool) -> (Vec, CompactOf, PhragmenScore) { let sp_phragmen::PhragmenResult { @@ -307,6 +310,7 @@ pub fn get_seq_phragmen_solution(do_reduce: bool) ) } +/// Remove all validator, nominators, votes and exposures. pub fn clean() { >::enumerate().for_each(|(k, _)| { let ctrl = >::bonded(&k).unwrap(); diff --git a/primitives/phragmen/fuzzer/Cargo.lock b/primitives/phragmen/fuzzer/Cargo.lock index 921dc910cb966..09303dde83c57 100644 --- a/primitives/phragmen/fuzzer/Cargo.lock +++ b/primitives/phragmen/fuzzer/Cargo.lock @@ -338,15 +338,6 @@ dependencies = [ "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "hashbrown" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "hashbrown" version = "0.6.3" @@ -458,14 +449,6 @@ dependencies = [ "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "lock_api" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "lock_api" version = "0.3.3" @@ -573,10 +556,10 @@ dependencies = [ [[package]] name = "once_cell" -version = "0.1.8" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -637,11 +620,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "parking_lot" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -655,11 +639,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.4.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -924,6 +910,11 @@ name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -953,11 +944,6 @@ dependencies = [ "zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "scopeguard" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "scopeguard" version = "1.0.0" @@ -1052,7 +1038,7 @@ dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-serde 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1073,7 +1059,7 @@ dependencies = [ "sp-std 2.0.0", "sp-storage 2.0.0", "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1320,15 +1306,15 @@ dependencies = [ [[package]] name = "tiny-bip39" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1512,7 +1498,6 @@ dependencies = [ "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" "checksum hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" -"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" "checksum hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" "checksum hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e" "checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" @@ -1526,7 +1511,6 @@ dependencies = [ "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" -"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" @@ -1540,7 +1524,7 @@ dependencies = [ "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" "checksum num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -"checksum once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37" +"checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507" "checksum parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34e513ff3e406f3ede6796dcdc83d0b32ffb86668cea1ccf7363118abeb00476" @@ -1548,8 +1532,8 @@ dependencies = [ "checksum parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" "checksum parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" "checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" -"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" -"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" "checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" "checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" @@ -1579,10 +1563,10 @@ dependencies = [ "checksum regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" "checksum regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" "checksum rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" -"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" @@ -1598,7 +1582,7 @@ dependencies = [ "checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -"checksum tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c5676413eaeb1ea35300a0224416f57abc3bd251657e0fafc12c47ff98c060" +"checksum tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1cd1fb03fe8e07d17cd851a624a9fff74642a997b67fbd1ccd77533241640d92" "checksum tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" "checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" "checksum trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de9222c50cc325855621271157c973da27a0dcd26fa06f8edf81020bd2333df0" diff --git a/primitives/phragmen/fuzzer/Cargo.toml b/primitives/phragmen/fuzzer/Cargo.toml index b0bb5671d36a5..906324cb8e5dd 100644 --- a/primitives/phragmen/fuzzer/Cargo.toml +++ b/primitives/phragmen/fuzzer/Cargo.toml @@ -6,7 +6,8 @@ edition = "2018" [dependencies] sp-phragmen = { version = "2.0.0", path = ".." } -honggfuzz = "0.5" +#honggfuzz = "0.5" +honggfuzz = { path = "../../../../honggfuzz-rs" } rand = "0.7.3" [workspace] From e72d642da6d6e97a12a5bfda423ba15c20fa0911 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 20 Feb 2020 14:32:34 +0100 Subject: [PATCH 052/106] Remove unused generics --- frame/staking/src/lib.rs | 13 +++--- frame/staking/src/mock.rs | 8 ++-- frame/staking/src/offchain_election.rs | 12 +++--- frame/staking/src/testing_utils.rs | 6 +-- primitives/phragmen/compact/src/assignment.rs | 9 ++-- primitives/phragmen/compact/src/lib.rs | 42 ++++++++++--------- primitives/phragmen/compact/src/staked.rs | 11 ++--- primitives/phragmen/src/lib.rs | 4 +- primitives/phragmen/src/tests.rs | 24 +++++------ 9 files changed, 65 insertions(+), 64 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 471c5ad5fcdc8..919ab8de5fcf5 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -631,11 +631,10 @@ impl Default for ElectionStatus { pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; -pub type CompactOf = CompactAssignments< +pub type Compact = CompactAssignments< NominatorIndex, ValidatorIndex, OffchainAccuracy, - ::AccountId, >; type PositiveImbalanceOf = @@ -1162,7 +1161,7 @@ decl_module! { pub fn submit_election_solution( origin, winners: Vec, - compact_assignments: CompactOf, + compact_assignments: Compact, score: PhragmenScore, ) { let _who = ensure_signed(origin)?; @@ -1180,7 +1179,7 @@ decl_module! { pub fn submit_election_solution_unsigned( origin, winners: Vec, - compact_assignments: CompactOf, + compact_assignments: Compact, score: PhragmenScore, // already used and checked in ValidateUnsigned. _validator_index: u32, @@ -1768,7 +1767,7 @@ impl Module { /// of the next round. This may be called by both a signed and an unsigned transaction. fn check_and_replace_solution( winners: Vec, - compact_assignments: CompactOf, + compact_assignments: Compact, compute: ElectionCompute, claimed_score: PhragmenScore, ) -> Result<(), Error> { @@ -2591,7 +2590,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { validator_index, signature, ) = call { - use offchain_election::SignaturePayloadOf; + use offchain_election::SignaturePayload; // discard early solution if Self::era_election_status() == ElectionStatus::Close { @@ -2614,7 +2613,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { } // check signature - let payload: SignaturePayloadOf = ( + let payload: SignaturePayload = ( winners, compact, score, diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 27e05600435fc..610d432c0b3ac 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -750,7 +750,7 @@ pub fn on_offence_now( pub fn horrible_phragmen_with_post_processing( do_reduce: bool, ) -> ( - CompactOf, + Compact, Vec, PhragmenScore, ) { @@ -851,7 +851,7 @@ pub fn horrible_phragmen_with_post_processing( OffchainAccuracy, >(staked_assignment); - let compact = >::from_assignment( + let compact = Compact::from_assignment( assignments_reduced, nominator_index, validator_index, @@ -869,7 +869,7 @@ pub fn do_phragmen_with_post_processing( do_reduce: bool, tweak: impl FnOnce(&mut Vec>), ) -> ( - CompactOf, + Compact, Vec, PhragmenScore, ) { @@ -920,7 +920,7 @@ pub fn do_phragmen_with_post_processing( evaluate_support::(&support_map) }; - let compact = >::from_assignment( + let compact = Compact::from_assignment( assignments_reduced, nominator_index, validator_index, diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 62b15cab6abef..87176676e339c 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -17,7 +17,7 @@ //! Helpers for offchain worker election. use crate::{ - Call, Module, Trait, ValidatorIndex, NominatorIndex, CompactOf, OffchainAccuracy, + Call, Module, Trait, ValidatorIndex, NominatorIndex, Compact, OffchainAccuracy, }; use codec::Encode; use frame_system::offchain::{SubmitUnsignedTransaction}; @@ -48,9 +48,9 @@ pub(crate) enum OffchainElectionError { } /// The type of signature data encoded with the unsigned submission -pub(crate) type SignaturePayloadOf<'a, T> = ( +pub(crate) type SignaturePayload<'a> = ( &'a [ValidatorIndex], - &'a CompactOf, + &'a Compact, &'a PhragmenScore, &'a u32, ); @@ -130,7 +130,7 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti ); // sign it. - let signature_payload: SignaturePayloadOf = + let signature_payload: SignaturePayload = (&winners, &compact, &score, &(index as u32)); let signature = pubkey.sign(&signature_payload.encode()) .ok_or(OffchainElectionError::SigningFailed)?; @@ -168,7 +168,7 @@ pub fn prepare_submission( assignments: Vec>, winners: Vec<(T::AccountId, ExtendedBalance)>, do_reduce: bool, -) -> (Vec, CompactOf, PhragmenScore) +) -> (Vec, Compact, PhragmenScore) where ExtendedBalance: From<::Inner>, { @@ -222,7 +222,7 @@ where }; // compact encode the assignment. - let compact = >::from_assignment( + let compact = Compact::from_assignment( low_accuracy_assignment, nominator_index, validator_index, diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index c7ae18431e324..0a01b59410d24 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -144,7 +144,7 @@ pub fn setup_chain_stakers( /// Build a _really bad_ but acceptable solution for election. This should always yield a solution /// which has a less score than the seq-phragmen. pub fn get_weak_solution(do_reduce: bool) --> (Vec, CompactOf, PhragmenScore) { +-> (Vec, Compact, PhragmenScore) { use sp_std::collections::btree_map::BTreeMap; let mut backing_stake_of: BTreeMap> = BTreeMap::new(); @@ -275,7 +275,7 @@ pub fn get_weak_solution(do_reduce: bool) // compact encode the assignment. - let compact = >::from_assignment( + let compact = Compact::from_assignment( low_accuracy_assignment, nominator_index, validator_index, @@ -292,7 +292,7 @@ pub fn get_weak_solution(do_reduce: bool) /// Create a solution for seq-phragmen. This uses the same internal function as used by the offchain /// worker code. pub fn get_seq_phragmen_solution(do_reduce: bool) --> (Vec, CompactOf, PhragmenScore) { +-> (Vec, Compact, PhragmenScore) { let sp_phragmen::PhragmenResult { winners, assignments, diff --git a/primitives/phragmen/compact/src/assignment.rs b/primitives/phragmen/compact/src/assignment.rs index b81c807dec25f..587e482ccb220 100644 --- a/primitives/phragmen/compact/src/assignment.rs +++ b/primitives/phragmen/compact/src/assignment.rs @@ -162,14 +162,13 @@ pub(crate) fn assignment( impl< #voter_type: _phragmen::codec::Codec + Default + Copy, #target_type: _phragmen::codec::Codec + Default + Copy, - A: _phragmen::codec::Codec + Default + Clone, Accuracy: _phragmen::codec::Codec + Default + Clone + _phragmen::sp_runtime::PerThing + PartialOrd, > - #ident<#voter_type, #target_type, Accuracy, A> + #ident<#voter_type, #target_type, Accuracy> { - pub fn from_assignment( + pub fn from_assignment( assignments: Vec<_phragmen::Assignment>, index_of_voter: FV, index_of_target: FT, @@ -177,12 +176,12 @@ pub(crate) fn assignment( where for<'r> FV: Fn(&'r A) -> Option<#voter_type>, for<'r> FT: Fn(&'r A) -> Option<#target_type>, + A: _phragmen::IdentifierT, { let mut compact: #ident< #voter_type, #target_type, Accuracy, - A, > = Default::default(); for _phragmen::Assignment { who, distribution } in assignments { @@ -197,7 +196,7 @@ pub(crate) fn assignment( Ok(compact) } - pub fn into_assignment( + pub fn into_assignment( self, voter_at: impl Fn(#voter_type) -> Option, target_at: impl Fn(#target_type) -> Option, diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index cc3eac63b9e8b..724c07eedc085 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -33,30 +33,16 @@ const PREFIX: &'static str = "votes"; /// Generates a struct to store the phragmen assignments in a compact way. The struct can only store /// distributions up to the given input count. The given count must be greater than 2. /// -/// 3 generic types must be given to the type -/// -/// - `V`: identifier/index type of the voter. -/// - `T`: identifier/index type of the target. -/// - `W`: any type used as the edge weight. -/// /// ```nocompile /// // generate a struct with nominator and edge weight u128, with maximum supported /// // edge per voter of 32. -/// generate_compact_solution_type(TestCompact, 32) +/// generate_compact_solution_type(pub TestCompact, 32) /// ``` /// -/// The generated structure creates one key for each possible count of distributions from 1 up to -/// the given length. A normal distribution is a tuple of `(candidate, weight)`. Typically, the -/// weight can refer to either the ratio of the voter's support or its absolute value. The following -/// rules hold regarding the compact representation: -/// - For single distribution, no weight is stored. The weight is known to be 100%. -/// - For all the rest, the weight if the last distribution is omitted. This value can be computed -/// from the rest. -/// -/// An example expansion of length 16 is as follows: +/// This generates: /// -/// ```nocompile -/// struct TestCompact { +/// ```ignore +/// pub struct TestCompact { /// votes1: Vec<(V, T)>, /// votes2: Vec<(V, (T, W), T)>, /// votes3: Vec<(V, [(T, W); 2usize], T)>, @@ -75,6 +61,22 @@ const PREFIX: &'static str = "votes"; /// votes16: Vec<(V, [(T, W); 15usize], T)>, /// } /// ``` +/// +/// The generic arguments are: +/// - `V`: identifier/index for voter (nominator) types. +/// - `T` identifier/index for candidate (validator) types. +/// - `W` weight type. +/// +/// Some conversion implementations are provided by default if +/// - `W` is u128, or +/// - `W` is anything that implements `PerThing` (such as `Perbill`) +/// +/// The ideas behind the structure are as follows: +/// +/// - For single distribution, no weight is stored. The weight is known to be 100%. +/// - For all the rest, the weight if the last distribution is omitted. This value can be computed +/// from the rest. +/// #[proc_macro] pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream { let CompactSolutionDef { @@ -168,8 +170,8 @@ fn struct_def( _phragmen::codec::Encode, _phragmen::codec::Decode, )] - #vis struct #ident<#voter_type, #target_type, #weight_type, A> { - _marker: sp_std::marker::PhantomData, + #vis struct #ident<#voter_type, #target_type, #weight_type> { + // _marker: sp_std::marker::PhantomData, #singles #doubles #rest diff --git a/primitives/phragmen/compact/src/staked.rs b/primitives/phragmen/compact/src/staked.rs index 46b1d6bf7aada..a7cf853f17086 100644 --- a/primitives/phragmen/compact/src/staked.rs +++ b/primitives/phragmen/compact/src/staked.rs @@ -158,12 +158,11 @@ pub(crate) fn staked( impl< #voter_type: _phragmen::codec::Codec + Default + Copy, #target_type: _phragmen::codec::Codec + Default + Copy, - A: _phragmen::codec::Codec + Default + Clone, > - #ident<#voter_type, #target_type, u128, A> + #ident<#voter_type, #target_type, u128> { /// Generate self from a vector of `StakedAssignment`. - pub fn from_staked( + pub fn from_staked( assignments: Vec<_phragmen::StakedAssignment>, index_of_voter: FV, index_of_target: FT, @@ -171,8 +170,9 @@ pub(crate) fn staked( where for<'r> FV: Fn(&'r A) -> Option<#voter_type>, for<'r> FT: Fn(&'r A) -> Option<#target_type>, + A: _phragmen::IdentifierT { - let mut compact: #ident<#voter_type, #target_type, u128, A> = Default::default(); + let mut compact: #ident<#voter_type, #target_type, u128> = Default::default(); for _phragmen::StakedAssignment { who, distribution } in assignments { match distribution.len() { 0 => continue, @@ -188,7 +188,7 @@ pub(crate) fn staked( /// Convert self into `StakedAssignment`. The given function should return the total /// weight of a voter. It is used to subtract the sum of all the encoded weights to /// infer the last one. - pub fn into_staked( + pub fn into_staked( self, max_of: FM, voter_at: impl Fn(#voter_type) -> Option, @@ -197,6 +197,7 @@ pub(crate) fn staked( -> Result>, _phragmen::Error> where for<'r> FM: Fn(&'r A) -> u128, + A: _phragmen::IdentifierT, { let mut assignments: Vec<_phragmen::StakedAssignment> = Default::default(); #into_impl diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 43533fd552361..8e4cfec3f848e 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -67,9 +67,9 @@ pub use sp_phragmen_compact::generate_compact_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 {} +pub trait IdentifierT: Clone + Eq + Default + Ord + Debug + codec::Codec {} -impl IdentifierT for T {} +impl IdentifierT for T {} /// The errors that might occur in the this crate and compact. #[derive(Debug, Eq, PartialEq)] diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index 59a94ad51bafc..f3c257b54dfea 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -600,7 +600,7 @@ mod compact { #[test] fn compact_struct_is_codec() { - let compact = TestCompact::<_, _, _, u64> { + let compact = TestCompact::<_, _, _> { votes1: vec![(2u64, 20), (4, 40)], votes2: vec![ (1, (10, Accuracy::from_percent(80)), 11), @@ -684,7 +684,7 @@ mod compact { targets.iter().position(|x| x == a).map(TryInto::try_into).unwrap().ok() }; - let compacted = >::from_assignment( + let compacted = >::from_assignment( assignments.clone(), voter_index, target_index, @@ -782,7 +782,7 @@ mod compact { targets.iter().position(|x| x == a).map(TryInto::try_into).unwrap().ok() }; - let compacted = >::from_staked( + let compacted = >::from_staked( assignments.clone(), voter_index, target_index, @@ -821,7 +821,7 @@ mod compact { fn compact_into_stake_must_report_overflow() { // The last edge which is computed from the rest should ALWAYS be positive. // in votes2 - let compact = TestCompact:: { + let compact = TestCompact:: { votes1: Default::default(), votes2: vec![(0, (1, 10), 2)], ..Default::default() @@ -836,7 +836,7 @@ mod compact { ); // in votes3 onwards - let compact = TestCompact:: { + let compact = TestCompact:: { votes1: Default::default(), votes2: Default::default(), votes3: vec![(0, [(1, 7), (2, 8)], 3)], @@ -849,7 +849,7 @@ mod compact { ); // Also if equal - let compact = TestCompact:: { + let compact = TestCompact:: { votes1: Default::default(), votes2: Default::default(), // 5 is total, we cannot leave none for 30 here. @@ -866,7 +866,7 @@ mod compact { #[test] fn compact_into_assignment_must_report_overflow() { // in votes2 - let compact = TestCompact:: { + let compact = TestCompact:: { votes1: Default::default(), votes2: vec![(0, (1, Accuracy::from_percent(100)), 2)], ..Default::default() @@ -880,7 +880,7 @@ mod compact { ); // in votes3 onwards - let compact = TestCompact:: { + let compact = TestCompact:: { votes1: Default::default(), votes2: Default::default(), votes3: vec![(0, [(1, Accuracy::from_percent(70)), (2, Accuracy::from_percent(80))], 3)], @@ -904,7 +904,7 @@ mod compact { let entity_index = |a: &AccountId| -> Option { Some(*a as u16) }; - let compacted = >::from_staked( + let compacted = >::from_staked( assignments.clone(), entity_index, entity_index, @@ -919,7 +919,7 @@ mod compact { }, ]; - let compacted = >::from_staked( + let compacted = >::from_staked( assignments.clone(), entity_index, entity_index, @@ -937,7 +937,7 @@ mod compact { }, ]; - let compacted = >::from_assignment( + let compacted = >::from_assignment( assignments.clone(), entity_index, entity_index, @@ -972,7 +972,7 @@ mod compact { targets.iter().position(|x| x == a).map(TryInto::try_into).unwrap().ok() }; - let compacted = >::from_staked( + let compacted = >::from_staked( assignments.clone(), voter_index, target_index, From 9f227f9ab0c6f84f4c16286ba5d2a4b09a6cdaaf Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 20 Feb 2020 14:46:53 +0100 Subject: [PATCH 053/106] Remove max-nominator footgun --- frame/staking/src/lib.rs | 45 ++++++++++++++------------ primitives/phragmen/compact/src/lib.rs | 8 ++++- primitives/phragmen/src/lib.rs | 9 ++++-- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 919ab8de5fcf5..db498b3a74a7c 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -292,23 +292,23 @@ use frame_system::{ }; use sp_phragmen::{ ExtendedBalance, Assignment, PhragmenScore, PhragmenResult, build_support_map, evaluate_support, - elect, generate_compact_solution_type, is_score_better, + elect, generate_compact_solution_type, is_score_better, VotingLimit, }; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; -pub(crate) const MAX_NOMINATIONS: usize = 16; const MAX_UNLOCKING_CHUNKS: usize = 32; const STAKING_ID: LockIdentifier = *b"staking "; -// indexable by 2 bytes +/// Maximum number of stakers that can be stored in a snapshot. pub(crate) const MAX_VALIDATORS: u32 = ValidatorIndex::max_value() as u32; pub(crate) const MAX_NOMINATORS: u32 = NominatorIndex::max_value(); -// TODO: get rid of 16 +// Note: Maximum nomination limit is set here -- 16. generate_compact_solution_type!(pub CompactAssignments, 16); /// Data type used to index nominators in the compact type pub type NominatorIndex = u32; + /// Data type used to index validators in the compact type. pub type ValidatorIndex = u16; @@ -324,6 +324,23 @@ pub type ChainAccuracy = Perbill; /// Accuracy used for off-chain phragmen. This better be small. pub type OffchainAccuracy = sp_runtime::PerU16; +/// The balance type of this module. +pub type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + +/// The compact type for election solutions. +pub type Compact = CompactAssignments< + NominatorIndex, + ValidatorIndex, + OffchainAccuracy, +>; + +type PositiveImbalanceOf = + <::Currency as Currency<::AccountId>>::PositiveImbalance; +type NegativeImbalanceOf = + <::Currency as Currency<::AccountId>>::NegativeImbalance; +type MomentOf = <::Time as Time>::Moment; + pub mod sr25519 { mod app_sr25519 { use sp_application_crypto::{app_crypto, key_types::STAKING, sr25519}; @@ -629,20 +646,6 @@ impl Default for ElectionStatus { } } -pub type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; -pub type Compact = CompactAssignments< - NominatorIndex, - ValidatorIndex, - OffchainAccuracy, ->; - -type PositiveImbalanceOf = - <::Currency as Currency<::AccountId>>::PositiveImbalance; -type NegativeImbalanceOf = - <::Currency as Currency<::AccountId>>::NegativeImbalance; -type MomentOf = <::Time as Time>::Moment; - /// Means for interacting with a specialized version of the `session` trait. /// /// This is needed because `Staking` sets the `ValidatorIdOf` of the `pallet_session::Trait` @@ -1132,7 +1135,7 @@ decl_module! { /// E: number of edges. /// m: size of winner committee. /// n: number of nominators. - /// d: edge degree aka MAX_NOMINATIONS = 16 + /// d: edge degree (16 for now) /// v: number of on-chain validator candidates. /// /// NOTE: given a solution which is reduced, we can enable a new check the ensure `|E| < n + @@ -1390,7 +1393,7 @@ decl_module! { /// /// # /// - The transaction's complexity is proportional to the size of `targets`, - /// which is capped at `MAX_NOMINATIONS`. + /// which is capped at Compact::LIMIT. /// - Both the reads and writes follow a similar pattern. /// # #[weight = SimpleDispatchInfo::FixedNormal(750_000)] @@ -1402,7 +1405,7 @@ decl_module! { let stash = &ledger.stash; ensure!(!targets.is_empty(), Error::::EmptyTargets); let targets = targets.into_iter() - .take(MAX_NOMINATIONS) + .take(::LIMIT) .map(|t| T::Lookup::lookup(t)) .collect::, _>>()?; diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index 724c07eedc085..1d6ce46bcc4c0 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -33,7 +33,7 @@ const PREFIX: &'static str = "votes"; /// Generates a struct to store the phragmen assignments in a compact way. The struct can only store /// distributions up to the given input count. The given count must be greater than 2. /// -/// ```nocompile +/// ```ignore /// // generate a struct with nominator and edge weight u128, with maximum supported /// // edge per voter of 32. /// generate_compact_solution_type(pub TestCompact, 32) @@ -176,6 +176,12 @@ fn struct_def( #doubles #rest } + + impl<#voter_type, #target_type, #weight_type> _phragmen::VotingLimit + for #ident<#voter_type, #target_type, #weight_type> + { + const LIMIT: usize = #count; + } )) } diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 8e4cfec3f848e..764aeff7b3178 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -65,8 +65,13 @@ pub use sp_runtime; // re-export the compact solution type. pub use sp_phragmen_compact::generate_compact_solution_type; -// an aggregator trait for a generic type of a voter/target identifier. This usually maps to -// substrate's account id. +/// A trait to limit the number of votes per voter. The generated compact type will implement this. +pub trait VotingLimit { + const LIMIT: usize; +} + +/// 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 {} From 9116285e7d5d408048e9d4c1edafbd26c8024454 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 21 Feb 2020 10:03:32 +0100 Subject: [PATCH 054/106] Better fuzzer --- frame/session/src/lib.rs | 1 + frame/staking/fuzz/.gitignore | 4 + frame/staking/{fuzzer => fuzz}/Cargo.lock | 39 ++--- frame/staking/{fuzzer => fuzz}/Cargo.toml | 19 ++- .../{fuzzer/src => fuzz/fuzz_targets}/mock.rs | 0 .../fuzz/fuzz_targets/submit_solution.rs | 130 ++++++++++++++++ frame/staking/fuzzer/.gitignore | 3 - frame/staking/fuzzer/src/submit_solution.rs | 139 ------------------ frame/staking/src/testing_utils.rs | 2 + primitives/phragmen/src/helpers.rs | 2 +- 10 files changed, 165 insertions(+), 174 deletions(-) create mode 100644 frame/staking/fuzz/.gitignore rename frame/staking/{fuzzer => fuzz}/Cargo.lock (99%) rename frame/staking/{fuzzer => fuzz}/Cargo.toml (80%) rename frame/staking/{fuzzer/src => fuzz/fuzz_targets}/mock.rs (100%) create mode 100644 frame/staking/fuzz/fuzz_targets/submit_solution.rs delete mode 100644 frame/staking/fuzzer/.gitignore delete mode 100644 frame/staking/fuzzer/src/submit_solution.rs diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index fe97606eacc17..0abe06527b391 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -393,6 +393,7 @@ decl_storage! { >::load_keys(&who).is_none(), "genesis config contained duplicate validator {:?}", who, ); + >::do_set_keys(&who, keys) .expect("genesis config must not contain duplicates; qed"); } diff --git a/frame/staking/fuzz/.gitignore b/frame/staking/fuzz/.gitignore new file mode 100644 index 0000000000000..572e03bdf321b --- /dev/null +++ b/frame/staking/fuzz/.gitignore @@ -0,0 +1,4 @@ + +target +corpus +artifacts diff --git a/frame/staking/fuzzer/Cargo.lock b/frame/staking/fuzz/Cargo.lock similarity index 99% rename from frame/staking/fuzzer/Cargo.lock rename to frame/staking/fuzz/Cargo.lock index f784cd078a3a4..bd12ff7f88f70 100644 --- a/frame/staking/fuzzer/Cargo.lock +++ b/frame/staking/fuzz/Cargo.lock @@ -32,7 +32,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "arbitrary" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -564,15 +564,6 @@ dependencies = [ "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "honggfuzz" -version = "0.5.45" -dependencies = [ - "arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "impl-codec" version = "0.4.2" @@ -635,6 +626,15 @@ name = "libc" version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "libfuzzer-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arbitrary 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libsecp256k1" version = "0.3.5" @@ -676,15 +676,6 @@ name = "memchr" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "memmap" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "memory-db" version = "0.19.0" @@ -861,12 +852,12 @@ dependencies = [ ] [[package]] -name = "pallet-staking-fuzzer" -version = "2.0.0" +name = "pallet-staking-fuzz" +version = "0.0.0" dependencies = [ "frame-support 2.0.0", "frame-system 2.0.0", - "honggfuzz 0.5.45", + "libfuzzer-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "pallet-balances 2.0.0", "pallet-indices 2.0.0", "pallet-session 2.0.0", @@ -2059,7 +2050,7 @@ dependencies = [ "checksum ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3" "checksum aho-corasick 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811" "checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" -"checksum arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" +"checksum arbitrary 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16971f2f0ce65c5cf2a1546cc6a0af102ecb11e265ddaa9433fb3e5bfdf676a4" "checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" "checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" @@ -2126,12 +2117,12 @@ dependencies = [ "checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum libfuzzer-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c103ca347f75325d3d3ee31702bd6c09b7744e71883b7a8da9562b0c5dcdaead" "checksum libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" "checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978" -"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" "checksum memory-db 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "198831fe8722331a395bc199a5d08efbc197497ef354cb4c77b969c02ffc0fc4" "checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" "checksum merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0942b357c1b4d0dc43ba724674ec89c3218e6ca2b3e8269e7cb53bcecd2f6e" diff --git a/frame/staking/fuzzer/Cargo.toml b/frame/staking/fuzz/Cargo.toml similarity index 80% rename from frame/staking/fuzzer/Cargo.toml rename to frame/staking/fuzz/Cargo.toml index 995ff03951c9a..1a7ee83a06470 100644 --- a/frame/staking/fuzzer/Cargo.toml +++ b/frame/staking/fuzz/Cargo.toml @@ -1,10 +1,16 @@ + [package] -name = "pallet-staking-fuzzer" -version = "2.0.0" -authors = ["Parity Technologies "] +name = "pallet-staking-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false edition = "2018" +[package.metadata] +cargo-fuzz = true + [dependencies] +libfuzzer-sys = "0.3" codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } pallet-staking = { version = "2.0.0", path = "..", features = ["testing-utils"] } pallet-staking-reward-curve = { version = "2.0.0", path = "../reward-curve" } @@ -19,13 +25,12 @@ sp-io ={ version = "2.0.0", path = "../../../primitives/io" } sp-core = { version = "2.0.0", path = "../../../primitives/core" } sp-phragmen = { version = "2.0.0", path = "../../../primitives/phragmen" } sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } -# honggfuzz = "0.5" -honggfuzz = { path = "../../../../honggfuzz-rs"} rand = "0.7.3" +# Prevent this from interfering with workspaces [workspace] +members = ["."] [[bin]] name = "submit_solution" -path = "src/submit_solution.rs" - +path = "fuzz_targets/submit_solution.rs" diff --git a/frame/staking/fuzzer/src/mock.rs b/frame/staking/fuzz/fuzz_targets/mock.rs similarity index 100% rename from frame/staking/fuzzer/src/mock.rs rename to frame/staking/fuzz/fuzz_targets/mock.rs diff --git a/frame/staking/fuzz/fuzz_targets/submit_solution.rs b/frame/staking/fuzz/fuzz_targets/submit_solution.rs new file mode 100644 index 0000000000000..67f52320ddefd --- /dev/null +++ b/frame/staking/fuzz/fuzz_targets/submit_solution.rs @@ -0,0 +1,130 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Fuzzing for staking pallet. + +#![no_main] +use libfuzzer_sys::fuzz_target; +use mock::Test; +use pallet_staking::testing_utils::{ + self, USER, get_seq_phragmen_solution, get_weak_solution, setup_chain_stakers, + set_validator_count, signed_account, +}; +use frame_support::assert_ok; +use sp_runtime::{traits::Dispatchable, DispatchError}; + +mod mock; + +#[repr(u32)] +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +enum Mode { + /// Initial submission. This will be rather cheap + InitialSubmission, + /// A better submission that will replace the previous ones. This is the most expensive. + StrongerSubmission, + /// A weak submission that will be rejected. This will be rather cheap. + WeakerSubmission, +} + +pub fn new_test_ext() -> Result { + frame_system::GenesisConfig::default().build_storage::().map(Into::into) +} + +fuzz_target!(|do_reduce: bool| { + let ext = new_test_ext(); + let mode: Mode = unsafe { std::mem::transmute(testing_utils::random(0, 2)) }; + let num_validators = testing_utils::random(50, 500); + let num_nominators = testing_utils::random(100, 500); + let edge_per_voter = testing_utils::random(1, 16); + let to_elect = testing_utils::random(10, 50); + + println!("+++ instance with params {} / {} / {} / {:?} / {}", + num_nominators, + num_validators, + edge_per_voter, + mode, + to_elect, + ); + + ext.unwrap_or_default().execute_with(|| { + // initial setup + set_validator_count::(to_elect); + setup_chain_stakers::( + num_validators, + num_nominators, + edge_per_voter, + ); + + println!("++ Chain setup done."); + + // stuff to submit + let (winners, compact, score) = match mode { + Mode::InitialSubmission => { + /* No need to setup anything */ + get_seq_phragmen_solution::(do_reduce) + }, + Mode::StrongerSubmission => { + let (winners, compact, score) = get_weak_solution::(false); + assert_ok!( + >::submit_election_solution( + signed_account::(USER), + winners, + compact, + score, + ) + ); + get_seq_phragmen_solution::(do_reduce) + }, + Mode::WeakerSubmission => { + let (winners, compact, score) = get_seq_phragmen_solution::(do_reduce); + assert_ok!( + >::submit_election_solution( + signed_account::(USER), + winners, + compact, + score, + ) + ); + get_weak_solution::(false) + } + }; + + println!("++ Submission ready."); + + // must have chosen correct number of winners. + assert_eq!(winners.len() as u32, >::validator_count()); + + // final call and origin + let call = pallet_staking::Call::::submit_election_solution( + winners, + compact, + score, + ); + let caller = signed_account::(USER); + + // actually submit + match mode { + Mode::WeakerSubmission => { + assert_eq!( + call.dispatch(caller.into()).unwrap_err(), + DispatchError::Module { index: 0, error: 11, message: Some("PhragmenWeakSubmission") }, + ); + }, + _ => assert_ok!(call.dispatch(caller.into())), + }; + }) +}); diff --git a/frame/staking/fuzzer/.gitignore b/frame/staking/fuzzer/.gitignore deleted file mode 100644 index bd4ff56b3c310..0000000000000 --- a/frame/staking/fuzzer/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -hfuzz_target -hfuzz_workspace - diff --git a/frame/staking/fuzzer/src/submit_solution.rs b/frame/staking/fuzzer/src/submit_solution.rs deleted file mode 100644 index e10e0c2a61ecd..0000000000000 --- a/frame/staking/fuzzer/src/submit_solution.rs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Fuzzing for staking pallet. -//! -//! # Running -//! -//! Run with `cargo hfuzz run submit_solution`. `honggfuzz`. -//! -//! # Debugging a panic -//! -//! Once a panic is found, it can be debugged with -//! `cargo hfuzz run-debug submit_solution hfuzz_workspace/submit_solution/*.fuzz`. - -use honggfuzz::fuzz; - -use mock::Test; -use pallet_staking::testing_utils::{ - self, USER, get_seq_phragmen_solution, get_weak_solution, setup_chain_stakers, - set_validator_count, signed_account, -}; -use frame_support::assert_ok; -use sp_runtime::{traits::Dispatchable, DispatchError}; - -mod mock; - -#[repr(u32)] -#[allow(dead_code)] -#[derive(Debug, Clone, Copy)] -enum Mode { - /// Initial submission. This will be rather cheap - InitialSubmission, - /// A better submission that will replace the previous ones. This is the most expensive. - StrongerSubmission, - /// A weak submission that will be rejected. This will be rather cheap. - WeakerSubmission, -} - -pub fn new_test_ext() -> Result { - frame_system::GenesisConfig::default().build_storage::().map(Into::into) -} - -fn main() { - loop { - fuzz!(|do_reduce: bool| { - let ext = new_test_ext(); - let mode: Mode = unsafe { std::mem::transmute(testing_utils::random(0, 2)) }; - let num_validators = testing_utils::random(100, 2_000); - let num_nominators = testing_utils::random(100, 2_000); - let edge_per_voter = 16; - let to_elect = testing_utils::random(100, num_validators); - - println!("++ instance with params {} / {} / {} / {:?} / {}", - num_nominators, - num_validators, - edge_per_voter, - mode, - to_elect, - ); - - ext.unwrap_or_default().execute_with(|| { - // initial setup - set_validator_count::(to_elect); - setup_chain_stakers::( - num_validators, - num_nominators, - edge_per_voter, - ); - - // stuff to submit - let (winners, compact, score) = match mode { - Mode::InitialSubmission => { - /* No need to setup anything */ - get_seq_phragmen_solution::(do_reduce) - }, - Mode::StrongerSubmission => { - let (winners, compact, score) = get_weak_solution::(false); - assert_ok!( - >::submit_election_solution( - signed_account::(USER), - winners, - compact, - score, - ) - ); - get_seq_phragmen_solution::(do_reduce) - }, - Mode::WeakerSubmission => { - let (winners, compact, score) = get_seq_phragmen_solution::(do_reduce); - assert_ok!( - >::submit_election_solution( - signed_account::(USER), - winners, - compact, - score, - ) - ); - get_weak_solution::(false) - } - }; - - // must have chosen correct number of winners. - assert_eq!(winners.len() as u32, >::validator_count()); - - // final call and origin - let call = pallet_staking::Call::::submit_election_solution( - winners, - compact, - score, - ); - let caller = signed_account::(USER); - - // actually submit - match mode { - Mode::WeakerSubmission => { - assert_eq!( - call.dispatch(caller.into()).unwrap_err(), - DispatchError::Module { index: 0, error: 11, message: Some("PhragmenWeakSubmission") }, - ); - }, - _ => assert_ok!(call.dispatch(caller.into())), - }; - }) - }); - } -} diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index 0a01b59410d24..8903dabae5746 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -114,6 +114,7 @@ pub fn setup_chain_stakers( edge_per_voter: u32, ) where T::Lookup: StaticLookup> { (0..num_validators).for_each(|i| { + // println!("bonding validator {}/{}", i, num_validators); bond_validator::( account::(i), i + CTRL_PREFIX, @@ -129,6 +130,7 @@ pub fn setup_chain_stakers( let target = all_targets.remove(random(0, all_targets.len() as u32 - 1) as usize); targets.push(target); }); + // println!("bonding voter {}/{}", i, num_voters); bond_nominator::( account::(i + NOMINATOR_PREFIX), i + NOMINATOR_PREFIX + CTRL_PREFIX, diff --git a/primitives/phragmen/src/helpers.rs b/primitives/phragmen/src/helpers.rs index d3db897027231..d1af4113849d9 100644 --- a/primitives/phragmen/src/helpers.rs +++ b/primitives/phragmen/src/helpers.rs @@ -39,7 +39,7 @@ pub fn assignment_ratio_to_staked( .collect() } -/// Converts a vector of ratio assignments into ones with absolute budget value. +/// Converts a vector of staked assignments into ones with ratio values. pub fn assignment_staked_to_ratio( ratio: Vec>, ) -> Vec> where ExtendedBalance: From<::Inner> From 44c0a6bebcb1e50927962bfad2e6e114f4b21005 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 21 Feb 2020 17:23:05 +0100 Subject: [PATCH 055/106] =?UTF-8?q?Disable=20it=20=E2=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/node/runtime/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index bda3e9b3db1e3..a00a13d21d585 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -315,7 +315,8 @@ parameter_types! { pub const BondingDuration: pallet_staking::EraIndex = 24 * 28; pub const SlashDeferDuration: pallet_staking::EraIndex = 24 * 7; // 1/4 the bonding duration. pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; - pub const ElectionLookahead: BlockNumber = 150; + /// This means that the offchain election is disabled for now. + pub const ElectionLookahead: BlockNumber = 0; } impl pallet_staking::Trait for Runtime { From f21df59cf9d51a03b95eb88c59fc2abff8dfcbed Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 21 Feb 2020 17:48:03 +0100 Subject: [PATCH 056/106] Bump. --- 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 90d8c78343b9e..281030f512473 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -127,8 +127,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 224, - impl_version: 2, + spec_version: 225, + impl_version: 0, apis: RUNTIME_API_VERSIONS, }; From f9f92c4350d45e71c5184fb3f02c9625766eb8ff Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 26 Feb 2020 10:24:37 +0100 Subject: [PATCH 057/106] Another round of self-review --- frame/staking/src/lib.rs | 294 ++++++++++++------------- frame/staking/src/mock.rs | 3 +- frame/staking/src/offchain_election.rs | 10 +- frame/staking/src/tests.rs | 28 +-- 4 files changed, 164 insertions(+), 171 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index db498b3a74a7c..952d5183db112 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -270,7 +270,7 @@ use frame_support::{ }; use pallet_session::historical; use sp_runtime::{ - Perbill, PerThing, RuntimeDebug, RuntimeAppPublic, + Perbill, PerU16, PerThing, RuntimeDebug, RuntimeAppPublic, curve::PiecewiseLinear, traits::{ Convert, Zero, One, StaticLookup, CheckedSub, Saturating, Bounded, SaturatedConversion, @@ -322,7 +322,7 @@ pub type Points = u32; pub type ChainAccuracy = Perbill; /// Accuracy used for off-chain phragmen. This better be small. -pub type OffchainAccuracy = sp_runtime::PerU16; +pub type OffchainAccuracy = PerU16; /// The balance type of this module. pub type BalanceOf = @@ -341,6 +341,7 @@ type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; type MomentOf = <::Time as Time>::Moment; +/// Staking's key type used for submitting unsigned solutions. pub mod sr25519 { mod app_sr25519 { use sp_application_crypto::{app_crypto, key_types::STAKING, sr25519}; @@ -621,28 +622,27 @@ pub enum ElectionCompute { /// The result of an election round. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub struct ElectionResult { - /// Type of the result, - compute: ElectionCompute, - /// The new computed slot stake. - slot_stake: Balance, /// Flat list of validators who have been elected. elected_stashes: Vec, /// Flat list of new exposures, to be updated in the [`Exposure`] storage. exposures: Vec<(AccountId, Exposure)>, + /// Type of the result. This is kept on chain only to track and report the best score's + /// submission type. An optimisation can could remove this. + compute: ElectionCompute, } /// The status of the upcoming (offchain) election. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub enum ElectionStatus { /// Nothing has and will happen for now. submission window is not open. - Close, + Closed, /// The submission window has been open since the contained block number. Open(BlockNumber), } impl Default for ElectionStatus { fn default() -> Self { - Self::Close + Self::Closed } } @@ -975,24 +975,26 @@ decl_error! { PhragmenEarlySubmission, /// The submitted result is not as good as the one stored on chain. PhragmenWeakSubmission, - /// The submitted result has unknown edges that are not among the presented winners. - PhragmenBogusEdge, - /// One of the submitted winners is not an active candidate on chain. + /// The snapshot data of the current window is missing. + SnapshotUnavailable, + /// Incorrect number of winners were presented. + PhragmenBogusWinnerCount, + /// One of the submitted winners is not an active candidate on chain (index is out of range + /// in snapshot). PhragmenBogusWinner, - /// One of the submitted nominators is not an active nominator on chain. + /// Error while building the assignment type from the compact. This can happen if an index + /// is invalid, or if the weights _overflow_. + PhragmenBogusCompact, + /// One of the submitted nominators is not an active nominator on chain. DEPRECATED. PhragmenBogusNominator, /// One of the submitted nominators has an edge to which they have not voted on chain. PhragmenBogusNomination, /// A self vote must only be originated from a validator to ONLY themselves. PhragmenBogusSelfVote, + /// The submitted result has unknown edges that are not among the presented winners. + PhragmenBogusEdge, /// The claimed score does not match with the one computed from the data. PhragmenBogusScore, - /// Error while building the assignment type from the compact. - PhragmenBogusCompact, - /// Incorrect number of winners were presented. - PhragmenBogusWinnerCount, - /// The snapshot data of the current window is missing. - SnapshotUnavailable, } } @@ -1011,7 +1013,7 @@ decl_module! { /// Does the following: /// /// 1. potential storage migration - /// 2. sets `ElectionStatus` to `Triggered(now)` where `now` is the block number at which + /// 2. sets `ElectionStatus` to `Open(now)` where `now` is the block number at which /// the election window has opened. The offchain worker, if applicable, will execute at /// the end of the current block. `submit_election_solution` will accept solutions from /// this block until the end of the era. @@ -1019,7 +1021,7 @@ decl_module! { Self::ensure_storage_upgraded(); if // if we don't have any ongoing offchain compute. - Self::era_election_status() == ElectionStatus::Close && + Self::era_election_status() == ElectionStatus::Closed && // and an era is about to be changed. Self::is_current_session_final() { @@ -1088,115 +1090,6 @@ decl_module! { } } - /// Submit a phragmen result to the chain. If the solution: - /// - /// 1. is valid - /// 2. has a better score than a potentially existing solution on chain - /// - /// then, it will be put on chain. - /// - /// A solution consists of two pieces of data: - /// - /// 1. `winners`: a flat vector of all the winners of the round. - /// 2. `assignments`: the compact version of an assignment vector that encodes the edge - /// weights. - /// - /// Both of which may be computed using the [`phragmen`], or any other algorithm. - /// - /// Additionally, the submitter must provide: - /// - /// - The score that they claim their solution has. - /// - /// Both validators and nominators will be represented by indices in the solution. The - /// indices should respect the corresponding types ([`ValidatorIndex`] and - /// [`NominatorIndex`]). Moreover, they should be valid when used to index into - /// [`SnapshotValidators`] and [`SnapshotNominators`]. Any invalid index will cause the - /// solution to be rejected. - /// - /// A solution is valid if: - /// - /// 0. It is submitted when [`EraElectionStatus`] is `Open`. - /// 1. Its claimed score is equal to the score computed on-chain. - /// 2. Presents the correct number of winners. - /// 3. All indexes must be value according to the snapshot vectors. All edge values must - /// also be correct and should not overflow the granularity of the ratio type (i.e. 256 - /// or billion). - /// 4. For each edge, all targets are actually nominated by the voter. - /// 5. Has correct self-votes. - /// - /// A solutions score is consisted of 3 parameters: - /// - /// 1. `min { support.total }` for each support of a winner. This value should be maximized. - /// 2. `sum { support.total }` for each support of a winner. This value should be minimized. - /// 3. `sum { support.total^2 }` for each support of a winner. This value should be - /// minimized (to ensure less variance) - /// - /// # - /// E: number of edges. - /// m: size of winner committee. - /// n: number of nominators. - /// d: edge degree (16 for now) - /// v: number of on-chain validator candidates. - /// - /// NOTE: given a solution which is reduced, we can enable a new check the ensure `|E| < n + - /// m`. - /// - /// major steps (all done in `check_and_replace_solution`): - /// - /// - Storage: O(1) read `ElectionStatus`. - /// - Storage: O(1) read `PhragmenScore`. - /// - Storage: O(1) read `ValidatorCount`. - /// - Storage: O(1) length read from `SnapshotValidators`. - /// - Storage: O(v) reads of `AccountId`. - /// - Memory: O(m) iterations to map winner index to validator id. - /// - Storage: O(n) reads `AccountId`. - /// - Memory: O(n + m) reads to map index to `AccountId` for un-compact. - /// - Storage: O(e) accountid reads from `Nomination` to read correct nominations. - /// - Storage: O(e) calls into `slashable_balance_of_extended` to convert ratio to staked. - /// - Memory: build_support_map. O(e). - /// - Memory: evaluate_support: O(E). - /// - Storage: O(e) writes to `QueuedElected`. - /// - Storage: O(1) write to `QueuedScore` - /// - /// The weight of this call is 1/10th of the blocks total weight. - /// # - #[weight = SimpleDispatchInfo::FixedNormal(100_000_000)] - pub fn submit_election_solution( - origin, - winners: Vec, - compact_assignments: Compact, - score: PhragmenScore, - ) { - let _who = ensure_signed(origin)?; - Self::check_and_replace_solution( - winners, - compact_assignments, - ElectionCompute::Signed, - score, - )? - } - - /// Unsigned version of `submit_election_solution`. Will only be accepted from those who are - /// in the current validator set. - #[weight = SimpleDispatchInfo::FixedNormal(100_000_000)] - pub fn submit_election_solution_unsigned( - origin, - winners: Vec, - compact_assignments: Compact, - score: PhragmenScore, - // already used and checked in ValidateUnsigned. - _validator_index: u32, - _signature: ::Signature, - ) { - ensure_none(origin)?; - Self::check_and_replace_solution( - winners, - compact_assignments, - ElectionCompute::Authority, - score, - )? - } - /// Take the origin account as a stash and lock up `value` of its balance. `controller` will /// be the account that controls it. /// @@ -1597,6 +1490,115 @@ decl_module! { Self::update_ledger(&controller, &ledger); } + + /// Submit a phragmen result to the chain. If the solution: + /// + /// 1. is valid + /// 2. has a better score than a potentially existing solution on chain + /// + /// then, it will be put on chain. + /// + /// A solution consists of two pieces of data: + /// + /// 1. `winners`: a flat vector of all the winners of the round. + /// 2. `assignments`: the compact version of an assignment vector that encodes the edge + /// weights. + /// + /// Both of which may be computed using [`phragmen`], or any other algorithm. + /// + /// Additionally, the submitter must provide: + /// + /// - The score that they claim their solution has. + /// + /// Both validators and nominators will be represented by indices in the solution. The + /// indices should respect the corresponding types ([`ValidatorIndex`] and + /// [`NominatorIndex`]). Moreover, they should be valid when used to index into + /// [`SnapshotValidators`] and [`SnapshotNominators`]. Any invalid index will cause the + /// solution to be rejected. + /// + /// A solution is valid if: + /// + /// 0. It is submitted when [`EraElectionStatus`] is `Open`. + /// 1. Its claimed score is equal to the score computed on-chain. + /// 2. Presents the correct number of winners. + /// 3. All indexes must be value according to the snapshot vectors. All edge values must + /// also be correct and should not overflow the granularity of the ratio type (i.e. 256 + /// or billion). + /// 4. For each edge, all targets are actually nominated by the voter. + /// 5. Has correct self-votes. + /// + /// A solutions score is consisted of 3 parameters: + /// + /// 1. `min { support.total }` for each support of a winner. This value should be maximized. + /// 2. `sum { support.total }` for each support of a winner. This value should be minimized. + /// 3. `sum { support.total^2 }` for each support of a winner. This value should be + /// minimized (to ensure less variance) + /// + /// # + /// E: number of edges. + /// m: size of winner committee. + /// n: number of nominators. + /// d: edge degree (16 for now) + /// v: number of on-chain validator candidates. + /// + /// NOTE: given a solution which is reduced, we can enable a new check the ensure `|E| < n + + /// m`. + /// + /// major steps (all done in `check_and_replace_solution`): + /// + /// - Storage: O(1) read `ElectionStatus`. + /// - Storage: O(1) read `PhragmenScore`. + /// - Storage: O(1) read `ValidatorCount`. + /// - Storage: O(1) length read from `SnapshotValidators`. + /// - Storage: O(v) reads of `AccountId`. + /// - Memory: O(m) iterations to map winner index to validator id. + /// - Storage: O(n) reads `AccountId`. + /// - Memory: O(n + m) reads to map index to `AccountId` for un-compact. + /// - Storage: O(e) accountid reads from `Nomination` to read correct nominations. + /// - Storage: O(e) calls into `slashable_balance_of_extended` to convert ratio to staked. + /// - Memory: build_support_map. O(e). + /// - Memory: evaluate_support: O(E). + /// - Storage: O(e) writes to `QueuedElected`. + /// - Storage: O(1) write to `QueuedScore` + /// + /// The weight of this call is 1/10th of the blocks total weight. + /// # + #[weight = SimpleDispatchInfo::FixedNormal(100_000_000)] + pub fn submit_election_solution( + origin, + winners: Vec, + compact_assignments: Compact, + score: PhragmenScore, + ) { + let _who = ensure_signed(origin)?; + Self::check_and_replace_solution( + winners, + compact_assignments, + ElectionCompute::Signed, + score, + )? + } + + /// Unsigned version of `submit_election_solution`. Will only be accepted from those who are + /// in the current validator set. + #[weight = SimpleDispatchInfo::FixedNormal(100_000_000)] + pub fn submit_election_solution_unsigned( + origin, + winners: Vec, + compact_assignments: Compact, + score: PhragmenScore, + // already used and checked in ValidateUnsigned. + _validator_index: u32, + _signature: ::Signature, + ) { + ensure_none(origin)?; + Self::check_and_replace_solution( + winners, + compact_assignments, + ElectionCompute::Authority, + score, + )? + } } } @@ -1629,7 +1631,8 @@ impl Module { /// Dump the list of validators and nominators into vectors and keep them on-chain. /// - /// This data is used to efficiently evaluate election results. + /// This data is used to efficiently evaluate election results. returns `true` if the operation + /// is successful. fn create_stakers_snapshot() -> bool { let validators = >::enumerate().map(|(v, _)| v).collect::>(); let mut nominators = >::enumerate().map(|(n, _)| n).collect::>(); @@ -1776,7 +1779,7 @@ impl Module { ) -> Result<(), Error> { // discard early solutions match Self::era_election_status() { - ElectionStatus::Close => Err(Error::::PhragmenEarlySubmission)?, + ElectionStatus::Closed => Err(Error::::PhragmenEarlySubmission)?, ElectionStatus::Open(_) => { /* Open and no solutions received. Allowed. */ }, } @@ -1858,7 +1861,7 @@ impl Module { is_validator is false; maybe_nomination is some; qed" ); // NOTE: we don't really have to check here if the sum of all edges are the - // nominator budged. By definition, compact -> staked ensures this. + // nominator correct. Un-compacting assures this by definition. ensure!( distribution.into_iter().all(|(t, _)| { nomination.targets.iter().find(|&tt| tt == t).is_some() @@ -1901,7 +1904,6 @@ impl Module { // At last, alles Ok. Exposures and store the result. let to_balance = |e: ExtendedBalance| >>::convert(e); - let mut slot_stake: BalanceOf = Bounded::max_value(); let exposures = supports.into_iter().map(|(validator, support)| { let mut others = Vec::new(); @@ -1920,9 +1922,6 @@ impl Module { }); let exposure = Exposure { own, others, total }; - if exposure.total < slot_stake { - slot_stake = exposure.total; - } (validator, exposure) }).collect::)>>(); @@ -1935,7 +1934,6 @@ impl Module { elected_stashes: winners, compute, exposures, - slot_stake, }); QueuedScore::put(submitted_score); @@ -2039,11 +2037,14 @@ impl Module { }) } + /// Select the new validator set at the end of the era. + /// /// Runs [`try_do_phragmen`] and updates the following storage items: + /// - [`EraElectionStatus`]: with `None`. /// - [`Stakers`]: with the new staker set. /// - [`SlotStake`]: with the new slot stake. /// - [`CurrentElected`]: with the new elected set. - /// - [`EraElectionStatus`]: with `None` + /// - [`SnapshotValidators`] and [`SnapshotNominators`] are both removed. /// /// Internally, [`QueuedElected`], snapshots and [`QueuedScore`] are also consumed. /// @@ -2054,11 +2055,10 @@ impl Module { if let Some(ElectionResult::> { elected_stashes, exposures, - slot_stake, compute, }) = Self::try_do_phragmen() { // We have chosen the new validator set. Submission is no longer allowed. - >::put(ElectionStatus::Close); + >::put(ElectionStatus::Closed); // kill the snapshots. Self::kill_stakers_snapshot(); @@ -2069,7 +2069,9 @@ impl Module { } // Populate Stakers and write slot stake. + let mut slot_stake: BalanceOf = Bounded::max_value(); exposures.into_iter().for_each(|(s, e)| { + if e.total < slot_stake { slot_stake = e.total } >::insert(s, e); }); @@ -2106,7 +2108,8 @@ impl Module { Self::do_phragmen_with_post_processing::(ElectionCompute::OnChain) ); - // either way, kill this + // either way, kill this. We remove it here to make sure it always has teh exact same + // lifetime as `QueuedElected`. QueuedScore::kill(); next_result @@ -2144,8 +2147,7 @@ impl Module { let to_balance = |e: ExtendedBalance| >>::convert(e); - // collect exposures and slot stake. - let mut slot_stake = BalanceOf::::max_value(); + // collect exposures let exposures = supports.into_iter().map(|(validator, support)| { // build `struct exposure` from `support` let mut others = Vec::new(); @@ -2172,9 +2174,6 @@ impl Module { total, }; - if exposure.total < slot_stake { - slot_stake = exposure.total; - } (validator, exposure) }).collect::)>>(); @@ -2184,7 +2183,6 @@ impl Module { // compare against the prior set. Some(ElectionResult::> { elected_stashes, - slot_stake, exposures, compute, }) @@ -2596,7 +2594,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { use offchain_election::SignaturePayload; // discard early solution - if Self::era_election_status() == ElectionStatus::Close { + if Self::era_election_status() == ElectionStatus::Closed { debug::native::debug!( target: "staking", "rejecting unsigned transaction because it is too early." diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 610d432c0b3ac..53bdd74358dbd 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -467,7 +467,8 @@ impl ExtBuilder { self } pub fn offchain_phragmen_ext(self) -> Self { - self.session_per_era(3) + self + .session_per_era(3) .session_length(5) .election_lookahead(3) .local_key_account(11) diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 87176676e339c..63d0565f6ff82 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -59,7 +59,7 @@ pub(crate) const OFFCHAIN_HEAD_DB: &[u8] = b"parity/staking-election/"; const OFFCHAIN_REPEAT: u32 = 5; pub(crate) fn set_check_offchain_execution_status( - now: T::BlockNumber + now: T::BlockNumber, ) -> Result<(), &'static str> { let storage = StorageValueRef::persistent(&OFFCHAIN_HEAD_DB); let threshold = T::BlockNumber::from(OFFCHAIN_REPEAT); @@ -99,13 +99,7 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti // For each local key is in the stored authority keys, try and submit. Breaks out after first // successful submission. for (index, ref pubkey) in local_keys.into_iter().filter_map(|key| - keys.iter().enumerate().find_map(|(index, val_key)| - if *val_key == key { - Some((index, val_key)) - } else { - None - } - ) + keys.iter().enumerate().find(|(_, val_key)| **val_key == key) ) { // make sure that the snapshot is available. let snapshot_validators = >::snapshot_validators() diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 928384ef91f2c..091dafb92d6e9 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2775,24 +2775,24 @@ mod offchain_phragmen { || { run_to_block(10); assert_session_era!(1, 0); - assert_eq!(Staking::era_election_status(), ElectionStatus::Close); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); run_to_block(18); assert_session_era!(1, 0); - assert_eq!(Staking::era_election_status(), ElectionStatus::Close); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); run_to_block(40); assert_session_era!(4, 0); - assert_eq!(Staking::era_election_status(), ElectionStatus::Close); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); run_to_block(46); - assert_eq!(Staking::era_election_status(), ElectionStatus::Close); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); @@ -2807,7 +2807,7 @@ mod offchain_phragmen { assert!(Staking::snapshot_validators().is_some()); run_to_block(50); - assert_eq!(Staking::era_election_status(), ElectionStatus::Close); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); }) @@ -2818,7 +2818,7 @@ mod offchain_phragmen { ExtBuilder::default().build().execute_with(|| { start_session(1); start_session(2); - assert_eq!(Staking::era_election_status(), ElectionStatus::Close); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); // some election must have happened by now. assert_eq!( System::events().into_iter().map(|r| r.event).filter_map(|e| { @@ -2855,7 +2855,7 @@ mod offchain_phragmen { run_to_block(27); assert!(Staking::snapshot_validators().is_none()); - assert_eq!(Staking::era_election_status(), ElectionStatus::Close); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); }) } @@ -2888,7 +2888,7 @@ mod offchain_phragmen { } #[test] - fn offchain_result_can_be_submitted() { + fn signed_result_can_be_submitted() { // should check that we have a new validator set normally, // event says that it comes from offchain. ExtBuilder::default() @@ -2912,7 +2912,7 @@ mod offchain_phragmen { assert_eq!(queued_result.compute, ElectionCompute::Signed); run_to_block(15); - assert_eq!(Staking::era_election_status(), ElectionStatus::Close); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert_eq!( System::events().into_iter().map(|r| r.event).filter_map(|e| { @@ -2928,8 +2928,8 @@ mod offchain_phragmen { } #[test] - fn offchain_result_can_be_submitted_later() { - // same as `offchain_result_can_be_submitted` but at a later block. + fn signed_result_can_be_submitted_later() { + // same as `signed_result_can_be_submitted` but at a later block. ExtBuilder::default() .offchain_phragmen_ext() .build() @@ -2952,7 +2952,7 @@ mod offchain_phragmen { assert_eq!(queued_result.compute, ElectionCompute::Signed); run_to_block(15); - assert_eq!(Staking::era_election_status(), ElectionStatus::Close); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert_eq!( System::events().into_iter().map(|r| r.event).filter_map(|e| { @@ -2978,7 +2978,7 @@ mod offchain_phragmen { || { run_to_block(11); // submission is not yet allowed - assert_eq!(Staking::era_election_status(), ElectionStatus::Close); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); // create all the indices just to build the solution. Staking::create_stakers_snapshot(); @@ -3097,7 +3097,7 @@ mod offchain_phragmen { // put a good solution on-chain let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); assert_ok!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score) + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), ); // now run the offchain worker in the same chain state. From 037057ccb9590e5e3a26e646eb2cbad5d758c572 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 27 Feb 2020 14:11:42 +0100 Subject: [PATCH 058/106] Refactor a lot --- frame/staking/src/slashing.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/staking/src/slashing.rs b/frame/staking/src/slashing.rs index 3c8f39501a25e..13b717ab19c8d 100644 --- a/frame/staking/src/slashing.rs +++ b/frame/staking/src/slashing.rs @@ -65,7 +65,7 @@ use codec::{Encode, Decode}; const REWARD_F1: Perbill = Perbill::from_percent(50); /// The index of a slashing span - unique to each stash. -pub(crate) type SpanIndex = u32; +pub type SpanIndex = u32; // A range of start..end eras for a slashing span. #[derive(Encode, Decode)] @@ -143,7 +143,7 @@ impl SlashingSpans { } /// Yields the era index where the most recent non-zero slash occurred. - pub(crate) fn last_nonzero_slash(&self) -> EraIndex { + pub fn last_nonzero_slash(&self) -> EraIndex { self.last_nonzero_slash } From 2b89c0e968c8ddfbb24ece4096ea3c06126ec9e3 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 28 Feb 2020 13:08:15 +0100 Subject: [PATCH 059/106] More major fixes in perThing --- frame/staking/src/mock.rs | 2 +- frame/staking/src/offchain_election.rs | 23 +++---- frame/staking/src/testing_utils.rs | 5 -- primitives/arithmetic/fuzzer/Cargo.lock | 87 ++++--------------------- primitives/arithmetic/fuzzer/Cargo.toml | 6 +- primitives/arithmetic/src/lib.rs | 14 +++- primitives/arithmetic/src/per_things.rs | 39 +++++++---- 7 files changed, 68 insertions(+), 108 deletions(-) diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index ac201a7465574..550e1a7e0906d 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -865,7 +865,7 @@ pub fn horrible_phragmen_with_post_processing( (compact, winners, score) } -// Note: this should always logicall reproduce [`offchain_election::prepare_submission`], yet we +// Note: this should always logically reproduce [`offchain_election::prepare_submission`], yet we // cannot do it since we want to have `tweak` injected into the process. pub fn do_phragmen_with_post_processing( do_reduce: bool, diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 63d0565f6ff82..7f11381957405 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -32,7 +32,7 @@ use sp_runtime::offchain::storage::StorageValueRef; use sp_runtime::PerThing; #[derive(RuntimeDebug)] -pub(crate) enum OffchainElectionError { +pub enum OffchainElectionError { /// No signing key has been found on the current node that maps to a validators. This node /// should not run the offchain election code. NoSigningKey, @@ -101,11 +101,6 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti for (index, ref pubkey) in local_keys.into_iter().filter_map(|key| keys.iter().enumerate().find(|(_, val_key)| **val_key == key) ) { - // make sure that the snapshot is available. - let snapshot_validators = >::snapshot_validators() - .ok_or(OffchainElectionError::SnapshotUnavailable)?; - let snapshot_nominators = >::snapshot_nominators() - .ok_or(OffchainElectionError::SnapshotUnavailable)?; // compute raw solution. let PhragmenResult { @@ -116,12 +111,10 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti // process and prepare it for submission. let (winners, compact, score) = prepare_submission::( - snapshot_nominators, - snapshot_validators, assignments, winners, true, - ); + )?; // sign it. let signature_payload: SignaturePayload = @@ -157,15 +150,19 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti /// /// This does a lot of stuff; read the inline comments. pub fn prepare_submission( - snapshot_nominators: Vec, - snapshot_validators: Vec, assignments: Vec>, winners: Vec<(T::AccountId, ExtendedBalance)>, do_reduce: bool, -) -> (Vec, Compact, PhragmenScore) +) -> Result<(Vec, Compact, PhragmenScore), OffchainElectionError> where ExtendedBalance: From<::Inner>, { + // make sure that the snapshot is available. + let snapshot_validators = >::snapshot_validators() + .ok_or(OffchainElectionError::SnapshotUnavailable)?; + let snapshot_nominators = >::snapshot_nominators() + .ok_or(OffchainElectionError::SnapshotUnavailable)?; + // all helper closures let nominator_index = |a: &T::AccountId| -> Option { snapshot_nominators.iter().position(|x| x == a).and_then(|i| @@ -227,5 +224,5 @@ where snapshot_validators.iter().position(|v| *v == w).unwrap().try_into().unwrap() ).collect::>(); - (winners, compact, score) + Ok((winners, compact, score)) } diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index 8903dabae5746..c22cba87b9d6d 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -300,12 +300,7 @@ pub fn get_seq_phragmen_solution(do_reduce: bool) assignments, } = >::do_phragmen::().unwrap(); - let snapshot_validators = >::snapshot_validators().unwrap(); - let snapshot_nominators = >::snapshot_nominators().unwrap(); - offchain_election::prepare_submission::( - snapshot_nominators, - snapshot_validators, assignments, winners, do_reduce, diff --git a/primitives/arithmetic/fuzzer/Cargo.lock b/primitives/arithmetic/fuzzer/Cargo.lock index 62b06674fbf67..a393fab576e27 100644 --- a/primitives/arithmetic/fuzzer/Cargo.lock +++ b/primitives/arithmetic/fuzzer/Cargo.lock @@ -32,9 +32,9 @@ checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" [[package]] name = "byteorder" -version = "1.3.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "c2-chacha" @@ -83,11 +83,6 @@ dependencies = [ [[package]] name = "honggfuzz" version = "0.5.45" -<<<<<<< HEAD -======= -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c27b4aa3049d6d10d8e33d52c9d03ca9aec18f8a449b246f8c4a5b0c10fb34" ->>>>>>> 657484a45b1f941b695c01ff3b944a4d81bd1849 dependencies = [ "arbitrary", "lazy_static", @@ -117,9 +112,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" [[package]] name = "memmap" @@ -214,9 +209,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" dependencies = [ "unicode-xid", ] @@ -299,7 +294,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" -version = "2.0.0" +version = "2.0.0-alpha.1" dependencies = [ "integer-sqrt", "num-traits", @@ -313,24 +308,16 @@ dependencies = [ name = "sp-arithmetic-fuzzer" version = "2.0.0" dependencies = [ -<<<<<<< HEAD - "honggfuzz 0.5.45", - "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-arithmetic 2.0.0", -======= "honggfuzz", "num-bigint", "num-traits", "primitive-types", "sp-arithmetic", ->>>>>>> 657484a45b1f941b695c01ff3b944a4d81bd1849 ] [[package]] name = "sp-debug-derive" -version = "2.0.0" +version = "2.0.0-alpha.1" dependencies = [ "proc-macro2", "quote", @@ -339,7 +326,7 @@ dependencies = [ [[package]] name = "sp-std" -version = "2.0.0" +version = "2.0.0-alpha.1" [[package]] name = "static_assertions" @@ -349,9 +336,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" dependencies = [ "proc-macro2", "quote", @@ -411,56 +398,4 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -<<<<<<< HEAD - -[metadata] -"checksum arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" -"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" -"checksum bitvec 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9633b74910e1870f50f5af189b08487195cdb83c0e27a71d6f64d5e09dd0538b" -"checksum byte-slice-cast 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f6209f3b2c1edea170002e016d5ead6903d3bb0a846477f53bbeb614967a52a9" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -"checksum fixed-hash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72fe7539e2c5692c6989f2f9c0457e42f1e5768f96b85c87d273574670ae459f" -"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" -"checksum impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" -"checksum integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea155abb3ba6f382a75f1418988c05fe82959ed9ce727de427f9cfd425b0c903" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" -"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -"checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" -"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" -"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "001fbbb956d8593f321c7a784f64d16b2c99b2657823976eea729006ad2c3668" -"checksum parity-scale-codec-derive 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42af752f59119656fa3cb31e8852ed24e895b968c0bdb41847da7f0cea6d155f" -"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0253db64c26d8b4e7896dd2063b516d2a1b9e0a5da26b5b78335f236d1e9522" -"checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" -"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8" -"checksum serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4b39bd9b0b087684013a792c59e3e07a46a01d2322518d8a1104641a0b1be0" -"checksum serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "ca13fc1a832f793322228923fbb3aba9f3f44444898f835d31ad1b74fa0a2bf8" -"checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92" -"checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf" -"checksum uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -======= checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" ->>>>>>> 657484a45b1f941b695c01ff3b944a4d81bd1849 diff --git a/primitives/arithmetic/fuzzer/Cargo.toml b/primitives/arithmetic/fuzzer/Cargo.toml index 19d677f744502..ae83e0e3df212 100644 --- a/primitives/arithmetic/fuzzer/Cargo.toml +++ b/primitives/arithmetic/fuzzer/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" license = "GPL-3.0" [dependencies] -sp-arithmetic = { version = "2.0.0", path = ".." } +sp-arithmetic = { version = "2.0.0-alpha.1", path = ".." } honggfuzz = "0.5" primitive-types = "0.6.2" num-bigint = "0.2" @@ -18,6 +18,10 @@ num-traits = "0.2" name = "biguint" path = "src/biguint.rs" +[[bin]] +name = "per_thing_rational" +path = "src/per_thing_rational.rs" + [[bin]] name = "rational128" path = "src/rational128.rs" diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs index a1cc3fa8d7d9d..f6d8b53e3499b 100644 --- a/primitives/arithmetic/src/lib.rs +++ b/primitives/arithmetic/src/lib.rs @@ -19,7 +19,7 @@ #![cfg_attr(not(feature = "std"), no_std)] /// Copied from `sp-runtime` and documented there. -#[cfg(test)] +#[macro_export] macro_rules! assert_eq_error_rate { ($x:expr, $y:expr, $error:expr $(,)?) => { assert!( @@ -42,3 +42,15 @@ mod rational128; pub use fixed64::Fixed64; pub use per_things::{PerThing, Percent, PerU16, Permill, Perbill, Perquintill}; pub use rational128::Rational128; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn peru16_rational_does_not_overflow() { + // A historical example that will panic only for per_thing type that are created with + // maximum capacity of their type, e.g. PerU16. + let _ = PerU16::from_rational_approximation(17424870u32, 17424870); + } +} diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index a548e9192f4ea..a80bc13c202fe 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -33,7 +33,7 @@ pub trait PerThing: type Inner: BaseArithmetic + Copy + fmt::Debug; /// The data type that is used to store values bigger than the maximum of this type. This must - /// at least be able to store twice the size of `Self::ACCURACY`. + /// at least be able to store `Self::ACCURACY * Self::ACCURACY`. type Upper: BaseArithmetic + Copy + fmt::Debug; /// accuracy of this type @@ -85,7 +85,7 @@ pub trait PerThing: fn from_rational_approximation(p: N, q: N) -> Self where N: Clone + Ord + From + TryInto + TryInto + - ops::Div; + ops::Div + ops::Rem + ops::Add; /// A mul implementation that always rounds down, whilst the standard `Mul` implementation /// rounds to the nearest numbers @@ -108,7 +108,14 @@ pub trait PerThing: } macro_rules! implement_per_thing { - ($name:ident, $test_mod:ident, [$($test_units:tt),+], $max:tt, $type:ty, $upper_type:ty, $title:expr $(,)?) => { + ( + $name:ident, + $test_mod:ident, + [$($test_units:tt),+], + $max:tt, $type:ty, + $upper_type:ty, + $title:expr $(,)? + ) => { /// A fixed point representation of a number between in the range [0, 1]. /// #[doc = $title] @@ -153,29 +160,39 @@ macro_rules! implement_per_thing { fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as Self::Inner) } fn from_rational_approximation(p: N, q: N) -> Self - where N: Clone + Ord + From + TryInto + TryInto + ops::Div + where N: + Clone + Ord + From + TryInto + TryInto + + ops::Div + ops::Rem + ops::Add { + let div_ceil = |x: N, f: N| -> N { + let mut o = x.clone() / f.clone(); + let r = x.rem(f.clone()); + if r > N::from(0) { + o = o + N::from(1); + } + o + }; + // q cannot be zero. let q: N = q.max((1 as Self::Inner).into()); // p should not be bigger than q. let p: N = p.min(q.clone()); - let factor: N = (q.clone() / $max.into()).max((1 as Self::Inner).into()); + let factor: N = div_ceil(q.clone(), $max.into()).max((1 as Self::Inner).into()); - // q cannot overflow: (q / (q/$max)) < 2 * $max. p < q hence p also cannot overflow. - // this implies that Self::Inner must be able to fit 2 * $max. - let q_reduce: $upper_type = (q / factor.clone()) + // q cannot overflow: (q / (q/$max)) < $max. p < q hence p also cannot overflow. + let q_reduce: $type = (q.clone() / factor.clone()) .try_into() .map_err(|_| "Failed to convert") .expect( - "q / (q/$max) < (2 * $max). Macro prevents any type being created that \ + "q / ceil(q/$max) < $max. Macro prevents any type being created that \ does not satisfy this; qed" ); - let p_reduce: $upper_type = (p / factor.clone()) + let p_reduce: $type = (p / factor) .try_into() .map_err(|_| "Failed to convert") .expect( - "q / (q/$max) < (2 * $max). Macro prevents any type being created that \ + "q / ceil(q/$max) < $max. Macro prevents any type being created that \ does not satisfy this; qed" ); From 4e445016ea9b6dad091a5b245a73b92282994846 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 28 Feb 2020 13:08:37 +0100 Subject: [PATCH 060/106] Add new fuzz file --- .../fuzzer/src/per_thing_rational.rs | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 primitives/arithmetic/fuzzer/src/per_thing_rational.rs diff --git a/primitives/arithmetic/fuzzer/src/per_thing_rational.rs b/primitives/arithmetic/fuzzer/src/per_thing_rational.rs new file mode 100644 index 0000000000000..84207cbd16469 --- /dev/null +++ b/primitives/arithmetic/fuzzer/src/per_thing_rational.rs @@ -0,0 +1,123 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! # Running +//! Running this fuzzer can be done with `cargo hfuzz run per_thing_rational`. `honggfuzz` CLI options can +//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads. +//! +//! # Debugging a panic +//! Once a panic is found, it can be debugged with +//! `cargo hfuzz run-debug per_thing_rational hfuzz_workspace/per_thing_rational/*.fuzz`. + +use honggfuzz::fuzz; +use sp_arithmetic::{ + PerThing, PerU16, Percent, Perbill, Perquintill, assert_eq_error_rate, + traits::SaturatedConversion, +}; + +fn main() { + loop { + fuzz!(| + data: ((u16, u16), (u32, u32), (u64, u64)) + | { + + let (u16_pair, u32_pair, u64_pair) = data; + + // peru16 + let (smaller, bigger) = (u16_pair.0.min(u16_pair.1), u16_pair.0.max(u16_pair.1)); + let ratio = PerU16::from_rational_approximation(smaller, bigger); + assert_per_thing_equal_error( + ratio, + PerU16::from_fraction(smaller as f64 / bigger.max(1) as f64), + 1, + ); + let (smaller, bigger) = (u32_pair.0.min(u32_pair.1), u32_pair.0.max(u32_pair.1)); + let ratio = PerU16::from_rational_approximation(smaller, bigger); + assert_per_thing_equal_error( + ratio, + PerU16::from_fraction(smaller as f64 / bigger.max(1) as f64), + 1, + ); + let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1)); + let ratio = PerU16::from_rational_approximation(smaller, bigger); + assert_per_thing_equal_error( + ratio, + PerU16::from_fraction(smaller as f64 / bigger.max(1) as f64), + 1, + ); + + // percent + let (smaller, bigger) = (u16_pair.0.min(u16_pair.1), u16_pair.0.max(u16_pair.1)); + let ratio = Percent::from_rational_approximation(smaller, bigger); + assert_per_thing_equal_error( + ratio, + Percent::from_fraction(smaller as f64 / bigger.max(1) as f64), + 1, + ); + + let (smaller, bigger) = (u32_pair.0.min(u32_pair.1), u32_pair.0.max(u32_pair.1)); + let ratio = Percent::from_rational_approximation(smaller, bigger); + assert_per_thing_equal_error( + ratio, + Percent::from_fraction(smaller as f64 / bigger.max(1) as f64), + 1, + ); + + let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1)); + let ratio = Percent::from_rational_approximation(smaller, bigger); + assert_per_thing_equal_error( + ratio, + Percent::from_fraction(smaller as f64 / bigger.max(1) as f64), + 1, + ); + + // perbill + let (smaller, bigger) = (u32_pair.0.min(u32_pair.1), u32_pair.0.max(u32_pair.1)); + let ratio = Perbill::from_rational_approximation(smaller, bigger); + assert_per_thing_equal_error( + ratio, + Perbill::from_fraction(smaller as f64 / bigger.max(1) as f64), + 100, + ); + + let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1)); + let ratio = Perbill::from_rational_approximation(smaller, bigger); + assert_per_thing_equal_error( + ratio, + Perbill::from_fraction(smaller as f64 / bigger.max(1) as f64), + 100, + ); + + // perquintillion + let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1)); + let ratio = Perquintill::from_rational_approximation(smaller, bigger); + assert_per_thing_equal_error( + ratio, + Perquintill::from_fraction(smaller as f64 / bigger.max(1) as f64), + 1000, + ); + + }) + } +} + +fn assert_per_thing_equal_error(a: T, b: T, err: u128) { + let a_abs = a.deconstruct().saturated_into::(); + let b_abs = b.deconstruct().saturated_into::(); + let diff = a_abs.max(b_abs) - a_abs.min(b_abs); + dbg!(&diff); + assert!(diff <= err, "{:?} !~ {:?}", a, b); +} From 7287c2b429fdc0a8f682527521652af7ae34a5e8 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 28 Feb 2020 14:08:52 +0100 Subject: [PATCH 061/106] Update lock --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 54cfa9161176e..f5f5382af1ded 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4360,6 +4360,7 @@ dependencies = [ "sp-application-crypto", "sp-core", "sp-io", + "sp-keyring", "sp-phragmen", "sp-runtime", "sp-staking", From ac6ce78b5c95df6a48bed7bfc7ccaf54c9f2f85f Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 28 Feb 2020 15:18:53 +0100 Subject: [PATCH 062/106] fix fuzzing code. --- frame/staking/fuzz/Cargo.lock | 371 +++++++++--------- frame/staking/fuzz/Cargo.toml | 26 +- frame/staking/fuzz/fuzz_targets/mock.rs | 2 +- .../fuzz/fuzz_targets/submit_solution.rs | 6 +- frame/staking/src/testing_utils.rs | 2 +- primitives/arithmetic/src/per_things.rs | 5 +- 6 files changed, 207 insertions(+), 205 deletions(-) diff --git a/frame/staking/fuzz/Cargo.lock b/frame/staking/fuzz/Cargo.lock index 7040f87b4bdd7..ded6e7cd64fd8 100644 --- a/frame/staking/fuzz/Cargo.lock +++ b/frame/staking/fuzz/Cargo.lock @@ -319,53 +319,54 @@ dependencies = [ [[package]] name = "frame-benchmarking" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0-dev", - "sp-io 2.0.0-dev", - "sp-runtime-interface 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-api 2.0.0-alpha.3", + "sp-io 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-runtime-interface 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "frame-metadata" -version = "11.0.0-dev" +version = "11.0.0-alpha.3" dependencies = [ "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-core 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "frame-support" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-metadata 11.0.0-dev", - "frame-support-procedural 2.0.0-dev", + "frame-metadata 11.0.0-alpha.3", + "frame-support-procedural 2.0.0-alpha.3", "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-arithmetic 2.0.0-dev", - "sp-core 2.0.0-dev", - "sp-inherents 2.0.0-dev", - "sp-io 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-state-machine 0.8.0-dev", - "sp-std 2.0.0-dev", + "sp-arithmetic 2.0.0-alpha.3", + "sp-core 2.0.0-alpha.3", + "sp-inherents 2.0.0-alpha.3", + "sp-io 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-state-machine 0.8.0-alpha.3", + "sp-std 2.0.0-alpha.3", "tracing 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "frame-support-procedural" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ - "frame-support-procedural-tools 2.0.0-dev", + "frame-support-procedural-tools 2.0.0-alpha.3", "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -373,9 +374,9 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ - "frame-support-procedural-tools-derive 2.0.0-dev", + "frame-support-procedural-tools-derive 2.0.0-alpha.3", "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -384,7 +385,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -393,17 +394,17 @@ dependencies = [ [[package]] name = "frame-system" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ - "frame-support 2.0.0-dev", + "frame-support 2.0.0-alpha.3", "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-dev", - "sp-io 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-std 2.0.0-dev", - "sp-version 2.0.0-dev", + "sp-core 2.0.0-alpha.3", + "sp-io 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", + "sp-version 2.0.0-alpha.3", ] [[package]] @@ -771,112 +772,112 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pallet-authorship" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ - "frame-support 2.0.0-dev", - "frame-system 2.0.0-dev", + "frame-support 2.0.0-alpha.3", + "frame-system 2.0.0-alpha.3", "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-authorship 2.0.0-dev", - "sp-core 2.0.0-dev", - "sp-inherents 2.0.0-dev", - "sp-io 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-authorship 2.0.0-alpha.3", + "sp-core 2.0.0-alpha.3", + "sp-inherents 2.0.0-alpha.3", + "sp-io 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "pallet-balances" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ - "frame-benchmarking 2.0.0-dev", - "frame-support 2.0.0-dev", - "frame-system 2.0.0-dev", + "frame-benchmarking 2.0.0-alpha.3", + "frame-support 2.0.0-alpha.3", + "frame-system 2.0.0-alpha.3", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-io 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-io 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "pallet-indices" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ - "frame-support 2.0.0-dev", - "frame-system 2.0.0-dev", + "frame-support 2.0.0-alpha.3", + "frame-system 2.0.0-alpha.3", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-dev", - "sp-io 2.0.0-dev", - "sp-keyring 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-core 2.0.0-alpha.3", + "sp-io 2.0.0-alpha.3", + "sp-keyring 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "pallet-session" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ - "frame-support 2.0.0-dev", - "frame-system 2.0.0-dev", + "frame-support 2.0.0-alpha.3", + "frame-system 2.0.0-alpha.3", "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-timestamp 2.0.0-dev", + "pallet-timestamp 2.0.0-alpha.3", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-io 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-staking 2.0.0-dev", - "sp-std 2.0.0-dev", - "sp-trie 2.0.0-dev", + "sp-io 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-staking 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", + "sp-trie 2.0.0-alpha.3", ] [[package]] name = "pallet-staking" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ - "frame-support 2.0.0-dev", - "frame-system 2.0.0-dev", - "pallet-authorship 2.0.0-dev", - "pallet-indices 2.0.0-dev", - "pallet-session 2.0.0-dev", + "frame-support 2.0.0-alpha.3", + "frame-system 2.0.0-alpha.3", + "pallet-authorship 2.0.0-alpha.3", + "pallet-indices 2.0.0-alpha.3", + "pallet-session 2.0.0-alpha.3", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0-dev", - "sp-core 2.0.0-dev", - "sp-io 2.0.0-dev", - "sp-phragmen 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-staking 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-application-crypto 2.0.0-alpha.3", + "sp-core 2.0.0-alpha.3", + "sp-io 2.0.0-alpha.3", + "sp-phragmen 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-staking 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "pallet-staking-fuzz" version = "0.0.0" dependencies = [ - "frame-support 2.0.0-dev", - "frame-system 2.0.0-dev", + "frame-support 2.0.0-alpha.3", + "frame-system 2.0.0-alpha.3", "libfuzzer-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-balances 2.0.0-dev", - "pallet-indices 2.0.0-dev", - "pallet-session 2.0.0-dev", - "pallet-staking 2.0.0-dev", - "pallet-staking-reward-curve 2.0.0-dev", - "pallet-timestamp 2.0.0-dev", + "pallet-balances 2.0.0-alpha.3", + "pallet-indices 2.0.0-alpha.3", + "pallet-session 2.0.0-alpha.3", + "pallet-staking 2.0.0-alpha.3", + "pallet-staking-reward-curve 2.0.0-alpha.3", + "pallet-timestamp 2.0.0-alpha.3", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-dev", - "sp-io 2.0.0-dev", - "sp-phragmen 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-core 2.0.0-alpha.3", + "sp-io 2.0.0-alpha.3", + "sp-phragmen 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "pallet-staking-reward-curve" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -886,19 +887,19 @@ dependencies = [ [[package]] name = "pallet-timestamp" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ - "frame-benchmarking 2.0.0-dev", - "frame-support 2.0.0-dev", - "frame-system 2.0.0-dev", + "frame-benchmarking 2.0.0-alpha.3", + "frame-support 2.0.0-alpha.3", + "frame-system 2.0.0-alpha.3", "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-inherents 2.0.0-dev", - "sp-io 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-std 2.0.0-dev", - "sp-timestamp 2.0.0-dev", + "sp-inherents 2.0.0-alpha.3", + "sp-io 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", + "sp-timestamp 2.0.0-alpha.3", ] [[package]] @@ -1365,21 +1366,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "sp-api" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api-proc-macro 2.0.0-dev", - "sp-core 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-state-machine 0.8.0-dev", - "sp-std 2.0.0-dev", - "sp-version 2.0.0-dev", + "sp-api-proc-macro 2.0.0-alpha.3", + "sp-core 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-state-machine 0.8.0-alpha.3", + "sp-std 2.0.0-alpha.3", + "sp-version 2.0.0-alpha.3", ] [[package]] name = "sp-api-proc-macro" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1390,40 +1391,40 @@ dependencies = [ [[package]] name = "sp-application-crypto" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-dev", - "sp-io 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-core 2.0.0-alpha.3", + "sp-io 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-arithmetic" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "integer-sqrt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-debug-derive 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-authorship" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-inherents 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-inherents 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-core" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1447,11 +1448,11 @@ dependencies = [ "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0-dev", - "sp-externalities 0.8.0-dev", - "sp-runtime-interface 2.0.0-dev", - "sp-std 2.0.0-dev", - "sp-storage 2.0.0-dev", + "sp-debug-derive 2.0.0-alpha.3", + "sp-externalities 0.8.0-alpha.3", + "sp-runtime-interface 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", + "sp-storage 2.0.0-alpha.3", "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1462,7 +1463,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1471,54 +1472,54 @@ dependencies = [ [[package]] name = "sp-externalities" -version = "0.8.0-dev" +version = "0.8.0-alpha.3" dependencies = [ "environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 2.0.0-dev", - "sp-storage 2.0.0-dev", + "sp-std 2.0.0-alpha.3", + "sp-storage 2.0.0-alpha.3", ] [[package]] name = "sp-inherents" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-core 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-io" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-dev", - "sp-externalities 0.8.0-dev", - "sp-runtime-interface 2.0.0-dev", - "sp-state-machine 0.8.0-dev", - "sp-std 2.0.0-dev", - "sp-trie 2.0.0-dev", - "sp-wasm-interface 2.0.0-dev", + "sp-core 2.0.0-alpha.3", + "sp-externalities 0.8.0-alpha.3", + "sp-runtime-interface 2.0.0-alpha.3", + "sp-state-machine 0.8.0-alpha.3", + "sp-std 2.0.0-alpha.3", + "sp-trie 2.0.0-alpha.3", + "sp-wasm-interface 2.0.0-alpha.3", ] [[package]] name = "sp-keyring" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-dev", - "sp-runtime 2.0.0-dev", + "sp-core 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-panic-handler" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1526,14 +1527,14 @@ dependencies = [ [[package]] name = "sp-phragmen" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-dev", + "sp-core 2.0.0-alpha.3", "sp-phragmen-compact 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-runtime 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] @@ -1548,7 +1549,7 @@ dependencies = [ [[package]] name = "sp-runtime" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1557,30 +1558,30 @@ dependencies = [ "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0-dev", - "sp-arithmetic 2.0.0-dev", - "sp-core 2.0.0-dev", - "sp-inherents 2.0.0-dev", - "sp-io 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-application-crypto 2.0.0-alpha.3", + "sp-arithmetic 2.0.0-alpha.3", + "sp-core 2.0.0-alpha.3", + "sp-inherents 2.0.0-alpha.3", + "sp-io 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-runtime-interface" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.8.0-dev", - "sp-runtime-interface-proc-macro 2.0.0-dev", - "sp-std 2.0.0-dev", - "sp-wasm-interface 2.0.0-dev", + "sp-externalities 0.8.0-alpha.3", + "sp-runtime-interface-proc-macro 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", + "sp-wasm-interface 2.0.0-alpha.3", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-runtime-interface-proc-macro" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1591,16 +1592,16 @@ dependencies = [ [[package]] name = "sp-staking" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-runtime 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-state-machine" -version = "0.8.0-dev" +version = "0.8.0-alpha.3" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1608,72 +1609,72 @@ dependencies = [ "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-dev", - "sp-externalities 0.8.0-dev", - "sp-panic-handler 2.0.0-dev", - "sp-trie 2.0.0-dev", + "sp-core 2.0.0-alpha.3", + "sp-externalities 0.8.0-alpha.3", + "sp-panic-handler 2.0.0-alpha.3", + "sp-trie 2.0.0-alpha.3", "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-std" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" [[package]] name = "sp-storage" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-debug-derive 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-timestamp" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0-dev", - "sp-inherents 2.0.0-dev", - "sp-runtime 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-api 2.0.0-alpha.3", + "sp-inherents 2.0.0-alpha.3", + "sp-runtime 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", "wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-trie" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "memory-db 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-core 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-version" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime 2.0.0-dev", - "sp-std 2.0.0-dev", + "sp-runtime 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-wasm-interface" -version = "2.0.0-dev" +version = "2.0.0-alpha.3" dependencies = [ "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 2.0.0-dev", + "sp-std 2.0.0-alpha.3", "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/frame/staking/fuzz/Cargo.toml b/frame/staking/fuzz/Cargo.toml index 7de772d4743bc..efcb5b1cfa2cf 100644 --- a/frame/staking/fuzz/Cargo.toml +++ b/frame/staking/fuzz/Cargo.toml @@ -12,19 +12,19 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.3" codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } -pallet-staking = { version = "2.0.0-dev", path = "..", features = ["testing-utils"] } -pallet-staking-reward-curve = { version = "2.0.0-dev", path = "../reward-curve" } -pallet-session = { version = "2.0.0-dev", path = "../../session" } -pallet-indices = { version = "2.0.0-dev", path = "../../indices" } -pallet-balances = { version = "2.0.0-dev", path = "../../balances" } -pallet-timestamp = { version = "2.0.0-dev", path = "../../timestamp" } -frame-system = { version = "2.0.0-dev", path = "../../system" } -frame-support = { version = "2.0.0-dev", path = "../../support" } -sp-std = { version = "2.0.0-dev", path = "../../../primitives/std" } -sp-io ={ version = "2.0.0-dev", path = "../../../primitives/io" } -sp-core = { version = "2.0.0-dev", path = "../../../primitives/core" } -sp-phragmen = { version = "2.0.0-dev", path = "../../../primitives/phragmen" } -sp-runtime = { version = "2.0.0-dev", path = "../../../primitives/runtime" } +pallet-staking = { version = "2.0.0-alpha.2", path = "..", features = ["testing-utils"] } +pallet-staking-reward-curve = { version = "2.0.0-alpha.2", path = "../reward-curve" } +pallet-session = { version = "2.0.0-alpha.2", path = "../../session" } +pallet-indices = { version = "2.0.0-alpha.2", path = "../../indices" } +pallet-balances = { version = "2.0.0-alpha.2", path = "../../balances" } +pallet-timestamp = { version = "2.0.0-alpha.2", path = "../../timestamp" } +frame-system = { version = "2.0.0-alpha.2", path = "../../system" } +frame-support = { version = "2.0.0-alpha.2", path = "../../support" } +sp-std = { version = "2.0.0-alpha.2", path = "../../../primitives/std" } +sp-io ={ version = "2.0.0-alpha.2", path = "../../../primitives/io" } +sp-core = { version = "2.0.0-alpha.2", path = "../../../primitives/core" } +sp-phragmen = { version = "2.0.0-alpha.2", path = "../../../primitives/phragmen" } +sp-runtime = { version = "2.0.0-alpha.2", path = "../../../primitives/runtime" } rand = "0.7.3" # Prevent this from interfering with workspaces diff --git a/frame/staking/fuzz/fuzz_targets/mock.rs b/frame/staking/fuzz/fuzz_targets/mock.rs index 523088c24eb00..8b68e74ba56e5 100644 --- a/frame/staking/fuzz/fuzz_targets/mock.rs +++ b/frame/staking/fuzz/fuzz_targets/mock.rs @@ -86,7 +86,7 @@ impl frame_system::Trait for Test { type ModuleToIndex = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); - type OnReapAccount = (Balances, Staking); + type OnKilledAccount = (Balances,); } parameter_types! { pub const ExistentialDeposit: Balance = 10; diff --git a/frame/staking/fuzz/fuzz_targets/submit_solution.rs b/frame/staking/fuzz/fuzz_targets/submit_solution.rs index 67f52320ddefd..5c9aa73bab585 100644 --- a/frame/staking/fuzz/fuzz_targets/submit_solution.rs +++ b/frame/staking/fuzz/fuzz_targets/submit_solution.rs @@ -47,10 +47,10 @@ pub fn new_test_ext() -> Result { fuzz_target!(|do_reduce: bool| { let ext = new_test_ext(); let mode: Mode = unsafe { std::mem::transmute(testing_utils::random(0, 2)) }; - let num_validators = testing_utils::random(50, 500); - let num_nominators = testing_utils::random(100, 500); + let num_validators = testing_utils::random(200, 1000); + let num_nominators = testing_utils::random(500, 2000); let edge_per_voter = testing_utils::random(1, 16); - let to_elect = testing_utils::random(10, 50); + let to_elect = testing_utils::random(10, 200); println!("+++ instance with params {} / {} / {} / {:?} / {}", num_nominators, diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index c22cba87b9d6d..badce8ca76f57 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -304,7 +304,7 @@ pub fn get_seq_phragmen_solution(do_reduce: bool) assignments, winners, do_reduce, - ) + ).unwrap() } /// Remove all validator, nominators, votes and exposures. diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index a80bc13c202fe..ca6967456b1b9 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -556,7 +556,7 @@ macro_rules! implement_per_thing { $num_type::max_value() ).0 as $upper_type, $name::one().0 as $upper_type, - 1, + 2, ); assert_eq_error_rate!( $name::from_rational_approximation( @@ -564,7 +564,7 @@ macro_rules! implement_per_thing { $num_type::max_value() ).0 as $upper_type, $name::from_parts($max / 3).0 as $upper_type, - 2 + 2, ); assert_eq!( $name::from_rational_approximation(1, $num_type::max_value()), @@ -604,6 +604,7 @@ macro_rules! implement_per_thing { $name::from_rational_approximation(3 * max_value / 2, 3 * max_value), $name::from_fraction(0.5), ); + $(per_thing_from_rationale_approx_test!($test_units);)* } From 7be2268863c36d827ce4c2d685c17deb16ebb071 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 2 Mar 2020 08:49:00 +0100 Subject: [PATCH 063/106] Fix nominator retain test --- frame/staking/src/tests.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index c5aa80025904f..53e9b29927341 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -3565,25 +3565,33 @@ mod offchain_phragmen { fn slash_kicks_validators_not_nominators() { ExtBuilder::default().build().execute_with(|| { start_era(1); + assert_eq_uvec!(Staking::current_elected(), vec![11, 21]); + // pre-slash balance assert_eq!(Balances::free_balance(11), 1000); - - let exposure = Staking::stakers(&11); assert_eq!(Balances::free_balance(101), 2000); - let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; + + // 11 and 21 both have the support of 100 + let exposure_11 = Staking::stakers(&11); + let exposure_21 = Staking::stakers(&21); + + assert_eq!(exposure_11.total, 1000 + 125); + assert_eq!(exposure_21.total, 1000 + 375); on_offence_now( &[ OffenceDetails { - offender: (11, exposure.clone()), + offender: (11, exposure_11.clone()), reporters: vec![], }, ], &[Perbill::from_percent(10)], ); + // post-slash balance + let nominator_slash_amount_11 = (125 / 10); assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); + assert_eq!(Balances::free_balance(101), 2000 - nominator_slash_amount_11); // This is the best way to check that the validator was chilled; `get` will // return default value. @@ -3597,6 +3605,19 @@ fn slash_kicks_validators_not_nominators() { // re-registers. let last_slash = ::SlashingSpans::get(&11).unwrap().last_nonzero_slash(); assert!(nominations.submitted_in < last_slash); + + // actually re-bond the slashed validator + assert_ok!(Staking::validate(Origin::signed(10), Default::default())); + + start_era(2); + let exposure_11 = Staking::stakers(&11); + let exposure_21 = Staking::stakers(&21); + + // 10 is re-elected, but without the support of 100 + assert_eq!(exposure_11.total, 900); + + // 20 is re-elected, with the (almost) entire support of 100 + assert_eq!(exposure_21.total, 1000 + 500 - nominator_slash_amount_11); }); } From 1bd7850751320a818f6f03aa419727f9925c7654 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 2 Mar 2020 12:11:21 +0100 Subject: [PATCH 064/106] Add slashing check --- frame/staking/src/lib.rs | 9 ++- frame/staking/src/mock.rs | 4 +- frame/staking/src/slashing.rs | 4 +- frame/staking/src/tests.rs | 109 +++++++++++++++++++++++++++------- 4 files changed, 101 insertions(+), 25 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 1f78d65d98c02..9cb01e95c7ad6 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -987,7 +987,7 @@ decl_error! { /// Error while building the assignment type from the compact. This can happen if an index /// is invalid, or if the weights _overflow_. PhragmenBogusCompact, - /// One of the submitted nominators is not an active nominator on chain. DEPRECATED. + /// One of the submitted nominators is not an active nominator on chain. PhragmenBogusNominator, /// One of the submitted nominators has an edge to which they have not voted on chain. PhragmenBogusNomination, @@ -1881,8 +1881,15 @@ impl Module { // NOTE: we don't really have to check here if the sum of all edges are the // nominator correct. Un-compacting assures this by definition. ensure!( + // each target in the provided distribution must be actually nominated by the + // nominator after the last non-zero slash. distribution.into_iter().all(|(t, _)| { nomination.targets.iter().find(|&tt| tt == t).is_some() + && + ::SlashingSpans::get(&t).map_or( + true, + |spans| nomination.submitted_in >= spans.last_nonzero_slash(), + ) }), Error::::PhragmenBogusNomination, ); diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 550e1a7e0906d..570214fffcba3 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -824,7 +824,7 @@ pub fn horrible_phragmen_with_post_processing( // Ensure that this result is worse than seq-phragmen. Otherwise, it should not have been used // for testing. let score = { - let (_, _, better_score) = do_phragmen_with_post_processing(true, |_| {}); + let (_, _, better_score) = prepare_submission_with(true, |_| {}); let support = build_support_map::(&winners, &staked_assignment).0; let score = evaluate_support(&support); @@ -867,7 +867,7 @@ pub fn horrible_phragmen_with_post_processing( // Note: this should always logically reproduce [`offchain_election::prepare_submission`], yet we // cannot do it since we want to have `tweak` injected into the process. -pub fn do_phragmen_with_post_processing( +pub fn prepare_submission_with( do_reduce: bool, tweak: impl FnOnce(&mut Vec>), ) -> ( diff --git a/frame/staking/src/slashing.rs b/frame/staking/src/slashing.rs index 13b717ab19c8d..efcd3d09de17d 100644 --- a/frame/staking/src/slashing.rs +++ b/frame/staking/src/slashing.rs @@ -52,7 +52,7 @@ use super::{ EraIndex, Trait, Module, Store, BalanceOf, Exposure, Perbill, SessionInterface, NegativeImbalanceOf, UnappliedSlash, }; -use sp_runtime::{traits::{Zero, Saturating}, PerThing}; +use sp_runtime::{traits::{Zero, Saturating}, PerThing, RuntimeDebug}; use frame_support::{ StorageMap, StorageDoubleMap, traits::{Currency, OnUnbalanced, Imbalance}, @@ -83,7 +83,7 @@ impl SlashingSpan { } /// An encoding of all of a nominator's slashing spans. -#[derive(Encode, Decode)] +#[derive(Encode, Decode, RuntimeDebug)] pub struct SlashingSpans { // the index of the current slashing span of the nominator. different for // every stash, resets when the account hits free balance 0. diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 53e9b29927341..1e0599c41a553 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2910,7 +2910,7 @@ mod offchain_phragmen { assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); assert!(Staking::snapshot_validators().is_some()); - let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); assert_ok!(Staking::submit_election_solution( Origin::signed(10), winners, @@ -2948,7 +2948,7 @@ mod offchain_phragmen { run_to_block(14); assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); - let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); assert_ok!( Staking::submit_election_solution( Origin::signed(10), @@ -2992,7 +2992,7 @@ mod offchain_phragmen { // create all the indices just to build the solution. Staking::create_stakers_snapshot(); - let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); Staking::kill_stakers_snapshot(); assert_noop!( @@ -3016,7 +3016,7 @@ mod offchain_phragmen { run_to_block(12); // a good solution - let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); // a bad solution @@ -3046,7 +3046,7 @@ mod offchain_phragmen { assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); // a better solution - let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); }) } @@ -3105,7 +3105,7 @@ mod offchain_phragmen { ext.execute_with(||{ run_to_block(12); // put a good solution on-chain - let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); assert_ok!( Staking::submit_election_solution(Origin::signed(10), winners, compact, score), ); @@ -3164,7 +3164,7 @@ mod offchain_phragmen { run_to_block(12); ValidatorCount::put(3); - let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); ValidatorCount::put(4); assert_eq!(winners.len(), 3); @@ -3190,7 +3190,7 @@ mod offchain_phragmen { run_to_block(12); ValidatorCount::put(3); - let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); ValidatorCount::put(4); assert_eq!(winners.len(), 3); @@ -3215,7 +3215,7 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); assert_eq!(winners.len(), 4); @@ -3241,7 +3241,7 @@ mod offchain_phragmen { assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); - let (mut compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + let (mut compact, winners, score) = prepare_submission_with(true, |_| {}); // index 9 doesn't exist. compact.votes1.push((9, 2)); @@ -3269,7 +3269,7 @@ mod offchain_phragmen { assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); - let (mut compact, winners, score) = do_phragmen_with_post_processing(true, |_| {}); + let (mut compact, winners, score) = prepare_submission_with(true, |_| {}); // index 4 doesn't exist. compact.votes1.push((3, 4)); @@ -3297,7 +3297,7 @@ mod offchain_phragmen { assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); - let (compact, _, score) = do_phragmen_with_post_processing(true, |_| {}); + let (compact, _, score) = prepare_submission_with(true, |_| {}); // index 4 doesn't exist. let winners = vec![0, 1, 2, 4]; @@ -3325,7 +3325,7 @@ mod offchain_phragmen { assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); - let (compact, winners, score) = do_phragmen_with_post_processing(true, |a| { + let (compact, winners, score) = prepare_submission_with(true, |a| { a.iter_mut().find(|x| x.who == 5).map(|x| x.distribution = vec![(20, 50), (40, 30), (30, 20)] ); @@ -3351,7 +3351,7 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (compact, winners, score) = do_phragmen_with_post_processing(true, |a| { + let (compact, winners, score) = prepare_submission_with(true, |a| { // mutate a self vote to target someone else. That someone else is still among the // winners a.iter_mut().find(|x| x.who == 10).map(|x| @@ -3379,7 +3379,7 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (compact, winners, score) = do_phragmen_with_post_processing(true, |a| { + let (compact, winners, score) = prepare_submission_with(true, |a| { // Remove the self vote. a.retain(|x| x.who != 10); // add is as a new double vote @@ -3409,7 +3409,7 @@ mod offchain_phragmen { // Note: we don't reduce here to be able to tweak votes3. votes3 will vanish if you // reduce. - let (mut compact, winners, score) = do_phragmen_with_post_processing(false, |_| {}); + let (mut compact, winners, score) = prepare_submission_with(false, |_| {}); if let Some(c) = compact.votes3.iter_mut().find(|x| x.0 == 0) { // by default it should have been (0, [(2, 33%), (1, 33%)], 0) @@ -3446,7 +3446,7 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (compact, winners, score) = do_phragmen_with_post_processing(false, |a| { + let (compact, winners, score) = prepare_submission_with(false, |a| { // 3 only voted for 20 and 40. We add a fake vote to 30. The stake sum is still // correctly 100. a.iter_mut().find(|x| x.who == 3).map(|x| { @@ -3461,6 +3461,75 @@ mod offchain_phragmen { }) } + #[test] + fn nomination_slash_filter_is_checked() { + // If a nominator has voted for someone who has been recently slashed, that particular + // nomination should be disabled for the upcoming election. A solution mut respect this + // rule. + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with(|| { + build_offchain_phragmen_test_ext(); + // finalize the round with fallback. This is needed since all nominator submission + // are in era zero and we want this one to pass with no problems. + run_to_block(15); + + // open the election window and create snapshots. + run_to_block(27); + + // a solution that has been prepared before the slash. + let (pre_compact, pre_winners, pre_score) = prepare_submission_with(false, |_| {}); + + // slash 10 + let offender_expo = Staking::stakers(10); + on_offence_now( + &[ + OffenceDetails { + offender: (10, offender_expo.clone()), + reporters: vec![], + }, + ], + &[Perbill::from_percent(10)], + ); + + // validate 10 again for the next round. + assert_ok!(Staking::validate(Origin::signed(10 + 1000), Default::default())); + + // a solution that has been prepared after the slash. + let (compact, winners, score) = prepare_submission_with(false, |_| {}); + + // The edges are different. + assert_ne!(compact, pre_compact); + // But the winners are the same. + assert_eq_uvec!(winners, pre_winners); + // 2 votes for 10 and 20, but the edge for 20 is gone, so there must be one less + // entry in votes2. + assert_eq!(compact.votes2.len(), pre_compact.votes2.len() - 1); + + // old results are invalid. + assert_noop!( + Staking::submit_election_solution( + Origin::signed(10), + pre_winners, + pre_compact, + pre_score, + ), + Error::::PhragmenBogusNomination, + ); + + // new results are okay. + assert_ok!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + ); + + // finish the round. + run_to_block(30); + }) + } + #[test] fn invalid_phragmen_result_wrong_score() { // A valid voter who's total distributed stake is more than what they bond @@ -3474,7 +3543,7 @@ mod offchain_phragmen { build_offchain_phragmen_test_ext(); run_to_block(12); - let (compact, winners, mut score) = do_phragmen_with_post_processing(true, |_| {}); + let (compact, winners, mut score) = prepare_submission_with(true, |_| {}); score[0] += 1; assert_noop!( @@ -3562,7 +3631,7 @@ mod offchain_phragmen { } #[test] -fn slash_kicks_validators_not_nominators() { +fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_validator() { ExtBuilder::default().build().execute_with(|| { start_era(1); assert_eq_uvec!(Staking::current_elected(), vec![11, 21]); @@ -3589,7 +3658,7 @@ fn slash_kicks_validators_not_nominators() { ); // post-slash balance - let nominator_slash_amount_11 = (125 / 10); + let nominator_slash_amount_11 = 125 / 10; assert_eq!(Balances::free_balance(11), 900); assert_eq!(Balances::free_balance(101), 2000 - nominator_slash_amount_11); From 5f9844d99aac4af3f7985b70581167479cfc88a2 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Mon, 2 Mar 2020 15:37:05 +0100 Subject: [PATCH 065/106] Update frame/staking/src/tests.rs Co-Authored-By: Joshy Orndorff --- frame/staking/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 1e0599c41a553..e189cfee74f8d 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -3464,7 +3464,7 @@ mod offchain_phragmen { #[test] fn nomination_slash_filter_is_checked() { // If a nominator has voted for someone who has been recently slashed, that particular - // nomination should be disabled for the upcoming election. A solution mut respect this + // nomination should be disabled for the upcoming election. A solution must respect this // rule. ExtBuilder::default() .offchain_phragmen_ext() From 555007f98f8d65a9677a0213a0c245c4910c9d1b Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 2 Mar 2020 19:49:55 +0100 Subject: [PATCH 066/106] Some formatting nits --- frame/staking/src/offchain_election.rs | 148 +++-- frame/staking/src/testing_utils.rs | 164 ++--- frame/staking/src/tests.rs | 842 +++++++++++++------------ primitives/phragmen/src/helpers.rs | 36 +- primitives/phragmen/src/lib.rs | 70 +- primitives/phragmen/src/node.rs | 45 +- primitives/phragmen/src/reduce.rs | 800 ++++++++++------------- 7 files changed, 1030 insertions(+), 1075 deletions(-) diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 7f11381957405..2e7daa92cab88 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -16,20 +16,18 @@ //! Helpers for offchain worker election. -use crate::{ - Call, Module, Trait, ValidatorIndex, NominatorIndex, Compact, OffchainAccuracy, -}; +use crate::{Call, Compact, Module, NominatorIndex, OffchainAccuracy, Trait, ValidatorIndex}; use codec::Encode; -use frame_system::offchain::{SubmitUnsignedTransaction}; use frame_support::debug; +use frame_system::offchain::SubmitUnsignedTransaction; use sp_phragmen::{ - reduce, ExtendedBalance, PhragmenResult, Assignment, PhragmenScore, - build_support_map, evaluate_support, + build_support_map, evaluate_support, reduce, Assignment, ExtendedBalance, PhragmenResult, + PhragmenScore, }; -use sp_std::{prelude::*, convert::TryInto}; -use sp_runtime::{RuntimeAppPublic, RuntimeDebug}; use sp_runtime::offchain::storage::StorageValueRef; use sp_runtime::PerThing; +use sp_runtime::{RuntimeAppPublic, RuntimeDebug}; +use sp_std::{convert::TryInto, prelude::*}; #[derive(RuntimeDebug)] pub enum OffchainElectionError { @@ -64,22 +62,23 @@ pub(crate) fn set_check_offchain_execution_status( 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), - } - }); + 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 @@ -98,10 +97,11 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti // For each local key is in the stored authority keys, try and submit. Breaks out after first // successful submission. - for (index, ref pubkey) in local_keys.into_iter().filter_map(|key| - keys.iter().enumerate().find(|(_, val_key)| **val_key == key) - ) { - + for (index, ref pubkey) in local_keys.into_iter().filter_map(|key| { + keys.iter() + .enumerate() + .find(|(_, val_key)| **val_key == key) + }) { // compute raw solution. let PhragmenResult { winners, @@ -110,16 +110,12 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti .ok_or(OffchainElectionError::ElectionFailed)?; // process and prepare it for submission. - let (winners, compact, score) = prepare_submission::( - assignments, - winners, - true, - )?; + let (winners, compact, score) = prepare_submission::(assignments, winners, true)?; // sign it. - let signature_payload: SignaturePayload = - (&winners, &compact, &score, &(index as u32)); - let signature = pubkey.sign(&signature_payload.encode()) + let signature_payload: SignaturePayload = (&winners, &compact, &score, &(index as u32)); + let signature = pubkey + .sign(&signature_payload.encode()) .ok_or(OffchainElectionError::SigningFailed)?; // send it. @@ -129,16 +125,21 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti score, index as u32, signature, - ).into(); - - let ok = T::SubmitTransaction::submit_unsigned(call).map_err(|_| { - debug::native::warn!( - target: "staking", - "failed to submit offchain solution with key {:?}", - pubkey, - ); - }).is_ok(); - if ok { return Ok(()) } + ) + .into(); + + let ok = T::SubmitTransaction::submit_unsigned(call) + .map_err(|_| { + debug::native::warn!( + target: "staking", + "failed to submit offchain solution with key {:?}", + pubkey, + ); + }) + .is_ok(); + if ok { + return Ok(()); + } } // no key left and no submission. @@ -158,25 +159,30 @@ where ExtendedBalance: From<::Inner>, { // make sure that the snapshot is available. - let snapshot_validators = >::snapshot_validators() - .ok_or(OffchainElectionError::SnapshotUnavailable)?; - let snapshot_nominators = >::snapshot_nominators() - .ok_or(OffchainElectionError::SnapshotUnavailable)?; + let snapshot_validators = + >::snapshot_validators().ok_or(OffchainElectionError::SnapshotUnavailable)?; + let snapshot_nominators = + >::snapshot_nominators().ok_or(OffchainElectionError::SnapshotUnavailable)?; // all helper closures let nominator_index = |a: &T::AccountId| -> Option { - snapshot_nominators.iter().position(|x| x == a).and_then(|i| - >::try_into(i).ok() - ) + snapshot_nominators + .iter() + .position(|x| x == a) + .and_then(|i| >::try_into(i).ok()) }; let validator_index = |a: &T::AccountId| -> Option { - snapshot_validators.iter().position(|x| x == a).and_then(|i| - >::try_into(i).ok() - ) + snapshot_validators + .iter() + .position(|x| x == a) + .and_then(|i| >::try_into(i).ok()) }; // Clean winners. - let winners = winners.into_iter().map(|(w, _)| w).collect::>(); + let winners = winners + .into_iter() + .map(|(w, _)| w) + .collect::>(); // convert into absolute value and to obtain the reduced version. let mut staked = sp_phragmen::assignment_ratio_to_staked( @@ -205,24 +211,28 @@ where >::slashable_balance_of_extended, ); - let (support_map, _) = build_support_map::( - winners.as_slice(), - staked.as_slice(), - ); + let (support_map, _) = + build_support_map::(winners.as_slice(), staked.as_slice()); evaluate_support::(&support_map) }; // compact encode the assignment. - let compact = Compact::from_assignment( - low_accuracy_assignment, - nominator_index, - validator_index, - ).unwrap(); + let compact = + Compact::from_assignment(low_accuracy_assignment, nominator_index, validator_index) + .unwrap(); // winners to index. - let winners = winners.into_iter().map(|w| - snapshot_validators.iter().position(|v| *v == w).unwrap().try_into().unwrap() - ).collect::>(); + let winners = winners + .into_iter() + .map(|w| { + snapshot_validators + .iter() + .position(|v| *v == w) + .unwrap() + .try_into() + .unwrap() + }) + .collect::>(); Ok((winners, compact, score)) } diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index badce8ca76f57..94fdd18597b9a 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -21,14 +21,16 @@ //! this feature in the current crate's Cargo.toml will leak the all of this into a normal release //! build. Just don't do it. -use rand::Rng; -use codec::{Encode, Decode}; -use sp_core::hashing::blake2_256; -use sp_phragmen::{reduce, evaluate_support, build_support_map, Assignment, StakedAssignment, PhragmenScore}; +use crate::*; +use codec::{Decode, Encode}; +use frame_support::assert_ok; use frame_system::RawOrigin; -use frame_support::{assert_ok}; use pallet_indices::address::Address; -use crate::*; +use rand::Rng; +use sp_core::hashing::blake2_256; +use sp_phragmen::{ + build_support_map, evaluate_support, reduce, Assignment, PhragmenScore, StakedAssignment, +}; const CTRL_PREFIX: u32 = 1000; const NOMINATOR_PREFIX: u32 = 1_000_000; @@ -47,7 +49,7 @@ pub fn random(a: u32, b: u32) -> u32 { /// Set the desired validator count, with related storage items. pub fn set_validator_count(to_elect: u32) { ValidatorCount::put(to_elect); - MinimumValidatorCount::put(to_elect/2); + MinimumValidatorCount::put(to_elect / 2); >::put(ElectionStatus::Open(T::BlockNumber::from(1u32))); } @@ -74,7 +76,8 @@ pub fn signed_account(index: u32) -> T::Origin { /// Bond a validator. pub fn bond_validator(stash: T::AccountId, ctrl: u32, val: BalanceOf) - where T::Lookup: StaticLookup> +where + T::Lookup: StaticLookup>, { let _ = T::Currency::make_free_balance_be(&stash, val); assert_ok!(>::bond( @@ -93,9 +96,10 @@ pub fn bond_nominator( stash: T::AccountId, ctrl: u32, val: BalanceOf, - target: Vec> -) where T::Lookup: StaticLookup> { - + target: Vec>, +) where + T::Lookup: StaticLookup>, +{ let _ = T::Currency::make_free_balance_be(&stash, val); assert_ok!(>::bond( signed::(stash), @@ -108,11 +112,10 @@ pub fn bond_nominator( /// Bond `nun_validators` validators and `num_nominator` nominators with `edge_per_voter` random /// votes per nominator. -pub fn setup_chain_stakers( - num_validators: u32, - num_voters: u32, - edge_per_voter: u32, -) where T::Lookup: StaticLookup> { +pub fn setup_chain_stakers(num_validators: u32, num_voters: u32, edge_per_voter: u32) +where + T::Lookup: StaticLookup>, +{ (0..num_validators).for_each(|i| { // println!("bonding validator {}/{}", i, num_validators); bond_validator::( @@ -124,7 +127,9 @@ pub fn setup_chain_stakers( (0..num_voters).for_each(|i| { let mut targets: Vec> = Vec::with_capacity(edge_per_voter as usize); - let mut all_targets = (0..num_validators).map(|t| address::(t)).collect::>(); + let mut all_targets = (0..num_validators) + .map(|t| address::(t)) + .collect::>(); assert!(num_validators >= edge_per_voter); (0..edge_per_voter).for_each(|_| { let target = all_targets.remove(random(0, all_targets.len() as u32 - 1) as usize); @@ -139,14 +144,14 @@ pub fn setup_chain_stakers( ); }); - >::create_stakers_snapshot(); } /// Build a _really bad_ but acceptable solution for election. This should always yield a solution /// which has a less score than the seq-phragmen. -pub fn get_weak_solution(do_reduce: bool) --> (Vec, Compact, PhragmenScore) { +pub fn get_weak_solution( + do_reduce: bool, +) -> (Vec, Compact, PhragmenScore) { use sp_std::collections::btree_map::BTreeMap; let mut backing_stake_of: BTreeMap> = BTreeMap::new(); @@ -164,7 +169,6 @@ pub fn get_weak_solution(do_reduce: bool) }) }); - // elect winners let mut sorted: Vec = backing_stake_of.keys().cloned().collect(); sorted.sort_by_key(|x| backing_stake_of.get(x).unwrap()); @@ -197,15 +201,17 @@ pub fn get_weak_solution(do_reduce: bool) // assign main portion // only take the first half into account. This should highly imbalance stuff, which is good. - dist - .iter_mut() - .take( if dist_len > 1 { (dist_len as usize) / 2 } else { 1 } ) - .for_each(|(_, w)| - { - let partial = stake / dist_len; - *w = partial; - sum += partial; - }); + dist.iter_mut() + .take(if dist_len > 1 { + (dist_len as usize) / 2 + } else { + 1 + }) + .for_each(|(_, w)| { + let partial = stake / dist_len; + *w = partial; + sum += partial; + }); // assign the leftover to last. let leftover = stake - sum; @@ -219,15 +225,17 @@ pub fn get_weak_solution(do_reduce: bool) }); // add self support to winners. - winners.iter().for_each(|w| staked_assignments.push(StakedAssignment { - who: w.clone(), - distribution: vec![( - w.clone(), - , u64>>::convert( - >::slashable_balance_of(&w) - ) as ExtendedBalance, - )] - })); + winners.iter().for_each(|w| { + staked_assignments.push(StakedAssignment { + who: w.clone(), + distribution: vec![( + w.clone(), + , u64>>::convert( + >::slashable_balance_of(&w), + ) as ExtendedBalance, + )], + }) + }); if do_reduce { reduce(&mut staked_assignments); @@ -238,73 +246,77 @@ pub fn get_weak_solution(do_reduce: bool) let snapshot_nominators = >::snapshot_nominators().unwrap(); let nominator_index = |a: &T::AccountId| -> Option { - snapshot_nominators.iter().position(|x| x == a).and_then(|i| - >::try_into(i).ok() - ) + snapshot_nominators + .iter() + .position(|x| x == a) + .and_then(|i| >::try_into(i).ok()) }; let validator_index = |a: &T::AccountId| -> Option { - snapshot_validators.iter().position(|x| x == a).and_then(|i| - >::try_into(i).ok() - ) + snapshot_validators + .iter() + .position(|x| x == a) + .and_then(|i| >::try_into(i).ok()) }; let stake_of = |who: &T::AccountId| -> ExtendedBalance { , u64>>::convert( - >::slashable_balance_of(who) + >::slashable_balance_of(who), ) as ExtendedBalance }; // convert back to ratio assignment. This takes less space. - let low_accuracy_assignment: Vec> = staked_assignments - .into_iter() - .map(|sa| sa.into_assignment(true)) - .collect(); + let low_accuracy_assignment: Vec> = + staked_assignments + .into_iter() + .map(|sa| sa.into_assignment(true)) + .collect(); // re-calculate score based on what the chain will decode. let score = { let staked: Vec> = low_accuracy_assignment - .iter() - .map(|a| { - let stake = stake_of(&a.who); - a.clone().into_staked(stake, true) - }).collect(); - - let (support_map, _) = build_support_map::( - winners.as_slice(), - staked.as_slice(), - ); + .iter() + .map(|a| { + let stake = stake_of(&a.who); + a.clone().into_staked(stake, true) + }) + .collect(); + + let (support_map, _) = + build_support_map::(winners.as_slice(), staked.as_slice()); evaluate_support::(&support_map) }; - // compact encode the assignment. - let compact = Compact::from_assignment( - low_accuracy_assignment, - nominator_index, - validator_index, - ).unwrap(); + let compact = + Compact::from_assignment(low_accuracy_assignment, nominator_index, validator_index) + .unwrap(); // winners to index. - let winners = winners.into_iter().map(|w| - snapshot_validators.iter().position(|v| *v == w).unwrap().try_into().unwrap() - ).collect::>(); + let winners = winners + .into_iter() + .map(|w| { + snapshot_validators + .iter() + .position(|v| *v == w) + .unwrap() + .try_into() + .unwrap() + }) + .collect::>(); (winners, compact, score) } /// Create a solution for seq-phragmen. This uses the same internal function as used by the offchain /// worker code. -pub fn get_seq_phragmen_solution(do_reduce: bool) --> (Vec, Compact, PhragmenScore) { +pub fn get_seq_phragmen_solution( + do_reduce: bool, +) -> (Vec, Compact, PhragmenScore) { let sp_phragmen::PhragmenResult { winners, assignments, } = >::do_phragmen::().unwrap(); - offchain_election::prepare_submission::( - assignments, - winners, - do_reduce, - ).unwrap() + offchain_election::prepare_submission::(assignments, winners, do_reduce).unwrap() } /// Remove all validator, nominators, votes and exposures. diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 1e0599c41a553..07001039f3eea 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2687,22 +2687,21 @@ fn remove_multi_deferred() { } mod offchain_phragmen { - use std::sync::Arc; - use parking_lot::RwLock; use crate::*; + use frame_support::{assert_noop, assert_ok, debug}; use mock::*; - use sp_core::traits::KeystoreExt; - use sp_core::testing::KeyStore; + use parking_lot::RwLock; use sp_core::offchain::{ - OffchainExt, - TransactionPoolExt, - testing::{TestOffchainExt, TestTransactionPoolExt, PoolState}, + testing::{PoolState, TestOffchainExt, TestTransactionPoolExt}, + OffchainExt, TransactionPoolExt, }; + use sp_core::testing::KeyStore; + use sp_core::traits::KeystoreExt; use sp_io::TestExternalities; - use substrate_test_utils::assert_eq_uvec; - use frame_support::{assert_ok, assert_noop, debug}; - use sp_runtime::traits::OffchainWorker; use sp_phragmen::StakedAssignment; + use sp_runtime::traits::OffchainWorker; + use std::sync::Arc; + use substrate_test_utils::assert_eq_uvec; type DummyT = dummy_sr25519::AuthorityId; @@ -2736,11 +2735,14 @@ mod offchain_phragmen { let account = LOCAL_KEY_ACCOUNT.with(|v| *v.borrow()); let key = dummy_sr25519::dummy_key_for(account); - let _ = keystore.write().insert_unknown( - ::ID, - &format!("{}/staking{}", mock::PHRASE, account), - key.as_ref(), - ).unwrap(); + let _ = keystore + .write() + .insert_unknown( + ::ID, + &format!("{}/staking{}", mock::PHRASE, account), + key.as_ref(), + ) + .unwrap(); debug::native::debug!( target: "staking", "generated key for account {}: {:?}", @@ -2755,23 +2757,26 @@ mod offchain_phragmen { #[test] fn is_current_session_final_works() { - ExtBuilder::default().session_per_era(3).build().execute_with(|| { - start_era(1); - assert_eq!(Session::current_index(), 4); - assert_eq!(Staking::current_era(), 1); - assert_eq!(Staking::is_current_session_final(), false); - - start_session(4); - assert_eq!(Session::current_index(), 5); - assert_eq!(Staking::current_era(), 1); - assert_eq!(Staking::is_current_session_final(), true); - - start_session(5); - assert_eq!(Session::current_index(), 6); - // era changed. - assert_eq!(Staking::current_era(), 2); - assert_eq!(Staking::is_current_session_final(), false); - }) + ExtBuilder::default() + .session_per_era(3) + .build() + .execute_with(|| { + start_era(1); + assert_eq!(Session::current_index(), 4); + assert_eq!(Staking::current_era(), 1); + assert_eq!(Staking::is_current_session_final(), false); + + start_session(4); + assert_eq!(Session::current_index(), 5); + assert_eq!(Staking::current_era(), 1); + assert_eq!(Staking::is_current_session_final(), true); + + start_session(5); + assert_eq!(Session::current_index(), 6); + // era changed. + assert_eq!(Staking::current_era(), 2); + assert_eq!(Staking::is_current_session_final(), false); + }) } #[test] @@ -2781,46 +2786,45 @@ mod offchain_phragmen { .session_length(10) .election_lookahead(3) .build() - .execute_with( - || { - run_to_block(10); - assert_session_era!(1, 0); - assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); - assert!(Staking::snapshot_nominators().is_none()); - assert!(Staking::snapshot_validators().is_none()); - - run_to_block(18); - assert_session_era!(1, 0); - assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); - assert!(Staking::snapshot_nominators().is_none()); - assert!(Staking::snapshot_validators().is_none()); - - run_to_block(40); - assert_session_era!(4, 0); - assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); - assert!(Staking::snapshot_nominators().is_none()); - assert!(Staking::snapshot_validators().is_none()); - - run_to_block(46); - assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); - assert!(Staking::snapshot_nominators().is_none()); - assert!(Staking::snapshot_validators().is_none()); - - run_to_block(47); - assert_eq!(Staking::era_election_status(), ElectionStatus::Open(47)); - assert!(Staking::snapshot_nominators().is_some()); - assert!(Staking::snapshot_validators().is_some()); - - run_to_block(49); - assert_eq!(Staking::era_election_status(), ElectionStatus::Open(47)); - assert!(Staking::snapshot_nominators().is_some()); - assert!(Staking::snapshot_validators().is_some()); - - run_to_block(50); - assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); - assert!(Staking::snapshot_nominators().is_none()); - assert!(Staking::snapshot_validators().is_none()); - }) + .execute_with(|| { + run_to_block(10); + assert_session_era!(1, 0); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); + assert!(Staking::snapshot_nominators().is_none()); + assert!(Staking::snapshot_validators().is_none()); + + run_to_block(18); + assert_session_era!(1, 0); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); + assert!(Staking::snapshot_nominators().is_none()); + assert!(Staking::snapshot_validators().is_none()); + + run_to_block(40); + assert_session_era!(4, 0); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); + assert!(Staking::snapshot_nominators().is_none()); + assert!(Staking::snapshot_validators().is_none()); + + run_to_block(46); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); + assert!(Staking::snapshot_nominators().is_none()); + assert!(Staking::snapshot_validators().is_none()); + + run_to_block(47); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(47)); + assert!(Staking::snapshot_nominators().is_some()); + assert!(Staking::snapshot_validators().is_some()); + + run_to_block(49); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(47)); + assert!(Staking::snapshot_nominators().is_some()); + assert!(Staking::snapshot_validators().is_some()); + + run_to_block(50); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); + assert!(Staking::snapshot_nominators().is_none()); + assert!(Staking::snapshot_validators().is_none()); + }) } #[test] @@ -2831,13 +2835,18 @@ mod offchain_phragmen { assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); // some election must have happened by now. assert_eq!( - System::events().into_iter().map(|r| r.event).filter_map(|e| { - if let MetaEvent::staking(inner) = e { - Some(inner) - } else { - None - } - }).last().unwrap(), + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| { + if let MetaEvent::staking(inner) = e { + Some(inner) + } else { + None + } + }) + .last() + .unwrap(), RawEvent::StakingElection(ElectionCompute::OnChain), ); }) @@ -2850,23 +2859,22 @@ mod offchain_phragmen { .offchain_phragmen_ext() .election_lookahead(3) .build() - .execute_with( - || { - run_to_block(12); - assert!(Staking::snapshot_validators().is_some()); - assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); - - // nominate more than the limit - let limit: NominatorIndex = ValidatorIndex::max_value() as NominatorIndex + 1; - let ctrl = 1_000_000; - for i in 0..limit { - bond_validator((1000 + i).into(), (1000 + i + ctrl).into(), 100); - } + .execute_with(|| { + run_to_block(12); + assert!(Staking::snapshot_validators().is_some()); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); + + // nominate more than the limit + let limit: NominatorIndex = ValidatorIndex::max_value() as NominatorIndex + 1; + let ctrl = 1_000_000; + for i in 0..limit { + bond_validator((1000 + i).into(), (1000 + i + ctrl).into(), 100); + } - run_to_block(27); - assert!(Staking::snapshot_validators().is_none()); - assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); - }) + run_to_block(27); + assert!(Staking::snapshot_validators().is_none()); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); + }) } #[test] @@ -2875,26 +2883,20 @@ mod offchain_phragmen { .offchain_phragmen_ext() .election_lookahead(3) .build() - .execute_with( - || { - run_to_block(12); - assert!(Staking::snapshot_validators().is_some()); - assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); + .execute_with(|| { + run_to_block(12); + assert!(Staking::snapshot_validators().is_some()); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); - let call = crate::Call::bond(999, 998, Default::default()); - let outer: mock::Call = call.into(); + let call = crate::Call::bond(999, 998, Default::default()); + let outer: mock::Call = call.into(); - let lock_staking: LockStakingStatus = Default::default(); - assert_eq!( - lock_staking.validate( - &10, - &outer, - Default::default(), - Default::default(), - ), - TransactionValidity::Err(InvalidTransaction::Stale.into()), - ) - }) + let lock_staking: LockStakingStatus = Default::default(); + assert_eq!( + lock_staking.validate(&10, &outer, Default::default(), Default::default(),), + TransactionValidity::Err(InvalidTransaction::Stale.into()), + ) + }) } #[test] @@ -2904,37 +2906,41 @@ mod offchain_phragmen { ExtBuilder::default() .offchain_phragmen_ext() .build() - .execute_with( - || { - run_to_block(12); - assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); - assert!(Staking::snapshot_validators().is_some()); - - let (compact, winners, score) = prepare_submission_with(true, |_| {}); - assert_ok!(Staking::submit_election_solution( - Origin::signed(10), - winners, - compact, - score, - )); + .execute_with(|| { + run_to_block(12); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); + assert!(Staking::snapshot_validators().is_some()); - let queued_result = Staking::queued_elected().unwrap(); - assert_eq!(queued_result.compute, ElectionCompute::Signed); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); + assert_ok!(Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + )); - run_to_block(15); - assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); + let queued_result = Staking::queued_elected().unwrap(); + assert_eq!(queued_result.compute, ElectionCompute::Signed); - assert_eq!( - System::events().into_iter().map(|r| r.event).filter_map(|e| { - if let MetaEvent::staking(inner) = e { - Some(inner) - } else { - None - } - }).last().unwrap(), - RawEvent::StakingElection(ElectionCompute::Signed), - ); - }) + run_to_block(15); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); + + assert_eq!( + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| { + if let MetaEvent::staking(inner) = e { + Some(inner) + } else { + None + } + }) + .last() + .unwrap(), + RawEvent::StakingElection(ElectionCompute::Signed), + ); + }) } #[test] @@ -2943,38 +2949,40 @@ mod offchain_phragmen { ExtBuilder::default() .offchain_phragmen_ext() .build() - .execute_with( - || { - run_to_block(14); - assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); + .execute_with(|| { + run_to_block(14); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); - let (compact, winners, score) = prepare_submission_with(true, |_| {}); - assert_ok!( - Staking::submit_election_solution( + let (compact, winners, score) = prepare_submission_with(true, |_| {}); + assert_ok!(Staking::submit_election_solution( Origin::signed(10), winners, compact, score, - ) - ); - - let queued_result = Staking::queued_elected().unwrap(); - assert_eq!(queued_result.compute, ElectionCompute::Signed); + )); - run_to_block(15); - assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); + let queued_result = Staking::queued_elected().unwrap(); + assert_eq!(queued_result.compute, ElectionCompute::Signed); - assert_eq!( - System::events().into_iter().map(|r| r.event).filter_map(|e| { - if let MetaEvent::staking(inner) = e { - Some(inner) - } else { - None - } - }).last().unwrap(), - RawEvent::StakingElection(ElectionCompute::Signed), - ); - }) + run_to_block(15); + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); + + assert_eq!( + System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| { + if let MetaEvent::staking(inner) = e { + Some(inner) + } else { + None + } + }) + .last() + .unwrap(), + RawEvent::StakingElection(ElectionCompute::Signed), + ); + }) } #[test] @@ -2984,22 +2992,21 @@ mod offchain_phragmen { ExtBuilder::default() .offchain_phragmen_ext() .build() - .execute_with( - || { - run_to_block(11); - // submission is not yet allowed - assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); + .execute_with(|| { + run_to_block(11); + // submission is not yet allowed + assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); - // create all the indices just to build the solution. - Staking::create_stakers_snapshot(); - let (compact, winners, score) = prepare_submission_with(true, |_| {}); - Staking::kill_stakers_snapshot(); + // create all the indices just to build the solution. + Staking::create_stakers_snapshot(); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); + Staking::kill_stakers_snapshot(); - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenEarlySubmission, - ); - }) + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenEarlySubmission, + ); + }) } #[test] @@ -3010,22 +3017,26 @@ mod offchain_phragmen { .has_stakers(false) .validator_count(4) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); - // a good solution - let (compact, winners, score) = prepare_submission_with(true, |_| {}); - assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); + // a good solution + let (compact, winners, score) = prepare_submission_with(true, |_| {}); + assert_ok!(Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score + )); - // a bad solution - let (compact, winners, score) = horrible_phragmen_with_post_processing(false); - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenWeakSubmission, - ); - }) + // a bad solution + let (compact, winners, score) = horrible_phragmen_with_post_processing(false); + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenWeakSubmission, + ); + }) } #[test] @@ -3036,19 +3047,28 @@ mod offchain_phragmen { .validator_count(4) .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); - // a meeeeh solution - let (compact, winners, score) = horrible_phragmen_with_post_processing(false); - assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); + // a meeeeh solution + let (compact, winners, score) = horrible_phragmen_with_post_processing(false); + assert_ok!(Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score + )); - // a better solution - let (compact, winners, score) = prepare_submission_with(true, |_| {}); - assert_ok!(Staking::submit_election_solution(Origin::signed(10), winners, compact, score)); - }) + // a better solution + let (compact, winners, score) = prepare_submission_with(true, |_| {}); + assert_ok!(Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score + )); + }) } #[test] @@ -3060,7 +3080,7 @@ mod offchain_phragmen { .validator_count(2) .build(); let state = offchainify(&mut ext); - ext.execute_with(||{ + ext.execute_with(|| { run_to_block(12); // local key 11 is in the elected set. @@ -3074,7 +3094,7 @@ mod offchain_phragmen { let call = extrinsic.call; let inner = match call { - mock::Call::Staking(inner) => { inner }, + mock::Call::Staking(inner) => inner, }; // pass this call to ValidateUnsigned @@ -3085,9 +3105,8 @@ mod offchain_phragmen { priority: 1125, // the proposed slot stake. requires: vec![], provides: vec![(Staking::current_era(), signing_key).encode()], - longevity: TryInto::::try_into( - ::ElectionLookahead::get() - ).unwrap_or(150_u64), + longevity: TryInto::::try_into(::ElectionLookahead::get()) + .unwrap_or(150_u64), propagate: true, }) ) @@ -3102,13 +3121,16 @@ mod offchain_phragmen { .validator_count(4) .build(); let state = offchainify(&mut ext); - ext.execute_with(||{ + ext.execute_with(|| { run_to_block(12); // put a good solution on-chain let (compact, winners, score) = prepare_submission_with(true, |_| {}); - assert_ok!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - ); + assert_ok!(Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score + ),); // now run the offchain worker in the same chain state. Staking::offchain_worker(12); @@ -3119,7 +3141,7 @@ mod offchain_phragmen { let call = extrinsic.call; let inner = match call { - mock::Call::Staking(inner) => { inner }, + mock::Call::Staking(inner) => inner, }; // pass this call to ValidateUnsigned @@ -3138,7 +3160,7 @@ mod offchain_phragmen { .local_key_account(5) .build(); let state = offchainify(&mut ext); - ext.execute_with(||{ + ext.execute_with(|| { run_to_block(12); // local key 5 is not there. @@ -3158,22 +3180,21 @@ mod offchain_phragmen { .validator_count(4) .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); - ValidatorCount::put(3); - let (compact, winners, score) = prepare_submission_with(true, |_| {}); - ValidatorCount::put(4); + ValidatorCount::put(3); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); + ValidatorCount::put(4); - assert_eq!(winners.len(), 3); + assert_eq!(winners.len(), 3); - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusWinnerCount, - ); - }) + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusWinnerCount, + ); + }) } #[test] @@ -3184,22 +3205,21 @@ mod offchain_phragmen { .validator_count(8) // we simply cannot elect 8 .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); - ValidatorCount::put(3); - let (compact, winners, score) = prepare_submission_with(true, |_| {}); - ValidatorCount::put(4); + ValidatorCount::put(3); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); + ValidatorCount::put(4); - assert_eq!(winners.len(), 3); + assert_eq!(winners.len(), 3); - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusWinnerCount, - ); - }) + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusWinnerCount, + ); + }) } #[test] @@ -3210,20 +3230,22 @@ mod offchain_phragmen { .validator_count(8) // we simply cannot elect 8 .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); - let (compact, winners, score) = prepare_submission_with(true, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, |_| {}); - assert_eq!(winners.len(), 4); + assert_eq!(winners.len(), 4); - // all good. We chose 4 and it works. - assert_ok!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - ); - }) + // all good. We chose 4 and it works. + assert_ok!(Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score + ),); + }) } #[test] @@ -3234,24 +3256,23 @@ mod offchain_phragmen { .validator_count(4) .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); - assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); - assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); - let (mut compact, winners, score) = prepare_submission_with(true, |_| {}); + assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); + assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); + let (mut compact, winners, score) = prepare_submission_with(true, |_| {}); - // index 9 doesn't exist. - compact.votes1.push((9, 2)); + // index 9 doesn't exist. + compact.votes1.push((9, 2)); - // The error type sadly cannot be more specific now. - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusCompact, - ); - }) + // The error type sadly cannot be more specific now. + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusCompact, + ); + }) } #[test] @@ -3262,24 +3283,23 @@ mod offchain_phragmen { .validator_count(4) .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); - assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); - assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); - let (mut compact, winners, score) = prepare_submission_with(true, |_| {}); + assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); + assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); + let (mut compact, winners, score) = prepare_submission_with(true, |_| {}); - // index 4 doesn't exist. - compact.votes1.push((3, 4)); + // index 4 doesn't exist. + compact.votes1.push((3, 4)); - // The error type sadly cannot be more specific now. - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusCompact, - ); - }) + // The error type sadly cannot be more specific now. + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusCompact, + ); + }) } #[test] @@ -3290,23 +3310,22 @@ mod offchain_phragmen { .validator_count(4) .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); - assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); - assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); - let (compact, _, score) = prepare_submission_with(true, |_| {}); + assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); + assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); + let (compact, _, score) = prepare_submission_with(true, |_| {}); - // index 4 doesn't exist. - let winners = vec![0, 1, 2, 4]; + // index 4 doesn't exist. + let winners = vec![0, 1, 2, 4]; - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusWinner, - ); - }) + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusWinner, + ); + }) } #[test] @@ -3318,24 +3337,23 @@ mod offchain_phragmen { .validator_count(2) .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); + assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); + let (compact, winners, score) = prepare_submission_with(true, |a| { + a.iter_mut() + .find(|x| x.who == 5) + .map(|x| x.distribution = vec![(20, 50), (40, 30), (30, 20)]); + }); - assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); - assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); - let (compact, winners, score) = prepare_submission_with(true, |a| { - a.iter_mut().find(|x| x.who == 5).map(|x| - x.distribution = vec![(20, 50), (40, 30), (30, 20)] + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusEdge, ); - }); - - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusEdge, - ); - }) + }) } #[test] @@ -3346,24 +3364,26 @@ mod offchain_phragmen { .validator_count(4) .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + let (compact, winners, score) = prepare_submission_with(true, |a| { + // mutate a self vote to target someone else. That someone else is still among the + // winners + a.iter_mut().find(|x| x.who == 10).map(|x| { + x.distribution + .iter_mut() + .find(|y| y.0 == 10) + .map(|y| y.0 = 20) + }); + }); - let (compact, winners, score) = prepare_submission_with(true, |a| { - // mutate a self vote to target someone else. That someone else is still among the - // winners - a.iter_mut().find(|x| x.who == 10).map(|x| - x.distribution.iter_mut().find(|y| y.0 == 10).map(|y| y.0 = 20) + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusSelfVote, ); - }); - - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusSelfVote, - ); - }) + }) } #[test] @@ -3374,24 +3394,26 @@ mod offchain_phragmen { .validator_count(4) .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); - - let (compact, winners, score) = prepare_submission_with(true, |a| { - // Remove the self vote. - a.retain(|x| x.who != 10); - // add is as a new double vote - a.push(StakedAssignment { who: 10, distribution: vec![(10, 50), (20, 50)]}); - }); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + let (compact, winners, score) = prepare_submission_with(true, |a| { + // Remove the self vote. + a.retain(|x| x.who != 10); + // add is as a new double vote + a.push(StakedAssignment { + who: 10, + distribution: vec![(10, 50), (20, 50)], + }); + }); - // This raises score issue. - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusSelfVote, - ); - }) + // This raises score issue. + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusSelfVote, + ); + }) } #[test] @@ -3402,26 +3424,25 @@ mod offchain_phragmen { .validator_count(4) .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); - // Note: we don't reduce here to be able to tweak votes3. votes3 will vanish if you - // reduce. - let (mut compact, winners, score) = prepare_submission_with(false, |_| {}); + // Note: we don't reduce here to be able to tweak votes3. votes3 will vanish if you + // reduce. + let (mut compact, winners, score) = prepare_submission_with(false, |_| {}); - if let Some(c) = compact.votes3.iter_mut().find(|x| x.0 == 0) { - // by default it should have been (0, [(2, 33%), (1, 33%)], 0) - // now the sum is above 100% - c.1 = [(2, percent(66)), (1, percent(66))]; - } + if let Some(c) = compact.votes3.iter_mut().find(|x| x.0 == 0) { + // by default it should have been (0, [(2, 33%), (1, 33%)], 0) + // now the sum is above 100% + c.1 = [(2, percent(66)), (1, percent(66))]; + } - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusCompact, - ); - }) + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusCompact, + ); + }) } #[test] @@ -3441,24 +3462,23 @@ mod offchain_phragmen { .validator_count(4) .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); - - let (compact, winners, score) = prepare_submission_with(false, |a| { - // 3 only voted for 20 and 40. We add a fake vote to 30. The stake sum is still - // correctly 100. - a.iter_mut().find(|x| x.who == 3).map(|x| { - x.distribution = vec![(20, 50), (40, 30), (30, 20)] + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); + + let (compact, winners, score) = prepare_submission_with(false, |a| { + // 3 only voted for 20 and 40. We add a fake vote to 30. The stake sum is still + // correctly 100. + a.iter_mut() + .find(|x| x.who == 3) + .map(|x| x.distribution = vec![(20, 50), (40, 30), (30, 20)]); }); - }); - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusNomination, - ); - }) + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusNomination, + ); + }) } #[test] @@ -3486,17 +3506,18 @@ mod offchain_phragmen { // slash 10 let offender_expo = Staking::stakers(10); on_offence_now( - &[ - OffenceDetails { - offender: (10, offender_expo.clone()), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: (10, offender_expo.clone()), + reporters: vec![], + }], &[Perbill::from_percent(10)], ); // validate 10 again for the next round. - assert_ok!(Staking::validate(Origin::signed(10 + 1000), Default::default())); + assert_ok!(Staking::validate( + Origin::signed(10 + 1000), + Default::default() + )); // a solution that has been prepared after the slash. let (compact, winners, score) = prepare_submission_with(false, |_| {}); @@ -3521,9 +3542,12 @@ mod offchain_phragmen { ); // new results are okay. - assert_ok!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - ); + assert_ok!(Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score + ),); // finish the round. run_to_block(30); @@ -3538,19 +3562,18 @@ mod offchain_phragmen { .validator_count(4) .has_stakers(false) .build() - .execute_with( - || { - build_offchain_phragmen_test_ext(); - run_to_block(12); + .execute_with(|| { + build_offchain_phragmen_test_ext(); + run_to_block(12); - let (compact, winners, mut score) = prepare_submission_with(true, |_| {}); - score[0] += 1; + let (compact, winners, mut score) = prepare_submission_with(true, |_| {}); + score[0] += 1; - assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), - Error::::PhragmenBogusScore, - ); - }) + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenBogusScore, + ); + }) } #[test] @@ -3648,19 +3671,20 @@ fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_valid assert_eq!(exposure_21.total, 1000 + 375); on_offence_now( - &[ - OffenceDetails { - offender: (11, exposure_11.clone()), - reporters: vec![], - }, - ], + &[OffenceDetails { + offender: (11, exposure_11.clone()), + reporters: vec![], + }], &[Perbill::from_percent(10)], ); // post-slash balance let nominator_slash_amount_11 = 125 / 10; assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(101), 2000 - nominator_slash_amount_11); + assert_eq!( + Balances::free_balance(101), + 2000 - nominator_slash_amount_11 + ); // This is the best way to check that the validator was chilled; `get` will // return default value. @@ -3672,7 +3696,9 @@ fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_valid // and make sure that the vote will be ignored even if the validator // re-registers. - let last_slash = ::SlashingSpans::get(&11).unwrap().last_nonzero_slash(); + let last_slash = ::SlashingSpans::get(&11) + .unwrap() + .last_nonzero_slash(); assert!(nominations.submitted_in < last_slash); // actually re-bond the slashed validator diff --git a/primitives/phragmen/src/helpers.rs b/primitives/phragmen/src/helpers.rs index d1af4113849d9..167c0de94d191 100644 --- a/primitives/phragmen/src/helpers.rs +++ b/primitives/phragmen/src/helpers.rs @@ -16,19 +16,19 @@ //! Helper methods for phragmen. -use crate::{Assignment, StakedAssignment, ExtendedBalance, IdentifierT}; -use sp_std::prelude::*; +use crate::{Assignment, ExtendedBalance, IdentifierT, StakedAssignment}; use sp_runtime::PerThing; +use sp_std::prelude::*; /// Converts a vector of ratio assignments into ones with absolute budget value. pub fn assignment_ratio_to_staked( ratio: Vec>, stake_of: FS, ) -> Vec> - where - for <'r> FS: Fn(&'r A) -> ExtendedBalance, - T: sp_std::ops::Mul, - ExtendedBalance: From<::Inner>, +where + for<'r> FS: Fn(&'r A) -> ExtendedBalance, + T: sp_std::ops::Mul, + ExtendedBalance: From<::Inner>, { ratio .into_iter() @@ -42,12 +42,11 @@ pub fn assignment_ratio_to_staked( /// Converts a vector of staked assignments into ones with ratio values. pub fn assignment_staked_to_ratio( ratio: Vec>, -) -> Vec> where ExtendedBalance: From<::Inner> +) -> Vec> +where + ExtendedBalance: From<::Inner>, { - ratio - .into_iter() - .map(|a| a.into_assignment(true)) - .collect() + ratio.into_iter().map(|a| a.into_assignment(true)).collect() } #[cfg(test)] @@ -64,14 +63,14 @@ mod tests { distribution: vec![ (10u32, Perbill::from_fraction(0.5)), (20, Perbill::from_fraction(0.5)), - ] + ], }, Assignment { who: 2u32, distribution: vec![ (10, Perbill::from_fraction(0.33)), (20, Perbill::from_fraction(0.67)), - ] + ], }, ]; @@ -83,20 +82,13 @@ mod tests { vec![ StakedAssignment { who: 1u32, - distribution: vec![ - (10u32, 50), - (20, 50), - ] + distribution: vec![(10u32, 50), (20, 50),] }, StakedAssignment { who: 2u32, - distribution: vec![ - (10u32, 33), - (20, 67), - ] + distribution: vec![(10u32, 33), (20, 67),] } ] ); } } - diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 8540584557cf2..8e27d40b14646 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -149,7 +149,7 @@ pub struct PhragmenResult { pub winners: Vec<(AccountId, ExtendedBalance)>, /// Individual assignments. for each tuple, the first elements is a voter and the second /// is the list of candidates that it supports. - pub assignments: Vec> + pub assignments: Vec>, } /// A voter's stake assignment among a set of targets, represented as ratios. @@ -163,7 +163,8 @@ pub struct Assignment { } impl Assignment - where ExtendedBalance: From<::Inner> +where + ExtendedBalance: From<::Inner>, { /// Convert from a ratio assignment into one with absolute values aka. [`StakedAssignment`]. /// @@ -175,20 +176,25 @@ impl Assignment /// If an edge ratio is [`Bounded::max_value()`], it is dropped. This edge can never mean /// anything useful. pub fn into_staked(self, stake: ExtendedBalance, fill: bool) -> StakedAssignment - where T: sp_std::ops::Mul + where + T: sp_std::ops::Mul, { let mut sum: ExtendedBalance = Bounded::min_value(); - let mut distribution = self.distribution.into_iter().filter_map(|(target, p)| { - // if this ratio is zero, then skip it. - if p == Bounded::min_value() { - None - } else { - let distribution_stake = p * stake; - // defensive only. We assume that balance cannot exceed extended balance. - sum = sum.saturating_add(distribution_stake); - Some((target, distribution_stake)) - } - }).collect::>(); + let mut distribution = self + .distribution + .into_iter() + .filter_map(|(target, p)| { + // if this ratio is zero, then skip it. + if p == Bounded::min_value() { + None + } else { + let distribution_stake = p * stake; + // defensive only. We assume that balance cannot exceed extended balance. + sum = sum.saturating_add(distribution_stake); + Some((target, distribution_stake)) + } + }) + .collect::>(); if fill { // NOTE: we can do this better. @@ -226,8 +232,7 @@ pub struct StakedAssignment { pub distribution: Vec<(AccountId, ExtendedBalance)>, } -impl 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 @@ -241,29 +246,38 @@ impl StakedAssignment /// If an edge stake is so small that it cannot be represented in `T`, it is ignored. This edge /// can never be re-created and does not mean anything useful anymore. pub fn into_assignment(self, fill: bool) -> Assignment - where ExtendedBalance: From<::Inner> + where + ExtendedBalance: From<::Inner>, { let accuracy: u128 = T::ACCURACY.saturated_into(); let mut sum: u128 = Zero::zero(); let stake = self.distribution.iter().map(|x| x.1).sum(); - let mut distribution = self.distribution.into_iter().filter_map(|(target, w)| { - let per_thing = T::from_rational_approximation(w, stake); - if per_thing == Bounded::min_value() { - None - } else { - sum += per_thing.clone().deconstruct().saturated_into(); - Some((target, per_thing)) - } - }).collect::>(); + let mut distribution = self + .distribution + .into_iter() + .filter_map(|(target, w)| { + let per_thing = T::from_rational_approximation(w, stake); + if per_thing == Bounded::min_value() { + None + } else { + sum += per_thing.clone().deconstruct().saturated_into(); + Some((target, per_thing)) + } + }) + .collect::>(); if fill { if let Some(leftover) = accuracy.checked_sub(sum) { if let Some(last) = distribution.last_mut() { - last.1 = last.1.saturating_add(T::from_parts(leftover.saturated_into())); + last.1 = last + .1 + .saturating_add(T::from_parts(leftover.saturated_into())); } } else if let Some(excess) = sum.checked_sub(accuracy) { if let Some(last) = distribution.last_mut() { - last.1 = last.1.saturating_sub(T::from_parts(excess.saturated_into())); + last.1 = last + .1 + .saturating_sub(T::from_parts(excess.saturated_into())); } } } diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index e4061ec750872..7687e3c33c7fe 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -17,7 +17,7 @@ //! (very) Basic implementation of a graph node used in the reduce algorithm. use sp_runtime::RuntimeDebug; -use sp_std::{prelude::*, cell::RefCell, rc::Rc, fmt}; +use sp_std::{cell::RefCell, fmt, prelude::*, rc::Rc}; /// The role that a node can accept. #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, RuntimeDebug)] @@ -59,7 +59,16 @@ impl Eq for NodeId {} #[cfg(feature = "std")] impl sp_std::fmt::Debug for NodeId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> sp_std::fmt::Result { - write!(f, "Node({:?}, {:?})", self.who, if self.role == NodeRole::Voter { "V" } else { "T" }) + write!( + f, + "Node({:?}, {:?})", + self.who, + if self.role == NodeRole::Voter { + "V" + } else { + "T" + } + ) } } @@ -83,7 +92,12 @@ impl Eq for Node {} #[cfg(feature = "std")] impl fmt::Debug for Node { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({:?} --> {:?})", self.id, self.parent.as_ref().map(|p| p.borrow().id.clone())) + write!( + f, + "({:?} --> {:?})", + self.id, + self.parent.as_ref().map(|p| p.borrow().id.clone()) + ) } } @@ -129,7 +143,9 @@ impl Node { let mut current = start.clone(); while let Some(ref next_parent) = current.clone().borrow().parent { - if visited.contains(next_parent) { break; } + if visited.contains(next_parent) { + break; + } parent_path.push(next_parent.clone()); current = next_parent.clone(); visited.push(current.clone()); @@ -156,7 +172,16 @@ mod tests { #[test] fn basic_create_works() { let node = Node::new(id(10)); - assert_eq!(node, Node { id: NodeId { who: 10, role: NodeRole::Target }, parent: None }); + assert_eq!( + node, + Node { + id: NodeId { + who: 10, + role: NodeRole::Target + }, + parent: None + } + ); } #[test] @@ -197,10 +222,7 @@ mod tests { (d.clone(), vec![e.clone(), a.clone(), d.clone()]), ); - assert_eq!( - Node::root(&a), - (d.clone(), vec![a.clone(), d.clone()]), - ); + assert_eq!(Node::root(&a), (d.clone(), vec![a.clone(), d.clone()]),); assert_eq!( Node::root(&c), @@ -212,10 +234,7 @@ mod tests { // <-- E Node::set_parent_of(&a, &f); - assert_eq!( - Node::root(&a), - (f.clone(), vec![a.clone(), f.clone()]), - ); + assert_eq!(Node::root(&a), (f.clone(), vec![a.clone(), f.clone()]),); assert_eq!( Node::root(&c), diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index d6a9ac3b5a53d..fea2552f6171b 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -47,16 +47,13 @@ //! //! 1. https://hackmd.io/JOn9x98iS0e0DPWQ87zGWg?view -// TODO: should be able to handle self votes. + Fuzzer should randomly create them - +use crate::node::{Node, NodeId, NodeRef, NodeRole}; +use crate::{ExtendedBalance, IdentifierT, StakedAssignment}; +use sp_runtime::traits::{Bounded, Zero}; use sp_std::{ + collections::btree_map::{BTreeMap, Entry::*}, prelude::*, - collections::{btree_map::{Entry::*, BTreeMap}}, }; -use sp_runtime::traits::{Zero, Bounded}; -use crate::node::{Node, NodeRef, NodeRole, NodeId}; -use crate::{ExtendedBalance, StakedAssignment, IdentifierT}; - /// Map type used for reduce_4. Can be easily swapped with HashMap. type Map = BTreeMap<(A, A), A>; @@ -65,12 +62,12 @@ type Map = BTreeMap<(A, A), A>; fn combinations_2(input: &[T]) -> Vec<(T, T)> { let n = input.len(); if n < 2 { - return Default::default() + return Default::default(); } - let mut comb = Vec::with_capacity(n * (n-1) / 2); + let mut comb = Vec::with_capacity(n * (n - 1) / 2); for i in 0..n { - for j in i+1..n { + for j in i + 1..n { comb.push((input[i].clone(), input[j].clone())) } } @@ -96,10 +93,7 @@ pub(crate) fn trailing_common(t1: &[T], t2: &[T]) -> usize { } /// Merges two parent roots as described by the reduce algorithm. -fn merge( - voter_root_path: Vec>, - target_root_path: Vec> -) { +fn merge(voter_root_path: Vec>, target_root_path: Vec>) { if voter_root_path.len() <= target_root_path.len() { // iterate from last to beginning, skipping the first one. This asserts that // indexing is always correct. @@ -107,20 +101,16 @@ fn merge( .iter() .take(voter_root_path.len() - 1) // take all except for last. .enumerate() - .map(|(i, n)| (n, voter_root_path[i+1].clone())) - .for_each(|(voter, next)| { - Node::set_parent_of(&next, &voter) - }); + .map(|(i, n)| (n, voter_root_path[i + 1].clone())) + .for_each(|(voter, next)| Node::set_parent_of(&next, &voter)); Node::set_parent_of(&voter_root_path[0], &target_root_path[0]); } else { target_root_path .iter() .take(target_root_path.len() - 1) // take all except for last. .enumerate() - .map(|(i, n)| (n, target_root_path[i+1].clone())) - .for_each(|(target, next)| { - Node::set_parent_of(&next, &target) - }); + .map(|(i, n)| (n, target_root_path[i + 1].clone())) + .for_each(|(target, next)| Node::set_parent_of(&next, &target)); Node::set_parent_of(&target_root_path[0], &voter_root_path[0]); } } @@ -133,10 +123,7 @@ fn merge( /// result will most likely be corrupt otherwise. /// /// O(|E_w| ⋅ k). -fn reduce_4( - assignments: &mut Vec>, -) -> u32 { - +fn reduce_4(assignments: &mut Vec>) -> u32 { let mut combination_map: Map = Map::new(); let mut num_changed: u32 = Zero::zero(); @@ -157,7 +144,7 @@ fn reduce_4( match combination_map.entry((v1.clone(), v2.clone())) { Vacant(entry) => { entry.insert(who.clone()); - }, + } Occupied(mut entry) => { let other_who = entry.get_mut(); @@ -166,7 +153,8 @@ fn reduce_4( // reason for this is subtle; candidate_combinations is created once while the // inner loop might remove some edges. Note that if count() > 2, the we have // duplicates. - if assignments[assignment_index].distribution + if assignments[assignment_index] + .distribution .iter() .filter(|(t, _)| *t == v1 || *t == v2) .count() != 2 @@ -179,15 +167,19 @@ fn reduce_4( if maybe_other_assignments.is_none() { continue; } - let other_assignment = maybe_other_assignments - .expect("value is checked to be 'Some'"); + let other_assignment = + maybe_other_assignments.expect("value is checked to be 'Some'"); // Collect potential cycle votes - let mut other_cycle_votes = other_assignment.distribution + let mut other_cycle_votes = other_assignment + .distribution .iter() .filter_map(|(t, w)| { - if *t == v1 || *t == v2 { Some((t.clone(), *w)) } - else { None } + if *t == v1 || *t == v2 { + Some((t.clone(), *w)) + } else { + None + } }) .collect::>(); @@ -205,11 +197,18 @@ fn reduce_4( } else if other_votes_count == 2 { // This is a cycle. let mut who_cycle_votes: Vec<(A, ExtendedBalance)> = Vec::with_capacity(2); - assignments[assignment_index].distribution.iter().for_each(|(t, w)| { - if *t == v1 || *t == v2 { who_cycle_votes.push((t.clone(), *w)); } - }); + assignments[assignment_index] + .distribution + .iter() + .for_each(|(t, w)| { + if *t == v1 || *t == v2 { + who_cycle_votes.push((t.clone(), *w)); + } + }); - if who_cycle_votes.len() != 2 { continue; } + if who_cycle_votes.len() != 2 { + continue; + } // Align the targets similarly. This helps with the circulation below. if other_cycle_votes[0].0 != who_cycle_votes[0].0 { @@ -224,9 +223,13 @@ fn reduce_4( .chain(other_cycle_votes.iter()) .enumerate() .map(|(index, (t, w))| { - if *w <= min_value { min_value = *w; min_index = index; } + if *w <= min_value { + min_value = *w; + min_index = index; + } (t.clone(), *w) - }).collect::>(); + }) + .collect::>(); // min was in the first part of the chained iters let mut increase_indices: Vec = Vec::new(); @@ -255,51 +258,72 @@ fn reduce_4( // apply changes let mut remove_indices: Vec = Vec::with_capacity(1); increase_indices.into_iter().for_each(|i| { - let voter = if i < 2 { who.clone() } else { other_who.clone() }; + let voter = if i < 2 { + who.clone() + } else { + other_who.clone() + }; // Note: so this is pretty ambiguous. We should only look for one // assignment that meets this criteria and if we find multiple then that // is a corrupt input. Same goes for the next block. - assignments.iter_mut().filter(|a| a.who == voter).for_each(|ass| { - ass.distribution - .iter_mut() - .position(|(t, _)| *t == cycle[i].0) - .map(|idx| { - let next_value = ass.distribution[idx].1.saturating_add(min_value); - ass.distribution[idx].1 = next_value; - }); - }); + assignments + .iter_mut() + .filter(|a| a.who == voter) + .for_each(|ass| { + ass.distribution + .iter_mut() + .position(|(t, _)| *t == cycle[i].0) + .map(|idx| { + let next_value = + ass.distribution[idx].1.saturating_add(min_value); + ass.distribution[idx].1 = next_value; + }); + }); }); decrease_indices.into_iter().for_each(|i| { - let voter = if i < 2 { who.clone() } else { other_who.clone() }; - assignments.iter_mut().filter(|a| a.who == voter).for_each(|ass| { - ass.distribution - .iter_mut() - .position(|(t, _)| *t == cycle[i].0) - .map(|idx| { - let next_value = ass.distribution[idx].1.saturating_sub(min_value); - if next_value.is_zero() { - ass.distribution.remove(idx); - remove_indices.push(i); - num_changed += 1; - } else { - ass.distribution[idx].1 = next_value; - } - }); - }); + let voter = if i < 2 { + who.clone() + } else { + other_who.clone() + }; + assignments + .iter_mut() + .filter(|a| a.who == voter) + .for_each(|ass| { + ass.distribution + .iter_mut() + .position(|(t, _)| *t == cycle[i].0) + .map(|idx| { + let next_value = + ass.distribution[idx].1.saturating_sub(min_value); + if next_value.is_zero() { + ass.distribution.remove(idx); + remove_indices.push(i); + num_changed += 1; + } else { + ass.distribution[idx].1 = next_value; + } + }); + }); }); // remove either one of them. let who_removed = remove_indices.iter().find(|i| **i < 2usize).is_some(); - let other_removed = remove_indices.into_iter().find(|i| *i >= 2usize).is_some(); + let other_removed = + remove_indices.into_iter().find(|i| *i >= 2usize).is_some(); match (who_removed, other_removed) { - (false, true) => { *other_who = who.clone(); }, - (true, false) => {}, // nothing, other_who can stay there. - (true, true) => { entry.remove(); }, // remove and don't replace + (false, true) => { + *other_who = who.clone(); + } + (true, false) => {} // nothing, other_who can stay there. + (true, true) => { + entry.remove(); + } // remove and don't replace (false, false) => { // Neither of the edges was removed? impossible. debug_assert!(false, "Duplicate voter (or other corrupt input)."); - }, + } } } } @@ -318,9 +342,7 @@ fn reduce_4( /// result will most likely be corrupt otherwise. /// /// O(|Ew| ⋅ m) -fn reduce_all( - assignments: &mut Vec>, -) -> u32 { +fn reduce_all(assignments: &mut Vec>) -> u32 { let mut num_changed: u32 = Zero::zero(); let mut tree: BTreeMap, NodeRef> = BTreeMap::new(); @@ -352,10 +374,14 @@ fn reduce_all( let target_exists = tree.contains_key(&target_id); // create both. - let voter_node = tree.entry(voter_id.clone()) - .or_insert(Node::new(voter_id).into_ref()).clone(); - let target_node = tree.entry(target_id.clone()) - .or_insert(Node::new(target_id).into_ref()).clone(); + let voter_node = tree + .entry(voter_id.clone()) + .or_insert(Node::new(voter_id).into_ref()) + .clone(); + let target_node = tree + .entry(target_id.clone()) + .or_insert(Node::new(target_id).into_ref()) + .clone(); // If one exists but the other one doesn't, or if both does not, then set the existing // one as the parent of the non-existing one and move on. Else, continue with the rest @@ -365,12 +391,12 @@ fn reduce_all( Node::set_parent_of(&target_node, &voter_node); dist_index += 1; continue; - }, + } (false, true) => { Node::set_parent_of(&voter_node, &target_node); dist_index += 1; continue; - }, + } (true, false) => { Node::set_parent_of(&target_node, &voter_node); dist_index += 1; @@ -392,21 +418,31 @@ fn reduce_all( // because roots are the same. #[cfg(feature = "std")] - debug_assert_eq!(target_root_path.last().unwrap(), voter_root_path.last().unwrap()); + debug_assert_eq!( + target_root_path.last().unwrap(), + voter_root_path.last().unwrap() + ); debug_assert!(common_count > 0); // cycle part of each path will be `path[path.len() - common_count - 1 : 0]` // NOTE: the order of chaining is important! it is always build from [target, ..., // voter] - let cycle = - target_root_path.iter().take(target_root_path.len() - common_count + 1).cloned() - .chain(voter_root_path.iter().take(voter_root_path.len() - common_count).rev().cloned()) + let cycle = target_root_path + .iter() + .take(target_root_path.len() - common_count + 1) + .cloned() + .chain( + voter_root_path + .iter() + .take(voter_root_path.len() - common_count) + .rev() + .cloned(), + ) .collect::>>(); // a cycle's length shall always be multiple of two. #[cfg(feature = "std")] debug_assert_eq!(cycle.len() % 2, 0); - // find minimum of cycle. let mut min_value: ExtendedBalance = Bounded::max_value(); // The voter and the target pair that create the min edge. @@ -417,8 +453,20 @@ fn reduce_all( // 1 -> next // 0 -> prev let mut min_direction = 0u32; // helpers - let next_index = |i| { if i < (cycle.len() - 1) { i + 1 } else { 0 } }; - let prev_index = |i| { if i > 0 { i - 1 } else { cycle.len() - 1 } }; + let next_index = |i| { + if i < (cycle.len() - 1) { + i + 1 + } else { + 0 + } + }; + let prev_index = |i| { + if i > 0 { + i - 1 + } else { + cycle.len() - 1 + } + }; for i in 0..cycle.len() { if cycle[i].borrow().id.role == NodeRole::Voter { // NOTE: sadly way too many clones since I don't want to make A: Copy @@ -463,72 +511,95 @@ fn reduce_all( let current = cycle[i].borrow(); if current.id.role == NodeRole::Voter { let prev = cycle[prev_index(i)].borrow(); - assignments.iter_mut().enumerate().filter(|(_, a)| a.who == current.id.who).for_each(|(target_ass_index, ass)| { - ass.distribution.iter_mut().position(|(t, _)| *t == prev.id.who).map(|idx| { - let next_value = if i % 2 == 0 { - if start_operation_add { - ass.distribution[idx].1.saturating_add(min_value) - } else { - ass.distribution[idx].1.saturating_sub(min_value) - } - } else { - if start_operation_add { - ass.distribution[idx].1.saturating_sub(min_value) - } else { - ass.distribution[idx].1.saturating_add(min_value) - } - }; - - if next_value.is_zero() { - // if the removed edge is from the current assignment, dis_index - // should NOT be increased. - if target_ass_index == assignment_index { should_inc_counter = false } - ass.distribution.remove(idx); - num_changed += 1; - // only add if this is not the min itself. - if !(i == min_index && min_direction == 0) { - additional_removed.push((cycle[i].clone(), cycle[prev_index(i)].clone())); - } - } else { - ass.distribution[idx].1 = next_value; - } + assignments + .iter_mut() + .enumerate() + .filter(|(_, a)| a.who == current.id.who) + .for_each(|(target_ass_index, ass)| { + ass.distribution + .iter_mut() + .position(|(t, _)| *t == prev.id.who) + .map(|idx| { + let next_value = if i % 2 == 0 { + if start_operation_add { + ass.distribution[idx].1.saturating_add(min_value) + } else { + ass.distribution[idx].1.saturating_sub(min_value) + } + } else { + if start_operation_add { + ass.distribution[idx].1.saturating_sub(min_value) + } else { + ass.distribution[idx].1.saturating_add(min_value) + } + }; + + if next_value.is_zero() { + // if the removed edge is from the current assignment, dis_index + // should NOT be increased. + if target_ass_index == assignment_index { + should_inc_counter = false + } + ass.distribution.remove(idx); + num_changed += 1; + // only add if this is not the min itself. + if !(i == min_index && min_direction == 0) { + additional_removed.push(( + cycle[i].clone(), + cycle[prev_index(i)].clone(), + )); + } + } else { + ass.distribution[idx].1 = next_value; + } + }); }); - }); let next = cycle[next_index(i)].borrow(); - assignments.iter_mut().enumerate().filter(|(_, a)| a.who == current.id.who).for_each(|(target_ass_index, ass)| { - ass.distribution.iter_mut().position(|(t, _)| *t == next.id.who).map(|idx| { - let next_value = if i % 2 == 0 { - if start_operation_add { - ass.distribution[idx].1.saturating_sub(min_value) - } else { - ass.distribution[idx].1.saturating_add(min_value) - } - } else { - if start_operation_add { - ass.distribution[idx].1.saturating_add(min_value) - } else { - ass.distribution[idx].1.saturating_sub(min_value) - } - }; - - if next_value.is_zero() { - // if the removed edge is from the current assignment, dis_index - // should NOT be increased. - if target_ass_index == assignment_index { should_inc_counter = false } - ass.distribution.remove(idx); - num_changed += 1; - if !(i == min_index && min_direction == 1) { - additional_removed.push((cycle[i].clone(), cycle[next_index(i)].clone())); - } - } else { - ass.distribution[idx].1 = next_value; - } + assignments + .iter_mut() + .enumerate() + .filter(|(_, a)| a.who == current.id.who) + .for_each(|(target_ass_index, ass)| { + ass.distribution + .iter_mut() + .position(|(t, _)| *t == next.id.who) + .map(|idx| { + let next_value = if i % 2 == 0 { + if start_operation_add { + ass.distribution[idx].1.saturating_sub(min_value) + } else { + ass.distribution[idx].1.saturating_add(min_value) + } + } else { + if start_operation_add { + ass.distribution[idx].1.saturating_add(min_value) + } else { + ass.distribution[idx].1.saturating_sub(min_value) + } + }; + + if next_value.is_zero() { + // if the removed edge is from the current assignment, dis_index + // should NOT be increased. + if target_ass_index == assignment_index { + should_inc_counter = false + } + ass.distribution.remove(idx); + num_changed += 1; + if !(i == min_index && min_direction == 1) { + additional_removed.push(( + cycle[i].clone(), + cycle[next_index(i)].clone(), + )); + } + } else { + ass.distribution[idx].1 = next_value; + } + }); }); - }); } - }; - + } // don't do anything if the edge removed itself. This is always the first and last // element @@ -539,7 +610,7 @@ fn reduce_all( let min_edge = vec![min_voter, min_target]; if min_chain_in_voter { // NOTE: safe; voter_root_path is always bigger than 1 element. - for i in 0..voter_root_path.len()-1 { + for i in 0..voter_root_path.len() - 1 { let current = voter_root_path[i].clone().borrow().id.who.clone(); let next = voter_root_path[i + 1].clone().borrow().id.who.clone(); if min_edge.contains(¤t) && min_edge.contains(&next) { @@ -550,7 +621,7 @@ fn reduce_all( Node::set_parent_of(&voter_node, &target_node); } else { // NOTE: safe; target_root_path is always bigger than 1 element. - for i in 0..target_root_path.len()-1 { + for i in 0..target_root_path.len() - 1 { let current = target_root_path[i].clone().borrow().id.who.clone(); let next = target_root_path[i + 1].clone().borrow().id.who.clone(); if min_edge.contains(¤t) && min_edge.contains(&next) { @@ -572,7 +643,9 @@ fn reduce_all( } // increment the counter if needed. - if should_inc_counter { dist_index += 1; } + if should_inc_counter { + dist_index += 1; + } } } } @@ -590,9 +663,7 @@ fn reduce_all( /// duplicate ids, only the first instance is ever updates. /// /// O(min{ |Ew| ⋅ k + m3 , |Ew| ⋅ m }) -pub fn reduce( - assignments: &mut Vec>, -) -> u32 where { +pub fn reduce(assignments: &mut Vec>) -> u32 where { let mut num_changed = reduce_4(assignments); num_changed += reduce_all(assignments); num_changed @@ -636,17 +707,11 @@ mod tests { let assignments = vec![ StakedAssignment { who: 1, - distribution: vec![ - (10, 25), - (20, 75), - ], + distribution: vec![(10, 25), (20, 75)], }, StakedAssignment { who: 2, - distribution: vec![ - (10, 50), - (20, 50), - ], + distribution: vec![(10, 50), (20, 50)], }, ]; @@ -659,16 +724,11 @@ mod tests { vec![ StakedAssignment { who: 1, - distribution: vec![ - (20, 100), - ], + distribution: vec![(20, 100),], }, StakedAssignment { who: 2, - distribution: vec![ - (10, 75), - (20, 25), - ], + distribution: vec![(10, 75), (20, 25),], }, ], ); @@ -679,37 +739,23 @@ mod tests { let mut assignments = vec![ StakedAssignment { who: 1, - distribution: vec![(10, 10)] + distribution: vec![(10, 10)], }, StakedAssignment { who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], + distribution: vec![(10, 15), (20, 5)], }, StakedAssignment { who: 3, - distribution: vec![ - (20, 15), - (40, 15) - ], + distribution: vec![(20, 15), (40, 15)], }, StakedAssignment { - who: 4, distribution: - vec![ - (20, 10), - (30, 10), - (40, 20), - ] + who: 4, + distribution: vec![(20, 10), (30, 10), (40, 20)], }, StakedAssignment { who: 5, - distribution: vec![ - (20, 20), - (30, 10), - (40, 20), - ], + distribution: vec![(20, 20), (30, 10), (40, 20)], }, ]; @@ -719,38 +765,25 @@ mod tests { assignments, vec![ StakedAssignment { - who: 1, - distribution: vec![ - (10, 10), - ] - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], - }, - StakedAssignment { - who: 3, - distribution: vec![ - (20, 30), - ], - }, - StakedAssignment { - who: 4, distribution: - vec![ - (40, 40), - ] - }, - StakedAssignment { - who: 5, - distribution: vec![ - (20, 15), - (30, 20), - (40, 15), - ], - }, + who: 1, + distribution: vec![(10, 10),] + }, + StakedAssignment { + who: 2, + distribution: vec![(10, 15), (20, 5),], + }, + StakedAssignment { + who: 3, + distribution: vec![(20, 30),], + }, + StakedAssignment { + who: 4, + distribution: vec![(40, 40),] + }, + StakedAssignment { + who: 5, + distribution: vec![(20, 15), (30, 20), (40, 15),], + }, ], ) } @@ -760,37 +793,23 @@ mod tests { let mut assignments = vec![ StakedAssignment { who: 1, - distribution: vec![(10, 10)] + distribution: vec![(10, 10)], }, StakedAssignment { who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], + distribution: vec![(10, 15), (20, 5)], }, StakedAssignment { who: 3, - distribution: vec![ - (20, 15), - (40, 15) - ], + distribution: vec![(20, 15), (40, 15)], }, StakedAssignment { - who: 4, distribution: - vec![ - (20, 10), - (30, 10), - (40, 20), - ] + who: 4, + distribution: vec![(20, 10), (30, 10), (40, 20)], }, StakedAssignment { who: 5, - distribution: vec![ - (20, 20), - (30, 10), - (40, 20), - ], + distribution: vec![(20, 20), (30, 10), (40, 20)], }, ]; @@ -800,38 +819,25 @@ mod tests { assignments, vec![ StakedAssignment { - who: 1, - distribution: vec![ - (10, 10), - ] - }, - StakedAssignment { - who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], - }, - StakedAssignment { - who: 3, - distribution: vec![ - (20, 30), - ], - }, - StakedAssignment { - who: 4, distribution: - vec![ - (40, 40), - ] - }, - StakedAssignment { - who: 5, - distribution: vec![ - (20, 15), - (30, 20), - (40, 15), - ], - }, + who: 1, + distribution: vec![(10, 10),] + }, + StakedAssignment { + who: 2, + distribution: vec![(10, 15), (20, 5),], + }, + StakedAssignment { + who: 3, + distribution: vec![(20, 30),], + }, + StakedAssignment { + who: 4, + distribution: vec![(40, 40),] + }, + StakedAssignment { + who: 5, + distribution: vec![(20, 15), (30, 20), (40, 15),], + }, ], ) } @@ -841,41 +847,33 @@ mod tests { let mut assignments = vec![ StakedAssignment { who: 1, - distribution: vec![(10, 10)] + distribution: vec![(10, 10)], }, StakedAssignment { who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], + distribution: vec![(10, 15), (20, 5)], }, StakedAssignment { who: 3, - distribution: vec![ - (20, 15), - (40, 15) - ], + distribution: vec![(20, 15), (40, 15)], }, StakedAssignment { - who: 4, distribution: - vec![ - (20, 10), - (30, 10), - (40, 20), - ] + who: 4, + distribution: vec![(20, 10), (30, 10), (40, 20)], }, StakedAssignment { who: 5, - distribution: vec![ - (20, 20), - (30, 10), - (40, 20), - ], + distribution: vec![(20, 20), (30, 10), (40, 20)], }, // self vote from 10 and 20 to itself. - StakedAssignment { who: 10, distribution: vec![(10, 100)] }, - StakedAssignment { who: 20, distribution: vec![(20, 200)] }, + StakedAssignment { + who: 10, + distribution: vec![(10, 100)], + }, + StakedAssignment { + who: 20, + distribution: vec![(20, 200)], + }, ]; assert_eq!(3, reduce(&mut assignments)); @@ -885,40 +883,33 @@ mod tests { vec![ StakedAssignment { who: 1, - distribution: vec![ - (10, 10), - ] + distribution: vec![(10, 10),] }, StakedAssignment { who: 2, - distribution: vec![ - (10, 15), - (20, 5), - ], + distribution: vec![(10, 15), (20, 5),], }, StakedAssignment { who: 3, - distribution: vec![ - (20, 30), - ], + distribution: vec![(20, 30),], }, StakedAssignment { - who: 4, distribution: - vec![ - (40, 40), - ] + who: 4, + distribution: vec![(40, 40),] }, StakedAssignment { who: 5, - distribution: vec![ - (20, 15), - (30, 20), - (40, 15), - ], + distribution: vec![(20, 15), (30, 20), (40, 15),], }, // should stay untouched. - StakedAssignment { who: 10, distribution: vec![(10, 100)] }, - StakedAssignment { who: 20, distribution: vec![(20, 200)] }, + StakedAssignment { + who: 10, + distribution: vec![(10, 100)] + }, + StakedAssignment { + who: 20, + distribution: vec![(20, 200)] + }, ], ) } @@ -926,53 +917,16 @@ mod tests { #[test] fn reduce_3_common_votes_same_weight() { let mut assignments = vec![ - StakedAssignment { - who: 4, - distribution: vec![ - ( - 1000000, - 100, - ), - ( - 1000002, - 100, - ), - ( - 1000004, - 100, - ), - ], - }, - StakedAssignment { - who: 5, - distribution: vec![ - ( - 1000000, - 100, - ), - ( - 1000002, - 100, - ), - ( - 1000004, - 100, - ), - ], - }, - ]; - - reduce_4(&mut assignments); - - assert_eq!( - assignments, - vec![ StakedAssignment { who: 4, distribution: vec![ ( 1000000, - 200, + 100, + ), + ( + 1000002, + 100, ), ( 1000004, @@ -983,9 +937,13 @@ mod tests { StakedAssignment { who: 5, distribution: vec![ + ( + 1000000, + 100, + ), ( 1000002, - 200, + 100, ), ( 1000004, @@ -993,11 +951,23 @@ mod tests { ), ], }, - ], - ) - + ]; + reduce_4(&mut assignments); + assert_eq!( + assignments, + vec![ + StakedAssignment { + who: 4, + distribution: vec![(1000000, 200,), (1000004, 100,),], + }, + StakedAssignment { + who: 5, + distribution: vec![(1000002, 200,), (1000004, 100,),], + }, + ], + ) } #[test] @@ -1006,24 +976,15 @@ mod tests { let mut assignments = vec![ StakedAssignment { who: 1, - distribution: vec![ - (10, 10), - (20, 10) - ] + distribution: vec![(10, 10), (20, 10)], }, StakedAssignment { who: 1, - distribution: vec![ - (10, 15), - (20, 5), - ], + distribution: vec![(10, 15), (20, 5)], }, StakedAssignment { who: 2, - distribution: vec![ - (10, 15), - (20, 15) - ], + distribution: vec![(10, 15), (20, 15)], }, ]; @@ -1035,10 +996,7 @@ mod tests { let mut assignments = vec![ StakedAssignment { who: 1, - distribution: vec![ - (10, 15), - (20, 5), - ], + distribution: vec![(10, 15), (20, 5)], }, StakedAssignment { who: 2, @@ -1060,9 +1018,7 @@ mod tests { vec![ StakedAssignment { who: 1, - distribution: vec![ - (10, 20), - ], + distribution: vec![(10, 20),], }, StakedAssignment { who: 2, @@ -1083,101 +1039,27 @@ mod tests { let mut assignments = vec![ StakedAssignment { who: 1, - distribution: vec![ - ( - 103, - 72, - ), - ( - 101, - 53, - ), - ( - 100, - 83, - ), - ( - 102, - 38, - ), - ], + distribution: vec![(103, 72), (101, 53), (100, 83), (102, 38)], }, StakedAssignment { who: 2, - distribution: vec![ - ( - 103, - 18, - ), - ( - 101, - 36, - ), - ( - 102, - 54, - ), - ( - 100, - 94, - ), - ], + distribution: vec![(103, 18), (101, 36), (102, 54), (100, 94)], }, StakedAssignment { who: 3, - distribution: vec![ - ( - 100, - 96, - ), - ( - 101, - 35, - ), - ( - 102, - 52, - ), - ( - 103, - 69, - ), - ], + distribution: vec![(100, 96), (101, 35), (102, 52), (103, 69)], }, StakedAssignment { who: 4, - distribution: vec![ - ( - 102, - 34, - ), - ( - 100, - 47, - ), - ( - 103, - 91, - ), - ( - 101, - 73, - ), - ], + distribution: vec![(102, 34), (100, 47), (103, 91), (101, 73)], }, ]; - let winners = vec![ - 103, - 101, - 100, - 102, - ]; + let winners = vec![103, 101, 100, 102]; let n = 4; let m = winners.len() as u32; let num_reduced = reduce_all(&mut assignments); assert!(16 - num_reduced <= n + m); - } } From c8cc72f5cb70e32d62719cc795602351d4c36fe2 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 3 Mar 2020 08:34:44 +0100 Subject: [PATCH 067/106] Review comments. --- frame/babe/src/lib.rs | 8 +++--- frame/staking/Cargo.toml | 7 +++++- frame/staking/src/lib.rs | 23 ++++++++--------- frame/staking/src/mock.rs | 34 ++++++++------------------ frame/staking/src/offchain_election.rs | 17 ++++++++----- frame/staking/src/testing_utils.rs | 13 ++++++---- frame/support/src/traits.rs | 8 +++--- 7 files changed, 54 insertions(+), 56 deletions(-) diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index e3d3d2f1b0a4b..a9faf2ffe2f93 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -320,10 +320,10 @@ 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 -------------- - /// 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. + // + // -------------- 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. pub fn next_expected_epoch_change(now: T::BlockNumber) -> T::BlockNumber { let next_slot = Self::current_epoch_start().saturating_add(T::EpochDuration::get()); let slots_remaining = next_slot diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 9c533c9c13e62..27840cbc4f477 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -38,7 +38,12 @@ env_logger = "0.7.1" [features] migrate = [] -testing-utils = ["std"] +testing-utils = [ + "std", + "pallet-indices/std", + "sp-core/std", + "rand/std", +] default = ["std"] std = [ "serde", diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 9cb01e95c7ad6..6b5c7033055c0 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -243,7 +243,7 @@ //! - [Session](../pallet_session/index.html): Used to manage sessions. Also, a list of new //! validators is stored in the Session module's `Validators` at the end of each era. -#![recursion_limit="128"] +#![recursion_limit = "128"] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(test)] @@ -304,7 +304,7 @@ pub(crate) const MAX_VALIDATORS: u32 = ValidatorIndex::max_value() as u32; pub(crate) const MAX_NOMINATORS: u32 = NominatorIndex::max_value(); // Note: Maximum nomination limit is set here -- 16. -generate_compact_solution_type!(pub CompactAssignments, 16); +generate_compact_solution_type!(pub GenericCompactAssignments, 16); /// Data type used to index nominators in the compact type pub type NominatorIndex = u32; @@ -329,11 +329,8 @@ pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; /// The compact type for election solutions. -pub type Compact = CompactAssignments< - NominatorIndex, - ValidatorIndex, - OffchainAccuracy, ->; +pub type CompactAssignments = + GenericCompactAssignments; type PositiveImbalanceOf = <::Currency as Currency<::AccountId>>::PositiveImbalance; @@ -737,7 +734,7 @@ pub trait Trait: frame_system::Trait { /// The NPoS reward curve to use. type RewardCurve: Get<&'static PiecewiseLinear<'static>>; - /// Something that can predict the next session change. + /// Something that can estimate the next session change, accurately or as a best effort guess. type NextSessionChange: EstimateNextSessionChange; /// How many blocks ahead of the era do we try to run the phragmen offchain? Setting this to @@ -1290,7 +1287,7 @@ decl_module! { /// /// # /// - The transaction's complexity is proportional to the size of `targets`, - /// which is capped at Compact::LIMIT. + /// which is capped at CompactAssignments::LIMIT. /// - Both the reads and writes follow a similar pattern. /// # #[weight = SimpleDispatchInfo::FixedNormal(750_000)] @@ -1302,7 +1299,7 @@ decl_module! { let stash = &ledger.stash; ensure!(!targets.is_empty(), Error::::EmptyTargets); let targets = targets.into_iter() - .take(::LIMIT) + .take(::LIMIT) .map(|t| T::Lookup::lookup(t)) .collect::, _>>()?; @@ -1585,7 +1582,7 @@ decl_module! { pub fn submit_election_solution( origin, winners: Vec, - compact_assignments: Compact, + compact_assignments: CompactAssignments, score: PhragmenScore, ) { let _who = ensure_signed(origin)?; @@ -1603,7 +1600,7 @@ decl_module! { pub fn submit_election_solution_unsigned( origin, winners: Vec, - compact_assignments: Compact, + compact_assignments: CompactAssignments, score: PhragmenScore, // already used and checked in ValidateUnsigned. _validator_index: u32, @@ -1791,7 +1788,7 @@ impl Module { /// of the next round. This may be called by both a signed and an unsigned transaction. fn check_and_replace_solution( winners: Vec, - compact_assignments: Compact, + compact_assignments: CompactAssignments, compute: ElectionCompute, claimed_score: PhragmenScore, ) -> Result<(), Error> { diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 570214fffcba3..6a32cb6ae474a 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -751,11 +751,7 @@ pub fn on_offence_now( // distributed evenly. pub fn horrible_phragmen_with_post_processing( do_reduce: bool, -) -> ( - Compact, - Vec, - PhragmenScore, -) { +) -> (CompactAssignments, Vec, PhragmenScore) { use std::collections::BTreeMap; let mut backing_stake_of: BTreeMap = BTreeMap::new(); @@ -848,16 +844,12 @@ pub fn horrible_phragmen_with_post_processing( }; // convert back to ratio assignment. This takes less space. - let assignments_reduced = sp_phragmen::assignment_staked_to_ratio::< - AccountId, - OffchainAccuracy, - >(staked_assignment); + let assignments_reduced = + sp_phragmen::assignment_staked_to_ratio::(staked_assignment); - let compact = Compact::from_assignment( - assignments_reduced, - nominator_index, - validator_index, - ).unwrap(); + let compact = + CompactAssignments::from_assignment(assignments_reduced, nominator_index, validator_index) + .unwrap(); // winner ids to index let winners = winners.into_iter().map(|w| validator_index(&w).unwrap()).collect::>(); @@ -870,11 +862,7 @@ pub fn horrible_phragmen_with_post_processing( pub fn prepare_submission_with( do_reduce: bool, tweak: impl FnOnce(&mut Vec>), -) -> ( - Compact, - Vec, - PhragmenScore, -) { +) -> (CompactAssignments, Vec, PhragmenScore) { // run phragmen on the default stuff. let sp_phragmen::PhragmenResult { winners, @@ -922,11 +910,9 @@ pub fn prepare_submission_with( evaluate_support::(&support_map) }; - let compact = Compact::from_assignment( - assignments_reduced, - nominator_index, - validator_index, - ).unwrap(); + let compact = + CompactAssignments::from_assignment(assignments_reduced, nominator_index, validator_index) + .unwrap(); // winner ids to index diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 2e7daa92cab88..b6f221a1183af 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -16,7 +16,9 @@ //! Helpers for offchain worker election. -use crate::{Call, Compact, Module, NominatorIndex, OffchainAccuracy, Trait, ValidatorIndex}; +use crate::{ + Call, CompactAssignments, Module, NominatorIndex, OffchainAccuracy, Trait, ValidatorIndex, +}; use codec::Encode; use frame_support::debug; use frame_system::offchain::SubmitUnsignedTransaction; @@ -48,7 +50,7 @@ pub enum OffchainElectionError { /// The type of signature data encoded with the unsigned submission pub(crate) type SignaturePayload<'a> = ( &'a [ValidatorIndex], - &'a Compact, + &'a CompactAssignments, &'a PhragmenScore, &'a u32, ); @@ -154,7 +156,7 @@ pub fn prepare_submission( assignments: Vec>, winners: Vec<(T::AccountId, ExtendedBalance)>, do_reduce: bool, -) -> Result<(Vec, Compact, PhragmenScore), OffchainElectionError> +) -> Result<(Vec, CompactAssignments, PhragmenScore), OffchainElectionError> where ExtendedBalance: From<::Inner>, { @@ -217,9 +219,12 @@ where }; // compact encode the assignment. - let compact = - Compact::from_assignment(low_accuracy_assignment, nominator_index, validator_index) - .unwrap(); + let compact = CompactAssignments::from_assignment( + low_accuracy_assignment, + nominator_index, + validator_index, + ) + .unwrap(); // winners to index. let winners = winners diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index 94fdd18597b9a..bae1060987b38 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -151,7 +151,7 @@ where /// which has a less score than the seq-phragmen. pub fn get_weak_solution( do_reduce: bool, -) -> (Vec, Compact, PhragmenScore) { +) -> (Vec, CompactAssignments, PhragmenScore) { use sp_std::collections::btree_map::BTreeMap; let mut backing_stake_of: BTreeMap> = BTreeMap::new(); @@ -286,9 +286,12 @@ pub fn get_weak_solution( }; // compact encode the assignment. - let compact = - Compact::from_assignment(low_accuracy_assignment, nominator_index, validator_index) - .unwrap(); + let compact = CompactAssignments::from_assignment( + low_accuracy_assignment, + nominator_index, + validator_index, + ) + .unwrap(); // winners to index. let winners = winners @@ -310,7 +313,7 @@ pub fn get_weak_solution( /// worker code. pub fn get_seq_phragmen_solution( do_reduce: bool, -) -> (Vec, Compact, PhragmenScore) { +) -> (Vec, CompactAssignments, PhragmenScore) { let sp_phragmen::PhragmenResult { winners, assignments, diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 1e82263ee5def..a8364769d38b9 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -87,7 +87,7 @@ impl< Created: Happened, Removed: Happened, K: FullCodec, - T: FullCodec + T: FullCodec, > StoredMap for StorageMapShim { fn get(k: &K) -> T { S::get(k) } fn is_explicit(k: &K) -> bool { S::contains_key(k) } @@ -138,9 +138,11 @@ impl< } } -/// Something that can predict at which block number the next era change will happen. +/// Something that can estimate at which block number the next era change will happen in a best +/// effort manner. The result may or may night be accurate, based on the chain configuration and +/// semantics. pub trait EstimateNextSessionChange { - /// Return the block number at which the next era change will happen. + /// Return the block number at which the next era change is estimated to happen. fn estimate_next_session_change(now: BlockNumber) -> BlockNumber; } From 62d4f5e139b467dc81069da2735f8bcbe18d3398 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 3 Mar 2020 14:30:24 +0100 Subject: [PATCH 068/106] Fix cargo file --- Cargo.lock | 1 - frame/staking/Cargo.toml | 3 --- frame/staking/src/lib.rs | 5 +++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a2660bb80622..b0ce1c17902ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4360,7 +4360,6 @@ dependencies = [ "sp-application-crypto", "sp-core", "sp-io", - "sp-keyring", "sp-phragmen", "sp-runtime", "sp-staking", diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 27840cbc4f477..68ba32330a23b 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -11,7 +11,6 @@ description = "FRAME pallet staking" [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } -sp-keyring = { version = "2.0.0-alpha.2", optional = true, path = "../../primitives/keyring" } sp-std = { version = "2.0.0-alpha.2", default-features = false, path = "../../primitives/std" } sp-phragmen = { version = "2.0.0-alpha.2", default-features = false, path = "../../primitives/phragmen" } sp-io ={ path = "../../primitives/io", default-features = false , version = "2.0.0-alpha.2"} @@ -55,9 +54,7 @@ std = [ "sp-runtime/std", "sp-staking/std", "pallet-session/std", - "pallet-indices/std", "frame-system/std", "pallet-authorship/std", "sp-core/std", - "rand/std" ] diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index da2355c0ae304..3a4d186033873 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -737,8 +737,9 @@ pub trait Trait: frame_system::Trait { /// Something that can estimate the next session change, accurately or as a best effort guess. type NextSessionChange: EstimateNextSessionChange; - /// How many blocks ahead of the era do we try to run the phragmen offchain? Setting this to - /// zero will disable the offchain compute and only on-chain seq-phragmen will be used. + /// How many blocks ahead of the era, within the last do we try to run the phragmen offchain? + /// Setting this to zero will disable the offchain compute and only on-chain seq-phragmen will + /// be used. type ElectionLookahead: Get; /// The overarching call type. From 85f1aa482e366d41dfa723b345b9bdc5fb174173 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 4 Mar 2020 13:25:29 +0100 Subject: [PATCH 069/106] Almost all tests work again --- frame/staking/src/lib.rs | 56 +++++++++++++---------------- frame/staking/src/mock.rs | 4 +-- frame/staking/src/tests.rs | 74 +++++++++++++++++++------------------- 3 files changed, 64 insertions(+), 70 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index b024c17b67d8a..73b3785d073ef 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -950,25 +950,28 @@ decl_storage! { /// Snapshot of validators at the beginning of the current election window. This should only /// have a value when [`EraElectionStatus`] == `ElectionStatus::Open(_)`. - pub SnapshotValidators get(fn snapshot_validators): Option>; + 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(_)`. - pub SnapshotNominators get(fn snapshot_nominators): Option>; + SnapshotNominators get(fn snapshot_nominators): Option>; /// The current set of staking keys. - pub Keys get(fn keys): Vec; + Keys get(fn keys): Vec; /// 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. - pub QueuedElected get(fn queued_elected): Option>>; + QueuedElected get(fn queued_elected): Option>>; /// The score of the current [`QueuedElected`]. - pub QueuedScore get(fn queued_score): Option; + QueuedScore get(fn queued_score): Option; /// Flag to control the execution of the offchain election. - pub EraElectionStatus get(fn era_election_status): ElectionStatus; + EraElectionStatus get(fn era_election_status): ElectionStatus; + + /// True of the current planned session is final + IsCurrentSessionFinal get(fn is_current_session_final): bool = false; /// True if network has been upgraded to this version. /// @@ -1100,7 +1103,8 @@ decl_module! { /// this block until the end of the era. fn on_initialize(now: T::BlockNumber) { Self::ensure_storage_upgraded(); - // @guillaume check: can this ever be none? except for the initial one? And what should I use here? this or ActiveEra? + // @guillaume Is using active era okay here? not current era? These two tests are related + // offchain_election_flag_is_triggered and is_current_session_final_works let maybe_current_era = Self::active_era(); if maybe_current_era.is_none() { return; } let current_era = maybe_current_era.expect("value checked to be 'Some'").index; @@ -1108,8 +1112,7 @@ decl_module! { if // if we don't have any ongoing offchain compute. Self::era_election_status() == ElectionStatus::Closed && - // and an era is about to be changed. - Self::is_current_session_final(current_era) + Self::is_current_session_final() { let next_session_change = T::NextSessionChange::estimate_next_session_change(now); @@ -1801,7 +1804,7 @@ impl Module { ) as ExtendedBalance } - /// The session index of the current era TODO: @guillaume check + /// The session index of the given era. fn start_session_index_of(era: EraIndex) -> SessionIndex { Self::eras_start_session_index(era) .unwrap_or_else(|| { @@ -1810,18 +1813,6 @@ impl Module { }) } - /// returns true if the end of the current session leads to an era change. - fn is_current_session_final(current_era: EraIndex) -> bool { - let current_index = T::SessionInterface::current_index(); - let era_length = current_index - .checked_sub(Self::start_session_index_of(current_era)) - .unwrap_or(0); - let session_per_era = T::SessionsPerEra::get() - .checked_sub(One::one()) - .unwrap_or(0); - era_length >= session_per_era - } - /// Dump the list of validators and nominators into vectors and keep them on-chain. /// /// This data is used to efficiently evaluate election results. returns `true` if the operation @@ -2018,7 +2009,6 @@ impl Module { /// Plan a new session potentially trigger a new era. fn new_session(session_index: SessionIndex) -> Option> { - println!("new session"); if let Some(current_era) = Self::current_era() { // Initial era has been set. @@ -2027,20 +2017,21 @@ impl Module { let era_length = session_index.checked_sub(current_era_start_session_index) .unwrap_or(0); // Must never happen. - dbg!( - >::block_number(), - Self::current_era(), - current_era_start_session_index, - session_index, - ); match ForceEra::get() { Forcing::ForceNew => ForceEra::kill(), Forcing::ForceAlways => (), Forcing::NotForcing if era_length >= T::SessionsPerEra::get() => (), - _ => return None, + _ => { + // not forcing, not a new era either. If final, set the flag. + if era_length + 1 >= >::get() { + IsCurrentSessionFinal::put(true); + } + return None + }, } - dbg!("NEW ERAAAAAAA"); + // new era. + IsCurrentSessionFinal::put(false); Self::new_era(session_index) } else { // Set initial era @@ -2411,9 +2402,10 @@ impl Module { debug::native::info!( target: "staking", - "new validator set of size {:?} has been elected via {:?}", + "new validator set of size {:?} has been elected via {:?} for era {:?}", elected_stashes.len(), compute, + current_era, ); Some(elected_stashes) diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 9262a39ce2157..0ad5a2a5ef720 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -472,7 +472,7 @@ impl ExtBuilder { } pub fn offchain_phragmen_ext(self) -> Self { self - .session_per_era(3) + .session_per_era(4) .session_length(5) .election_lookahead(3) .local_key_account(11) @@ -680,7 +680,7 @@ pub fn advance_session() { } pub fn start_session(session_index: SessionIndex) { - assert_eq!(>::get(), 1, "start_session can only be used with session length 1."); + assert_eq!(>::get(), 1, "start_session can only be used with session length 1."); for i in Session::current_index()..session_index { Staking::on_finalize(System::block_number()); System::set_block_number((i + 1).into()); diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 289eaa2bab924..0ec9e7581228c 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -1920,7 +1920,6 @@ fn era_is_always_same_length() { let session_per_era = >::get(); start_era(1); - // TODO: @guillaume maybe we can use start_session_index_of here. assert_eq!(Staking::eras_start_session_index(active_era()).unwrap(), session_per_era); start_era(2); @@ -2770,29 +2769,20 @@ mod offchain_phragmen { .build() .execute_with(|| { start_era(1); - assert_eq!(Session::current_index(), 4); + assert_eq!(Session::current_index(), 3); assert_eq!(Staking::current_era(), Some(1)); - assert_eq!(Staking::is_current_session_final( - Staking::active_era().unwrap().index), - false, - ); + assert_eq!(Staking::is_current_session_final(), false); start_session(4); - assert_eq!(Session::current_index(), 5); + assert_eq!(Session::current_index(), 4); assert_eq!(Staking::current_era(), Some(1)); - assert_eq!(Staking::is_current_session_final( - Staking::active_era().unwrap().index), - true, - ); + assert_eq!(Staking::is_current_session_final(), true); start_session(5); - assert_eq!(Session::current_index(), 6); + assert_eq!(Session::current_index(), 5); // era changed. assert_eq!(Staking::current_era(), Some(2)); - assert_eq!(Staking::is_current_session_final( - Staking::active_era().unwrap().index), - false, - ); + assert_eq!(Staking::is_current_session_final(), false); }) } @@ -2815,40 +2805,47 @@ mod offchain_phragmen { run_to_block(18); assert_session_era!(1, 0); - assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); - assert!(Staking::snapshot_nominators().is_none()); - assert!(Staking::snapshot_validators().is_none()); - run_to_block(39); + run_to_block(27); + assert_session_era!(2, 0); + + run_to_block(36); assert_session_era!(3, 0); + // fist era has session 0, which has 0 blocks length, so we have in total 40 blocks + // in the era. + run_to_block(37); + assert_session_era!(3, 0); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(37)); + assert!(Staking::snapshot_nominators().is_some()); + assert!(Staking::snapshot_validators().is_some()); + + run_to_block(38); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(37)); + + run_to_block(39); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(37)); + run_to_block(40); - assert_session_era!(4, 0); + assert_session_era!(4, 0); // TODO @guillaume I was expecting active era to be bumped by this point. Double check assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); - run_to_block(46); - assert_session_era!(4, 0); + run_to_block(86); + assert_session_era!(8, 1); assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); - run_to_block(47); - assert_session_era!(4, 0); - assert_eq!(Staking::era_election_status(), ElectionStatus::Open(47)); + // second era onwards has 50 blocks per era. + run_to_block(87); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(87)); assert!(Staking::snapshot_nominators().is_some()); assert!(Staking::snapshot_validators().is_some()); - run_to_block(49); - assert_session_era!(4, 0); - assert_eq!(Staking::era_election_status(), ElectionStatus::Open(47)); - assert!(Staking::snapshot_nominators().is_some()); - assert!(Staking::snapshot_validators().is_some()); - - run_to_block(50); - dbg!("HERE"); - assert_session_era!(5, 1); + run_to_block(90); + assert_session_era!(9, 1); assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); @@ -3526,7 +3523,7 @@ mod offchain_phragmen { run_to_block(15); // open the election window and create snapshots. - run_to_block(27); + run_to_block(32); // a solution that has been prepared before the slash. let (pre_compact, pre_winners, pre_score) = prepare_submission_with(false, |_| {}); @@ -3679,6 +3676,11 @@ mod offchain_phragmen { ); }) } + + #[test] + fn forcing_eras_works_with_offchain_fallback_or_submission() { + unimplemented!(); + } } #[test] From 9b8af93b0b075a060f0ddc1af6e93d6361d0134b Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Wed, 4 Mar 2020 14:30:26 +0100 Subject: [PATCH 070/106] Update frame/staking/src/tests.rs Co-Authored-By: thiolliere --- frame/staking/src/tests.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 0ec9e7581228c..a1d94b436426e 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2827,7 +2827,7 @@ mod offchain_phragmen { assert_eq!(Staking::era_election_status(), ElectionStatus::Open(37)); run_to_block(40); - assert_session_era!(4, 0); // TODO @guillaume I was expecting active era to be bumped by this point. Double check + assert_session_era!(4, 0); assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); @@ -4217,4 +4217,3 @@ fn set_history_depth_works() { assert!(!::ErasTotalStake::contains_key(10 - 5)); }); } - From 58fcf2784da3ee9543f1f85a11006f656593e919 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 4 Mar 2020 14:37:24 +0100 Subject: [PATCH 071/106] Fix review comments --- Cargo.lock | 1 + frame/staking/Cargo.toml | 9 +- frame/staking/src/lib.rs | 184 ++++++++++++------------- frame/staking/src/offchain_election.rs | 40 +++--- frame/staking/src/tests.rs | 5 - primitives/phragmen/src/helpers.rs | 4 +- primitives/phragmen/src/node.rs | 2 +- 7 files changed, 119 insertions(+), 126 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba9b22592cd46..16e3f67c30c11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4391,6 +4391,7 @@ dependencies = [ "sp-runtime", "sp-staking", "sp-std", + "static_assertions", "substrate-test-utils", ] diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 475f55de7f06b..3bab1d26d4c78 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -21,18 +21,19 @@ frame-system = { version = "2.0.0-alpha.2", default-features = false, path = ".. pallet-session = { version = "2.0.0-alpha.2", features = ["historical"], path = "../session", default-features = false } pallet-authorship = { version = "2.0.0-alpha.2", default-features = false, path = "../authorship" } sp-application-crypto = { version = "2.0.0-alpha.2", default-features = false, path = "../../primitives/application-crypto" } +static_assertions = "1.1.0" # Optional imports for tesing-utils feature -pallet-indices = { version = "2.0.0-alpha.2", optional = true, path = "../indices" } -sp-core = { version = "2.0.0-alpha.2", optional = true, path = "../../primitives/core" } -rand = { version = "0.7.3", optional = true } +pallet-indices = { version = "2.0.0-alpha.2", optional = true, path = "../indices", default-features = false } +sp-core = { version = "2.0.0-alpha.2", optional = true, path = "../../primitives/core", default-features = false } +rand = { version = "0.7.3", optional = true, default-features = false } [dev-dependencies] pallet-balances = { version = "2.0.0-alpha.2", path = "../balances" } pallet-timestamp = { version = "2.0.0-alpha.2", path = "../timestamp" } pallet-staking-reward-curve = { version = "2.0.0-alpha.2", path = "../staking/reward-curve" } substrate-test-utils = { version = "2.0.0-alpha.2", path = "../../test-utils" } -parking_lot = { version = "0.10.0" } +parking_lot = "0.10.0" env_logger = "0.7.1" [features] diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 73b3785d073ef..08a898de8f79e 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -281,6 +281,7 @@ use sp_runtime::{ }, transaction_validity::{ TransactionValidityError, TransactionValidity, ValidTransaction, InvalidTransaction, + UnknownTransaction, }, }; use sp_staking::{ @@ -295,7 +296,7 @@ use frame_system::{ }; use sp_phragmen::{ ExtendedBalance, Assignment, PhragmenScore, PhragmenResult, build_support_map, evaluate_support, - elect, generate_compact_solution_type, is_score_better, VotingLimit, + elect, generate_compact_solution_type, is_score_better, VotingLimit, SupportMap, }; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; @@ -303,25 +304,29 @@ const MAX_UNLOCKING_CHUNKS: usize = 32; const MAX_NOMINATIONS: usize = ::LIMIT; const STAKING_ID: LockIdentifier = *b"staking "; -/// Maximum number of stakers that can be stored in a snapshot. -pub(crate) const MAX_VALIDATORS: u32 = ValidatorIndex::max_value() as u32; -pub(crate) const MAX_NOMINATORS: u32 = NominatorIndex::max_value(); - -// Note: Maximum nomination limit is set here -- 16. -generate_compact_solution_type!(pub GenericCompactAssignments, 16); - /// Data type used to index nominators in the compact type pub type NominatorIndex = u32; /// Data type used to index validators in the compact type. pub type ValidatorIndex = u16; +// Ensure the size of both ValidatorIndex and NominatorIndex +static_assertions::const_assert!(sp_std::mem::size_of::() <= sp_std::mem::size_of::()); +static_assertions::const_assert!(sp_std::mem::size_of::() <= sp_std::mem::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; + /// Counter for the number of eras that have passed. pub type EraIndex = u32; /// Counter for the number of "reward" points earned by a given validator. pub type RewardPoint = u32; +// Note: Maximum nomination limit is set here -- 16. +generate_compact_solution_type!(pub GenericCompactAssignments, 16); + /// Information regarding the active era (era in used in session). #[derive(Encode, Decode, Debug)] pub struct ActiveEraInfo { @@ -653,6 +658,23 @@ pub enum ElectionStatus { Open(BlockNumber), } +impl ElectionStatus { + fn is_open_at(&self, n: BlockNumber) -> bool { + *self == Self::Open(n) + } + + fn is_closed(&self) -> bool { + match self { + Self::Closed => true, + _ => false + } + } + + fn is_open(&self) -> bool { + !self.is_closed() + } +} + impl Default for ElectionStatus { fn default() -> Self { Self::Closed @@ -673,8 +695,6 @@ pub trait SessionInterface: frame_system::Trait { fn validators() -> Vec; /// Prune historical session tries up to but not including the given index. fn prune_historical_up_to(up_to: SessionIndex); - /// Current session index. - fn current_index() -> SessionIndex; } impl SessionInterface<::AccountId> for T where @@ -698,10 +718,6 @@ impl SessionInterface<::AccountId> for T whe fn prune_historical_up_to(up_to: SessionIndex) { >::prune_up_to(up_to); } - - fn current_index() -> SessionIndex { - >::current_index() - } } pub trait Trait: frame_system::Trait { @@ -1103,15 +1119,9 @@ decl_module! { /// this block until the end of the era. fn on_initialize(now: T::BlockNumber) { Self::ensure_storage_upgraded(); - // @guillaume Is using active era okay here? not current era? These two tests are related - // offchain_election_flag_is_triggered and is_current_session_final_works - let maybe_current_era = Self::active_era(); - if maybe_current_era.is_none() { return; } - let current_era = maybe_current_era.expect("value checked to be 'Some'").index; - if // if we don't have any ongoing offchain compute. - Self::era_election_status() == ElectionStatus::Closed && + Self::era_election_status().is_closed() && Self::is_current_session_final() { let next_session_change = @@ -1146,11 +1156,9 @@ decl_module! { /// Check if the current block number is the one at which the election window has been set /// to open. If so, it runs the offchain worker code. fn offchain_worker(now: T::BlockNumber) { - debug::RuntimeLogger::init(); use offchain_election::{set_check_offchain_execution_status, compute_offchain_election}; - let window_open = - Self::era_election_status() == ElectionStatus::::Open(now); + let window_open = Self::era_election_status().is_open_at(now); if window_open { let offchain_status = set_check_offchain_execution_status::(now); @@ -1821,12 +1829,12 @@ impl Module { let validators = >::enumerate().map(|(v, _)| v).collect::>(); let mut nominators = >::enumerate().map(|(n, _)| n).collect::>(); - // all validators nominate themselves; - nominators.extend(validators.clone()); - - let num_validators = validators.len() as u32; - let num_nominators = nominators.len() as u32; - if num_validators > MAX_VALIDATORS || num_nominators > MAX_NOMINATORS { + let num_validators = validators.len(); + let num_nominators = nominators.len(); + if + num_validators > MAX_VALIDATORS || + num_nominators.saturating_add(num_validators) > MAX_NOMINATORS + { debug::native::warn!( target: "staking", "Snapshot size too big [{} <> {}][{} <> {}].", @@ -1837,11 +1845,13 @@ impl Module { ); false } else { + // all validators nominate themselves; + nominators.extend(validators.clone()); + >::put(validators); >::put(nominators); true } - } /// Clears both snapshots of stakers. @@ -2048,10 +2058,10 @@ impl Module { claimed_score: PhragmenScore, ) -> Result<(), Error> { // discard early solutions - match Self::era_election_status() { - ElectionStatus::Closed => Err(Error::::PhragmenEarlySubmission)?, - ElectionStatus::Open(_) => { /* Open and no solutions received. Allowed. */ }, - } + ensure!( + Self::era_election_status().is_open(), + Error::::PhragmenEarlySubmission, + ); // assume the given score is valid. Is it better than what we have on-chain, if we have any? if let Some(queued_score) = Self::queued_score() { @@ -2084,11 +2094,9 @@ impl Module { // helpers let nominator_at = |i: NominatorIndex| -> Option { - // NOTE: we assume both `ValidatorIndex` and `NominatorIndex` are smaller than usize. snapshot_nominators.get(i as usize).cloned() }; let validator_at = |i: ValidatorIndex| -> Option { - // NOTE: we assume both `ValidatorIndex` and `NominatorIndex` are smaller than usize. snapshot_validators.get(i as usize).cloned() }; @@ -2179,28 +2187,7 @@ impl Module { ensure!(submitted_score == claimed_score, Error::::PhragmenBogusScore); // At last, alles Ok. Exposures and store the result. - let to_balance = |e: ExtendedBalance| - >>::convert(e); - - let exposures = supports.into_iter().map(|(validator, support)| { - let mut others = Vec::new(); - let mut own: BalanceOf = Zero::zero(); - let mut total: BalanceOf = Zero::zero(); - support.voters - .into_iter() - .map(|(target, weight)| (target, to_balance(weight))) - .for_each(|(nominator, stake)| { - if nominator == validator { - own = own.saturating_add(stake); - } else { - others.push(IndividualExposure { who: nominator, value: stake }); - } - total = total.saturating_add(stake); - }); - let exposure = Exposure { own, others, total }; - - (validator, exposure) - }).collect::)>>(); + let exposures = Self::collect_exposure(supports); debug::native::info!( target: "staking", @@ -2425,7 +2412,7 @@ impl Module { Self::do_phragmen_with_post_processing::(ElectionCompute::OnChain) ); - // either way, kill this. We remove it here to make sure it always has teh exact same + // either way, kill this. We remove it here to make sure it always has the exact same // lifetime as `QueuedElected`. QueuedScore::kill(); @@ -2461,39 +2448,8 @@ impl Module { &staked_assignments, ); - let to_balance = |e: ExtendedBalance| - >>::convert(e); - // collect exposures - let exposures = supports.into_iter().map(|(validator, support)| { - // build `struct exposure` from `support` - let mut others = Vec::new(); - let mut own: BalanceOf = Zero::zero(); - let mut total: BalanceOf = Zero::zero(); - support.voters - .into_iter() - .map(|(nominator, weight)| (nominator, to_balance(weight))) - .for_each(|(nominator, stake)| { - if nominator == validator { - own = own.saturating_add(stake); - } else { - others.push(IndividualExposure { who: nominator, value: stake }); - } - total = total.saturating_add(stake); - }); - - let exposure = Exposure { - own, - others, - // This might reasonably saturate and we cannot do much about it. The sum of - // someone's stake might exceed the balance type if they have the maximum amount - // of balance and receive some support. This is super unlikely to happen, yet we - // simulate it in some tests. - total, - }; - - (validator, exposure) - }).collect::)>>(); + let exposures = Self::collect_exposure(supports); // In order to keep the property required by `n_session_ending` that we must return the // new validator set even if it's the same as the old, as long as any underlying @@ -2521,8 +2477,7 @@ impl Module { /// No storage item is updated. fn do_phragmen() -> Option> { let mut all_nominators: Vec<(T::AccountId, Vec)> = Vec::new(); - let all_validator_candidates_iter = >::enumerate(); - let all_validators = all_validator_candidates_iter.map(|(who, _pref)| { + let all_validators = >::enumerate().map(|(who, _pref)| { // append self vote let self_vote = (who.clone(), vec![who.clone()]); all_nominators.push(self_vote); @@ -2555,6 +2510,38 @@ impl Module { ) } + /// Consume a set of [`Supports`] from [`sp_phragmen`] and collect them into a [`Exposure`] + fn collect_exposure(supports: SupportMap) -> Vec<(T::AccountId, Exposure>)> { + let to_balance = |e: ExtendedBalance| + >>::convert(e); + + supports.into_iter().map(|(validator, support)| { + // build `struct exposure` from `support` + let mut others = Vec::new(); + let mut own: BalanceOf = Zero::zero(); + let mut total: BalanceOf = Zero::zero(); + support.voters + .into_iter() + .map(|(nominator, weight)| (nominator, to_balance(weight))) + .for_each(|(nominator, stake)| { + if nominator == validator { + own = own.saturating_add(stake); + } else { + others.push(IndividualExposure { who: nominator, value: stake }); + } + total = total.saturating_add(stake); + }); + + let exposure = Exposure { + own, + others, + total, + }; + + (validator, exposure) + }).collect::)>>() + } + /// Remove all associated data of a stash account from the staking system. /// /// Assumes storage is upgraded before calling. @@ -3098,7 +3085,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { use offchain_election::SignaturePayload; // discard early solution - if Self::era_election_status() == ElectionStatus::Closed { + if Self::era_election_status().is_closed() { debug::native::debug!( target: "staking", "rejecting unsigned transaction because it is too early." @@ -3128,7 +3115,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { let all_keys = Self::keys(); let validator_key = all_keys.get(*validator_index as usize) // validator index is incorrect -- no key corresponds to it. - .ok_or(TransactionValidityError::Invalid(InvalidTransaction::Custom(0u8).into()))?; + .ok_or(TransactionValidityError::Unknown(UnknownTransaction::CannotLookup.into()))?; let signature_valid = payload.using_encoded(|encoded_payload| { validator_key.verify(&encoded_payload, &signature) @@ -3142,7 +3129,10 @@ impl frame_support::unsigned::ValidateUnsigned for Module { priority: score[0].saturated_into(), requires: vec![], provides: vec![(Self::current_era(), validator_key).encode()], - longevity: TryInto::::try_into(T::ElectionLookahead::get()).unwrap_or(150_u64), + // longevity: TryInto::::try_into(T::ElectionLookahead::get()).unwrap_or(150_u64), + longevity: TryInto::::try_into( + T::NextSessionChange::estimate_next_session_change(>::block_number()) - >::block_number() + ).unwrap_or(150_u64), propagate: true, }) } else { diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index b6f221a1183af..81a0bb05f1c8b 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -43,8 +43,16 @@ pub enum OffchainElectionError { ElectionFailed, /// The snapshot data is not available. SnapshotUnavailable, - /// Failed to create the compact type. - CompactFailed, + /// Error from phragmen crate. This usually relates to compact operation. + PhragmenError(sp_phragmen::Error), + /// One of the computed winners is invalid. + InvalidWinner, +} + +impl From for OffchainElectionError { + fn from(e: sp_phragmen::Error) -> Self { + Self::PhragmenError(e) + } } /// The type of signature data encoded with the unsigned submission @@ -213,8 +221,7 @@ where >::slashable_balance_of_extended, ); - let (support_map, _) = - build_support_map::(winners.as_slice(), staked.as_slice()); + let (support_map, _) = build_support_map::(&winners, &staked); evaluate_support::(&support_map) }; @@ -223,21 +230,20 @@ where low_accuracy_assignment, nominator_index, validator_index, - ) - .unwrap(); + ).map_err(|e| OffchainElectionError::from(e))?; // winners to index. - let winners = winners - .into_iter() - .map(|w| { - snapshot_validators - .iter() - .position(|v| *v == w) - .unwrap() + let mut winners_indexed: Vec = Vec::with_capacity(winners.len()); + for w in winners { + if let Some(idx) = snapshot_validators.iter().position(|v| *v == w) { + let compact_index: ValidatorIndex = idx .try_into() - .unwrap() - }) - .collect::>(); + .map_err(|_| OffchainElectionError::InvalidWinner)?; + winners_indexed.push(compact_index); + } else { + return Err(OffchainElectionError::InvalidWinner); + } + } - Ok((winners, compact, score)) + Ok((winners_indexed, compact, score)) } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 0ec9e7581228c..3e1ed2f3cb1c0 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -3676,11 +3676,6 @@ mod offchain_phragmen { ); }) } - - #[test] - fn forcing_eras_works_with_offchain_fallback_or_submission() { - unimplemented!(); - } } #[test] diff --git a/primitives/phragmen/src/helpers.rs b/primitives/phragmen/src/helpers.rs index 167c0de94d191..b05bd41ec518e 100644 --- a/primitives/phragmen/src/helpers.rs +++ b/primitives/phragmen/src/helpers.rs @@ -33,8 +33,8 @@ where ratio .into_iter() .map(|a| { - let who = a.who.clone(); - a.into_staked(stake_of(&who), true) + let stake = stake_of(&a.who); + a.into_staked(stake, true) }) .collect() } diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index 7687e3c33c7fe..a66f9dbb4817b 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -57,7 +57,7 @@ impl PartialEq for NodeId { impl Eq for NodeId {} #[cfg(feature = "std")] -impl sp_std::fmt::Debug for NodeId { +impl sp_std::fmt::Debug for NodeId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> sp_std::fmt::Result { write!( f, From ef7eab27963537e84f928c1769c7a11eb666df32 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 4 Mar 2020 15:35:17 +0100 Subject: [PATCH 072/106] More review stuff --- frame/staking/src/lib.rs | 47 +++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 08a898de8f79e..afc282132015c 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -276,7 +276,7 @@ use sp_runtime::{ Perbill, PerU16, PerThing, RuntimeDebug, RuntimeAppPublic, curve::PiecewiseLinear, traits::{ - Convert, Zero, One, StaticLookup, CheckedSub, Saturating, SaturatedConversion, AtLeast32Bit, + Convert, Zero, StaticLookup, CheckedSub, Saturating, SaturatedConversion, AtLeast32Bit, EnsureOrigin, Member, SignedExtension, }, transaction_validity::{ @@ -1489,6 +1489,8 @@ decl_module! { } } + // ----- Root calls. + /// The ideal number of validators. #[weight = SimpleDispatchInfo::FixedNormal(5_000)] fn set_validator_count(origin, #[compact] new: u32) { @@ -1496,8 +1498,6 @@ decl_module! { ValidatorCount::put(new); } - // ----- Root calls. - /// Force there to be no new eras indefinitely. /// /// # @@ -1812,15 +1812,6 @@ impl Module { ) as ExtendedBalance } - /// The session index of the given era. - fn start_session_index_of(era: EraIndex) -> SessionIndex { - Self::eras_start_session_index(era) - .unwrap_or_else(|| { - frame_support::print("Error: start_session_index must be set for current_era"); - 0 - }) - } - /// Dump the list of validators and nominators into vectors and keep them on-chain. /// /// This data is used to efficiently evaluate election results. returns `true` if the operation @@ -2022,7 +2013,11 @@ impl Module { if let Some(current_era) = Self::current_era() { // Initial era has been set. - let current_era_start_session_index = Self::start_session_index_of(current_era); + let current_era_start_session_index = Self::eras_start_session_index(current_era) + .unwrap_or_else(|| { + frame_support::print("Error: start_session_index must be set for current_era"); + 0 + }); let era_length = session_index.checked_sub(current_era_start_session_index) .unwrap_or(0); // Must never happen. @@ -2033,7 +2028,7 @@ impl Module { Forcing::NotForcing if era_length >= T::SessionsPerEra::get() => (), _ => { // not forcing, not a new era either. If final, set the flag. - if era_length + 1 >= >::get() { + if era_length + 1 >= T::SessionsPerEra::get() { IsCurrentSessionFinal::put(true); } return None @@ -3053,16 +3048,24 @@ impl SignedExtension for LockStakingStatus { if let Some(inner_call) = call.is_sub_type() { if let ElectionStatus::Open(_) = >::era_election_status() { match inner_call { - Call::::bond(..) | - Call::::unbond(..) | - Call::::bond_extra(..) | - Call::::rebond(..) | - Call::::validate(..) | - Call::::nominate(..) | - Call::::chill(..) => { + Call::::set_payee(..) | + Call::::set_controller(..) | + Call::::set_validator_count(..) | + Call::::force_no_eras(..) | + Call::::force_new_era(..) | + Call::::set_invulnerables(..) | + Call::::force_unstake(..) | + Call::::force_new_era_always(..) | + Call::::cancel_deferred_slash(..) | + Call::::set_history_depth(..) | + Call::::reap_stash(..) | + Call::::submit_election_solution(..) | + Call::::submit_election_solution_unsigned(..) => { + // These calls are allowed. Nothing. + } + _ => { return Err(InvalidTransaction::Stale.into()); } - _ => { /* no problem */ } } } } From 5f704341f32f43e8f07335fd00e9095f7710089f Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 4 Mar 2020 16:06:49 +0100 Subject: [PATCH 073/106] Some nits --- bin/node/runtime/src/lib.rs | 1 - frame/staking/Cargo.toml | 1 + frame/staking/fuzz/Cargo.lock | 67 ++++++++++++++++++----------------- frame/staking/src/lib.rs | 2 +- 4 files changed, 36 insertions(+), 35 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index f1ce3ff88494e..902a3682a1224 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -316,7 +316,6 @@ parameter_types! { pub const BondingDuration: pallet_staking::EraIndex = 24 * 28; pub const SlashDeferDuration: pallet_staking::EraIndex = 24 * 7; // 1/4 the bonding duration. pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; - /// This means that the offchain election is disabled for now. pub const ElectionLookahead: BlockNumber = 30; pub const MaxNominatorRewardedPerValidator: u32 = 64; } diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 3bab1d26d4c78..bae804f99b08e 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -57,5 +57,6 @@ std = [ "pallet-session/std", "frame-system/std", "pallet-authorship/std", + "sp-application-crypto/std", "sp-core/std", ] diff --git a/frame/staking/fuzz/Cargo.lock b/frame/staking/fuzz/Cargo.lock index ded6e7cd64fd8..693dd7a95cc56 100644 --- a/frame/staking/fuzz/Cargo.lock +++ b/frame/staking/fuzz/Cargo.lock @@ -321,7 +321,7 @@ dependencies = [ name = "frame-benchmarking" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-api 2.0.0-alpha.3", "sp-io 2.0.0-alpha.3", "sp-runtime 2.0.0-alpha.3", @@ -333,7 +333,7 @@ dependencies = [ name = "frame-metadata" version = "11.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0-alpha.3", "sp-std 2.0.0-alpha.3", @@ -349,7 +349,7 @@ dependencies = [ "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-arithmetic 2.0.0-alpha.3", @@ -398,7 +398,7 @@ version = "2.0.0-alpha.3" dependencies = [ "frame-support 2.0.0-alpha.3", "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0-alpha.3", "sp-io 2.0.0-alpha.3", @@ -571,7 +571,7 @@ name = "impl-codec" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -777,7 +777,7 @@ dependencies = [ "frame-support 2.0.0-alpha.3", "frame-system 2.0.0-alpha.3", "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-authorship 2.0.0-alpha.3", "sp-core 2.0.0-alpha.3", "sp-inherents 2.0.0-alpha.3", @@ -793,7 +793,7 @@ dependencies = [ "frame-benchmarking 2.0.0-alpha.3", "frame-support 2.0.0-alpha.3", "frame-system 2.0.0-alpha.3", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-io 2.0.0-alpha.3", "sp-runtime 2.0.0-alpha.3", @@ -806,7 +806,7 @@ version = "2.0.0-alpha.3" dependencies = [ "frame-support 2.0.0-alpha.3", "frame-system 2.0.0-alpha.3", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0-alpha.3", "sp-io 2.0.0-alpha.3", @@ -823,7 +823,7 @@ dependencies = [ "frame-system 2.0.0-alpha.3", "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "pallet-timestamp 2.0.0-alpha.3", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-io 2.0.0-alpha.3", "sp-runtime 2.0.0-alpha.3", @@ -841,7 +841,7 @@ dependencies = [ "pallet-authorship 2.0.0-alpha.3", "pallet-indices 2.0.0-alpha.3", "pallet-session 2.0.0-alpha.3", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-application-crypto 2.0.0-alpha.3", @@ -851,6 +851,7 @@ dependencies = [ "sp-runtime 2.0.0-alpha.3", "sp-staking 2.0.0-alpha.3", "sp-std 2.0.0-alpha.3", + "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -866,7 +867,7 @@ dependencies = [ "pallet-staking 2.0.0-alpha.3", "pallet-staking-reward-curve 2.0.0-alpha.3", "pallet-timestamp 2.0.0-alpha.3", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0-alpha.3", "sp-io 2.0.0-alpha.3", @@ -893,7 +894,7 @@ dependencies = [ "frame-support 2.0.0-alpha.3", "frame-system 2.0.0-alpha.3", "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-inherents 2.0.0-alpha.3", "sp-io 2.0.0-alpha.3", @@ -904,19 +905,19 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "1.1.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "byte-slice-cast 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec-derive 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parity-scale-codec-derive" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1369,7 +1370,7 @@ name = "sp-api" version = "2.0.0-alpha.3" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-api-proc-macro 2.0.0-alpha.3", "sp-core 2.0.0-alpha.3", "sp-runtime 2.0.0-alpha.3", @@ -1393,7 +1394,7 @@ dependencies = [ name = "sp-application-crypto" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0-alpha.3", "sp-io 2.0.0-alpha.3", @@ -1406,7 +1407,7 @@ version = "2.0.0-alpha.3" dependencies = [ "integer-sqrt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-debug-derive 2.0.0-alpha.3", "sp-std 2.0.0-alpha.3", @@ -1416,7 +1417,7 @@ dependencies = [ name = "sp-authorship" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-inherents 2.0.0-alpha.3", "sp-runtime 2.0.0-alpha.3", "sp-std 2.0.0-alpha.3", @@ -1438,7 +1439,7 @@ dependencies = [ "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1484,7 +1485,7 @@ name = "sp-inherents" version = "2.0.0-alpha.3" dependencies = [ "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0-alpha.3", "sp-std 2.0.0-alpha.3", @@ -1497,7 +1498,7 @@ dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0-alpha.3", "sp-externalities 0.8.0-alpha.3", "sp-runtime-interface 2.0.0-alpha.3", @@ -1529,7 +1530,7 @@ dependencies = [ name = "sp-phragmen" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0-alpha.3", "sp-phragmen-compact 2.0.0-dev", @@ -1553,7 +1554,7 @@ version = "2.0.0-alpha.3" dependencies = [ "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1570,7 +1571,7 @@ dependencies = [ name = "sp-runtime-interface" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "sp-externalities 0.8.0-alpha.3", "sp-runtime-interface-proc-macro 2.0.0-alpha.3", @@ -1594,7 +1595,7 @@ dependencies = [ name = "sp-staking" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-runtime 2.0.0-alpha.3", "sp-std 2.0.0-alpha.3", ] @@ -1606,7 +1607,7 @@ dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0-alpha.3", @@ -1636,7 +1637,7 @@ name = "sp-timestamp" version = "2.0.0-alpha.3" dependencies = [ "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-api 2.0.0-alpha.3", "sp-inherents 2.0.0-alpha.3", "sp-runtime 2.0.0-alpha.3", @@ -1650,7 +1651,7 @@ version = "2.0.0-alpha.3" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "memory-db 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-core 2.0.0-alpha.3", "sp-std 2.0.0-alpha.3", "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1662,7 +1663,7 @@ name = "sp-version" version = "2.0.0-alpha.3" dependencies = [ "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sp-runtime 2.0.0-alpha.3", "sp-std 2.0.0-alpha.3", @@ -1673,7 +1674,7 @@ name = "sp-wasm-interface" version = "2.0.0-alpha.3" dependencies = [ "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-std 2.0.0-alpha.3", "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2136,8 +2137,8 @@ dependencies = [ "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" "checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507" -"checksum parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34e513ff3e406f3ede6796dcdc83d0b32ffb86668cea1ccf7363118abeb00476" +"checksum parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f509c5e67ca0605ee17dcd3f91ef41cadd685c75a298fb6261b781a5acb3f910" +"checksum parity-scale-codec-derive 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a0ec292e92e8ec7c58e576adacc1e3f399c597c8f263c42f18420abe58e7245" "checksum parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ef1476e40bf8f5c6776e9600983435821ca86eb9819d74a6207cca69d091406a" "checksum parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" "checksum parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index afc282132015c..31e1b24adad73 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -645,7 +645,7 @@ pub struct ElectionResult { /// Flat list of new exposures, to be updated in the [`Exposure`] storage. exposures: Vec<(AccountId, Exposure)>, /// Type of the result. This is kept on chain only to track and report the best score's - /// submission type. An optimisation can could remove this. + /// submission type. An optimisation could remove this. compute: ElectionCompute, } From 58267dc480fe83221f53c9f2c1583ac6251dd40b Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 5 Mar 2020 11:06:42 +0100 Subject: [PATCH 074/106] Fix new staking / session / babe relation --- bin/node/runtime/src/lib.rs | 3 +- frame/authority-discovery/src/lib.rs | 1 + frame/babe/src/lib.rs | 4 +- frame/babe/src/mock.rs | 1 + frame/im-online/src/mock.rs | 1 + frame/session/src/lib.rs | 56 ++++++++++++++++++++++--- frame/session/src/mock.rs | 1 + frame/staking/fuzz/Cargo.lock | 2 + frame/staking/fuzz/Cargo.toml | 2 +- frame/staking/fuzz/fuzz_targets/mock.rs | 19 +++------ frame/staking/src/lib.rs | 11 ++--- frame/staking/src/mock.rs | 17 ++------ frame/staking/src/testing_utils.rs | 10 +++-- frame/support/src/traits.rs | 30 +++++++++---- 14 files changed, 100 insertions(+), 58 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 902a3682a1224..c77ad035c1938 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -293,6 +293,7 @@ impl pallet_session::Trait for Runtime { type SessionHandler = ::KeyTypeIdProviders; type Keys = SessionKeys; type DisabledValidatorsThreshold = DisabledValidatorsThreshold; + type NextSessionRotation = Babe; } impl pallet_session::historical::Trait for Runtime { @@ -335,7 +336,7 @@ impl pallet_staking::Trait for Runtime { type SlashCancelOrigin = pallet_collective::EnsureProportionAtLeast<_3, _4, AccountId, CouncilCollective>; type SessionInterface = Self; type RewardCurve = RewardCurve; - type NextSessionChange = Babe; + type NextNewSession = Session; type ElectionLookahead = ElectionLookahead; type Call = Call; type SubmitTransaction = TransactionSubmitterOf; diff --git a/frame/authority-discovery/src/lib.rs b/frame/authority-discovery/src/lib.rs index 8ee4931e488c3..b8f28b432ba75 100644 --- a/frame/authority-discovery/src/lib.rs +++ b/frame/authority-discovery/src/lib.rs @@ -121,6 +121,7 @@ mod tests { type ValidatorId = AuthorityId; type ValidatorIdOf = ConvertInto; type DisabledValidatorsThreshold = DisabledValidatorsThreshold; + type NextSessionRotation = pallet_session::PeriodicSessions; } impl pallet_session::historical::Trait for Test { diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index a9faf2ffe2f93..8f504c19dba7b 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -491,8 +491,8 @@ impl OnTimestampSet for Module { fn on_timestamp_set(_moment: T::Moment) { } } -impl frame_support::traits::EstimateNextSessionChange for Module { - fn estimate_next_session_change(now: T::BlockNumber) -> T::BlockNumber { +impl frame_support::traits::EstimateNextSessionRotation for Module { + fn estimate_next_session_rotation(now: T::BlockNumber) -> T::BlockNumber { Self::next_expected_epoch_change(now) } } diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 7aad3febb58f0..90ec0efdde14e 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -86,6 +86,7 @@ impl pallet_session::Trait for Test { type ValidatorIdOf = (); type Keys = MockSessionKeys; type DisabledValidatorsThreshold = DisabledValidatorsThreshold; + type NextSessionRotation = Babe; } impl pallet_timestamp::Trait for Test { diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 78b6409d543eb..1fd1bcbdfe386 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -141,6 +141,7 @@ impl pallet_session::Trait for Runtime { type Keys = UintAuthorityId; type Event = (); type DisabledValidatorsThreshold = DisabledValidatorsThreshold; + type NextSessionRotation = pallet_session::PeriodicSessions; } impl pallet_session::historical::Trait for Runtime { diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index 1097cfd6be2e4..f1c761c357974 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -103,10 +103,14 @@ use sp_std::{prelude::*, marker::PhantomData, ops::{Sub, Rem}}; use codec::Decode; use sp_runtime::{KeyTypeId, Perbill, RuntimeAppPublic, BoundToRuntimeAppPublic}; use frame_support::weights::SimpleDispatchInfo; -use sp_runtime::traits::{Convert, Zero, Member, OpaqueKeys}; +use sp_runtime::traits::{Convert, Zero, Member, OpaqueKeys, Saturating}; use sp_staking::SessionIndex; -use frame_support::{ensure, decl_module, decl_event, decl_storage, decl_error, ConsensusEngineId}; -use frame_support::{traits::{Get, FindAuthor, ValidatorRegistration}, Parameter}; +use frame_support::{ + ensure, decl_module, decl_event, decl_storage, decl_error, ConsensusEngineId, Parameter, +}; +use frame_support::traits::{ + Get, FindAuthor, ValidatorRegistration, EstimateNextSessionRotation, EstimateNextNewSession, +}; use frame_support::dispatch::{self, DispatchResult, DispatchError}; use frame_system::{self as system, ensure_signed}; @@ -143,6 +147,31 @@ impl< } } +impl< + BlockNumber: Rem + Sub + Zero + PartialOrd + Saturating + Clone, + Period: Get, + Offset: Get, +> EstimateNextSessionRotation for PeriodicSessions { + fn estimate_next_session_rotation(now: BlockNumber) -> BlockNumber { + let offset = Offset::get(); + let period = Period::get(); + if now > offset { + let block_after_last_session = (now.clone() - offset) % period.clone(); + match block_after_last_session > Zero::zero() { + true => + { + now.saturating_add( + period.saturating_sub(block_after_last_session) + ) + } + false => Zero::zero() + } + } else { + offset + } + } +} + /// A trait for managing creation of new validator set. pub trait SessionManager { /// Plan a new session, and optionally provide the new validator set. @@ -155,7 +184,7 @@ pub trait SessionManager { /// The returned validator set, if any, will not be applied until `new_index`. /// `new_index` is strictly greater than from previous call. /// - /// The first session start at index 0. + /// The first session starts at index 0. fn new_session(new_index: SessionIndex) -> Option>; /// End the session. /// @@ -324,6 +353,11 @@ pub trait Trait: frame_system::Trait { /// Indicator for when to end the session. type ShouldEndSession: ShouldEndSession; + /// Something that can predict the next session rotation. This should typically come from the + /// same logical unit that provides [`ShouldEndSession`], yet, it gives a best effort estimate. + /// It is helpful to implement [`EstimateNextNewSession`]. + type NextSessionRotation: EstimateNextSessionRotation; + /// Handler for managing new session. type SessionManager: SessionManager; @@ -744,6 +778,14 @@ impl> FindAuthor } } +impl EstimateNextNewSession for Module { + /// This session module always calls new_session and next_session at the same time, hence we + /// do a simple proxy and pass the function to next rotation. + fn estimate_next_new_session(now: T::BlockNumber) -> T::BlockNumber { + T::NextSessionRotation::estimate_next_session_rotation(now) + } +} + #[cfg(test)] mod tests { use super::*; @@ -1001,17 +1043,19 @@ mod tests { fn get() -> u64 { 3 } } - type P = PeriodicSessions; for i in 0..3 { + assert_eq!(P::estimate_next_session_rotation(i), 3); assert!(!P::should_end_session(i)); } assert!(P::should_end_session(3)); + assert_eq!(P::estimate_next_session_rotation(3), 3); - for i in (1..10).map(|i| 3 + i) { + for i in 4..13 { assert!(!P::should_end_session(i)); + assert_eq!(P::estimate_next_session_rotation(i), 13); } assert!(P::should_end_session(13)); diff --git a/frame/session/src/mock.rs b/frame/session/src/mock.rs index 9d64285b900f8..dd28d357491da 100644 --- a/frame/session/src/mock.rs +++ b/frame/session/src/mock.rs @@ -205,6 +205,7 @@ impl Trait for Test { type Keys = MockSessionKeys; type Event = (); type DisabledValidatorsThreshold = DisabledValidatorsThreshold; + type NextSessionRotation = (); } #[cfg(feature = "historical")] diff --git a/frame/staking/fuzz/Cargo.lock b/frame/staking/fuzz/Cargo.lock index 693dd7a95cc56..b24f525047b5e 100644 --- a/frame/staking/fuzz/Cargo.lock +++ b/frame/staking/fuzz/Cargo.lock @@ -321,6 +321,8 @@ dependencies = [ name = "frame-benchmarking" version = "2.0.0-alpha.3" dependencies = [ + "frame-support 2.0.0-alpha.3", + "frame-system 2.0.0-alpha.3", "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-api 2.0.0-alpha.3", "sp-io 2.0.0-alpha.3", diff --git a/frame/staking/fuzz/Cargo.toml b/frame/staking/fuzz/Cargo.toml index efcb5b1cfa2cf..0b5bc21815f91 100644 --- a/frame/staking/fuzz/Cargo.toml +++ b/frame/staking/fuzz/Cargo.toml @@ -11,7 +11,7 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.3" -codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.2.0", default-features = false, features = ["derive"] } pallet-staking = { version = "2.0.0-alpha.2", path = "..", features = ["testing-utils"] } pallet-staking-reward-curve = { version = "2.0.0-alpha.2", path = "../reward-curve" } pallet-session = { version = "2.0.0-alpha.2", path = "../../session" } diff --git a/frame/staking/fuzz/fuzz_targets/mock.rs b/frame/staking/fuzz/fuzz_targets/mock.rs index 8b68e74ba56e5..4bb3437f92368 100644 --- a/frame/staking/fuzz/fuzz_targets/mock.rs +++ b/frame/staking/fuzz/fuzz_targets/mock.rs @@ -18,7 +18,6 @@ use sp_runtime::traits::{Convert, SaturatedConversion}; use frame_support::{impl_outer_origin, impl_outer_dispatch, parameter_types}; -use frame_support::traits::{Get, EstimateNextSessionChange}; type AccountId = u64; type AccountIndex = u32; @@ -29,6 +28,7 @@ type System = frame_system::Module; type Balances = pallet_balances::Module; type Staking = pallet_staking::Module; type Indices = pallet_indices::Module; +type Session = pallet_session::Module; impl_outer_origin! { pub enum Origin for Test where system = frame_system {} @@ -52,18 +52,6 @@ impl Convert for CurrencyToVoteHandler { } } -pub struct PeriodicSessionChange

(sp_std::marker::PhantomData

); -impl

EstimateNextSessionChange for PeriodicSessionChange

-where - P: Get, -{ - fn estimate_next_session_change(now: BlockNumber) -> BlockNumber { - let period = P::get(); - let excess = now % period; - now - excess + period - } -} - #[derive(Clone, Eq, PartialEq, Debug)] pub struct Test; @@ -142,6 +130,7 @@ impl pallet_session::Trait for Test { type SessionManager = pallet_session::historical::NoteHistoricalRoot; type Keys = SessionKeys; type ShouldEndSession = pallet_session::PeriodicSessions<(), ()>; + type NextSessionRotation = pallet_session::PeriodicSessions<(), ()>; type SessionHandler = TestSessionHandler; type Event = (); type ValidatorId = AccountId; @@ -160,6 +149,7 @@ pallet_staking_reward_curve::build! { } parameter_types! { pub const RewardCurve: &'static sp_runtime::curve::PiecewiseLinear<'static> = &I_NPOS; + pub const MaxNominatorRewardedPerValidator: u32 = 64; } pub type Extrinsic = sp_runtime::testing::TestXt; @@ -183,9 +173,10 @@ impl pallet_staking::Trait for Test { type BondingDuration = (); type SessionInterface = Self; type RewardCurve = RewardCurve; - type NextSessionChange = PeriodicSessionChange<()>; + type NextNewSession = Session; type ElectionLookahead = (); type Call = Call; type SubmitTransaction = SubmitTransaction; type KeyType = sp_runtime::testing::UintAuthorityId; + type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 31e1b24adad73..8d18e5d074b98 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -268,7 +268,7 @@ use frame_support::{ dispatch::{IsSubType, DispatchResult}, traits::{ Currency, LockIdentifier, LockableCurrency, WithdrawReasons, OnUnbalanced, Imbalance, Get, - Time, EstimateNextSessionChange, + Time, EstimateNextNewSession, } }; use pallet_session::historical; @@ -770,7 +770,7 @@ pub trait Trait: frame_system::Trait { type RewardCurve: Get<&'static PiecewiseLinear<'static>>; /// Something that can estimate the next session change, accurately or as a best effort guess. - type NextSessionChange: EstimateNextSessionChange; + type NextNewSession: EstimateNextNewSession; /// How many blocks ahead of the era, within the last do we try to run the phragmen offchain? /// Setting this to zero will disable the offchain compute and only on-chain seq-phragmen will @@ -1125,7 +1125,7 @@ decl_module! { Self::is_current_session_final() { let next_session_change = - T::NextSessionChange::estimate_next_session_change(now); + T::NextNewSession::estimate_next_new_session(now); if let Some(remaining) = next_session_change.checked_sub(&now) { if remaining <= T::ElectionLookahead::get() && !remaining.is_zero() { // create snapshot. @@ -3132,10 +3132,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { priority: score[0].saturated_into(), requires: vec![], provides: vec![(Self::current_era(), validator_key).encode()], - // longevity: TryInto::::try_into(T::ElectionLookahead::get()).unwrap_or(150_u64), - longevity: TryInto::::try_into( - T::NextSessionChange::estimate_next_session_change(>::block_number()) - >::block_number() - ).unwrap_or(150_u64), + longevity: TryInto::::try_into(T::ElectionLookahead::get()).unwrap_or(150_u64), propagate: true, }) } else { diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 0ad5a2a5ef720..e524fa116b62c 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -29,7 +29,7 @@ use sp_core::H256; use frame_support::{ assert_ok, impl_outer_origin, parameter_types, StorageLinkedMap, StorageValue, StorageMap, impl_outer_dispatch, StorageDoubleMap, impl_outer_event, - traits::{Currency, Get, FindAuthor, EstimateNextSessionChange}, + traits::{Currency, Get, FindAuthor}, weights::Weight, }; use frame_system::offchain::{CreateTransaction, Signer, TransactionSubmitter}; @@ -178,18 +178,6 @@ impl_outer_event! { } } -pub struct PeriodicSessionChange

(sp_std::marker::PhantomData

); -impl

EstimateNextSessionChange for PeriodicSessionChange

-where - P: Get, -{ - fn estimate_next_session_change(now: BlockNumber) -> BlockNumber { - let period = P::get(); - let excess = now % period; - now - excess + period - } -} - /// Author of block is always 11 pub struct Author11; impl FindAuthor for Author11 { @@ -267,6 +255,7 @@ impl pallet_session::Trait for Test { type ValidatorId = AccountId; type ValidatorIdOf = crate::StashOf; type DisabledValidatorsThreshold = DisabledValidatorsThreshold; + type NextSessionRotation = pallet_session::PeriodicSessions; } impl pallet_session::historical::Trait for Test { @@ -317,7 +306,7 @@ impl Trait for Test { type BondingDuration = BondingDuration; type SessionInterface = Self; type RewardCurve = RewardCurve; - type NextSessionChange = PeriodicSessionChange; + type NextNewSession = Session; type ElectionLookahead = ElectionLookahead; type Call = Call; type SubmitTransaction = SubmitTransaction; diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index bae1060987b38..79a462335b8c0 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -152,7 +152,6 @@ where pub fn get_weak_solution( do_reduce: bool, ) -> (Vec, CompactAssignments, PhragmenScore) { - use sp_std::collections::btree_map::BTreeMap; let mut backing_stake_of: BTreeMap> = BTreeMap::new(); // self stake @@ -323,16 +322,19 @@ pub fn get_seq_phragmen_solution( } /// Remove all validator, nominators, votes and exposures. -pub fn clean() { +pub fn clean(era: EraIndex) + where + ::AccountId: codec::EncodeLike, + u32: codec::EncodeLike, +{ >::enumerate().for_each(|(k, _)| { let ctrl = >::bonded(&k).unwrap(); >::remove(&k); >::remove(&k); - >::remove(&k); >::remove(&ctrl); + >::remove(k, era); }); >::enumerate().for_each(|(k, _)| >::remove(k)); - >::remove_all(); >::remove_all(); >::remove_all(); >::kill(); diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index a8364769d38b9..5626b1371980c 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -138,17 +138,29 @@ impl< } } -/// Something that can estimate at which block number the next era change will happen in a best -/// effort manner. The result may or may night be accurate, based on the chain configuration and -/// semantics. -pub trait EstimateNextSessionChange { - /// Return the block number at which the next era change is estimated to happen. - fn estimate_next_session_change(now: BlockNumber) -> BlockNumber; +/// 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 block number at which the next session rotation is estimated to happen. + fn estimate_next_session_rotation(now: BlockNumber) -> BlockNumber; } -impl EstimateNextSessionChange for () { - fn estimate_next_session_change(_: BlockNumber) -> BlockNumber { - // practically never +impl EstimateNextSessionRotation for () { + fn estimate_next_session_rotation(_: BlockNumber) -> BlockNumber { + Bounded::max_value() + } +} + +/// 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 block number at which the next new session is estimated to happen. + fn estimate_next_new_session(now: BlockNumber) -> BlockNumber; +} + +impl EstimateNextNewSession for () { + fn estimate_next_new_session(_: BlockNumber) -> BlockNumber { Bounded::max_value() } } From bee2f5d84db65f05d3fdb42a4edf113dc10263fc Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Thu, 5 Mar 2020 16:59:29 +0100 Subject: [PATCH 075/106] Update primitives/phragmen/src/lib.rs Co-Authored-By: thiolliere --- primitives/phragmen/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 8e27d40b14646..dcca878d82e28 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -613,7 +613,7 @@ pub fn evaluate_support( } /// Compares two sets of phragmen scores based on desirability and returns true if `that` is -/// better `this`. +/// better than `this`. /// /// Evaluation is done in a lexicographic manner. /// From 3def57b55f46f78418a8f79327da3be4b40719fe Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Thu, 5 Mar 2020 16:59:53 +0100 Subject: [PATCH 076/106] Update primitives/phragmen/src/lib.rs Co-Authored-By: thiolliere --- primitives/phragmen/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index dcca878d82e28..4ec63a4b2e39c 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -639,11 +639,11 @@ pub fn is_score_better(this: PhragmenScore, that: PhragmenScore) -> bool { /// /// No value is returned from the function and the `supports` parameter is updated. /// -/// `assignments`: exactly the same is the output of phragmen. -/// `supports`: mutable reference to s `SupportMap`. This parameter is updated. -/// `tolerance`: maximum difference that can occur before an early quite happens. -/// `iterations`: maximum number of iterations that will be processed. -/// `stake_of`: something that can return the stake stake of a particular candidate or voter. +/// - `assignments`: exactly the same is the output of phragmen. +/// - `supports`: mutable reference to s `SupportMap`. This parameter is updated. +/// - `tolerance`: maximum difference that can occur before an early quite happens. +/// - `iterations`: maximum number of iterations that will be processed. +/// - `stake_of`: something that can return the stake stake of a particular candidate or voter. pub fn equalize( mut assignments: Vec>, supports: &mut SupportMap, From 9590986002a9b809aac381742ec11e28e41e0fbc Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Thu, 5 Mar 2020 17:00:30 +0100 Subject: [PATCH 077/106] Update primitives/phragmen/compact/src/lib.rs Co-Authored-By: thiolliere --- primitives/phragmen/compact/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/primitives/phragmen/compact/src/lib.rs b/primitives/phragmen/compact/src/lib.rs index 1d6ce46bcc4c0..9c6b126c1b4c9 100644 --- a/primitives/phragmen/compact/src/lib.rs +++ b/primitives/phragmen/compact/src/lib.rs @@ -35,8 +35,8 @@ const PREFIX: &'static str = "votes"; /// /// ```ignore /// // generate a struct with nominator and edge weight u128, with maximum supported -/// // edge per voter of 32. -/// generate_compact_solution_type(pub TestCompact, 32) +/// // edge per voter of 16. +/// generate_compact_solution_type(pub TestCompact, 16) /// ``` /// /// This generates: From 6566d816d67cae5d9b1358dbbe5963e8da289dcf Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 5 Mar 2020 19:50:26 +0100 Subject: [PATCH 078/106] Some doc updates to slashing --- frame/staking/src/lib.rs | 5 ++--- frame/staking/src/slashing.rs | 8 +++---- frame/staking/src/tests.rs | 41 ++++++++++++++++++++++++++--------- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 8d18e5d074b98..deffbb7ec5e55 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -2875,7 +2875,7 @@ impl OnOffenceHandler OnOffenceHandler>::contains_key(11)); + on_offence_now( &[OffenceDetails { offender: ( @@ -1990,9 +1992,12 @@ fn offence_deselects_validator_when_slash_is_zero() { }], &[Perbill::from_percent(0)], ); + assert_eq!(Staking::force_era(), Forcing::ForceNew); assert!(!>::contains_key(11)); + start_era(1); + assert!(!Session::validators().contains(&11)); assert!(!>::contains_key(11)); }); @@ -2033,6 +2038,7 @@ fn slash_in_old_span_does_not_deselect() { assert!(>::contains_key(11)); assert!(Session::validators().contains(&11)); + on_offence_now( &[OffenceDetails { offender: ( @@ -2043,6 +2049,7 @@ fn slash_in_old_span_does_not_deselect() { }], &[Perbill::from_percent(0)], ); + assert_eq!(Staking::force_era(), Forcing::ForceNew); assert!(!>::contains_key(11)); @@ -2070,7 +2077,7 @@ fn slash_in_old_span_does_not_deselect() { 1, ); - // not for zero-slash. + // not forcing for zero-slash and previous span. assert_eq!(Staking::force_era(), Forcing::NotForcing); assert!(>::contains_key(11)); assert!(Session::validators().contains(&11)); @@ -2091,7 +2098,7 @@ fn slash_in_old_span_does_not_deselect() { // or non-zero. assert_eq!(Staking::force_era(), Forcing::NotForcing); assert!(>::contains_key(11)); - assert!(Session::validators().contains(&11)); + assert!(Session::validators().contains(&11)); assert_ledger_consistent(11); }); } @@ -2130,7 +2137,7 @@ fn reporters_receive_their_slice() { #[test] fn subsequent_reports_in_same_span_pay_out_less() { // This test verifies that the reporters of the offence receive their slice from the slashed - // amount. + // amount, but less and less if they submit multiple reports in one span. ExtBuilder::default().build().execute_with(|| { // The reporters' reward is calculated from the total exposure. let initial_balance = 1125; @@ -2237,12 +2244,16 @@ fn dont_slash_if_fraction_is_zero() { // The validator hasn't been slashed. The new era is not forced. assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Staking::force_era(), Forcing::ForceNew); + assert_ledger_consistent(11); }); } #[test] fn only_slash_for_max_in_era() { + // multiple slashes within one era are only applied if it is more than any previous slash in the + // same era. ExtBuilder::default().build().execute_with(|| { assert_eq!(Balances::free_balance(11), 1000); @@ -2291,6 +2302,7 @@ fn only_slash_for_max_in_era() { #[test] fn garbage_collection_after_slashing() { + // ensures that `SlashingSpans` and `SpanSlash` of an account is removed after reaping. ExtBuilder::default().existential_deposit(2).build().execute_with(|| { assert_eq!(Balances::free_balance(11), 256_000); @@ -2333,27 +2345,28 @@ fn garbage_collection_after_slashing() { #[test] fn garbage_collection_on_window_pruning() { + // ensures that `ValidatorSlashInEra` and `NominatorSlashInEra` are cleared after + // `BondingDuration`. ExtBuilder::default().build().execute_with(|| { start_era(1); assert_eq!(Balances::free_balance(11), 1000); + let now = Staking::active_era().unwrap().index; - let exposure = Staking::eras_stakers(Staking::active_era().unwrap().index, 11); + let exposure = Staking::eras_stakers(now, 11); assert_eq!(Balances::free_balance(101), 2000); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; on_offence_now( &[ OffenceDetails { - offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)), + offender: (11, Staking::eras_stakers(now, 11)), reporters: vec![], }, ], &[Perbill::from_percent(10)], ); - let now = Staking::active_era().unwrap().index; - assert_eq!(Balances::free_balance(11), 900); assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); @@ -2385,10 +2398,8 @@ fn slashing_nominators_by_span_max() { assert_eq!(Balances::free_balance(101), 2000); assert_eq!(Staking::slashable_balance_of(&21), 1000); - let exposure_11 = Staking::eras_stakers(Staking::active_era().unwrap().index, 11); let exposure_21 = Staking::eras_stakers(Staking::active_era().unwrap().index, 21); - assert_eq!(Balances::free_balance(101), 2000); let nominated_value_11 = exposure_11.others.iter().find(|o| o.who == 101).unwrap().value; let nominated_value_21 = exposure_21.others.iter().find(|o| o.who == 101).unwrap().value; @@ -3579,6 +3590,16 @@ mod offchain_phragmen { }) } + #[test] + fn slashing_while_election_window() { + unimplemented!(); + } + + #[test] + fn era_forcing() { + unimplemented!(); + } + #[test] fn invalid_phragmen_result_wrong_score() { // A valid voter who's total distributed stake is more than what they bond From 1667f6e95690d28e5eb38fa4ede445d340c676bd Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 5 Mar 2020 21:42:19 +0100 Subject: [PATCH 079/106] Fix derive --- primitives/phragmen/fuzzer/Cargo.lock | 160 +++++++++++++------------- primitives/phragmen/fuzzer/Cargo.toml | 2 +- primitives/phragmen/src/node.rs | 10 +- 3 files changed, 82 insertions(+), 90 deletions(-) diff --git a/primitives/phragmen/fuzzer/Cargo.lock b/primitives/phragmen/fuzzer/Cargo.lock index 09303dde83c57..14286d239d29d 100644 --- a/primitives/phragmen/fuzzer/Cargo.lock +++ b/primitives/phragmen/fuzzer/Cargo.lock @@ -385,7 +385,7 @@ name = "impl-codec" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -569,19 +569,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "parity-scale-codec" -version = "1.1.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "byte-slice-cast 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec-derive 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parity-scale-codec-derive" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1006,30 +1006,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "sp-application-crypto" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-std 2.0.0", + "sp-core 2.0.0-alpha.3", + "sp-io 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-arithmetic" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ "integer-sqrt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0", - "sp-std 2.0.0", + "sp-debug-derive 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-core" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1043,7 +1043,7 @@ dependencies = [ "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1053,11 +1053,11 @@ dependencies = [ "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0", - "sp-externalities 0.8.0", - "sp-runtime-interface 2.0.0", - "sp-std 2.0.0", - "sp-storage 2.0.0", + "sp-debug-derive 2.0.0-alpha.3", + "sp-externalities 0.8.0-alpha.3", + "sp-runtime-interface 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", + "sp-storage 2.0.0-alpha.3", "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1068,7 +1068,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1077,44 +1077,44 @@ dependencies = [ [[package]] name = "sp-externalities" -version = "0.8.0" +version = "0.8.0-alpha.3" dependencies = [ "environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 2.0.0", - "sp-storage 2.0.0", + "sp-std 2.0.0-alpha.3", + "sp-storage 2.0.0-alpha.3", ] [[package]] name = "sp-inherents" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-std 2.0.0", + "sp-core 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-io" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-externalities 0.8.0", - "sp-runtime-interface 2.0.0", - "sp-state-machine 0.8.0", - "sp-std 2.0.0", - "sp-trie 2.0.0", - "sp-wasm-interface 2.0.0", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0-alpha.3", + "sp-externalities 0.8.0-alpha.3", + "sp-runtime-interface 2.0.0-alpha.3", + "sp-state-machine 0.8.0-alpha.3", + "sp-std 2.0.0-alpha.3", + "sp-trie 2.0.0-alpha.3", + "sp-wasm-interface 2.0.0-alpha.3", ] [[package]] name = "sp-panic-handler" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ "backtrace 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1122,19 +1122,19 @@ dependencies = [ [[package]] name = "sp-phragmen" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-phragmen-compact 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "sp-core 2.0.0-alpha.3", + "sp-phragmen-compact 2.0.0-dev", + "sp-runtime 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-phragmen-compact" -version = "2.0.0" +version = "2.0.0-dev" dependencies = [ "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1148,44 +1148,44 @@ version = "2.0.0" dependencies = [ "honggfuzz 0.5.45", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-phragmen 2.0.0", + "sp-phragmen 2.0.0-alpha.3", ] [[package]] name = "sp-runtime" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0", - "sp-arithmetic 2.0.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-std 2.0.0", + "sp-application-crypto 2.0.0-alpha.3", + "sp-arithmetic 2.0.0-alpha.3", + "sp-core 2.0.0-alpha.3", + "sp-inherents 2.0.0-alpha.3", + "sp-io 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-runtime-interface" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.8.0", - "sp-runtime-interface-proc-macro 2.0.0", - "sp-std 2.0.0", - "sp-wasm-interface 2.0.0", + "sp-externalities 0.8.0-alpha.3", + "sp-runtime-interface-proc-macro 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", + "sp-wasm-interface 2.0.0-alpha.3", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-runtime-interface-proc-macro" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ "Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1196,56 +1196,56 @@ dependencies = [ [[package]] name = "sp-state-machine" -version = "0.8.0" +version = "0.8.0-alpha.3" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-externalities 0.8.0", - "sp-panic-handler 2.0.0", - "sp-trie 2.0.0", + "sp-core 2.0.0-alpha.3", + "sp-externalities 0.8.0-alpha.3", + "sp-panic-handler 2.0.0-alpha.3", + "sp-trie 2.0.0-alpha.3", "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-std" -version = "2.0.0" +version = "2.0.0-alpha.3" [[package]] name = "sp-storage" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0", - "sp-std 2.0.0", + "sp-debug-derive 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", ] [[package]] name = "sp-trie" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "memory-db 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-std 2.0.0", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core 2.0.0-alpha.3", + "sp-std 2.0.0-alpha.3", "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-wasm-interface" -version = "2.0.0" +version = "2.0.0-alpha.3" dependencies = [ "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 2.0.0", + "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-std 2.0.0-alpha.3", "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1526,8 +1526,8 @@ dependencies = [ "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" "checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507" -"checksum parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34e513ff3e406f3ede6796dcdc83d0b32ffb86668cea1ccf7363118abeb00476" +"checksum parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f509c5e67ca0605ee17dcd3f91ef41cadd685c75a298fb6261b781a5acb3f910" +"checksum parity-scale-codec-derive 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a0ec292e92e8ec7c58e576adacc1e3f399c597c8f263c42f18420abe58e7245" "checksum parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ef1476e40bf8f5c6776e9600983435821ca86eb9819d74a6207cca69d091406a" "checksum parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" "checksum parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" diff --git a/primitives/phragmen/fuzzer/Cargo.toml b/primitives/phragmen/fuzzer/Cargo.toml index c9f79e3f17e51..645b1c151bcfd 100644 --- a/primitives/phragmen/fuzzer/Cargo.toml +++ b/primitives/phragmen/fuzzer/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -sp-phragmen = { version = "2.0.0-dev", path = ".." } +sp-phragmen = { version = "2.0.0-alpha.3", path = ".." } honggfuzz = "0.5" rand = "0.7.3" diff --git a/primitives/phragmen/src/node.rs b/primitives/phragmen/src/node.rs index a66f9dbb4817b..92ef325a34872 100644 --- a/primitives/phragmen/src/node.rs +++ b/primitives/phragmen/src/node.rs @@ -33,7 +33,7 @@ pub(crate) type NodeRef = RefCellOf>; /// Identifier of a node. This is particularly handy to have a proper `PartialEq` implementation. /// Otherwise, self votes wouldn't have been indistinguishable. -#[derive(PartialOrd, Ord, Clone)] +#[derive(PartialOrd, Ord, Clone, PartialEq, Eq)] pub(crate) struct NodeId { /// An account-like identifier representing the node. pub who: A, @@ -48,14 +48,6 @@ impl NodeId { } } -impl PartialEq for NodeId { - fn eq(&self, other: &NodeId) -> bool { - self.who == other.who && self.role == other.role - } -} - -impl Eq for NodeId {} - #[cfg(feature = "std")] impl sp_std::fmt::Debug for NodeId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> sp_std::fmt::Result { From e25c10356389a365c3b30b58f4979e090c8804ed Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 5 Mar 2020 21:54:30 +0100 Subject: [PATCH 080/106] Remove imports --- bin/node/runtime/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index c23e2729a6666..afcecbb2060e5 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -31,7 +31,7 @@ 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, Percent, ApplyExtrinsicResult, RuntimeString, + Permill, Perbill, Percent, ApplyExtrinsicResult, impl_opaque_keys, generic, create_runtime_str, }; use sp_runtime::curve::PiecewiseLinear; @@ -68,7 +68,6 @@ use impls::{CurrencyToVoteHandler, Author, LinearWeightToFee, TargetedFeeAdjustm /// Constant values used within the runtime. pub mod constants; use constants::{time::*, currency::*}; -use frame_system::Trait; // Make the WASM binary available. #[cfg(feature = "std")] From 81ddd48af603b6a7f541da562667e40f79d8f474 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 6 Mar 2020 09:06:20 +0100 Subject: [PATCH 081/106] Remove unimplemented tests --- frame/staking/src/tests.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 1d65ab3c37d1a..e971601393f6c 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -3586,16 +3586,6 @@ mod offchain_phragmen { }) } - #[test] - fn slashing_while_election_window() { - unimplemented!(); - } - - #[test] - fn era_forcing() { - unimplemented!(); - } - #[test] fn invalid_phragmen_result_wrong_score() { // A valid voter who's total distributed stake is more than what they bond From eda82aef96654cc33b6fee681a3c7b118cb97a68 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 9 Mar 2020 08:27:19 +0100 Subject: [PATCH 082/106] nits --- frame/staking/src/lib.rs | 6 +++--- primitives/phragmen/src/lib.rs | 7 ++++--- primitives/phragmen/src/reduce.rs | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 522a4e2bb9318..6db4f8ebcf364 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -261,7 +261,7 @@ pub mod slashing; pub mod offchain_election; pub mod inflation; -use sp_std::{prelude::*, result, collections::btree_map::BTreeMap, convert::{TryInto, From}}; +use sp_std::{prelude::*, result, collections::btree_map::BTreeMap, convert::{TryInto, From}, mem::size_of}; use codec::{HasCompact, Encode, Decode}; use frame_support::{ decl_module, decl_event, decl_storage, ensure, decl_error, debug, Parameter, @@ -312,8 +312,8 @@ pub type NominatorIndex = u32; pub type ValidatorIndex = u16; // Ensure the size of both ValidatorIndex and NominatorIndex -static_assertions::const_assert!(sp_std::mem::size_of::() <= sp_std::mem::size_of::()); -static_assertions::const_assert!(sp_std::mem::size_of::() <= sp_std::mem::size_of::()); +static_assertions::const_assert!(size_of::() <= size_of::()); +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; diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 4ec63a4b2e39c..eb360d718e969 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -490,16 +490,17 @@ pub fn elect( } } - if assignment.distribution.len() > 0 { + let len = assignment.distribution.len(); + if len > 0 { // To ensure an assertion indicating: no stake from the voter going to waste, // we add a minimal post-processing to equally assign all of the leftover stake ratios. - let vote_count: R::Inner = assignment.distribution.len().saturated_into(); + let vote_count: R::Inner = len.saturated_into(); let accuracy = R::ACCURACY; - let len = assignment.distribution.len(); let mut sum: R::Inner = Zero::zero(); assignment.distribution.iter().for_each(|a| sum = sum.saturating_add(a.1.deconstruct())); let diff = accuracy.saturating_sub(sum); + dbg!(diff); let diff_per_vote = (diff / vote_count).min(accuracy); if !diff_per_vote.is_zero() { diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index fea2552f6171b..a3ad1c8be15ce 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -972,7 +972,7 @@ mod tests { #[test] #[should_panic] - fn should_deal_with_duplicates_voter() { + fn reduce_panics_on_duplicate_voter() { let mut assignments = vec![ StakedAssignment { who: 1, From 56d1f5e345035a8658adc1f74feaefc4fa419640 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 9 Mar 2020 08:44:42 +0100 Subject: [PATCH 083/106] Remove dbg --- primitives/phragmen/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index eb360d718e969..3f7f0523bda16 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -500,7 +500,6 @@ pub fn elect( assignment.distribution.iter().for_each(|a| sum = sum.saturating_add(a.1.deconstruct())); let diff = accuracy.saturating_sub(sum); - dbg!(diff); let diff_per_vote = (diff / vote_count).min(accuracy); if !diff_per_vote.is_zero() { From 0c9c20fb91270347a671742c1cf74015d75fc94e Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 9 Mar 2020 14:12:16 +0100 Subject: [PATCH 084/106] Better fuzzing params --- frame/staking/fuzz/Cargo.lock | 1754 ++++++++--------- .../fuzz/fuzz_targets/submit_solution.rs | 6 +- frame/staking/src/testing_utils.rs | 2 - 3 files changed, 856 insertions(+), 906 deletions(-) diff --git a/frame/staking/fuzz/Cargo.lock b/frame/staking/fuzz/Cargo.lock index b24f525047b5e..e8469483dbe5c 100644 --- a/frame/staking/fuzz/Cargo.lock +++ b/frame/staking/fuzz/Cargo.lock @@ -4,1620 +4,1740 @@ name = "Inflector" version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "regex", ] [[package]] name = "ahash" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3" dependencies = [ - "const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "const-random", ] [[package]] name = "aho-corasick" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" dependencies = [ - "memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] -[[package]] -name = "anyhow" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "arbitrary" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16971f2f0ce65c5cf2a1546cc6a0af102ecb11e265ddaa9433fb3e5bfdf676a4" [[package]] name = "arrayref" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" dependencies = [ - "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop", ] [[package]] name = "arrayvec" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" [[package]] name = "autocfg" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" [[package]] name = "autocfg" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.44" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8" dependencies = [ - "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys", + "cfg-if", + "libc", + "rustc-demangle", ] [[package]] name = "backtrace-sys" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17b52e737c40a7d75abca20b29a19a0eb7ba9fc72c5a72dd282a0a3c2c0dc35" dependencies = [ - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", ] [[package]] name = "base58" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitmask" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead" [[package]] name = "bitvec" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" [[package]] name = "blake2-rfc" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" dependencies = [ - "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12", + "constant_time_eq", ] [[package]] name = "block-buffer" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding", + "byte-tools", + "byteorder", + "generic-array", ] [[package]] name = "block-padding" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools", ] [[package]] name = "bumpalo" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" [[package]] name = "byte-slice-cast" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" [[package]] name = "byte-tools" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "c2-chacha" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" dependencies = [ - "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86", ] [[package]] name = "cc" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "clear_on_drop" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" dependencies = [ - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", ] [[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", ] [[package]] name = "const-random" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" dependencies = [ - "const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "const-random-macro", + "proc-macro-hack", ] [[package]] name = "const-random-macro" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "proc-macro-hack", ] [[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-mac" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", + "subtle 1.0.0", ] [[package]] name = "curve25519-dalek" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "clear_on_drop", + "digest", + "rand_core 0.3.1", + "subtle 2.2.2", ] [[package]] name = "curve25519-dalek" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "digest", + "rand_core 0.5.1", + "subtle 2.2.2", + "zeroize 1.1.0", ] [[package]] name = "derive_more" version = "0.99.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a806e96c59a76a5ba6e18735b6cf833344671e61e7863f2edb5c518ea2cac95c" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "digest" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", ] [[package]] name = "ed25519-dalek" version = "1.0.0-pre.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" dependencies = [ - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "clear_on_drop", + "curve25519-dalek 2.0.0", + "rand 0.7.3", + "sha2", ] [[package]] name = "environmental" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516aa8d7a71cb00a1c4146f0798549b93d083d4f189b3ced8f3de6b8f11ee6c4" [[package]] name = "failure" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" dependencies = [ - "backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "failure_derive", ] [[package]] name = "failure_derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fixed-hash" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3367952ceb191f4ab95dd5685dc163ac539e36202f9fcfd0cb22f9f9c542fefc" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "libc", + "rand 0.7.3", + "rustc-hex", + "static_assertions", ] [[package]] name = "frame-benchmarking" version = "2.0.0-alpha.3" dependencies = [ - "frame-support 2.0.0-alpha.3", - "frame-system 2.0.0-alpha.3", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0-alpha.3", - "sp-io 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-runtime-interface 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "frame-support", + "frame-system", + "parity-scale-codec", + "sp-api", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", ] [[package]] name = "frame-metadata" version = "11.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "parity-scale-codec", + "serde", + "sp-core", + "sp-std", ] [[package]] name = "frame-support" version = "2.0.0-alpha.3" dependencies = [ - "bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-metadata 11.0.0-alpha.3", - "frame-support-procedural 2.0.0-alpha.3", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-arithmetic 2.0.0-alpha.3", - "sp-core 2.0.0-alpha.3", - "sp-inherents 2.0.0-alpha.3", - "sp-io 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-state-machine 0.8.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "tracing 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "bitmask", + "frame-metadata", + "frame-support-procedural", + "impl-trait-for-tuples", + "log", + "once_cell", + "parity-scale-codec", + "paste", + "serde", + "sp-arithmetic", + "sp-core", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "tracing", ] [[package]] name = "frame-support-procedural" version = "2.0.0-alpha.3" dependencies = [ - "frame-support-procedural-tools 2.0.0-alpha.3", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "frame-support-procedural-tools", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "frame-support-procedural-tools" version = "2.0.0-alpha.3" dependencies = [ - "frame-support-procedural-tools-derive 2.0.0-alpha.3", - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "frame-support-procedural-tools-derive", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "frame-support-procedural-tools-derive" version = "2.0.0-alpha.3" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "frame-system" version = "2.0.0-alpha.3" dependencies = [ - "frame-support 2.0.0-alpha.3", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-io 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "sp-version 2.0.0-alpha.3", + "frame-support", + "impl-trait-for-tuples", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", ] [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "futures" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" dependencies = [ - "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] name = "futures-channel" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" dependencies = [ - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-sink", ] [[package]] name = "futures-core" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" [[package]] name = "futures-executor" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" dependencies = [ - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-task", + "futures-util", ] [[package]] name = "futures-io" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" [[package]] name = "futures-macro" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" dependencies = [ - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "futures-sink" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" [[package]] name = "futures-task" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" [[package]] name = "futures-util" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" dependencies = [ - "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", ] [[package]] name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", ] [[package]] name = "getrandom" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "wasi", ] [[package]] name = "hash-db" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" [[package]] name = "hash256-std-hasher" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" dependencies = [ - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy", ] [[package]] name = "hashbrown" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" dependencies = [ - "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "ahash", + "autocfg 0.1.7", ] [[package]] name = "heck" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" dependencies = [ - "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation", ] [[package]] name = "hex" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" [[package]] name = "hmac" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" dependencies = [ - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac", + "digest", ] [[package]] name = "hmac-drbg" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" dependencies = [ - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "digest", + "generic-array", + "hmac", ] [[package]] name = "impl-codec" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" dependencies = [ - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec", ] [[package]] name = "impl-serde" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8" dependencies = [ - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "impl-serde" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bbe9ea9b182f0fb1cabbd61f4ff9b7b7b9197955e95a7e4c27de5055eb29ff8" dependencies = [ - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "impl-trait-for-tuples" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "integer-sqrt" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65877bf7d44897a473350b1046277941cee20b263397e90869c50b6e766088b" [[package]] name = "js-sys" -version = "0.3.35" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cb931d43e71f560c81badb0191596562bafad2be06a3f9025b845c847c60df5" dependencies = [ - "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen", ] [[package]] name = "keccak" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" [[package]] name = "libfuzzer-sys" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb789afcc589a08928d1e466087445ab740a0f70a2ee23d9349a0e3723d65e1b" dependencies = [ - "arbitrary 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "arbitrary", + "cc", ] [[package]] name = "libsecp256k1" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" dependencies = [ - "arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref", + "crunchy", + "digest", + "hmac-drbg", + "rand 0.7.3", + "sha2", + "subtle 2.2.2", + "typenum", ] [[package]] name = "lock_api" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" dependencies = [ - "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard", ] [[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", ] [[package]] name = "maybe-uninit" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] name = "memory-db" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "198831fe8722331a395bc199a5d08efbc197497ef354cb4c77b969c02ffc0fc4" dependencies = [ - "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ahash", + "hash-db", + "hashbrown", + "parity-util-mem", ] [[package]] name = "memory_units" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" [[package]] name = "merlin" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b0942b357c1b4d0dc43ba724674ec89c3218e6ca2b3e8269e7cb53bcecd2f6e" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "keccak", + "rand_core 0.4.2", + "zeroize 1.1.0", ] [[package]] name = "nodrop" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "num-bigint" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", + "num-integer", + "num-traits", ] [[package]] name = "num-integer" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", + "num-traits", ] [[package]] name = "num-rational" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", + "num-bigint", + "num-integer", + "num-traits", ] [[package]] name = "num-traits" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", ] [[package]] name = "once_cell" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" dependencies = [ - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0", ] [[package]] name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "pallet-authorship" version = "2.0.0-alpha.3" dependencies = [ - "frame-support 2.0.0-alpha.3", - "frame-system 2.0.0-alpha.3", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-authorship 2.0.0-alpha.3", - "sp-core 2.0.0-alpha.3", - "sp-inherents 2.0.0-alpha.3", - "sp-io 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-authorship", + "sp-core", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-balances" version = "2.0.0-alpha.3" dependencies = [ - "frame-benchmarking 2.0.0-alpha.3", - "frame-support 2.0.0-alpha.3", - "frame-system 2.0.0-alpha.3", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-io 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "serde", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-indices" version = "2.0.0-alpha.3" dependencies = [ - "frame-support 2.0.0-alpha.3", - "frame-system 2.0.0-alpha.3", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-io 2.0.0-alpha.3", - "sp-keyring 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "frame-support", + "frame-system", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-session" version = "2.0.0-alpha.3" dependencies = [ - "frame-support 2.0.0-alpha.3", - "frame-system 2.0.0-alpha.3", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-timestamp 2.0.0-alpha.3", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-io 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-staking 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "sp-trie 2.0.0-alpha.3", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "pallet-timestamp", + "parity-scale-codec", + "serde", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", + "sp-trie", ] [[package]] name = "pallet-staking" version = "2.0.0-alpha.3" dependencies = [ - "frame-support 2.0.0-alpha.3", - "frame-system 2.0.0-alpha.3", - "pallet-authorship 2.0.0-alpha.3", - "pallet-indices 2.0.0-alpha.3", - "pallet-session 2.0.0-alpha.3", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0-alpha.3", - "sp-core 2.0.0-alpha.3", - "sp-io 2.0.0-alpha.3", - "sp-phragmen 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-staking 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "frame-support", + "frame-system", + "pallet-authorship", + "pallet-indices", + "pallet-session", + "parity-scale-codec", + "rand 0.7.3", + "serde", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-phragmen", + "sp-runtime", + "sp-staking", + "sp-std", + "static_assertions", ] [[package]] name = "pallet-staking-fuzz" version = "0.0.0" dependencies = [ - "frame-support 2.0.0-alpha.3", - "frame-system 2.0.0-alpha.3", - "libfuzzer-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-balances 2.0.0-alpha.3", - "pallet-indices 2.0.0-alpha.3", - "pallet-session 2.0.0-alpha.3", - "pallet-staking 2.0.0-alpha.3", - "pallet-staking-reward-curve 2.0.0-alpha.3", - "pallet-timestamp 2.0.0-alpha.3", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-io 2.0.0-alpha.3", - "sp-phragmen 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "frame-support", + "frame-system", + "libfuzzer-sys", + "pallet-balances", + "pallet-indices", + "pallet-session", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-timestamp", + "parity-scale-codec", + "rand 0.7.3", + "sp-core", + "sp-io", + "sp-phragmen", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-staking-reward-curve" version = "2.0.0-alpha.3" dependencies = [ - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "pallet-timestamp" version = "2.0.0-alpha.3" dependencies = [ - "frame-benchmarking 2.0.0-alpha.3", - "frame-support 2.0.0-alpha.3", - "frame-system 2.0.0-alpha.3", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-inherents 2.0.0-alpha.3", - "sp-io 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "sp-timestamp 2.0.0-alpha.3", + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "serde", + "sp-inherents", + "sp-runtime", + "sp-std", + "sp-timestamp", ] [[package]] name = "parity-scale-codec" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f509c5e67ca0605ee17dcd3f91ef41cadd685c75a298fb6261b781a5acb3f910" dependencies = [ - "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-slice-cast 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec-derive 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.5.1", + "bitvec", + "byte-slice-cast", + "parity-scale-codec-derive", + "serde", ] [[package]] name = "parity-scale-codec-derive" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0ec292e92e8ec7c58e576adacc1e3f399c597c8f263c42f18420abe58e7245" dependencies = [ - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "parity-util-mem" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1476e40bf8f5c6776e9600983435821ca86eb9819d74a6207cca69d091406a" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "impl-trait-for-tuples", + "parity-util-mem-derive", + "parking_lot 0.10.0", + "primitive-types", + "winapi", ] [[package]] name = "parity-util-mem-derive" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "syn", + "synstructure", ] [[package]] name = "parity-wasm" version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" [[package]] name = "parking_lot" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ - "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api", + "parking_lot_core 0.6.2", + "rustc_version", ] [[package]] name = "parking_lot" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" dependencies = [ - "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api", + "parking_lot_core 0.7.0", ] [[package]] name = "parking_lot_core" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "rustc_version", + "smallvec 0.6.13", + "winapi", ] [[package]] name = "parking_lot_core" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec 1.2.0", + "winapi", ] [[package]] name = "paste" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e1afe738d71b1ebab5f1207c055054015427dbfc7bbe9ee1266894156ec046" dependencies = [ - "paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "paste-impl", + "proc-macro-hack", ] [[package]] name = "paste-impl" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d4dc4a7f6f743211c5aab239640a65091535d97d43d92a52bca435a640892bb" dependencies = [ - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "pbkdf2" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "crypto-mac", ] [[package]] name = "pin-utils" version = "0.1.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" [[package]] name = "ppv-lite86" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" [[package]] name = "primitive-types" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4336f4f5d5524fa60bcbd6fe626f9223d8142a50e7053e979acdf0da41ab975" dependencies = [ - "fixed-hash 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash", + "impl-codec", + "impl-serde 0.3.0", + "uint", ] [[package]] name = "proc-macro-crate" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" dependencies = [ - "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "toml", ] [[package]] name = "proc-macro-hack" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "proc-macro-nested" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" [[package]] name = "proc-macro2" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "quote" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "rand" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi", ] [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "libc", + "rand_chacha 0.2.1", + "rand_core 0.5.1", + "rand_hc 0.2.0", ] [[package]] name = "rand_chacha" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "rand_core 0.3.1", ] [[package]] name = "rand_chacha" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" dependencies = [ - "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "c2-chacha", + "rand_core 0.5.1", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", ] [[package]] name = "rand_hc" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1", ] [[package]] name = "rand_isaac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "rand_core 0.4.2", + "winapi", ] [[package]] name = "rand_os" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", ] [[package]] name = "rand_pcg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "rand_core 0.4.2", ] [[package]] name = "rand_xorshift" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "rdrand" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" [[package]] name = "regex" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" dependencies = [ - "aho-corasick 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.14" +version = "0.6.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1" [[package]] name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hex" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", ] [[package]] name = "schnorrkel" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" dependencies = [ - "curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 1.2.3", + "failure", + "merlin", + "rand 0.6.5", + "rand_core 0.4.2", + "rand_os", + "sha2", + "subtle 2.2.2", + "zeroize 0.9.3", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "send_wrapper" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" [[package]] name = "serde" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" dependencies = [ - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "sha2" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", ] [[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" dependencies = [ - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit", ] [[package]] name = "smallvec" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sourcefile" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" [[package]] name = "sp-api" version = "2.0.0-alpha.3" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api-proc-macro 2.0.0-alpha.3", - "sp-core 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-state-machine 0.8.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "sp-version 2.0.0-alpha.3", + "hash-db", + "parity-scale-codec", + "sp-api-proc-macro", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-version", ] [[package]] name = "sp-api-proc-macro" version = "2.0.0-alpha.3" dependencies = [ - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2-rfc", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "sp-application-crypto" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-io 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-std", ] [[package]] name = "sp-arithmetic" version = "2.0.0-alpha.3" dependencies = [ - "integer-sqrt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "serde", + "sp-debug-derive", + "sp-std", ] [[package]] name = "sp-authorship" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-inherents 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-core" version = "2.0.0-alpha.3" dependencies = [ - "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0-alpha.3", - "sp-externalities 0.8.0-alpha.3", - "sp-runtime-interface 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "sp-storage 2.0.0-alpha.3", - "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base58", + "blake2-rfc", + "byteorder", + "ed25519-dalek", + "hash-db", + "hash256-std-hasher", + "hex", + "impl-serde 0.3.0", + "lazy_static", + "libsecp256k1", + "log", + "num-traits", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.10.0", + "primitive-types", + "rand 0.7.3", + "regex", + "rustc-hex", + "schnorrkel", + "serde", + "sha2", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "substrate-bip39", + "tiny-bip39", + "tiny-keccak", + "twox-hash", + "wasmi", + "zeroize 1.1.0", ] [[package]] name = "sp-debug-derive" version = "2.0.0-alpha.3" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "sp-externalities" version = "0.8.0-alpha.3" dependencies = [ - "environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 2.0.0-alpha.3", - "sp-storage 2.0.0-alpha.3", + "environmental", + "sp-std", + "sp-storage", ] [[package]] name = "sp-inherents" version = "2.0.0-alpha.3" dependencies = [ - "derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "derive_more", + "parity-scale-codec", + "parking_lot 0.10.0", + "sp-core", + "sp-std", ] [[package]] name = "sp-io" version = "2.0.0-alpha.3" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-externalities 0.8.0-alpha.3", - "sp-runtime-interface 2.0.0-alpha.3", - "sp-state-machine 0.8.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "sp-trie 2.0.0-alpha.3", - "sp-wasm-interface 2.0.0-alpha.3", + "hash-db", + "libsecp256k1", + "log", + "parity-scale-codec", + "sp-core", + "sp-externalities", + "sp-runtime-interface", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-wasm-interface", ] [[package]] name = "sp-keyring" version = "2.0.0-alpha.3" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "sp-core", + "sp-runtime", + "strum", ] [[package]] name = "sp-panic-handler" version = "2.0.0-alpha.3" dependencies = [ - "backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "log", ] [[package]] name = "sp-phragmen" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-phragmen-compact 2.0.0-dev", - "sp-runtime 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "parity-scale-codec", + "serde", + "sp-core", + "sp-phragmen-compact", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-phragmen-compact" version = "2.0.0-dev" dependencies = [ - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "sp-runtime" version = "2.0.0-alpha.3" dependencies = [ - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0-alpha.3", - "sp-arithmetic 2.0.0-alpha.3", - "sp-core 2.0.0-alpha.3", - "sp-inherents 2.0.0-alpha.3", - "sp-io 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "parity-util-mem", + "paste", + "rand 0.7.3", + "serde", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-inherents", + "sp-io", + "sp-std", ] [[package]] name = "sp-runtime-interface" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.8.0-alpha.3", - "sp-runtime-interface-proc-macro 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "sp-wasm-interface 2.0.0-alpha.3", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std", + "sp-wasm-interface", + "static_assertions", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "2.0.0-alpha.3" dependencies = [ - "Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "sp-staking" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "parity-scale-codec", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-state-machine" version = "0.8.0-alpha.3" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-externalities 0.8.0-alpha.3", - "sp-panic-handler 2.0.0-alpha.3", - "sp-trie 2.0.0-alpha.3", - "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "log", + "num-traits", + "parity-scale-codec", + "parking_lot 0.10.0", + "rand 0.7.3", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-trie", + "trie-db", + "trie-root", ] [[package]] @@ -1628,610 +1748,442 @@ version = "2.0.0-alpha.3" name = "sp-storage" version = "2.0.0-alpha.3" dependencies = [ - "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "impl-serde 0.2.3", + "serde", + "sp-debug-derive", + "sp-std", ] [[package]] name = "sp-timestamp" version = "2.0.0-alpha.3" dependencies = [ - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0-alpha.3", - "sp-inherents 2.0.0-alpha.3", - "sp-runtime 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-api", + "sp-inherents", + "sp-runtime", + "sp-std", + "wasm-timer", ] [[package]] name = "sp-trie" version = "2.0.0-alpha.3" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "memory-db", + "parity-scale-codec", + "sp-core", + "sp-std", + "trie-db", + "trie-root", ] [[package]] name = "sp-version" version = "2.0.0-alpha.3" dependencies = [ - "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "impl-serde 0.2.3", + "parity-scale-codec", + "serde", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-wasm-interface" version = "2.0.0-alpha.3" dependencies = [ - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 2.0.0-alpha.3", - "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-std", + "wasmi", ] [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strum" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22" dependencies = [ - "strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strum_macros", ] [[package]] name = "strum_macros" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" dependencies = [ - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "substrate-bip39" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be511be555a3633e71739a79e4ddff6a6aaa6579fa6114182a51d72c3eb93c5" dependencies = [ - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac", + "pbkdf2", + "schnorrkel", + "sha2", ] [[package]] name = "subtle" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" [[package]] name = "subtle" version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" [[package]] name = "syn" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "synstructure" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", + "unicode-xid", ] [[package]] name = "thread_local" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", ] [[package]] name = "tiny-bip39" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6848cd8f566953ce1e8faeba12ee23cbdbb0437754792cd857d44628b5685e3" dependencies = [ - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure", + "hmac", + "once_cell", + "pbkdf2", + "rand 0.7.3", + "rustc-hash", + "sha2", + "unicode-normalization", ] [[package]] name = "tiny-keccak" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" dependencies = [ - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy", ] [[package]] name = "toml" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" dependencies = [ - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "tracing" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1721cc8cf7d770cc4257872507180f35a4797272f5962f24c806af9e7faf52ab" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-attributes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "tracing-attributes", + "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbad39da2f9af1cae3016339ad7f2c7a9e870f12e8fd04c4fd7ef35b30c0d2b" dependencies = [ - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "quote", + "syn", ] [[package]] name = "tracing-core" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa83a9a47081cd522c09c81b31aec2c9273424976f922ad61c053b58350b715" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", ] [[package]] name = "trie-db" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de9222c50cc325855621271157c973da27a0dcd26fa06f8edf81020bd2333df0" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "hashbrown", + "log", + "rustc-hex", + "smallvec 1.2.0", ] [[package]] name = "trie-root" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "652931506d2c1244d7217a70b99f56718a7b4161b37f04e7cd868072a99f68cd" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", ] [[package]] name = "twox-hash" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" dependencies = [ - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3", ] [[package]] name = "typenum" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" [[package]] name = "uint" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "crunchy", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +dependencies = [ + "smallvec 1.2.0", ] [[package]] name = "unicode-segmentation" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" -version = "0.2.58" +version = "0.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3557c397ab5a8e347d434782bcd31fc1483d927a6826804cec05cc792ee2519d" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.58" +version = "0.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0da9c9a19850d3af6df1cb9574970b566d617ecfaf36eb0b706b6f3ef9bd2f8" dependencies = [ - "bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457414a91863c0ec00090dba537f88ab955d93ca6555862c29b6d860990b8a8a" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.58" +version = "0.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f6fde1d36e75a714b5fe0cffbb78978f222ea6baebb726af13c78869fdb4205" dependencies = [ - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "quote", + "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.58" +version = "0.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25bda4168030a6412ea8a047e27238cadf56f0e53516e1e83fec0a8b7c786f6d" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.58" +version = "0.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasm-bindgen-webidl" -version = "0.2.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "fc9f36ad51f25b0219a3d4d13b90eb44cd075dff8b6280cca015775d7acaddd8" [[package]] name = "wasm-timer" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "324c5e65a08699c9c4334ba136597ab22b85dccd4b65dd1e36ccf8f723a95b54" dependencies = [ - "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "js-sys", + "parking_lot 0.9.0", + "pin-utils", + "send_wrapper", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] name = "wasmi" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf617d864d25af3587aa745529f7aaa541066c876d57e050c0d0c85c61c92aff" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "memory_units", + "num-rational", + "num-traits", + "parity-wasm", + "wasmi-validation", ] [[package]] name = "wasmi-validation" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea78c597064ba73596099281e2f4cfc019075122a65cdda3205af94f0b264d93" dependencies = [ - "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm", ] [[package]] name = "web-sys" -version = "0.3.35" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721c6263e2c66fd44501cc5efbfa2b7dfa775d13e4ea38c46299646ed1f9c70a" dependencies = [ - "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", - "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "weedle" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys", + "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "zeroize" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" [[package]] name = "zeroize" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" dependencies = [ - "zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize_derive", ] [[package]] name = "zeroize_derive" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -"checksum ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3" -"checksum aho-corasick 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811" -"checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" -"checksum arbitrary 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16971f2f0ce65c5cf2a1546cc6a0af102ecb11e265ddaa9433fb3e5bfdf676a4" -"checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" -"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" -"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" -"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -"checksum backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e4036b9bf40f3cf16aba72a3d65e8a520fc4bafcdc7079aea8f848c58c5b5536" -"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" -"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead" -"checksum bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" -"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -"checksum bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" -"checksum byte-slice-cast 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" -"checksum const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" -"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" -"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -"checksum curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d" -"checksum curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" -"checksum derive_more 0.99.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a806e96c59a76a5ba6e18735b6cf833344671e61e7863f2edb5c518ea2cac95c" -"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)" = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" -"checksum environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "516aa8d7a71cb00a1c4146f0798549b93d083d4f189b3ced8f3de6b8f11ee6c4" -"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" -"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fixed-hash 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3367952ceb191f4ab95dd5685dc163ac539e36202f9fcfd0cb22f9f9c542fefc" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" -"checksum futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" -"checksum futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" -"checksum futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" -"checksum futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" -"checksum futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" -"checksum futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" -"checksum futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" -"checksum futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -"checksum hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" -"checksum hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" -"checksum hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" -"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" -"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" -"checksum hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" -"checksum impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" -"checksum impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8" -"checksum impl-serde 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bbe9ea9b182f0fb1cabbd61f4ff9b7b7b9197955e95a7e4c27de5055eb29ff8" -"checksum impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" -"checksum integer-sqrt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f65877bf7d44897a473350b1046277941cee20b263397e90869c50b6e766088b" -"checksum js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "7889c7c36282151f6bf465be4700359318aef36baa951462382eae49e9577cf9" -"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" -"checksum libfuzzer-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c103ca347f75325d3d3ee31702bd6c09b7744e71883b7a8da9562b0c5dcdaead" -"checksum libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" -"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" -"checksum memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978" -"checksum memory-db 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "198831fe8722331a395bc199a5d08efbc197497ef354cb4c77b969c02ffc0fc4" -"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -"checksum merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0942b357c1b4d0dc43ba724674ec89c3218e6ca2b3e8269e7cb53bcecd2f6e" -"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -"checksum num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" -"checksum num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" -"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -"checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" -"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f509c5e67ca0605ee17dcd3f91ef41cadd685c75a298fb6261b781a5acb3f910" -"checksum parity-scale-codec-derive 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a0ec292e92e8ec7c58e576adacc1e3f399c597c8f263c42f18420abe58e7245" -"checksum parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ef1476e40bf8f5c6776e9600983435821ca86eb9819d74a6207cca69d091406a" -"checksum parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" -"checksum parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" -"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" -"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" -"checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" -"checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" -"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" -"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" -"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e4336f4f5d5524fa60bcbd6fe626f9223d8142a50e7053e979acdf0da41ab975" -"checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" -"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" -"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" -"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" -"checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" -"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -"checksum rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -"checksum rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" -"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" -"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" -"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" -"checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" -"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" -"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" -"checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -"checksum strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22" -"checksum strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" -"checksum substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3be511be555a3633e71739a79e4ddff6a6aaa6579fa6114182a51d72c3eb93c5" -"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" -"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" -"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" -"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -"checksum tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1cd1fb03fe8e07d17cd851a624a9fff74642a997b67fbd1ccd77533241640d92" -"checksum tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" -"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" -"checksum tracing 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e213bd24252abeb86a0b7060e02df677d367ce6cb772cef17e9214b8390a8d3" -"checksum tracing-attributes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04cfd395def5a60236e187e1ff905cb55668a59f29928dec05e6e1b1fd2ac1f3" -"checksum tracing-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "13a46f11e372b8bd4b4398ea54353412fdd7fd42a8370c7e543e218cf7661978" -"checksum trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de9222c50cc325855621271157c973da27a0dcd26fa06f8edf81020bd2333df0" -"checksum trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "652931506d2c1244d7217a70b99f56718a7b4161b37f04e7cd868072a99f68cd" -"checksum twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" -"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" -"checksum uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" -"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" -"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -"checksum wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c" -"checksum wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45" -"checksum wasm-bindgen-futures 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8bbdd49e3e28b40dec6a9ba8d17798245ce32b019513a845369c641b275135d9" -"checksum wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "574094772ce6921576fb6f2e3f7497b8a76273b6db092be18fc48a082de09dc3" -"checksum wasm-bindgen-macro-support 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "e85031354f25eaebe78bb7db1c3d86140312a911a106b2e29f9cc440ce3e7668" -"checksum wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e7e61fc929f4c0dddb748b102ebf9f632e2b8d739f2016542b4de2965a9601" -"checksum wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "ef012a0d93fc0432df126a8eaf547b2dce25a8ce9212e1d3cbeef5c11157975d" -"checksum wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "324c5e65a08699c9c4334ba136597ab22b85dccd4b65dd1e36ccf8f723a95b54" -"checksum wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bf617d864d25af3587aa745529f7aaa541066c876d57e050c0d0c85c61c92aff" -"checksum wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea78c597064ba73596099281e2f4cfc019075122a65cdda3205af94f0b264d93" -"checksum web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf97caf6aa8c2b1dac90faf0db529d9d63c93846cca4911856f78a83cebf53b" -"checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" -"checksum zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" -"checksum zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/frame/staking/fuzz/fuzz_targets/submit_solution.rs b/frame/staking/fuzz/fuzz_targets/submit_solution.rs index 5c9aa73bab585..bbc480f116306 100644 --- a/frame/staking/fuzz/fuzz_targets/submit_solution.rs +++ b/frame/staking/fuzz/fuzz_targets/submit_solution.rs @@ -47,10 +47,10 @@ pub fn new_test_ext() -> Result { fuzz_target!(|do_reduce: bool| { let ext = new_test_ext(); let mode: Mode = unsafe { std::mem::transmute(testing_utils::random(0, 2)) }; - let num_validators = testing_utils::random(200, 1000); - let num_nominators = testing_utils::random(500, 2000); + let num_validators = testing_utils::random(50, 500); + let num_nominators = testing_utils::random(200, 2000); let edge_per_voter = testing_utils::random(1, 16); - let to_elect = testing_utils::random(10, 200); + let to_elect = testing_utils::random(10, num_validators); println!("+++ instance with params {} / {} / {} / {:?} / {}", num_nominators, diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index 79a462335b8c0..29a395b89de4d 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -117,7 +117,6 @@ where T::Lookup: StaticLookup>, { (0..num_validators).for_each(|i| { - // println!("bonding validator {}/{}", i, num_validators); bond_validator::( account::(i), i + CTRL_PREFIX, @@ -135,7 +134,6 @@ where let target = all_targets.remove(random(0, all_targets.len() as u32 - 1) as usize); targets.push(target); }); - // println!("bonding voter {}/{}", i, num_voters); bond_nominator::( account::(i + NOMINATOR_PREFIX), i + NOMINATOR_PREFIX + CTRL_PREFIX, From f014e2c6b899712456d2f4d847d94721439420e0 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 9 Mar 2020 16:21:20 +0100 Subject: [PATCH 085/106] Remove unused pref map --- frame/staking/src/lib.rs | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 05af4f67514b6..81b166ea8b918 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -982,28 +982,28 @@ decl_storage! { /// Snapshot of validators at the beginning of the current election window. This should only /// have a value when [`EraElectionStatus`] == `ElectionStatus::Open(_)`. - SnapshotValidators get(fn snapshot_validators): Option>; + 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(_)`. - SnapshotNominators get(fn snapshot_nominators): Option>; + pub SnapshotNominators get(fn snapshot_nominators): Option>; /// The current set of staking keys. - Keys get(fn keys): Vec; + pub Keys get(fn keys): Vec; /// 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. - QueuedElected get(fn queued_elected): Option>>; + pub QueuedElected get(fn queued_elected): Option>>; /// The score of the current [`QueuedElected`]. - QueuedScore get(fn queued_score): Option; + pub QueuedScore get(fn queued_score): Option; /// Flag to control the execution of the offchain election. - EraElectionStatus get(fn era_election_status): ElectionStatus; + pub EraElectionStatus get(fn era_election_status): ElectionStatus; /// True of the current planned session is final - IsCurrentSessionFinal get(fn is_current_session_final): bool = false; + pub IsCurrentSessionFinal get(fn is_current_session_final): bool = false; /// True if network has been upgraded to this version. /// Storage version of the pallet. @@ -2056,7 +2056,7 @@ impl Module { /// Checks a given solution and if correct and improved, writes it on chain as the queued result /// of the next round. This may be called by both a signed and an unsigned transaction. - fn check_and_replace_solution( + pub fn check_and_replace_solution( winners: Vec, compact_assignments: CompactAssignments, compute: ElectionCompute, @@ -2366,21 +2366,9 @@ impl Module { // Insert current era staking information >::insert(¤t_era, total_stake); - // -------- - // TODO: this snapshot need to be taken elsewhere... this is super inefficient now. - // The current abstraction is such that we do `>::enumerate()` down to line - // in `do_phragmen` and don't really update the values there. There are numerous ways to fix this. - // check @guillaume. - let mut all_validators_and_prefs = BTreeMap::new(); - for (validator, preference) in >::enumerate() { - all_validators_and_prefs.insert(validator.clone(), preference); - } - // --------- - - let default_pref = ValidatorPrefs::default(); + // collect the pref of all winners for stash in &elected_stashes { - let pref = all_validators_and_prefs.get(stash) - .unwrap_or(&default_pref); // Must never happen, but better to be safe. + let pref = Self::validators(stash); >::insert(¤t_era, stash, pref); } From 26d93297ebe2dc307417dda24477fbdf7c4fda6c Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Tue, 10 Mar 2020 11:44:42 +0100 Subject: [PATCH 086/106] Deferred Slashing/Offence for offchain Phragmen (#5151) * Some boilerplate * Add test * One more test * Review comments --- frame/offences/src/lib.rs | 72 ++++++++++++++--- frame/offences/src/mock.rs | 23 +++++- frame/offences/src/tests.rs | 59 +++++++++++++- frame/staking/src/lib.rs | 44 ++++++---- frame/staking/src/mock.rs | 44 ++++++++-- frame/staking/src/tests.rs | 129 ++++++++++++++++++------------ primitives/staking/src/offence.rs | 15 +++- 7 files changed, 294 insertions(+), 92 deletions(-) diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 27983cbb5332e..0b4a80e561821 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -26,10 +26,11 @@ mod tests; use sp_std::vec::Vec; use frame_support::{ - decl_module, decl_event, decl_storage, Parameter, + decl_module, decl_event, decl_storage, Parameter, debug, }; -use sp_runtime::traits::Hash; +use sp_runtime::{traits::Hash, Perbill}; use sp_staking::{ + SessionIndex, offence::{Offence, ReportOffence, Kind, OnOffenceHandler, OffenceDetails, OffenceError}, }; use codec::{Encode, Decode}; @@ -54,7 +55,16 @@ pub trait Trait: frame_system::Trait { decl_storage! { trait Store for Module as Offences { /// The primary structure that holds all offence records keyed by report identifiers. - Reports get(fn reports): map hasher(blake2_256) ReportIdOf => Option>; + Reports get(fn reports): map hasher(blake2_256) ReportIdOf + => Option>; + + /// Deferred reports that have been rejected by the offence handler and need to be submitted + /// at a later time. + DeferredOffences get(deferred_reports): Vec<( + Vec>, + Vec, + SessionIndex, + )>; /// A vector of reports of the same kind that happened at the same time slot. ConcurrentReportsIndex: @@ -74,17 +84,35 @@ decl_storage! { decl_event!( pub enum Event { /// There is an offence reported of the given `kind` happened at the `session_index` and - /// (kind-specific) time slot. This event is not deposited for duplicate slashes. - Offence(Kind, OpaqueTimeSlot), + /// (kind-specific) time slot. This event is not deposited for duplicate slashes. last + /// element indicates of the offence was applied (true) or queued (false). + Offence(Kind, OpaqueTimeSlot, bool), } ); decl_module! { - /// Offences module, currently just responsible for taking offence reports. pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; + + fn on_initialize(now: T::BlockNumber) { + // only decode storage if we can actually submit anything again. + if T::OnOffenceHandler::can_report() { + >::take() + .iter() + .for_each(|(o, p, s)| { + if !Self::report_or_store_offence(o, p, *s) { + debug::native::error!( + target: "pallet-offences", + "re-submitting a deferred slash returned Err at {}. This should not happen with pallet-staking", + now, + ) + } + }); + } + } } } + impl> ReportOffence for Module where @@ -107,9 +135,6 @@ where None => return Err(OffenceError::DuplicateReport), }; - // Deposit the event. - Self::deposit_event(Event::Offence(O::ID, time_slot.encode())); - let offenders_count = concurrent_offenders.len() as u32; // The amount new offenders are slashed @@ -118,17 +143,42 @@ where let slash_perbill: Vec<_> = (0..concurrent_offenders.len()) .map(|_| new_fraction.clone()).collect(); - T::OnOffenceHandler::on_offence( + let applied = Self::report_or_store_offence( &concurrent_offenders, &slash_perbill, - offence.session_index(), + offence.session_index() ); + // Deposit the event. + Self::deposit_event(Event::Offence(O::ID, time_slot.encode(), applied)); + Ok(()) } } impl Module { + /// Tries (without checking) to report an offence. Stores them in [`DeferredOffences`] in case + /// it fails. Returns false in case it has to store the offence. + fn report_or_store_offence( + concurrent_offenders: &[OffenceDetails], + slash_perbill: &[Perbill], + session_index: SessionIndex, + ) -> bool { + match T::OnOffenceHandler::on_offence( + &concurrent_offenders, + &slash_perbill, + session_index, + ) { + Ok(_) => true, + Err(_) => { + >::mutate(|d| + d.push((concurrent_offenders.to_vec(), slash_perbill.to_vec(), session_index)) + ); + false + } + } + } + /// Compute the ID for the given report properties. /// /// The report id depends on the offence kind, time slot and the id of offender. diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index a003ad69157fc..7ddbc6726d05f 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -43,6 +43,7 @@ pub struct OnOffenceHandler; thread_local! { pub static ON_OFFENCE_PERBILL: RefCell> = RefCell::new(Default::default()); + pub static CAN_REPORT: RefCell = RefCell::new(true); } impl offence::OnOffenceHandler for OnOffenceHandler { @@ -50,11 +51,25 @@ impl offence::OnOffenceHandler for OnOff _offenders: &[OffenceDetails], slash_fraction: &[Perbill], _offence_session: SessionIndex, - ) { - ON_OFFENCE_PERBILL.with(|f| { - *f.borrow_mut() = slash_fraction.to_vec(); - }); + ) -> Result<(), ()> { + if >::can_report() { + ON_OFFENCE_PERBILL.with(|f| { + *f.borrow_mut() = slash_fraction.to_vec(); + }); + + Ok(()) + } else { + Err(()) + } } + + fn can_report() -> bool { + CAN_REPORT.with(|c| *c.borrow()) + } +} + +pub fn set_can_report(can_report: bool) { + CAN_REPORT.with(|c| *c.borrow_mut() = can_report); } pub fn with_on_offence_fractions) -> R>(f: F) -> R { diff --git a/frame/offences/src/tests.rs b/frame/offences/src/tests.rs index 0ed98427c65f8..ab9975d416577 100644 --- a/frame/offences/src/tests.rs +++ b/frame/offences/src/tests.rs @@ -21,9 +21,9 @@ use super::*; use crate::mock::{ Offences, System, Offence, TestEvent, KIND, new_test_ext, with_on_offence_fractions, - offence_reports, + offence_reports, set_can_report, }; -use sp_runtime::Perbill; +use sp_runtime::{Perbill, traits::OnInitialize}; use frame_system::{EventRecord, Phase}; #[test] @@ -130,7 +130,7 @@ fn should_deposit_event() { System::events(), vec![EventRecord { phase: Phase::ApplyExtrinsic(0), - event: TestEvent::offences(crate::Event::Offence(KIND, time_slot.encode())), + event: TestEvent::offences(crate::Event::Offence(KIND, time_slot.encode(), true)), topics: vec![], }] ); @@ -165,7 +165,7 @@ fn doesnt_deposit_event_for_dups() { System::events(), vec![EventRecord { phase: Phase::ApplyExtrinsic(0), - event: TestEvent::offences(crate::Event::Offence(KIND, time_slot.encode())), + event: TestEvent::offences(crate::Event::Offence(KIND, time_slot.encode(), true)), topics: vec![], }] ); @@ -212,3 +212,54 @@ fn should_properly_count_offences() { ); }); } + +#[test] +fn should_queue_and_resubmit_rejected_offence() { + new_test_ext().execute_with(|| { + set_can_report(false); + + // will get deferred + let offence = Offence { + validator_set_count: 5, + time_slot: 42, + offenders: vec![5], + }; + Offences::report_offence(vec![], offence).unwrap(); + assert_eq!(Offences::deferred_reports().len(), 1); + // event also indicates unapplied. + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: TestEvent::offences(crate::Event::Offence(KIND, 42u128.encode(), false)), + topics: vec![], + }] + ); + + // will not dequeue + Offences::on_initialize(2); + + // again + let offence = Offence { + validator_set_count: 5, + time_slot: 62, + offenders: vec![5], + }; + Offences::report_offence(vec![], offence).unwrap(); + assert_eq!(Offences::deferred_reports().len(), 2); + + set_can_report(true); + + // can be submitted + let offence = Offence { + validator_set_count: 5, + time_slot: 72, + offenders: vec![5], + }; + Offences::report_offence(vec![], offence).unwrap(); + assert_eq!(Offences::deferred_reports().len(), 2); + + Offences::on_initialize(3); + assert_eq!(Offences::deferred_reports().len(), 0); + }) +} diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 81b166ea8b918..9f9419f3f7cd8 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -1108,6 +1108,9 @@ decl_error! { PhragmenBogusNominator, /// One of the submitted nominators has an edge to which they have not voted on chain. PhragmenBogusNomination, + /// One of the submitted nominators has an edge which is submitted before the last non-zero + /// slash of the target. + PhragmenSlashedNomination, /// A self vote must only be originated from a validator to ONLY themselves. PhragmenBogusSelfVote, /// The submitted result has unknown edges that are not among the presented winners. @@ -2145,19 +2148,21 @@ impl Module { ); // NOTE: we don't really have to check here if the sum of all edges are the // nominator correct. Un-compacting assures this by definition. - ensure!( + + for (t, _) in distribution { // each target in the provided distribution must be actually nominated by the // nominator after the last non-zero slash. - distribution.into_iter().all(|(t, _)| { - nomination.targets.iter().find(|&tt| tt == t).is_some() - && - ::SlashingSpans::get(&t).map_or( - true, - |spans| nomination.submitted_in >= spans.last_nonzero_slash(), - ) - }), - Error::::PhragmenBogusNomination, - ); + if nomination.targets.iter().find(|&tt| tt == t).is_none() { + return Err(Error::::PhragmenBogusNomination); + } + + if ::SlashingSpans::get(&t).map_or( + false, + |spans| nomination.submitted_in < spans.last_nonzero_slash(), + ) { + return Err(Error::::PhragmenSlashedNomination); + } + } } else { // a self vote ensure!(distribution.len() == 1, Error::::PhragmenBogusSelfVote); @@ -2716,13 +2721,18 @@ impl OnOffenceHandler>], slash_fraction: &[Perbill], slash_session: SessionIndex, - ) { + ) -> Result<(), ()> { + if !Self::can_report() { + return Err(()) + } + let reward_proportion = SlashRewardFraction::get(); let active_era = { let active_era = Self::active_era(); if active_era.is_none() { - return + // this offence need not be re-submitted. + return Ok(()) } active_era.expect("value checked not to be `None`; qed").index }; @@ -2743,7 +2753,7 @@ impl OnOffenceHandler return, // before bonding period. defensive - should be filtered out. + None => return Ok(()), // before bonding period. defensive - should be filtered out. Some(&(ref slash_era, _)) => *slash_era, } }; @@ -2788,6 +2798,12 @@ impl OnOffenceHandler bool { + Self::era_election_status().is_closed() } } diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index e524fa116b62c..b30683495bd38 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -721,7 +721,7 @@ pub fn on_offence_in_era( let bonded_eras = crate::BondedEras::get(); for &(bonded_era, start_session) in bonded_eras.iter() { if bonded_era == era { - Staking::on_offence(offenders, slash_fraction, start_session); + let _ = Staking::on_offence(offenders, slash_fraction, start_session).unwrap(); return; } else if bonded_era > era { break; @@ -729,7 +729,12 @@ pub fn on_offence_in_era( } if Staking::active_era().unwrap().index == era { - Staking::on_offence(offenders, slash_fraction, Staking::eras_start_session_index(era).unwrap()); + let _ = + Staking::on_offence( + offenders, + slash_fraction, + Staking::eras_start_session_index(era).unwrap() + ).unwrap(); } else { panic!("cannot slash in era {}", era); } @@ -884,10 +889,22 @@ pub fn prepare_submission_with( let snapshot_validators = Staking::snapshot_validators().expect("snapshot not created."); let snapshot_nominators = Staking::snapshot_nominators().expect("snapshot not created."); let nominator_index = |a: &AccountId| -> Option { - snapshot_nominators.iter().position(|x| x == a).map(|i| i as NominatorIndex) + snapshot_nominators + .iter() + .position(|x| x == a) + .map_or_else( + || { println!("unable to find nominator index for {:?}", a); None }, + |i| Some(i as NominatorIndex), + ) }; let validator_index = |a: &AccountId| -> Option { - snapshot_validators.iter().position(|x| x == a).map(|i| i as ValidatorIndex) + snapshot_validators + .iter() + .position(|x| x == a) + .map_or_else( + || { println!("unable to find validator index for {:?}", a); None }, + |i| Some(i as ValidatorIndex), + ) }; let assignments_reduced = sp_phragmen::assignment_staked_to_ratio(staked); @@ -908,7 +925,8 @@ pub fn prepare_submission_with( let compact = CompactAssignments::from_assignment(assignments_reduced, nominator_index, validator_index) - .unwrap(); + .map_err(|e| { println!("error in compact: {:?}", e); e }) + .expect("Failed to create compact"); // winner ids to index @@ -952,7 +970,19 @@ pub fn make_all_reward_payment(era: EraIndex) { #[macro_export] macro_rules! assert_session_era { ($session:expr, $era:expr) => { - assert_eq!(Session::current_index(), $session); - assert_eq!(Staking::active_era().unwrap().index, $era); + 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, + ); }; } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index d211ea6842f8a..a3425b6ad4f81 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2772,19 +2772,22 @@ mod offchain_phragmen { /// file. This produces a edge graph that can be reduced. fn build_offchain_phragmen_test_ext() { for i in (10..=40).step_by(10) { - bond_validator(i, i + 1000, 100); + // Note: we respect the convention of the mock (10, 11 pairs etc.) since these accounts + // have corresponding keys in session which makes everything more ergonomic and + // realistic. + bond_validator(i + 1, i, 100); } let mut voter = 1; - bond_nominator(voter, 1000 + voter, 100, vec![10]); + bond_nominator(voter, 1000 + voter, 100, vec![11]); voter = 2; - bond_nominator(voter, 1000 + voter, 100, vec![10, 20]); + bond_nominator(voter, 1000 + voter, 100, vec![11, 11]); voter = 3; - bond_nominator(voter, 1000 + voter, 100, vec![20, 40]); + bond_nominator(voter, 1000 + voter, 100, vec![21, 41]); voter = 4; - bond_nominator(voter, 1000 + voter, 100, vec![20, 30, 40]); + bond_nominator(voter, 1000 + voter, 100, vec![21, 31, 41]); voter = 5; - bond_nominator(voter, 1000 + voter, 100, vec![20, 30, 40]); + bond_nominator(voter, 1000 + voter, 100, vec![21, 31, 41]); } fn offchainify(ext: &mut TestExternalities) -> Arc> { @@ -3423,7 +3426,7 @@ mod offchain_phragmen { let (compact, winners, score) = prepare_submission_with(true, |a| { a.iter_mut() .find(|x| x.who == 5) - .map(|x| x.distribution = vec![(20, 50), (40, 30), (30, 20)]); + .map(|x| x.distribution = vec![(21, 50), (41, 30), (31, 20)]); }); assert_noop!( @@ -3448,11 +3451,11 @@ mod offchain_phragmen { let (compact, winners, score) = prepare_submission_with(true, |a| { // mutate a self vote to target someone else. That someone else is still among the // winners - a.iter_mut().find(|x| x.who == 10).map(|x| { + a.iter_mut().find(|x| x.who == 11).map(|x| { x.distribution .iter_mut() - .find(|y| y.0 == 10) - .map(|y| y.0 = 20) + .find(|y| y.0 == 11) + .map(|y| y.0 = 21) }); }); @@ -3477,11 +3480,11 @@ mod offchain_phragmen { let (compact, winners, score) = prepare_submission_with(true, |a| { // Remove the self vote. - a.retain(|x| x.who != 10); + a.retain(|x| x.who != 11); // add is as a new double vote a.push(StakedAssignment { - who: 10, - distribution: vec![(10, 50), (20, 50)], + who: 11, + distribution: vec![(11, 50), (21, 50)], }); }); @@ -3548,7 +3551,7 @@ mod offchain_phragmen { // correctly 100. a.iter_mut() .find(|x| x.who == 3) - .map(|x| x.distribution = vec![(20, 50), (40, 30), (30, 20)]); + .map(|x| x.distribution = vec![(21, 50), (41, 30), (31, 20)]); }); assert_noop!( @@ -3570,64 +3573,65 @@ mod offchain_phragmen { .build() .execute_with(|| { build_offchain_phragmen_test_ext(); + // finalize the round with fallback. This is needed since all nominator submission // are in era zero and we want this one to pass with no problems. run_to_block(15); - // open the election window and create snapshots. - run_to_block(32); - - // a solution that has been prepared before the slash. - let (pre_compact, pre_winners, pre_score) = prepare_submission_with(false, |_| {}); + // go to the next session to trigger start_era and bump the active era + run_to_block(20); - // slash 10 - let offender_expo = Staking::eras_stakers(active_era(), 10); + // slash 10. This must happen outside of the election window. + let offender_expo = Staking::eras_stakers(active_era(), 11); on_offence_now( &[OffenceDetails { - offender: (10, offender_expo.clone()), + offender: (11, offender_expo.clone()), reporters: vec![], }], - &[Perbill::from_percent(10)], + &[Perbill::from_percent(50)], ); - // validate 10 again for the next round. + // validate 10 again for the next round. But this guy will not have the votes that + // it should have had from 1 and 2. assert_ok!(Staking::validate( - Origin::signed(10 + 1000), + Origin::signed(10), Default::default() )); - // a solution that has been prepared after the slash. - let (compact, winners, score) = prepare_submission_with(false, |_| {}); - - // The edges are different. - assert_ne!(compact, pre_compact); - // But the winners are the same. - assert_eq_uvec!(winners, pre_winners); - // 2 votes for 10 and 20, but the edge for 20 is gone, so there must be one less - // entry in votes2. - assert_eq!(compact.votes2.len(), pre_compact.votes2.len() - 1); + // open the election window and create snapshots. + run_to_block(32); - // old results are invalid. - assert_noop!( - Staking::submit_election_solution( - Origin::signed(10), - pre_winners, - pre_compact, - pre_score, - ), - Error::::PhragmenBogusNomination, - ); + // a solution that has been prepared after the slash. + let (compact, winners, score) = prepare_submission_with(false, |a| { + // no one is allowed to vote for 10, except for itself. + a.into_iter() + .filter(|s| s.who != 11) + .for_each(|s| + assert!(s.distribution.iter().find(|(t, _)| *t == 11).is_none()) + ); + }); - // new results are okay. + // can also be submitted. assert_ok!(Staking::submit_election_solution( Origin::signed(10), winners, compact, score - ),); + )); - // finish the round. - run_to_block(30); + // a wrong solution. + let (compact, winners, score) = prepare_submission_with(false, |a| { + // add back the vote that has been filtered out. + a.push(StakedAssignment { + who: 1, + distribution: vec![(11, 100)] + }); + }); + + assert_noop!( + Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Error::::PhragmenSlashedNomination, + ); }) } @@ -3728,6 +3732,31 @@ mod offchain_phragmen { ); }) } + + #[test] + #[should_panic] + fn offence_is_blocked_when_window_open() { + ExtBuilder::default() + .offchain_phragmen_ext() + .validator_count(4) + .has_stakers(false) + .build() + .execute_with(|| { + run_to_block(12); + assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); + + let offender_expo = Staking::eras_stakers(active_era(), 10); + + // panic from the impl in mock + on_offence_now( + &[OffenceDetails { + offender: (10, offender_expo.clone()), + reporters: vec![], + }], + &[Perbill::from_percent(10)], + ); + }) + } } #[test] @@ -3902,7 +3931,7 @@ fn zero_slash_keeps_nominators() { assert_eq!(Balances::free_balance(11), 1000); - let exposure = Staking::eras_stakers(Staking::active_era().unwrap().index, 11); + let exposure = Staking::eras_stakers(active_era(), 11); assert_eq!(Balances::free_balance(101), 2000); on_offence_now( diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 06e73f018b765..584f3a75ea3ab 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -142,11 +142,20 @@ pub trait OnOffenceHandler { /// Zero is a valid value for a fraction. /// /// The `session` parameter is the session index of the offence. + /// + /// The receiver might decide to not accept this offence. In this case, the call site is + /// responsible for queuing the report and re-submitting again. fn on_offence( offenders: &[OffenceDetails], slash_fraction: &[Perbill], session: SessionIndex, - ); + ) -> Result<(), ()>; + + /// Can an offence be reported now or not. This is an method to short-circuit a call into + /// `on_offence`. Ideally, a correct implementation should return `false` if `on_offence` will + /// return `Err`. Nonetheless, this is up to the implementation and this trait cannot guarantee + /// it. + fn can_report() -> bool; } impl OnOffenceHandler for () { @@ -154,7 +163,9 @@ impl OnOffenceHandler for () { _offenders: &[OffenceDetails], _slash_fraction: &[Perbill], _session: SessionIndex, - ) {} + ) -> Result<(), ()> { Ok(()) } + + fn can_report() -> bool { true } } /// A details about an offending authority for a particular kind of offence. From ecd042eb30159c850c83093afe33f0bd6b10c5fa Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 10 Mar 2020 11:51:44 +0100 Subject: [PATCH 087/106] Fix build --- frame/offences/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 0b4a80e561821..bce4697fc6dfd 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -105,7 +105,7 @@ decl_module! { target: "pallet-offences", "re-submitting a deferred slash returned Err at {}. This should not happen with pallet-staking", now, - ) + ); } }); } From 63eb8c43d115b0cde7aacec36531571750c9e0f8 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 12 Mar 2020 11:58:16 +0100 Subject: [PATCH 088/106] review comments --- frame/babe/src/lib.rs | 21 +- frame/babe/src/tests.rs | 2 +- frame/session/src/lib.rs | 28 +- frame/staking/src/lib.rs | 51 +- frame/support/src/traits.rs | 14 +- primitives/phragmen/fuzzer/Cargo.lock | 1166 ++++++++++++------------- primitives/phragmen/fuzzer/Cargo.toml | 3 +- primitives/phragmen/src/reduce.rs | 78 +- 8 files changed, 690 insertions(+), 673 deletions(-) diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 8f504c19dba7b..c2bcbbb304319 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -26,7 +26,7 @@ use sp_std::{result, prelude::*}; use frame_support::{decl_storage, decl_module, traits::{FindAuthor, Get, Randomness as RandomnessT}}; use sp_timestamp::OnTimestampSet; use sp_runtime::{generic::DigestItem, ConsensusEngineId, Perbill, PerThing}; -use sp_runtime::traits::{IsMember, SaturatedConversion, Saturating, Hash, One, Bounded}; +use sp_runtime::traits::{IsMember, SaturatedConversion, Saturating, Hash, One}; use sp_staking::{ SessionIndex, offence::{Offence, Kind}, @@ -317,21 +317,24 @@ impl Module { /// Return the _best guess_ block number, at which the next epoch change is predicted to happen. /// + /// Returns None if the prediction is in the past; This implies an error internally in the Babe + /// and should not happen under normal circumstances. + /// /// 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 -------------- // 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. - pub fn next_expected_epoch_change(now: T::BlockNumber) -> T::BlockNumber { + pub fn next_expected_epoch_change(now: T::BlockNumber) -> Option { let next_slot = Self::current_epoch_start().saturating_add(T::EpochDuration::get()); - let slots_remaining = next_slot + next_slot .checked_sub(CurrentSlot::get()) - .unwrap_or(Bounded::max_value()); - // This is a best effort guess. Drifts in the slot/block ratio will cause errors here. - let blocks_remaining: T::BlockNumber = slots_remaining.saturated_into(); - now.saturating_add(blocks_remaining) + .map(|slots_remaining| { + // This is a best effort guess. Drifts in the slot/block ratio will cause errors here. + let blocks_remaining: T::BlockNumber = slots_remaining.saturated_into(); + now.saturating_add(blocks_remaining) + }) } /// DANGEROUS: Enact an epoch change. Should be done on every block where `should_epoch_change` has returned `true`, @@ -492,7 +495,7 @@ impl OnTimestampSet for Module { } impl frame_support::traits::EstimateNextSessionRotation for Module { - fn estimate_next_session_rotation(now: T::BlockNumber) -> T::BlockNumber { + fn estimate_next_session_rotation(now: T::BlockNumber) -> Option { Self::next_expected_epoch_change(now) } } diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index c98fc58529fdd..026d632b803ee 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -130,6 +130,6 @@ fn can_predict_next_epoch_change() { // next epoch change will be at assert_eq!(Babe::current_epoch_start(), 9); // next change will be 12, 2 slots from now - assert_eq!(Babe::next_expected_epoch_change(System::block_number()), 5 + 2); + assert_eq!(Babe::next_expected_epoch_change(System::block_number()), Some(5 + 2)); }) } diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index 7689163dbc080..da27af975f769 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -152,23 +152,21 @@ impl< Period: Get, Offset: Get, > EstimateNextSessionRotation for PeriodicSessions { - fn estimate_next_session_rotation(now: BlockNumber) -> BlockNumber { + fn estimate_next_session_rotation(now: BlockNumber) -> Option { let offset = Offset::get(); let period = Period::get(); - if now > offset { + Some(if now > offset { let block_after_last_session = (now.clone() - offset) % period.clone(); - match block_after_last_session > Zero::zero() { - true => - { - now.saturating_add( - period.saturating_sub(block_after_last_session) - ) - } - false => Zero::zero() + if block_after_last_session > Zero::zero() { + now.saturating_add( + period.saturating_sub(block_after_last_session) + ) + } else { + Zero::zero() } } else { offset - } + }) } } @@ -783,7 +781,7 @@ impl> FindAuthor impl EstimateNextNewSession for Module { /// This session module always calls new_session and next_session at the same time, hence we /// do a simple proxy and pass the function to next rotation. - fn estimate_next_new_session(now: T::BlockNumber) -> T::BlockNumber { + fn estimate_next_new_session(now: T::BlockNumber) -> Option { T::NextSessionRotation::estimate_next_session_rotation(now) } } @@ -1048,16 +1046,16 @@ mod tests { type P = PeriodicSessions; for i in 0..3 { - assert_eq!(P::estimate_next_session_rotation(i), 3); + assert_eq!(P::estimate_next_session_rotation(i), Some(3)); assert!(!P::should_end_session(i)); } assert!(P::should_end_session(3)); - assert_eq!(P::estimate_next_session_rotation(3), 3); + assert_eq!(P::estimate_next_session_rotation(3), Some(3)); for i in 4..13 { assert!(!P::should_end_session(i)); - assert_eq!(P::estimate_next_session_rotation(i), 13); + assert_eq!(P::estimate_next_session_rotation(i), Some(13)); } assert!(P::should_end_session(13)); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 9f9419f3f7cd8..7cfd897c70f09 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -1145,31 +1145,36 @@ decl_module! { Self::era_election_status().is_closed() && Self::is_current_session_final() { - let next_session_change = - T::NextNewSession::estimate_next_new_session(now); - if let Some(remaining) = next_session_change.checked_sub(&now) { - if remaining <= T::ElectionLookahead::get() && !remaining.is_zero() { - // create snapshot. - if Self::create_stakers_snapshot() { - // Set the flag to make sure we don't waste any compute here in the same era - // after we have triggered the offline compute. - >::put( - ElectionStatus::::Open(now) - ); - debug::native::info!( - target: "staking", - "Election window is Open({:?}). Snapshot created", - now, - ); - } else { - debug::native::warn!( - target: "staking", - "Failed to create snapshot at {:?}. Election window will remain closed.", - now, - ); - } + if let Some(next_session_change) = T::NextNewSession::estimate_next_new_session(now){ + if let Some(remaining) = next_session_change.checked_sub(&now) { + if remaining <= T::ElectionLookahead::get() && !remaining.is_zero() { + // create snapshot. + if Self::create_stakers_snapshot() { + // Set the flag to make sure we don't waste any compute here in the same era + // after we have triggered the offline compute. + >::put( + ElectionStatus::::Open(now) + ); + debug::native::info!( + target: "staking", + "Election window is Open({:?}). Snapshot created", + now, + ); + } else { + debug::native::warn!( + target: "staking", + "Failed to create snapshot at {:?}. Election window will remain closed.", + now, + ); + } + } } + } else { + debug::native::warn!( + target: "staking", + "estimate_next_new_session() failed to predict. Election status cannot be changed.", + ); } } } diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index bd8328ed4b833..ae52109ee72b8 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -143,12 +143,14 @@ impl< /// are made about the scheduling of the sessions. pub trait EstimateNextSessionRotation { /// Return the block number at which the next session rotation is estimated to happen. - fn estimate_next_session_rotation(now: BlockNumber) -> BlockNumber; + /// + /// None should be returned if the estimation fails to come to an answer + fn estimate_next_session_rotation(now: BlockNumber) -> Option; } impl EstimateNextSessionRotation for () { - fn estimate_next_session_rotation(_: BlockNumber) -> BlockNumber { - Bounded::max_value() + fn estimate_next_session_rotation(_: BlockNumber) -> Option { + Default::default() } } @@ -156,12 +158,12 @@ impl EstimateNextSessionRotation for () { /// always be implemented by the session module. pub trait EstimateNextNewSession { /// Return the block number at which the next new session is estimated to happen. - fn estimate_next_new_session(now: BlockNumber) -> BlockNumber; + fn estimate_next_new_session(now: BlockNumber) -> Option; } impl EstimateNextNewSession for () { - fn estimate_next_new_session(_: BlockNumber) -> BlockNumber { - Bounded::max_value() + fn estimate_next_new_session(_: BlockNumber) -> Option { + Default::default() } } diff --git a/primitives/phragmen/fuzzer/Cargo.lock b/primitives/phragmen/fuzzer/Cargo.lock index 14286d239d29d..5f4e9a2451611 100644 --- a/primitives/phragmen/fuzzer/Cargo.lock +++ b/primitives/phragmen/fuzzer/Cargo.lock @@ -4,1212 +4,1326 @@ name = "Inflector" version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "regex", ] [[package]] name = "ahash" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3" dependencies = [ - "const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "const-random", ] [[package]] name = "aho-corasick" -version = "0.7.6" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" dependencies = [ - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] name = "arbitrary" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" [[package]] name = "arrayref" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" dependencies = [ - "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop", ] [[package]] name = "arrayvec" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" [[package]] name = "autocfg" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" [[package]] name = "autocfg" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.42" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8" dependencies = [ - "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys", + "cfg-if", + "libc", + "rustc-demangle", ] [[package]] name = "backtrace-sys" -version = "0.1.32" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca797db0057bae1a7aa2eef3283a874695455cecf08a43bfb8507ee0ebc1ed69" dependencies = [ - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", ] [[package]] name = "base58" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitvec" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" [[package]] name = "blake2-rfc" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" dependencies = [ - "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12", + "constant_time_eq", ] [[package]] name = "block-buffer" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding", + "byte-tools", + "byteorder", + "generic-array", ] [[package]] name = "block-padding" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools", ] [[package]] name = "byte-slice-cast" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" [[package]] name = "byte-tools" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" -version = "1.3.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "c2-chacha" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "cc" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "clear_on_drop" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" dependencies = [ - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", ] [[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", ] [[package]] name = "const-random" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" dependencies = [ - "const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "const-random-macro", + "proc-macro-hack", ] [[package]] name = "const-random-macro" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "proc-macro-hack", ] [[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-mac" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", + "subtle 1.0.0", ] [[package]] name = "curve25519-dalek" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "clear_on_drop", + "digest", + "rand_core 0.3.1", + "subtle 2.2.2", ] [[package]] name = "curve25519-dalek" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "digest", + "rand_core 0.5.1", + "subtle 2.2.2", + "zeroize 1.1.0", ] [[package]] name = "derive_more" -version = "0.99.2" +version = "0.99.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a806e96c59a76a5ba6e18735b6cf833344671e61e7863f2edb5c518ea2cac95c" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "digest" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", ] [[package]] name = "ed25519-dalek" version = "1.0.0-pre.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" dependencies = [ - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "clear_on_drop", + "curve25519-dalek 2.0.0", + "rand 0.7.3", + "sha2", ] [[package]] name = "environmental" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516aa8d7a71cb00a1c4146f0798549b93d083d4f189b3ced8f3de6b8f11ee6c4" [[package]] name = "failure" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" dependencies = [ - "backtrace 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "failure_derive", ] [[package]] name = "failure_derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fixed-hash" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3367952ceb191f4ab95dd5685dc163ac539e36202f9fcfd0cb22f9f9c542fefc" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "libc", + "rand 0.7.3", + "rustc-hex", + "static_assertions", ] [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", ] [[package]] name = "getrandom" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "wasi", ] [[package]] name = "hash-db" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" [[package]] name = "hash256-std-hasher" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" dependencies = [ - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy", ] [[package]] name = "hashbrown" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" dependencies = [ - "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "ahash", + "autocfg 0.1.7", ] [[package]] name = "hex" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" [[package]] name = "hmac" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" dependencies = [ - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac", + "digest", ] [[package]] name = "hmac-drbg" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" dependencies = [ - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "digest", + "generic-array", + "hmac", ] [[package]] name = "honggfuzz" version = "0.5.45" dependencies = [ - "arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "arbitrary", + "lazy_static", + "memmap", ] [[package]] name = "impl-codec" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" dependencies = [ - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec", ] [[package]] name = "impl-serde" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8" dependencies = [ - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "impl-serde" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bbe9ea9b182f0fb1cabbd61f4ff9b7b7b9197955e95a7e4c27de5055eb29ff8" dependencies = [ - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "impl-trait-for-tuples" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "integer-sqrt" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65877bf7d44897a473350b1046277941cee20b263397e90869c50b6e766088b" [[package]] name = "keccak" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" [[package]] name = "libsecp256k1" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref", + "crunchy", + "digest", + "hmac-drbg", + "rand 0.7.3", + "sha2", + "subtle 2.2.2", + "typenum", ] [[package]] name = "lock_api" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" dependencies = [ - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard", ] [[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", ] [[package]] name = "maybe-uninit" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" -version = "2.3.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi", ] [[package]] name = "memory-db" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "198831fe8722331a395bc199a5d08efbc197497ef354cb4c77b969c02ffc0fc4" dependencies = [ - "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ahash", + "hash-db", + "hashbrown", + "parity-util-mem", ] [[package]] name = "memory_units" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" [[package]] name = "merlin" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b0942b357c1b4d0dc43ba724674ec89c3218e6ca2b3e8269e7cb53bcecd2f6e" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "keccak", + "rand_core 0.4.2", + "zeroize 1.1.0", ] [[package]] name = "nodrop" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "num-bigint" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", + "num-integer", + "num-traits", ] [[package]] name = "num-integer" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", + "num-traits", ] [[package]] name = "num-rational" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", + "num-bigint", + "num-integer", + "num-traits", ] [[package]] name = "num-traits" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", ] [[package]] name = "once_cell" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" dependencies = [ - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0", ] [[package]] name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "parity-scale-codec" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f509c5e67ca0605ee17dcd3f91ef41cadd685c75a298fb6261b781a5acb3f910" dependencies = [ - "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-slice-cast 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec-derive 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.5.1", + "bitvec", + "byte-slice-cast", + "parity-scale-codec-derive", + "serde", ] [[package]] name = "parity-scale-codec-derive" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0ec292e92e8ec7c58e576adacc1e3f399c597c8f263c42f18420abe58e7245" dependencies = [ - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "parity-util-mem" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1476e40bf8f5c6776e9600983435821ca86eb9819d74a6207cca69d091406a" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "impl-trait-for-tuples", + "parity-util-mem-derive", + "parking_lot 0.10.0", + "primitive-types", + "winapi", ] [[package]] name = "parity-util-mem-derive" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "syn", + "synstructure", ] [[package]] name = "parity-wasm" version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" [[package]] name = "parking_lot" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ - "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api", + "parking_lot_core 0.6.2", + "rustc_version", ] [[package]] name = "parking_lot" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" dependencies = [ - "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api", + "parking_lot_core 0.7.0", ] [[package]] name = "parking_lot_core" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "rustc_version", + "smallvec 0.6.13", + "winapi", ] [[package]] name = "parking_lot_core" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec 1.2.0", + "winapi", ] [[package]] name = "paste" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e1afe738d71b1ebab5f1207c055054015427dbfc7bbe9ee1266894156ec046" dependencies = [ - "paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "paste-impl", + "proc-macro-hack", ] [[package]] name = "paste-impl" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d4dc4a7f6f743211c5aab239640a65091535d97d43d92a52bca435a640892bb" dependencies = [ - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "pbkdf2" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "crypto-mac", ] [[package]] name = "ppv-lite86" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" [[package]] name = "primitive-types" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4336f4f5d5524fa60bcbd6fe626f9223d8142a50e7053e979acdf0da41ab975" dependencies = [ - "fixed-hash 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash", + "impl-codec", + "impl-serde 0.3.0", + "uint", ] [[package]] name = "proc-macro-crate" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" dependencies = [ - "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "toml", ] [[package]] name = "proc-macro-hack" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "proc-macro2" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "quote" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "rand" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi", ] [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", ] [[package]] name = "rand_chacha" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "rand_core 0.3.1", ] [[package]] name = "rand_chacha" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", ] [[package]] name = "rand_hc" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1", ] [[package]] name = "rand_isaac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "rand_core 0.4.2", + "winapi", ] [[package]] name = "rand_os" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", ] [[package]] name = "rand_pcg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "rand_core 0.4.2", ] [[package]] name = "rand_xorshift" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "rdrand" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" [[package]] name = "regex" -version = "1.3.3" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" dependencies = [ - "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.13" +version = "0.6.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1" [[package]] name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hex" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", ] [[package]] name = "schnorrkel" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" dependencies = [ - "curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 1.2.3", + "failure", + "merlin", + "rand 0.6.5", + "rand_core 0.4.2", + "rand_os", + "sha2", + "subtle 2.2.2", + "zeroize 0.9.3", ] [[package]] name = "scopeguard" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" dependencies = [ - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "sha2" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", ] [[package]] name = "smallvec" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" dependencies = [ - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit", ] [[package]] name = "smallvec" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" [[package]] name = "sp-application-crypto" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-io 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-std", ] [[package]] name = "sp-arithmetic" version = "2.0.0-alpha.3" dependencies = [ - "integer-sqrt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "serde", + "sp-debug-derive", + "sp-std", ] [[package]] name = "sp-core" version = "2.0.0-alpha.3" dependencies = [ - "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0-alpha.3", - "sp-externalities 0.8.0-alpha.3", - "sp-runtime-interface 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "sp-storage 2.0.0-alpha.3", - "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base58", + "blake2-rfc", + "byteorder", + "ed25519-dalek", + "hash-db", + "hash256-std-hasher", + "hex", + "impl-serde 0.3.0", + "lazy_static", + "libsecp256k1", + "log", + "num-traits", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.10.0", + "primitive-types", + "rand 0.7.3", + "regex", + "rustc-hex", + "schnorrkel", + "serde", + "sha2", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "substrate-bip39", + "tiny-bip39", + "tiny-keccak", + "twox-hash", + "wasmi", + "zeroize 1.1.0", ] [[package]] name = "sp-debug-derive" version = "2.0.0-alpha.3" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "sp-externalities" version = "0.8.0-alpha.3" dependencies = [ - "environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 2.0.0-alpha.3", - "sp-storage 2.0.0-alpha.3", + "environmental", + "sp-std", + "sp-storage", ] [[package]] name = "sp-inherents" version = "2.0.0-alpha.3" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "derive_more", + "parity-scale-codec", + "parking_lot 0.10.0", + "sp-core", + "sp-std", ] [[package]] name = "sp-io" version = "2.0.0-alpha.3" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-externalities 0.8.0-alpha.3", - "sp-runtime-interface 2.0.0-alpha.3", - "sp-state-machine 0.8.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "sp-trie 2.0.0-alpha.3", - "sp-wasm-interface 2.0.0-alpha.3", + "hash-db", + "libsecp256k1", + "log", + "parity-scale-codec", + "sp-core", + "sp-externalities", + "sp-runtime-interface", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-wasm-interface", ] [[package]] name = "sp-panic-handler" version = "2.0.0-alpha.3" dependencies = [ - "backtrace 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "log", ] [[package]] name = "sp-phragmen" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-phragmen-compact 2.0.0-dev", - "sp-runtime 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "parity-scale-codec", + "serde", + "sp-core", + "sp-phragmen-compact", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-phragmen-compact" version = "2.0.0-dev" dependencies = [ - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "sp-phragmen-fuzzer" version = "2.0.0" dependencies = [ - "honggfuzz 0.5.45", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-phragmen 2.0.0-alpha.3", + "honggfuzz", + "rand 0.7.3", + "sp-phragmen", ] [[package]] name = "sp-runtime" version = "2.0.0-alpha.3" dependencies = [ - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0-alpha.3", - "sp-arithmetic 2.0.0-alpha.3", - "sp-core 2.0.0-alpha.3", - "sp-inherents 2.0.0-alpha.3", - "sp-io 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "parity-util-mem", + "paste", + "rand 0.7.3", + "serde", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-inherents", + "sp-io", + "sp-std", ] [[package]] name = "sp-runtime-interface" version = "2.0.0-alpha.3" dependencies = [ - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.8.0-alpha.3", - "sp-runtime-interface-proc-macro 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "sp-wasm-interface 2.0.0-alpha.3", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std", + "sp-wasm-interface", + "static_assertions", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "2.0.0-alpha.3" dependencies = [ - "Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "sp-state-machine" version = "0.8.0-alpha.3" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-externalities 0.8.0-alpha.3", - "sp-panic-handler 2.0.0-alpha.3", - "sp-trie 2.0.0-alpha.3", - "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "log", + "num-traits", + "parity-scale-codec", + "parking_lot 0.10.0", + "rand 0.7.3", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-trie", + "trie-db", + "trie-root", ] [[package]] @@ -1220,383 +1334,269 @@ version = "2.0.0-alpha.3" name = "sp-storage" version = "2.0.0-alpha.3" dependencies = [ - "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", + "impl-serde 0.2.3", + "serde", + "sp-debug-derive", + "sp-std", ] [[package]] name = "sp-trie" version = "2.0.0-alpha.3" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0-alpha.3", - "sp-std 2.0.0-alpha.3", - "trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "memory-db", + "parity-scale-codec", + "sp-core", + "sp-std", + "trie-db", + "trie-root", ] [[package]] name = "sp-wasm-interface" version = "2.0.0-alpha.3" dependencies = [ - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 2.0.0-alpha.3", - "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-std", + "wasmi", ] [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "substrate-bip39" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be511be555a3633e71739a79e4ddff6a6aaa6579fa6114182a51d72c3eb93c5" dependencies = [ - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac", + "pbkdf2", + "schnorrkel", + "sha2", ] [[package]] name = "subtle" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" [[package]] name = "subtle" version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" [[package]] name = "syn" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "synstructure" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", + "unicode-xid", ] [[package]] name = "thread_local" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", ] [[package]] name = "tiny-bip39" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6848cd8f566953ce1e8faeba12ee23cbdbb0437754792cd857d44628b5685e3" dependencies = [ - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure", + "hmac", + "once_cell", + "pbkdf2", + "rand 0.7.3", + "rustc-hash", + "sha2", + "unicode-normalization", ] [[package]] name = "tiny-keccak" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" dependencies = [ - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy", ] [[package]] name = "toml" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" dependencies = [ - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "trie-db" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de9222c50cc325855621271157c973da27a0dcd26fa06f8edf81020bd2333df0" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "hashbrown", + "log", + "rustc-hex", + "smallvec 1.2.0", ] [[package]] name = "trie-root" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "652931506d2c1244d7217a70b99f56718a7b4161b37f04e7cd868072a99f68cd" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", ] [[package]] name = "twox-hash" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" dependencies = [ - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3", ] [[package]] name = "typenum" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" [[package]] name = "uint" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "crunchy", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +dependencies = [ + "smallvec 1.2.0", ] [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasmi" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf617d864d25af3587aa745529f7aaa541066c876d57e050c0d0c85c61c92aff" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "memory_units", + "num-rational", + "num-traits", + "parity-wasm", + "wasmi-validation", ] [[package]] name = "wasmi-validation" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea78c597064ba73596099281e2f4cfc019075122a65cdda3205af94f0b264d93" dependencies = [ - "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm", ] [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "zeroize" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" [[package]] name = "zeroize" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" dependencies = [ - "zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize_derive", ] [[package]] name = "zeroize_derive" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -"checksum ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3" -"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" -"checksum arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" -"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" -"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -"checksum backtrace 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b4b1549d804b6c73f4817df2ba073709e96e426f12987127c48e6745568c350b" -"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" -"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" -"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -"checksum byte-slice-cast 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" -"checksum const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" -"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" -"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -"checksum curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d" -"checksum curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" -"checksum derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2159be042979966de68315bce7034bb000c775f22e3e834e1c52ff78f041cae8" -"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)" = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" -"checksum environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "516aa8d7a71cb00a1c4146f0798549b93d083d4f189b3ced8f3de6b8f11ee6c4" -"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" -"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fixed-hash 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3367952ceb191f4ab95dd5685dc163ac539e36202f9fcfd0cb22f9f9c542fefc" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -"checksum hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" -"checksum hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" -"checksum hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" -"checksum hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e" -"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" -"checksum hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" -"checksum impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" -"checksum impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8" -"checksum impl-serde 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bbe9ea9b182f0fb1cabbd61f4ff9b7b7b9197955e95a7e4c27de5055eb29ff8" -"checksum impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" -"checksum integer-sqrt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f65877bf7d44897a473350b1046277941cee20b263397e90869c50b6e766088b" -"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" -"checksum libsecp256k1 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" -"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" -"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" -"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -"checksum memory-db 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "198831fe8722331a395bc199a5d08efbc197497ef354cb4c77b969c02ffc0fc4" -"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -"checksum merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0942b357c1b4d0dc43ba724674ec89c3218e6ca2b3e8269e7cb53bcecd2f6e" -"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -"checksum num-bigint 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f115de20ad793e857f76da2563ff4a09fbcfd6fe93cca0c5d996ab5f3ee38d" -"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" -"checksum num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" -"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -"checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" -"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum parity-scale-codec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f509c5e67ca0605ee17dcd3f91ef41cadd685c75a298fb6261b781a5acb3f910" -"checksum parity-scale-codec-derive 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a0ec292e92e8ec7c58e576adacc1e3f399c597c8f263c42f18420abe58e7245" -"checksum parity-util-mem 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ef1476e40bf8f5c6776e9600983435821ca86eb9819d74a6207cca69d091406a" -"checksum parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" -"checksum parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" -"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" -"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" -"checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" -"checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" -"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" -"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum primitive-types 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e4336f4f5d5524fa60bcbd6fe626f9223d8142a50e7053e979acdf0da41ab975" -"checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" -"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" -"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" -"checksum regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" -"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -"checksum rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -"checksum rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" -"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" -"checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" -"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" -"checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" -"checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -"checksum substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3be511be555a3633e71739a79e4ddff6a6aaa6579fa6114182a51d72c3eb93c5" -"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" -"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" -"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" -"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -"checksum tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1cd1fb03fe8e07d17cd851a624a9fff74642a997b67fbd1ccd77533241640d92" -"checksum tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" -"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" -"checksum trie-db 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de9222c50cc325855621271157c973da27a0dcd26fa06f8edf81020bd2333df0" -"checksum trie-root 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "652931506d2c1244d7217a70b99f56718a7b4161b37f04e7cd868072a99f68cd" -"checksum twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" -"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" -"checksum uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -"checksum wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bf617d864d25af3587aa745529f7aaa541066c876d57e050c0d0c85c61c92aff" -"checksum wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea78c597064ba73596099281e2f4cfc019075122a65cdda3205af94f0b264d93" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" -"checksum zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" -"checksum zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/primitives/phragmen/fuzzer/Cargo.toml b/primitives/phragmen/fuzzer/Cargo.toml index 645b1c151bcfd..39df42d4c9f02 100644 --- a/primitives/phragmen/fuzzer/Cargo.toml +++ b/primitives/phragmen/fuzzer/Cargo.toml @@ -6,7 +6,8 @@ edition = "2018" [dependencies] sp-phragmen = { version = "2.0.0-alpha.3", path = ".." } -honggfuzz = "0.5" +# honggfuzz = "0.5" +honggfuzz = { path = "../../../../honggfuzz-rs"} rand = "0.7.3" [workspace] diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index a3ad1c8be15ce..ad4874dcbe722 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -76,43 +76,26 @@ fn combinations_2(input: &[T]) -> Vec<(T, T)> { /// Returns the count of trailing common elements in two slices. pub(crate) fn trailing_common(t1: &[T], t2: &[T]) -> usize { - let mut t1_pointer = t1.len() - 1; - let mut t2_pointer = t2.len() - 1; - let mut common = 0usize; - - while t1[t1_pointer] == t2[t2_pointer] { - common += 1; - if t1_pointer == 0 || t2_pointer == 0 { - break; - } - t1_pointer -= 1; - t2_pointer -= 1; - } - - common + t1.iter().rev().zip(t2.iter().rev()).take_while(|e| e.0 == e.1).count() } /// Merges two parent roots as described by the reduce algorithm. fn merge(voter_root_path: Vec>, target_root_path: Vec>) { - if voter_root_path.len() <= target_root_path.len() { - // iterate from last to beginning, skipping the first one. This asserts that - // indexing is always correct. - voter_root_path - .iter() - .take(voter_root_path.len() - 1) // take all except for last. - .enumerate() - .map(|(i, n)| (n, voter_root_path[i + 1].clone())) - .for_each(|(voter, next)| Node::set_parent_of(&next, &voter)); - Node::set_parent_of(&voter_root_path[0], &target_root_path[0]); + let (shorter_path, longer_path) = if voter_root_path.len() <= target_root_path.len() { + (voter_root_path, target_root_path) } else { - target_root_path - .iter() - .take(target_root_path.len() - 1) // take all except for last. - .enumerate() - .map(|(i, n)| (n, target_root_path[i + 1].clone())) - .for_each(|(target, next)| Node::set_parent_of(&next, &target)); - Node::set_parent_of(&target_root_path[0], &voter_root_path[0]); - } + (target_root_path, voter_root_path) + }; + + // iterate from last to beginning, skipping the first one. This asserts that + // indexing is always correct. + shorter_path + .iter() + .take(shorter_path.len() - 1) // take all except for last. + .enumerate() + .map(|(i, n)| (n, shorter_path[i + 1].clone())) + .for_each(|(voter, next)| Node::set_parent_of(&next, &voter)); + Node::set_parent_of(&shorter_path[0], &longer_path[0]); } /// Reduce only redundant edges with cycle length of 4. @@ -334,7 +317,9 @@ fn reduce_4(assignments: &mut Vec>) -> u32 { num_changed } -/// Reduce all redundant edges from the edge weight graph. +/// Reduce redundant edges from the edge weight graph, with all possible length. +/// +/// To get the best performance, this should be called after `reduce_4()`. /// /// Returns the number of edges removed. /// @@ -417,7 +402,6 @@ fn reduce_all(assignments: &mut Vec>) -> u32 let common_count = trailing_common(&voter_root_path, &target_root_path); // because roots are the same. - #[cfg(feature = "std")] debug_assert_eq!( target_root_path.last().unwrap(), voter_root_path.last().unwrap() @@ -440,7 +424,6 @@ fn reduce_all(assignments: &mut Vec>) -> u32 ) .collect::>>(); // a cycle's length shall always be multiple of two. - #[cfg(feature = "std")] debug_assert_eq!(cycle.len() % 2, 0); // find minimum of cycle. @@ -700,6 +683,31 @@ mod tests { assert_eq!(e.borrow().clone().parent.unwrap().borrow().id.who, 4u32); // c } + #[test] + fn merge_with_len_one() { + // D <-- A <-- B <-- C + // + // F <-- E + let d = Node::new(NodeId::from(1, NodeRole::Target)).into_ref(); + let a = Node::new(NodeId::from(2, NodeRole::Target)).into_ref(); + let b = Node::new(NodeId::from(3, NodeRole::Target)).into_ref(); + let c = Node::new(NodeId::from(4, NodeRole::Target)).into_ref(); + let f = Node::new(NodeId::from(6, NodeRole::Target)).into_ref(); + + Node::set_parent_of(&c, &b); + Node::set_parent_of(&b, &a); + Node::set_parent_of(&a, &d); + + let path1 = vec![c.clone(), b.clone(), a.clone(), d.clone()]; + let path2 = vec![f.clone()]; + + merge(path1, path2); + // D <-- A <-- B <-- C + // | + // F --> --> + assert_eq!(f.borrow().clone().parent.unwrap().borrow().id.who, 4u32); // c + } + #[test] fn basic_reduce_4_cycle_works() { use super::*; From 3cd2c55dc102212cf9ccb3a8e1b6651181dac253 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 12 Mar 2020 14:53:51 +0100 Subject: [PATCH 089/106] fix more --- primitives/phragmen/fuzzer/Cargo.toml | 3 +-- primitives/phragmen/fuzzer/src/reduce.rs | 4 ++-- primitives/phragmen/src/reduce.rs | 4 +--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/primitives/phragmen/fuzzer/Cargo.toml b/primitives/phragmen/fuzzer/Cargo.toml index 39df42d4c9f02..645b1c151bcfd 100644 --- a/primitives/phragmen/fuzzer/Cargo.toml +++ b/primitives/phragmen/fuzzer/Cargo.toml @@ -6,8 +6,7 @@ edition = "2018" [dependencies] sp-phragmen = { version = "2.0.0-alpha.3", path = ".." } -# honggfuzz = "0.5" -honggfuzz = { path = "../../../../honggfuzz-rs"} +honggfuzz = "0.5" rand = "0.7.3" [workspace] diff --git a/primitives/phragmen/fuzzer/src/reduce.rs b/primitives/phragmen/fuzzer/src/reduce.rs index ca7ad921d0caa..4bf08590a149f 100644 --- a/primitives/phragmen/fuzzer/src/reduce.rs +++ b/primitives/phragmen/fuzzer/src/reduce.rs @@ -39,8 +39,8 @@ fn main() { let (assignments, winners) = generate_random_phragmen_assignment( rr(100, 1000), rr(100, 2000), - 10, - 6, + 8, + 8, ); reduce_and_compare(&assignments, &winners); }); diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index ad4874dcbe722..eb36bb93cfb83 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -91,9 +91,7 @@ fn merge(voter_root_path: Vec>, target_root_path: Vec // indexing is always correct. shorter_path .iter() - .take(shorter_path.len() - 1) // take all except for last. - .enumerate() - .map(|(i, n)| (n, shorter_path[i + 1].clone())) + .zip(shorter_path.iter().skip(1)) .for_each(|(voter, next)| Node::set_parent_of(&next, &voter)); Node::set_parent_of(&shorter_path[0], &longer_path[0]); } From 918e58e72ed1b297b1eec65199ee7058620447ee Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 12 Mar 2020 16:21:52 +0100 Subject: [PATCH 090/106] fix build --- primitives/phragmen/src/reduce.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index eb36bb93cfb83..334f49f15c057 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -400,6 +400,7 @@ fn reduce_all(assignments: &mut Vec>) -> u32 let common_count = trailing_common(&voter_root_path, &target_root_path); // because roots are the same. + #[cfg(feature = "std")] debug_assert_eq!( target_root_path.last().unwrap(), voter_root_path.last().unwrap() @@ -421,7 +422,9 @@ fn reduce_all(assignments: &mut Vec>) -> u32 .cloned(), ) .collect::>>(); + // a cycle's length shall always be multiple of two. + #[cfg(feature = "std")] debug_assert_eq!(cycle.len() % 2, 0); // find minimum of cycle. From 537d6eeba9effd89e979f68e8e7e207f7a0c8a2a Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 13 Mar 2020 08:58:02 +0100 Subject: [PATCH 091/106] Some cleanups and self-reviews --- frame/offences/src/lib.rs | 2 +- frame/staking/fuzz/Cargo.toml | 1 - frame/staking/src/lib.rs | 112 +++++++++++++++---------- frame/staking/src/mock.rs | 9 +- frame/staking/src/offchain_election.rs | 42 +++++----- 5 files changed, 91 insertions(+), 75 deletions(-) diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index bce4697fc6dfd..796dd1ed236c1 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -146,7 +146,7 @@ where let applied = Self::report_or_store_offence( &concurrent_offenders, &slash_perbill, - offence.session_index() + offence.session_index(), ); // Deposit the event. diff --git a/frame/staking/fuzz/Cargo.toml b/frame/staking/fuzz/Cargo.toml index 0b5bc21815f91..e4e08a065c467 100644 --- a/frame/staking/fuzz/Cargo.toml +++ b/frame/staking/fuzz/Cargo.toml @@ -1,4 +1,3 @@ - [package] name = "pallet-staking-fuzz" version = "0.0.0" diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index ea94ba0207552..af948cbb2d81d 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -261,7 +261,13 @@ pub mod slashing; pub mod offchain_election; pub mod inflation; -use sp_std::{prelude::*, result, collections::btree_map::BTreeMap, convert::{TryInto, From}, mem::size_of}; +use sp_std::{ + result, + prelude::*, + collections::btree_map::BTreeMap, + convert::{TryInto, From}, + mem::size_of, +}; use codec::{HasCompact, Encode, Decode}; use frame_support::{ decl_module, decl_event, decl_storage, ensure, decl_error, debug, Parameter, @@ -311,7 +317,7 @@ pub type NominatorIndex = u32; /// Data type used to index validators in the compact type. pub type ValidatorIndex = u16; -// Ensure the size of both ValidatorIndex and NominatorIndex +// Ensure the size of both ValidatorIndex and NominatorIndex. They both need to be well below usize. static_assertions::const_assert!(size_of::() <= size_of::()); static_assertions::const_assert!(size_of::() <= size_of::()); @@ -340,7 +346,7 @@ pub struct ActiveEraInfo { start: Option, } -/// Accuracy used for on-chain phragmen +/// Accuracy used for on-chain phragmen. pub type ChainAccuracy = Perbill; /// Accuracy used for off-chain phragmen. This better be small. @@ -634,8 +640,9 @@ pub enum ElectionCompute { OnChain, /// Result was submitted and accepted to the chain via a signed transaction. Signed, - /// Result was submitted by an authority (probably via an unsigned transaction) - Authority, + /// Result was submitted and accepted to the chain via an unsigned transaction (by an + /// authority). + Unsigned, } /// The result of an election round. @@ -706,7 +713,8 @@ impl SessionInterface<::AccountId> for T whe >, T::SessionHandler: pallet_session::SessionHandler<::AccountId>, T::SessionManager: pallet_session::SessionManager<::AccountId>, - T::ValidatorIdOf: Convert<::AccountId, Option<::AccountId>>, + T::ValidatorIdOf: + Convert<::AccountId, Option<::AccountId>>, { fn disable_validator(validator: &::AccountId) -> Result { >::disable(validator) @@ -1000,10 +1008,11 @@ decl_storage! { /// The score of the current [`QueuedElected`]. pub QueuedScore get(fn queued_score): Option; - /// Flag to control the execution of the offchain election. + /// Flag to control the execution of the offchain election. When `Open(_)`, we accept + /// solutions to be submitted. pub EraElectionStatus get(fn era_election_status): ElectionStatus; - /// True of the current planned session is final + /// True if the current planned session is final. pub IsCurrentSessionFinal get(fn is_current_session_final): bool = false; /// True if network has been upgraded to this version. @@ -1133,13 +1142,11 @@ decl_module! { fn deposit_event() = default; - /// Does the following: - /// - /// 1. potential storage migration - /// 2. sets `ElectionStatus` to `Open(now)` where `now` is the block number at which - /// the election window has opened. The offchain worker, if applicable, will execute at - /// the end of the current block. `submit_election_solution` will accept solutions from - /// this block until the end of the era. + /// sets `ElectionStatus` to `Open(now)` where `now` is the block number at which the + /// election window has opened, if we are at the last session and less blocks than + /// `T::ElectionLookahead` is remaining until the next new session schedule. The offchain + /// worker, if applicable, will execute at the end of the current block, and solutions may + /// be submitted. fn on_initialize(now: T::BlockNumber) { if // if we don't have any ongoing offchain compute. @@ -1174,7 +1181,7 @@ decl_module! { } else { debug::native::warn!( target: "staking", - "estimate_next_new_session() failed to predict. Election status cannot be changed.", + "estimate_next_new_session() failed to execute. Election status cannot be changed.", ); } } @@ -1185,21 +1192,21 @@ decl_module! { fn offchain_worker(now: T::BlockNumber) { use offchain_election::{set_check_offchain_execution_status, compute_offchain_election}; - let window_open = Self::era_election_status().is_open_at(now); - if window_open { + + if Self::era_election_status().is_open_at(now) { let offchain_status = set_check_offchain_execution_status::(now); if let Err(why) = offchain_status { debug::native::warn!( target: "staking", - "skipping offchain call in open election window due to [{}]", + "skipping offchain worker in open election window due to [{}]", why, ); } else { if let Err(e) = compute_offchain_election::() { debug::native::warn!( target: "staking", - "Error in phragmen offchain worker call: {:?}", + "Error in phragmen offchain worker: {:?}", e, ); }; @@ -1713,10 +1720,10 @@ decl_module! { /// Submit a phragmen result to the chain. If the solution: /// - /// 1. is valid - /// 2. has a better score than a potentially existing solution on chain + /// 1. is valid. + /// 2. has a better score than a potentially existing solution on chain. /// - /// then, it will be put on chain. + /// then, it will be _put_ on chain. /// /// A solution consists of two pieces of data: /// @@ -1728,13 +1735,14 @@ decl_module! { /// /// Additionally, the submitter must provide: /// - /// - The score that they claim their solution has. + /// - The `score` that they claim their solution has. /// /// Both validators and nominators will be represented by indices in the solution. The /// indices should respect the corresponding types ([`ValidatorIndex`] and /// [`NominatorIndex`]). Moreover, they should be valid when used to index into /// [`SnapshotValidators`] and [`SnapshotNominators`]. Any invalid index will cause the - /// solution to be rejected. + /// solution to be rejected. These two storage items are set during the election window and + /// may be used to determine the indices. /// /// A solution is valid if: /// @@ -1755,14 +1763,11 @@ decl_module! { /// minimized (to ensure less variance) /// /// # - /// E: number of edges. - /// m: size of winner committee. - /// n: number of nominators. - /// d: edge degree (16 for now) - /// v: number of on-chain validator candidates. + /// E: number of edges. m: size of winner committee. n: number of nominators. d: edge degree + /// (16 for now) v: number of on-chain validator candidates. /// /// NOTE: given a solution which is reduced, we can enable a new check the ensure `|E| < n + - /// m`. + /// m`. We don't do this _yet_, but our offchain worker code executes it nonetheless. /// /// major steps (all done in `check_and_replace_solution`): /// @@ -1770,14 +1775,18 @@ decl_module! { /// - Storage: O(1) read `PhragmenScore`. /// - Storage: O(1) read `ValidatorCount`. /// - Storage: O(1) length read from `SnapshotValidators`. - /// - Storage: O(v) reads of `AccountId`. + /// + /// - Storage: O(v) reads of `AccountId` to fetch `snapshot_validators`. /// - Memory: O(m) iterations to map winner index to validator id. - /// - Storage: O(n) reads `AccountId`. + /// - Storage: O(n) reads `AccountId` to fetch `snapshot_nominators`. /// - Memory: O(n + m) reads to map index to `AccountId` for un-compact. + /// /// - Storage: O(e) accountid reads from `Nomination` to read correct nominations. /// - Storage: O(e) calls into `slashable_balance_of_extended` to convert ratio to staked. + /// /// - Memory: build_support_map. O(e). /// - Memory: evaluate_support: O(E). + /// /// - Storage: O(e) writes to `QueuedElected`. /// - Storage: O(1) write to `QueuedScore` /// @@ -1799,8 +1808,10 @@ decl_module! { )? } - /// Unsigned version of `submit_election_solution`. Will only be accepted from those who are - /// in the current validator set. + /// Unsigned version of `submit_election_solution`. A signature and claimed validator index + /// must be provided. For the call to be validated, the signature must contain the signed + /// payload of the first three elements, and match the public key at the claimed validator + /// index (using the staking key). #[weight = SimpleDispatchInfo::FixedNormal(100_000_000)] pub fn submit_election_solution_unsigned( origin, @@ -1815,7 +1826,7 @@ decl_module! { Self::check_and_replace_solution( winners, compact_assignments, - ElectionCompute::Authority, + ElectionCompute::Unsigned, score, )? } @@ -2102,6 +2113,9 @@ impl Module { // check if all winners were legit; this is rather cheap. Replace with accountId. let winners = winners.into_iter().map(|widx| { + // NOTE: at the moment, since staking is explicitly blocking any offence until election + // is closed, we don't check here if the account id at `snapshot_validators[widx]` is + // actually a validator. If this ever changes, this loop needs to also check this. snapshot_validators.get(widx as usize).cloned().ok_or(Error::::PhragmenBogusWinner) }).collect::, Error>>()?; @@ -2122,6 +2136,7 @@ impl Module { nominator_at, validator_at, ).map_err(|e| { + // log the error since it is not propagated into the runtime error. debug::native::warn!( target: "staking", "un-compacting solution failed due to {:?}", @@ -2152,9 +2167,10 @@ impl Module { if !is_validator { // a normal vote let nomination = maybe_nomination.expect( - "exactly one of maybe_validator and maybe_nomination is true. \ + "exactly one of `maybe_validator` and `maybe_nomination.is_some` is true. \ is_validator is false; maybe_nomination is some; qed" ); + // NOTE: we don't really have to check here if the sum of all edges are the // nominator correct. Un-compacting assures this by definition. @@ -2207,12 +2223,13 @@ impl Module { // At last, alles Ok. Exposures and store the result. let exposures = Self::collect_exposure(supports); - debug::native::info!( target: "staking", - "A better solution has been validated and stored on chain.", + "A better solution (with compute {:?}) has been validated and stored on chain.", + compute, ); + // write new results. >::put(ElectionResult { elected_stashes: winners, compute, @@ -2453,7 +2470,7 @@ impl Module { // collect exposures let exposures = Self::collect_exposure(supports); - // In order to keep the property required by `n_session_ending` that we must return the + // 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 // economic conditions have changed, we don't attempt to do any optimization where we // compare against the prior set. @@ -2876,13 +2893,8 @@ impl pallet_session::OneSessionHandler for Module { pub struct LockStakingStatus(sp_std::marker::PhantomData); impl sp_std::fmt::Debug for LockStakingStatus { - #[cfg(feature = "std")] fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - write!(f, "LockStakingStatus<{:?}>", self.0) - } - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) + write!(f, "LockStakingStatus") } } @@ -3000,9 +3012,17 @@ impl frame_support::unsigned::ValidateUnsigned for Module { } Ok(ValidTransaction { + // The higher the score[0], the better a solution is. priority: score[0].saturated_into(), requires: vec![], + // a duplicate solution from the same validator within an era cannot be accepted + // at the transaction pool layer. provides: vec![(Self::current_era(), validator_key).encode()], + // Note: this can be more accurate in the future. We do something like + // `era_end_block - current_block` but that is not needed now as we eagerly run + // offchain workers now and the above should be same as `T::ElectionLookahead` + // without the need to query more storage in the validation phase. If we randomize + // offchain worker, then we might re-consider this. longevity: TryInto::::try_into(T::ElectionLookahead::get()).unwrap_or(150_u64), propagate: true, }) diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index b30683495bd38..6c1269cbe1cb8 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -234,14 +234,14 @@ parameter_types! { /// We prefer using the dummy key defined in `mock::dummy_sr25519`, not `crate::sr25519`, since the /// dummy one gives us some nice helpers and a fake `IdentifyAccount`. -pub struct TestStaking; -impl sp_runtime::BoundToRuntimeAppPublic for TestStaking { +pub struct TestStakingKeys; +impl sp_runtime::BoundToRuntimeAppPublic for TestStakingKeys { type Public = dummy_sr25519::AuthorityId; } sp_runtime::impl_opaque_keys! { pub struct SessionKeys { - pub staking: TestStaking, + pub staking: TestStakingKeys, pub other: OtherSessionHandler, } } @@ -460,8 +460,7 @@ impl ExtBuilder { self } pub fn offchain_phragmen_ext(self) -> Self { - self - .session_per_era(4) + self.session_per_era(4) .session_length(5) .election_lookahead(3) .local_key_account(11) diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 81a0bb05f1c8b..e945008a55499 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -100,7 +100,9 @@ pub(crate) fn set_check_offchain_execution_status( } } -/// The internal logic of the offchain worker of this module. +/// The internal logic of the offchain worker of this module. This runs the phragmen election, +/// compacts and reduces the solution, computes the score and submits it back to the chain as an +/// unsigned transaction. pub(crate) fn compute_offchain_election() -> Result<(), OffchainElectionError> { let keys = >::keys(); let local_keys = T::KeyType::all(); @@ -112,7 +114,7 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti .enumerate() .find(|(_, val_key)| **val_key == key) }) { - // compute raw solution. + // compute raw solution. Note that we use `OffchainAccuracy`. let PhragmenResult { winners, assignments, @@ -135,19 +137,16 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti score, index as u32, signature, - ) - .into(); - - let ok = T::SubmitTransaction::submit_unsigned(call) - .map_err(|_| { - debug::native::warn!( - target: "staking", - "failed to submit offchain solution with key {:?}", - pubkey, - ); - }) - .is_ok(); - if ok { + ).into(); + + if T::SubmitTransaction::submit_unsigned(call).map_err(|_| { + debug::native::warn!( + target: "staking", + "failed to submit offchain solution with key {:?}", + pubkey, + ); + }).is_ok() { + // return and break out after the first successful submission return Ok(()); } } @@ -156,8 +155,7 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti Err(OffchainElectionError::NoSigningKey) } -/// Takes a phragmen result and the snapshot data and spits out some data that can be submitted to -/// the chain. +/// Takes a phragmen result and spits out some data that can be submitted to the chain. /// /// This does a lot of stuff; read the inline comments. pub fn prepare_submission( @@ -211,10 +209,10 @@ where // nicer, for now we do it as such since this code is not time-critical. This ensure that the // score _predicted_ here is the same as the one computed on chain and you will not get a // `PhragmenBogusScore` error. This is totally NOT needed if we don't do reduce. This whole - // accuracy glitch happens because reduce breaks that assumption of _scale_. The initial - // phragmen results are computed in `OffchainAccuracy` and the initial `staked` assignment set - // is also all multiples of this value. After reduce, this no longer holds. Hence converting to - // ratio thereafter is not trivially reversible. + // _accuracy glitch_ happens because reduce breaks that assumption of rounding and **scale**. + // The initial phragmen results are computed in `OffchainAccuracy` and the initial `staked` + // assignment set is also all multiples of this value. After reduce, this no longer holds. Hence + // converting to ratio thereafter is not trivially reversible. let score = { let staked = sp_phragmen::assignment_ratio_to_staked( low_accuracy_assignment.clone(), @@ -232,7 +230,7 @@ where validator_index, ).map_err(|e| OffchainElectionError::from(e))?; - // winners to index. + // winners to index. Use a simple for loop for a more expressive early exit in case of error. let mut winners_indexed: Vec = Vec::with_capacity(winners.len()); for w in winners { if let Some(idx) = snapshot_validators.iter().position(|v| *v == w) { From 98f9c1b67e9c4914981a5051339ff61c50c8b594 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 13 Mar 2020 11:43:13 +0100 Subject: [PATCH 092/106] More minor self reviews --- frame/staking/src/tests.rs | 53 +++++++++++++------------ primitives/api/test/Cargo.toml | 2 +- primitives/arithmetic/fuzzer/Cargo.lock | 18 ++++----- primitives/arithmetic/fuzzer/Cargo.toml | 2 +- primitives/phragmen/Cargo.toml | 2 +- primitives/phragmen/src/helpers.rs | 2 +- 6 files changed, 41 insertions(+), 38 deletions(-) diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index a3425b6ad4f81..7e642ade3f4ec 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2739,7 +2739,6 @@ fn remove_multi_deferred() { let slashes = ::UnappliedSlashes::get(&1); assert_eq!(slashes.len(), 2); - println!("Slashes: {:?}", slashes); assert_eq!(slashes[0].validator, 21); assert_eq!(slashes[1].validator, 42); }) @@ -2858,12 +2857,6 @@ mod offchain_phragmen { assert!(Staking::snapshot_nominators().is_none()); assert!(Staking::snapshot_validators().is_none()); - run_to_block(18); - assert_session_era!(1, 0); - - run_to_block(27); - assert_session_era!(2, 0); - run_to_block(36); assert_session_era!(3, 0); @@ -2944,13 +2937,14 @@ mod offchain_phragmen { assert!(Staking::snapshot_validators().is_some()); assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); - // nominate more than the limit + // validate more than the limit let limit: NominatorIndex = ValidatorIndex::max_value() as NominatorIndex + 1; let ctrl = 1_000_000; for i in 0..limit { bond_validator((1000 + i).into(), (1000 + i + ctrl).into(), 100); } + // window stays closed since no snapshot was taken. run_to_block(27); assert!(Staking::snapshot_validators().is_none()); assert_eq!(Staking::era_election_status(), ElectionStatus::Closed); @@ -3107,7 +3101,7 @@ mod offchain_phragmen { Origin::signed(10), winners, compact, - score + score, )); // a bad solution @@ -3137,7 +3131,7 @@ mod offchain_phragmen { Origin::signed(10), winners, compact, - score + score, )); // a better solution @@ -3146,13 +3140,12 @@ mod offchain_phragmen { Origin::signed(10), winners, compact, - score + score, )); }) } #[test] - #[allow(deprecated)] fn offchain_worker_runs_when_window_open() { // at the end of the first finalized block with ElectionStatus::open(_), it should execute. let mut ext = ExtBuilder::default() @@ -3185,8 +3178,7 @@ mod offchain_phragmen { priority: 1125, // the proposed slot stake. requires: vec![], provides: vec![(Staking::current_era(), signing_key).encode()], - longevity: TryInto::::try_into(::ElectionLookahead::get()) - .unwrap_or(150_u64), + longevity: 3, propagate: true, }) ) @@ -3194,7 +3186,6 @@ mod offchain_phragmen { } #[test] - #[allow(deprecated)] fn mediocre_submission_from_authority_is_early_rejected() { let mut ext = ExtBuilder::default() .offchain_phragmen_ext() @@ -3209,7 +3200,7 @@ mod offchain_phragmen { Origin::signed(10), winners, compact, - score + score, ),); // now run the offchain worker in the same chain state. @@ -3323,7 +3314,7 @@ mod offchain_phragmen { Origin::signed(10), winners, compact, - score + score, ),); }) } @@ -3414,7 +3405,7 @@ mod offchain_phragmen { // similar to the test that raises `PhragmenBogusNomination`. ExtBuilder::default() .offchain_phragmen_ext() - .validator_count(2) + .validator_count(2) // we select only 2. .has_stakers(false) .build() .execute_with(|| { @@ -3426,6 +3417,8 @@ mod offchain_phragmen { let (compact, winners, score) = prepare_submission_with(true, |a| { a.iter_mut() .find(|x| x.who == 5) + // all 3 cannot be among the winners. Although, all of them are validator + // candidates. .map(|x| x.distribution = vec![(21, 50), (41, 30), (31, 20)]); }); @@ -3535,8 +3528,8 @@ mod offchain_phragmen { #[test] fn invalid_phragmen_result_invalid_target_stealing() { - // A valid voter who voted for someone who is a candidate, but is actually NOT nominated by - // this nominator. + // A valid voter who voted for someone who is a candidate, and is a correct winner, but is + // actually NOT nominated by this nominator. ExtBuilder::default() .offchain_phragmen_ext() .validator_count(4) @@ -3611,12 +3604,12 @@ mod offchain_phragmen { ); }); - // can also be submitted. + // can be submitted. assert_ok!(Staking::submit_election_solution( Origin::signed(10), winners, compact, - score + score, )); // a wrong solution. @@ -3628,6 +3621,7 @@ mod offchain_phragmen { }); }); + // is rejected. assert_noop!( Staking::submit_election_solution(Origin::signed(10), winners, compact, score), Error::::PhragmenSlashedNomination, @@ -3658,7 +3652,7 @@ mod offchain_phragmen { } #[test] - fn session_handler_work() { + fn session_keys_are_set() { let mut ext = ExtBuilder::default() .offchain_phragmen_ext() .validator_count(4) @@ -3711,8 +3705,9 @@ mod offchain_phragmen { use sp_runtime::offchain::storage::StorageValueRef; let storage = StorageValueRef::persistent(&OFFCHAIN_HEAD_DB); - // first run -- ok run_to_block(12); + + // first run -- ok assert_eq!( offchain_election::set_check_offchain_execution_status::(12), Ok(()), @@ -3725,11 +3720,19 @@ mod offchain_phragmen { Err("recently executed."), ); - // a fork like situation -- re-execute 12. But it won't go through. + // a fork like situation -- re-execute 10, 11, 12. But it won't go through. + assert_eq!( + offchain_election::set_check_offchain_execution_status::(10), + Err("fork."), + ); assert_eq!( offchain_election::set_check_offchain_execution_status::(11), Err("fork."), ); + assert_eq!( + offchain_election::set_check_offchain_execution_status::(12), + Err("recently executed."), + ); }) } diff --git a/primitives/api/test/Cargo.toml b/primitives/api/test/Cargo.toml index 3b41e28cf3b2b..bd2a25d5505f8 100644 --- a/primitives/api/test/Cargo.toml +++ b/primitives/api/test/Cargo.toml @@ -24,7 +24,7 @@ rustversion = "1.0.0" [dev-dependencies] criterion = "0.3.0" substrate-test-runtime-client = { version = "2.0.0-dev", path = "../../../test-utils/runtime/client" } -sp-core = { version = "2.0.0-alpha.1", path = "../../core" } +sp-core = { version = "2.0.0-alpha.2", path = "../../core" } [[bench]] name = "bench" diff --git a/primitives/arithmetic/fuzzer/Cargo.lock b/primitives/arithmetic/fuzzer/Cargo.lock index a393fab576e27..c7b703c139127 100644 --- a/primitives/arithmetic/fuzzer/Cargo.lock +++ b/primitives/arithmetic/fuzzer/Cargo.lock @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "1.1.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507" +checksum = "f509c5e67ca0605ee17dcd3f91ef41cadd685c75a298fb6261b781a5acb3f910" dependencies = [ "arrayvec", "bitvec", @@ -171,9 +171,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34e513ff3e406f3ede6796dcdc83d0b32ffb86668cea1ccf7363118abeb00476" +checksum = "5a0ec292e92e8ec7c58e576adacc1e3f399c597c8f263c42f18420abe58e7245" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -218,9 +218,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" dependencies = [ "proc-macro2", ] @@ -294,7 +294,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.3" dependencies = [ "integer-sqrt", "num-traits", @@ -317,7 +317,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.3" dependencies = [ "proc-macro2", "quote", @@ -326,7 +326,7 @@ dependencies = [ [[package]] name = "sp-std" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.3" [[package]] name = "static_assertions" diff --git a/primitives/arithmetic/fuzzer/Cargo.toml b/primitives/arithmetic/fuzzer/Cargo.toml index ae83e0e3df212..a21ca43904c41 100644 --- a/primitives/arithmetic/fuzzer/Cargo.toml +++ b/primitives/arithmetic/fuzzer/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" license = "GPL-3.0" [dependencies] -sp-arithmetic = { version = "2.0.0-alpha.1", path = ".." } +sp-arithmetic = { version = "2.0.0-alpha.2", path = ".." } honggfuzz = "0.5" primitive-types = "0.6.2" num-bigint = "0.2" diff --git a/primitives/phragmen/Cargo.toml b/primitives/phragmen/Cargo.toml index bb89de218d345..963812af0a772 100644 --- a/primitives/phragmen/Cargo.toml +++ b/primitives/phragmen/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" license = "GPL-3.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" -description = "PHRAGMENT primitives" +description = "Phragmen primitives" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } diff --git a/primitives/phragmen/src/helpers.rs b/primitives/phragmen/src/helpers.rs index b05bd41ec518e..27f51b4a05fe2 100644 --- a/primitives/phragmen/src/helpers.rs +++ b/primitives/phragmen/src/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// Copyright 2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify From 199d8fba648f505ff0da3b99e1633860d8398bab Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 13 Mar 2020 14:53:24 +0100 Subject: [PATCH 093/106] Final nits --- primitives/phragmen/src/lib.rs | 25 ++++++++++++------------- primitives/phragmen/src/reduce.rs | 14 ++++++++------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index 3f7f0523bda16..620f36f56934e 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -188,6 +188,8 @@ where if p == Bounded::min_value() { None } else { + // NOTE: this mul impl will always round to the nearest number, so we might both + // overflow and underflow. let distribution_stake = p * stake; // defensive only. We assume that balance cannot exceed extended balance. sum = sum.saturating_add(distribution_stake); @@ -207,10 +209,6 @@ where if let Some(last) = distribution.last_mut() { last.1 = last.1.saturating_sub(excess); } - } else if let Some(excess) = sum.checked_sub(stake) { - if let Some(last) = distribution.last_mut() { - last.1 -= excess; - } } } @@ -269,15 +267,15 @@ impl StakedAssignment { if fill { if let Some(leftover) = accuracy.checked_sub(sum) { if let Some(last) = distribution.last_mut() { - last.1 = last - .1 - .saturating_add(T::from_parts(leftover.saturated_into())); + last.1 = last.1.saturating_add( + T::from_parts(leftover.saturated_into()) + ); } } else if let Some(excess) = sum.checked_sub(accuracy) { if let Some(last) = distribution.last_mut() { - last.1 = last - .1 - .saturating_sub(T::from_parts(excess.saturated_into())); + last.1 = last.1.saturating_sub( + T::from_parts(excess.saturated_into()) + ); } } } @@ -554,9 +552,10 @@ pub fn elect( /// }, /// } /// ``` -/// The second returned flag indicates the number of edges who corresponded to an actual winner from -/// the given winner set. A value in this place larger than 0 indicates a potentially faulty -/// assignment. +/// +/// 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. /// /// `O(E)` where `E` is the total number of edges. pub fn build_support_map( diff --git a/primitives/phragmen/src/reduce.rs b/primitives/phragmen/src/reduce.rs index 334f49f15c057..54a71a7ff29aa 100644 --- a/primitives/phragmen/src/reduce.rs +++ b/primitives/phragmen/src/reduce.rs @@ -41,7 +41,6 @@ //! > designed to detect and remove cycles of length four exclusively. This pre-computation is //! > optional, and if we skip it then the running time becomes `O (|E_w| ⋅ m), so the //! > pre-computation makes sense only if `m >> k` and `|E_w| >> m^2`. -//! > //! //! ### Resources: //! @@ -297,10 +296,13 @@ fn reduce_4(assignments: &mut Vec>) -> u32 { (false, true) => { *other_who = who.clone(); } - (true, false) => {} // nothing, other_who can stay there. + (true, false) => { + // nothing, other_who can stay there. + } (true, true) => { + // remove and don't replace entry.remove(); - } // remove and don't replace + } (false, false) => { // Neither of the edges was removed? impossible. debug_assert!(false, "Duplicate voter (or other corrupt input)."); @@ -642,9 +644,9 @@ fn reduce_all(assignments: &mut Vec>) -> u32 /// /// Returns the number of edges removed. /// -/// It is strictly assumed that the `who` attribute of all provided assignments are unique. The -/// result will most likely be corrupt otherwise. Furthermore, if the _distribution vector_ contains -/// duplicate ids, only the first instance is ever updates. +/// IMPORTANT: It is strictly assumed that the `who` attribute of all provided assignments are +/// unique. The result will most likely be corrupt otherwise. Furthermore, if the _distribution +/// vector_ contains duplicate ids, only the first instance is ever updates. /// /// O(min{ |Ew| ⋅ k + m3 , |Ew| ⋅ m }) pub fn reduce(assignments: &mut Vec>) -> u32 where { From 202db20fa97288ab68d1972982aac1943b59914d Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 19 Mar 2020 14:59:35 +0100 Subject: [PATCH 094/106] Some merge fixes. --- frame/offences/src/lib.rs | 6 ++++++ frame/staking/src/lib.rs | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 5abd781830bb1..a654a9972df0b 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -95,6 +95,12 @@ decl_module! { pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; + fn on_runtime_upgrade() { + Reports::::remove_all(); + ConcurrentReportsIndex::::remove_all(); + ReportsByKindIndex::remove_all(); + } + fn on_initialize(now: T::BlockNumber) { // only decode storage if we can actually submit anything again. if T::OnOffenceHandler::can_report() { diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index c2571a498d098..11524a71a2c1b 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -1936,8 +1936,6 @@ impl Module { >::kill(); } - // MUTABLES (DANGEROUS) - fn do_payout_nominator(who: T::AccountId, era: EraIndex, validators: Vec<(T::AccountId, u32)>) -> DispatchResult { From 8372453a5873506622c87f78f373b260987b5e79 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 20 Mar 2020 09:01:04 +0100 Subject: [PATCH 095/106] opt comment --- frame/offences/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index a654a9972df0b..5fa93ce46fa40 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -107,6 +107,9 @@ decl_module! { >::take() .iter() .for_each(|(o, p, s)| { + // OPTIMIZATION: this function mutates once per loop; we could manage all + // the writes in-memory and mutate only once. Overlay cache should alleviate + // the main bottleneck though. if !Self::report_or_store_offence(o, p, *s) { debug::native::error!( target: "pallet-offences", From 26926053b053227d26468bf824e93b4aadcb97c0 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 20 Mar 2020 10:01:35 +0100 Subject: [PATCH 096/106] Fix build --- frame/staking/src/lib.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 229a60b655970..de7abcf2951d2 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -298,7 +298,6 @@ use sp_staking::{ }; #[cfg(feature = "std")] use sp_runtime::{Serialize, Deserialize}; -<<<<<<< HEAD use frame_system::{ self as system, ensure_signed, ensure_root, ensure_none, offchain::SubmitUnsignedTransaction, @@ -307,11 +306,6 @@ use sp_phragmen::{ ExtendedBalance, Assignment, PhragmenScore, PhragmenResult, build_support_map, evaluate_support, elect, generate_compact_solution_type, is_score_better, VotingLimit, SupportMap, }; -======= -use frame_system::{self as system, ensure_signed, ensure_root}; - -use sp_phragmen::ExtendedBalance; ->>>>>>> 653c89f6bb4e069bdf2af8be97b7c6b01bc62921 const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; const STAKING_ID: LockIdentifier = *b"staking "; From 2cf090cbdd76bfd3bc4d7a6cd2cab973ae4b73ca Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 20 Mar 2020 10:07:05 +0100 Subject: [PATCH 097/106] Fix build again. --- frame/staking/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index de7abcf2951d2..2b8d686d6d6f8 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -276,7 +276,7 @@ use frame_support::{ dispatch::{IsSubType, DispatchResult}, traits::{ Currency, LockIdentifier, LockableCurrency, WithdrawReasons, OnUnbalanced, Imbalance, Get, - Time, EstimateNextNewSession, MigrateAccount, + Time, EstimateNextNewSession, } }; use pallet_session::historical; From 18558af0ef6936db3214078906ecde607aafb19c Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Sun, 22 Mar 2020 15:58:19 +0100 Subject: [PATCH 098/106] Update frame/staking/fuzz/fuzz_targets/submit_solution.rs Co-Authored-By: Gavin Wood --- frame/staking/fuzz/fuzz_targets/submit_solution.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/staking/fuzz/fuzz_targets/submit_solution.rs b/frame/staking/fuzz/fuzz_targets/submit_solution.rs index bbc480f116306..5d1fcf1d7ea85 100644 --- a/frame/staking/fuzz/fuzz_targets/submit_solution.rs +++ b/frame/staking/fuzz/fuzz_targets/submit_solution.rs @@ -32,7 +32,7 @@ mod mock; #[allow(dead_code)] #[derive(Debug, Clone, Copy)] enum Mode { - /// Initial submission. This will be rather cheap + /// Initial submission. This will be rather cheap. InitialSubmission, /// A better submission that will replace the previous ones. This is the most expensive. StrongerSubmission, From 6f9a2c537650f3a55324f03d9680696ad7e46c7f Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Mon, 23 Mar 2020 08:03:08 +0100 Subject: [PATCH 099/106] Update frame/staking/src/slashing.rs Co-Authored-By: Gavin Wood --- frame/staking/src/slashing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/staking/src/slashing.rs b/frame/staking/src/slashing.rs index 22ed739740fe0..3d5ea3bad5332 100644 --- a/frame/staking/src/slashing.rs +++ b/frame/staking/src/slashing.rs @@ -16,7 +16,7 @@ //! A slashing implementation for NPoS systems. //! -//! For the purposes of the economic model, it is easiest to think of each validator of a nominator +//! For the purposes of the economic model, it is easiest to think of each validator as a nominator //! which nominates only its own identity. //! //! The act of nomination signals intent to unify economic identity with the validator - to take From cd3df8016ea49b410110905c4f2edfd25b481439 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Mon, 23 Mar 2020 08:03:21 +0100 Subject: [PATCH 100/106] Update frame/staking/src/offchain_election.rs Co-Authored-By: Gavin Wood --- frame/staking/src/offchain_election.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index e945008a55499..02f6ce8ef7a12 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -162,8 +162,7 @@ pub fn prepare_submission( assignments: Vec>, winners: Vec<(T::AccountId, ExtendedBalance)>, do_reduce: bool, -) -> Result<(Vec, CompactAssignments, PhragmenScore), OffchainElectionError> -where +) -> Result<(Vec, CompactAssignments, PhragmenScore), OffchainElectionError> where ExtendedBalance: From<::Inner>, { // make sure that the snapshot is available. From 11530841039606396111920d9e7987b1f370a789 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 23 Mar 2020 08:30:06 +0100 Subject: [PATCH 101/106] Fix review comments --- frame/offences/src/lib.rs | 30 ++++++++++++++------------ frame/offences/src/tests.rs | 12 +++++------ frame/staking/src/offchain_election.rs | 14 ++++++++++-- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 5fa93ce46fa40..262d0fbd69822 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -42,6 +42,13 @@ type OpaqueTimeSlot = Vec; /// A type alias for a report identifier. type ReportIdOf = ::Hash; +/// Type of data stored as a deferred offence +type DeferredOffenceOf = ( + Vec::AccountId, ::IdentificationTuple>>, + Vec, + SessionIndex, +); + /// Offences trait pub trait Trait: frame_system::Trait { /// The overarching event type. @@ -61,11 +68,7 @@ decl_storage! { /// Deferred reports that have been rejected by the offence handler and need to be submitted /// at a later time. - DeferredOffences get(deferred_reports): Vec<( - Vec>, - Vec, - SessionIndex, - )>; + DeferredOffences get(deferred_offences): Vec>; /// A vector of reports of the same kind that happened at the same time slot. ConcurrentReportsIndex: @@ -104,20 +107,19 @@ decl_module! { fn on_initialize(now: T::BlockNumber) { // only decode storage if we can actually submit anything again. if T::OnOffenceHandler::can_report() { - >::take() - .iter() - .for_each(|(o, p, s)| { - // OPTIMIZATION: this function mutates once per loop; we could manage all - // the writes in-memory and mutate only once. Overlay cache should alleviate - // the main bottleneck though. - if !Self::report_or_store_offence(o, p, *s) { + >::mutate(|deferred| { + // keep those that fail to be reported again. An error log is emitted here; this + // should not happen if staking's `can_report` is implemented properly. + deferred.retain(|(o, p, s)| { + T::OnOffenceHandler::on_offence(&o, &p, *s).map_err(|_| { debug::native::error!( target: "pallet-offences", "re-submitting a deferred slash returned Err at {}. This should not happen with pallet-staking", now, ); - } - }); + }).is_err() + }) + }) } } } diff --git a/frame/offences/src/tests.rs b/frame/offences/src/tests.rs index a99928b323a97..5a240799c484a 100644 --- a/frame/offences/src/tests.rs +++ b/frame/offences/src/tests.rs @@ -165,7 +165,7 @@ fn doesnt_deposit_event_for_dups() { System::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::offences(crate::Event::Offence(KIND, time_slot.encode()), true), + event: TestEvent::offences(crate::Event::Offence(KIND, time_slot.encode(), true)), topics: vec![], }] ); @@ -225,12 +225,12 @@ fn should_queue_and_resubmit_rejected_offence() { offenders: vec![5], }; Offences::report_offence(vec![], offence).unwrap(); - assert_eq!(Offences::deferred_reports().len(), 1); + assert_eq!(Offences::deferred_offences().len(), 1); // event also indicates unapplied. assert_eq!( System::events(), vec![EventRecord { - phase: Phase::ApplyExtrinsic(0), + phase: Phase::Initialization, event: TestEvent::offences(crate::Event::Offence(KIND, 42u128.encode(), false)), topics: vec![], }] @@ -246,7 +246,7 @@ fn should_queue_and_resubmit_rejected_offence() { offenders: vec![5], }; Offences::report_offence(vec![], offence).unwrap(); - assert_eq!(Offences::deferred_reports().len(), 2); + assert_eq!(Offences::deferred_offences().len(), 2); set_can_report(true); @@ -257,9 +257,9 @@ fn should_queue_and_resubmit_rejected_offence() { offenders: vec![5], }; Offences::report_offence(vec![], offence).unwrap(); - assert_eq!(Offences::deferred_reports().len(), 2); + assert_eq!(Offences::deferred_offences().len(), 2); Offences::on_initialize(3); - assert_eq!(Offences::deferred_reports().len(), 0); + assert_eq!(Offences::deferred_offences().len(), 0); }) } diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index e945008a55499..393ecab8165d8 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -31,6 +31,7 @@ use sp_runtime::PerThing; use sp_runtime::{RuntimeAppPublic, RuntimeDebug}; use sp_std::{convert::TryInto, prelude::*}; +/// Error types related to the offchain election machinery. #[derive(RuntimeDebug)] pub enum OffchainElectionError { /// No signing key has been found on the current node that maps to a validators. This node @@ -55,7 +56,7 @@ impl From for OffchainElectionError { } } -/// The type of signature data encoded with the unsigned submission +/// The type of signature data encoded with the unsigned submission. pub(crate) type SignaturePayload<'a> = ( &'a [ValidatorIndex], &'a CompactAssignments, @@ -63,9 +64,18 @@ pub(crate) type SignaturePayload<'a> = ( &'a u32, ); +/// Storage key used to store the persistent offchain worker status. pub(crate) const OFFCHAIN_HEAD_DB: &[u8] = b"parity/staking-election/"; -const OFFCHAIN_REPEAT: u32 = 5; +/// 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; +/// 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> { From 34e3d7b1d1d2a7d4b7375ba88677b8ad88fb25a0 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 23 Mar 2020 17:43:50 +0100 Subject: [PATCH 102/106] fix test --- frame/babe/src/mock.rs | 4 ++-- frame/babe/src/tests.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 9a26b6e83ba07..c6c568b040619 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -111,7 +111,7 @@ pub fn new_test_ext(authorities: Vec) -> sp_io::TestExternalit } pub fn go_to_block(n: u64, s: u64) { - let pre_digest = make_pre_digest(0, s, [1; 32], [0xff; 64]); + let pre_digest = make_pre_digest(0, s, RawVRFOutput([1; 32]), RawVRFProof([0xff; 64])); System::initialize(&n, &Default::default(), &Default::default(), &pre_digest, InitKind::Full); System::set_block_number(n); if s > 1 { @@ -130,7 +130,7 @@ pub fn progress_to_block(n: u64) { } } -fn make_pre_digest( +pub fn make_pre_digest( authority_index: sp_consensus_babe::AuthorityIndex, slot_number: sp_consensus_babe::SlotNumber, vrf_output: RawVRFOutput, diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 216eade0074cd..54fe3367fc411 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -20,6 +20,7 @@ use super::*; use mock::*; use sp_runtime::traits::OnFinalize; use pallet_session::ShouldEndSession; +use sp_consensus_vrf::schnorrkel::{RawVRFOutput, RawVRFProof}; const EMPTY_RANDOMNESS: [u8; 32] = [ 74, 25, 49, 128, 53, 97, 244, 49, From ee3c4cb450643f80f2400bfabace8ec9d05517bb Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 24 Mar 2020 17:06:44 +0100 Subject: [PATCH 103/106] =?UTF-8?q?=3D=3D=3D=20=F0=9F=94=91=20Revamp=20wit?= =?UTF-8?q?hout=20staking=20key.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/node/cli/src/chain_spec.rs | 18 +-- bin/node/runtime/src/lib.rs | 6 +- bin/node/testing/src/keyring.rs | 1 - bin/utils/chain-spec-builder/src/main.rs | 7 +- frame/staking/src/lib.rs | 121 ++++----------------- frame/staking/src/mock.rs | 83 +------------- frame/staking/src/offchain_election.rs | 85 ++++----------- frame/staking/src/tests.rs | 71 +----------- primitives/consensus/vrf/src/schnorrkel.rs | 3 +- 9 files changed, 63 insertions(+), 332 deletions(-) diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index f69d809c69147..94f088ac14290 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -32,7 +32,6 @@ use sc_telemetry::TelemetryEndpoints; use grandpa_primitives::{AuthorityId as GrandpaId}; use sp_consensus_babe::{AuthorityId as BabeId}; use pallet_im_online::sr25519::{AuthorityId as ImOnlineId}; -use pallet_staking::sr25519::{AuthorityId as StakingId}; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_runtime::{Perbill, traits::{Verify, IdentifyAccount}}; @@ -71,9 +70,8 @@ fn session_keys( babe: BabeId, im_online: ImOnlineId, authority_discovery: AuthorityDiscoveryId, - staking: StakingId, ) -> SessionKeys { - SessionKeys { grandpa, babe, im_online, authority_discovery, staking } + SessionKeys { grandpa, babe, im_online, authority_discovery } } fn staging_testnet_config_genesis() -> GenesisConfig { @@ -83,7 +81,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig { // and // for i in 1 2 3 4 ; do for j in session; do subkey --ed25519 inspect "$secret"//fir//$j//$i; done; done - let initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId, StakingId)> = vec![( + let initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId)> = vec![( // 5Fbsd6WXDGiLTxunqeK5BATNiocfCqu9bS1yArVjCgeBLkVy hex!["9c7a2ee14e565db0c69f78c7b4cd839fbf52b607d867e9e9c5a79042898a0d12"].into(), // 5EnCiV7wSHeNhjW3FSUwiJNkcc2SBkPLn5Nj93FmbLtBjQUq @@ -96,8 +94,6 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(), // 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8 hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(), - // 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8 - hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(), ),( // 5ERawXCzCWkjVq3xz1W5KGNtVx2VdefvZ62Bw1FEuZW4Vny2 hex!["68655684472b743e456907b398d3a44c113f189e56d1bbfd55e889e295dfde78"].into(), @@ -111,8 +107,6 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(), // 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(), - // 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ - hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(), ),( // 5DyVtKWPidondEu8iHZgi6Ffv9yrJJ1NDNLom3X9cTDi98qp hex!["547ff0ab649283a7ae01dbc2eb73932eba2fb09075e9485ff369082a2ff38d65"].into(), @@ -126,8 +120,6 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(), // 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(), - // 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH - hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(), ),( // 5HYZnKWe5FVZQ33ZRJK1rG3WaLMztxWrrNDb1JRwaHHVWyP9 hex!["f26cdb14b5aec7b2789fd5ca80f979cef3761897ae1f37ffb3e154cbcc1c2663"].into(), @@ -141,8 +133,6 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(), // 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(), - // 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x - hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(), )]; // generated with secret: subkey inspect "$secret"/fir @@ -198,7 +188,6 @@ pub fn get_authority_keys_from_seed(seed: &str) -> ( BabeId, ImOnlineId, AuthorityDiscoveryId, - StakingId, ) { ( get_account_id_from_seed::(&format!("{}//stash", seed)), @@ -207,7 +196,6 @@ pub fn get_authority_keys_from_seed(seed: &str) -> ( get_from_seed::(seed), get_from_seed::(seed), get_from_seed::(seed), - get_from_seed::(seed), ) } @@ -220,7 +208,6 @@ pub fn testnet_genesis( BabeId, ImOnlineId, AuthorityDiscoveryId, - StakingId, )>, root_key: AccountId, endowed_accounts: Option>, @@ -268,7 +255,6 @@ pub fn testnet_genesis( x.3.clone(), x.4.clone(), x.5.clone(), - x.6.clone(), )) }).collect::>(), }), diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index ef005c3daa3c3..027016d35dffe 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -59,7 +59,7 @@ pub use pallet_timestamp::Call as TimestampCall; pub use pallet_balances::Call as BalancesCall; pub use pallet_contracts::Gas; pub use frame_support::StorageValue; -pub use pallet_staking::{StakerStatus, LockStakingStatus, sr25519::AuthorityId as StakingId}; +pub use pallet_staking::{StakerStatus, LockStakingStatus}; /// Implementations of some helper traits passed into runtime modules as associated types. pub mod impls; @@ -286,7 +286,6 @@ impl_opaque_keys! { pub babe: Babe, pub im_online: ImOnline, pub authority_discovery: AuthorityDiscovery, - pub staking: Staking, } } @@ -349,8 +348,7 @@ impl pallet_staking::Trait for Runtime { type NextNewSession = Session; type ElectionLookahead = ElectionLookahead; type Call = Call; - type SubmitTransaction = TransactionSubmitterOf; - type KeyType = StakingId; + type SubmitTransaction = TransactionSubmitterOf<()>; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; } diff --git a/bin/node/testing/src/keyring.rs b/bin/node/testing/src/keyring.rs index 3154ad719f1e9..5fa1e48b03218 100644 --- a/bin/node/testing/src/keyring.rs +++ b/bin/node/testing/src/keyring.rs @@ -62,7 +62,6 @@ pub fn to_session_keys( babe: sr25519_keyring.to_owned().public().into(), im_online: sr25519_keyring.to_owned().public().into(), authority_discovery: sr25519_keyring.to_owned().public().into(), - staking: sr25519_keyring.to_owned().public().into(), } } diff --git a/bin/utils/chain-spec-builder/src/main.rs b/bin/utils/chain-spec-builder/src/main.rs index 1e7fada483b70..3673909706cdb 100644 --- a/bin/utils/chain-spec-builder/src/main.rs +++ b/bin/utils/chain-spec-builder/src/main.rs @@ -141,7 +141,7 @@ fn generate_authority_keys_and_store( None, ).map_err(|err| err.to_string())?; - let (_, _, grandpa, babe, im_online, authority_discovery, staking) = + let (_, _, grandpa, babe, im_online, authority_discovery) = chain_spec::get_authority_keys_from_seed(seed); let insert_key = |key_type, public| { @@ -171,11 +171,6 @@ fn generate_authority_keys_and_store( sp_core::crypto::key_types::AUTHORITY_DISCOVERY, authority_discovery.as_slice(), )?; - - insert_key( - sp_core::crypto::key_types::STAKING, - staking.as_slice(), - )?; } Ok(()) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 38842ee62cf30..1751d689b964b 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -271,7 +271,7 @@ use sp_std::{ }; use codec::{HasCompact, Encode, Decode}; use frame_support::{ - decl_module, decl_event, decl_storage, ensure, decl_error, debug, Parameter, IterableStorageMap, + decl_module, decl_event, decl_storage, ensure, decl_error, debug, IterableStorageMap, weights::SimpleDispatchInfo, dispatch::{IsSubType, DispatchResult}, traits::{ @@ -281,15 +281,14 @@ use frame_support::{ }; use pallet_session::historical; use sp_runtime::{ - Perbill, PerU16, PerThing, RuntimeDebug, RuntimeAppPublic, + Perbill, PerU16, PerThing, RuntimeDebug, curve::PiecewiseLinear, traits::{ Convert, Zero, StaticLookup, CheckedSub, Saturating, SaturatedConversion, AtLeast32Bit, - EnsureOrigin, Member, SignedExtension, + EnsureOrigin, SignedExtension, }, transaction_validity::{ TransactionValidityError, TransactionValidity, ValidTransaction, InvalidTransaction, - UnknownTransaction, }, }; use sp_staking::{ @@ -367,25 +366,6 @@ type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; type MomentOf = <::Time as Time>::Moment; -/// Staking's key type used for submitting unsigned solutions. -pub mod sr25519 { - mod app_sr25519 { - use sp_application_crypto::{app_crypto, key_types::STAKING, sr25519}; - app_crypto!(sr25519, STAKING); - } - - sp_application_crypto::with_pair! { - /// A staking keypair using sr25519 as its crypto. - pub type AuthorityPair = app_sr25519::Pair; - } - - /// Staking signature using sr25519 as its crypto. - pub type AuthoritySignature = app_sr25519::Signature; - - /// Staking identifier using sr25519 as its crypto. - pub type AuthorityId = app_sr25519::Public; -} - /// Reward points of an era. Used to split era total payout between validators. /// /// This points will be used to reward validators and their respective nominators. @@ -793,9 +773,6 @@ pub trait Trait: frame_system::Trait { /// A transaction submitter. type SubmitTransaction: SubmitUnsignedTransaction::Call>; - /// Key type used to sign and verify transaction. - type KeyType: RuntimeAppPublic + Member + Parameter + Default; - /// The maximum number of nominator rewarded for each validator. /// /// For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can claim @@ -998,9 +975,6 @@ decl_storage! { /// have a value when [`EraElectionStatus`] == `ElectionStatus::Open(_)`. pub SnapshotNominators get(fn snapshot_nominators): Option>; - /// The current set of staking keys. - pub Keys get(fn keys): Vec; - /// 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. @@ -1831,19 +1805,16 @@ decl_module! { )? } - /// Unsigned version of `submit_election_solution`. A signature and claimed validator index - /// must be provided. For the call to be validated, the signature must contain the signed - /// payload of the first three elements, and match the public key at the claimed validator - /// index (using the staking key). + /// Unsigned version of `submit_election_solution`. + /// + /// Note that this must pass the [`ValidateUnsigned`] check which only allows transactions + /// from the local node to be included. #[weight = SimpleDispatchInfo::FixedNormal(100_000_000)] pub fn submit_election_solution_unsigned( origin, winners: Vec, compact_assignments: CompactAssignments, score: PhragmenScore, - // already used and checked in ValidateUnsigned. - _validator_index: u32, - _signature: ::Signature, ) { ensure_none(origin)?; Self::check_and_replace_solution( @@ -2513,7 +2484,7 @@ impl Module { fn do_phragmen() -> Option> { let mut all_nominators: Vec<(T::AccountId, BalanceOf, Vec)> = Vec::new(); let mut all_validators = Vec::new(); - for (validator, preference) in >::iter() { + for (validator, _) in >::iter() { // append self vote let self_vote = (validator.clone(), Self::slashable_balance_of(&validator), vec![validator.clone()]); all_nominators.push(self_vote); @@ -2878,33 +2849,6 @@ impl ReportOffence } } -impl sp_runtime::BoundToRuntimeAppPublic for Module { - type Public = T::KeyType; -} - -impl pallet_session::OneSessionHandler for Module { - type Key = T::KeyType; - - fn on_genesis_session<'a, I: 'a>(validators: I) - where I: Iterator - { - assert!(Self::keys().is_empty(), "Keys are already initialized!"); - >::put(validators.map(|x| x.1).collect::>()); - } - - fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, _queued_validators: I) - where I: Iterator - { - // Update they keys - >::put(validators.map(|x| x.1).collect::>()); - } - - fn on_before_session_ending() {} - - fn on_disabled(_i: usize) {} -} - - /// Disallows any transactions that change the election result to be submitted after the election /// window is open. #[derive(Encode, Decode, Clone, Eq, PartialEq)] @@ -2980,15 +2924,16 @@ impl frame_support::unsigned::ValidateUnsigned for Module { type Call = Call; fn validate_unsigned(call: &Self::Call) -> TransactionValidity { if let Call::submit_election_solution_unsigned( - winners, - compact, + _, + _, score, - validator_index, - signature, ) = call { - use offchain_election::SignaturePayload; + use offchain_election::DEFAULT_LONGEVITY; - // discard early solution + // discard solution not coming from the local OCW. + // TODO: ... + + // discard early solution. if Self::era_election_status().is_closed() { debug::native::debug!( target: "staking", @@ -2997,7 +2942,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { return InvalidTransaction::Future.into(); } - // discard weak solution + // discard weak solution. if let Some(queued_score) = Self::queued_score() { if !is_score_better(queued_score, *score) { debug::native::debug!( @@ -3008,41 +2953,23 @@ impl frame_support::unsigned::ValidateUnsigned for Module { } } - // check signature - let payload: SignaturePayload = ( - winners, - compact, - score, - validator_index, - ); - - let all_keys = Self::keys(); - let validator_key = all_keys.get(*validator_index as usize) - // validator index is incorrect -- no key corresponds to it. - .ok_or(TransactionValidityError::Unknown(UnknownTransaction::CannotLookup.into()))?; - - let signature_valid = payload.using_encoded(|encoded_payload| { - validator_key.verify(&encoded_payload, &signature) - }); - - if !signature_valid { - return InvalidTransaction::BadProof.into(); - } - Ok(ValidTransaction { // The higher the score[0], the better a solution is. priority: score[0].saturated_into(), + // no requires. requires: vec![], - // a duplicate solution from the same validator within an era cannot be accepted - // at the transaction pool layer. - provides: vec![(Self::current_era(), validator_key).encode()], + // Defensive only. A single solution can exist in the pool per era. Each validator + // will run OCW at most once per era, hence there should never exist more than one + // transaction anyhow. + provides: vec![Self::current_era().encode()], // Note: this can be more accurate in the future. We do something like // `era_end_block - current_block` but that is not needed now as we eagerly run // offchain workers now and the above should be same as `T::ElectionLookahead` // without the need to query more storage in the validation phase. If we randomize // offchain worker, then we might re-consider this. - longevity: TryInto::::try_into(T::ElectionLookahead::get()).unwrap_or(150_u64), - propagate: true, + longevity: TryInto::::try_into(T::ElectionLookahead::get()).unwrap_or(DEFAULT_LONGEVITY), + // We don't propagate this. This can never the validated at a remote node. + propagate: false, }) } else { InvalidTransaction::Call.into() diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 9998f9bb9e8f6..baac424915df1 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -20,8 +20,7 @@ use std::{collections::{HashSet, HashMap}, cell::RefCell}; use sp_runtime::Perbill; use sp_runtime::curve::PiecewiseLinear; use sp_runtime::traits::{ - IdentityLookup, Convert, OnInitialize, OnFinalize, SaturatedConversion, - Extrinsic as ExtrinsicT, Zero + IdentityLookup, Convert, OnInitialize, OnFinalize, SaturatedConversion, Zero, }; use sp_runtime::testing::{Header, TestXt, UintAuthorityId}; use sp_staking::{SessionIndex, offence::{OffenceDetails, OnOffenceHandler}}; @@ -32,7 +31,7 @@ use frame_support::{ traits::{Currency, Get, FindAuthor}, weights::Weight, }; -use frame_system::offchain::{CreateTransaction, Signer, TransactionSubmitter}; +use frame_system::offchain::TransactionSubmitter; use sp_io; use sp_phragmen::{ build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment, PhragmenScore, @@ -49,8 +48,6 @@ pub(crate) type AccountIndex = u64; pub(crate) type BlockNumber = u64; pub(crate) type Balance = u64; -pub const PHRASE: &str = "news slush supreme milk chapter athlete soap sausage put clutch what kitten"; - /// Simple structure that exposes how u64 currency can be represented as... u64. pub struct CurrencyToVoteHandler; impl Convert for CurrencyToVoteHandler { @@ -71,7 +68,6 @@ thread_local! { static SLASH_DEFER_DURATION: RefCell = RefCell::new(0); static ELECTION_LOOKAHEAD: RefCell = RefCell::new(0); static PERIOD: RefCell = RefCell::new(1); - pub(crate) static LOCAL_KEY_ACCOUNT: RefCell = RefCell::new(10); } /// Another session handler struct to test on_disabled. @@ -231,26 +227,16 @@ parameter_types! { pub const UncleGenerations: u64 = 0; pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(25); } - -/// We prefer using the dummy key defined in `mock::dummy_sr25519`, not `crate::sr25519`, since the -/// dummy one gives us some nice helpers and a fake `IdentifyAccount`. -pub struct TestStakingKeys; -impl sp_runtime::BoundToRuntimeAppPublic for TestStakingKeys { - type Public = dummy_sr25519::AuthorityId; -} - sp_runtime::impl_opaque_keys! { pub struct SessionKeys { - pub staking: TestStakingKeys, pub other: OtherSessionHandler, } } - impl pallet_session::Trait for Test { type SessionManager = pallet_session::historical::NoteHistoricalRoot; type Keys = SessionKeys; type ShouldEndSession = pallet_session::PeriodicSessions; - type SessionHandler = (Staking, OtherSessionHandler,); + type SessionHandler = (OtherSessionHandler,); type Event = MetaEvent; type ValidatorId = AccountId; type ValidatorIdOf = crate::StashOf; @@ -310,59 +296,11 @@ impl Trait for Test { type ElectionLookahead = ElectionLookahead; type Call = Call; type SubmitTransaction = SubmitTransaction; - type KeyType = dummy_sr25519::AuthorityId; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; } -pub(crate) mod dummy_sr25519 { - use super::{LOCAL_KEY_ACCOUNT, AccountId}; - - mod app_sr25519 { - use sp_application_crypto::{app_crypto, KeyTypeId, sr25519}; - app_crypto!(sr25519, KeyTypeId(*b"vali")); - } - - pub type AuthoritySignature = app_sr25519::Signature; - pub type AuthorityId = app_sr25519::Public; - - impl sp_runtime::traits::IdentifyAccount for AuthorityId { - type AccountId = u64; - fn into_account(self) -> Self::AccountId { - LOCAL_KEY_ACCOUNT.with(|v| *v.borrow()) - } - } - - pub fn dummy_key_for(x: AccountId) -> AuthorityId { - use sp_core::Pair; - let mut raw_key = [0u8; 32]; - raw_key[0] = x as u8; - let generic_key = sp_core::sr25519::Pair::from_string( - &format!("{}/staking{}", super::PHRASE, x), - None, - ).unwrap().public(); - generic_key.into() - } -} - -impl CreateTransaction for Test { - type Signature = dummy_sr25519::AuthoritySignature; - type Public = dummy_sr25519::AuthorityId; - fn create_transaction>( - call: Call, - _public: Self::Public, - account: AccountId, - _index: AccountIndex, - ) -> Option<( - ::Call, - ::SignaturePayload, - )> { - let extra = (); - Some((call, (account, extra))) - } -} - pub type Extrinsic = TestXt; -type SubmitTransaction = TransactionSubmitter; +type SubmitTransaction = TransactionSubmitter<(), Test, Extrinsic>; pub struct ExtBuilder { session_length: BlockNumber, @@ -378,7 +316,6 @@ pub struct ExtBuilder { num_validators: Option, invulnerables: Vec, has_stakers: bool, - local_key_account: AccountId, } impl Default for ExtBuilder { @@ -397,7 +334,6 @@ impl Default for ExtBuilder { num_validators: None, invulnerables: vec![], has_stakers: true, - local_key_account: 10, } } } @@ -455,15 +391,10 @@ impl ExtBuilder { self.has_stakers = has; self } - pub fn local_key_account(mut self, key: AccountId) -> Self { - self.local_key_account = key; - self - } pub fn offchain_phragmen_ext(self) -> Self { self.session_per_era(4) .session_length(5) .election_lookahead(3) - .local_key_account(11) } pub fn set_associated_constants(&self) { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); @@ -471,7 +402,6 @@ impl ExtBuilder { SESSION_PER_ERA.with(|v| *v.borrow_mut() = self.session_per_era); ELECTION_LOOKAHEAD.with(|v| *v.borrow_mut() = self.election_lookahead); PERIOD.with(|v| *v.borrow_mut() = self.session_length); - LOCAL_KEY_ACCOUNT.with(|v| *v.borrow_mut() = self.local_key_account); } pub fn build(self) -> sp_io::TestExternalities { let _ = env_logger::try_init(); @@ -545,10 +475,7 @@ impl ExtBuilder { keys: validators.iter().map(|x| ( *x, *x, - SessionKeys { - staking: dummy_sr25519::dummy_key_for(*x), - other: UintAuthorityId(*x), - } + SessionKeys { other: UintAuthorityId(*x) } )).collect(), }.assimilate_storage(&mut storage); diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 6753cff7f810d..3536915b00dc4 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -19,8 +19,6 @@ use crate::{ Call, CompactAssignments, Module, NominatorIndex, OffchainAccuracy, Trait, ValidatorIndex, }; -use codec::Encode; -use frame_support::debug; use frame_system::offchain::SubmitUnsignedTransaction; use sp_phragmen::{ build_support_map, evaluate_support, reduce, Assignment, ExtendedBalance, PhragmenResult, @@ -28,20 +26,17 @@ use sp_phragmen::{ }; use sp_runtime::offchain::storage::StorageValueRef; use sp_runtime::PerThing; -use sp_runtime::{RuntimeAppPublic, RuntimeDebug}; +use sp_runtime::RuntimeDebug; use sp_std::{convert::TryInto, prelude::*}; /// Error types related to the offchain election machinery. #[derive(RuntimeDebug)] pub enum OffchainElectionError { - /// No signing key has been found on the current node that maps to a validators. This node - /// should not run the offchain election code. - NoSigningKey, - /// Signing operation failed. - SigningFailed, /// Phragmen election returned None. This means less candidate that minimum number of needed /// validators were present. The chain is in trouble and not much that we can do about it. ElectionFailed, + /// Submission to the transaction pool failed. + PoolSubmissionFailed, /// The snapshot data is not available. SnapshotUnavailable, /// Error from phragmen crate. This usually relates to compact operation. @@ -56,19 +51,13 @@ impl From for OffchainElectionError { } } -/// The type of signature data encoded with the unsigned submission. -pub(crate) type SignaturePayload<'a> = ( - &'a [ValidatorIndex], - &'a CompactAssignments, - &'a PhragmenScore, - &'a u32, -); - /// Storage key used to store the persistent offchain worker status. pub(crate) const OFFCHAIN_HEAD_DB: &[u8] = b"parity/staking-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; +/// Default number of blocks for which the unsigned transaction should stay in the pool +pub(crate) const DEFAULT_LONGEVITY: u64 = 25; /// Checks if an execution of the offchain worker is permitted at the given block number, or not. /// @@ -112,57 +101,27 @@ pub(crate) fn set_check_offchain_execution_status( /// The internal logic of the offchain worker of this module. This runs the phragmen election, /// compacts and reduces the solution, computes the score and submits it back to the chain as an -/// unsigned transaction. +/// unsigned transaction, without any signature. pub(crate) fn compute_offchain_election() -> Result<(), OffchainElectionError> { - let keys = >::keys(); - let local_keys = T::KeyType::all(); - - // For each local key is in the stored authority keys, try and submit. Breaks out after first - // successful submission. - for (index, ref pubkey) in local_keys.into_iter().filter_map(|key| { - keys.iter() - .enumerate() - .find(|(_, val_key)| **val_key == key) - }) { - // compute raw solution. Note that we use `OffchainAccuracy`. - let PhragmenResult { - winners, - assignments, - } = >::do_phragmen::() - .ok_or(OffchainElectionError::ElectionFailed)?; - - // process and prepare it for submission. - let (winners, compact, score) = prepare_submission::(assignments, winners, true)?; - - // sign it. - let signature_payload: SignaturePayload = (&winners, &compact, &score, &(index as u32)); - let signature = pubkey - .sign(&signature_payload.encode()) - .ok_or(OffchainElectionError::SigningFailed)?; + // compute raw solution. Note that we use `OffchainAccuracy`. + let PhragmenResult { + winners, + assignments, + } = >::do_phragmen::() + .ok_or(OffchainElectionError::ElectionFailed)?; - // send it. - let call: ::Call = Call::submit_election_solution_unsigned( - winners, - compact, - score, - index as u32, - signature, - ).into(); + // process and prepare it for submission. + let (winners, compact, score) = prepare_submission::(assignments, winners, true)?; - if T::SubmitTransaction::submit_unsigned(call).map_err(|_| { - debug::native::warn!( - target: "staking", - "failed to submit offchain solution with key {:?}", - pubkey, - ); - }).is_ok() { - // return and break out after the first successful submission - return Ok(()); - } - } + // send it. + let call: ::Call = Call::submit_election_solution_unsigned( + winners, + compact, + score, + ).into(); - // no key left and no submission. - Err(OffchainElectionError::NoSigningKey) + T::SubmitTransaction::submit_unsigned(call) + .map_err(|_| OffchainElectionError::PoolSubmissionFailed) } /// Takes a phragmen result and spits out some data that can be submitted to the chain. diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 2c343c5a7869f..52c33a246e19f 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2746,23 +2746,19 @@ fn remove_multi_deferred() { mod offchain_phragmen { use crate::*; - use frame_support::{assert_noop, assert_ok, debug}; + use frame_support::{assert_noop, assert_ok}; use mock::*; use parking_lot::RwLock; use sp_core::offchain::{ testing::{PoolState, TestOffchainExt, TestTransactionPoolExt}, OffchainExt, TransactionPoolExt, }; - use sp_core::testing::KeyStore; - use sp_core::traits::KeystoreExt; use sp_io::TestExternalities; use sp_phragmen::StakedAssignment; use sp_runtime::traits::OffchainWorker; use std::sync::Arc; use substrate_test_utils::assert_eq_uvec; - type DummyT = dummy_sr25519::AuthorityId; - fn percent(x: u16) -> OffchainAccuracy { OffchainAccuracy::from_percent(x) } @@ -2792,27 +2788,10 @@ mod offchain_phragmen { fn offchainify(ext: &mut TestExternalities) -> Arc> { let (offchain, _state) = TestOffchainExt::new(); let (pool, state) = TestTransactionPoolExt::new(); - let keystore = KeyStore::new(); - - let account = LOCAL_KEY_ACCOUNT.with(|v| *v.borrow()); - let key = dummy_sr25519::dummy_key_for(account); - let _ = keystore - .write() - .insert_unknown( - ::ID, - &format!("{}/staking{}", mock::PHRASE, account), - key.as_ref(), - ) - .unwrap(); - debug::native::debug!( - target: "staking", - "generated key for account {}: {:?}", - account, - key, - ); + ext.register_extension(OffchainExt::new(offchain)); ext.register_extension(TransactionPoolExt::new(pool)); - ext.register_extension(KeystoreExt(keystore)); + state } @@ -3170,16 +3149,14 @@ mod offchain_phragmen { mock::Call::Staking(inner) => inner, }; - // pass this call to ValidateUnsigned - let signing_key = dummy_sr25519::dummy_key_for(11); assert_eq!( ::validate_unsigned(&inner), TransactionValidity::Ok(ValidTransaction { priority: 1125, // the proposed slot stake. requires: vec![], - provides: vec![(Staking::current_era(), signing_key).encode()], + provides: vec![Staking::current_era().encode()], longevity: 3, - propagate: true, + propagate: false, }) ) }) @@ -3223,27 +3200,6 @@ mod offchain_phragmen { }) } - #[test] - fn offchain_wont_run_without_signing_key() { - let mut ext = ExtBuilder::default() - .offchain_phragmen_ext() - .validator_count(2) - .local_key_account(5) - .build(); - let state = offchainify(&mut ext); - ext.execute_with(|| { - run_to_block(12); - - // local key 5 is not there. - assert_eq_uvec!(Session::validators(), vec![11, 21]); - - assert_eq!(state.read().transactions.len(), 0); - Staking::offchain_worker(12); - assert_eq!(state.read().transactions.len(), 0); - // Error in phragmen offchain worker call: OffchainElectionError::NoSigningKey - }) - } - #[test] fn invalid_phragmen_result_correct_number_of_winners() { ExtBuilder::default() @@ -3651,23 +3607,6 @@ mod offchain_phragmen { }) } - #[test] - fn session_keys_are_set() { - let mut ext = ExtBuilder::default() - .offchain_phragmen_ext() - .validator_count(4) - .build(); - let _ = offchainify(&mut ext); - ext.execute_with(|| { - assert_eq_uvec!( - Staking::keys(), - >::iter() - .map(|(acc, _)| dummy_sr25519::dummy_key_for(acc)) - .collect::>() - ); - }) - } - #[test] fn offchain_storage_is_set() { let mut ext = ExtBuilder::default() diff --git a/primitives/consensus/vrf/src/schnorrkel.rs b/primitives/consensus/vrf/src/schnorrkel.rs index e4ae68ced4190..265572dbdaee7 100644 --- a/primitives/consensus/vrf/src/schnorrkel.rs +++ b/primitives/consensus/vrf/src/schnorrkel.rs @@ -17,7 +17,6 @@ //! Schnorrkel-based VRF. use codec::{Encode, Decode}; -use sp_core::U512; use sp_runtime::RuntimeDebug; use sp_std::ops::{Deref, DerefMut}; #[cfg(feature = "std")] @@ -26,6 +25,8 @@ use std::convert::TryFrom; use codec::EncodeLike; #[cfg(feature = "std")] use schnorrkel::errors::MultiSignatureStage; +#[cfg(feature = "std")] +use sp_core::U512; #[cfg(feature = "std")] pub use schnorrkel::{SignatureError, vrf::{VRF_PROOF_LENGTH, VRF_OUTPUT_LENGTH}}; From 03bb2a9f8ec97315dbf4c4c8c49cd4c2ed7adb7f Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 26 Mar 2020 08:48:35 +0100 Subject: [PATCH 104/106] final round of changes. --- bin/node/runtime/src/lib.rs | 2 +- frame/staking/src/lib.rs | 101 ++++++++++++++------ frame/staking/src/offchain_election.rs | 4 + frame/staking/src/tests.rs | 126 +++++++++++++++++++++---- 4 files changed, 189 insertions(+), 44 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a6d8e504a376d..a6701392e4bfa 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -325,7 +325,7 @@ parameter_types! { pub const BondingDuration: pallet_staking::EraIndex = 24 * 28; pub const SlashDeferDuration: pallet_staking::EraIndex = 24 * 7; // 1/4 the bonding duration. pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; - pub const ElectionLookahead: BlockNumber = 30; + pub const ElectionLookahead: BlockNumber = 25; // 10 minutes per session => 100 block. pub const MaxNominatorRewardedPerValidator: u32 = 64; } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 3fcb88521bf13..8708c11b8d397 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -1196,7 +1196,12 @@ decl_module! { "Error in phragmen offchain worker: {:?}", e, ); - }; + } else { + debug::native::debug!( + target: "staking", + "Executed offchain worker thread without errors. Transaction submitted to the pool.", + ); + } } } } @@ -1799,6 +1804,7 @@ decl_module! { winners: Vec, compact_assignments: CompactAssignments, score: PhragmenScore, + era: EraIndex, ) { let _who = ensure_signed(origin)?; Self::check_and_replace_solution( @@ -1806,19 +1812,22 @@ decl_module! { compact_assignments, ElectionCompute::Signed, score, + era, )? } /// Unsigned version of `submit_election_solution`. /// /// Note that this must pass the [`ValidateUnsigned`] check which only allows transactions - /// from the local node to be included. + /// from the local node to be included. In other words, only the block author can include a + /// transaction in the block. #[weight = SimpleDispatchInfo::FixedNormal(100_000_000)] pub fn submit_election_solution_unsigned( origin, winners: Vec, compact_assignments: CompactAssignments, score: PhragmenScore, + era: EraIndex, ) { ensure_none(origin)?; Self::check_and_replace_solution( @@ -1826,7 +1835,12 @@ decl_module! { compact_assignments, ElectionCompute::Unsigned, score, + era, )? + // TODO: instead of returning an error, panic. This makes the entire produced block + // invalid. + // This ensures that block authors will not ever try and submit a solution which is not + // an improvement, since they will lose their authoring points/rewards. } } } @@ -2068,28 +2082,46 @@ impl Module { } } - /// Checks a given solution and if correct and improved, writes it on chain as the queued result - /// of the next round. This may be called by both a signed and an unsigned transaction. - pub fn check_and_replace_solution( - winners: Vec, - compact_assignments: CompactAssignments, - compute: ElectionCompute, - claimed_score: PhragmenScore, - ) -> Result<(), Error> { - // discard early solutions + /// Basic and cheap checks that we perform in validate unsigned, and in the execution. + pub fn pre_dispatch_checks(score: PhragmenScore, era: EraIndex) -> Result<(), Error> { + // discard solutions that are not in-time + // check window open ensure!( Self::era_election_status().is_open(), Error::::PhragmenEarlySubmission, ); + // check current era. + if let Some(current_era) = Self::active_era().map(|e| e.index) { + ensure!( + current_era == era, + Error::::PhragmenEarlySubmission, + ) + } + // assume the given score is valid. Is it better than what we have on-chain, if we have any? if let Some(queued_score) = Self::queued_score() { ensure!( - is_score_better(queued_score, claimed_score), + is_score_better(queued_score, score), Error::::PhragmenWeakSubmission, ) } + Ok(()) + } + + /// Checks a given solution and if correct and improved, writes it on chain as the queued result + /// of the next round. This may be called by both a signed and an unsigned transaction. + pub fn check_and_replace_solution( + winners: Vec, + compact_assignments: CompactAssignments, + compute: ElectionCompute, + claimed_score: PhragmenScore, + era: EraIndex, + ) -> Result<(), Error> { + // Do the basic checks. era, claimed score and window open. + Self::pre_dispatch_checks(claimed_score, era)?; + // Check that the number of presented winners is sane. Most often we have more candidates // that we need. Then it should be Self::validator_count(). Else it should be all the // candidates. @@ -2923,6 +2955,15 @@ impl SignedExtension for LockStakingStatus { } } +impl From> for InvalidTransaction { + fn from(e: Error) -> Self { + match e { + >::PhragmenEarlySubmission => InvalidTransaction::Future, + _ => InvalidTransaction::Custom(e.as_u8()), + } + } +} + #[allow(deprecated)] impl frame_support::unsigned::ValidateUnsigned for Module { type Call = Call; @@ -2931,6 +2972,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { _, _, score, + era, ) = call { use offchain_election::DEFAULT_LONGEVITY; @@ -2946,25 +2988,21 @@ impl frame_support::unsigned::ValidateUnsigned for Module { } } - // discard early solution. - if Self::era_election_status().is_closed() { + if let Err(e) = Self::pre_dispatch_checks(*score, *era) { debug::native::debug!( target: "staking", - "rejecting unsigned transaction because it is too early." + "validate unsigned failed due to {:?}.", + e, ); - return InvalidTransaction::Future.into(); + let invalid: InvalidTransaction = e.into(); + return invalid.into(); } - // discard weak solution. - if let Some(queued_score) = Self::queued_score() { - if !is_score_better(queued_score, *score) { - debug::native::debug!( - target: "staking", - "rejecting unsigned transaction because the claimed score is not good enough." - ); - return InvalidTransaction::Custom(1u8).into(); - } - } + debug::native::debug!( + target: "staking", + "Validated an unsigned transaction from the local node for era {}.", + era, + ); Ok(ValidTransaction { // The higher the score[0], the better a solution is. @@ -2974,7 +3012,7 @@ impl frame_support::unsigned::ValidateUnsigned for Module { // Defensive only. A single solution can exist in the pool per era. Each validator // will run OCW at most once per era, hence there should never exist more than one // transaction anyhow. - provides: vec![Self::current_era().encode()], + provides: vec![("StakingOffchain", era).encode()], // Note: this can be more accurate in the future. We do something like // `era_end_block - current_block` but that is not needed now as we eagerly run // offchain workers now and the above should be same as `T::ElectionLookahead` @@ -2988,6 +3026,15 @@ impl frame_support::unsigned::ValidateUnsigned for Module { InvalidTransaction::Call.into() } } + + fn pre_dispatch(_: &Self::Call) -> Result<(), TransactionValidityError> { + // IMPORTANT NOTE: By default, a sane `pre-dispatch` should always do the same checks as + // `validate_unsigned` and overriding this should be done with care. this module has only + // one unsigned entry point, in which we call into `>::pre_dispatch_checks()` + // which is all the important checks that we do in `validate_unsigned`. Hence, we can safely + // override this to save some time. + Ok(()) + } } /// Check that list is sorted and has no duplicates. diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 3536915b00dc4..0d4cf49f103b9 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -113,11 +113,15 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti // process and prepare it for submission. let (winners, compact, score) = prepare_submission::(assignments, winners, true)?; + // defensive-only: active era can never be none except genesis. + let era = >::active_era().map(|e| e.index).unwrap_or_default(); + // send it. let call: ::Call = Call::submit_election_solution_unsigned( winners, compact, score, + era, ).into(); T::SubmitTransaction::submit_unsigned(call) diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index cb0cf44a3176d..1a737066c1d66 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2970,6 +2970,7 @@ mod offchain_phragmen { winners, compact, score, + active_era(), )); let queued_result = Staking::queued_elected().unwrap(); @@ -3012,6 +3013,7 @@ mod offchain_phragmen { winners, compact, score, + active_era(), )); let queued_result = Staking::queued_elected().unwrap(); @@ -3056,7 +3058,13 @@ mod offchain_phragmen { Staking::kill_stakers_snapshot(); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenEarlySubmission, ); }) @@ -3081,12 +3089,19 @@ mod offchain_phragmen { winners, compact, score, + active_era(), )); // a bad solution let (compact, winners, score) = horrible_phragmen_with_post_processing(false); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenWeakSubmission, ); }) @@ -3111,6 +3126,7 @@ mod offchain_phragmen { winners, compact, score, + active_era(), )); // a better solution @@ -3120,6 +3136,7 @@ mod offchain_phragmen { winners, compact, score, + active_era(), )); }) } @@ -3157,7 +3174,7 @@ mod offchain_phragmen { TransactionValidity::Ok(ValidTransaction { priority: 1125, // the proposed slot stake. requires: vec![], - provides: vec![Staking::current_era().encode()], + provides: vec![("StakingOffchain", active_era()).encode()], longevity: 3, propagate: false, }) @@ -3181,6 +3198,7 @@ mod offchain_phragmen { winners, compact, score, + active_era(), ),); // now run the offchain worker in the same chain state. @@ -3201,7 +3219,9 @@ mod offchain_phragmen { TransactionSource::Local, &inner, ), - TransactionValidity::Err(InvalidTransaction::Custom(1u8).into()), + TransactionValidity::Err( + InvalidTransaction::Custom(>::PhragmenWeakSubmission.as_u8()).into(), + ), ) }) } @@ -3224,7 +3244,13 @@ mod offchain_phragmen { assert_eq!(winners.len(), 3); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenBogusWinnerCount, ); }) @@ -3249,7 +3275,13 @@ mod offchain_phragmen { assert_eq!(winners.len(), 3); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenBogusWinnerCount, ); }) @@ -3277,6 +3309,7 @@ mod offchain_phragmen { winners, compact, score, + active_era(), ),); }) } @@ -3302,7 +3335,13 @@ mod offchain_phragmen { // The error type sadly cannot be more specific now. assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenBogusCompact, ); }) @@ -3329,7 +3368,13 @@ mod offchain_phragmen { // The error type sadly cannot be more specific now. assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenBogusCompact, ); }) @@ -3355,7 +3400,13 @@ mod offchain_phragmen { let winners = vec![0, 1, 2, 4]; assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenBogusWinner, ); }) @@ -3385,7 +3436,13 @@ mod offchain_phragmen { }); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenBogusEdge, ); }) @@ -3415,7 +3472,13 @@ mod offchain_phragmen { }); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenBogusSelfVote, ); }) @@ -3445,7 +3508,13 @@ mod offchain_phragmen { // This raises score issue. assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenBogusSelfVote, ); }) @@ -3474,7 +3543,13 @@ mod offchain_phragmen { } assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenBogusCompact, ); }) @@ -3510,7 +3585,13 @@ mod offchain_phragmen { }); assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenBogusNomination, ); }) @@ -3572,6 +3653,7 @@ mod offchain_phragmen { winners, compact, score, + active_era(), )); // a wrong solution. @@ -3585,7 +3667,13 @@ mod offchain_phragmen { // is rejected. assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenSlashedNomination, ); }) @@ -3607,7 +3695,13 @@ mod offchain_phragmen { score[0] += 1; assert_noop!( - Staking::submit_election_solution(Origin::signed(10), winners, compact, score), + Staking::submit_election_solution( + Origin::signed(10), + winners, + compact, + score, + active_era(), + ), Error::::PhragmenBogusScore, ); }) From 45144324e5955bae272d594ed341350929d81246 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 26 Mar 2020 09:19:53 +0100 Subject: [PATCH 105/106] Fix cargo-deny --- Cargo.lock | 213 ++++++++++++------------- primitives/phragmen/compact/Cargo.toml | 4 + 2 files changed, 108 insertions(+), 109 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6561f1587f89d..8660b50240c4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,12 +70,11 @@ dependencies = [ [[package]] name = "alga" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658f9468113d34781f6ca9d014d174c74b73de870f1e0e3ad32079bbab253b19" +checksum = "4f823d037a7ec6ea2197046bafd4ae150e6bc36f9ca347404f46a46823fa84f2" dependencies = [ "approx", - "libm", "num-complex", "num-traits", ] @@ -100,9 +99,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" +checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785" [[package]] name = "app_dirs" @@ -133,9 +132,9 @@ checksum = "75153c95fdedd7db9732dfbfc3702324a1627eec91ba56e37cd0ac78314ab2ed" [[package]] name = "arc-swap" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff" +checksum = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825" [[package]] name = "arrayref" @@ -179,9 +178,9 @@ dependencies = [ [[package]] name = "assert_cmd" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6283bac8dd7226470d491bc4737816fea4ca1fba7a2847f2e9097fd6bfb4624c" +checksum = "35ad62275a8bda1c2c9a9303aea121eb04204272d3be0735d5dc1f49eb9ff9a9" dependencies = [ "doc-comment", "escargot", @@ -269,9 +268,9 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.45" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8" +checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" dependencies = [ "backtrace-sys", "cfg-if", @@ -281,9 +280,9 @@ dependencies = [ [[package]] name = "backtrace-sys" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca797db0057bae1a7aa2eef3283a874695455cecf08a43bfb8507ee0ebc1ed69" +checksum = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118" dependencies = [ "cc", "libc", @@ -457,9 +456,9 @@ checksum = "b170cd256a3f9fa6b9edae3e44a7dfdfc77e8124dbc3e2612d75f9c3e2396dae" [[package]] name = "bstr" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502ae1441a0a5adb8fbd38a5955a6416b9493e92b465de5e4a9bde6a539c2c48" +checksum = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41" dependencies = [ "lazy_static", "memchr", @@ -1109,9 +1108,9 @@ dependencies = [ [[package]] name = "doc-comment" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "807e5847c39ad6a11eac66de492ed1406f76a260eb8656e8740cad9eabc69c27" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "ed25519-dalek" @@ -1411,9 +1410,9 @@ checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" [[package]] name = "flate2" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" +checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" dependencies = [ "cfg-if", "crc32fast", @@ -1917,9 +1916,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "globset" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2" +checksum = "7ad1da430bd7281dde2576f44c84cc3f0f7b475e7202cd503042dff01a8c8120" dependencies = [ "aho-corasick", "bstr", @@ -1972,16 +1971,16 @@ dependencies = [ [[package]] name = "h2" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5c295d1c0c68e4e42003d75f908f5e16a1edd1cbe0b0d02e4dc2006a384f47" +checksum = "7938e6aa2a31df4e21f224dc84704bd31c089a6d1355c535b03667371cccc843" dependencies = [ "bytes 0.5.4", "fnv", "futures-core", "futures-sink", "futures-util", - "http 0.2.0", + "http 0.2.1", "indexmap", "log 0.4.8", "slab", @@ -2102,9 +2101,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" +checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" dependencies = [ "bytes 0.5.4", "fnv", @@ -2130,7 +2129,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ "bytes 0.5.4", - "http 0.2.0", + "http 0.2.1", ] [[package]] @@ -2199,16 +2198,16 @@ dependencies = [ [[package]] name = "hyper" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7b15203263d1faa615f9337d79c1d37959439dc46c2b4faab33286fadc2a1c5" +checksum = "ed6081100e960d9d74734659ffc9cc91daf1c0fc7aceb8eaa94ee1a3f5046f2e" dependencies = [ "bytes 0.5.4", "futures-channel", "futures-core", "futures-util", - "h2 0.2.2", - "http 0.2.0", + "h2 0.2.3", + "http 0.2.1", "http-body 0.3.1", "httparse", "itoa", @@ -2230,7 +2229,7 @@ dependencies = [ "bytes 0.5.4", "ct-logs", "futures-util", - "hyper 0.13.3", + "hyper 0.13.4", "log 0.4.8", "rustls 0.17.0", "rustls-native-certs", @@ -2353,9 +2352,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a859057dc563d1388c1e816f98a1892629075fc046ed06e845b883bb8b2916fb" +checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" [[package]] name = "itertools" @@ -2383,9 +2382,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb931d43e71f560c81badb0191596562bafad2be06a3f9025b845c847c60df5" +checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" dependencies = [ "wasm-bindgen", ] @@ -2620,9 +2619,9 @@ checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" [[package]] name = "libc" -version = "0.2.67" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" +checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" [[package]] name = "libloading" @@ -2636,9 +2635,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.1.4" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" +checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" [[package]] name = "libp2p" @@ -3212,11 +3211,11 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" +checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" dependencies = [ - "rustc_version", + "autocfg 1.0.0", ] [[package]] @@ -3835,9 +3834,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ "autocfg 1.0.0", "num-bigint", @@ -3852,6 +3851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ "autocfg 1.0.0", + "libm", ] [[package]] @@ -4844,9 +4844,9 @@ dependencies = [ [[package]] name = "paste" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e1afe738d71b1ebab5f1207c055054015427dbfc7bbe9ee1266894156ec046" +checksum = "8292c1e1e81ddb552c4c90c36af201a0ce7e34995f55f0480f01052f242811c9" dependencies = [ "paste-impl", "proc-macro-hack", @@ -4854,9 +4854,9 @@ dependencies = [ [[package]] name = "paste-impl" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d4dc4a7f6f743211c5aab239640a65091535d97d43d92a52bca435a640892bb" +checksum = "5e9c43f2645f06ee452544ad032886a75f3d1797b9487dcadcae9100ba58a51c" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -5032,9 +5032,9 @@ dependencies = [ [[package]] name = "proc-macro-error" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7959c6467d962050d639361f7703b2051c43036d03493c36f01d440fdd3138a" +checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" dependencies = [ "proc-macro-error-attr", "proc-macro2", @@ -5045,9 +5045,9 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4002d9f55991d5e019fb940a90e1a95eb80c24e77cb2462dd4dc869604d543a" +checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" dependencies = [ "proc-macro2", "quote", @@ -5058,20 +5058,15 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.11" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "fcfdefadc3d57ca21cf17990a28ef4c0f7c61383a28cb7604cf4a18e6ede1420" [[package]] name = "proc-macro-nested" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" +checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" [[package]] name = "proc-macro2" @@ -5149,9 +5144,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "2.10.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a5325d019a4d837d3abde0a836920f959e33d350f77b5f1e289e061e774942" +checksum = "fc1b4a8efc42cf150049e8a490f618c7c60e82332405065f202a7e33aa5a1f06" [[package]] name = "pwasm-utils" @@ -5465,9 +5460,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.3.4" +version = "1.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" +checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" dependencies = [ "aho-corasick", "memchr", @@ -5486,9 +5481,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.16" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1" +checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" [[package]] name = "region" @@ -5528,9 +5523,9 @@ dependencies = [ [[package]] name = "rlp" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a44d5ae8afcb238af8b75640907edc6c931efcfab2c854e81ed35fa080f84cd" +checksum = "4a7d3f9bed94764eac15b8f14af59fac420c236adaff743b7bcc88e265cb4345" dependencies = [ "rustc-hex", ] @@ -5656,9 +5651,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" [[package]] name = "safe-mix" @@ -6411,7 +6406,7 @@ dependencies = [ "fnv", "futures 0.3.4", "futures-timer 3.0.2", - "hyper 0.13.3", + "hyper 0.13.4", "hyper-rustls", "log 0.4.8", "num_cpus", @@ -6704,9 +6699,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295" +checksum = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19" dependencies = [ "lazy_static", "winapi 0.3.8", @@ -7030,7 +7025,7 @@ dependencies = [ "bytes 0.5.4", "flate2", "futures 0.3.4", - "http 0.2.0", + "http 0.2.1", "httparse", "log 0.4.8", "rand 0.7.3", @@ -7733,9 +7728,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fe43617218c0805c6eb37160119dc3c548110a67786da7218d1c6555212f073" +checksum = "c8faa2719539bbe9d77869bfb15d4ee769f99525e707931452c97b693b3f159d" dependencies = [ "clap", "lazy_static", @@ -7744,9 +7739,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e79c80e0f4efd86ca960218d4e056249be189ff1c42824dcd9a7f51a56f0bd" +checksum = "3f88b8e18c69496aad6f9ddf4630dd7d585bcaf765786cb415b9aec2fe5a0430" dependencies = [ "heck", "proc-macro-error", @@ -7892,7 +7887,7 @@ dependencies = [ "async-std", "derive_more", "futures-util", - "hyper 0.13.3", + "hyper 0.13.4", "log 0.4.8", "prometheus", "tokio 0.2.13", @@ -8097,9 +8092,9 @@ checksum = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" [[package]] name = "syn" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" dependencies = [ "proc-macro2", "quote", @@ -8209,18 +8204,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db" +checksum = "e3711fd1c4e75b3eff12ba5c40dba762b6b65c5476e8174c1a664772060c49bf" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4" +checksum = "ae2b85ba4c9aa32dd3343bd80eb8d22e9b54b7688c17ea3907f236885353b233" dependencies = [ "proc-macro2", "quote", @@ -8258,9 +8253,9 @@ dependencies = [ [[package]] name = "tiny-bip39" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6848cd8f566953ce1e8faeba12ee23cbdbb0437754792cd857d44628b5685e3" +checksum = "e255ec4f7d4aaccbede17dffcfb2e71434d17f5c921d5a06823b8e58a2bcd468" dependencies = [ "failure", "hmac", @@ -8908,9 +8903,9 @@ dependencies = [ [[package]] name = "wabt-sys" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af5d153dc96aad7dc13ab90835b892c69867948112d95299e522d370c4e13a08" +checksum = "23d7043ebb3e5d96fad7a8d3ca22ee9880748ff8c3e18092cfb2a49d3b8f9084" dependencies = [ "cc", "cmake", @@ -8957,9 +8952,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3557c397ab5a8e347d434782bcd31fc1483d927a6826804cec05cc792ee2519d" +checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -8967,9 +8962,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0da9c9a19850d3af6df1cb9574970b566d617ecfaf36eb0b706b6f3ef9bd2f8" +checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" dependencies = [ "bumpalo", "lazy_static", @@ -8982,9 +8977,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457414a91863c0ec00090dba537f88ab955d93ca6555862c29b6d860990b8a8a" +checksum = "7add542ea1ac7fdaa9dc25e031a6af33b7d63376292bd24140c637d00d1c312a" dependencies = [ "cfg-if", "js-sys", @@ -8994,9 +8989,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6fde1d36e75a714b5fe0cffbb78978f222ea6baebb726af13c78869fdb4205" +checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9004,9 +8999,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bda4168030a6412ea8a047e27238cadf56f0e53516e1e83fec0a8b7c786f6d" +checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" dependencies = [ "proc-macro2", "quote", @@ -9017,9 +9012,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc9f36ad51f25b0219a3d4d13b90eb44cd075dff8b6280cca015775d7acaddd8" +checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" [[package]] name = "wasm-gc-api" @@ -9141,27 +9136,27 @@ dependencies = [ [[package]] name = "wast" -version = "10.0.0" +version = "11.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4efb62ecebf5cc9dbf2954309a20d816289c6550c0597a138b9e811cefc05007" +checksum = "df4d67ba9266f4fcaf2e8a1afadc5e2a959e51aecc07b1ecbdf85a6ddaf08bde" dependencies = [ "leb128", ] [[package]] name = "wat" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdea5e25273cc3a62f3ae3a1a4c7d7996625875b50c0b4475fee6698c2b069c" +checksum = "9a9400dc1c8512087b2d974b1b9b0a6c4e6e26e7e8acf629e3e351165a1ed301" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721c6263e2c66fd44501cc5efbfa2b7dfa775d13e4ea38c46299646ed1f9c70a" +checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/primitives/phragmen/compact/Cargo.toml b/primitives/phragmen/compact/Cargo.toml index b8dc58ea817be..fb61a90b09b6e 100644 --- a/primitives/phragmen/compact/Cargo.toml +++ b/primitives/phragmen/compact/Cargo.toml @@ -3,6 +3,10 @@ name = "sp-phragmen-compact" version = "2.0.0-dev" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" +homepage = "https://substrate.dev" +repository = "https://github.com/paritytech/substrate/" +description = "Phragmen Compact Solution" [lib] proc-macro = true From 8182947fc7b513157dd12dd82ed18e61491d7bf2 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Thu, 26 Mar 2020 12:22:38 +0100 Subject: [PATCH 106/106] Update frame/staking/src/lib.rs Co-Authored-By: Gavin Wood --- frame/staking/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 829ba3de0e0a8..b19caec258fa5 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -2480,10 +2480,10 @@ impl Module { /// /// No storage item is updated. fn do_phragmen_with_post_processing(compute: ElectionCompute) - -> Option>> - where - Accuracy: sp_std::ops::Mul, - ExtendedBalance: From<::Inner>, + -> Option>> + where + Accuracy: sp_std::ops::Mul, + ExtendedBalance: From<::Inner>, { if let Some(phragmen_result) = Self::do_phragmen::() { let elected_stashes = phragmen_result.winners.iter()