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

Commit

Permalink
check runtime version in staking miner (#3628)
Browse files Browse the repository at this point in the history
* check runtime version in staking miner

* fmt

* add short alias for things

* fix fee

* print length as well

* fix build

* review comments
  • Loading branch information
kianenigma authored Aug 16, 2021
1 parent a79d094 commit 39f7be4
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 40 deletions.
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://github.com/paritytech/substrate", branch = "master" }

sp-core = { 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-runtime = { git = "/~https://github.com/paritytech/substrate", branch = "master" }
sp-npos-elections = { git = "/~https://github.com/paritytech/substrate", branch = "master" }
Expand All @@ -32,6 +33,7 @@ frame-support = { git = "/~https://github.com/paritytech/substrate", branch = "mas
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-staking = { git = "/~https://github.com/paritytech/substrate", branch = "master" }
pallet-balances = { git = "/~https://github.com/paritytech/substrate", branch = "master" }
pallet-transaction-payment = { git = "/~https://github.com/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://github.com/paritytech/substrate-debug-kit", branch = "master" }

[dev-dependencies]
sp-version = { git = "/~https://github.com/paritytech/substrate", branch = "master" }
76 changes: 64 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,53 @@ 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::SolutionOf<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 {:?} / length {:?}",
&raw_solution.score.iter().map(|x| Token::from(*x)).collect::<Vec<_>>(),
Token::from(deposit),
raw_solution.encode().len(),
);
});

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 +112,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;

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
79 changes: 56 additions & 23 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 @@ -93,8 +94,9 @@ macro_rules! construct_runtime_prelude {
let address = <Runtime as frame_system::Config>::Lookup::unlookup(account.clone());
let extrinsic = UncheckedExtrinsic::new_signed(call, address, signature.into(), extra);
log::debug!(
target: crate::LOG_TARGET, "constructed extrinsic {}",
sp_core::hexdisplay::HexDisplay::from(&extrinsic.encode())
target: crate::LOG_TARGET, "constructed extrinsic {} with length {}",
sp_core::hexdisplay::HexDisplay::from(&extrinsic.encode()),
extrinsic.encode().len(),
);
extrinsic
}
Expand Down Expand Up @@ -172,14 +174,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 +206,7 @@ enum Error {
AccountDoesNotExists,
IncorrectPhase,
AlreadySubmitted,
VersionMismatch,
}

impl From<sp_core::crypto::SecretStringError> for Error {
Expand Down Expand Up @@ -270,14 +276,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 @@ -291,34 +297,25 @@ struct Opt {
command: Command,
}

/// Build the `Ext` at `hash` with all the data of `ElectionProviderMultiPhase` and `Staking`
/// stored.
/// 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>(
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 +383,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 +447,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 +459,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 +471,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 +486,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 +499,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 All @@ -477,7 +511,6 @@ mod tests {
use super::*;

fn get_version<T: frame_system::Config>() -> sp_version::RuntimeVersion {
use frame_support::traits::Get;
T::Version::get()
}

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?;

// 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

0 comments on commit 39f7be4

Please sign in to comment.