Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Bound RewardPoints in pallet-staking #12125

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ impl pallet_staking::Config for Runtime {
type OnStakerSlash = NominationPools;
type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>;
type BenchmarkingConfig = StakingBenchmarkingConfig;
type MaxValidators = ConstU32<3072>;
}

parameter_types! {
Expand Down
8 changes: 5 additions & 3 deletions frame/staking/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,11 @@ pub mod pallet {
/// Reward a validator.
#[pallet::weight(0)]
pub fn reward_myself(origin: OriginFor<T>) -> DispatchResult {
let reported = ensure_signed(origin)?;
<staking::Pallet<T>>::reward_by_ids(vec![(reported, 10)]);
Ok(())
let reported = ensure_signed(origin)?;
let mut validator_point = BoundedBTreeMap::new();
validator_point.try_insert(reported, 10).map_err(|_| staking::Error::<T>::MaxRewardPointsReached)?;
<staking::Pallet<T>>::reward_by_ids(validator_point);
Ok(())
}
}
}
Expand Down
18 changes: 9 additions & 9 deletions frame/staking/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,16 @@ pub fn create_validator_with_nominators<T: Config>(
// Clean up any existing state.
clear_validators_and_nominators::<T>();
let mut points_total = 0;
let mut points_individual = Vec::new();
let mut points_individual = BoundedBTreeMap::new();

let (v_stash, v_controller) = create_stash_controller::<T>(0, 100, destination.clone())?;
let validator_prefs =
ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() };
Staking::<T>::validate(RawOrigin::Signed(v_controller).into(), validator_prefs)?;
let stash_lookup = T::Lookup::unlookup(v_stash.clone());

points_individual.try_insert(v_stash.clone(), 10).expect("maximum reward points reached");
points_total += 10;
points_individual.push((v_stash.clone(), 10));

let mut nominators = Vec::new();

Expand Down Expand Up @@ -117,9 +117,9 @@ pub fn create_validator_with_nominators<T: Config>(
assert_ne!(Nominators::<T>::count(), 0);

// Give Era Points
let reward = EraRewardPoints::<T::AccountId> {
let reward = EraRewardPoints::<T> {
total: points_total,
individual: points_individual.into_iter().collect(),
individual: points_individual,
};

let current_era = CurrentEra::<T>::get().unwrap();
Expand Down Expand Up @@ -670,7 +670,7 @@ benchmarks! {
<ErasStakersClipped<T>>::insert(i, dummy(), Exposure::<T::AccountId, BalanceOf<T>>::default());
<ErasValidatorPrefs<T>>::insert(i, dummy(), ValidatorPrefs::default());
<ErasValidatorReward<T>>::insert(i, BalanceOf::<T>::one());
<ErasRewardPoints<T>>::insert(i, EraRewardPoints::<T::AccountId>::default());
<ErasRewardPoints<T>>::insert(i, EraRewardPoints::<T>::default());
<ErasTotalStake<T>>::insert(i, BalanceOf::<T>::one());
ErasStartSessionIndex::<T>::insert(i, i);
}
Expand Down Expand Up @@ -747,19 +747,19 @@ benchmarks! {

let current_era = CurrentEra::<T>::get().unwrap();
let mut points_total = 0;
let mut points_individual = Vec::new();
let mut points_individual = BoundedBTreeMap::new();
let mut payout_calls_arg = Vec::new();

for validator in new_validators.iter() {
points_total += 10;
points_individual.push((validator.clone(), 10));
points_individual.try_insert(validator.clone(), 10).expect("maximum reward points reached");
payout_calls_arg.push((validator.clone(), current_era));
}

// Give Era Points
let reward = EraRewardPoints::<T::AccountId> {
let reward = EraRewardPoints::<T> {
total: points_total,
individual: points_individual.into_iter().collect(),
individual: points_individual,
};

ErasRewardPoints::<T>::insert(current_era, reward);
Expand Down
54 changes: 44 additions & 10 deletions frame/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
//! use super::*;
//! use frame_support::pallet_prelude::*;
//! use frame_system::pallet_prelude::*;
//! use frame_support::BoundedBTreeMap;
//!
//! #[pallet::pallet]
//! pub struct Pallet<T>(_);
Expand All @@ -177,8 +178,10 @@
//! #[pallet::weight(0)]
//! pub fn reward_myself(origin: OriginFor<T>) -> DispatchResult {
//! let reported = ensure_signed(origin)?;
//! <staking::Pallet<T>>::reward_by_ids(vec![(reported, 10)]);
//! Ok(())
//! let mut validator_point = BoundedBTreeMap::new();
//! validator_point.try_insert(reported, 10).map_err(|_| staking::Error::<T>::MaxRewardPointsReached)?;
//! <staking::Pallet<T>>::reward_by_ids(validator_point)?;
//! Ok(())
//! }
//! }
//! }
Expand Down Expand Up @@ -301,7 +304,9 @@ mod pallet;

use codec::{Decode, Encode, HasCompact};
use frame_support::{
parameter_types,
defensive, parameter_types,
pallet_prelude::{MaxEncodedLen, CloneNoBound},
storage::bounded_btree_map::BoundedBTreeMap,
traits::{Currency, Defensive, Get},
weights::Weight,
BoundedVec, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
Expand Down Expand Up @@ -335,7 +340,6 @@ macro_rules! log {
}

/// Counter for the number of "reward" points earned by a given validator.
pub type RewardPoint = u32;

/// The balance type of this pallet.
pub type BalanceOf<T> = <T as Config>::CurrencyBalance;
Expand Down Expand Up @@ -368,17 +372,47 @@ pub struct ActiveEraInfo {
/// 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.
#[derive(PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct EraRewardPoints<AccountId: Ord> {
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebugNoBound, CloneNoBound)]
#[cfg_attr(feature = "std", derive(frame_support::PartialEqNoBound))]
#[codec(mel_bound(T: Config))]
#[scale_info(skip_type_params(T))]
pub struct EraRewardPoints<T: Config> {
/// Total number of points. Equals the sum of reward points for each validator.
pub total: RewardPoint,
pub total: u32,
/// The reward points earned by a given validator.
pub individual: BTreeMap<AccountId, RewardPoint>,
pub individual: BoundedBTreeMap<T::AccountId, u32, T::MaxValidators>,
}

impl<AccountId: Ord> Default for EraRewardPoints<AccountId> {
impl<T: Config> Default for EraRewardPoints<T> {
fn default() -> Self {
EraRewardPoints { total: Default::default(), individual: BTreeMap::new() }
EraRewardPoints { total: Default::default(), individual: BoundedBTreeMap::new() }
}
}

impl<T: Config> EraRewardPoints<T> {
fn add_points(&mut self, validator: T::AccountId, reward_points: u32) -> Result<(), Error<T>> {
let mut points = self.individual.get(&validator).map(|x| *x).unwrap_or_default();
points += reward_points;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
points += reward_points;
points = points.saturating_add(reward_points);

if points < T::MaxValidators::get() {
match self.individual.get_mut(&validator) {
Some(current_reward_points) =>
*current_reward_points =
current_reward_points.saturating_add(reward_points),
None => self
.individual
.try_insert(validator.clone(), reward_points.clone())
.map(|old| {
if old.is_some() {
defensive!("value checked to not exist in the map");
}
})
.map_err(|_| Error::<T>::TooManyValidators)?,
}
self.total += reward_points;
Ok(())
} else {
Err(Error::<T>::TooManyValidators)
}
}
}

Expand Down
8 changes: 6 additions & 2 deletions frame/staking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ impl crate::pallet::pallet::Config for Test {
type OnStakerSlash = OnStakerSlashMock<Test>;
type BenchmarkingConfig = TestBenchmarkingConfig;
type WeightInfo = ();
type MaxValidators = ConstU32<3072>;
}

pub(crate) type StakingCall = crate::Call<Test>;
Expand Down Expand Up @@ -769,9 +770,12 @@ pub(crate) fn reward_time_per_era() -> u64 {
}

pub(crate) fn reward_all_elected() {
let rewards = <Test as Config>::SessionInterface::validators().into_iter().map(|v| (v, 1));
let mut rewards = BoundedBTreeMap::new();

<Pallet<Test>>::reward_by_ids(rewards)
for validator in <Test as Config>::SessionInterface::validators().iter() {
rewards.try_insert(validator.clone(), 1).expect("Maximum points for validator reached");
};
<Pallet<Test>>::reward_by_ids(rewards.clone()).unwrap();
}

pub(crate) fn validator_controllers() -> Vec<AccountId> {
Expand Down
27 changes: 18 additions & 9 deletions frame/staking/src/pallet/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use frame_election_provider_support::{
};
use frame_support::{
pallet_prelude::*,
storage::bounded_btree_map::BoundedBTreeMap,
traits::{
Currency, CurrencyToVote, Defensive, EstimateNextNewSession, Get, Imbalance,
LockableCurrency, OnUnbalanced, UnixTime, WithdrawReasons,
Expand Down Expand Up @@ -623,15 +624,16 @@ impl<T: Config> Pallet<T> {
/// relatively to their points.
///
/// COMPLEXITY: Complexity is `number_of_validator_to_reward x current_elected_len`.
pub fn reward_by_ids(validators_points: impl IntoIterator<Item = (T::AccountId, u32)>) {
pub fn reward_by_ids(validators_points: BoundedBTreeMap<T::AccountId, u32, T::MaxValidators>) -> DispatchResult {
if let Some(active_era) = Self::active_era() {
<ErasRewardPoints<T>>::mutate(active_era.index, |era_rewards| {
for (validator, points) in validators_points.into_iter() {
*era_rewards.individual.entry(validator).or_default() += points;
era_rewards.total += points;
<ErasRewardPoints<T>>::try_mutate(active_era.index, |era_rewards| -> DispatchResult {
for (validator, points) in validators_points.iter() {
era_rewards.add_points(validator.clone(), points.clone())?;
}
});
}
Ok(())
})?;
}
Ok(())
}

/// Ensures that at the end of the current session there will be a new era.
Expand Down Expand Up @@ -1128,12 +1130,19 @@ where
T: Config + pallet_authorship::Config + pallet_session::Config,
{
fn note_author(author: T::AccountId) {
Self::reward_by_ids(vec![(author, 20)])
let mut validator_points = BoundedBTreeMap::new();
validator_points.try_insert(author, 20).unwrap_or_default();

Self::reward_by_ids(validator_points).unwrap();
}
fn note_uncle(uncle_author: T::AccountId, _age: T::BlockNumber) {
// defensive-only: block author must exist.
let mut validator_points = BoundedBTreeMap::new();

if let Some(block_author) = <pallet_authorship::Pallet<T>>::author() {
Self::reward_by_ids(vec![(block_author, 2), (uncle_author, 1)])
validator_points.try_insert(block_author, 2).unwrap_or_default();
validator_points.try_insert(uncle_author, 1).unwrap_or_default();
Self::reward_by_ids(validator_points).unwrap();
} else {
crate::log!(warn, "block author not set, this should never happen");
}
Expand Down
10 changes: 8 additions & 2 deletions frame/staking/src/pallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ pub mod pallet {

/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;

// Maximum number of validators.
#[pallet::constant]
type MaxValidators: Get<u32>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we set the ValidatorCount, we should make sure we are not above this limit.

}

#[pallet::type_value]
Expand Down Expand Up @@ -399,7 +403,7 @@ pub mod pallet {
#[pallet::storage]
#[pallet::getter(fn eras_reward_points)]
pub type ErasRewardPoints<T: Config> =
StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints<T::AccountId>, ValueQuery>;
StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints<T>, ValueQuery>;

/// The total amount staked for the last `HISTORY_DEPTH` eras.
/// If total hasn't been set or has been removed then 0 stake is returned.
Expand Down Expand Up @@ -683,7 +687,7 @@ pub mod pallet {
AlreadyClaimed,
/// Incorrect previous history depth input provided.
IncorrectHistoryDepth,
/// Incorrect number of slashing spans provided.
/// Incorrect number Bound RewardPointsin pallet-staking #12039 of slashing spans provided.
IncorrectSlashingSpans,
/// Internal state has become somehow corrupted and the operation cannot continue.
BadState,
Expand All @@ -701,6 +705,8 @@ pub mod pallet {
TooManyValidators,
/// Commission is too low. Must be at least `MinCommission`.
CommissionTooLow,
/// Maximum reward points for given validator reached.
MaxRewardPointsReached,
}

#[pallet::hooks]
Expand Down
Loading