use crate::{
build_executor, full_extensions, rpc_err_handler, state_machine_call_with_proof, LiveState,
SharedParams, State, LOG_TARGET,
};
use parity_scale_codec::Encode;
use sc_executor::sp_wasm_interface::HostFunctions;
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
use sp_runtime::{
generic::SignedBlock,
traits::{Block as BlockT, Header as HeaderT, NumberFor},
};
use std::{fmt::Debug, str::FromStr};
use substrate_rpc_client::{ws_client, ChainApi};
#[derive(Debug, Clone, clap::Parser)]
pub struct ExecuteBlockCmd {
#[arg(long, default_value = "all")]
pub try_state: frame_try_runtime::TryStateSelect,
#[arg(
long,
value_parser = crate::parse::url
)]
pub block_ws_uri: Option<String>,
#[command(subcommand)]
pub state: State,
}
impl ExecuteBlockCmd {
fn block_ws_uri<Block: BlockT>(&self) -> String
where
<Block::Hash as FromStr>::Err: Debug,
{
match (&self.block_ws_uri, &self.state) {
(Some(block_ws_uri), State::Snap { .. }) => block_ws_uri.to_owned(),
(Some(block_ws_uri), State::Live { .. }) => {
log::error!(target: LOG_TARGET, "--block-uri is provided while state type is live, Are you sure you know what you are doing?");
block_ws_uri.to_owned()
},
(None, State::Live(LiveState { uri, .. })) => uri.clone(),
(None, State::Snap { .. }) => {
panic!("either `--block-uri` must be provided, or state must be `live`");
},
}
}
}
pub(crate) async fn execute_block<Block, HostFns>(
shared: SharedParams,
command: ExecuteBlockCmd,
) -> sc_cli::Result<()>
where
Block: BlockT + serde::de::DeserializeOwned,
<Block::Hash as FromStr>::Err: Debug,
Block::Hash: serde::de::DeserializeOwned,
Block::Header: serde::de::DeserializeOwned,
<NumberFor<Block> as TryInto<u64>>::Error: Debug,
HostFns: HostFunctions,
{
let executor = build_executor::<HostFns>(&shared);
let ext = command.state.into_ext::<Block, HostFns>(&shared, &executor, None, true).await?;
let block_ws_uri = command.block_ws_uri::<Block>();
let rpc = ws_client(&block_ws_uri).await?;
let next_hash = next_hash_of::<Block>(&rpc, ext.block_hash).await?;
log::info!(target: LOG_TARGET, "fetching next block: {:?} ", next_hash);
let block = ChainApi::<(), Block::Hash, Block::Header, SignedBlock<Block>>::block(
&rpc,
Some(next_hash),
)
.await
.map_err(rpc_err_handler)?
.expect("header exists, block should also exist; qed")
.block;
let (mut header, extrinsics) = block.deconstruct();
header.digest_mut().pop();
let block = Block::new(header, extrinsics);
let state_root_check = false;
let signature_check = false;
let payload = (block.clone(), state_root_check, signature_check, command.try_state).encode();
let _ = state_machine_call_with_proof::<Block, HostFns>(
&ext,
&executor,
"TryRuntime_execute_block",
&payload,
full_extensions(executor.clone()),
shared.export_proof,
)?;
Ok(())
}
pub(crate) async fn next_hash_of<Block: BlockT>(
rpc: &substrate_rpc_client::WsClient,
hash: Block::Hash,
) -> sc_cli::Result<Block::Hash>
where
Block: BlockT + serde::de::DeserializeOwned,
Block::Header: serde::de::DeserializeOwned,
{
let number = ChainApi::<(), Block::Hash, Block::Header, ()>::header(rpc, Some(hash))
.await
.map_err(rpc_err_handler)
.and_then(|maybe_header| maybe_header.ok_or("header_not_found").map(|h| *h.number()))?;
let next = number + sp_runtime::traits::One::one();
let next_hash = match ChainApi::<(), Block::Hash, Block::Header, ()>::block_hash(
rpc,
Some(ListOrValue::Value(NumberOrHex::Number(
next.try_into().map_err(|_| "failed to convert number to block number")?,
))),
)
.await
.map_err(rpc_err_handler)?
{
ListOrValue::Value(t) => t.expect("value passed in; value comes out; qed"),
_ => unreachable!(),
};
Ok(next_hash)
}