mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-29 05:37:24 +00:00
0b3444c51d
* new test for try-runtime tuple stuff * fix * remove development comment * formatting * remove todo comment * follow-chain working test * refactor common cli testing utils * fix comment * revert Cargo.lock changes * update Cargo.lock * improve doc comment * fix error typo * update Cargo.lock * feature gate try-runtime test * build_substrate cli test util * feature gate follow_chain tests * move fn start_node to test-utils * improve test pkg name * use tokio Child and Command * remove redundant import * fix ci * fix ci * don't leave hanging processes * improved child process cleanup * use existing KillChildOnDrop * remove redundant comment * Update test-utils/cli/src/lib.rs Co-authored-by: Koute <koute@users.noreply.github.com> --------- Co-authored-by: kianenigma <kian@parity.io> Co-authored-by: Koute <koute@users.noreply.github.com>
206 lines
6.5 KiB
Rust
206 lines
6.5 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use crate::{
|
|
build_executor, full_extensions, parse, rpc_err_handler, state_machine_call_with_proof,
|
|
LiveState, SharedParams, State, LOG_TARGET,
|
|
};
|
|
use parity_scale_codec::{Decode, Encode};
|
|
use sc_executor::sp_wasm_interface::HostFunctions;
|
|
use serde::{de::DeserializeOwned, Serialize};
|
|
use sp_core::H256;
|
|
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, FinalizedHeaders, Subscription, WsClient};
|
|
|
|
const SUB: &str = "chain_subscribeFinalizedHeads";
|
|
const UN_SUB: &str = "chain_unsubscribeFinalizedHeads";
|
|
|
|
/// Configurations of the [`crate::Command::FollowChain`].
|
|
#[derive(Debug, Clone, clap::Parser)]
|
|
pub struct FollowChainCmd {
|
|
/// The url to connect to.
|
|
#[arg(short, long, value_parser = parse::url)]
|
|
pub uri: String,
|
|
|
|
/// If set, then the state root check is enabled.
|
|
#[arg(long)]
|
|
pub state_root_check: bool,
|
|
|
|
/// Which try-state targets to execute when running this command.
|
|
///
|
|
/// Expected values:
|
|
/// - `all`
|
|
/// - `none`
|
|
/// - A comma separated list of pallets, as per pallet names in `construct_runtime!()` (e.g.
|
|
/// `Staking, System`).
|
|
/// - `rr-[x]` where `[x]` is a number. Then, the given number of pallets are checked in a
|
|
/// round-robin fashion.
|
|
#[arg(long, default_value = "all")]
|
|
pub try_state: frame_try_runtime::TryStateSelect,
|
|
|
|
/// If present, a single connection to a node will be kept and reused for fetching blocks.
|
|
#[arg(long)]
|
|
pub keep_connection: bool,
|
|
}
|
|
|
|
/// Start listening for with `SUB` at `url`.
|
|
///
|
|
/// Returns a pair `(client, subscription)` - `subscription` alone will be useless, because it
|
|
/// relies on the related alive `client`.
|
|
async fn start_subscribing<Header: DeserializeOwned + Serialize + Send + Sync + 'static>(
|
|
url: &str,
|
|
) -> sc_cli::Result<(WsClient, Subscription<Header>)> {
|
|
let client = ws_client(url).await.map_err(|e| sc_cli::Error::Application(e.into()))?;
|
|
|
|
log::info!(target: LOG_TARGET, "subscribing to {:?} / {:?}", SUB, UN_SUB);
|
|
|
|
let sub = ChainApi::<(), (), Header, ()>::subscribe_finalized_heads(&client)
|
|
.await
|
|
.map_err(|e| sc_cli::Error::Application(e.into()))?;
|
|
Ok((client, sub))
|
|
}
|
|
|
|
pub(crate) async fn follow_chain<Block, HostFns>(
|
|
shared: SharedParams,
|
|
command: FollowChainCmd,
|
|
) -> sc_cli::Result<()>
|
|
where
|
|
Block: BlockT<Hash = H256> + DeserializeOwned,
|
|
Block::Hash: FromStr,
|
|
Block::Header: DeserializeOwned,
|
|
<Block::Hash as FromStr>::Err: Debug,
|
|
NumberFor<Block>: FromStr,
|
|
<NumberFor<Block> as FromStr>::Err: Debug,
|
|
HostFns: HostFunctions,
|
|
{
|
|
let (rpc, subscription) = start_subscribing::<Block::Header>(&command.uri).await?;
|
|
let mut finalized_headers: FinalizedHeaders<Block, _, _> =
|
|
FinalizedHeaders::new(&rpc, subscription);
|
|
|
|
let mut maybe_state_ext = None;
|
|
let executor = build_executor::<HostFns>(&shared);
|
|
|
|
while let Some(header) = finalized_headers.next().await {
|
|
let hash = header.hash();
|
|
let number = header.number();
|
|
|
|
let block =
|
|
ChainApi::<(), Block::Hash, Block::Header, SignedBlock<Block>>::block(&rpc, Some(hash))
|
|
.await
|
|
.or_else(|e| {
|
|
if matches!(e, substrate_rpc_client::Error::ParseError(_)) {
|
|
log::error!(
|
|
target: LOG_TARGET,
|
|
"failed to parse the block format of remote against the local \
|
|
codebase. The block format has changed, and follow-chain cannot run in \
|
|
this case. Try running this command in a branch of your codebase that
|
|
has the same block format as the remote chain. For now, we replace the \
|
|
block with an empty one."
|
|
);
|
|
}
|
|
Err(rpc_err_handler(e))
|
|
})?
|
|
.expect("if header exists, block should also exist.")
|
|
.block;
|
|
|
|
log::debug!(
|
|
target: LOG_TARGET,
|
|
"new block event: {:?} => {:?}, extrinsics: {}",
|
|
hash,
|
|
number,
|
|
block.extrinsics().len()
|
|
);
|
|
|
|
// create an ext at the state of this block, whatever is the first subscription event.
|
|
if maybe_state_ext.is_none() {
|
|
let state = State::Live(LiveState {
|
|
uri: command.uri.clone(),
|
|
// a bit dodgy, we have to un-parse the has to a string again and re-parse it
|
|
// inside.
|
|
at: Some(hex::encode(header.parent_hash().encode())),
|
|
pallet: vec![],
|
|
child_tree: true,
|
|
});
|
|
let ext = state.into_ext::<Block, HostFns>(&shared, &executor, None, true).await?;
|
|
maybe_state_ext = Some(ext);
|
|
}
|
|
|
|
let state_ext =
|
|
maybe_state_ext.as_mut().expect("state_ext either existed or was just created");
|
|
|
|
let result = state_machine_call_with_proof::<Block, HostFns>(
|
|
state_ext,
|
|
&executor,
|
|
"TryRuntime_execute_block",
|
|
(block, command.state_root_check, true, command.try_state.clone())
|
|
.encode()
|
|
.as_ref(),
|
|
full_extensions(executor.clone()),
|
|
shared
|
|
.export_proof
|
|
.as_ref()
|
|
.map(|path| path.as_path().join(&format!("{}.json", number))),
|
|
);
|
|
|
|
if let Err(why) = result {
|
|
log::error!(
|
|
target: LOG_TARGET,
|
|
"failed to execute block {:?} due to {:?}",
|
|
number,
|
|
why
|
|
);
|
|
continue
|
|
}
|
|
|
|
let (mut changes, encoded_result) = result.expect("checked to be Ok; qed");
|
|
|
|
let consumed_weight = <sp_weights::Weight as Decode>::decode(&mut &*encoded_result)
|
|
.map_err(|e| format!("failed to decode weight: {:?}", e))?;
|
|
|
|
let storage_changes = changes
|
|
.drain_storage_changes(
|
|
&state_ext.backend,
|
|
&mut Default::default(),
|
|
// Note that in case a block contains a runtime upgrade, state version could
|
|
// potentially be incorrect here, this is very niche and would only result in
|
|
// unaligned roots, so this use case is ignored for now.
|
|
state_ext.state_version,
|
|
)
|
|
.unwrap();
|
|
|
|
state_ext.backend.apply_transaction(
|
|
storage_changes.transaction_storage_root,
|
|
storage_changes.transaction,
|
|
);
|
|
|
|
log::info!(
|
|
target: LOG_TARGET,
|
|
"executed block {}, consumed weight {}, new storage root {:?}",
|
|
number,
|
|
consumed_weight,
|
|
state_ext.as_backend().root(),
|
|
);
|
|
}
|
|
|
|
log::error!(target: LOG_TARGET, "ws subscription must have terminated.");
|
|
Ok(())
|
|
}
|