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

staking miner: Check the queue one last time before submission #4819

Merged
merged 37 commits into from
Mar 4, 2022
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
feb0a7b
staking miner: use config for emergency solution
niklasad1 Jan 11, 2022
633ee73
bump jsonrpsee
niklasad1 Jan 11, 2022
4c2dbd6
Merge remote-tracking branch 'origin/master' into na-staking-miner-pl…
niklasad1 Jan 13, 2022
4d9644e
run `monitor_cmd_for` until the connection is closed
niklasad1 Jan 13, 2022
21546b1
new tokio task for submit_and_watch xt
niklasad1 Jan 13, 2022
00ff014
re-use header subscription
niklasad1 Jan 14, 2022
54f3bf1
Merge remote-tracking branch 'origin/master' into HEAD
niklasad1 Jan 24, 2022
ecfe004
update jsonrpsee + simplify code
niklasad1 Jan 24, 2022
5fddbbe
revert polkadot runtime changes
niklasad1 Jan 24, 2022
b760a30
feat: add `ensure_no_better_solution` function
niklasad1 Jan 24, 2022
1d08111
Merge remote-tracking branch 'origin/master' into na-fix-3740
niklasad1 Jan 31, 2022
69a3096
storage access for submissions and indices
niklasad1 Jan 31, 2022
32c877a
check ensure_no_previous_solution before remote ext
niklasad1 Jan 31, 2022
9ea9f99
Merge remote-tracking branch 'origin/master' into na-fix-3740
niklasad1 Feb 4, 2022
e87406d
fix todos
niklasad1 Feb 4, 2022
1a5d18b
Merge remote-tracking branch 'origin/master' into na-fix-3740
niklasad1 Feb 7, 2022
c94aed1
Merge remote-tracking branch 'origin/master' into na-fix-3740
niklasad1 Feb 18, 2022
92de8a0
grumbles: Perbill::from_percent
niklasad1 Feb 21, 2022
756694a
hacky fix
niklasad1 Feb 21, 2022
5488de1
use modified EPM pallet and various fixes
niklasad1 Feb 21, 2022
b3b9a58
diener update --substrate --branch na-epm-pub
niklasad1 Feb 21, 2022
b3740f5
Revert "diener update --substrate --branch na-epm-pub"
niklasad1 Feb 22, 2022
359a469
update substrate
niklasad1 Feb 22, 2022
fabfd6e
tokio spawn on concurrent stuff
niklasad1 Feb 25, 2022
c633e44
cleanup
niklasad1 Feb 25, 2022
2e638d5
Update utils/staking-miner/src/monitor.rs
niklasad1 Feb 25, 2022
5946494
Update utils/staking-miner/src/monitor.rs
niklasad1 Feb 25, 2022
0d8aca7
more cleanup
niklasad1 Feb 25, 2022
be2f3fc
Merge remote-tracking branch 'origin/na-fix-3740' into na-fix-3740
niklasad1 Feb 25, 2022
51c4140
Merge remote-tracking branch 'origin/master' into na-fix-3740
niklasad1 Feb 25, 2022
deeee60
fix nits
niklasad1 Feb 25, 2022
e42aca8
address grumbles
niklasad1 Mar 1, 2022
19fc667
only run batch reqs when signed phase
niklasad1 Mar 1, 2022
75d9698
better help menu for submission strategy CLI
niklasad1 Mar 1, 2022
e3ebf7c
Merge remote-tracking branch 'origin/master' into na-fix-3740
niklasad1 Mar 3, 2022
64c4a33
Merge remote-tracking branch 'origin/master' into na-fix-3740
niklasad1 Mar 4, 2022
35f26ff
add tests for submission strategy
niklasad1 Mar 4, 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: 0 additions & 1 deletion utils/staking-miner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master
sp-npos-elections = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-transaction-pool-api = { git = "https://github.com/paritytech/substrate", branch = "master" }


frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-election-provider-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
Expand Down
37 changes: 35 additions & 2 deletions utils/staking-miner/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ mod prelude;
mod rpc_helpers;
mod signer;

use std::str::FromStr;

pub(crate) use prelude::*;
pub(crate) use signer::get_account_info;

Expand All @@ -44,7 +46,7 @@ use frame_support::traits::Get;
use jsonrpsee::ws_client::{WsClient, WsClientBuilder};
use remote_externalities::{Builder, Mode, OnlineConfig};
use sp_npos_elections::ExtendedBalance;
use sp_runtime::{traits::Block as BlockT, DeserializeOwned};
use sp_runtime::{traits::Block as BlockT, DeserializeOwned, Perbill};

pub(crate) enum AnyRuntime {
Polkadot,
Expand Down Expand Up @@ -240,6 +242,7 @@ enum Error<T: EPM::Config> {
IncorrectPhase,
AlreadySubmitted,
VersionMismatch,
AlreadyExistSolutionWithBetterScore,
}

impl<T: EPM::Config> From<sp_core::crypto::SecretStringError> for Error<T> {
Expand Down Expand Up @@ -294,6 +297,32 @@ enum Solvers {
},
}

#[derive(Debug, Copy, Clone)]
enum SubmissionStrategy {
// Only submit if at the time, we are the best.
OnlySubmitIfLeading,
// Always submit.
AlwaysSubmit,
// 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.
SubmitIfClaimBetterThan(Perbill),
}

impl FromStr for SubmissionStrategy {
type Err = std::num::ParseFloatError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let res = if s == "only-submit-if-leading" {
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
Self::OnlySubmitIfLeading
} else if s == "always-submit" {
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
Self::AlwaysSubmit
} else {
Self::SubmitIfClaimBetterThan(Perbill::from_float(s.parse()?))
};
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
Ok(res)
}
}

frame_support::parameter_types! {
/// Number of balancing iterations for a solution algorithm. Set based on the [`Solvers`] CLI
/// config.
Expand All @@ -314,6 +343,10 @@ struct MonitorConfig {
/// The solver algorithm to use.
#[clap(subcommand)]
solver: Solvers,

/// Submission strategy to use.
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
#[clap(long, parse(try_from_str), default_value = "only-submit-if-leading")]
submission_strategy: SubmissionStrategy,
}

#[derive(Debug, Clone, Parser)]
Expand Down Expand Up @@ -611,7 +644,7 @@ async fn main() {
log::error!(target: LOG_TARGET, "DryRun error: {:?}", e);
}),
Command::EmergencySolution(c) => emergency_solution_cmd(shared.clone(), c).await
.map_err(|e| {
.map_err(|e| {
log::error!(target: LOG_TARGET, "EmergencySolution error: {:?}", e);
}),
}
Expand Down
114 changes: 99 additions & 15 deletions utils/staking-miner/src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@

//! The monitor command.

use crate::{prelude::*, rpc_helpers::*, signer::Signer, Error, MonitorConfig, SharedConfig};
use crate::{
prelude::*, rpc_helpers::*, signer::Signer, Error, MonitorConfig, SharedConfig,
SubmissionStrategy,
};
use codec::Encode;
use frame_support::{StorageHasher, Twox64Concat};
use jsonrpsee::{
core::{
client::{Subscription, SubscriptionClientT},
Expand All @@ -28,15 +32,20 @@ use jsonrpsee::{
};
use sc_transaction_pool_api::TransactionStatus;
use sp_core::storage::StorageKey;
use sp_runtime::Perbill;
use std::sync::Arc;
use tokio::sync::mpsc;
use EPM::signed::{SignedSubmissionOf, SubmissionIndicesOf};

const EPM_MODULE_PREFIX: &[u8] = b"ElectionProviderMultiPhase";

/// Ensure that now is the signed phase.
async fn ensure_signed_phase<T: EPM::Config, B: BlockT>(
client: &WsClient,
at: B::Hash,
) -> Result<(), Error<T>> {
let key = StorageKey(EPM::CurrentPhase::<T>::hashed_key().to_vec());

let phase = get_storage::<EPM::Phase<BlockNumber>>(client, rpc_params! {key, at})
.await
.map_err::<Error<T>, _>(Into::into)?
Expand All @@ -54,17 +63,63 @@ async fn ensure_no_previous_solution<
T: EPM::Config + frame_system::Config<AccountId = AccountId>,
B: BlockT,
>(
ext: &mut Ext,
client: &WsClient,
at: B::Hash,
us: &AccountId,
) -> Result<(), Error<T>> {
use EPM::signed::SignedSubmissions;
ext.execute_with(|| {
if <SignedSubmissions<T>>::get().iter().any(|ss| &ss.who == us) {
Err(Error::AlreadySubmitted)
} else {
Ok(())
let indices_key = storage_value(EPM_MODULE_PREFIX, b"SignedSubmissionIndices");
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved

let indices: SubmissionIndicesOf<T> = get_storage(client, rpc_params! {indices_key, at})
.await
.map_err::<Error<T>, _>(Into::into)?
.unwrap_or_default();

for (_score, id) in indices {
let key = storage_value_by_key::<Twox64Concat>(
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
EPM_MODULE_PREFIX,
b"SignedSubmissionsMap",
&id.encode(),
);

if let Some(submission) =
Copy link
Contributor

Choose a reason for hiding this comment

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

I would wait until your other PR is merged (which you can do ASAP since it is all approved now) to rewrite this stuff with the new PRC helpers? I think the code would be cleaner.

get_storage::<SignedSubmissionOf<T>>(client, rpc_params! {key, at})
.await
.map_err::<Error<T>, _>(Into::into)?
{
if &submission.who == us {
return Err(Error::AlreadySubmitted)
}
}
})
}

Ok(())
}

/// Queries the chain for the best solution and checks whether the computed score
/// is better than best known.
async fn ensure_no_better_solution<T: EPM::Config, B: BlockT>(
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
client: &WsClient,
at: B::Hash,
score: sp_npos_elections::ElectionScore,
strategy: SubmissionStrategy,
) -> Result<(), Error<T>> {
let epsilon = match strategy {
emostov marked this conversation as resolved.
Show resolved Hide resolved
SubmissionStrategy::AlwaysSubmit => return Ok(()),
SubmissionStrategy::OnlySubmitIfLeading => Perbill::zero(),
SubmissionStrategy::SubmitIfClaimBetterThan(epsilon) => epsilon,
};

let key = StorageKey(EPM::QueuedSolution::<T>::hashed_key().to_vec());
let best_score = get_storage::<EPM::ReadySolution<AccountId>>(client, rpc_params! {key, at})
.await
.map_err::<Error<T>, _>(Into::into)?
.map(|s| s.score)
.unwrap_or_default();
if sp_npos_elections::is_score_better(score, best_score, epsilon) {
Ok(())
} else {
Err(Error::AlreadyExistSolutionWithBetterScore)
}
}

macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
Expand Down Expand Up @@ -155,6 +210,11 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
return;
}

if ensure_no_previous_solution::<Runtime, Block>(&*client, hash, &signer.account).await.is_err() {
log::debug!(target: LOG_TARGET, "We already have a solution in this phase, skipping.");
return;
}

// grab an externalities without staking, just the election snapshot.
let mut ext = match crate::create_election_ext::<Runtime, Block>(
shared.uri.clone(),
Expand All @@ -168,11 +228,6 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
}
};

if ensure_no_previous_solution::<Runtime, Block>(&mut ext, &signer.account).await.is_err() {
log::debug!(target: LOG_TARGET, "We already have a solution in this phase, skipping.");
return;
}

// mine a solution, and run feasibility check on it as well.
let (raw_solution, witness) = match crate::mine_with::<Runtime>(&config.solver, &mut ext, true) {
Ok(r) => r,
Expand All @@ -182,7 +237,8 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
}
};

log::info!(target: LOG_TARGET, "mined solution with {:?}", &raw_solution.score);
let score = raw_solution.score;
log::info!(target: LOG_TARGET, "mined solution with {:?}", score);

let nonce = match crate::get_account_info::<Runtime>(&*client, &signer.account, Some(hash)).await {
Ok(maybe_account) => {
Expand All @@ -209,6 +265,10 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
let extrinsic = ext.execute_with(|| create_uxt(raw_solution, witness, signer.clone(), nonce, tip, era));
let bytes = sp_core::Bytes(extrinsic.encode());

if ensure_no_better_solution::<Runtime, Block>(&*client, hash, score, config.submission_strategy).await.is_err() {
return;
}

let mut tx_subscription: Subscription<
TransactionStatus<<Block as BlockT>::Hash, <Block as BlockT>::Hash>
> = match client.subscribe(
Expand Down Expand Up @@ -313,3 +373,27 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
monitor_cmd_for!(polkadot);
monitor_cmd_for!(kusama);
monitor_cmd_for!(westend);

fn storage_prefix(m: &[u8], s: &[u8]) -> Vec<u8> {
let k1 = sp_core::hashing::twox_128(m);
let k2 = sp_core::hashing::twox_128(s);
let mut key = Vec::with_capacity(k1.len() + k2.len());
key.extend_from_slice(&k1);
key.extend_from_slice(&k2);
key
}

/// Get storage value.
fn storage_value(m: &[u8], s: &[u8]) -> StorageKey {
StorageKey(storage_prefix(m, s))
}

/// Get storage value at given key.
fn storage_value_by_key<H: StorageHasher>(m: &[u8], s: &[u8], encoded_key: &[u8]) -> StorageKey {
let k1 = storage_prefix(m, s);
let k2 = H::hash(encoded_key);
let mut key = Vec::with_capacity(k1.len() + k2.as_ref().len());
key.extend_from_slice(&k1);
key.extend_from_slice(k2.as_ref());
StorageKey(key)
}