diff --git a/bin/node/runtime/src/impls.rs b/bin/node/runtime/src/impls.rs index 039093ddee697..16666997b3a55 100644 --- a/bin/node/runtime/src/impls.rs +++ b/bin/node/runtime/src/impls.rs @@ -17,8 +17,6 @@ //! Some configurable implementations as associated type for the substrate runtime. -use node_primitives::Balance; -use sp_runtime::traits::Convert; use frame_support::traits::{OnUnbalanced, Currency}; use crate::{Balances, Authorship, NegativeImbalance}; @@ -29,26 +27,9 @@ impl OnUnbalanced for Author { } } -/// Struct that handles the conversion of Balance -> `u64`. This is used for staking's election -/// calculation. -pub struct CurrencyToVoteHandler; - -impl CurrencyToVoteHandler { - fn factor() -> Balance { (Balances::total_issuance() / u64::max_value() as Balance).max(1) } -} - -impl Convert for CurrencyToVoteHandler { - fn convert(x: Balance) -> u64 { (x / Self::factor()) as u64 } -} - -impl Convert for CurrencyToVoteHandler { - fn convert(x: u128) -> Balance { x * Self::factor() } -} - #[cfg(test)] mod multiplier_tests { - use super::*; - use sp_runtime::{assert_eq_error_rate, FixedPointNumber}; + use sp_runtime::{assert_eq_error_rate, FixedPointNumber, traits::Convert}; use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment}; use crate::{ diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index f1793d40ee139..75439fe948bdf 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -30,7 +30,10 @@ use frame_support::{ Weight, IdentityFee, constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, }, - traits::{Currency, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness, LockIdentifier}, + traits::{ + Currency, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness, LockIdentifier, + U128CurrencyToVote, + }, }; use frame_system::{EnsureRoot, EnsureOneOf}; use frame_support::traits::InstanceFilter; @@ -78,7 +81,7 @@ pub use pallet_staking::StakerStatus; /// Implementations of some helper traits passed into runtime modules as associated types. pub mod impls; -use impls::{CurrencyToVoteHandler, Author}; +use impls::Author; /// Constant values used within the runtime. pub mod constants; @@ -448,7 +451,7 @@ parameter_types! { impl pallet_staking::Trait for Runtime { type Currency = Balances; type UnixTime = Timestamp; - type CurrencyToVote = CurrencyToVoteHandler; + type CurrencyToVote = U128CurrencyToVote; type RewardRemainder = Treasury; type Event = Event; type Slash = Treasury; // send the slashed funds to the treasury. @@ -574,7 +577,7 @@ impl pallet_elections_phragmen::Trait for Runtime { // NOTE: this implies that council's genesis members cannot be set directly and must come from // this module. type InitializeMembers = Council; - type CurrencyToVote = CurrencyToVoteHandler; + type CurrencyToVote = U128CurrencyToVote; type CandidacyBond = CandidacyBond; type VotingBond = VotingBond; type LoserCandidate = (); diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index b820a3108daf1..b6dbade24e3f9 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -23,7 +23,7 @@ use sp_runtime::{ Perbill, impl_opaque_keys, curve::PiecewiseLinear, testing::{Digest, DigestItem, Header, TestXt,}, - traits::{Convert, Header as _, IdentityLookup, OpaqueKeys, SaturatedConversion}, + traits::{Header as _, IdentityLookup, OpaqueKeys}, }; use frame_system::InitKind; use frame_support::{ @@ -183,23 +183,9 @@ parameter_types! { pub const StakingUnsignedPriority: u64 = u64::max_value() / 2; } -pub struct CurrencyToVoteHandler; - -impl Convert for CurrencyToVoteHandler { - fn convert(x: u128) -> u128 { - x - } -} - -impl Convert for CurrencyToVoteHandler { - fn convert(x: u128) -> u64 { - x.saturated_into() - } -} - impl pallet_staking::Trait for Test { type RewardRemainder = (); - type CurrencyToVote = CurrencyToVoteHandler; + type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; type Event = (); type Currency = Balances; type Slash = (); diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index cd20fcf2ef127..964cf6daf2cee 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -83,25 +83,26 @@ #![cfg_attr(not(feature = "std"), no_std)] -use codec::{Encode, Decode}; -use sp_std::prelude::*; -use sp_runtime::{ - DispatchError, RuntimeDebug, Perbill, - traits::{Zero, StaticLookup, Convert, Saturating}, -}; +use codec::{Decode, Encode}; use frame_support::{ - decl_storage, decl_event, ensure, decl_module, decl_error, - weights::Weight, - storage::{StorageMap, IterableStorageMap}, + decl_error, decl_event, decl_module, decl_storage, dispatch::{DispatchResultWithPostInfo, WithPostDispatchInfo}, + ensure, + storage::{IterableStorageMap, StorageMap}, traits::{ - Currency, Get, LockableCurrency, LockIdentifier, ReservableCurrency, WithdrawReasons, - ChangeMembers, OnUnbalanced, WithdrawReason, Contains, InitializeMembers, BalanceStatus, - ContainsLengthBound, - } + BalanceStatus, ChangeMembers, Contains, ContainsLengthBound, Currency, CurrencyToVote, Get, + InitializeMembers, LockIdentifier, LockableCurrency, OnUnbalanced, ReservableCurrency, + WithdrawReason, WithdrawReasons, + }, + weights::Weight, }; -use sp_npos_elections::{ExtendedBalance, VoteWeight, ElectionResult}; -use frame_system::{ensure_signed, ensure_root}; +use frame_system::{ensure_root, ensure_signed}; +use sp_npos_elections::{ElectionResult, ExtendedBalance}; +use sp_runtime::{ + traits::{Saturating, StaticLookup, Zero}, + DispatchError, Perbill, RuntimeDebug, +}; +use sp_std::prelude::*; mod benchmarking; mod default_weights; @@ -172,7 +173,7 @@ pub trait Trait: frame_system::Trait { /// Convert a balance into a number used for election calculation. /// This must fit into a `u64` but is allowed to be sensibly lossy. - type CurrencyToVote: Convert, VoteWeight> + Convert>; + type CurrencyToVote: CurrencyToVote>; /// How much should be locked up in order to submit one's candidacy. type CandidacyBond: Get>; @@ -871,16 +872,13 @@ impl Module { } // helper closures to deal with balance/stake. - let to_votes = |b: BalanceOf| -> VoteWeight { - , VoteWeight>>::convert(b) - }; - let to_balance = |e: ExtendedBalance| -> BalanceOf { - >>::convert(e) - }; + let total_issuance = T::Currency::total_issuance(); + let to_votes = |b: BalanceOf| T::CurrencyToVote::to_vote(b, total_issuance); + let to_balance = |e: ExtendedBalance| T::CurrencyToVote::to_currency(e, total_issuance); // used for prime election. let voters_and_stakes = Voting::::iter() - .map(|(voter, (stake, votes))| { (voter, stake, votes) }) + .map(|(voter, (stake, votes))| (voter, stake, votes)) .collect::>(); // used for phragmen. let voters_and_votes = voters_and_stakes.iter() @@ -1186,17 +1184,6 @@ mod tests { } } - /// 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 } - } - impl Convert for CurrencyToVoteHandler { - fn convert(x: u128) -> u64 { - x as u64 - } - } - parameter_types!{ pub const ElectionsPhragmenModuleId: LockIdentifier = *b"phrelect"; } @@ -1205,7 +1192,7 @@ mod tests { type ModuleId = ElectionsPhragmenModuleId; type Event = Event; type Currency = Balances; - type CurrencyToVote = CurrencyToVoteHandler; + type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; type ChangeMembers = TestChangeMembers; type InitializeMembers = (); type CandidacyBond = CandidacyBond; diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 1244c97c1c4a6..d3461eec12dc4 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -36,7 +36,7 @@ use sp_runtime::{ curve::PiecewiseLinear, impl_opaque_keys, testing::{Header, TestXt, UintAuthorityId}, - traits::{Convert, IdentityLookup, OpaqueKeys, SaturatedConversion}, + traits::{IdentityLookup, OpaqueKeys}, DigestItem, Perbill, }; use sp_staking::SessionIndex; @@ -198,23 +198,9 @@ parameter_types! { pub const StakingUnsignedPriority: u64 = u64::max_value() / 2; } -pub struct CurrencyToVoteHandler; - -impl Convert for CurrencyToVoteHandler { - fn convert(x: u128) -> u128 { - x - } -} - -impl Convert for CurrencyToVoteHandler { - fn convert(x: u128) -> u64 { - x.saturated_into() - } -} - impl pallet_staking::Trait for Test { type RewardRemainder = (); - type CurrencyToVote = CurrencyToVoteHandler; + type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; type Event = TestEvent; type Currency = Balances; type Slash = (); diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 685aba012d45a..527e0ede81ab9 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -26,7 +26,6 @@ use frame_support::{ }; use frame_system as system; use sp_runtime::{ - SaturatedConversion, traits::{IdentityLookup, Block as BlockT}, testing::{Header, UintAuthorityId}, }; @@ -150,22 +149,10 @@ parameter_types! { pub type Extrinsic = sp_runtime::testing::TestXt; -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() - } -} - impl pallet_staking::Trait for Test { type Currency = Balances; type UnixTime = pallet_timestamp::Module; - type CurrencyToVote = CurrencyToVoteHandler; + type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; type RewardRemainder = (); type Event = Event; type Slash = (); diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 2ba92c26530a3..6a9cfc5f98a1b 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -19,7 +19,7 @@ #![cfg(test)] -use sp_runtime::traits::{Convert, SaturatedConversion, IdentityLookup}; +use sp_runtime::traits::IdentityLookup; use frame_support::{impl_outer_origin, impl_outer_dispatch, parameter_types}; type AccountId = u64; @@ -42,18 +42,6 @@ impl_outer_dispatch! { } } -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() - } -} - #[derive(Clone, Eq, PartialEq, Debug)] pub struct Test; @@ -172,7 +160,7 @@ impl frame_system::offchain::SendTransactionTypes for Test where impl pallet_staking::Trait for Test { type Currency = Balances; type UnixTime = pallet_timestamp::Module; - type CurrencyToVote = CurrencyToVoteHandler; + type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; type RewardRemainder = (); type Event = (); type Slash = (); diff --git a/frame/staking/fuzzer/src/mock.rs b/frame/staking/fuzzer/src/mock.rs index ac7c7efe43d46..96df7674e9f44 100644 --- a/frame/staking/fuzzer/src/mock.rs +++ b/frame/staking/fuzzer/src/mock.rs @@ -17,7 +17,6 @@ //! Mock file for staking fuzzing. -use sp_runtime::traits::{Convert, SaturatedConversion}; use frame_support::{impl_outer_origin, impl_outer_dispatch, parameter_types}; type AccountId = u64; @@ -41,18 +40,6 @@ impl_outer_dispatch! { } } -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() - } -} - #[derive(Clone, Eq, PartialEq, Debug)] pub struct Test; @@ -177,7 +164,7 @@ impl frame_system::offchain::SendTransactionTypes for Test where impl pallet_staking::Trait for Test { type Currency = Balances; type UnixTime = pallet_timestamp::Module; - type CurrencyToVote = CurrencyToVoteHandler; + type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; type RewardRemainder = (); type Event = (); type Slash = (); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 362e71f17d602..9436dfd3bb847 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -299,7 +299,7 @@ use frame_support::{ }, traits::{ Currency, LockIdentifier, LockableCurrency, WithdrawReasons, OnUnbalanced, Imbalance, Get, - UnixTime, EstimateNextNewSession, EnsureOrigin, + UnixTime, EstimateNextNewSession, EnsureOrigin, CurrencyToVote, } }; use pallet_session::historical; @@ -811,7 +811,7 @@ pub trait Trait: frame_system::Trait + SendTransactionTypes> { /// [`sp_npos_elections`] crate which accepts u64 numbers and does operations in 128. /// Consequently, the backward convert is used convert the u128s from sp-elections back to a /// [`BalanceOf`]. - type CurrencyToVote: Convert, VoteWeight> + Convert>; + type CurrencyToVote: CurrencyToVote>; /// Tokens have been minted and are unused for validator-reward. /// See [Era payout](./index.html#era-payout). @@ -2192,11 +2192,22 @@ impl Module { Self::bonded(stash).and_then(Self::ledger).map(|l| l.active).unwrap_or_default() } - /// internal impl of [`slashable_balance_of`] that returns [`VoteWeight`]. - pub fn slashable_balance_of_vote_weight(stash: &T::AccountId) -> VoteWeight { - , VoteWeight>>::convert( - Self::slashable_balance_of(stash) - ) + /// Internal impl of [`slashable_balance_of`] that returns [`VoteWeight`]. + pub fn slashable_balance_of_vote_weight(stash: &T::AccountId, issuance: BalanceOf) -> VoteWeight { + T::CurrencyToVote::to_vote(Self::slashable_balance_of(stash), issuance) + } + + /// Returns a closure around `slashable_balance_of_vote_weight` that can be passed around. + /// + /// This prevents call sites from repeatedly requesting `total_issuance` from backend. But it is + /// important to be only used while the total issuance is not changing. + pub fn slashable_balance_of_fn() -> Box VoteWeight> { + // NOTE: changing this to unboxed `impl Fn(..)` return type and the module will still + // compile, while some types in mock fail to resolve. + let issuance = T::Currency::total_issuance(); + Box::new(move |who: &T::AccountId| -> VoteWeight { + Self::slashable_balance_of_vote_weight(who, issuance) + }) } /// Dump the list of validators and nominators into vectors and keep them on-chain. @@ -2601,7 +2612,7 @@ impl Module { // convert into staked assignments. let staked_assignments = sp_npos_elections::assignment_ratio_to_staked( assignments, - Self::slashable_balance_of_vote_weight, + Self::slashable_balance_of_fn(), ); // build the support map thereof in order to evaluate. @@ -2852,7 +2863,7 @@ impl Module { /// `PrimitiveElectionResult` into `ElectionResult`. /// /// No storage item is updated. - fn do_on_chain_phragmen() -> Option>> { + pub fn do_on_chain_phragmen() -> Option>> { if let Some(phragmen_result) = Self::do_phragmen::(0) { let elected_stashes = phragmen_result.winners.iter() .map(|(s, _)| s.clone()) @@ -2861,7 +2872,7 @@ impl Module { let staked_assignments = sp_npos_elections::assignment_ratio_to_staked( assignments, - Self::slashable_balance_of_vote_weight, + Self::slashable_balance_of_fn(), ); let supports = build_support_map::( @@ -2903,16 +2914,16 @@ impl Module { /// Self votes are added and nominations before the most recent slashing span are ignored. /// /// No storage item is updated. - pub fn do_phragmen( - iterations: usize, - ) -> Option> + pub fn do_phragmen(iterations: usize) + -> Option> where ExtendedBalance: From> { + let weight_of = Self::slashable_balance_of_fn(); let mut all_nominators: Vec<(T::AccountId, VoteWeight, Vec)> = Vec::new(); let mut all_validators = Vec::new(); for (validator, _) in >::iter() { // append self vote - let self_vote = (validator.clone(), Self::slashable_balance_of_vote_weight(&validator), vec![validator.clone()]); + let self_vote = (validator.clone(), weight_of(&validator), vec![validator.clone()]); all_nominators.push(self_vote); all_validators.push(validator); } @@ -2932,7 +2943,7 @@ impl Module { (nominator, targets) }); all_nominators.extend(nominator_votes.map(|(n, ns)| { - let s = Self::slashable_balance_of_vote_weight(&n); + let s = weight_of(&n); (n, s, ns) })); @@ -2956,8 +2967,8 @@ impl Module { fn collect_exposure( supports: SupportMap, ) -> Vec<(T::AccountId, Exposure>)> { - let to_balance = |e: ExtendedBalance| - >>::convert(e); + let total_issuance = T::Currency::total_issuance(); + let to_currency = |e: ExtendedBalance| T::CurrencyToVote::to_currency(e, total_issuance); supports.into_iter().map(|(validator, support)| { // build `struct exposure` from `support` @@ -2966,7 +2977,7 @@ impl Module { let mut total: BalanceOf = Zero::zero(); support.voters .into_iter() - .map(|(nominator, weight)| (nominator, to_balance(weight))) + .map(|(nominator, weight)| (nominator, to_currency(weight))) .for_each(|(nominator, stake)| { if nominator == validator { own = own.saturating_add(stake); diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 4e9da1a3bf730..055ebb9730805 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -27,18 +27,14 @@ use frame_support::{ use sp_core::H256; use sp_io; use sp_npos_elections::{ - build_support_map, evaluate_support, reduce, ElectionScore, ExtendedBalance, StakedAssignment, + build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment, ElectionScore, }; use sp_runtime::{ curve::PiecewiseLinear, testing::{Header, TestXt, UintAuthorityId}, - traits::{Convert, IdentityLookup, SaturatedConversion, Zero}, - Perbill, -}; -use sp_staking::{ - offence::{OffenceDetails, OnOffenceHandler}, - SessionIndex, + traits::{IdentityLookup, Zero}, }; +use sp_staking::offence::{OffenceDetails, OnOffenceHandler}; use std::{cell::RefCell, collections::HashSet}; pub const INIT_TIMESTAMP: u64 = 30_000; @@ -49,19 +45,6 @@ pub(crate) type AccountIndex = u64; pub(crate) type BlockNumber = u64; pub(crate) type Balance = u128; -/// Simple structure that exposes how u64 currency can be represented as... u64. -pub struct CurrencyToVoteHandler; -impl Convert for CurrencyToVoteHandler { - fn convert(x: Balance) -> u64 { - x.saturated_into() - } -} -impl Convert for CurrencyToVoteHandler { - fn convert(x: u128) -> Balance { - x - } -} - thread_local! { static SESSION: RefCell<(Vec, HashSet)> = RefCell::new(Default::default()); static SESSION_PER_ERA: RefCell = RefCell::new(3); @@ -319,7 +302,7 @@ impl OnUnbalanced> for RewardRemainderMock { impl Trait for Test { type Currency = Balances; type UnixTime = Timestamp; - type CurrencyToVote = CurrencyToVoteHandler; + type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; type RewardRemainder = RewardRemainderMock; type Event = MetaEvent; type Slash = (); @@ -926,7 +909,7 @@ pub(crate) fn prepare_submission_with( let mut staked = sp_npos_elections::assignment_ratio_to_staked( assignments, - Staking::slashable_balance_of_vote_weight, + Staking::slashable_balance_of_fn(), ); // apply custom tweaks. awesome for testing. @@ -964,7 +947,7 @@ pub(crate) fn prepare_submission_with( let score = if compute_real_score { let staked = sp_npos_elections::assignment_ratio_to_staked( assignments_reduced.clone(), - Staking::slashable_balance_of_vote_weight, + Staking::slashable_balance_of_fn(), ); let support_map = build_support_map::( @@ -978,10 +961,8 @@ pub(crate) fn prepare_submission_with( let compact = CompactAssignments::from_assignment(assignments_reduced, nominator_index, validator_index) - .map_err(|e| { println!("error in compact: {:?}", e); e }) .expect("Failed to create compact"); - // winner ids to index let winners = winners.into_iter().map(|w| validator_index(&w).unwrap()).collect::>(); diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 2f6e6384bf879..cb4d460f68035 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -268,13 +268,9 @@ where match compact.len().checked_sub(maximum_allowed_voters as usize) { Some(to_remove) if to_remove > 0 => { // grab all voters and sort them by least stake. + let balance_of = >::slashable_balance_of_fn(); let mut voters_sorted = >::iter() - .map(|(who, _)| { - ( - who.clone(), - >::slashable_balance_of_vote_weight(&who), - ) - }) + .map(|(who, _)| (who.clone(), balance_of(&who))) .collect::>(); voters_sorted.sort_by_key(|(_, y)| *y); @@ -378,7 +374,7 @@ where // convert into absolute value and to obtain the reduced version. let mut staked = sp_npos_elections::assignment_ratio_to_staked( assignments, - >::slashable_balance_of_vote_weight, + >::slashable_balance_of_fn(), ); // reduce @@ -423,8 +419,8 @@ where let compact = compact.clone(); let assignments = compact.into_assignment(nominator_at, validator_at).unwrap(); let staked = sp_npos_elections::assignment_ratio_to_staked( - assignments, - >::slashable_balance_of_vote_weight, + assignments.clone(), + >::slashable_balance_of_fn(), ); let support_map = build_support_map::(&winners, &staked) diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index a6660de1ebbd6..57ad95bcf586f 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -193,9 +193,10 @@ pub fn get_weak_solution( who: w.clone(), distribution: vec![( w.clone(), - , u64>>::convert( - >::slashable_balance_of(&w), - ) as ExtendedBalance, + >::slashable_balance_of_vote_weight( + &w, + T::Currency::total_issuance(), + ).into(), )], }) }); @@ -220,11 +221,6 @@ pub fn get_weak_solution( .position(|x| x == a) .and_then(|i| >::try_into(i).ok()) }; - let stake_of = |who: &T::AccountId| -> VoteWeight { - , u64>>::convert( - >::slashable_balance_of(who), - ) - }; // convert back to ratio assignment. This takes less space. let low_accuracy_assignment = assignment_staked_to_ratio_normalized(staked_assignments) @@ -234,7 +230,7 @@ pub fn get_weak_solution( let score = { let staked = assignment_ratio_to_staked::<_, OffchainAccuracy, _>( low_accuracy_assignment.clone(), - stake_of + >::slashable_balance_of_fn(), ); let support_map = build_support_map::( @@ -325,7 +321,7 @@ pub fn get_single_winner_solution( let stake = >::slashable_balance_of(&winner); let stake = - , VoteWeight>>::convert(stake) as ExtendedBalance; + ::to_vote(stake, T::Currency::total_issuance()) as ExtendedBalance; let val_index = val_index as ValidatorIndex; let nom_index = nom_index as NominatorIndex; diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 1e1a1293790d2..88071f9f00202 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -23,9 +23,11 @@ use sp_std::{prelude::*, result, marker::PhantomData, ops::Div, fmt::Debug}; use codec::{FullCodec, Codec, Encode, Decode, EncodeLike}; use sp_core::u32_trait::Value as U32; use sp_runtime::{ - RuntimeDebug, ConsensusEngineId, DispatchResult, DispatchError, traits::{ + RuntimeDebug, ConsensusEngineId, DispatchResult, DispatchError, + traits::{ MaybeSerializeDeserialize, AtLeast32Bit, Saturating, TrailingZeroInput, Bounded, Zero, - BadOrigin, AtLeast32BitUnsigned + BadOrigin, AtLeast32BitUnsigned, UniqueSaturatedFrom, UniqueSaturatedInto, + SaturatedConversion, }, }; use crate::dispatch::Parameter; @@ -1710,6 +1712,73 @@ pub trait Instance: 'static { const PREFIX: &'static str ; } +/// A trait similar to `Convert` to convert values from `B` an abstract balance type +/// into u64 and back from u128. (This conversion is used in election and other places where complex +/// calculation over balance type is needed) +/// +/// Total issuance of the currency is passed in, but an implementation of this trait may or may not +/// use it. +/// +/// # WARNING +/// +/// the total issuance being passed in implies that the implementation must be aware of the fact +/// that its values can affect the outcome. This implies that if the vote value is dependent on the +/// total issuance, it should never ber written to storage for later re-use. +pub trait CurrencyToVote { + /// Convert balance to u64. + fn to_vote(value: B, issuance: B) -> u64; + + /// Convert u128 to balance. + fn to_currency(value: u128, issuance: B) -> B; +} + +/// An implementation of `CurrencyToVote` tailored for chain's that have a balance type of u128. +/// +/// The factor is the `(total_issuance / u64::max()).max(1)`, represented as u64. Let's look at the +/// important cases: +/// +/// If the chain's total issuance is less than u64::max(), this will always be 1, which means that +/// the factor will not have any effect. In this case, any account's balance is also less. Thus, +/// both of the conversions are basically an `as`; Any balance can fit in u64. +/// +/// If the chain's total issuance is more than 2*u64::max(), then a factor might be multiplied and +/// divided upon conversion. +pub struct U128CurrencyToVote; + +impl U128CurrencyToVote { + fn factor(issuance: u128) -> u128 { + (issuance / u64::max_value() as u128).max(1) + } +} + +impl CurrencyToVote for U128CurrencyToVote { + fn to_vote(value: u128, issuance: u128) -> u64 { + (value / Self::factor(issuance)).saturated_into() + } + + fn to_currency(value: u128, issuance: u128) -> u128 { + value.saturating_mul(Self::factor(issuance)) + } +} + + +/// A naive implementation of `CurrencyConvert` that simply saturates all conversions. +/// +/// # Warning +/// +/// This is designed to be used mostly for testing. Use with care, and think about the consequences. +pub struct SaturatingCurrencyToVote; + +impl + UniqueSaturatedFrom> CurrencyToVote for SaturatingCurrencyToVote { + fn to_vote(value: B, _: B) -> u64 { + value.unique_saturated_into() + } + + fn to_currency(value: u128, _: B) -> B { + B::unique_saturated_from(value) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/primitives/npos-elections/src/helpers.rs b/primitives/npos-elections/src/helpers.rs index cd8c199205cab..bfde63676c6e8 100644 --- a/primitives/npos-elections/src/helpers.rs +++ b/primitives/npos-elections/src/helpers.rs @@ -27,7 +27,7 @@ use sp_std::prelude::*; /// /// Note that this will NOT attempt at normalizing the result. pub fn assignment_ratio_to_staked( - ratio: Vec>, + ratios: Vec>, stake_of: FS, ) -> Vec> where @@ -35,7 +35,7 @@ where P: sp_std::ops::Mul, ExtendedBalance: From>, { - ratio + ratios .into_iter() .map(|a| { let stake = stake_of(&a.who);