Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
0402ecc
Adds try_state to elections pallet
gpestana Mar 27, 2023
af0e6a5
Finishes elections try-state hook; starts EPM hook impl
gpestana Mar 28, 2023
788ddf4
Adds scaffolding for EPM try_state checks
gpestana Mar 28, 2023
b721463
implements checks for EPM
gpestana Mar 30, 2023
3103262
Update frame/election-provider-multi-phase/src/lib.rs
gpestana Apr 20, 2023
6cbf9ab
Update frame/elections-phragmen/src/lib.rs
gpestana Apr 20, 2023
a483470
chore(sc-cli): improve runner and signals (#13688)
yjhmelody Mar 26, 2023
8077b19
add a new ci job to fuzz sp-arithmetic (#13673)
Mar 27, 2023
19e9edc
sc-slots: Forward block size limit (#13716)
bkchr Mar 27, 2023
322d31b
FRAME: Fix the Referenda confirming alarm (#13704)
gavofyork Mar 27, 2023
c04f812
Update pallet licenses to Apache-2.0 (#13467)
aaronbassett Mar 27, 2023
f0bcd0d
Reward pool migration fix (#13715)
Mar 27, 2023
f94fce0
contracts: Add host function tracing (#13648)
pgherveou Mar 27, 2023
67e1cab
Change license of node-template and FRAME examples to MIT-0 (#13465)
aaronbassett Mar 27, 2023
08e037a
updating labels descriptions (#13557)
the-right-joyce Mar 27, 2023
9522b75
CI: Investigate why `RUSTFLAGS` differs on `master` and PRs (#13686)
rcny Mar 28, 2023
998d768
Remove HeaderBackend requirement from AuthorityDiscovery and NetworkW…
skunert Mar 28, 2023
f34444b
CI: rephrase `RUSTFLAGS` (#13735)
rcny Mar 28, 2023
f799177
contracts: proper event link in docs (#13729)
kziemianek Mar 28, 2023
9bc5215
Support stable rust for compiling the runtime (#13580)
bkchr Mar 29, 2023
6618041
provide a default value for RELENG_SCRIPTS_BRANCH (#13743)
Mar 29, 2023
42ee907
roll out new debian 11 ci image (#13744)
Mar 29, 2023
2482f20
Swap 'base58' with 'bs58' (#13739)
davxy Mar 29, 2023
204296a
proc-macro: check for non-args runtime calls added (#13742)
michalkucharczyk Mar 29, 2023
08a35e1
Clean up after debian 11 rollout (#13762)
Mar 29, 2023
17a5d9e
[Fix] Bump tuple element number in frame-support. (#13760)
ruseinov Mar 29, 2023
57dddd2
Generic keystore internals (#13749)
davxy Mar 29, 2023
8929804
Application Crypto cleanup (#13746)
davxy Mar 30, 2023
cb3869d
Attempt to relieve pressure on `mpsc_network_worker` (#13725)
altonen Mar 30, 2023
daf303f
[Enhancement] Throw an error when there are too many pallets (#13763)
ruseinov Mar 30, 2023
6e4e5f5
Build wasm for mvp cpu (#13758)
athei Mar 30, 2023
2fbf914
BEEFY: gossip finality proofs (#13727)
acatangiu Mar 30, 2023
6c1b1ad
Fix nomiantion pools doc render (#13748)
kianenigma Mar 30, 2023
e070908
sp-runtime-interface-test: Fix flaky test (#13770)
bkchr Mar 30, 2023
83d9516
bump zombienet version (#13772)
pepoviola Mar 30, 2023
7756bb1
[Contracts] Overflowing bounded `DeletionQueue` allows DoS against co…
pgherveou Mar 31, 2023
668d61b
Refactor: extract most aura logic out to standalone module, make use …
rphmeier Mar 31, 2023
c9a1ff0
contracts: make test work with debugger (#13776)
pgherveou Mar 31, 2023
19fe15a
add claim_commission weight (#13774)
Apr 1, 2023
76adcec
FRAME: Minor fix for failsafe. (#13741)
gavofyork Apr 1, 2023
81ea064
Adjustments to RPC-query docstrings. (#13698)
DamianStraszak Apr 3, 2023
e1b12eb
Force upgrade snow to 0.9.2 (#13806)
bkchr Apr 3, 2023
a066028
Scheduler pre block limit note (#13231)
muharem Apr 3, 2023
6d38833
Disable `sign-ext` WASM feature when building runtimes (#13804)
koute Apr 3, 2023
71eaef9
refactor: inconsistent BalanceConversion fn (#13610)
wischli Apr 4, 2023
fae245d
Remove deprecated batch verification (#13799)
bkchr Apr 4, 2023
d5ddea6
Uniform pallet warnings (#13798)
ggwpez Apr 4, 2023
cc3f56a
Update documentation for uniques (This PR renames classes and instanc…
simonsso Apr 4, 2023
ee40229
Implement #[pallet::composite_enum] (#13722)
KiChjang Apr 4, 2023
f6f7ced
Expose WASM extensions in executor semantics (#13811)
s0me0ne-unkn0wn Apr 4, 2023
6c45413
Deprecate V1 Weights (#13699)
ggwpez Apr 4, 2023
32f601b
update links to ink! (#13819)
agryaznov Apr 5, 2023
d1e309f
Move registration of `ReadRuntimeVersionExt` to `ExecutionExtension` …
bkchr Apr 5, 2023
5b508e9
Mention `on_idle` round-robin logic to `trait Hooks` cargo doc (#13797)
gpestana Apr 5, 2023
a930258
ProofRecorder: Implement transactional support (#13769)
bkchr Apr 5, 2023
a98e3c2
remove duplicate sync option documentation (#13828)
liamaharon Apr 6, 2023
74c0cf6
Add HoldReason to the NIS pallet (#13823)
KiChjang Apr 6, 2023
3c139bc
Fix `try-runtime follow-chain`, try-runtime upgrade tuple tests, cli …
liamaharon Apr 6, 2023
37479ad
Use proper weights in the `pallet-template` (#13817)
alexbcasas Apr 6, 2023
359d86d
Addresses PR review comments
gpestana Apr 23, 2023
c6633c8
Merge branch 'master' into gpestana/try_state_elections
gpestana Apr 23, 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions bin/node-template/pallets/template/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ scale-info = { version = "2.5.0", default-features = false, features = ["derive"
frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../../../frame/benchmarking" }
frame-support = { version = "4.0.0-dev", default-features = false, path = "../../../../frame/support" }
frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../../frame/system" }
sp-std = { version = "5.0.0", default-features = false, path = "../../../../primitives/std" }

[dev-dependencies]
sp-core = { version = "7.0.0", path = "../../../../primitives/core" }
Expand Down
17 changes: 17 additions & 0 deletions client/network/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,23 @@ impl<B: BlockT> NetworkBehaviour for Protocol<B> {
return Poll::Ready(NetworkBehaviourAction::CloseConnection { peer_id, connection }),
};

while let Poll::Ready(Some(validation_result)) =
self.sync_substream_validations.poll_next_unpin(cx)
{
match validation_result {
Ok((peer, roles)) => {
self.peers.insert(peer, roles);
},
Err(peer) => {
log::debug!(
target: "sub-libp2p",
"`SyncingEngine` rejected stream"
);
self.behaviour.disconnect_peer(&peer, HARDCODED_PEERSETS_SYNC);
},
}
}

let outcome = match event {
NotificationsOut::CustomProtocolOpen {
peer_id,
Expand Down
88 changes: 88 additions & 0 deletions frame/election-provider-multi-phase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,11 @@ pub mod pallet {
// configure this pallet.
assert!(T::SignedMaxSubmissions::get() >= T::SignedMaxRefunds::get());
}

#[cfg(feature = "try-runtime")]
fn try_state(_n: T::BlockNumber) -> Result<(), &'static str> {
Self::do_try_state()
}
}

#[pallet::call]
Expand Down Expand Up @@ -1252,6 +1257,8 @@ pub mod pallet {
pub type CurrentPhase<T: Config> = StorageValue<_, Phase<T::BlockNumber>, ValueQuery>;

/// Current best solution, signed or unsigned, queued to be returned upon `elect`.
///
/// Always sorted by score.
#[pallet::storage]
#[pallet::getter(fn queued_solution)]
pub type QueuedSolution<T: Config> =
Expand Down Expand Up @@ -1570,6 +1577,84 @@ impl<T: Config> Pallet<T> {
}
}

#[cfg(any(feature = "try-runtime", test))]
impl<T: Config> Pallet<T> {
fn do_try_state() -> Result<(), &'static str> {
Self::try_state_snapshot()?;
Self::try_state_signed_submissions_map()?;
Self::try_state_phase_off()
}

// [`Snapshot`] state check. Invariants:
// - [`DesiredTargets`] exist IFF [`Snapshot`] is present.
// - [`SnapshotMetadata`] exist IFF [`Snapshot`] is present.
Comment on lines +1588 to +1590

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.

Would it be more thorough to check this: if Snapshot or DesiredTargets or SnapshotMetadata exist, then they must all exist?

Otherwise this check would pass if we're in a weird state like only DesiredTargets is set but Snapshot and SnapshotMetadata are not

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 one more check to ensure DesiredTargets and SnapshotMetadata are not set if Snapshot is not set.

fn try_state_snapshot() -> Result<(), &'static str> {
let set = <DesiredTargets<T>>::get().is_some() && <SnapshotMetadata<T>>::get().is_some();

match <Snapshot<T>>::take() {
Some(_) if !set => Err("If the snapshot exists, the desired targets and snapshot metadata must also exist."),
None if set => {
println!("desired targets: {:?}", <DesiredTargets<T>>::get());
println!("snapshot metadata: {:?}", <SnapshotMetadata<T>>::get());
Err("If the snapshot does not exists, the desired targets and snapshot metadata should also not exists")
},
_ => Ok(()),
}
}

// [`SignedSubmissionsMap`] state check. Invariants:
// - All [`SignedSubmissionIndices`] are present in [`SignedSubmissionsMap`], and no more;
// - [`SignedSubmissionNextIndex`] is not present in [`SignedSubmissionsMap`];

@liamaharon liamaharon Apr 12, 2023 •

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.

I wonder if we can be more specific with this invariant. Should SignedSubmissionNextIndex be equal to the size of SignedSubmissionsMap?

@gpestana gpestana Apr 23, 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.

Should SignedSubmissionNextIndex be equal to the size of SignedSubmissionsMap?

Not necessarily, that is the reason why we keep the index in storage (also to avoid iterating over the map).

// - [`SignedSubmissionIndices`] is sorted by election score.
fn try_state_signed_submissions_map() -> Result<(), &'static str> {
let mut best_score: ElectionScore = Default::default();
let indices = <SignedSubmissionIndices<T>>::get();

for (i, indice) in indices.iter().enumerate() {
let submission = <SignedSubmissionsMap<T>>::get(indice.2);
if submission.is_none() {
return Err("All signed submissions indices must be part of the submissions map")
}

if i == 0 {
best_score = indice.0
} else {
if best_score.strict_threshold_better(indice.0, Perbill::zero()) {
return Err("Signed submission indices vector must be ordered by election score")
}
}
}

if <SignedSubmissionsMap<T>>::iter().nth(indices.len()).is_some() {
return Err("Signed submissions map length should be the same as the indices vec length")
}

match <SignedSubmissionNextIndex<T>>::get() {
0 => Ok(()),
next =>
if <SignedSubmissionsMap<T>>::get(next).is_none() {
Ok(())
} else {
Err("The next submissions index should not be in the submissions maps already")
},
}
}

// [`Phase::Off`] state check. Invariants:
// - If phase is `Phase::Off`, [`Snapshot`] must be none.
fn try_state_phase_off() -> Result<(), &'static str> {
match Self::current_phase().is_off() {
false => Ok(()),
true =>
if <Snapshot<T>>::take().is_some() {
Err("Snapshot must be none when in Phase::Off")
} else {
Ok(())
},
}
}
}

impl<T: Config> ElectionProviderBase for Pallet<T> {
type AccountId = T::AccountId;
type BlockNumber = T::BlockNumber;
Expand Down Expand Up @@ -1642,6 +1727,9 @@ mod feasibility_check {
MultiPhase::feasibility_check(solution, COMPUTE),
FeasibilityError::SnapshotUnavailable
);

// restore noop snapshot to ensure post-test `try_state` checks pass.
<Snapshot<Runtime>>::set(Some(Default::default()));
})
}

Expand Down
19 changes: 18 additions & 1 deletion frame/election-provider-multi-phase/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,24 @@ impl ExtBuilder {
}

pub fn build_and_execute(self, test: impl FnOnce() -> ()) {
self.build().execute_with(test)
sp_tracing::try_init_simple();

let mut ext = self.build();
ext.execute_with(test);

// run `try-runtime` post conditions logic at the end of each test, even if
// `try-runtime` feature is disabled.
#[cfg(not(feature = "try-runtime"))]
ext.execute_with(|| {
assert_ok!(MultiPhase::do_try_state());
});

#[cfg(feature = "try-runtime")]
ext.execute_with(|| {
assert_ok!(<MultiPhase as frame_support::traits::Hooks<u64>>::try_state(
System::block_number()
));
});
}
}

Expand Down
166 changes: 122 additions & 44 deletions frame/elections-phragmen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,11 @@ pub mod pallet {
T::MaxVotesPerVoter::get(),
);
}

#[cfg(feature = "try-runtime")]
fn try_state(_n: T::BlockNumber) -> Result<(), &'static str> {
Self::do_try_state()
}
}

#[pallet::call]
Expand Down Expand Up @@ -893,7 +898,7 @@ impl<T: Config> Pallet<T> {
}

/// Get the members' account ids.
fn members_ids() -> Vec<T::AccountId> {
pub(crate) fn members_ids() -> Vec<T::AccountId> {
Self::members().into_iter().map(|m| m.who).collect::<Vec<T::AccountId>>()
}

Expand Down Expand Up @@ -1192,6 +1197,104 @@ impl<T: Config> ContainsLengthBound for Pallet<T> {
}
}

#[cfg(any(feature = "try-runtime", test))]
impl<T: Config> Pallet<T> {
fn do_try_state() -> Result<(), &'static str> {
Self::try_state_members()?;
Self::try_state_runners_up()?;
Self::try_state_candidates()?;
Self::try_state_candidates_runners_up_disjoint()?;
Self::try_state_members_disjoint()?;
Self::try_state_members_approval_stake()
}

/// [`Members`] state checks. Invariants:
/// - Members are always sorted based on account ID.
fn try_state_members() -> Result<(), &'static str> {
let mut members = Members::<T>::get().clone();
members.sort_by_key(|m| m.who.clone());

if Members::<T>::get() == members {
Ok(())
} else {
Err("try_state checks: Members must be always sorted by account ID")
}
}

// [`RunnersUp`] state checks. Invariants:
// - Elements are sorted based on weight (worst to best).
fn try_state_runners_up() -> Result<(), &'static str> {
let mut sorted = RunnersUp::<T>::get();
// worst stake first
sorted.sort_by(|a, b| a.stake.cmp(&b.stake));

if RunnersUp::<T>::get() == sorted {
Ok(())
} else {
Err("try_state checks: Runners Up must always be sorted by stake (worst to best)")
}
}

// [`Candidates`] state checks. Invariants:
// - Always sorted based on account ID.
fn try_state_candidates() -> Result<(), &'static str> {
let mut candidates = Candidates::<T>::get().clone();
candidates.sort_by_key(|(c, _)| c.clone());

if Candidates::<T>::get() == candidates {
Ok(())
} else {
Err("try_state checks: Candidates must be always sorted by account ID")
}
}
// [`Candidates`] and [`RunnersUp`] state checks. Invariants:
// - Candidates and runners-ups sets are disjoint.
fn try_state_candidates_runners_up_disjoint() -> Result<(), &'static str> {
match Self::intersects(&Self::candidates_ids(), &Self::runners_up_ids()) {
true => Err("Candidates and runners up sets should always be disjoint"),
false => Ok(()),
}
}

// [`Members`], [`Candidates`] and [`RunnersUp`] state checks. Invariants:
// - Members and candidates sets are disjoint;
// - Members and runners-ups sets are disjoint.
fn try_state_members_disjoint() -> Result<(), &'static str> {
match Self::intersects(&Pallet::<T>::members_ids(), &Self::candidates_ids()) &&
Self::intersects(&Pallet::<T>::members_ids(), &Self::runners_up_ids())
{
true => Err("Members set should be disjoint from candidates and runners-up sets"),
false => Ok(()),
}
}

// [`Members`], [`RunnersUp`] and approval stake state checks. Invariants:
// - Selected members should have approval stake;
// - Selected RunnersUp should have approval stake.
fn try_state_members_approval_stake() -> Result<(), &'static str> {
match Members::<T>::get()
.iter()
.chain(RunnersUp::<T>::get().iter())
.all(|s| s.stake != BalanceOf::<T>::zero())
{
true => Ok(()),
false => Err("Members and RunnersUp must have approval stake"),
}
}

fn intersects<P: PartialEq>(a: &[P], b: &[P]) -> bool {
a.iter().any(|e| b.contains(e))
}

fn candidates_ids() -> Vec<T::AccountId> {
Pallet::<T>::candidates().iter().map(|(x, _)| x).cloned().collect::<Vec<_>>()
}

fn runners_up_ids() -> Vec<T::AccountId> {
Pallet::<T>::runners_up().into_iter().map(|r| r.who).collect::<Vec<_>>()
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -1418,7 +1521,20 @@ mod tests {
.into();
ext.execute_with(pre_conditions);
ext.execute_with(test);
ext.execute_with(post_conditions)

// run `try-runtime` post conditions logic at the end of each test, even if
// `try-runtime` feature is disabled.
#[cfg(not(feature = "try-runtime"))]
ext.execute_with(|| {
assert_ok!(Elections::do_try_state());
});

#[cfg(feature = "try-runtime")]
ext.execute_with(|| {
assert_ok!(<Elections as frame_support::traits::Hooks<u64>>::try_state(
System::block_number()
));
});
}
}

Expand Down Expand Up @@ -1475,54 +1591,16 @@ mod tests {
.unwrap_or_default()
}

fn intersects<T: PartialEq>(a: &[T], b: &[T]) -> bool {
a.iter().any(|e| b.contains(e))
}

fn ensure_members_sorted() {
let mut members = Elections::members().clone();
members.sort_by_key(|m| m.who);
assert_eq!(Elections::members(), members);
}

fn ensure_candidates_sorted() {
let mut candidates = Elections::candidates().clone();
candidates.sort_by_key(|(c, _)| *c);
assert_eq!(Elections::candidates(), candidates);
}

fn locked_stake_of(who: &u64) -> u64 {
Voting::<Test>::get(who).stake
}

fn ensure_members_has_approval_stake() {
// we filter members that have no approval state. This means that even we have more seats
// than candidates, we will never ever chose a member with no votes.
assert!(Elections::members()
.iter()
.chain(Elections::runners_up().iter())
.all(|s| s.stake != u64::zero()));
}

fn ensure_member_candidates_runners_up_disjoint() {
// members, candidates and runners-up must always be disjoint sets.
assert!(!intersects(&members_ids(), &candidate_ids()));
assert!(!intersects(&members_ids(), &runners_up_ids()));
assert!(!intersects(&candidate_ids(), &runners_up_ids()));
}

fn pre_conditions() {
System::set_block_number(1);
ensure_members_sorted();
ensure_candidates_sorted();
ensure_member_candidates_runners_up_disjoint();
}

fn post_conditions() {
ensure_members_sorted();
ensure_candidates_sorted();
ensure_member_candidates_runners_up_disjoint();
ensure_members_has_approval_stake();
Elections::try_state_members().unwrap();
Elections::try_state_members_approval_stake().unwrap();
Elections::try_state_candidates().unwrap();
Elections::try_state_candidates_runners_up_disjoint().unwrap();
}

fn submit_candidacy(origin: RuntimeOrigin) -> sp_runtime::DispatchResult {
Expand Down