mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 04:41:03 +00:00
Companion for EPM duplicate submissions (#6115)
* make it work * add migration * fix * Update utils/staking-miner/src/opts.rs Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com> * Update utils/staking-miner/src/monitor.rs * small tweaks * Update utils/staking-miner/src/opts.rs Co-authored-by: Bastian Köcher <info@kchr.de> * fmt * fix print' * fmt * update lockfile for {"substrate"} Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com> Co-authored-by: Bastian Köcher <info@kchr.de> Co-authored-by: parity-processbot <>
This commit is contained in:
Generated
+180
-180
File diff suppressed because it is too large
Load Diff
@@ -1478,6 +1478,7 @@ pub type Executive = frame_executive::Executive<
|
|||||||
pallet_multisig::migrations::v1::MigrateToV1<Runtime>,
|
pallet_multisig::migrations::v1::MigrateToV1<Runtime>,
|
||||||
// "Properly migrate weights to v2" <https://github.com/paritytech/polkadot/pull/6091>
|
// "Properly migrate weights to v2" <https://github.com/paritytech/polkadot/pull/6091>
|
||||||
parachains_configuration::migration::v3::MigrateToV3<Runtime>,
|
parachains_configuration::migration::v3::MigrateToV3<Runtime>,
|
||||||
|
pallet_election_provider_multi_phase::migrations::v1::MigrateToV1<Runtime>,
|
||||||
),
|
),
|
||||||
>;
|
>;
|
||||||
/// The payload being signed in the transactions.
|
/// The payload being signed in the transactions.
|
||||||
|
|||||||
@@ -1571,6 +1571,7 @@ pub type Executive = frame_executive::Executive<
|
|||||||
pallet_multisig::migrations::v1::MigrateToV1<Runtime>,
|
pallet_multisig::migrations::v1::MigrateToV1<Runtime>,
|
||||||
// "Properly migrate weights to v2" <https://github.com/paritytech/polkadot/pull/6091>
|
// "Properly migrate weights to v2" <https://github.com/paritytech/polkadot/pull/6091>
|
||||||
parachains_configuration::migration::v3::MigrateToV3<Runtime>,
|
parachains_configuration::migration::v3::MigrateToV3<Runtime>,
|
||||||
|
pallet_election_provider_multi_phase::migrations::v1::MigrateToV1<Runtime>,
|
||||||
),
|
),
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
|||||||
@@ -1222,6 +1222,7 @@ pub type Executive = frame_executive::Executive<
|
|||||||
pallet_multisig::migrations::v1::MigrateToV1<Runtime>,
|
pallet_multisig::migrations::v1::MigrateToV1<Runtime>,
|
||||||
// "Properly migrate weights to v2" <https://github.com/paritytech/polkadot/pull/6091>
|
// "Properly migrate weights to v2" <https://github.com/paritytech/polkadot/pull/6091>
|
||||||
parachains_configuration::migration::v3::MigrateToV3<Runtime>,
|
parachains_configuration::migration::v3::MigrateToV3<Runtime>,
|
||||||
|
pallet_election_provider_multi_phase::migrations::v1::MigrateToV1<Runtime>,
|
||||||
),
|
),
|
||||||
>;
|
>;
|
||||||
/// The payload being signed in transactions.
|
/// The payload being signed in transactions.
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ enum Error<T: EPM::Config> {
|
|||||||
AlreadySubmitted,
|
AlreadySubmitted,
|
||||||
VersionMismatch,
|
VersionMismatch,
|
||||||
StrategyNotSatisfied,
|
StrategyNotSatisfied,
|
||||||
|
QueueFull,
|
||||||
Other(String),
|
Other(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,6 +426,7 @@ fn mine_dpos<T: EPM::Config>(ext: &mut Ext) -> Result<(), Error<T>> {
|
|||||||
|
|
||||||
pub(crate) async fn check_versions<T: frame_system::Config + EPM::Config>(
|
pub(crate) async fn check_versions<T: frame_system::Config + EPM::Config>(
|
||||||
rpc: &SharedRpcClient,
|
rpc: &SharedRpcClient,
|
||||||
|
print: bool,
|
||||||
) -> Result<(), Error<T>> {
|
) -> Result<(), Error<T>> {
|
||||||
let linked_version = T::Version::get();
|
let linked_version = T::Version::get();
|
||||||
let on_chain_version = rpc
|
let on_chain_version = rpc
|
||||||
@@ -432,10 +434,31 @@ pub(crate) async fn check_versions<T: frame_system::Config + EPM::Config>(
|
|||||||
.await
|
.await
|
||||||
.expect("runtime version RPC should always work; qed");
|
.expect("runtime version RPC should always work; qed");
|
||||||
|
|
||||||
log::debug!(target: LOG_TARGET, "linked version {:?}", linked_version);
|
let do_print = || {
|
||||||
log::debug!(target: LOG_TARGET, "on-chain version {:?}", on_chain_version);
|
log::info!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
"linked version {:?}",
|
||||||
|
(&linked_version.spec_name, &linked_version.spec_version)
|
||||||
|
);
|
||||||
|
log::info!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
"on-chain version {:?}",
|
||||||
|
(&on_chain_version.spec_name, &on_chain_version.spec_version)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
if linked_version != on_chain_version {
|
if print {
|
||||||
|
do_print();
|
||||||
|
}
|
||||||
|
|
||||||
|
// we relax the checking here a bit, which should not cause any issues in production (a chain
|
||||||
|
// that messes up its spec name is highly unlikely), but it allows us to do easier testing.
|
||||||
|
if linked_version.spec_name != on_chain_version.spec_name ||
|
||||||
|
linked_version.spec_version != on_chain_version.spec_version
|
||||||
|
{
|
||||||
|
if !print {
|
||||||
|
do_print();
|
||||||
|
}
|
||||||
log::error!(
|
log::error!(
|
||||||
target: LOG_TARGET,
|
target: LOG_TARGET,
|
||||||
"VERSION MISMATCH: any transaction will fail with bad-proof"
|
"VERSION MISMATCH: any transaction will fail with bad-proof"
|
||||||
@@ -563,7 +586,7 @@ async fn main() {
|
|||||||
log::info!(target: LOG_TARGET, "connected to chain {:?}", chain);
|
log::info!(target: LOG_TARGET, "connected to chain {:?}", chain);
|
||||||
|
|
||||||
any_runtime_unit! {
|
any_runtime_unit! {
|
||||||
check_versions::<Runtime>(&rpc).await
|
check_versions::<Runtime>(&rpc, true).await
|
||||||
};
|
};
|
||||||
|
|
||||||
let outcome = any_runtime! {
|
let outcome = any_runtime! {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ where
|
|||||||
.map_err::<Error<T>, _>(Into::into)?
|
.map_err::<Error<T>, _>(Into::into)?
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
for (_score, idx) in indices {
|
for (_score, _bn, idx) in indices {
|
||||||
let key = StorageKey(EPM::SignedSubmissionsMap::<T>::hashed_key_for(idx));
|
let key = StorageKey(EPM::SignedSubmissionsMap::<T>::hashed_key_for(idx));
|
||||||
|
|
||||||
if let Some(submission) = rpc
|
if let Some(submission) = rpc
|
||||||
@@ -81,20 +81,36 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `true` if `our_score` should pass the onchain `best_score` with the given strategy.
|
||||||
|
pub(crate) fn score_passes_strategy(
|
||||||
|
our_score: sp_npos_elections::ElectionScore,
|
||||||
|
best_score: sp_npos_elections::ElectionScore,
|
||||||
|
strategy: SubmissionStrategy,
|
||||||
|
) -> bool {
|
||||||
|
match strategy {
|
||||||
|
SubmissionStrategy::Always => true,
|
||||||
|
SubmissionStrategy::IfLeading =>
|
||||||
|
our_score == best_score ||
|
||||||
|
our_score.strict_threshold_better(best_score, Perbill::zero()),
|
||||||
|
SubmissionStrategy::ClaimBetterThan(epsilon) =>
|
||||||
|
our_score.strict_threshold_better(best_score, epsilon),
|
||||||
|
SubmissionStrategy::ClaimNoWorseThan(epsilon) =>
|
||||||
|
!best_score.strict_threshold_better(our_score, epsilon),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads all current solutions and checks the scores according to the `SubmissionStrategy`.
|
/// Reads all current solutions and checks the scores according to the `SubmissionStrategy`.
|
||||||
async fn ensure_no_better_solution<T: EPM::Config, B: BlockT>(
|
async fn ensure_strategy_met<T: EPM::Config, B: BlockT>(
|
||||||
rpc: &SharedRpcClient,
|
rpc: &SharedRpcClient,
|
||||||
at: Hash,
|
at: Hash,
|
||||||
score: sp_npos_elections::ElectionScore,
|
score: sp_npos_elections::ElectionScore,
|
||||||
strategy: SubmissionStrategy,
|
strategy: SubmissionStrategy,
|
||||||
max_submissions: u32,
|
max_submissions: u32,
|
||||||
) -> Result<(), Error<T>> {
|
) -> Result<(), Error<T>> {
|
||||||
let epsilon = match strategy {
|
// don't care about current scores.
|
||||||
// don't care about current scores.
|
if matches!(strategy, SubmissionStrategy::Always) {
|
||||||
SubmissionStrategy::Always => return Ok(()),
|
return Ok(())
|
||||||
SubmissionStrategy::IfLeading => Perbill::zero(),
|
}
|
||||||
SubmissionStrategy::ClaimBetterThan(epsilon) => epsilon,
|
|
||||||
};
|
|
||||||
|
|
||||||
let indices_key = StorageKey(EPM::SignedSubmissionIndices::<T>::hashed_key().to_vec());
|
let indices_key = StorageKey(EPM::SignedSubmissionIndices::<T>::hashed_key().to_vec());
|
||||||
|
|
||||||
@@ -104,34 +120,16 @@ async fn ensure_no_better_solution<T: EPM::Config, B: BlockT>(
|
|||||||
.map_err::<Error<T>, _>(Into::into)?
|
.map_err::<Error<T>, _>(Into::into)?
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let mut is_best_score = true;
|
// we check the queue here as well. Could be checked elsewhere.
|
||||||
let mut scores = 0;
|
if indices.len() as u32 >= max_submissions {
|
||||||
|
return Err(Error::<T>::QueueFull)
|
||||||
log::debug!(target: LOG_TARGET, "submitted solutions on chain: {:?}", indices);
|
|
||||||
|
|
||||||
// BTreeMap is ordered, take last to get the max score.
|
|
||||||
for (curr_max_score, _) in indices.into_iter() {
|
|
||||||
if !score.strict_threshold_better(curr_max_score, epsilon) {
|
|
||||||
log::warn!(target: LOG_TARGET, "mined score is not better; skipping to submit");
|
|
||||||
is_best_score = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if score == curr_max_score {
|
|
||||||
log::warn!(
|
|
||||||
target: LOG_TARGET,
|
|
||||||
"mined score has the same score as already submitted score"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indices can't be bigger than u32::MAX so can't overflow.
|
|
||||||
scores += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if scores == max_submissions {
|
// default score is all zeros, any score is better than it.
|
||||||
log::warn!(target: LOG_TARGET, "The submissions queue is full");
|
let best_score = indices.last().map(|(score, _, _)| *score).unwrap_or_default();
|
||||||
}
|
log::debug!(target: LOG_TARGET, "best onchain score is {:?}", best_score);
|
||||||
|
|
||||||
if is_best_score {
|
if score_passes_strategy(score, best_score, strategy) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::StrategyNotSatisfied)
|
Err(Error::StrategyNotSatisfied)
|
||||||
@@ -233,7 +231,7 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
|
|||||||
|
|
||||||
// block on this because if this fails there is no way to recover from
|
// block on this because if this fails there is no way to recover from
|
||||||
// that error i.e, upgrade/downgrade required.
|
// that error i.e, upgrade/downgrade required.
|
||||||
if let Err(err) = crate::check_versions::<Runtime>(&rpc).await {
|
if let Err(err) = crate::check_versions::<Runtime>(&rpc, false).await {
|
||||||
let _ = tx.send(err.into());
|
let _ = tx.send(err.into());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -314,9 +312,14 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let ensure_no_better_fut = tokio::spawn(async move {
|
let ensure_strategy_met_fut = tokio::spawn(async move {
|
||||||
ensure_no_better_solution::<Runtime, Block>(&rpc1, latest_head, score, config.submission_strategy,
|
ensure_strategy_met::<Runtime, Block>(
|
||||||
SignedMaxSubmissions::get()).await
|
&rpc1,
|
||||||
|
latest_head,
|
||||||
|
score,
|
||||||
|
config.submission_strategy,
|
||||||
|
SignedMaxSubmissions::get()
|
||||||
|
).await
|
||||||
});
|
});
|
||||||
|
|
||||||
let ensure_signed_phase_fut = tokio::spawn(async move {
|
let ensure_signed_phase_fut = tokio::spawn(async move {
|
||||||
@@ -325,7 +328,7 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
|
|||||||
|
|
||||||
// Run the calls in parallel and return once all has completed or any failed.
|
// Run the calls in parallel and return once all has completed or any failed.
|
||||||
if let Err(err) = tokio::try_join!(
|
if let Err(err) = tokio::try_join!(
|
||||||
flatten(ensure_no_better_fut),
|
flatten(ensure_strategy_met_fut),
|
||||||
flatten(ensure_signed_phase_fut),
|
flatten(ensure_signed_phase_fut),
|
||||||
) {
|
) {
|
||||||
log::debug!(target: LOG_TARGET, "Skipping to submit at block {}; {}", at.number, err);
|
log::debug!(target: LOG_TARGET, "Skipping to submit at block {}; {}", at.number, err);
|
||||||
@@ -420,3 +423,46 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
|
|||||||
monitor_cmd_for!(polkadot);
|
monitor_cmd_for!(polkadot);
|
||||||
monitor_cmd_for!(kusama);
|
monitor_cmd_for!(kusama);
|
||||||
monitor_cmd_for!(westend);
|
monitor_cmd_for!(westend);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn score_passes_strategy_works() {
|
||||||
|
let s = |x| sp_npos_elections::ElectionScore { minimal_stake: x, ..Default::default() };
|
||||||
|
let two = Perbill::from_percent(2);
|
||||||
|
|
||||||
|
// anything passes Always
|
||||||
|
assert!(score_passes_strategy(s(0), s(0), SubmissionStrategy::Always));
|
||||||
|
assert!(score_passes_strategy(s(5), s(0), SubmissionStrategy::Always));
|
||||||
|
assert!(score_passes_strategy(s(5), s(10), SubmissionStrategy::Always));
|
||||||
|
|
||||||
|
// if leading
|
||||||
|
assert!(score_passes_strategy(s(0), s(0), SubmissionStrategy::IfLeading));
|
||||||
|
assert!(score_passes_strategy(s(1), s(0), SubmissionStrategy::IfLeading));
|
||||||
|
assert!(score_passes_strategy(s(2), s(0), SubmissionStrategy::IfLeading));
|
||||||
|
assert!(!score_passes_strategy(s(5), s(10), SubmissionStrategy::IfLeading));
|
||||||
|
assert!(!score_passes_strategy(s(9), s(10), SubmissionStrategy::IfLeading));
|
||||||
|
assert!(score_passes_strategy(s(10), s(10), SubmissionStrategy::IfLeading));
|
||||||
|
|
||||||
|
// if better by 2%
|
||||||
|
assert!(!score_passes_strategy(s(50), s(100), SubmissionStrategy::ClaimBetterThan(two)));
|
||||||
|
assert!(!score_passes_strategy(s(100), s(100), SubmissionStrategy::ClaimBetterThan(two)));
|
||||||
|
assert!(!score_passes_strategy(s(101), s(100), SubmissionStrategy::ClaimBetterThan(two)));
|
||||||
|
assert!(!score_passes_strategy(s(102), s(100), SubmissionStrategy::ClaimBetterThan(two)));
|
||||||
|
assert!(score_passes_strategy(s(103), s(100), SubmissionStrategy::ClaimBetterThan(two)));
|
||||||
|
assert!(score_passes_strategy(s(150), s(100), SubmissionStrategy::ClaimBetterThan(two)));
|
||||||
|
|
||||||
|
// if no less than 2% worse
|
||||||
|
assert!(!score_passes_strategy(s(50), s(100), SubmissionStrategy::ClaimNoWorseThan(two)));
|
||||||
|
assert!(!score_passes_strategy(s(97), s(100), SubmissionStrategy::ClaimNoWorseThan(two)));
|
||||||
|
assert!(score_passes_strategy(s(98), s(100), SubmissionStrategy::ClaimNoWorseThan(two)));
|
||||||
|
assert!(score_passes_strategy(s(99), s(100), SubmissionStrategy::ClaimNoWorseThan(two)));
|
||||||
|
assert!(score_passes_strategy(s(100), s(100), SubmissionStrategy::ClaimNoWorseThan(two)));
|
||||||
|
assert!(score_passes_strategy(s(101), s(100), SubmissionStrategy::ClaimNoWorseThan(two)));
|
||||||
|
assert!(score_passes_strategy(s(102), s(100), SubmissionStrategy::ClaimNoWorseThan(two)));
|
||||||
|
assert!(score_passes_strategy(s(103), s(100), SubmissionStrategy::ClaimNoWorseThan(two)));
|
||||||
|
assert!(score_passes_strategy(s(150), s(100), SubmissionStrategy::ClaimNoWorseThan(two)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -89,7 +89,9 @@ pub(crate) struct MonitorConfig {
|
|||||||
/// `--submission-strategy always`: always submit.
|
/// `--submission-strategy always`: always submit.
|
||||||
///
|
///
|
||||||
/// `--submission-strategy "percent-better <percent>"`: submit if the submission is `n` percent better.
|
/// `--submission-strategy "percent-better <percent>"`: submit if the submission is `n` percent better.
|
||||||
#[arg(long, default_value = "if-leading")]
|
///
|
||||||
|
/// `--submission-strategy "no-worse-than <percent>"`: submit if submission is no more than `n` percent worse.
|
||||||
|
#[clap(long, default_value = "if-leading")]
|
||||||
pub submission_strategy: SubmissionStrategy,
|
pub submission_strategy: SubmissionStrategy,
|
||||||
|
|
||||||
/// Delay in number seconds to wait until starting mining a solution.
|
/// Delay in number seconds to wait until starting mining a solution.
|
||||||
@@ -157,12 +159,14 @@ pub(crate) struct InfoOpts {
|
|||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
pub enum SubmissionStrategy {
|
pub enum SubmissionStrategy {
|
||||||
// Only submit if at the time, we are the best.
|
/// Always submit.
|
||||||
IfLeading,
|
|
||||||
// Always submit.
|
|
||||||
Always,
|
Always,
|
||||||
// Submit if we are leading, or if the solution that's leading is more that the given `Perbill`
|
/// Only submit if at the time, we are the best (or equal to it).
|
||||||
// better than us. This helps detect obviously fake solutions and still combat them.
|
IfLeading,
|
||||||
|
/// Submit if we are no worse than `Perbill` worse than the best.
|
||||||
|
ClaimNoWorseThan(Perbill),
|
||||||
|
/// Submit if we are leading, or if the solution that's leading is more that the given `Perbill`
|
||||||
|
/// better than us. This helps detect obviously fake solutions and still combat them.
|
||||||
ClaimBetterThan(Perbill),
|
ClaimBetterThan(Perbill),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,6 +189,7 @@ pub(crate) enum Solver {
|
|||||||
/// * --submission-strategy if-leading: only submit if leading
|
/// * --submission-strategy if-leading: only submit if leading
|
||||||
/// * --submission-strategy always: always submit
|
/// * --submission-strategy always: always submit
|
||||||
/// * --submission-strategy "percent-better <percent>": submit if submission is `n` percent better.
|
/// * --submission-strategy "percent-better <percent>": submit if submission is `n` percent better.
|
||||||
|
/// * --submission-strategy "no-worse-than<percent>": submit if submission is no more than `n` percent worse.
|
||||||
///
|
///
|
||||||
impl FromStr for SubmissionStrategy {
|
impl FromStr for SubmissionStrategy {
|
||||||
type Err = String;
|
type Err = String;
|
||||||
@@ -196,8 +201,11 @@ impl FromStr for SubmissionStrategy {
|
|||||||
Self::IfLeading
|
Self::IfLeading
|
||||||
} else if s == "always" {
|
} else if s == "always" {
|
||||||
Self::Always
|
Self::Always
|
||||||
} else if s.starts_with("percent-better ") {
|
} else if let Some(percent) = s.strip_prefix("no-worse-than ") {
|
||||||
let percent: u32 = s[15..].parse().map_err(|e| format!("{:?}", e))?;
|
let percent: u32 = percent.parse().map_err(|e| format!("{:?}", e))?;
|
||||||
|
Self::ClaimNoWorseThan(Perbill::from_percent(percent))
|
||||||
|
} else if let Some(percent) = s.strip_prefix("percent-better ") {
|
||||||
|
let percent: u32 = percent.parse().map_err(|e| format!("{:?}", e))?;
|
||||||
Self::ClaimBetterThan(Perbill::from_percent(percent))
|
Self::ClaimBetterThan(Perbill::from_percent(percent))
|
||||||
} else {
|
} else {
|
||||||
return Err(s.into())
|
return Err(s.into())
|
||||||
|
|||||||
Reference in New Issue
Block a user