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

check runtime version in staking miner #3628

Merged
9 commits merged into from
Aug 16, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions utils/staking-miner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ thiserror = "1.0.26"
remote-externalities = { git = "https:/paritytech/substrate", branch = "master" }

sp-core = { git = "https:/paritytech/substrate", branch = "master" }
sp-version = { git = "https:/paritytech/substrate", branch = "master" }
sp-io = { git = "https:/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https:/paritytech/substrate", branch = "master" }
sp-npos-elections = { git = "https:/paritytech/substrate", branch = "master" }
Expand All @@ -32,6 +33,7 @@ frame-support = { git = "https:/paritytech/substrate", branch = "mas
frame-election-provider-support = { git = "https:/paritytech/substrate", branch = "master" }
pallet-election-provider-multi-phase = { git = "https:/paritytech/substrate", branch = "master" }
pallet-staking = { git = "https:/paritytech/substrate", branch = "master" }
pallet-balances = { git = "https:/paritytech/substrate", branch = "master" }
pallet-transaction-payment = { git = "https:/paritytech/substrate", branch = "master" }

core-primitives = { package = "polkadot-core-primitives", path = "../../core-primitives" }
Expand All @@ -41,5 +43,7 @@ polkadot-runtime = { path = "../../runtime/polkadot" }
kusama-runtime = { path = "../../runtime/kusama" }
westend-runtime = { path = "../../runtime/westend" }

sub-tokens = { git = "https:/paritytech/substrate-debug-kit", branch = "master" }

[dev-dependencies]
sp-version = { git = "https:/paritytech/substrate", branch = "master" }
75 changes: 63 additions & 12 deletions utils/staking-miner/src/dry_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::{
params, prelude::*, rpc_helpers::*, signer::Signer, DryRunConfig, Error, SharedConfig, WsClient,
};
use codec::Encode;
use frame_support::traits::Currency;

/// 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> {
Expand All @@ -35,18 +36,52 @@ fn force_create_snapshot<T: EPM::Config>(ext: &mut Ext) -> Result<(), Error> {
}

/// Helper method to print the encoded size of the snapshot.
fn measure_snapshot_size<T: EPM::Config>(ext: &mut Ext) {
async fn print_info<T: EPM::Config>(
client: &WsClient,
ext: &mut Ext,
raw_solution: &EPM::RawSolution<EPM::CompactOf<T>>,
extrinsic: sp_core::Bytes,
) where
<T as EPM::Config>::Currency: Currency<T::AccountId, Balance = Balance>,
{
ext.execute_with(|| {
log::info!(target: LOG_TARGET, "Metadata: {:?}", <EPM::Pallet<T>>::snapshot_metadata());
log::info!(
target: LOG_TARGET,
"Encoded Length: {:?}",
"Snapshot Metadata: {:?}",
<EPM::Pallet<T>>::snapshot_metadata()
);
log::info!(
target: LOG_TARGET,
"Snapshot Encoded Length: {:?}",
<EPM::Pallet<T>>::snapshot()
.expect("snapshot must exist before calling `measure_snapshot_size`")
.encode()
.len()
);
})

let snapshot_size =
<EPM::Pallet<T>>::snapshot_metadata().expect("snapshot must exist by now; qed.");
let deposit = EPM::Pallet::<T>::deposit_for(&raw_solution, snapshot_size);
log::info!(
target: LOG_TARGET,
"solution score {:?} / deposit {:?}",
&raw_solution.score.iter().map(|x| Token::from(*x)).collect::<Vec<_>>(),
Token::from(deposit),
);
});

let info = rpc::<pallet_transaction_payment::RuntimeDispatchInfo<Balance>>(
client,
"payment_queryInfo",
params! { extrinsic },
)
.await;
log::info!(
target: LOG_TARGET,
"payment_queryInfo: (fee = {}) {:?}",
info.as_ref().map(|d| Token::from(d.partial_fee)).unwrap_or(Token::from(0)),
info,
);
}

/// Find the stake threshold in order to have at most `count` voters.
Expand Down Expand Up @@ -76,24 +111,40 @@ macro_rules! dry_run_cmd_for { ($runtime:ident) => { paste::paste! {
signer: Signer,
) -> Result<(), Error> {
use $crate::[<$runtime _runtime_exports>]::*;
let mut ext = crate::create_election_ext::<Runtime, Block>(shared.uri.clone(), config.at, true).await?;
let mut ext = crate::create_election_ext::<Runtime, Block>(
shared.uri.clone(),
config.at,
vec!["Staking".to_string(), "System".to_string(), "Balances".to_string()]
).await?;
force_create_snapshot::<Runtime>(&mut ext)?;
measure_snapshot_size::<Runtime>(&mut ext);
let (raw_solution, witness) = crate::mine_unchecked::<Runtime>(&mut ext, config.iterations, false)?;
log::info!(target: LOG_TARGET, "mined solution with {:?}", &raw_solution.score);

let (raw_solution, witness) = crate::mine_unchecked::<Runtime>(&mut ext, config.iterations, false)?;
let nonce = crate::get_account_info::<Runtime>(client, &signer.account, config.at)
.await?
.map(|i| i.nonce)
.expect("signer account is checked to exist upon startup; it can only die if it \
transfers funds out of it, or get slashed. If it does not exist at this point, \
it is likely due to a bug, or the signer got slashed. Terminating."
);
transfers funds out of it, or get slashed. If it does not exist at this point, \
it is likely due to a bug, or the signer got slashed. Terminating."
);
let tip = 0 as Balance;
let era = sp_runtime::generic::Era::Immortal;
let extrinsic = ext.execute_with(|| create_uxt(raw_solution, witness, signer.clone(), nonce, tip, era));
let extrinsic = ext.execute_with(|| create_uxt(raw_solution.clone(), witness, signer.clone(), nonce, tip, era));

let bytes = sp_core::Bytes(extrinsic.encode().to_vec());
print_info::<Runtime>(client, &mut ext, &raw_solution, bytes.clone()).await;
Copy link
Contributor

Choose a reason for hiding this comment

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

what would happen if we didn't block on this call? Would it still print correctly? If so it could be a performance improvement

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not sure what you mean, elaborate?


let feasibility_result = ext.execute_with(|| {
EPM::Pallet::<Runtime>::feasibility_check(raw_solution.clone(), EPM::ElectionCompute::Signed)
});
log::info!(target: LOG_TARGET, "feasibility result is {:?}", feasibility_result.map(|_| ()));

let dispatch_result = ext.execute_with(|| {
// manually tweak the phase.
EPM::CurrentPhase::<Runtime>::put(EPM::Phase::Signed);
EPM::Pallet::<Runtime>::submit(frame_system::RawOrigin::Signed(signer.account).into(), Box::new(raw_solution), witness)
});
log::info!(target: LOG_TARGET, "dispatch result is {:?}", dispatch_result);

let outcome = rpc_decode::<sp_runtime::ApplyExtrinsicResult>(client, "system_dryRun", params!{ bytes }).await?;
log::info!(target: LOG_TARGET, "dry-run outcome is {:?}", outcome);
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion utils/staking-miner/src/emergency_solution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ macro_rules! emergency_solution_cmd_for { ($runtime:ident) => { paste::paste! {
shared: SharedConfig,
) -> Result<(), Error> {
use $crate::[<$runtime _runtime_exports>]::*;
let mut ext = crate::create_election_ext::<Runtime, Block>(shared.uri.clone(), None, false).await?;
let mut ext = crate::create_election_ext::<Runtime, Block>(shared.uri.clone(), None, vec![]).await?;
ext.execute_with(|| {
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
Expand Down
69 changes: 51 additions & 18 deletions utils/staking-miner/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ mod signer;
pub(crate) use prelude::*;
pub(crate) use signer::get_account_info;

use frame_support::traits::Get;
use jsonrpsee_ws_client::{WsClient, WsClientBuilder};
use remote_externalities::{Builder, Mode, OnlineConfig};
use sp_runtime::traits::Block as BlockT;
Expand Down Expand Up @@ -172,14 +173,17 @@ macro_rules! any_runtime {
unsafe {
match $crate::RUNTIME {
$crate::AnyRuntime::Polkadot => {
#[allow(unused)]
use $crate::polkadot_runtime_exports::*;
$($code)*
},
$crate::AnyRuntime::Kusama => {
#[allow(unused)]
use $crate::kusama_runtime_exports::*;
$($code)*
},
$crate::AnyRuntime::Westend => {
#[allow(unused)]
use $crate::westend_runtime_exports::*;
$($code)*
}
Expand All @@ -201,6 +205,7 @@ enum Error {
AccountDoesNotExists,
IncorrectPhase,
AlreadySubmitted,
VersionMismatch,
}

impl From<sp_core::crypto::SecretStringError> for Error {
Expand Down Expand Up @@ -270,14 +275,14 @@ struct DryRunConfig {
#[derive(Debug, Clone, StructOpt)]
struct SharedConfig {
/// The `ws` node to connect to.
#[structopt(long, default_value = DEFAULT_URI)]
#[structopt(long, short, default_value = DEFAULT_URI)]
uri: String,

/// The file from which we read the account seed.
///
/// WARNING: don't use an account with a large stash for this. Based on how the bot is
/// configured, it might re-try lose funds through transaction fees/deposits.
#[structopt(long)]
#[structopt(long, short)]
account_seed: std::path::PathBuf,
}

Expand All @@ -296,29 +301,20 @@ struct Opt {
async fn create_election_ext<T: EPM::Config, B: BlockT>(
Copy link
Contributor

@emostov emostov Aug 12, 2021

Choose a reason for hiding this comment

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

Maybe update this doc comment to "Build the Ext at hash with all the data of ElectionProviderMultiPhase and any additional pallets." Since I it doesn't look like we actually default with the staking module.

uri: String,
at: Option<B::Hash>,
with_staking: bool,
additional: Vec<String>,
) -> Result<Ext, Error> {
use frame_support::{storage::generator::StorageMap, traits::PalletInfo};
use sp_core::hashing::twox_128;

let mut modules = vec![<T as frame_system::Config>::PalletInfo::name::<EPM::Pallet<T>>()
.expect("Pallet always has name; qed.")
.to_string()];
modules.extend(additional);
Builder::<B>::new()
.mode(Mode::Online(OnlineConfig {
transport: uri.into(),
at,
modules: if with_staking {
vec![
<T as frame_system::Config>::PalletInfo::name::<EPM::Pallet<T>>()
.expect("Pallet always has name; qed.")
.to_string(),
<T as frame_system::Config>::PalletInfo::name::<pallet_staking::Pallet<T>>()
.expect("Pallet always has name; qed.")
.to_string(),
]
} else {
vec![<T as frame_system::Config>::PalletInfo::name::<EPM::Pallet<T>>()
.expect("Pallet always has name; qed.")
.to_string()]
},
modules,
..Default::default()
}))
.inject_hashed_prefix(&<frame_system::BlockHash<T>>::prefix_hash())
Expand Down Expand Up @@ -386,6 +382,34 @@ fn mine_dpos<T: EPM::Config>(ext: &mut Ext) -> Result<(), Error> {
})
}

pub(crate) async fn check_versions<T: frame_system::Config>(
client: &WsClient,
print: bool,
) -> Result<(), Error> {
let linked_version = T::Version::get();
let on_chain_version = rpc_helpers::rpc::<sp_version::RuntimeVersion>(
client,
"state_getRuntimeVersion",
params! {},
)
.await
.expect("runtime version RPC should always work; qed");

if print {
log::info!(target: LOG_TARGET, "linked version {:?}", linked_version);
log::info!(target: LOG_TARGET, "on-chain version {:?}", on_chain_version);
}
if linked_version != on_chain_version {
log::error!(
target: LOG_TARGET,
"VERSION MISMATCH: any transaction will fail with bad-proof"
);
Err(Error::VersionMismatch)
} else {
Ok(())
}
}

#[tokio::main]
async fn main() {
env_logger::Builder::from_default_env()
Expand Down Expand Up @@ -422,6 +446,8 @@ async fn main() {
sp_core::crypto::set_default_ss58_version(
sp_core::crypto::Ss58AddressFormat::PolkadotAccount,
);
sub_tokens::dynamic::set_name("DOT");
sub_tokens::dynamic::set_decimal_points(10_000_000_000);
// safety: this program will always be single threaded, thus accessing global static is
// safe.
unsafe {
Expand All @@ -432,6 +458,8 @@ async fn main() {
sp_core::crypto::set_default_ss58_version(
sp_core::crypto::Ss58AddressFormat::KusamaAccount,
);
sub_tokens::dynamic::set_name("KSM");
sub_tokens::dynamic::set_decimal_points(1_000_000_000_000);
// safety: this program will always be single threaded, thus accessing global static is
// safe.
unsafe {
Expand All @@ -442,6 +470,8 @@ async fn main() {
sp_core::crypto::set_default_ss58_version(
sp_core::crypto::Ss58AddressFormat::PolkadotAccount,
);
sub_tokens::dynamic::set_name("WND");
sub_tokens::dynamic::set_decimal_points(1_000_000_000_000);
// safety: this program will always be single threaded, thus accessing global static is
// safe.
unsafe {
Expand All @@ -455,6 +485,10 @@ async fn main() {
}
log::info!(target: LOG_TARGET, "connected to chain {:?}", chain);

let _ = any_runtime! {
check_versions::<Runtime>(&client, true).await
};

let signer_account = any_runtime! {
signer::read_signer_uri::<_, Runtime>(&shared.account_seed, &client)
.await
Expand All @@ -464,7 +498,6 @@ async fn main() {
let outcome = any_runtime! {
match command.clone() {
Command::Monitor(c) => monitor_cmd(&client, shared, c, signer_account).await,
// --------------------^^ comes from the macro prelude, needs no generic.
Command::DryRun(c) => dry_run_cmd(&client, shared, c, signer_account).await,
Command::EmergencySolution => emergency_solution_cmd(shared.clone()).await,
}
Expand Down
5 changes: 4 additions & 1 deletion utils/staking-miner/src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
let hash = now.hash();
log::debug!(target: LOG_TARGET, "new event at #{:?} ({:?})", now.number, hash);

// if the runtime version has changed, terminate
crate::check_versions::<Runtime>(client, false).await?;
Copy link
Contributor

Choose a reason for hiding this comment

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

Not neccessary for this PR, but is there a way we could parrell-ize the calls to check_versions, ensure_signed_phase & create_election_ext? Basicaly a Promise.all and then we can analyze the results of each independtly and figure out how to proceed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah this is a good improvement. Also a critical improvement. is that currently we need to scrape the data of the whole pallet to check if we have a solution in this round or not (since we pass the entire ext), this can be done much better by just querying the appropriate storage item (see ensure_no_previous_solution).

we should be able to early-exit from this check, without the need to scrape the entire ext.

Copy link
Contributor Author

Choose a reason for hiding this comment

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


// we prefer doing this check before fetching anything into a remote-ext.
if ensure_signed_phase::<Runtime, Block>(client, hash).await.is_err() {
log::debug!(target: LOG_TARGET, "phase closed, not interested in this block at all.");
Expand All @@ -99,7 +102,7 @@ macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! {
// this will not be a solution.

// grab an externalities without staking, just the election snapshot.
let mut ext = crate::create_election_ext::<Runtime, Block>(shared.uri.clone(), Some(hash), false).await?;
let mut ext = crate::create_election_ext::<Runtime, Block>(shared.uri.clone(), Some(hash), vec![]).await?;

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.");
Expand Down
3 changes: 3 additions & 0 deletions utils/staking-miner/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ pub type Ext = sp_io::TestExternalities;

/// The key pair type being used. We "strongly" assume sr25519 for simplicity.
pub type Pair = sp_core::sr25519::Pair;

/// A dynamic token type used to represent account balances.
pub type Token = sub_tokens::dynamic::DynamicToken;
Loading