Adds export-state subcommand (#5842)

* Export state cli

* More work

* Fix tests

* Make it work

* Fix compilation

* Apply suggestions from code review
This commit is contained in:
Bastian Köcher
2020-04-30 15:44:40 +02:00
committed by GitHub
parent 929bd07bef
commit 5b8d3607e9
12 changed files with 329 additions and 86 deletions
@@ -14,15 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use crate::error;
use crate::params::ImportParams;
use crate::params::SharedParams;
use crate::CliConfiguration;
use crate::{
CliConfiguration, error, params::{ImportParams, SharedParams, BlockNumberOrHash},
};
use sc_service::{Configuration, ServiceBuilderCommand};
use sp_runtime::generic::BlockId;
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
use std::fmt::Debug;
use std::str::FromStr;
use sp_runtime::traits::{Block as BlockT, NumberFor};
use std::{fmt::Debug, str::FromStr};
use structopt::StructOpt;
/// The `check-block` command used to validate blocks.
@@ -30,7 +27,7 @@ use structopt::StructOpt;
pub struct CheckBlockCmd {
/// Block hash or number
#[structopt(value_name = "HASH or NUMBER")]
pub input: String,
pub input: BlockNumberOrHash,
/// The default number of 64KB pages to ever allocate for Wasm execution.
///
@@ -57,29 +54,13 @@ impl CheckBlockCmd {
where
B: FnOnce(Configuration) -> Result<BC, sc_service::error::Error>,
BC: ServiceBuilderCommand<Block = BB> + Unpin,
BB: sp_runtime::traits::Block + Debug,
<<<BB as BlockT>::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug,
<BB as BlockT>::Hash: std::str::FromStr,
BB: BlockT + Debug,
<NumberFor<BB> as FromStr>::Err: std::fmt::Debug,
BB::Hash: FromStr,
<BB::Hash as FromStr>::Err: std::fmt::Debug,
{
let input = if self.input.starts_with("0x") {
&self.input[2..]
} else {
&self.input[..]
};
let block_id = match FromStr::from_str(input) {
Ok(hash) => BlockId::hash(hash),
Err(_) => match self.input.parse::<u32>() {
Ok(n) => BlockId::number((n as u32).into()),
Err(_) => {
return Err(error::Error::Input(
"Invalid hash or number specified".into(),
))
}
},
};
let start = std::time::Instant::now();
builder(config)?.check_block(block_id).await?;
builder(config)?.check_block(self.input.parse()?).await?;
println!("Completed in {} ms.", start.elapsed().as_millis());
Ok(())
@@ -0,0 +1,81 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use crate::{
CliConfiguration, error, params::{PruningParams, SharedParams, BlockNumberOrHash},
};
use log::info;
use sc_service::{Configuration, ServiceBuilderCommand};
use sp_runtime::traits::{Block as BlockT, NumberFor};
use std::{fmt::Debug, str::FromStr};
use structopt::StructOpt;
/// The `export-state` command used to export the state of a given block into
/// a chain spec.
#[derive(Debug, StructOpt, Clone)]
pub struct ExportStateCmd {
/// Block hash or number.
#[structopt(value_name = "HASH or NUMBER")]
pub input: Option<BlockNumberOrHash>,
#[allow(missing_docs)]
#[structopt(flatten)]
pub shared_params: SharedParams,
#[allow(missing_docs)]
#[structopt(flatten)]
pub pruning_params: PruningParams,
}
impl ExportStateCmd {
/// Run the `export-state` command
pub fn run<B, BC, BB>(
&self,
config: Configuration,
builder: B,
) -> error::Result<()>
where
B: FnOnce(Configuration) -> Result<BC, sc_service::error::Error>,
BC: ServiceBuilderCommand<Block = BB> + Unpin,
BB: BlockT + Debug,
<NumberFor<BB> as FromStr>::Err: std::fmt::Debug,
BB::Hash: FromStr,
<BB::Hash as FromStr>::Err: std::fmt::Debug,
{
info!("Exporting raw state...");
let mut input_spec = config.chain_spec.cloned_box();
let block_id = self.input.clone().map(|b| b.parse()).transpose()?;
let raw_state = builder(config)?.export_raw_state(block_id)?;
input_spec.set_storage(raw_state);
info!("Generating new chain spec...");
let json = sc_service::chain_ops::build_spec(&*input_spec, true)?;
print!("{}", json);
Ok(())
}
}
impl CliConfiguration for ExportStateCmd {
fn shared_params(&self) -> &SharedParams {
&self.shared_params
}
fn pruning_params(&self) -> Option<&PruningParams> {
Some(&self.pruning_params)
}
}
+19 -10
View File
@@ -17,18 +17,20 @@
mod build_spec_cmd;
mod check_block_cmd;
mod export_blocks_cmd;
mod export_state_cmd;
mod import_blocks_cmd;
mod purge_chain_cmd;
mod revert_cmd;
mod runcmd;
mod run_cmd;
pub use crate::commands::build_spec_cmd::BuildSpecCmd;
pub use crate::commands::check_block_cmd::CheckBlockCmd;
pub use crate::commands::export_blocks_cmd::ExportBlocksCmd;
pub use crate::commands::import_blocks_cmd::ImportBlocksCmd;
pub use crate::commands::purge_chain_cmd::PurgeChainCmd;
pub use crate::commands::revert_cmd::RevertCmd;
pub use crate::commands::runcmd::RunCmd;
pub use self::build_spec_cmd::BuildSpecCmd;
pub use self::check_block_cmd::CheckBlockCmd;
pub use self::export_blocks_cmd::ExportBlocksCmd;
pub use self::import_blocks_cmd::ImportBlocksCmd;
pub use self::purge_chain_cmd::PurgeChainCmd;
pub use self::revert_cmd::RevertCmd;
pub use self::run_cmd::RunCmd;
pub use self::export_state_cmd::ExportStateCmd;
use std::fmt::Debug;
use structopt::StructOpt;
@@ -56,6 +58,9 @@ pub enum Subcommand {
/// Remove the whole chain data.
PurgeChain(PurgeChainCmd),
/// Export state as raw chain spec.
ExportState(ExportStateCmd),
}
// TODO: move to config.rs?
@@ -339,7 +344,10 @@ macro_rules! substrate_cli_subcommands {
}
}
fn offchain_worker(&self, role: &::sc_service::Role) -> $crate::Result<::sc_service::config::OffchainWorkerConfig> {
fn offchain_worker(
&self,
role: &::sc_service::Role,
) -> $crate::Result<::sc_service::config::OffchainWorkerConfig> {
match self {
$($enum::$variant(cmd) => cmd.offchain_worker(role)),*
}
@@ -398,5 +406,6 @@ macro_rules! substrate_cli_subcommands {
}
substrate_cli_subcommands!(
Subcommand => BuildSpec, ExportBlocks, ImportBlocks, CheckBlock, Revert, PurgeChain
Subcommand => BuildSpec, ExportBlocks, ImportBlocks, CheckBlock, Revert, PurgeChain, ExportState
);
+89 -8
View File
@@ -24,8 +24,8 @@ mod pruning_params;
mod shared_params;
mod transaction_pool_params;
use std::fmt::Debug;
use std::str::FromStr;
use std::{fmt::Debug, str::FromStr};
use sp_runtime::{generic::BlockId, traits::{Block as BlockT, NumberFor}};
pub use crate::params::database_params::*;
pub use crate::params::import_params::*;
@@ -45,10 +45,10 @@ impl FromStr for BlockNumber {
type Err = String;
fn from_str(block_number: &str) -> Result<Self, Self::Err> {
if block_number.chars().any(|d| !d.is_digit(10)) {
if let Some(pos) = block_number.chars().position(|d| !d.is_digit(10)) {
Err(format!(
"Invalid block number: {}, expected decimal formatted unsigned integer",
block_number,
"Expected block number, found illegal digit at position: {}",
pos,
))
} else {
Ok(Self(block_number.to_owned()))
@@ -66,8 +66,89 @@ impl BlockNumber {
N: FromStr,
N::Err: std::fmt::Debug,
{
self.0
.parse()
.map_err(|e| format!("BlockNumber: {} parsing failed because of {:?}", self.0, e))
FromStr::from_str(&self.0).map_err(|e| format!("Failed to parse block number: {:?}", e))
}
}
/// Wrapper type that is either a `Hash` or the number of a `Block`.
#[derive(Debug, Clone)]
pub struct BlockNumberOrHash(String);
impl FromStr for BlockNumberOrHash {
type Err = String;
fn from_str(block_number: &str) -> Result<Self, Self::Err> {
if block_number.starts_with("0x") {
if let Some(pos) = &block_number[2..].chars().position(|c| !c.is_ascii_hexdigit()) {
Err(format!(
"Expected block hash, found illegal hex character at position: {}",
2 + pos,
))
} else {
Ok(Self(block_number.into()))
}
} else {
BlockNumber::from_str(block_number).map(|v| Self(v.0))
}
}
}
impl BlockNumberOrHash {
/// Parse the inner value as `BlockId`.
pub fn parse<B: BlockT>(&self) -> Result<BlockId<B>, String>
where
B::Hash: FromStr,
<B::Hash as FromStr>::Err: std::fmt::Debug,
NumberFor<B>: FromStr,
<NumberFor<B> as FromStr>::Err: std::fmt::Debug,
{
if self.0.starts_with("0x") {
Ok(BlockId::Hash(
FromStr::from_str(&self.0[2..])
.map_err(|e| format!("Failed to parse block hash: {:?}", e))?
))
} else {
BlockNumber(self.0.clone()).parse().map(BlockId::Number)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
type Header = sp_runtime::generic::Header<u32, sp_runtime::traits::BlakeTwo256>;
type Block = sp_runtime::generic::Block<Header, sp_runtime::OpaqueExtrinsic>;
#[test]
fn parse_block_number() {
let block_number_or_hash = BlockNumberOrHash::from_str("1234").unwrap();
let parsed = block_number_or_hash.parse::<Block>().unwrap();
assert_eq!(BlockId::Number(1234), parsed);
}
#[test]
fn parse_block_hash() {
let hash = sp_core::H256::default();
let hash_str = format!("{:?}", hash);
let block_number_or_hash = BlockNumberOrHash::from_str(&hash_str).unwrap();
let parsed = block_number_or_hash.parse::<Block>().unwrap();
assert_eq!(BlockId::Hash(hash), parsed);
}
#[test]
fn parse_block_hash_fails() {
assert_eq!(
"Expected block hash, found illegal hex character at position: 2",
BlockNumberOrHash::from_str("0xHello").unwrap_err(),
);
}
#[test]
fn parse_block_number_fails() {
assert_eq!(
"Expected block number, found illegal digit at position: 3",
BlockNumberOrHash::from_str("345Hello").unwrap_err(),
);
}
}
+5 -5
View File
@@ -26,9 +26,7 @@ use log::info;
use sc_service::{AbstractService, Configuration, Role, ServiceBuilderCommand, TaskType};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
use sp_utils::metrics::{TOKIO_THREADS_ALIVE, TOKIO_THREADS_TOTAL};
use std::fmt::Debug;
use std::marker::PhantomData;
use std::sync::Arc;
use std::{str::FromStr, fmt::Debug, marker::PhantomData, sync::Arc};
#[cfg(target_family = "unix")]
async fn main<F, E>(func: F) -> std::result::Result<(), Box<dyn std::error::Error>>
@@ -183,8 +181,9 @@ impl<C: SubstrateCli> Runner<C> {
B: FnOnce(Configuration) -> sc_service::error::Result<BC>,
BC: ServiceBuilderCommand<Block = BB> + Unpin,
BB: sp_runtime::traits::Block + Debug,
<<<BB as BlockT>::Header as HeaderT>::Number as std::str::FromStr>::Err: Debug,
<BB as BlockT>::Hash: std::str::FromStr,
<<<BB as BlockT>::Header as HeaderT>::Number as FromStr>::Err: Debug,
<BB as BlockT>::Hash: FromStr,
<<BB as BlockT>::Hash as FromStr>::Err: Debug,
{
match subcommand {
Subcommand::BuildSpec(cmd) => cmd.run(self.config),
@@ -199,6 +198,7 @@ impl<C: SubstrateCli> Runner<C> {
}
Subcommand::Revert(cmd) => cmd.run(self.config, builder),
Subcommand::PurgeChain(cmd) => cmd.run(self.config),
Subcommand::ExportState(cmd) => cmd.run(self.config, builder),
}
}