Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
8cb3404
Implements dynamic nominations per nominator
gpestana Dec 16, 2022
c9b8a17
Adds SnapshotBounds and ElectionSizeTracker
gpestana Dec 19, 2022
fcc7657
Changes the ElectionDataProvider interface to receive ElectionBounds …
gpestana Dec 19, 2022
3ae3e29
Implements get_npos_voters with ElectionBounds
gpestana Dec 19, 2022
98f6e82
Implements get_npos_targets with ElectionBounds
gpestana Dec 19, 2022
5434b47
Adds comments
gpestana Dec 19, 2022
0a7b714
tests
gpestana Dec 21, 2022
1c347f9
Truncates nomninations that exceed nominations quota; Old tests passing
gpestana Dec 26, 2022
9bf66c3
Uses DataProviderBounds and ElectionBounds (to continue)
gpestana Dec 27, 2022
cdaeb7c
Finishes conversions - tests passing
gpestana Dec 27, 2022
4e38839
Refactor staking in babe mocks
gpestana Dec 27, 2022
11bd646
Replaces MaxElectableTargets and MaxElectingVoters with ElectionBound…
gpestana Dec 27, 2022
3fe1076
Merge branch 'master' into gpestana/staking-dynamic-nominators
gpestana Dec 27, 2022
a1f77ad
Fixes nits; node compiling
gpestana Dec 28, 2022
905b8e0
bechmarks
gpestana Dec 29, 2022
775fe25
removes nomination_quota extrinsic to request the nomination quota
gpestana Jan 3, 2023
eb2456b
Lazy quota check, ie. at nominate time only
gpestana Jan 3, 2023
b0b21a4
remove non-working test (for now)
gpestana Jan 3, 2023
82ac2db
tests lazy nominations quota when quota is lower than current number …
gpestana Jan 4, 2023
8218e17
Adds runtime API and custom RPC call for clients to query the nominat…
gpestana Jan 4, 2023
af0c52b
removes old rpc
gpestana Feb 28, 2023
5b5231a
Merge branch 'master' into gpestana/staking-dynamic-nominators
gpestana Feb 28, 2023
8e7ecbb
Cosmetic touches
gpestana Feb 28, 2023
bfbb91b
All mocks working
gpestana Feb 28, 2023
2c44857
Fixes benchmarking mocks
gpestana Feb 28, 2023
1bf29f7
nits
gpestana Feb 28, 2023
125ff54
more tests
gpestana Mar 1, 2023
7d9fdfc
renames trait methods
gpestana Mar 1, 2023
8fddf41
nit
gpestana Mar 1, 2023
805e3f7
".git/.scripts/commands/fmt/fmt.sh"
Mar 1, 2023
3317bd7
Fix V2 PoV benchmarking (#13485)
ggwpez Feb 28, 2023
1c20979
Move BEEFY code to consensus (#13484)
davxy Feb 28, 2023
8ba9e1e
chore: move genesis block builder to chain-spec crate. (#13427)
yjhmelody Feb 28, 2023
761d7ae
Speed up storage iteration from within the runtime (#13479)
koute Mar 1, 2023
3136966
Make unbounded channels size warning exact (part 1) (#13490)
dmitry-markin Mar 1, 2023
8d78bee
Removal of Prometheus alerting rules deployment in cloud-infra (#13499)
lazam Mar 1, 2023
397ce89
sp-consensus: remove unused error variants (#13495)
andresilva Mar 1, 2023
1349e6a
Expose `ChargedAmount` (#13488)
pmikolajczyk41 Mar 1, 2023
92b1231
sc-consensus-beefy: fix metrics: use correct names (#13494)
acatangiu Mar 1, 2023
42eeb97
clippy fix
gpestana Mar 1, 2023
5a29c9c
Merge remote-tracking branch 'origin/master' into gpestana/staking-dy…
Mar 1, 2023
f51083b
Merge remote-tracking branch 'origin/master' into gpestana/staking-dy…
Mar 6, 2023
d067a1c
removes NominationsQuotaExceeded event
gpestana Mar 6, 2023
422393c
Update frame/staking/src/lib.rs
gpestana Mar 16, 2023
fb418a9
adds back the npos_max_iter
gpestana Mar 16, 2023
b786d33
Merge branch 'master' into gpestana/staking-dynamic-nominators
gpestana Mar 16, 2023
af3b8ba
remove duplicate imports added after merge
gpestana Mar 16, 2023
7d8ffda
fmt
gpestana Mar 16, 2023
1246c73
Adds comment in public struct; Refactors CountBound and SizeCount to …
gpestana Mar 21, 2023
325c91f
addresses various pr comments
gpestana Mar 21, 2023
1f8f502
PR comment reviews
gpestana Mar 23, 2023
67c4734
Fixes on-chain election bounds and related code
gpestana Mar 24, 2023
70d8579
EPM checks the size of the voter list returned by the data provider
gpestana Mar 24, 2023
a68a37d
cosmetic changes
gpestana Mar 24, 2023
849a2e0
updates e2e tests mock
gpestana Mar 24, 2023
306668e
Adds more tests for size tracker and refactors code
gpestana Mar 25, 2023
7ec6305
Adds back only_iterates_max_2_times_max_allowed_len test
gpestana Mar 25, 2023
e8576b7
Refactor
gpestana Mar 25, 2023
e12f287
removes unecessary dependency
gpestana Mar 25, 2023
c7acde4
Merge branch 'master' into gpestana/staking-dynamic-nominators
gpestana Mar 25, 2023
61a8ca5
empty commit -- restart all stuck CI jobs
gpestana Mar 27, 2023
5c8a034
restarts ci jobs
gpestana Mar 31, 2023
187ce07
Renames ElectionBounds -> Bounds in benchmarking mocks et al
gpestana Apr 2, 2023
9023997
updates mocks
gpestana Apr 2, 2023
ee5c863
Merge branch 'master' into gpestana/staking-dynamic-nominators
gpestana Apr 2, 2023
c3b4375
Update frame/election-provider-support/src/lib.rs
gpestana Apr 11, 2023
9f0319d
Update frame/staking/src/pallet/impls.rs
gpestana Apr 11, 2023
bece9d5
Merge branch 'master' into gpestana/staking-dynamic-nominators
gpestana Apr 11, 2023
8bd5ce9
Update frame/election-provider-support/src/lib.rs
gpestana Apr 11, 2023
7970ade
Update frame/staking/src/tests.rs
gpestana Apr 11, 2023
208ee20
more checks in api_nominations_quota in tests
gpestana Apr 11, 2023
6c5b3b1
Improves docs
gpestana Apr 11, 2023
6619b5d
fixes e2e tests
gpestana Apr 12, 2023
02d8e78
Uses size_hint rather than mem::size_of in size tracker; Refactor siz…
gpestana Apr 12, 2023
4805516
Merge branch 'master' into gpestana/staking-dynamic-nominators
gpestana Apr 12, 2023
d18b175
nits from reviews
gpestana Apr 13, 2023
31ed378
Merge branch 'master' into gpestana/staking-dynamic-nominators
gpestana Apr 19, 2023
4769857
Refactors bounds to own module; improves docs
gpestana Apr 19, 2023
a5cd416
More tests and docs
gpestana Apr 19, 2023
742f07d
fixes docs
gpestana Apr 19, 2023
fe61184
Fixes benchmarks
gpestana Apr 20, 2023
87945f6
Fixes rust docs
gpestana Apr 20, 2023
75be862
fixes bags-list remote-ext-tests
gpestana Apr 20, 2023
b3aab85
Simplify bound checks in create_snapshot_external
gpestana Apr 20, 2023
bc0e8bd
Merge branch 'master' into gpestana/staking-dynamic-nominators
gpestana May 17, 2023
aab74d6
Merge branch 'master' into gpestana/staking-dynamic-nominators
gpestana Jun 3, 2023
fe2dfe3
Adds target size check in get_npos_targets
gpestana Jun 4, 2023
491ddfc
Merge branch 'master' into gpestana/staking-dynamic-nominators
gpestana Aug 5, 2023
8bfd731
".git/.scripts/commands/fmt/fmt.sh"
Aug 5, 2023
747aa52
restart ci
gpestana Aug 5, 2023
0bda7a4
rust doc fixes and cosmetic nits
gpestana Aug 6, 2023
157e3a7
Merge remote-tracking branch 'origin/master' into gpestana/staking-dy…
Aug 8, 2023
3c3fc5a
Merge remote-tracking branch 'origin/master' into gpestana/staking-dy…
Aug 9, 2023
86ab41c
rollback upgrade on parity-scale-codec version (unecessary)
gpestana Aug 9, 2023
e4f7f9f
reset cargo lock, no need to update it
gpestana Aug 9, 2023
07c4a7d
Merge branch 'master' into gpestana/staking-dynamic-nominators
gpestana Aug 9, 2023
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
4 changes: 2 additions & 2 deletions frame/babe/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ impl onchain::Config for OnChainSeqPhragmen {
type DataProvider = Staking;
type WeightInfo = ();
type MaxWinners = ConstU32<100>;
type VotersBound = ConstU32<{ u32::MAX }>;
type TargetsBound = ConstU32<{ u32::MAX }>;
type VotersBounds = ElectionBounds::new_unbounded();
type TargetsBounds = ElectionBounds::new_unbounded();
}

impl pallet_staking::Config for Test {
Expand Down
56 changes: 35 additions & 21 deletions frame/election-provider-multi-phase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@

use codec::{Decode, Encode};
use frame_election_provider_support::{
BoundedSupportsOf, ElectionDataProvider, ElectionProvider, ElectionProviderBase,
InstantElectionProvider, NposSolution,
BoundedSupportsOf, ElectionBounds, ElectionBoundsBuilder, ElectionDataProvider,
ElectionProvider, ElectionProviderBase, InstantElectionProvider, NposSolution,
};
use frame_support::{
dispatch::DispatchClass,
Expand Down Expand Up @@ -671,6 +671,14 @@ pub mod pallet {
#[pallet::constant]
type MaxWinners: Get<u32>;

// The limits of targets to include in the snapshot per block.
#[pallet::constant]
type TargetsBounds: Get<ElectionBounds>;
Comment thread
gpestana marked this conversation as resolved.
Outdated

// The limits of voters to include in the snapshot per block.
#[pallet::constant]
type VotersBounds: Get<ElectionBounds>;

/// Handler for the slashed deposits.
type SlashHandler: OnUnbalanced<NegativeImbalanceOf<Self>>;

Expand Down Expand Up @@ -1078,14 +1086,13 @@ pub mod pallet {
) -> DispatchResult {
T::ForceOrigin::ensure_origin(origin)?;
ensure!(Self::current_phase().is_emergency(), <Error<T>>::CallNotAllowed);

let supports =
T::GovernanceFallback::instant_elect(maybe_max_voters, maybe_max_targets).map_err(
|e| {
log!(error, "GovernanceFallback failed: {:?}", e);
Error::<T>::FallbackFailed
},
)?;
let voters_bounds = ElectionBoundsBuilder::new().count(maybe_max_voters).build();
let targets_bounds = ElectionBoundsBuilder::new().count(maybe_max_voters).build();
let supports = T::GovernanceFallback::instant_elect(voters_bounds, targets_bounds)
.map_err(|e| {
log!(error, "GovernanceFallback failed: {:?}", e);
Error::<T>::FallbackFailed
})?;

// transform BoundedVec<_, T::GovernanceFallback::MaxWinners> into
// `BoundedVec<_, T::MaxWinners>`
Expand Down Expand Up @@ -1401,15 +1408,19 @@ impl<T: Config> Pallet<T> {
/// Extracted for easier weight calculation.
fn create_snapshot_external(
) -> Result<(Vec<T::AccountId>, Vec<VoterOf<T>>, u32), ElectionError<T>> {
// TODO: do we need T::MaxElect* limits now or can we rely on T::*Bounds?
let target_limit = T::MaxElectableTargets::get().saturated_into::<usize>();
let voter_limit = T::MaxElectingVoters::get().saturated_into::<usize>();

let targets = T::DataProvider::electable_targets(Some(target_limit))
.map_err(ElectionError::DataProvider)?;
let targets_bounds = T::TargetsBounds::get();
let voters_bounds = T::VotersBounds::get();

let voters = T::DataProvider::electing_voters(Some(voter_limit))
let targets = T::DataProvider::electable_targets(targets_bounds)
.map_err(ElectionError::DataProvider)?;

let voters =
T::DataProvider::electing_voters(voters_bounds).map_err(ElectionError::DataProvider)?;

if targets.len() > target_limit || voters.len() > voter_limit {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We can just get rid of this. It is double checking some bounds that are already "guaranteed" to be met. If we are to check, we should also check the byte size (if possible to do cheaply).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The goal here was to double check that the data coming from the election provider meet the expected bounds, since we have no guarantee that the data provider is properly implemented. fwiw, there are a couple of old test checking for this.

Regarding checking the size in bytes of the voter list, this pallet doesn't have the concept of size in bytes of a voter list, since that is a concept only on the data provider side (the size tracker and bounds are implemented on the data provider side). Given the point made above that EPM should not blindly trust the data provider, perhaps we could also add the size in bytes of the voter list checks in EPM (needs a new bound config for this pallet), or we could just assume the data provider always behaves as expected and remove the checks altogether. wdyt?

@gpestana gpestana Mar 24, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added voter size checks here too.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your point about this pallet not dealing with byte size is correct, but in extension it would also not have to deal with length either.

In principle, this pallet only knows that there is an election_bounds, which has voters and targets, and once passed to the T::DataProvider, they need to be respected.

If you were to do this pedantically, you would make the fields of ElectionProviderBounds private to hide this actually.

I would formulate this then as:

let election_bounds = T::ElectionBounds::get();

let targets = T::DataProvider::electable_targets(election_bounds.targets)
	.and_then(|t| election_bounds.targets.ensure_respected(&v))
	.map_err(ElectionError::DataProvider)?;

let voters = T::DataProvider::electing_voters(election_bounds.voters)
	.and_then(|v| election_bounds.voters.ensure_respected(&v))
	.map_err(ElectionError::DataProvider)?;

and then inside ensure_respected implementation you can check length, and check byte-size again as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a great input. I have refactored this code and it ended up much simpler, thanks!

return Err(ElectionError::DataProvider("Snapshot too big for submission."))
}
Expand Down Expand Up @@ -1589,15 +1600,18 @@ impl<T: Config> Pallet<T> {
<QueuedSolution<T>>::take()
.ok_or(ElectionError::<T>::NothingQueued)
.or_else(|_| {
T::Fallback::instant_elect(None, None)
.map_err(|fe| ElectionError::Fallback(fe))
.and_then(|supports| {
Ok(ReadySolution {
supports,
score: Default::default(),
compute: ElectionCompute::Fallback,
})
T::Fallback::instant_elect(
ElectionBounds::new_unbounded(),
Comment thread
gpestana marked this conversation as resolved.
Outdated
ElectionBounds::new_unbounded(),
)
.map_err(|fe| ElectionError::Fallback(fe))
.and_then(|supports| {
Ok(ReadySolution {
supports,
score: Default::default(),
compute: ElectionCompute::Fallback,
})
})
})
.map(|ReadySolution { compute, score, supports }| {
Self::deposit_event(Event::ElectionFinalized { compute, score });
Expand Down
35 changes: 21 additions & 14 deletions frame/election-provider-multi-phase/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{self as multi_phase, unsigned::MinerConfig};
use frame_election_provider_support::{
data_provider,
onchain::{self},
ElectionDataProvider, NposSolution, SequentialPhragmen,
ElectionBounds, ElectionDataProvider, NposSolution, SequentialPhragmen,
};
pub use frame_support::{assert_noop, assert_ok, pallet_prelude::GetDefault};
use frame_support::{
Expand Down Expand Up @@ -298,6 +298,8 @@ parameter_types! {
pub static MaxElectingVoters: VoterIndex = u32::max_value();
pub static MaxElectableTargets: TargetIndex = TargetIndex::max_value();
pub static MaxWinners: u32 = 200;
pub static VotersBounds: ElectionBounds = ElectionBounds::new_unbounded();
pub static TargetsBounds: ElectionBounds = ElectionBounds::new_unbounded();

pub static EpochLength: u64 = 30;
pub static OnChainFallback: bool = true;
Expand All @@ -310,8 +312,8 @@ impl onchain::Config for OnChainSeqPhragmen {
type DataProvider = StakingMock;
type WeightInfo = ();
type MaxWinners = MaxWinners;
type VotersBound = ConstU32<{ u32::MAX }>;
type TargetsBound = ConstU32<{ u32::MAX }>;
type VotersBounds = VotersBounds;
type TargetsBounds = TargetsBounds;
}

pub struct MockFallback;
Expand All @@ -325,12 +327,15 @@ impl ElectionProviderBase for MockFallback {

impl InstantElectionProvider for MockFallback {
fn instant_elect(
max_voters: Option<u32>,
max_targets: Option<u32>,
voters_bounds: ElectionBounds,
targets_bounds: ElectionBounds,
) -> Result<BoundedSupportsOf<Self>, Self::Error> {
if OnChainFallback::get() {
onchain::OnChainExecution::<OnChainSeqPhragmen>::instant_elect(max_voters, max_targets)
.map_err(|_| "onchain::OnChainExecution failed.")
onchain::OnChainExecution::<OnChainSeqPhragmen>::instant_elect(
voters_bounds,
targets_bounds,
)
.map_err(|_| "onchain::OnChainExecution failed.")
} else {
Err("NoFallback.")
}
Expand Down Expand Up @@ -405,6 +410,8 @@ impl crate::Config for Runtime {
type MaxWinners = MaxWinners;
type MinerConfig = Self;
type Solver = SequentialPhragmen<AccountId, SolutionAccuracyOf<Runtime>, Balancing>;
type VotersBounds = VotersBounds;
type TargetsBounds = TargetsBounds;
}

impl<LocalCall> frame_system::offchain::SendTransactionTypes<LocalCall> for Runtime
Expand Down Expand Up @@ -432,25 +439,24 @@ impl ElectionDataProvider for StakingMock {
type BlockNumber = u64;
type MaxVotesPerVoter = MaxNominations;

fn electable_targets(maybe_max_len: Option<usize>) -> data_provider::Result<Vec<AccountId>> {
fn electable_targets(bounds: ElectionBounds) -> data_provider::Result<Vec<AccountId>> {
let targets = Targets::get();

if !DataProviderAllowBadData::get() &&
maybe_max_len.map_or(false, |max_len| targets.len() > max_len)
bounds.count.map_or(false, |max_len| targets.len() > max_len as usize)
{
return Err("Targets too big")
}

Ok(targets)
}

fn electing_voters(
maybe_max_len: Option<usize>,
) -> data_provider::Result<Vec<VoterOf<Runtime>>> {
fn electing_voters(bounds: ElectionBounds) -> data_provider::Result<Vec<VoterOf<Runtime>>> {
let mut voters = Voters::get();

if !DataProviderAllowBadData::get() {
if let Some(max_len) = maybe_max_len {
voters.truncate(max_len)
if let Some(max_len) = bounds.count {
voters.truncate(max_len as usize)
}
}

Expand All @@ -470,6 +476,7 @@ impl ElectionDataProvider for StakingMock {
voters: Vec<VoterOf<Runtime>>,
targets: Vec<AccountId>,
_target_stake: Option<VoteWeight>,
Comment thread
gpestana marked this conversation as resolved.
/* TODO(gpestana): this is the absolute max nomination, make it more explicit */
) {
Targets::set(targets);
Voters::set(voters);
Expand Down
122 changes: 109 additions & 13 deletions frame/election-provider-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ pub mod traits;
use sp_runtime::traits::{Bounded, Saturating, Zero};
use sp_std::{fmt::Debug, prelude::*};

pub use codec::{Decode, Encode};
/// Re-export the solution generation macro.
pub use frame_election_provider_solution_type::generate_solution_type;
pub use frame_support::{traits::Get, weights::Weight, BoundedVec, RuntimeDebug};
Expand Down Expand Up @@ -227,7 +228,7 @@ impl<T> __OrInvalidIndex<T> for Option<T> {
/// making it fast to repeatedly encode into a `SolutionOf<T>`. This property turns out
/// to be important when trimming for solution length.
#[derive(RuntimeDebug, Clone, Default)]
#[cfg_attr(feature = "std", derive(PartialEq, Eq, codec::Encode, codec::Decode))]
#[cfg_attr(feature = "std", derive(PartialEq, Eq, Encode, Decode))]
pub struct IndexAssignment<VoterIndex, TargetIndex, P: PerThing> {
/// Index of the voter among the voters list.
pub who: VoterIndex,
Expand Down Expand Up @@ -282,25 +283,23 @@ pub trait ElectionDataProvider {
/// All possible targets for the election, i.e. the targets that could become elected, thus
/// "electable".
///
/// If `maybe_max_len` is `Some(v)` then the resulting vector MUST NOT be longer than `v` items
/// long.
/// If `bounds` are defined, then the resulting vector MUST NOT be longer or larger in MB than
/// `v` items long.
///
/// This should be implemented as a self-weighing function. The implementor should register its
/// appropriate weight at the end of execution with the system pallet directly.
fn electable_targets(
maybe_max_len: Option<usize>,
) -> data_provider::Result<Vec<Self::AccountId>>;
fn electable_targets(bounds: ElectionBounds) -> data_provider::Result<Vec<Self::AccountId>>;

/// All the voters that participate in the election, thus "electing".
///
/// Note that if a notion of self-vote exists, it should be represented here.
///
/// If `maybe_max_len` is `Some(v)` then the resulting vector MUST NOT be longer than `v` items
/// long.
/// If `bounds` are defined, then the resulting vector MUST NOT be longer or larger in bytes
/// than `v` items long.
///
/// This should be implemented as a self-weighing function. The implementor should register its
/// appropriate weight at the end of execution with the system pallet directly.
fn electing_voters(maybe_max_len: Option<usize>) -> data_provider::Result<Vec<VoterOf<Self>>>;
fn electing_voters(bounds: ElectionBounds) -> data_provider::Result<Vec<VoterOf<Self>>>;

/// The number of targets to elect.
///
Expand Down Expand Up @@ -421,8 +420,8 @@ pub trait ElectionProvider: ElectionProviderBase {
/// data provider at runtime via `forced_input_voters_bound` and `forced_input_target_bound`.
pub trait InstantElectionProvider: ElectionProviderBase {
fn instant_elect(
forced_input_voters_bound: Option<u32>,
forced_input_target_bound: Option<u32>,
forced_input_voters_bound: ElectionBounds,
forced_input_target_bound: ElectionBounds,
) -> Result<BoundedSupportsOf<Self>, Self::Error>;
}

Expand Down Expand Up @@ -464,8 +463,8 @@ where
MaxWinners: Get<u32>,
{
fn instant_elect(
_: Option<u32>,
_: Option<u32>,
_: ElectionBounds,
_: ElectionBounds,
) -> Result<BoundedSupportsOf<Self>, Self::Error> {
Err("`NoElection` cannot do anything.")
}
Expand Down Expand Up @@ -674,3 +673,100 @@ pub type BoundedSupportsOf<E> = BoundedSupports<

sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $);
sp_core::generate_feature_enabled_macro!(runtime_benchmarks_or_fuzz_enabled, any(feature = "runtime-benchmarks", feature = "fuzzing"), $);

/// The limits of an election result. The bounds are defined over the count of element of the
/// election (voters or targets) or the overall datastructure size.
///
/// Ordering: when comparing two instances of `ElectionBounds`, the `count` has priority over the
/// `size`, ie. if `A.count > B.count`, then `A > B` regardless of their relative `size`.
#[derive(Clone, Copy, RuntimeDebug, scale_info::TypeInfo, Encode, Decode, Eq)]
pub struct ElectionBounds {
/// The bound on number of elements. `None` means unbounded.
pub count: Option<u32>,
/// The bound on size, in bytes. `None` means unbounded.
pub size: Option<u32>,
}
Comment thread
gpestana marked this conversation as resolved.
Outdated

impl ElectionBounds {
/// Returns a new instance of self without bounds.
pub const fn new_unbounded() -> Self {
ElectionBounds { count: None, size: None }
}

// Returns true if `given_count` exhausts `self.count`.
pub fn count_exhausted(self, given_count: Option<u32>) -> bool {
self.count.map_or(false, |count| given_count.unwrap_or(0) > count)
}

// Returns true if `given_size` exhausts `self.size`.
pub fn size_exhausted(self, given_size: Option<u32>) -> bool {
self.size.map_or(false, |count| given_size.unwrap_or(0) > count)
}

// Returns true if `given_size` or `given_count` exhausts `self.size` or `self_count`
// respectively.
pub fn exhausted(self, given_size: Option<u32>, given_count: Option<u32>) -> bool {
self.count_exhausted(given_count) || self.size_exhausted(given_size)
}
}

impl PartialEq for ElectionBounds {
fn eq(&self, other: &Self) -> bool {
self.count == other.count || self.size == self.size
}
}

impl PartialOrd for ElectionBounds {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Comment thread
gpestana marked this conversation as resolved.
Outdated
Some(self.cmp(other))
}
}

impl Ord for ElectionBounds {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
if self.count == other.count {
self.size.cmp(&other.size)
} else {
self.count.cmp(&other.count)
}
}
}

/// Utility builder for [`ElectionBounds`].
Comment thread
gpestana marked this conversation as resolved.
Outdated
///
/// The main purpose of this is to prevent mixing the order of similarly typed arguments (e.g. u32
/// size and count).
#[derive(Copy, Clone)]
pub struct ElectionBoundsBuilder {
Comment thread
gpestana marked this conversation as resolved.
Outdated
count: Option<u32>,
size: Option<u32>,
}

impl ElectionBoundsBuilder {
/// Returns a new election bounds builder
pub fn new() -> Self {
ElectionBoundsBuilder { count: None, size: None }
}

/// Set the count of the snapshot.
pub fn count(mut self, count: Option<u32>) -> Self {
self.count = count;
self
}

/// Set the size of the snapshot.
pub fn size(mut self, size: Option<u32>) -> Self {
self.size = size;
self
}

/// Returns an instance of `ElectionBounds` from the current state.
pub fn build(self) -> ElectionBounds {
ElectionBounds { count: self.count, size: self.size }
}
}
Comment thread
gpestana marked this conversation as resolved.
Outdated

#[cfg(test)]
mod snapshot_bounds {
// TODO(gpestana)
}
Loading