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

Conversation

@kianenigma
Copy link
Contributor

This PR adds the groundwork to store validators in bags-list, sorted based on stake. This builds on top of #10821 and should be merged after it.

Similar to what we did with nominators, we can deploy this in multiple stages:

  1. current code can be deployed as is
  2. Then, we can update staking to Release::V10, and migrate all validators to the bags-list, but use a hypothetical UseValidatorsMapAndUpdateBagsList.
  3. Once confident, we switch to type TargetList = ValidatorsBagsList.

@kianenigma kianenigma added A0-please_review Pull request needs code review. B0-silent Changes should not be mentioned in any release notes C1-low PR touches the given topic and has a low impact on builders. D9-needsaudit 👮 PR contains changes to fund-managing logic that should be properly reviewed and externally audited labels Feb 28, 2022
//! well-known origin, derivable deterministically from the set of account IDs and the threshold
//! number of accounts from the set that must approve it. In the case that the threshold is just one
//! then this is a stateless operation. This is useful for multisig wallets where cryptographic
//! threshold signatures are not available or desired.
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like just a line re-wrap? IMO it would be preferable to keep this out since it seems totally unrelated

Comment on lines +774 to +775
V9_0_0, // inject validators into `NPoSVoteProvider` as well.
V10_0_0, // inject validator into `NPoSTargetList`.
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
V9_0_0, // inject validators into `NPoSVoteProvider` as well.
V10_0_0, // inject validator into `NPoSTargetList`.
V9_0_0, // inject validators into `VoterList` as well.
V10_0_0, // inject validator into `TargetList`.

T::BlockWeights::get().max_block
} else {
log!(warn, "InjectValidatorsIntoSortedListProvider being executed on the wrong storage version, expected Releases::V8_0_0");
log!(warn, "InjectValidatorsIntoVoterList being executed on the wrong storage version, expected Releases::V8_0_0");
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
log!(warn, "InjectValidatorsIntoVoterList being executed on the wrong storage version, expected Releases::V8_0_0");
log!(warn, "InjectValidatorsIntoVoterList being attempted on the wrong storage version, expected Releases::V8_0_0");

maybe? Executed makes it sound like we did the migration anyways

StorageVersion::<T>::put(Releases::V10_0_0);
T::BlockWeights::get().max_block
} else {
log!(warn, "InjectValidatorsIntoTargetList being executed on the wrong storage version, expected Releases::V9_0_0");
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
log!(warn, "InjectValidatorsIntoTargetList being executed on the wrong storage version, expected Releases::V9_0_0");
log!(warn, "InjectValidatorsIntoTargetList being attempted on the wrong storage version, expected Releases::V9_0_0");

Same rationale as above - although not really a big deal


#[cfg(feature = "try-runtime")]
fn post_upgrade() -> Result<(), &'static str> {
ensure!(StorageVersion::<T>::get(), Releases::V10_0_0, "must upgrade linearly");
Copy link
Contributor

Choose a reason for hiding this comment

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

We should ensure!(Validators::count() == TargetList::count(), "each validator must be in the target list")

Copy link
Contributor

@emostov emostov Mar 1, 2022

Choose a reason for hiding this comment

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

Also T::TargetList::sanity_check().map_err(|_| "TargetList is not in a sane state.")?;

Comment on lines +74 to +79
for (v, _) in Validators::<T>::iter() {
let weight = Pallet::<T>::vote_weight(&v);
let _ = T::TargetList::on_insert(v.clone(), weight).map_err(|err| {
log!(warn, "failed to insert {:?} into TargetList: {:?}", v, err)
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

To avoid duplicate code we can instead use:

TargetList::unsafe_regenerate(				
    Validators::<T>::iter().map(|(id, _)| id),
	Pallet::<T>::weight_of_fn(),
)

There isn't any performance benefit though (originally there was, but that never made it into master because we refactored insert_many to just use the normal insert)

})
pub fn get_npos_targets(maybe_max_len: Option<usize>) -> Vec<T::AccountId> {
let targets = T::TargetList::iter()
.take(maybe_max_len.unwrap_or_else(Bounded::max_value))
Copy link
Contributor

Choose a reason for hiding this comment

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

Bounded::max_value is usize::MAX? This is a bit scary, especially since the point of using the bags list here is to allow it to be virtually unbounded. IMO we should either hardcode a fallback or have a configurable fallback value.


/// Perform all checks related to both [`Config::TargetList`] and [`Config::VoterList`].
#[cfg(debug_assertions)]
fn sanity_check_list_providers() {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

}

/// Perform all checks related to both [`Config::TargetList`] and [`Config::VoterList`].
#[cfg(debug_assertions)]
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this function is gated by #[cfg(debug_assertions)], could we remove that same expression from the calls sites i.e. change

#[cfg(debug_assertions)]
Self::sanity_check_list_providers();

to simply

Self::sanity_check_list_providers();

if maybe_max_len.map_or(false, |max_len| target_count > max_len as u32) {
return Err("Target snapshot too big")
let targets = Self::get_npos_targets(maybe_max_len);
if maybe_max_len.map_or(false, |m| targets.len() > m) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If you take my suggestion: https://github.com/paritytech/substrate/pull/10944/files#r816353287, then we should also make sure to check that fallback the upper bound respected if maybe_max_len is None

<Nominators<T>>::remove_all();

T::SortedListProvider::unsafe_clear();
T::VoterList::unsafe_clear();
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
T::VoterList::unsafe_clear();
T::VoterList::unsafe_clear();
T::TargetList::unsafe_clear();


/// A simple voter list implementation that does not require any additional pallets. Note, this
/// A simple sorted list implementation that does not require any additional pallets. Note, this
/// does not provided nominators in sorted ordered. If you desire nominators in a sorted order take
Copy link
Contributor

@emostov emostov Mar 1, 2022

Choose a reason for hiding this comment

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

Suggested change
/// does not provided nominators in sorted ordered. If you desire nominators in a sorted order take
/// does not provide voters in sorted ordered. If you desire voters in a sorted order take

}

/// A simple sorted list implementation that does not require any additional pallets. Note, this
/// does not provided validators in sorted ordered. If you desire nominators in a sorted order take
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
/// does not provided validators in sorted ordered. If you desire nominators in a sorted order take
/// does not provide validators in sorted ordered. If you desire nominators in a sorted order take


// NOTE: safe to call outside block production
T::SortedListProvider::unsafe_clear();
T::VoterList::unsafe_clear();
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
T::VoterList::unsafe_clear();
T::VoterList::unsafe_clear();
T::TargetList::unsafe_clear();

@emostov
Copy link
Contributor

emostov commented Mar 1, 2022

Check list for making sure target list (not voter list) is correctly called. Columns are the stakers state, rows are the extrinsics that can be applied to a staker.

extrinsic Chilled Nominator Validator
chill, chill_other nothing nothing on_remove
nominate nothing nothing on_remove
validate on_insert on_insert nothing
bond_extra nothing nothing on_update
rebond nothing nothing on_update
unbond nothing nothing on_update

I checked and this PR looks good against the above requirements. Would be good if others could double check, and also add anything that might be missing here

}
if T::TargetList::contains(&stash) {
T::TargetList::on_update(&stash, Self::weight_of(&ledger.stash));
debug_assert_eq!(T::VoterList::sanity_check(), Ok(()));
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
debug_assert_eq!(T::VoterList::sanity_check(), Ok(()));
debug_assert_eq!(T::TargetList::sanity_check(), Ok(()));

We also can remove the debug assert here and above and instead put Self::sanity_check_list_providers(); after these two if statements

// NOTE: ledger must be updated prior to calling `Self::weight_of`.
Self::update_ledger(&controller, &ledger);

// update this staker in the sorted list, if they exist in it.
Copy link
Contributor

@emostov emostov Mar 1, 2022

Choose a reason for hiding this comment

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

Suggested change
// update this staker in the sorted list, if they exist in it.
// update this staker in the relevant list(s).

// NOTE: ledger must be updated prior to calling `Self::weight_of`.
Self::update_ledger(&controller, &ledger);

// update this staker in the sorted list, if they exist in it.
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
// update this staker in the sorted list, if they exist in it.
// update this staker in the relevant list(s).

@kianenigma
Copy link
Contributor Author

This will be re-opened again with a new version that keeps track of approval stakes, not self-stakes.

@kianenigma kianenigma closed this Mar 8, 2022
@louismerlin louismerlin removed the D9-needsaudit 👮 PR contains changes to fund-managing logic that should be properly reviewed and externally audited label Oct 13, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

A0-please_review Pull request needs code review. B0-silent Changes should not be mentioned in any release notes C1-low PR touches the given topic and has a low impact on builders.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants