mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-19 15:45:41 +00:00
Fix 5560: add support for a new staking-miner info command (#5577)
* Refactoring opts out * Implement info command fix #5560 * remove useless change * Remove unnecessary brackets * Fix and add tests * Promote the uri flag to global * Ignore lint identity ops * Reverse adding #[allow(identity_op)] * Add cli test for the info command * Add licende headers and fix some grumbles * Add retrieval of the linked version and make the json output optional * Fix tests * Keep it generic and renamed builtin into linked * Rebase master * Add runtimes compatibility information * Silence erroneous warning about unsafe * Fix spellcheck * Update utils/staking-miner/src/runtime_versions.rs Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
This commit is contained in:
@@ -28,30 +28,34 @@
|
||||
//! development. It is intended to run this bot with a `restart = true` way, so that it reports it
|
||||
//! crash, but resumes work thereafter.
|
||||
|
||||
// Silence erroneous warning about unsafe not being required whereas it is
|
||||
// see https://github.com/rust-lang/rust/issues/49112
|
||||
#![allow(unused_unsafe)]
|
||||
|
||||
mod dry_run;
|
||||
mod emergency_solution;
|
||||
mod monitor;
|
||||
mod opts;
|
||||
mod prelude;
|
||||
mod rpc;
|
||||
mod runtime_versions;
|
||||
mod signer;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
pub(crate) use prelude::*;
|
||||
pub(crate) use signer::get_account_info;
|
||||
|
||||
use crate::opts::*;
|
||||
use clap::Parser;
|
||||
use frame_election_provider_support::NposSolver;
|
||||
use frame_support::traits::Get;
|
||||
use jsonrpsee::ws_client::{WsClient, WsClientBuilder};
|
||||
use remote_externalities::{Builder, Mode, OnlineConfig};
|
||||
use rpc::{RpcApiClient, SharedRpcClient};
|
||||
use runtime_versions::RuntimeVersions;
|
||||
use sp_npos_elections::BalancingConfig;
|
||||
use sp_runtime::{traits::Block as BlockT, DeserializeOwned, Perbill};
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
|
||||
use sp_runtime::{traits::Block as BlockT, DeserializeOwned};
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
pub(crate) enum AnyRuntime {
|
||||
Polkadot,
|
||||
Kusama,
|
||||
@@ -62,7 +66,6 @@ pub(crate) static mut RUNTIME: AnyRuntime = AnyRuntime::Polkadot;
|
||||
|
||||
macro_rules! construct_runtime_prelude {
|
||||
($runtime:ident) => { paste::paste! {
|
||||
#[allow(unused_import)]
|
||||
pub(crate) mod [<$runtime _runtime_exports>] {
|
||||
pub(crate) use crate::prelude::EPM;
|
||||
pub(crate) use [<$runtime _runtime>]::*;
|
||||
@@ -278,70 +281,6 @@ impl<T: EPM::Config> std::fmt::Display for Error<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
enum Command {
|
||||
/// Monitor for the phase being signed, then compute.
|
||||
Monitor(MonitorConfig),
|
||||
/// Just compute a solution now, and don't submit it.
|
||||
DryRun(DryRunConfig),
|
||||
/// Provide a solution that can be submitted to the chain as an emergency response.
|
||||
EmergencySolution(EmergencySolutionConfig),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
enum Solver {
|
||||
SeqPhragmen {
|
||||
#[clap(long, default_value = "10")]
|
||||
iterations: usize,
|
||||
},
|
||||
PhragMMS {
|
||||
#[clap(long, default_value = "10")]
|
||||
iterations: usize,
|
||||
},
|
||||
}
|
||||
|
||||
/// Submission strategy to use.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
enum SubmissionStrategy {
|
||||
// Only submit if at the time, we are the best.
|
||||
IfLeading,
|
||||
// Always submit.
|
||||
Always,
|
||||
// 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),
|
||||
}
|
||||
|
||||
/// Custom `impl` to parse `SubmissionStrategy` from CLI.
|
||||
///
|
||||
/// Possible options:
|
||||
/// * --submission-strategy if-leading: only submit if leading
|
||||
/// * --submission-strategy always: always submit
|
||||
/// * --submission-strategy "percent-better <percent>": submit if submission is `n` percent better.
|
||||
///
|
||||
impl FromStr for SubmissionStrategy {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.trim();
|
||||
|
||||
let res = if s == "if-leading" {
|
||||
Self::IfLeading
|
||||
} else if s == "always" {
|
||||
Self::Always
|
||||
} else if s.starts_with("percent-better ") {
|
||||
let percent: u32 = s[15..].parse().map_err(|e| format!("{:?}", e))?;
|
||||
Self::ClaimBetterThan(Perbill::from_percent(percent))
|
||||
} else {
|
||||
return Err(s.into())
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
frame_support::parameter_types! {
|
||||
/// Number of balancing iterations for a solution algorithm. Set based on the [`Solvers`] CLI
|
||||
/// config.
|
||||
@@ -349,87 +288,6 @@ frame_support::parameter_types! {
|
||||
pub static Balancing: Option<BalancingConfig> = Some( BalancingConfig { iterations: BalanceIterations::get(), tolerance: 0 } );
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
struct MonitorConfig {
|
||||
/// They type of event to listen to.
|
||||
///
|
||||
/// Typically, finalized is safer and there is no chance of anything going wrong, but it can be
|
||||
/// slower. It is recommended to use finalized, if the duration of the signed phase is longer
|
||||
/// than the the finality delay.
|
||||
#[clap(long, default_value = "head", possible_values = &["head", "finalized"])]
|
||||
listen: String,
|
||||
|
||||
/// The solver algorithm to use.
|
||||
#[clap(subcommand)]
|
||||
solver: Solver,
|
||||
|
||||
/// Submission strategy to use.
|
||||
///
|
||||
/// Possible options:
|
||||
///
|
||||
/// `--submission-strategy if-leading`: only submit if leading.
|
||||
///
|
||||
/// `--submission-strategy always`: always submit.
|
||||
///
|
||||
/// `--submission-strategy "percent-better <percent>"`: submit if the submission is `n` percent better.
|
||||
#[clap(long, parse(try_from_str), default_value = "if-leading")]
|
||||
submission_strategy: SubmissionStrategy,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
struct EmergencySolutionConfig {
|
||||
/// The block hash at which scraping happens. If none is provided, the latest head is used.
|
||||
#[clap(long)]
|
||||
at: Option<Hash>,
|
||||
|
||||
/// The solver algorithm to use.
|
||||
#[clap(subcommand)]
|
||||
solver: Solver,
|
||||
|
||||
/// The number of top backed winners to take. All are taken, if not provided.
|
||||
take: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
struct DryRunConfig {
|
||||
/// The block hash at which scraping happens. If none is provided, the latest head is used.
|
||||
#[clap(long)]
|
||||
at: Option<Hash>,
|
||||
|
||||
/// The solver algorithm to use.
|
||||
#[clap(subcommand)]
|
||||
solver: Solver,
|
||||
|
||||
/// Force create a new snapshot, else expect one to exist onchain.
|
||||
#[clap(long)]
|
||||
force_snapshot: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
#[clap(author, version, about)]
|
||||
struct Opt {
|
||||
/// The `ws` node to connect to.
|
||||
#[clap(long, short, default_value = DEFAULT_URI, env = "URI")]
|
||||
uri: String,
|
||||
|
||||
/// The path to a file containing the seed of the account. If the file is not found, the seed is
|
||||
/// used as-is.
|
||||
///
|
||||
/// Can also be provided via the `SEED` environment variable.
|
||||
///
|
||||
/// WARNING: Don't use an account with a large stash for this. Based on how the bot is
|
||||
/// configured, it might re-try and lose funds through transaction fees/deposits.
|
||||
#[clap(long, short, env = "SEED")]
|
||||
seed_or_path: String,
|
||||
|
||||
#[clap(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
/// Build the Ext at hash with all the data of `ElectionProviderMultiPhase` and any additional
|
||||
/// pallets.
|
||||
async fn create_election_ext<T: EPM::Config, B: BlockT + DeserializeOwned>(
|
||||
@@ -582,7 +440,7 @@ pub(crate) async fn check_versions<T: frame_system::Config + EPM::Config>(
|
||||
async fn main() {
|
||||
fmt().with_env_filter(EnvFilter::from_default_env()).init();
|
||||
|
||||
let Opt { uri, seed_or_path, command } = Opt::parse();
|
||||
let Opt { uri, command } = Opt::parse();
|
||||
log::debug!(target: LOG_TARGET, "attempting to connect to {:?}", uri);
|
||||
|
||||
let rpc = loop {
|
||||
@@ -648,26 +506,51 @@ async fn main() {
|
||||
check_versions::<Runtime>(&rpc).await
|
||||
};
|
||||
|
||||
let signer_account = any_runtime! {
|
||||
signer::signer_uri_from_string::<Runtime>(&seed_or_path, &rpc)
|
||||
.await
|
||||
.expect("Provided account is invalid, terminating.")
|
||||
};
|
||||
|
||||
let outcome = any_runtime! {
|
||||
match command {
|
||||
Command::Monitor(cmd) => monitor_cmd(rpc, cmd, signer_account).await
|
||||
Command::Monitor(monitor_config) =>
|
||||
{
|
||||
let signer_account = any_runtime! {
|
||||
signer::signer_uri_from_string::<Runtime>(&monitor_config.seed_or_path , &rpc)
|
||||
.await
|
||||
.expect("Provided account is invalid, terminating.")
|
||||
};
|
||||
monitor_cmd(rpc, monitor_config, signer_account).await
|
||||
.map_err(|e| {
|
||||
log::error!(target: LOG_TARGET, "Monitor error: {:?}", e);
|
||||
}),
|
||||
Command::DryRun(cmd) => dry_run_cmd(rpc, cmd, signer_account).await
|
||||
})},
|
||||
Command::DryRun(dryrun_config) => {
|
||||
let signer_account = any_runtime! {
|
||||
signer::signer_uri_from_string::<Runtime>(&dryrun_config.seed_or_path , &rpc)
|
||||
.await
|
||||
.expect("Provided account is invalid, terminating.")
|
||||
};
|
||||
dry_run_cmd(rpc, dryrun_config, signer_account).await
|
||||
.map_err(|e| {
|
||||
log::error!(target: LOG_TARGET, "DryRun error: {:?}", e);
|
||||
}),
|
||||
Command::EmergencySolution(cmd) => emergency_solution_cmd(rpc, cmd).await
|
||||
})},
|
||||
Command::EmergencySolution(emergency_solution_config) =>
|
||||
emergency_solution_cmd(rpc, emergency_solution_config).await
|
||||
.map_err(|e| {
|
||||
log::error!(target: LOG_TARGET, "EmergencySolution error: {:?}", e);
|
||||
}),
|
||||
Command::Info(info_opts) => {
|
||||
let remote_runtime_version = rpc.runtime_version(None).await.expect("runtime_version infallible; qed.");
|
||||
|
||||
let builtin_version = any_runtime! {
|
||||
Version::get()
|
||||
};
|
||||
|
||||
let versions = RuntimeVersions::new(&remote_runtime_version, &builtin_version);
|
||||
|
||||
if !info_opts.json {
|
||||
println!("{}", versions);
|
||||
} else {
|
||||
let versions = serde_json::to_string_pretty(&versions).expect("Failed serializing version info");
|
||||
println!("{}", versions);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
};
|
||||
log::info!(target: LOG_TARGET, "round of execution finished. outcome = {:?}", outcome);
|
||||
@@ -696,102 +579,4 @@ mod tests {
|
||||
assert_eq!(polkadot_version.spec_name, "polkadot".into());
|
||||
assert_eq!(kusama_version.spec_name, "kusama".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_monitor_works() {
|
||||
let opt = Opt::try_parse_from([
|
||||
env!("CARGO_PKG_NAME"),
|
||||
"--uri",
|
||||
"hi",
|
||||
"--seed-or-path",
|
||||
"//Alice",
|
||||
"monitor",
|
||||
"--listen",
|
||||
"head",
|
||||
"seq-phragmen",
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
opt,
|
||||
Opt {
|
||||
uri: "hi".to_string(),
|
||||
seed_or_path: "//Alice".to_string(),
|
||||
command: Command::Monitor(MonitorConfig {
|
||||
listen: "head".to_string(),
|
||||
solver: Solver::SeqPhragmen { iterations: 10 },
|
||||
submission_strategy: SubmissionStrategy::IfLeading,
|
||||
}),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_dry_run_works() {
|
||||
let opt = Opt::try_parse_from([
|
||||
env!("CARGO_PKG_NAME"),
|
||||
"--uri",
|
||||
"hi",
|
||||
"--seed-or-path",
|
||||
"//Alice",
|
||||
"dry-run",
|
||||
"phrag-mms",
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
opt,
|
||||
Opt {
|
||||
uri: "hi".to_string(),
|
||||
seed_or_path: "//Alice".to_string(),
|
||||
command: Command::DryRun(DryRunConfig {
|
||||
at: None,
|
||||
solver: Solver::PhragMMS { iterations: 10 },
|
||||
force_snapshot: false,
|
||||
}),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_emergency_works() {
|
||||
let opt = Opt::try_parse_from([
|
||||
env!("CARGO_PKG_NAME"),
|
||||
"--uri",
|
||||
"hi",
|
||||
"--seed-or-path",
|
||||
"//Alice",
|
||||
"emergency-solution",
|
||||
"99",
|
||||
"phrag-mms",
|
||||
"--iterations",
|
||||
"1337",
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
opt,
|
||||
Opt {
|
||||
uri: "hi".to_string(),
|
||||
seed_or_path: "//Alice".to_string(),
|
||||
command: Command::EmergencySolution(EmergencySolutionConfig {
|
||||
take: Some(99),
|
||||
at: None,
|
||||
solver: Solver::PhragMMS { iterations: 1337 }
|
||||
}),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn submission_strategy_from_str_works() {
|
||||
use std::str::FromStr;
|
||||
|
||||
assert_eq!(SubmissionStrategy::from_str("if-leading"), Ok(SubmissionStrategy::IfLeading));
|
||||
assert_eq!(SubmissionStrategy::from_str("always"), Ok(SubmissionStrategy::Always));
|
||||
assert_eq!(
|
||||
SubmissionStrategy::from_str(" percent-better 99 "),
|
||||
Ok(SubmissionStrategy::ClaimBetterThan(Perbill::from_percent(99)))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user