Allow staking miner to use different election algorithms (#3752)

* WIP

* Dry run cmd working

* Monitor cmd works

* Configure balance with parameter type

* Comments

* cleannnn

* Add balancing to PhragMMS

* Move OffchainRanomBalancing to common

* DRY mine_unchecked over config.solver

* FMT

* Apply suggestions from code review

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Improve docs for any_runtime_unit!

* Some cleanup

* fmt

* Correct capitilaztion

* Improve version mismatch log

* Revert "Improve version mismatch log"

This reverts commit 57570403f654f1efa4307956cda2de6f0c64f70a.

* Apply suggestions from code review

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Remove Balancing struct and use Balancing Parameter type instead

* update Substrate

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: parity-processbot <>
This commit is contained in:
Zeke Mostov
2021-09-09 13:21:54 -07:00
committed by GitHub
parent 83a35874ee
commit b048be06fe
13 changed files with 395 additions and 272 deletions
+164 -170
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -22,6 +22,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master
sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-npos-elections = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
@@ -90,6 +91,7 @@ std = [
"libsecp256k1/std", "libsecp256k1/std",
"runtime-parachains/std", "runtime-parachains/std",
"xcm/std", "xcm/std",
"sp-npos-elections/std",
] ]
runtime-benchmarks = [ runtime-benchmarks = [
"libsecp256k1/hmac", "libsecp256k1/hmac",
+27
View File
@@ -55,3 +55,30 @@ impl pallet_election_provider_multi_phase::BenchmarkingConfig for BenchmarkConfi
const MINER_MAXIMUM_VOTERS: u32 = 15_000; const MINER_MAXIMUM_VOTERS: u32 = 15_000;
const MAXIMUM_TARGETS: u32 = 2000; const MAXIMUM_TARGETS: u32 = 2000;
} }
/// Maximum number of iterations for balancing that will be executed in the embedded miner of
/// pallet-election-provider-multi-phase.
pub const MINER_MAX_ITERATIONS: u32 = 10;
/// A source of random balance for the NPoS Solver, which is meant to be run by the OCW election
/// miner.
pub struct OffchainRandomBalancing;
impl frame_support::pallet_prelude::Get<Option<(usize, sp_npos_elections::ExtendedBalance)>>
for OffchainRandomBalancing
{
fn get() -> Option<(usize, sp_npos_elections::ExtendedBalance)> {
use sp_runtime::{codec::Decode, traits::TrailingZeroInput};
let iters = match MINER_MAX_ITERATIONS {
0 => 0,
max @ _ => {
let seed = sp_io::offchain::random_seed();
let random = <u32>::decode(&mut TrailingZeroInput::new(&seed))
.expect("input is padded with zeroes; qed") %
max.saturating_add(1);
random as usize
},
};
Some((iters, 0))
}
}
+5 -2
View File
@@ -356,7 +356,6 @@ parameter_types! {
pub SolutionImprovementThreshold: Perbill = Perbill::from_rational(5u32, 10_000); pub SolutionImprovementThreshold: Perbill = Perbill::from_rational(5u32, 10_000);
// miner configs // miner configs
pub const MinerMaxIterations: u32 = 10;
pub OffchainRepeat: BlockNumber = 5; pub OffchainRepeat: BlockNumber = 5;
} }
@@ -384,7 +383,6 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
type RewardHandler = (); // nothing to do upon rewards type RewardHandler = (); // nothing to do upon rewards
type SignedPhase = SignedPhase; type SignedPhase = SignedPhase;
type SolutionImprovementThreshold = SolutionImprovementThreshold; type SolutionImprovementThreshold = SolutionImprovementThreshold;
type MinerMaxIterations = MinerMaxIterations;
type MinerMaxWeight = OffchainSolutionWeightLimit; // For now use the one from staking. type MinerMaxWeight = OffchainSolutionWeightLimit; // For now use the one from staking.
type MinerMaxLength = OffchainSolutionLengthLimit; type MinerMaxLength = OffchainSolutionLengthLimit;
type OffchainRepeat = OffchainRepeat; type OffchainRepeat = OffchainRepeat;
@@ -393,6 +391,11 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
type Solution = NposCompactSolution24; type Solution = NposCompactSolution24;
type OnChainAccuracy = Perbill; type OnChainAccuracy = Perbill;
type Fallback = Fallback; type Fallback = Fallback;
type Solver = frame_election_provider_support::SequentialPhragmen<
AccountId,
pallet_election_provider_multi_phase::SolutionAccuracyOf<Runtime>,
runtime_common::elections::OffchainRandomBalancing,
>;
type BenchmarkingConfig = runtime_common::elections::BenchmarkConfig; type BenchmarkingConfig = runtime_common::elections::BenchmarkConfig;
type ForceOrigin = EnsureOneOf< type ForceOrigin = EnsureOneOf<
AccountId, AccountId,
+5 -2
View File
@@ -362,7 +362,6 @@ parameter_types! {
pub SolutionImprovementThreshold: Perbill = Perbill::from_rational(5u32, 10_000); pub SolutionImprovementThreshold: Perbill = Perbill::from_rational(5u32, 10_000);
// miner configs // miner configs
pub const MinerMaxIterations: u32 = 10;
pub OffchainRepeat: BlockNumber = 5; pub OffchainRepeat: BlockNumber = 5;
} }
@@ -390,7 +389,6 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
type SlashHandler = (); // burn slashes type SlashHandler = (); // burn slashes
type RewardHandler = (); // nothing to do upon rewards type RewardHandler = (); // nothing to do upon rewards
type SolutionImprovementThreshold = SolutionImprovementThreshold; type SolutionImprovementThreshold = SolutionImprovementThreshold;
type MinerMaxIterations = MinerMaxIterations;
type MinerMaxWeight = OffchainSolutionWeightLimit; // For now use the one from staking. type MinerMaxWeight = OffchainSolutionWeightLimit; // For now use the one from staking.
type MinerMaxLength = OffchainSolutionLengthLimit; type MinerMaxLength = OffchainSolutionLengthLimit;
type OffchainRepeat = OffchainRepeat; type OffchainRepeat = OffchainRepeat;
@@ -399,6 +397,11 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
type OnChainAccuracy = Perbill; type OnChainAccuracy = Perbill;
type Solution = NposCompactSolution16; type Solution = NposCompactSolution16;
type Fallback = Fallback; type Fallback = Fallback;
type Solver = frame_election_provider_support::SequentialPhragmen<
AccountId,
pallet_election_provider_multi_phase::SolutionAccuracyOf<Runtime>,
runtime_common::elections::OffchainRandomBalancing,
>;
type BenchmarkingConfig = runtime_common::elections::BenchmarkConfig; type BenchmarkingConfig = runtime_common::elections::BenchmarkConfig;
type ForceOrigin = EnsureOneOf< type ForceOrigin = EnsureOneOf<
AccountId, AccountId,
+5 -2
View File
@@ -342,7 +342,6 @@ parameter_types! {
pub SolutionImprovementThreshold: Perbill = Perbill::from_rational(5u32, 10_000); pub SolutionImprovementThreshold: Perbill = Perbill::from_rational(5u32, 10_000);
// miner configs // miner configs
pub const MinerMaxIterations: u32 = 10;
pub OffchainRepeat: BlockNumber = 5; pub OffchainRepeat: BlockNumber = 5;
} }
@@ -370,7 +369,6 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
type SlashHandler = (); // burn slashes type SlashHandler = (); // burn slashes
type RewardHandler = (); // nothing to do upon rewards type RewardHandler = (); // nothing to do upon rewards
type SolutionImprovementThreshold = SolutionImprovementThreshold; type SolutionImprovementThreshold = SolutionImprovementThreshold;
type MinerMaxIterations = MinerMaxIterations;
type MinerMaxWeight = OffchainSolutionWeightLimit; // For now use the one from staking. type MinerMaxWeight = OffchainSolutionWeightLimit; // For now use the one from staking.
type MinerMaxLength = OffchainSolutionLengthLimit; type MinerMaxLength = OffchainSolutionLengthLimit;
type OffchainRepeat = OffchainRepeat; type OffchainRepeat = OffchainRepeat;
@@ -379,6 +377,11 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
type OnChainAccuracy = Perbill; type OnChainAccuracy = Perbill;
type Solution = NposCompactSolution16; type Solution = NposCompactSolution16;
type Fallback = Fallback; type Fallback = Fallback;
type Solver = frame_election_provider_support::SequentialPhragmen<
AccountId,
pallet_election_provider_multi_phase::SolutionAccuracyOf<Runtime>,
runtime_common::elections::OffchainRandomBalancing,
>;
type BenchmarkingConfig = runtime_common::elections::BenchmarkConfig; type BenchmarkingConfig = runtime_common::elections::BenchmarkConfig;
type ForceOrigin = EnsureRoot<AccountId>; type ForceOrigin = EnsureRoot<AccountId>;
type WeightInfo = weights::pallet_election_provider_multi_phase::WeightInfo<Runtime>; type WeightInfo = weights::pallet_election_provider_multi_phase::WeightInfo<Runtime>;
+2
View File
@@ -22,11 +22,13 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } 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" } sc-transaction-pool-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-system = { 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-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-election-provider-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-election-provider-multi-phase = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-election-provider-multi-phase = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }
+7 -4
View File
@@ -23,7 +23,7 @@ use codec::Encode;
use frame_support::traits::Currency; use frame_support::traits::Currency;
/// Forcefully create the snapshot. This can be used to compute the election at anytime. /// Forcefully create the snapshot. This can be used to compute the election at anytime.
fn force_create_snapshot<T: EPM::Config>(ext: &mut Ext) -> Result<(), Error> { fn force_create_snapshot<T: EPM::Config>(ext: &mut Ext) -> Result<(), Error<T>> {
ext.execute_with(|| { ext.execute_with(|| {
if <EPM::Snapshot<T>>::exists() { if <EPM::Snapshot<T>>::exists() {
log::info!(target: LOG_TARGET, "snapshot already exists."); log::info!(target: LOG_TARGET, "snapshot already exists.");
@@ -112,7 +112,7 @@ macro_rules! dry_run_cmd_for { ($runtime:ident) => { paste::paste! {
shared: SharedConfig, shared: SharedConfig,
config: DryRunConfig, config: DryRunConfig,
signer: Signer, signer: Signer,
) -> Result<(), Error> { ) -> Result<(), Error<$crate::[<$runtime _runtime_exports>]::Runtime>> {
use $crate::[<$runtime _runtime_exports>]::*; use $crate::[<$runtime _runtime_exports>]::*;
let mut ext = crate::create_election_ext::<Runtime, Block>( let mut ext = crate::create_election_ext::<Runtime, Block>(
shared.uri.clone(), shared.uri.clone(),
@@ -121,7 +121,8 @@ macro_rules! dry_run_cmd_for { ($runtime:ident) => { paste::paste! {
).await?; ).await?;
force_create_snapshot::<Runtime>(&mut ext)?; force_create_snapshot::<Runtime>(&mut ext)?;
let (raw_solution, witness) = crate::mine_unchecked::<Runtime>(&mut ext, config.iterations, false)?; let (raw_solution, witness) = crate::mine_with::<Runtime>(&config.solver, &mut ext)?;
let nonce = crate::get_account_info::<Runtime>(client, &signer.account, config.at) let nonce = crate::get_account_info::<Runtime>(client, &signer.account, config.at)
.await? .await?
.map(|i| i.nonce) .map(|i| i.nonce)
@@ -148,7 +149,9 @@ macro_rules! dry_run_cmd_for { ($runtime:ident) => { paste::paste! {
}); });
log::info!(target: LOG_TARGET, "dispatch result is {:?}", dispatch_result); log::info!(target: LOG_TARGET, "dispatch result is {:?}", dispatch_result);
let outcome = rpc_decode::<sp_runtime::ApplyExtrinsicResult>(client, "system_dryRun", params!{ bytes }).await?; let outcome = rpc_decode::<sp_runtime::ApplyExtrinsicResult>(client, "system_dryRun", params!{ bytes })
.await
.map_err::<Error<Runtime>, _>(Into::into)?;
log::info!(target: LOG_TARGET, "dry-run outcome is {:?}", outcome); log::info!(target: LOG_TARGET, "dry-run outcome is {:?}", outcome);
Ok(()) Ok(())
} }
@@ -18,20 +18,22 @@
use crate::{prelude::*, Error, SharedConfig}; use crate::{prelude::*, Error, SharedConfig};
use codec::Encode; use codec::Encode;
use frame_election_provider_support::SequentialPhragmen;
use std::io::Write; use std::io::Write;
macro_rules! emergency_solution_cmd_for { ($runtime:ident) => { paste::paste! { macro_rules! emergency_solution_cmd_for { ($runtime:ident) => { paste::paste! {
/// Execute the emergency-solution command. /// Execute the emergency-solution command.
pub(crate) async fn [<emergency_solution_cmd_ $runtime>]( pub(crate) async fn [<emergency_solution_cmd_ $runtime>](
shared: SharedConfig, shared: SharedConfig,
) -> Result<(), Error> { ) -> Result<(), Error<$crate::[<$runtime _runtime_exports>]::Runtime>> {
use $crate::[<$runtime _runtime_exports>]::*; use $crate::[<$runtime _runtime_exports>]::*;
let mut ext = crate::create_election_ext::<Runtime, Block>(shared.uri.clone(), None, vec![]).await?; let mut ext = crate::create_election_ext::<Runtime, Block>(shared.uri.clone(), None, vec![]).await?;
ext.execute_with(|| { ext.execute_with(|| {
assert!(EPM::Pallet::<Runtime>::current_phase().is_emergency()); assert!(EPM::Pallet::<Runtime>::current_phase().is_emergency());
// NOTE: this internally calls feasibility_check, but we just re-do it here as an easy way // NOTE: this internally calls feasibility_check, but we just re-do it here as an easy way
// to get a `ReadySolution`. // to get a `ReadySolution`.
let (raw_solution, _) = <EPM::Pallet<Runtime>>::mine_solution(50)?; let (raw_solution, _) =
<EPM::Pallet<Runtime>>::mine_solution::<SequentialPhragmen<AccountId, sp_runtime::Perbill>>()?;
log::info!(target: LOG_TARGET, "mined solution with {:?}", &raw_solution.score); log::info!(target: LOG_TARGET, "mined solution with {:?}", &raw_solution.score);
let ready_solution = EPM::Pallet::<Runtime>::feasibility_check(raw_solution, EPM::ElectionCompute::Signed)?; let ready_solution = EPM::Pallet::<Runtime>::feasibility_check(raw_solution, EPM::ElectionCompute::Signed)?;
let encoded_ready = ready_solution.encode(); let encoded_ready = ready_solution.encode();
+129 -31
View File
@@ -38,9 +38,11 @@ mod signer;
pub(crate) use prelude::*; pub(crate) use prelude::*;
pub(crate) use signer::get_account_info; pub(crate) use signer::get_account_info;
use frame_election_provider_support::NposSolver;
use frame_support::traits::Get; use frame_support::traits::Get;
use jsonrpsee_ws_client::{WsClient, WsClientBuilder}; use jsonrpsee_ws_client::{WsClient, WsClientBuilder};
use remote_externalities::{Builder, Mode, OnlineConfig}; use remote_externalities::{Builder, Mode, OnlineConfig};
use sp_npos_elections::ExtendedBalance;
use sp_runtime::traits::Block as BlockT; use sp_runtime::traits::Block as BlockT;
use structopt::StructOpt; use structopt::StructOpt;
@@ -193,15 +195,43 @@ macro_rules! any_runtime {
} }
} }
#[derive(Debug, thiserror::Error)] /// Same as [`any_runtime`], but instead of returning a `Result`, this simply returns `()`. Useful
enum Error { /// for situations where the result is not useful and un-ergonomic to handle.
#[macro_export]
macro_rules! any_runtime_unit {
($($code:tt)*) => {
unsafe {
match $crate::RUNTIME {
$crate::AnyRuntime::Polkadot => {
#[allow(unused)]
use $crate::polkadot_runtime_exports::*;
let _ = $($code)*;
},
$crate::AnyRuntime::Kusama => {
#[allow(unused)]
use $crate::kusama_runtime_exports::*;
let _ = $($code)*;
},
$crate::AnyRuntime::Westend => {
#[allow(unused)]
use $crate::westend_runtime_exports::*;
let _ = $($code)*;
}
}
}
}
}
#[derive(frame_support::DebugNoBound, thiserror::Error)]
enum Error<T: EPM::Config> {
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
Jsonrpsee(#[from] jsonrpsee_ws_client::types::Error), JsonRpsee(#[from] jsonrpsee_ws_client::types::Error),
RpcHelperError(#[from] rpc_helpers::RpcHelperError),
Codec(#[from] codec::Error), Codec(#[from] codec::Error),
Crypto(sp_core::crypto::SecretStringError), Crypto(sp_core::crypto::SecretStringError),
RemoteExternalities(&'static str), RemoteExternalities(&'static str),
PalletMiner(EPM::unsigned::MinerError), PalletMiner(EPM::unsigned::MinerError<T>),
PalletElection(EPM::ElectionError), PalletElection(EPM::ElectionError<T>),
PalletFeasibility(EPM::FeasibilityError), PalletFeasibility(EPM::FeasibilityError),
AccountDoesNotExists, AccountDoesNotExists,
IncorrectPhase, IncorrectPhase,
@@ -209,33 +239,33 @@ enum Error {
VersionMismatch, VersionMismatch,
} }
impl From<sp_core::crypto::SecretStringError> for Error { impl<T: EPM::Config> From<sp_core::crypto::SecretStringError> for Error<T> {
fn from(e: sp_core::crypto::SecretStringError) -> Error { fn from(e: sp_core::crypto::SecretStringError) -> Error<T> {
Error::Crypto(e) Error::Crypto(e)
} }
} }
impl From<EPM::unsigned::MinerError> for Error { impl<T: EPM::Config> From<EPM::unsigned::MinerError<T>> for Error<T> {
fn from(e: EPM::unsigned::MinerError) -> Error { fn from(e: EPM::unsigned::MinerError<T>) -> Error<T> {
Error::PalletMiner(e) Error::PalletMiner(e)
} }
} }
impl From<EPM::ElectionError> for Error { impl<T: EPM::Config> From<EPM::ElectionError<T>> for Error<T> {
fn from(e: EPM::ElectionError) -> Error { fn from(e: EPM::ElectionError<T>) -> Error<T> {
Error::PalletElection(e) Error::PalletElection(e)
} }
} }
impl From<EPM::FeasibilityError> for Error { impl<T: EPM::Config> From<EPM::FeasibilityError> for Error<T> {
fn from(e: EPM::FeasibilityError) -> Error { fn from(e: EPM::FeasibilityError) -> Error<T> {
Error::PalletFeasibility(e) Error::PalletFeasibility(e)
} }
} }
impl std::fmt::Display for Error { impl<T: EPM::Config> std::fmt::Display for Error<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<Error as std::fmt::Debug>::fmt(self, f) <Error<T> as std::fmt::Debug>::fmt(self, f)
} }
} }
@@ -249,6 +279,58 @@ enum Command {
EmergencySolution, EmergencySolution,
} }
#[derive(Debug, Clone, StructOpt)]
enum Solvers {
SeqPhragmen {
#[structopt(long, default_value = "10")]
iterations: usize,
},
PhragMMS {
#[structopt(long, default_value = "10")]
iterations: usize,
},
}
/// Mine a solution with the given `solver`.
fn mine_with<T>(
solver: &Solvers,
ext: &mut Ext,
) -> Result<(EPM::RawSolution<EPM::SolutionOf<T>>, u32), Error<T>>
where
T: EPM::Config,
T::Solver: NposSolver<Error = sp_npos_elections::Error>,
{
use frame_election_provider_support::{PhragMMS, SequentialPhragmen};
match solver {
Solvers::SeqPhragmen { iterations } => {
BalanceIterations::set(*iterations);
mine_unchecked::<
T,
SequentialPhragmen<
<T as frame_system::Config>::AccountId,
sp_runtime::Perbill,
Balancing,
>,
>(ext, false)
},
Solvers::PhragMMS { iterations } => {
BalanceIterations::set(*iterations);
mine_unchecked::<
T,
PhragMMS<<T as frame_system::Config>::AccountId, sp_runtime::Perbill, Balancing>,
>(ext, false)
},
}
}
frame_support::parameter_types! {
/// Number of balancing iterations for a solution algorithm. Set based on the [`Solvers`] CLI
/// config.
pub static BalanceIterations: usize = 10;
pub static Balancing: Option<(usize, ExtendedBalance)> = Some((BalanceIterations::get(), 0));
}
#[derive(Debug, Clone, StructOpt)] #[derive(Debug, Clone, StructOpt)]
struct MonitorConfig { struct MonitorConfig {
/// They type of event to listen to. /// They type of event to listen to.
@@ -259,8 +341,8 @@ struct MonitorConfig {
#[structopt(long, default_value = "head", possible_values = &["head", "finalized"])] #[structopt(long, default_value = "head", possible_values = &["head", "finalized"])]
listen: String, listen: String,
#[structopt(long, short, default_value = "10")] #[structopt(subcommand)]
iterations: usize, solver: Solvers,
} }
#[derive(Debug, Clone, StructOpt)] #[derive(Debug, Clone, StructOpt)]
@@ -269,8 +351,8 @@ struct DryRunConfig {
#[structopt(long)] #[structopt(long)]
at: Option<Hash>, at: Option<Hash>,
#[structopt(long, short, default_value = "10")] #[structopt(subcommand)]
iterations: usize, solver: Solvers,
} }
#[derive(Debug, Clone, StructOpt)] #[derive(Debug, Clone, StructOpt)]
@@ -303,7 +385,7 @@ async fn create_election_ext<T: EPM::Config, B: BlockT>(
uri: String, uri: String,
at: Option<B::Hash>, at: Option<B::Hash>,
additional: Vec<String>, additional: Vec<String>,
) -> Result<Ext, Error> { ) -> Result<Ext, Error<T>> {
use frame_support::{storage::generator::StorageMap, traits::PalletInfo}; use frame_support::{storage::generator::StorageMap, traits::PalletInfo};
use sp_core::hashing::twox_128; use sp_core::hashing::twox_128;
@@ -327,13 +409,20 @@ async fn create_election_ext<T: EPM::Config, B: BlockT>(
/// Compute the election at the given block number. It expects to NOT be `Phase::Off`. In other /// Compute the election at the given block number. It expects to NOT be `Phase::Off`. In other
/// words, the snapshot must exists on the given externalities. /// words, the snapshot must exists on the given externalities.
fn mine_unchecked<T: EPM::Config>( fn mine_unchecked<T, S>(
ext: &mut Ext, ext: &mut Ext,
iterations: usize,
do_feasibility: bool, do_feasibility: bool,
) -> Result<(EPM::RawSolution<EPM::SolutionOf<T>>, u32), Error> { ) -> Result<(EPM::RawSolution<EPM::SolutionOf<T>>, u32), Error<T>>
where
T: EPM::Config,
S: NposSolver<
Error = <<T as EPM::Config>::Solver as NposSolver>::Error,
AccountId = <<T as EPM::Config>::Solver as NposSolver>::AccountId,
>,
{
ext.execute_with(|| { ext.execute_with(|| {
let (solution, _) = <EPM::Pallet<T>>::mine_solution(iterations)?; let (solution, _) =
<EPM::Pallet<T>>::mine_solution::<S>().map_err::<Error<T>, _>(Into::into)?;
if do_feasibility { if do_feasibility {
let _ = <EPM::Pallet<T>>::feasibility_check( let _ = <EPM::Pallet<T>>::feasibility_check(
solution.clone(), solution.clone(),
@@ -346,7 +435,7 @@ fn mine_unchecked<T: EPM::Config>(
} }
#[allow(unused)] #[allow(unused)]
fn mine_dpos<T: EPM::Config>(ext: &mut Ext) -> Result<(), Error> { fn mine_dpos<T: EPM::Config>(ext: &mut Ext) -> Result<(), Error<T>> {
ext.execute_with(|| { ext.execute_with(|| {
use std::collections::BTreeMap; use std::collections::BTreeMap;
use EPM::RoundSnapshot; use EPM::RoundSnapshot;
@@ -383,10 +472,10 @@ fn mine_dpos<T: EPM::Config>(ext: &mut Ext) -> Result<(), Error> {
}) })
} }
pub(crate) async fn check_versions<T: frame_system::Config>( pub(crate) async fn check_versions<T: frame_system::Config + EPM::Config>(
client: &WsClient, client: &WsClient,
print: bool, print: bool,
) -> Result<(), Error> { ) -> Result<(), Error<T>> {
let linked_version = T::Version::get(); let linked_version = T::Version::get();
let on_chain_version = rpc_helpers::rpc::<sp_version::RuntimeVersion>( let on_chain_version = rpc_helpers::rpc::<sp_version::RuntimeVersion>(
client, client,
@@ -486,7 +575,7 @@ async fn main() {
} }
log::info!(target: LOG_TARGET, "connected to chain {:?}", chain); log::info!(target: LOG_TARGET, "connected to chain {:?}", chain);
let _ = any_runtime! { any_runtime_unit! {
check_versions::<Runtime>(&client, true).await check_versions::<Runtime>(&client, true).await
}; };
@@ -498,9 +587,18 @@ async fn main() {
let outcome = any_runtime! { let outcome = any_runtime! {
match command.clone() { match command.clone() {
Command::Monitor(c) => monitor_cmd(&client, shared, c, signer_account).await, Command::Monitor(c) => monitor_cmd(&client, shared, c, signer_account).await
Command::DryRun(c) => dry_run_cmd(&client, shared, c, signer_account).await, .map_err(|e| {
Command::EmergencySolution => emergency_solution_cmd(shared.clone()).await, log::error!(target: LOG_TARGET, "Monitor error: {:?}", e);
}),
Command::DryRun(c) => dry_run_cmd(&client, shared, c, signer_account).await
.map_err(|e| {
log::error!(target: LOG_TARGET, "DryRun error: {:?}", e);
}),
Command::EmergencySolution => emergency_solution_cmd(shared.clone()).await
.map_err(|e| {
log::error!(target: LOG_TARGET, "EmergencySolution error: {:?}", e);
}),
} }
}; };
log::info!(target: LOG_TARGET, "round of execution finished. outcome = {:?}", outcome); log::info!(target: LOG_TARGET, "round of execution finished. outcome = {:?}", outcome);
+8 -7
View File
@@ -30,10 +30,11 @@ use sc_transaction_pool_api::TransactionStatus;
async fn ensure_signed_phase<T: EPM::Config, B: BlockT>( async fn ensure_signed_phase<T: EPM::Config, B: BlockT>(
client: &WsClient, client: &WsClient,
at: B::Hash, at: B::Hash,
) -> Result<(), Error> { ) -> Result<(), Error<T>> {
let key = sp_core::storage::StorageKey(EPM::CurrentPhase::<T>::hashed_key().to_vec()); let key = sp_core::storage::StorageKey(EPM::CurrentPhase::<T>::hashed_key().to_vec());
let phase = get_storage::<EPM::Phase<BlockNumber>>(client, params! {key, at}) let phase = get_storage::<EPM::Phase<BlockNumber>>(client, params! {key, at})
.await? .await
.map_err::<Error<T>, _>(Into::into)?
.unwrap_or_default(); .unwrap_or_default();
if phase.is_signed() { if phase.is_signed() {
@@ -50,7 +51,7 @@ async fn ensure_no_previous_solution<
>( >(
ext: &mut Ext, ext: &mut Ext,
us: &AccountId, us: &AccountId,
) -> Result<(), Error> { ) -> Result<(), Error<T>> {
use EPM::signed::SignedSubmissions; use EPM::signed::SignedSubmissions;
ext.execute_with(|| { ext.execute_with(|| {
if <SignedSubmissions<T>>::get().iter().any(|ss| &ss.who == us) { if <SignedSubmissions<T>>::get().iter().any(|ss| &ss.who == us) {
@@ -68,7 +69,7 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
shared: SharedConfig, shared: SharedConfig,
config: MonitorConfig, config: MonitorConfig,
signer: Signer, signer: Signer,
) -> Result<(), Error> { ) -> Result<(), Error<$crate::[<$runtime _runtime_exports>]::Runtime>> {
use $crate::[<$runtime _runtime_exports>]::*; use $crate::[<$runtime _runtime_exports>]::*;
let (sub, unsub) = if config.listen == "head" { let (sub, unsub) = if config.listen == "head" {
("chain_subscribeNewHeads", "chain_unsubscribeNewHeads") ("chain_subscribeNewHeads", "chain_unsubscribeNewHeads")
@@ -109,7 +110,8 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
continue; continue;
} }
let (raw_solution, witness) = crate::mine_unchecked::<Runtime>(&mut ext, config.iterations, true)?; let (raw_solution, witness) = crate::mine_with::<Runtime>(&config.solver, &mut ext)?;
log::info!(target: LOG_TARGET, "mined solution with {:?}", &raw_solution.score); log::info!(target: LOG_TARGET, "mined solution with {:?}", &raw_solution.score);
let nonce = crate::get_account_info::<Runtime>(client, &signer.account, Some(hash)) let nonce = crate::get_account_info::<Runtime>(client, &signer.account, Some(hash))
@@ -149,8 +151,7 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
TransactionStatus::InBlock(hash) => { TransactionStatus::InBlock(hash) => {
log::info!(target: LOG_TARGET, "included at {:?}", hash); log::info!(target: LOG_TARGET, "included at {:?}", hash);
let key = frame_support::storage::storage_prefix(b"System", b"Events"); let key = frame_support::storage::storage_prefix(b"System", b"Events");
let events =get_storage::< let events = get_storage::<Vec<frame_system::EventRecord<Event, <Block as BlockT>::Hash>>,
Vec<frame_system::EventRecord<Event, <Block as BlockT>::Hash>>
>(client, params!{ key, hash }).await?.unwrap_or_default(); >(client, params!{ key, hash }).await?.unwrap_or_default();
log::info!(target: LOG_TARGET, "events at inclusion {:?}", events); log::info!(target: LOG_TARGET, "events at inclusion {:?}", events);
} }
+28 -44
View File
@@ -20,6 +20,18 @@ use super::*;
use jsonrpsee_ws_client::types::traits::Client; use jsonrpsee_ws_client::types::traits::Client;
pub(crate) use jsonrpsee_ws_client::types::v2::params::JsonRpcParams; pub(crate) use jsonrpsee_ws_client::types::v2::params::JsonRpcParams;
#[derive(frame_support::DebugNoBound, thiserror::Error)]
pub(crate) enum RpcHelperError {
JsonRpsee(#[from] jsonrpsee_ws_client::types::Error),
Codec(#[from] codec::Error),
}
impl std::fmt::Display for RpcHelperError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<RpcHelperError as std::fmt::Debug>::fmt(self, f)
}
}
#[macro_export] #[macro_export]
macro_rules! params { macro_rules! params {
($($param:expr),*) => { ($($param:expr),*) => {
@@ -41,8 +53,11 @@ pub(crate) async fn rpc<'a, Ret: serde::de::DeserializeOwned>(
client: &WsClient, client: &WsClient,
method: &'a str, method: &'a str,
params: JsonRpcParams<'a>, params: JsonRpcParams<'a>,
) -> Result<Ret, Error> { ) -> Result<Ret, RpcHelperError> {
client.request::<Ret>(method, params).await.map_err(Into::into) client
.request::<Ret>(method, params)
.await
.map_err::<RpcHelperError, _>(Into::into)
} }
/// Make the rpc request, decode the outcome into `Dec`. Don't use for storage, it will fail for /// Make the rpc request, decode the outcome into `Dec`. Don't use for storage, it will fail for
@@ -51,57 +66,26 @@ pub(crate) async fn rpc_decode<'a, Dec: codec::Decode>(
client: &WsClient, client: &WsClient,
method: &'a str, method: &'a str,
params: JsonRpcParams<'a>, params: JsonRpcParams<'a>,
) -> Result<Dec, Error> { ) -> Result<Dec, RpcHelperError> {
let bytes = rpc::<sp_core::Bytes>(client, method, params).await?; let bytes = rpc::<sp_core::Bytes>(client, method, params)
<Dec as codec::Decode>::decode(&mut &*bytes.0).map_err(Into::into) .await
.map_err::<RpcHelperError, _>(Into::into)?;
<Dec as codec::Decode>::decode(&mut &*bytes.0).map_err::<RpcHelperError, _>(Into::into)
} }
/// Get the storage item. /// Get the storage item.
pub(crate) async fn get_storage<'a, T: codec::Decode>( pub(crate) async fn get_storage<'a, T: codec::Decode>(
client: &WsClient, client: &WsClient,
params: JsonRpcParams<'a>, params: JsonRpcParams<'a>,
) -> Result<Option<T>, Error> { ) -> Result<Option<T>, RpcHelperError> {
let maybe_bytes = rpc::<Option<sp_core::Bytes>>(client, "state_getStorage", params).await?; let maybe_bytes = rpc::<Option<sp_core::Bytes>>(client, "state_getStorage", params)
.await
.map_err::<RpcHelperError, _>(Into::into)?;
if let Some(bytes) = maybe_bytes { if let Some(bytes) = maybe_bytes {
let decoded = <T as codec::Decode>::decode(&mut &*bytes.0)?; let decoded = <T as codec::Decode>::decode(&mut &*bytes.0)
.map_err::<RpcHelperError, _>(Into::into)?;
Ok(Some(decoded)) Ok(Some(decoded))
} else { } else {
Ok(None) Ok(None)
} }
} }
use codec::{EncodeLike, FullCodec};
use frame_support::storage::{StorageMap, StorageValue};
#[allow(unused)]
pub(crate) async fn get_storage_value_frame_v2<'a, V: StorageValue<T>, T: FullCodec, Hash>(
client: &WsClient,
maybe_at: Option<Hash>,
) -> Result<Option<V::Query>, Error>
where
V::Query: codec::Decode,
Hash: serde::Serialize,
{
let key = <V as StorageValue<T>>::hashed_key();
get_storage::<V::Query>(client, params! { key, maybe_at }).await
}
#[allow(unused)]
pub(crate) async fn get_storage_map_frame_v2<
'a,
Hash,
KeyArg: EncodeLike<K>,
K: FullCodec,
T: FullCodec,
M: StorageMap<K, T>,
>(
client: &WsClient,
key: KeyArg,
maybe_at: Option<Hash>,
) -> Result<Option<M::Query>, Error>
where
M::Query: codec::Decode,
Hash: serde::Serialize,
{
let key = <M as StorageMap<K, T>>::hashed_key_for(key);
get_storage::<M::Query>(client, params! { key, maybe_at }).await
}
+9 -8
View File
@@ -34,11 +34,11 @@ pub(crate) struct Signer {
pub(crate) pair: Pair, pub(crate) pair: Pair,
} }
pub(crate) async fn get_account_info<T: frame_system::Config>( pub(crate) async fn get_account_info<T: frame_system::Config + EPM::Config>(
client: &WsClient, client: &WsClient,
who: &T::AccountId, who: &T::AccountId,
maybe_at: Option<T::Hash>, maybe_at: Option<T::Hash>,
) -> Result<Option<frame_system::AccountInfo<Index, T::AccountData>>, Error> { ) -> Result<Option<frame_system::AccountInfo<Index, T::AccountData>>, Error<T>> {
rpc_helpers::get_storage::<frame_system::AccountInfo<Index, T::AccountData>>( rpc_helpers::get_storage::<frame_system::AccountInfo<Index, T::AccountData>>(
client, client,
crate::params! { crate::params! {
@@ -47,26 +47,27 @@ pub(crate) async fn get_account_info<T: frame_system::Config>(
}, },
) )
.await .await
.map_err(Into::into)
} }
/// Read the signer account's URI /// Read the signer account's URI
pub(crate) async fn signer_uri_from_string< pub(crate) async fn signer_uri_from_string<
T: frame_system::Config< T: frame_system::Config<
AccountId = AccountId, AccountId = AccountId,
Index = Index, Index = Index,
AccountData = pallet_balances::AccountData<Balance>, AccountData = pallet_balances::AccountData<Balance>,
>, > + EPM::Config,
>( >(
seed: &str, seed: &str,
client: &WsClient, client: &WsClient,
) -> Result<Signer, Error> { ) -> Result<Signer, Error<T>> {
let seed = seed.trim(); let seed = seed.trim();
let pair = Pair::from_string(seed, None)?; let pair = Pair::from_string(seed, None)?;
let account = T::AccountId::from(pair.public()); let account = T::AccountId::from(pair.public());
let _info = get_account_info::<T>(client, &account, None) let _info = get_account_info::<T>(client, &account, None)
.await? .await?
.ok_or(Error::AccountDoesNotExists)?; .ok_or(Error::<T>::AccountDoesNotExists)?;
log::info!( log::info!(
target: LOG_TARGET, target: LOG_TARGET,
"loaded account {:?}, free: {:?}, info: {:?}", "loaded account {:?}, free: {:?}, info: {:?}",