This repository has been archived by the owner on Nov 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Add Score to Bags List #11357
Merged
Merged
Add Score to Bags List #11357
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
1b8e11b
Add Score to Bags List
shawntabrizi 8a19341
fix ordering
shawntabrizi 8ac50da
make compile
shawntabrizi 8825e6b
in progress migration
shawntabrizi 5fef49c
make migration compile
shawntabrizi 3d16d73
remove old check
shawntabrizi dfb0ca8
remove runtime specific migration
shawntabrizi 9492773
Merge branch 'master' into shawntabrizi-add-score
shawntabrizi ef2408f
fix warning
shawntabrizi 238f7b1
Apply suggestions from code review
shawntabrizi 8943fee
improve migration
shawntabrizi 7e86363
fix
shawntabrizi 0f516d8
Merge branch 'master' into shawntabrizi-add-score
shawntabrizi 213261c
Merge branch 'master' into shawntabrizi-add-score
shawntabrizi 338cb45
fix merge
shawntabrizi 26651c6
fmt
shawntabrizi 5a49d2b
Update migrations.rs
shawntabrizi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ use crate::Config; | |
use codec::{Decode, Encode, MaxEncodedLen}; | ||
use frame_election_provider_support::ScoreProvider; | ||
use frame_support::{ | ||
ensure, | ||
traits::{Defensive, Get}, | ||
DefaultNoBound, PalletError, | ||
}; | ||
|
@@ -38,7 +39,7 @@ use sp_std::{ | |
collections::{btree_map::BTreeMap, btree_set::BTreeSet}, | ||
iter, | ||
marker::PhantomData, | ||
vec::Vec, | ||
prelude::*, | ||
}; | ||
|
||
#[derive(Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo, PalletError)] | ||
|
@@ -227,6 +228,11 @@ impl<T: Config<I>, I: 'static> List<T, I> { | |
crate::ListNodes::<T, I>::contains_key(id) | ||
} | ||
|
||
/// Get the score of the given node, | ||
pub fn get_score(id: &T::AccountId) -> Result<T::Score, ListError> { | ||
Node::<T, I>::get(id).map(|node| node.score()).ok_or(ListError::NodeNotFound) | ||
} | ||
|
||
/// Iterate over all nodes in all bags in the list. | ||
/// | ||
/// Full iteration can be expensive; it's recommended to limit the number of items with | ||
|
@@ -310,7 +316,7 @@ impl<T: Config<I>, I: 'static> List<T, I> { | |
let bag_score = notional_bag_for::<T, I>(score); | ||
let mut bag = Bag::<T, I>::get_or_make(bag_score); | ||
// unchecked insertion is okay; we just got the correct `notional_bag_for`. | ||
bag.insert_unchecked(id.clone()); | ||
bag.insert_unchecked(id.clone(), score); | ||
|
||
// new inserts are always the tail, so we must write the bag. | ||
bag.put(); | ||
|
@@ -381,16 +387,18 @@ impl<T: Config<I>, I: 'static> List<T, I> { | |
/// If the node was in the correct bag, no effect. If the node was in the incorrect bag, they | ||
/// are moved into the correct bag. | ||
/// | ||
/// Returns `Some((old_idx, new_idx))` if the node moved, otherwise `None`. | ||
/// Returns `Some((old_idx, new_idx))` if the node moved, otherwise `None`. In both cases, the | ||
/// node's score is written to the `score` field. Thus, this is not a noop, even if `None`. | ||
/// | ||
/// This operation is somewhat more efficient than simply calling [`self.remove`] followed by | ||
/// [`self.insert`]. However, given large quantities of nodes to move, it may be more efficient | ||
/// to call [`self.remove_many`] followed by [`self.insert_many`]. | ||
pub(crate) fn update_position_for( | ||
node: Node<T, I>, | ||
mut node: Node<T, I>, | ||
new_score: T::Score, | ||
) -> Option<(T::Score, T::Score)> { | ||
node.is_misplaced(new_score).then(move || { | ||
node.score = new_score; | ||
if node.is_misplaced(new_score) { | ||
let old_bag_upper = node.bag_upper; | ||
|
||
if !node.is_terminal() { | ||
|
@@ -402,12 +410,9 @@ impl<T: Config<I>, I: 'static> List<T, I> { | |
bag.remove_node_unchecked(&node); | ||
bag.put(); | ||
} else { | ||
crate::log!( | ||
error, | ||
"Node {:?} did not have a bag; ListBags is in an inconsistent state", | ||
node.id, | ||
frame_support::defensive!( | ||
"Node did not have a bag; BagsList is in an inconsistent state" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to display the node ID here? |
||
); | ||
debug_assert!(false, "every node must have an extant bag associated with it"); | ||
} | ||
|
||
// put the node into the appropriate new bag. | ||
|
@@ -418,8 +423,12 @@ impl<T: Config<I>, I: 'static> List<T, I> { | |
bag.insert_node_unchecked(node); | ||
bag.put(); | ||
|
||
(old_bag_upper, new_bag_upper) | ||
}) | ||
Some((old_bag_upper, new_bag_upper)) | ||
} else { | ||
// just write the new score. | ||
node.put(); | ||
None | ||
} | ||
} | ||
|
||
/// Put `heavier_id` to the position directly in front of `lighter_id`. Both ids must be in the | ||
|
@@ -428,8 +437,6 @@ impl<T: Config<I>, I: 'static> List<T, I> { | |
lighter_id: &T::AccountId, | ||
heavier_id: &T::AccountId, | ||
) -> Result<(), ListError> { | ||
use frame_support::ensure; | ||
|
||
let lighter_node = Node::<T, I>::get(&lighter_id).ok_or(ListError::NodeNotFound)?; | ||
let heavier_node = Node::<T, I>::get(&heavier_id).ok_or(ListError::NodeNotFound)?; | ||
|
||
|
@@ -510,7 +517,6 @@ impl<T: Config<I>, I: 'static> List<T, I> { | |
/// all bags and nodes are checked per *any* update to `List`. | ||
#[cfg(feature = "std")] | ||
pub(crate) fn sanity_check() -> Result<(), &'static str> { | ||
use frame_support::ensure; | ||
let mut seen_in_list = BTreeSet::new(); | ||
ensure!( | ||
Self::iter().map(|node| node.id).all(|id| seen_in_list.insert(id)), | ||
|
@@ -523,7 +529,7 @@ impl<T: Config<I>, I: 'static> List<T, I> { | |
ensure!(iter_count == stored_count, "iter_count != stored_count"); | ||
ensure!(stored_count == nodes_count, "stored_count != nodes_count"); | ||
|
||
crate::log!(debug, "count of nodes: {}", stored_count); | ||
crate::log!(trace, "count of nodes: {}", stored_count); | ||
|
||
let active_bags = { | ||
let thresholds = T::BagThresholds::get().iter().copied(); | ||
|
@@ -544,7 +550,7 @@ impl<T: Config<I>, I: 'static> List<T, I> { | |
active_bags.clone().fold(0u32, |acc, cur| acc + cur.iter().count() as u32); | ||
ensure!(nodes_count == nodes_in_bags_count, "stored_count != nodes_in_bags_count"); | ||
|
||
crate::log!(debug, "count of active bags {}", active_bags.count()); | ||
crate::log!(trace, "count of active bags {}", active_bags.count()); | ||
|
||
// check that all nodes are sane. We check the `ListNodes` storage item directly in case we | ||
// have some "stale" nodes that are not in a bag. | ||
|
@@ -667,7 +673,7 @@ impl<T: Config<I>, I: 'static> Bag<T, I> { | |
/// | ||
/// Storage note: this modifies storage, but only for the nodes. You still need to call | ||
/// `self.put()` after use. | ||
fn insert_unchecked(&mut self, id: T::AccountId) { | ||
fn insert_unchecked(&mut self, id: T::AccountId, score: T::Score) { | ||
// insert_node will overwrite `prev`, `next` and `bag_upper` to the proper values. As long | ||
// as this bag is the correct one, we're good. All calls to this must come after getting the | ||
// correct [`notional_bag_for`]. | ||
|
@@ -676,6 +682,7 @@ impl<T: Config<I>, I: 'static> Bag<T, I> { | |
prev: None, | ||
next: None, | ||
bag_upper: Zero::zero(), | ||
score, | ||
_phantom: PhantomData, | ||
}); | ||
} | ||
|
@@ -784,11 +791,6 @@ impl<T: Config<I>, I: 'static> Bag<T, I> { | |
Ok(()) | ||
} | ||
|
||
#[cfg(not(feature = "std"))] | ||
fn sanity_check(&self) -> Result<(), &'static str> { | ||
Ok(()) | ||
} | ||
|
||
/// Iterate over the nodes in this bag (public for tests). | ||
#[cfg(feature = "std")] | ||
#[allow(dead_code)] | ||
|
@@ -809,12 +811,13 @@ impl<T: Config<I>, I: 'static> Bag<T, I> { | |
#[scale_info(skip_type_params(T, I))] | ||
#[cfg_attr(feature = "std", derive(frame_support::DebugNoBound, Clone, PartialEq))] | ||
pub struct Node<T: Config<I>, I: 'static = ()> { | ||
id: T::AccountId, | ||
prev: Option<T::AccountId>, | ||
next: Option<T::AccountId>, | ||
bag_upper: T::Score, | ||
pub(crate) id: T::AccountId, | ||
shawntabrizi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub(crate) prev: Option<T::AccountId>, | ||
pub(crate) next: Option<T::AccountId>, | ||
pub(crate) bag_upper: T::Score, | ||
pub(crate) score: T::Score, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need a migration since you added a field to storage?
|
||
#[codec(skip)] | ||
_phantom: PhantomData<I>, | ||
pub(crate) _phantom: PhantomData<I>, | ||
} | ||
|
||
impl<T: Config<I>, I: 'static> Node<T, I> { | ||
|
@@ -877,13 +880,23 @@ impl<T: Config<I>, I: 'static> Node<T, I> { | |
&self.id | ||
} | ||
|
||
/// Get the current vote weight of the node. | ||
pub(crate) fn score(&self) -> T::Score { | ||
self.score | ||
} | ||
|
||
/// Get the underlying voter (public fo tests). | ||
#[cfg(feature = "std")] | ||
#[allow(dead_code)] | ||
pub fn std_id(&self) -> &T::AccountId { | ||
&self.id | ||
} | ||
|
||
#[cfg(any(feature = "runtime-benchmarks", test))] | ||
pub fn set_score(&mut self, s: T::Score) { | ||
self.score = s | ||
} | ||
|
||
/// The bag this nodes belongs to (public for benchmarks). | ||
#[cfg(feature = "runtime-benchmarks")] | ||
#[allow(dead_code)] | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
@kianenigma can you sanity check the updated logic here
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.
you should be able to help me double check: Both
on_update
andon_remove
in my base PR return an error if the node does not exist, which didn't used to be the case before that.Seems like these changes went in your previous PR:
https://github.com/paritytech/substrate/pull/11342/files#diff-010d9ffd43a3e48c2e844e8223d9434dd3505d537942408f03af5c7716d14fa3R333
https://github.com/paritytech/substrate/pull/11342/files#diff-8d945d41310dfefb2a974a743cc5f7c27861d6d4b43b5808783e3fa7095fc8c6R263
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.
Does it have to be unwrap? Can we use
expect
andexpect_err
instead?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.
its a fuzzer, not a big deal either way? but yeah, expect with a message would be better