-
Notifications
You must be signed in to change notification settings - Fork 550
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds stake-tracker
pallet and integrates with the staking pallet
#1933
base: master
Are you sure you want to change the base?
Conversation
@gpestana https://gitlab.parity.io/parity/mirrors/polkadot-sdk/-/jobs/6383364 was started for your command Comment |
@gpestana Command |
bot bench polkadot-pallet --runtime=westend --pallet=pallet_staking |
@gpestana https://gitlab.parity.io/parity/mirrors/polkadot-sdk/-/jobs/6387304 was started for your command Comment |
…=westend --target_dir=polkadot --pallet=pallet_staking
@gpestana Command |
bot bench polkadot-pallet --pallet=pallet_staking |
@gpestana https://gitlab.parity.io/parity/mirrors/polkadot-sdk/-/jobs/6388231 was started for your command Comment |
@gpestana Command |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have reviewed most of this, and looks mainly good to me.
The only major issue I see is that the MBM step is way too small, and prefer not to pause the chain for a long long time.
]; | ||
|
||
/// Upper thresholds delimiting the targets bag list. | ||
pub const TARGET_THRESHOLDS: [u128; 200] = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the distribution of initial validators in these bags good? We could go for a different bag distribution for targets, given that approval stakes are generally much higher. Just an option.
|
||
/// Fired when a portion of a staker's balance has been withdrawn. | ||
fn on_withdraw(_stash: &AccountId, _amount: Balance) {} | ||
/// Representation of the `OnStakingUpdate` events. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This gives me a possible refactoring idea which I want to share but I don't necessarily want you to do, unless if you see clear benefits to it:
trait OnStakingUpdate {
fn update(event: OnStakingUpdateEvent) {}
}
pub enum OnStakingUpdateEvent { .. }
And now we just have the events listed once :D
} | ||
|
||
impl pallet_balances::Config for Test { | ||
type Balance = Balance; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use pallet_balances::TestDefaultConfig
please :)
I am generally going to shamelessly be more demanding on requesting people to use our latest features internally :D
|
||
/// V13 Multi-block migration to introduce the stake-tracker pallet. | ||
/// | ||
/// A step of the migration consists of processing one nominator in the [`Nominators`] list or one |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means the migration would take thousands of blocks, right?
not great, as all other extrinsics are blocked while this happens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason, from reading the docs first time, I got the impression that the migrations logic would fit as many steps in a block as possible. After revisiting the code and docs again, I see that's not the case and that a step will be executed once per block as you mentioned.
I will refactor this code accordingly and try to fit as many nominator migration per block as possible, thanks!
if meter.remaining().any_lt(required) { | ||
return Err(SteppedMigrationError::InsufficientWeight { required }); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the actual outcome when we return this? the chain will ditch this migration and move on? or just this step and it will retry?
// if no nominations are left, chill the nominator. | ||
let _ = <Pallet<T> as StakingInterface>::chill(&who) | ||
.map_err(|e| { | ||
log!(error, "error when chilling {:?}", who); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How many of these we have in Polkadot now?
The CI pipeline was cancelled due to failure one of the required jobs. |
This PR adds and integrates the
stake-tracker
pallet in the staking system.Goals:
TargetList
list of validators strictly and always sorted by their approval votes. Approvals consist of validator's self-vote and the sum of all the corresponding nominations across all the system.TargetList
sorting must be always kept up to date, even in the event of new nomination updates, nominator/validator slashes and rewards. Thestake-tracker
pallet must ensure that the scores of the targets are always up to date and the targets are sorted by score at all time.VoterList
list of voters that may be either 1) strictly and always sorted by their score (i.e. bonded stake of an individual voter) or 2) loosely sorted list. Choosing between mode 1) and 2) can be done through stake-tracker configurations.TL;DR 2nd order changes
T::TargetList
, insofar as there are still nominators nominating it;Staking::nominate
will fail;Staking::nominate
will remove all the duplicate nominations implicitly, if any.Why?
Currently, we select up to
N
registered validators to be part of the snapshot for the next election. When the number of registered validators in the system exceeds that number, we'll need to have an efficient way to select the top validators with more approvals stake to construct the snapshot.Thus, we need to keep list of validators sorted by their approval stakes at all time. This means that any update to nominations and their stake (due to slashing, bonding extra, rewards, etc) needs to be reflected in the targets nominated by the stake. Enters the
pallet-stake-tracker
: this pallet keeps track of relevant staking events (through implementing thetrait OnStakingEvent
) and updates the target bags list with the target's approvals stake accordingly.In order to achieve this, the target list must keep track of all target stashes that have at least one nomination in the system (i.e. their
approval_stake > 0
), regardless of their state. This means that it needs to keep track of the stake of active validators, idle validator and even targets that are not validators anymore but have still nominations lingering.How?
The
stake-tracker
pallet implements theOnStakingUpdate
trait to listen to staking events and multiplexes those events to one or multiple types (e.g. pallets). The stake tracker pallet is used as a degree of indirection to maintain the target and voter semi-sorted lists (implemented by the bags list pallet) up to date.The main goal is that all the updates to the targets and voters lists are performed at each relevant staking event through the stake-tracker pallet. However, the voter and target list reads are performed directly through the
SortedListProvider
set in the staking's config.Changes to assumptions in chilled and removed stakers
This PR changes some assumptions behind chilled stakers: the chilled/idle validators will be kept in the
Target
lists, and only removed from the target list when:This allows the
stake-tracker
to keep track of the chilled validators's and respective score after the validator is chilled and completely unbonds. This way, when a validator sets the intention to re-validate, the target's score is brought up with the correct sum of approvals in the system (i.e. self stake + all current nominations, which have been set previous to the re-validation).Changes to
Call::nominate
New nominations can only be performed on active or chilling validators. "Moot" nominations still exist (i.e. nominations that points at an inactive/inexistent validator), but only when a validator stops nominating or is chilled (in which case it may remain in the target list if the approvals are higher than 0).
In addition, the runtime ensures that each nominator does not nominates a target more than once at a time. This is achieved by deduplicating the nominations in the extrinsic
Staking::nominate
.Changes to
OnStakingUpdate
1. New methods
Added a couple more methods to the
OnStakingUpdate
trait in order to differentiate removed stakers from idle (chilling) stakers. For a rationale on why this is needed see in this discussion #1933 (comment).2. Refactor existing methods for safety
With this refactor, the event emitter needs to explicitly call the events with more information about the event. The advantage is that this new interface design prevents potential issues with data sync, e.g. the event emitter does not necessarily need to update the staking state before emitting the events and the OnStakingUpdate implementor does not need to rely as much on the staking interface, making the interface less error prone.
Changes to
SortedListProvider
Added a new method to the trait, gated by
try-runtime
, which returns whether a given node is in the correct position in the list or not given its current score. This method will help with the try state checks for the target list.Migrations
The migration code has been validated against the Polkadot using the externalities tests in polkadot/runtime/westend/src/lib.rs. Upon running the migrations, we ensure that:
Check #4673 for more info on migrations and related tests.
Note that the migrations will "clean" the current nominations in the system namely:
Weight complexity
Keeping both target and voter list sorted based on their scores requires their scores to be up to date after single operations (add nominator/validator, update stake, etc) and composite staking events (validator slashing and payouts). See https://hackmd.io/FH8Uhi2aQ5mD0aMm-BbqMQ for more details and back of the envelope calculations.
This PR #4686 shows how the target list affects the staking's
MaxExposurePageSize
based on benchmarks with different modes. In sum:To do later
A. Remove legacy
CurrencyToVote
CurrencyToVote
in staking #4725Remove the need for the
CurrencyToVote
converter type in the pallet-staking. This type converts coverts fromBalanceOf<T>
to au64
"vote" type, and from a safeu128
(i.e.ExtendedBalance
) back toBalanceOf<T>
. In both conversion directions, the total issuance of the system must be provided.The main reason for this convertion is that the current phragmen implementation does not correctly support types
u128
as the main type. Thus, the conversion between balance (u128
) and the supported "vote"u64
type.Relying on the current issuance will be a problem with the staking parachain (let's assume that the staking runtime is not deployed in AH). In addition, it removing the need for this conversion will simplify and make it cheaper to run the stake-tracker and the associated list updates.
To finish
stake-tracker
pallet and integration with the Staking palletvalidate
try-runtime
checks instake-tracker
which also run after the staking pallet testsCall::drop_dangling_nomination
Closes #442