Improve remote-externalities (#8397)

* make builder generic to allow using different hash types

* expose "cache", "block_number" and "modules" as cli options for live state

* Change Builder to be generic over Block instead of Hash
add rpc method to get hash from block number
allow passing of block numbers and hashes

* fix live tests

* fix formatting in utils/frame/remote-externalities/src/lib.rs

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

* change cli to only accept block hashes
break up lines that were too long
use starts_with instead of match s.get
use unwrap_or_default instead of unwrap_or(Vec::new())

* improve error message

* fix indentation

* replace Block with sp_runtime::testing::Block

* Move cache test out of remote-test feature tests
Add cache file (contains only "Proxy" module) for local test

* simplify match expression to and_then

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

* Combine the two cfg attributes into one

Co-authored-by: David <dvdplm@gmail.com>

* Restrict visibility of test_prelude use statements to crate level

* Fix usage of and_then

* Rename cache to snapshot

* Remove fully qualified path for Debug

* Refine naming. snapshot to state_snapshot

* Remove unnecessary comment

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

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: David <dvdplm@gmail.com>
This commit is contained in:
Steve Biedermann
2021-03-23 14:23:07 +01:00
committed by GitHub
parent b5b0ef592e
commit f9b6c869a3
5 changed files with 208 additions and 111 deletions
+106 -28
View File
@@ -18,7 +18,7 @@
//! `Structopt`-ready struct for `try-runtime`.
use parity_scale_codec::Decode;
use std::{fmt::Debug, str::FromStr};
use std::{fmt::Debug, path::PathBuf, str::FromStr};
use sc_service::Configuration;
use sc_cli::{CliConfiguration, ExecutionStrategy, WasmExecutionMethod};
use sc_executor::NativeExecutor;
@@ -37,10 +37,6 @@ pub struct TryRuntimeCmd {
#[structopt(flatten)]
pub shared_params: sc_cli::SharedParams,
/// The state to use to run the migration. Should be a valid FILE or HTTP URI.
#[structopt(short, long, default_value = "http://localhost:9933")]
pub state: State,
/// The execution strategy that should be used for benchmarks
#[structopt(
long = "execution",
@@ -60,32 +56,90 @@ pub struct TryRuntimeCmd {
default_value = "Interpreted"
)]
pub wasm_method: WasmExecutionMethod,
/// The state to use to run the migration.
#[structopt(subcommand)]
pub state: State,
}
/// The state to use for a migration dry-run.
#[derive(Debug)]
#[derive(Debug, structopt::StructOpt)]
pub enum State {
/// A snapshot. Inner value is a file path.
Snap(String),
/// Use a state snapshot as state to run the migration.
Snap {
#[structopt(flatten)]
snapshot_path: SnapshotPath,
},
/// A live chain. Inner value is the HTTP uri.
Live(String),
/// Use a live chain to run the migration.
Live {
/// An optional state snapshot file to WRITE to. Not written if set to `None`.
#[structopt(short, long)]
snapshot_path: Option<SnapshotPath>,
/// The block hash at which to connect.
/// Will be latest finalized head if not provided.
#[structopt(short, long, multiple = false, parse(try_from_str = parse_hash))]
block_at: Option<String>,
/// The modules to scrape. If empty, entire chain state will be scraped.
#[structopt(short, long, require_delimiter = true)]
modules: Option<Vec<String>>,
/// The url to connect to.
#[structopt(default_value = "http://localhost:9933", parse(try_from_str = parse_url))]
url: String,
},
}
impl FromStr for State {
fn parse_hash(block_number: &str) -> Result<String, String> {
let block_number = if block_number.starts_with("0x") {
&block_number[2..]
} else {
block_number
};
if let Some(pos) = block_number.chars().position(|c| !c.is_ascii_hexdigit()) {
Err(format!(
"Expected block hash, found illegal hex character at position: {}",
2 + pos,
))
} else {
Ok(block_number.into())
}
}
fn parse_url(s: &str) -> Result<String, &'static str> {
if s.starts_with("http://") {
// could use Url crate as well, but lets keep it simple for now.
Ok(s.to_string())
} else {
Err("not a valid HTTP url: must start with 'http://'")
}
}
#[derive(Debug, structopt::StructOpt)]
pub struct SnapshotPath {
/// The directory of the state snapshot.
#[structopt(short, long, default_value = ".")]
directory: String,
/// The file name of the state snapshot.
#[structopt(default_value = "SNAPSHOT")]
file_name: String,
}
impl FromStr for SnapshotPath {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.get(..7) {
// could use Url crate as well, but lets keep it simple for now.
Some("http://") => Ok(State::Live(s.to_string())),
Some("file://") => s
.split("//")
.collect::<Vec<_>>()
.get(1)
.map(|s| State::Snap(s.to_string()))
.ok_or("invalid file URI"),
_ => Err("invalid format. Must be a valid HTTP or File URI"),
}
let p: PathBuf = s.parse().map_err(|_| "invalid path")?;
let parent = p.parent();
let file_name = p.file_name();
file_name.and_then(|file_name| Some(Self {
directory: parent.map(|p| p.to_string_lossy().into()).unwrap_or(".".to_string()),
file_name: file_name.to_string_lossy().into()
})).ok_or("invalid path")
}
}
@@ -93,6 +147,10 @@ impl TryRuntimeCmd {
pub async fn run<B, ExecDispatch>(&self, config: Configuration) -> sc_cli::Result<()>
where
B: BlockT,
B::Hash: FromStr,
<B::Hash as FromStr>::Err: Debug,
NumberFor<B>: FromStr,
<NumberFor<B> as FromStr>::Err: Debug,
ExecDispatch: NativeExecutionDispatch + 'static,
{
let spec = config.chain_spec;
@@ -121,13 +179,33 @@ impl TryRuntimeCmd {
);
let ext = {
use remote_externalities::{Builder, Mode, CacheConfig, OfflineConfig, OnlineConfig};
use remote_externalities::{Builder, Mode, SnapshotConfig, OfflineConfig, OnlineConfig};
let builder = match &self.state {
State::Snap(file_path) => Builder::new().mode(Mode::Offline(OfflineConfig {
cache: CacheConfig { name: file_path.into(), ..Default::default() },
})),
State::Live(http_uri) => Builder::new().mode(Mode::Online(OnlineConfig {
uri: http_uri.into(),
State::Snap { snapshot_path } => {
let SnapshotPath { directory, file_name } = snapshot_path;
Builder::<B>::new().mode(Mode::Offline(OfflineConfig {
state_snapshot: SnapshotConfig {
name: file_name.into(),
directory: directory.into(),
},
}))
},
State::Live {
url,
snapshot_path,
block_at,
modules
} => Builder::<B>::new().mode(Mode::Online(OnlineConfig {
uri: url.into(),
state_snapshot: snapshot_path.as_ref().map(|c| SnapshotConfig {
name: c.file_name.clone(),
directory: c.directory.clone(),
}),
modules: modules.clone().unwrap_or_default(),
at: match block_at {
Some(b) => Some(b.parse().map_err(|e| format!("Could not parse hash: {:?}", e))?),
None => None,
},
..Default::default()
})),
};