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

submit emergency solution #11703

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5771803
submit emergency solution
Szegoo Jun 18, 2022
d3c0705
tests & configurable punishment
Szegoo Jun 21, 2022
54746ca
fix
Szegoo Jun 21, 2022
72f6d4d
better naming
Szegoo Jun 22, 2022
345e1a4
documentation
Szegoo Jun 22, 2022
704d73b
Merge branch 'paritytech:master' into submit-emergency-solution
Szegoo Jun 25, 2022
e7a2afb
Merge branch 'paritytech:master' into submit-emergency-solution
Szegoo Jun 30, 2022
9b2ef3b
Update frame/election-provider-multi-phase/src/lib.rs
Szegoo Jun 30, 2022
9946a3f
renaming
Szegoo Jun 30, 2022
3ce6710
Update frame/election-provider-multi-phase/src/lib.rs
kianenigma Jun 30, 2022
c51a51d
Update frame/election-provider-multi-phase/src/lib.rs
kianenigma Jun 30, 2022
28a93a6
fix
Szegoo Jun 30, 2022
340c65b
fmt
Szegoo Jun 30, 2022
3f125fd
test for events
Szegoo Jun 30, 2022
a176c1c
line wrapping fix
Szegoo Jun 30, 2022
8a29e94
Merge branch 'paritytech:master' into submit-emergency-solution
Szegoo Jun 30, 2022
32cf700
Merge branch 'paritytech:master' into submit-emergency-solution
Szegoo Jul 5, 2022
405dd34
Merge branch 'paritytech:master' into submit-emergency-solution
Szegoo Jul 15, 2022
1c38c68
fix inaccurate weight
Szegoo Jul 20, 2022
0c1932f
fix inaccurate weight
Szegoo Jul 20, 2022
d801649
fix inaccurate weight
Szegoo Jul 20, 2022
87f55d0
Merge branch 'paritytech:master' into submit-emergency-solution
Szegoo Jul 20, 2022
48c72de
Update frame/election-provider-multi-phase/src/lib.rs
Szegoo Jul 23, 2022
8dbc1ad
use solution_weight
Szegoo Jul 23, 2022
71fd834
use solution_weight
Szegoo Jul 23, 2022
6a362f5
fix
Szegoo Jul 23, 2022
c12cfa9
Merge branch 'paritytech:master' into submit-emergency-solution
Szegoo Jul 23, 2022
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 bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,7 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
type MaxElectingVoters = MaxElectingVoters;
type BenchmarkingConfig = ElectionProviderBenchmarkConfig;
type WeightInfo = pallet_election_provider_multi_phase::weights::SubstrateWeight<Self>;
type EmergencyDepositMultiple = ConstU32<2>;
}

parameter_types! {
Expand Down
126 changes: 118 additions & 8 deletions frame/election-provider-multi-phase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,12 @@
//! 2. Any other unforeseen internal error
//!
//! A call to `T::ElectionProvider::elect` is made, and `Ok(_)` cannot be returned, then the pallet
//! proceeds to the [`Phase::Emergency`]. During this phase, any solution can be submitted from
//! [`Config::ForceOrigin`], without any checking, via [`Pallet::set_emergency_election_result`]
//! transaction. Hence, `[`Config::ForceOrigin`]` should only be set to a trusted origin, such as
//! the council or root. Once submitted, the forced solution is kept in [`QueuedSolution`] until the
//! next call to `T::ElectionProvider::elect`, where it is returned and [`Phase`] goes back to
//! `Off`.
//! proceeds to the [`Phase::Emergency`]. During this phase, there are two possibilities. One of
//! them is to use the [`Pallet::set_emergency_election_result`] function that submits a solution
//! without any checking. It only accepts solutions from `[`Config::ForceOrigin`]`, hence it should
//! only be set to a trusted origin, such as the council or root. Once submitted,
//! the forced solution is kept in [`QueuedSolution`] until the next call to
//! `T::ElectionProvider::elect`, where it is returned and [`Phase`] goes back to `Off`.
//!
//! This implies that the user of this pallet (i.e. a staking pallet) should re-try calling
//! `T::ElectionProvider::elect` in case of error, until `OK(_)` is returned.
Expand All @@ -151,6 +151,14 @@
//!
//! See the `staking-miner` documentation in the Polkadot repository for more information.
//!
//! The other option is to use the `Pallet::submit_emergency_solution` function which allows
//! you to submit signed solutions during the emergency phase. The only difference between
//! this function and the [`Pallet::submit`] function is that in
//! [`Pallet::submit_emergency_solution`] the submissions are checked on the fly.
//! Good solutions will get rewarded, but bad submissions will be highly punished.
//! The deposit needed for submitting during the emergency phase is higher compared
//! to the one in the regular submission.
//!
//! ## Feasible Solution (correct solution)
//!
//! All submissions must undergo a feasibility check. Signed solutions are checked one by one at the
Expand All @@ -172,7 +180,7 @@
//!
//! ## Error types
//!
//! This pallet provides a verbose error system to ease future debugging and debugging. The overall
//! This pallet provides a verbose error system to ease future debugging. The overall
//! hierarchy of errors is as follows:
//!
//! 1. [`pallet::Error`]: These are the errors that can be returned in the dispatchables of the
Expand Down Expand Up @@ -705,6 +713,11 @@ pub mod pallet {

/// The weight of the pallet.
type WeightInfo: WeightInfo;

/// The multiple of regular deposit needed for signed phase during
/// emergency phase.
#[pallet::constant]
type EmergencyDepositMultiple: Get<u32>;
Szegoo marked this conversation as resolved.
Show resolved Hide resolved
}

#[pallet::hooks]
Expand Down Expand Up @@ -957,9 +970,58 @@ pub mod pallet {
Ok(())
}

/// Submit a signed solution during the emergency phase.
/// It will go through the `feasibility_check` right away.
///
/// The dispatch origin for this call must be __signed__.
///
/// The deposit that is reserved might be rewarded or slashed based on
Szegoo marked this conversation as resolved.
Show resolved Hide resolved
/// the outcome.
#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
pub fn submit_emergency_solution(
Szegoo marked this conversation as resolved.
Show resolved Hide resolved
origin: OriginFor<T>,
raw_solution: Box<RawSolution<SolutionOf<T::MinerConfig>>>,
) -> DispatchResult {
let who = ensure_signed(origin)?;

ensure!(Self::current_phase().is_emergency(), <Error<T>>::CallNotAllowed);

let size = Self::snapshot_metadata().ok_or(Error::<T>::MissingSnapshotMetadata)?;
kianenigma marked this conversation as resolved.
Show resolved Hide resolved

ensure!(
Self::solution_weight_of(&raw_solution, size) < T::SignedMaxWeight::get(),
Error::<T>::SignedTooMuchWeight
);

let deposit = Self::deposit_for_emergency(&raw_solution, size);

kianenigma marked this conversation as resolved.
Show resolved Hide resolved
T::Currency::reserve(&who, deposit).map_err(|_| Error::<T>::SignedCannotPayDeposit)?;

let call_fee = {
let call = Call::submit { raw_solution: raw_solution.clone() };
T::EstimateCallFee::estimate_call_fee(&call, None.into())
};

match Self::feasibility_check(*raw_solution, ElectionCompute::Signed) {
Ok(ready_solution) => {
Self::finalize_signed_phase_accept_solution(
ready_solution,
&who,
deposit,
call_fee,
);
},
Err(_) => {
Self::finalize_signed_phase_reject_solution(&who, deposit);
},
}

Ok(())
}

/// Submit a solution for the signed phase.
///
/// The dispatch origin fo this call must be __signed__.
/// The dispatch origin for this call must be __signed__.
///
/// The solution is potentially queued, based on the claimed score and processed at the end
/// of the signed phase.
Expand Down Expand Up @@ -2017,6 +2079,54 @@ mod tests {
})
}

#[test]
fn accepts_good_solution_during_emergency() {
ExtBuilder::default().onchain_fallback(false).build_and_execute(|| {
roll_to(25);
assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25)));

// No solutions are submitted so the queue should be empty
assert!(MultiPhase::queued_solution().is_none());
assert_eq!(MultiPhase::elect().unwrap_err(), ElectionError::Fallback("NoFallback."));

assert!(MultiPhase::current_phase().is_emergency());

let solution = crate::mock::raw_solution();
let origin = crate::mock::Origin::signed(99);

assert_ok!(MultiPhase::submit_emergency_solution(origin, Box::new(solution)));

// The queued solution shouldn't be none now because the submitted
// solution is correct.
assert!(MultiPhase::queued_solution().is_some());
Szegoo marked this conversation as resolved.
Show resolved Hide resolved
});
}

#[test]
fn rejects_bad_solution_during_emergency() {
ExtBuilder::default().onchain_fallback(false).build_and_execute(|| {
roll_to(25);
assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25)));

// No solutions are submitted so the queue should be empty.
assert!(MultiPhase::queued_solution().is_none());
assert_eq!(MultiPhase::elect().unwrap_err(), ElectionError::Fallback("NoFallback."));

assert!(MultiPhase::current_phase().is_emergency());

let mut solution = crate::mock::raw_solution();
// modifying the solution, so that it becomes incorrect.
solution.round += 1;
let origin = crate::mock::Origin::signed(99);

assert_ok!(MultiPhase::submit_emergency_solution(origin, Box::new(solution)));

// The queed solution should be none now because the submitted
// solution is incorrect.
assert!(MultiPhase::queued_solution().is_none());
Szegoo marked this conversation as resolved.
Show resolved Hide resolved
});
}

#[test]
fn fallback_strategy_works() {
ExtBuilder::default().onchain_fallback(true).build_and_execute(|| {
Expand Down
2 changes: 2 additions & 0 deletions frame/election-provider-multi-phase/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ parameter_types! {
pub static MockWeightInfo: MockedWeightInfo = MockedWeightInfo::Real;
pub static MaxElectingVoters: VoterIndex = u32::max_value();
pub static MaxElectableTargets: TargetIndex = TargetIndex::max_value();
pub static EmergencyDepositMultiple: u32 = 2;

pub static EpochLength: u64 = 30;
pub static OnChainFallback: bool = true;
Expand Down Expand Up @@ -387,6 +388,7 @@ impl crate::Config for Runtime {
type MaxElectableTargets = MaxElectableTargets;
type MinerConfig = Self;
type Solver = SequentialPhragmen<AccountId, SolutionAccuracyOf<Runtime>, Balancing>;
type EmergencyDepositMultiple = EmergencyDepositMultiple;
}

impl<LocalCall> frame_system::offchain::SendTransactionTypes<LocalCall> for Runtime
Expand Down
9 changes: 9 additions & 0 deletions frame/election-provider-multi-phase/src/signed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,15 @@ impl<T: Config> Pallet<T> {
.saturating_add(len_deposit)
.saturating_add(weight_deposit)
}

/// Same as `deposit_for`, but returns a multiple of the result.
Szegoo marked this conversation as resolved.
Show resolved Hide resolved
pub fn deposit_for_emergency(
raw_solution: &RawSolution<SolutionOf<T::MinerConfig>>,
size: SolutionOrSnapshotSize,
) -> BalanceOf<T> {
BalanceOf::<T>::from(T::EmergencyDepositMultiple::get()) *
Self::deposit_for(raw_solution, size)
}
}

#[cfg(test)]
Expand Down