Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
176 changes: 147 additions & 29 deletions frame/election-provider-multi-phase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1082,9 +1082,12 @@ pub mod pallet {
///
/// The `bool` is `true` when a previous solution was ejected to make room for this one.
SolutionStored { election_compute: ElectionCompute, prev_ejected: bool },
/// The election has been finalized, with `Some` of the given computation, or else if the
/// election failed, `None`.
ElectionFinalized { election_compute: Option<ElectionCompute> },
/// The election has been finalized, with `Some` of the given computation and score, or
/// else if the election failed, `None`.
ElectionFinalized {
election_compute: Option<ElectionCompute>,
score: Option<ElectionScore>,
},
/// An account has been rewarded for their signed submission being finalized.
Rewarded { account: <T as frame_system::Config>::AccountId, value: BalanceOf<T> },
/// An account has been slashed for submitting an invalid signed submission.
Expand Down Expand Up @@ -1530,29 +1533,48 @@ impl<T: Config> Pallet<T> {
// - signed phase was complete or not started, in which case finalization is idempotent and
// inexpensive (1 read of an empty vector).
let _ = Self::finalize_signed_phase();
<QueuedSolution<T>>::take()
.map_or_else(
|| {
T::Fallback::elect()
.map_err(|fe| ElectionError::Fallback(fe))
.map(|supports| (supports, ElectionCompute::Fallback))
},
|ReadySolution { supports, compute, .. }| Ok((supports, compute)),
)
.map(|(supports, compute)| {
Self::deposit_event(Event::ElectionFinalized { election_compute: Some(compute) });
if Self::round() != 1 {
log!(info, "Finalized election round with compute {:?}.", compute);
}
supports
})
.map_err(|err| {
Self::deposit_event(Event::ElectionFinalized { election_compute: None });
match <QueuedSolution<T>>::take() {
Some(ready_solution) => {
Self::deposit_event(Event::ElectionFinalized {
election_compute: Some(ready_solution.compute),
score: Some(ready_solution.score),
});
if Self::round() != 1 {
log!(warn, "Failed to finalize election round. reason {:?}", err);
log!(
info,
"Finalized election round with compute {:?}.",
ready_solution.compute
);
}
err
})
Ok(ready_solution.supports)
},
None => match T::Fallback::elect() {
Ok(supports) => {
Self::deposit_event(Event::ElectionFinalized {
election_compute: Some(ElectionCompute::Fallback),
score: None,
});
if Self::round() != 1 {
log!(
info,
"Finalized election round with compute {:?}.",
ElectionCompute::Fallback
);
}
Ok(supports)
},
Err(err) => {
Self::deposit_event(Event::ElectionFinalized {
election_compute: None,
score: None,
});
if Self::round() != 1 {
log!(warn, "Failed to finalize election round. reason {:?}", err);
}
Err(ElectionError::Fallback(err))
},
},
}
}

/// record the weight of the given `supports`.
Expand Down Expand Up @@ -1801,8 +1823,9 @@ mod tests {
use super::*;
use crate::{
mock::{
multi_phase_events, roll_to, AccountId, ExtBuilder, MockWeightInfo, MockedWeightInfo,
MultiPhase, Origin, Runtime, SignedMaxSubmissions, System, TargetIndex, Targets,
multi_phase_events, raw_solution, roll_to, AccountId, ExtBuilder, MockWeightInfo,
MockedWeightInfo, MultiPhase, Origin, Runtime, SignedMaxSubmissions, System,
TargetIndex, Targets,
},
Phase,
};
Expand Down Expand Up @@ -1968,7 +1991,10 @@ mod tests {
multi_phase_events(),
vec![
Event::SignedPhaseStarted { round: 1 },
Event::ElectionFinalized { election_compute: Some(ElectionCompute::Fallback) }
Event::ElectionFinalized {
election_compute: Some(ElectionCompute::Fallback),
score: None
}
],
);
// All storage items must be cleared.
Expand Down Expand Up @@ -2017,6 +2043,91 @@ mod tests {
})
}

#[test]
fn check_events_with_election_compute_signed() {
ExtBuilder::default().build_and_execute(|| {
roll_to(14);
assert_eq!(MultiPhase::current_phase(), Phase::Off);

roll_to(15);
assert!(MultiPhase::current_phase().is_signed());

let solution = raw_solution();
assert_ok!(MultiPhase::submit(crate::mock::Origin::signed(99), Box::new(solution)));

roll_to(30);
assert_ok!(MultiPhase::elect());

assert_eq!(
multi_phase_events(),
vec![
Event::SignedPhaseStarted { round: 1 },
Event::SolutionStored {
election_compute: ElectionCompute::Signed,
prev_ejected: false
},
Event::Rewarded { account: 99, value: 7 },
Event::UnsignedPhaseStarted { round: 1 },
Event::ElectionFinalized {
election_compute: Some(ElectionCompute::Signed),
score: Some(ElectionScore {
minimal_stake: 40,
sum_stake: 100,
sum_stake_squared: 5200
})
}
],
);
})
}

#[test]
fn check_events_with_election_compute_unsigned() {
ExtBuilder::default().build_and_execute(|| {
roll_to(25);
assert!(MultiPhase::current_phase().is_unsigned());

// ensure we have snapshots in place.
assert!(MultiPhase::snapshot().is_some());
assert_eq!(MultiPhase::desired_targets().unwrap(), 2);

// mine seq_phragmen solution with 2 iters.
let (solution, witness) = MultiPhase::mine_solution().unwrap();

// ensure this solution is valid.
assert!(MultiPhase::queued_solution().is_none());
assert_ok!(MultiPhase::submit_unsigned(
crate::mock::Origin::none(),
Box::new(solution),
witness
));
assert!(MultiPhase::queued_solution().is_some());

roll_to(30);
assert_ok!(MultiPhase::elect());

assert_eq!(
multi_phase_events(),
vec![
Event::SignedPhaseStarted { round: 1 },
Event::UnsignedPhaseStarted { round: 1 },
Event::SolutionStored {
election_compute: ElectionCompute::Unsigned,
prev_ejected: false
},
Event::ElectionFinalized {
election_compute: Some(ElectionCompute::Unsigned),
score: Some(ElectionScore {
minimal_stake: 40,
sum_stake: 100,
sum_stake_squared: 5200
})
}
],
);
})
}

#[test]
fn fallback_strategy_works() {
ExtBuilder::default().onchain_fallback(true).build_and_execute(|| {
Expand Down Expand Up @@ -2081,12 +2192,19 @@ mod tests {
vec![
Event::SignedPhaseStarted { round: 1 },
Event::UnsignedPhaseStarted { round: 1 },
Event::ElectionFinalized { election_compute: None },
Event::ElectionFinalized { election_compute: None, score: None },
Event::SolutionStored {
election_compute: ElectionCompute::Fallback,
prev_ejected: false
},
Event::ElectionFinalized { election_compute: Some(ElectionCompute::Fallback) }
Event::ElectionFinalized {
election_compute: Some(ElectionCompute::Fallback),
score: Some(ElectionScore {
minimal_stake: 0,
sum_stake: 0,
sum_stake_squared: 0
})
}
]
);
})
Expand Down