CLI refactoring

* [CLI] Make bridge definitions more complete

* [CLI] Refactor relay_headers_and_messages

Signed-off-by: Serban Iorga <serban@parity.io>
This commit is contained in:
Serban Iorga
2022-07-13 14:07:36 +03:00
committed by Bastian Köcher
parent ceefd1b05d
commit e1fd877b80
18 changed files with 1635 additions and 1365 deletions
+1 -365
View File
@@ -19,10 +19,8 @@
use std::convert::TryInto;
use codec::{Decode, Encode};
use relay_substrate_client::ChainRuntimeVersion;
use structopt::{clap::arg_enum, StructOpt};
use strum::{EnumString, EnumVariantNames};
use substrate_relay_helper::TransactionParams;
use bp_messages::LaneId;
@@ -31,6 +29,7 @@ pub(crate) mod encode_message;
pub(crate) mod estimate_fee;
pub(crate) mod send_message;
mod chain_schema;
mod init_bridge;
mod register_parachain;
mod relay_headers;
@@ -296,283 +295,9 @@ pub enum RuntimeVersionType {
Bundle,
}
/// Helper trait to override transaction parameters differently.
pub trait TransactionParamsProvider {
/// Returns `true` if transaction parameters are defined by this provider.
fn is_defined(&self) -> bool;
/// Returns transaction parameters.
fn transaction_params<Chain: CliChain>(
&self,
) -> anyhow::Result<TransactionParams<Chain::KeyPair>>;
/// Returns transaction parameters, defined by `self` provider or, if they're not defined,
/// defined by `other` provider.
fn transaction_params_or<Chain: CliChain, T: TransactionParamsProvider>(
&self,
other: &T,
) -> anyhow::Result<TransactionParams<Chain::KeyPair>> {
if self.is_defined() {
self.transaction_params::<Chain>()
} else {
other.transaction_params::<Chain>()
}
}
}
/// Create chain-specific set of configuration objects: connection parameters,
/// signing parameters and bridge initialization parameters.
#[macro_export]
macro_rules! declare_chain_options {
($chain:ident, $chain_prefix:ident) => {
paste::item! {
#[doc = $chain " connection params."]
#[derive(StructOpt, Debug, PartialEq, Eq, Clone)]
pub struct [<$chain ConnectionParams>] {
#[doc = "Connect to " $chain " node at given host."]
#[structopt(long, default_value = "127.0.0.1")]
pub [<$chain_prefix _host>]: String,
#[doc = "Connect to " $chain " node websocket server at given port."]
#[structopt(long, default_value = "9944")]
pub [<$chain_prefix _port>]: u16,
#[doc = "Use secure websocket connection."]
#[structopt(long)]
pub [<$chain_prefix _secure>]: bool,
#[doc = "Custom runtime version"]
#[structopt(flatten)]
pub [<$chain_prefix _runtime_version>]: [<$chain RuntimeVersionParams>],
}
#[doc = $chain " runtime version params."]
#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy)]
pub struct [<$chain RuntimeVersionParams>] {
#[doc = "The type of runtime version for chain " $chain]
#[structopt(long, default_value = "Bundle")]
pub [<$chain_prefix _version_mode>]: RuntimeVersionType,
#[doc = "The custom sepc_version for chain " $chain]
#[structopt(long)]
pub [<$chain_prefix _spec_version>]: Option<u32>,
#[doc = "The custom transaction_version for chain " $chain]
#[structopt(long)]
pub [<$chain_prefix _transaction_version>]: Option<u32>,
}
#[doc = $chain " signing params."]
#[derive(StructOpt, Debug, PartialEq, Eq, Clone)]
pub struct [<$chain SigningParams>] {
#[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."]
#[structopt(long)]
pub [<$chain_prefix _signer>]: Option<String>,
#[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."]
#[structopt(long)]
pub [<$chain_prefix _signer_password>]: Option<String>,
#[doc = "Path to the file, that contains SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer option."]
#[structopt(long)]
pub [<$chain_prefix _signer_file>]: Option<std::path::PathBuf>,
#[doc = "Path to the file, that password for the SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer_password option."]
#[structopt(long)]
pub [<$chain_prefix _signer_password_file>]: Option<std::path::PathBuf>,
#[doc = "Transactions mortality period, in blocks. MUST be a power of two in [4; 65536] range. MAY NOT be larger than `BlockHashCount` parameter of the chain system module."]
#[structopt(long)]
pub [<$chain_prefix _transactions_mortality>]: Option<u32>,
}
#[doc = "Parameters required to sign transaction on behalf of owner of the messages pallet at " $chain "."]
#[derive(StructOpt, Debug, PartialEq, Eq)]
pub struct [<$chain MessagesPalletOwnerSigningParams>] {
#[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."]
#[structopt(long)]
pub [<$chain_prefix _messages_pallet_owner>]: Option<String>,
#[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."]
#[structopt(long)]
pub [<$chain_prefix _messages_pallet_owner_password>]: Option<String>,
}
impl [<$chain SigningParams>] {
/// Return transactions mortality.
#[allow(dead_code)]
pub fn transactions_mortality(&self) -> anyhow::Result<Option<u32>> {
self.[<$chain_prefix _transactions_mortality>]
.map(|transactions_mortality| {
if !(4..=65536).contains(&transactions_mortality)
|| !transactions_mortality.is_power_of_two()
{
Err(anyhow::format_err!(
"Transactions mortality {} is not a power of two in a [4; 65536] range",
transactions_mortality,
))
} else {
Ok(transactions_mortality)
}
})
.transpose()
}
/// Parse signing params into chain-specific KeyPair.
#[allow(dead_code)]
pub fn to_keypair<Chain: CliChain>(&self) -> anyhow::Result<Chain::KeyPair> {
let suri = match (self.[<$chain_prefix _signer>].as_ref(), self.[<$chain_prefix _signer_file>].as_ref()) {
(Some(suri), _) => suri.to_owned(),
(None, Some(suri_file)) => std::fs::read_to_string(suri_file)
.map_err(|err| anyhow::format_err!(
"Failed to read SURI from file {:?}: {}",
suri_file,
err,
))?,
(None, None) => return Err(anyhow::format_err!(
"One of options must be specified: '{}' or '{}'",
stringify!([<$chain_prefix _signer>]),
stringify!([<$chain_prefix _signer_file>]),
)),
};
let suri_password = match (
self.[<$chain_prefix _signer_password>].as_ref(),
self.[<$chain_prefix _signer_password_file>].as_ref(),
) {
(Some(suri_password), _) => Some(suri_password.to_owned()),
(None, Some(suri_password_file)) => std::fs::read_to_string(suri_password_file)
.map(Some)
.map_err(|err| anyhow::format_err!(
"Failed to read SURI password from file {:?}: {}",
suri_password_file,
err,
))?,
_ => None,
};
use sp_core::crypto::Pair;
Chain::KeyPair::from_string(
&suri,
suri_password.as_deref()
).map_err(|e| anyhow::format_err!("{:?}", e))
}
}
#[allow(dead_code)]
impl TransactionParamsProvider for [<$chain SigningParams>] {
fn is_defined(&self) -> bool {
self.[<$chain_prefix _signer>].is_some() || self.[<$chain_prefix _signer_file>].is_some()
}
fn transaction_params<Chain: CliChain>(&self) -> anyhow::Result<TransactionParams<Chain::KeyPair>> {
Ok(TransactionParams {
mortality: self.transactions_mortality()?,
signer: self.to_keypair::<Chain>()?,
})
}
}
#[allow(dead_code)]
impl [<$chain MessagesPalletOwnerSigningParams>] {
/// Parse signing params into chain-specific KeyPair.
pub fn to_keypair<Chain: CliChain>(&self) -> anyhow::Result<Option<Chain::KeyPair>> {
use sp_core::crypto::Pair;
let [<$chain_prefix _messages_pallet_owner>] = match self.[<$chain_prefix _messages_pallet_owner>] {
Some(ref messages_pallet_owner) => messages_pallet_owner,
None => return Ok(None),
};
Chain::KeyPair::from_string(
[<$chain_prefix _messages_pallet_owner>],
self.[<$chain_prefix _messages_pallet_owner_password>].as_deref()
).map_err(|e| anyhow::format_err!("{:?}", e)).map(Some)
}
}
impl [<$chain ConnectionParams>] {
/// Returns `true` if version guard can be started.
///
/// There's no reason to run version guard when version mode is set to `Auto`. It can
/// lead to relay shutdown when chain is upgraded, even though we have explicitly
/// said that we don't want to shutdown.
#[allow(dead_code)]
pub fn can_start_version_guard(&self) -> bool {
self.[<$chain_prefix _runtime_version>].[<$chain_prefix _version_mode>] != RuntimeVersionType::Auto
}
/// Convert connection params into Substrate client.
pub async fn to_client<Chain: CliChain>(
&self,
) -> anyhow::Result<relay_substrate_client::Client<Chain>> {
let chain_runtime_version = self
.[<$chain_prefix _runtime_version>]
.into_runtime_version(Some(Chain::RUNTIME_VERSION))?;
Ok(relay_substrate_client::Client::new(relay_substrate_client::ConnectionParams {
host: self.[<$chain_prefix _host>].clone(),
port: self.[<$chain_prefix _port>],
secure: self.[<$chain_prefix _secure>],
chain_runtime_version,
})
.await
)
}
/// Return selected `chain_spec` version.
///
/// This function only connects to the node if version mode is set to `Auto`.
#[allow(dead_code)]
pub async fn selected_chain_spec_version<Chain: CliChain>(
&self,
) -> anyhow::Result<u32> {
let chain_runtime_version = self
.[<$chain_prefix _runtime_version>]
.into_runtime_version(Some(Chain::RUNTIME_VERSION))?;
Ok(match chain_runtime_version {
ChainRuntimeVersion::Auto => self
.to_client::<Chain>()
.await?
.simple_runtime_version()
.await?
.0,
ChainRuntimeVersion::Custom(spec_version, _) => spec_version,
})
}
}
impl [<$chain RuntimeVersionParams>] {
/// Converts self into `ChainRuntimeVersion`.
pub fn into_runtime_version(
self,
bundle_runtime_version: Option<sp_version::RuntimeVersion>,
) -> anyhow::Result<ChainRuntimeVersion> {
Ok(match self.[<$chain_prefix _version_mode>] {
RuntimeVersionType::Auto => ChainRuntimeVersion::Auto,
RuntimeVersionType::Custom => {
let except_spec_version = self.[<$chain_prefix _spec_version>]
.ok_or_else(|| anyhow::Error::msg(format!("The {}-spec-version is required when choose custom mode", stringify!($chain_prefix))))?;
let except_transaction_version = self.[<$chain_prefix _transaction_version>]
.ok_or_else(|| anyhow::Error::msg(format!("The {}-transaction-version is required when choose custom mode", stringify!($chain_prefix))))?;
ChainRuntimeVersion::Custom(
except_spec_version,
except_transaction_version
)
},
RuntimeVersionType::Bundle => match bundle_runtime_version {
Some(runtime_version) => ChainRuntimeVersion::Custom(
runtime_version.spec_version,
runtime_version.transaction_version
),
None => ChainRuntimeVersion::Auto
},
})
}
}
}
};
}
declare_chain_options!(Source, source);
declare_chain_options!(Target, target);
declare_chain_options!(Relaychain, relaychain);
declare_chain_options!(Parachain, parachain);
#[cfg(test)]
mod tests {
use super::*;
use sp_core::Pair;
#[test]
fn hex_bytes_display_matches_from_str_for_clap() {
@@ -586,93 +311,4 @@ mod tests {
// then
assert_eq!(hex.0, hex2.0);
}
#[test]
fn reads_suri_from_file() {
const ALICE: &str = "//Alice";
const BOB: &str = "//Bob";
const ALICE_PASSWORD: &str = "alice_password";
const BOB_PASSWORD: &str = "bob_password";
let alice = sp_core::sr25519::Pair::from_string(ALICE, Some(ALICE_PASSWORD)).unwrap();
let bob = sp_core::sr25519::Pair::from_string(BOB, Some(BOB_PASSWORD)).unwrap();
let bob_with_alice_password =
sp_core::sr25519::Pair::from_string(BOB, Some(ALICE_PASSWORD)).unwrap();
let temp_dir = tempfile::tempdir().unwrap();
let mut suri_file_path = temp_dir.path().to_path_buf();
let mut password_file_path = temp_dir.path().to_path_buf();
suri_file_path.push("suri");
password_file_path.push("password");
std::fs::write(&suri_file_path, BOB.as_bytes()).unwrap();
std::fs::write(&password_file_path, BOB_PASSWORD.as_bytes()).unwrap();
// when both seed and password are read from file
assert_eq!(
TargetSigningParams {
target_signer: Some(ALICE.into()),
target_signer_password: Some(ALICE_PASSWORD.into()),
target_signer_file: None,
target_signer_password_file: None,
target_transactions_mortality: None,
}
.to_keypair::<relay_rialto_client::Rialto>()
.map(|p| p.public())
.map_err(drop),
Ok(alice.public()),
);
// when both seed and password are read from file
assert_eq!(
TargetSigningParams {
target_signer: None,
target_signer_password: None,
target_signer_file: Some(suri_file_path.clone()),
target_signer_password_file: Some(password_file_path.clone()),
target_transactions_mortality: None,
}
.to_keypair::<relay_rialto_client::Rialto>()
.map(|p| p.public())
.map_err(drop),
Ok(bob.public()),
);
// when password are is overriden by cli option
assert_eq!(
TargetSigningParams {
target_signer: None,
target_signer_password: Some(ALICE_PASSWORD.into()),
target_signer_file: Some(suri_file_path.clone()),
target_signer_password_file: Some(password_file_path.clone()),
target_transactions_mortality: None,
}
.to_keypair::<relay_rialto_client::Rialto>()
.map(|p| p.public())
.map_err(drop),
Ok(bob_with_alice_password.public()),
);
// when both seed and password are overriden by cli options
assert_eq!(
TargetSigningParams {
target_signer: Some(ALICE.into()),
target_signer_password: Some(ALICE_PASSWORD.into()),
target_signer_file: Some(suri_file_path),
target_signer_password_file: Some(password_file_path),
target_transactions_mortality: None,
}
.to_keypair::<relay_rialto_client::Rialto>()
.map(|p| p.public())
.map_err(drop),
Ok(alice.public()),
);
}
}