referrerpolicy=no-referrer-when-downgrade

substrate_relay_helper/cli/
chain_schema.rs

1// Copyright 2019-2022 Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Primitives related to chain CLI options.
18
19use clap::Parser;
20use relay_substrate_client::{AccountKeyPairOf, ChainWithTransactions};
21use strum::{EnumString, VariantNames};
22
23use relay_substrate_client::{ChainRuntimeVersion, ChainWithRuntimeVersion, SimpleRuntimeVersion};
24
25use crate::TransactionParams;
26
27#[doc = "Runtime version params."]
28#[derive(Debug, PartialEq, Eq, Clone, Copy, Parser, EnumString, VariantNames)]
29pub enum RuntimeVersionType {
30	/// Auto query version from chain
31	Auto,
32	/// Custom `spec_version` and `transaction_version`
33	Custom,
34	/// Read version from bundle dependencies directly.
35	Bundle,
36}
37
38/// Create chain-specific set of runtime version parameters.
39#[macro_export]
40macro_rules! declare_chain_runtime_version_params_cli_schema {
41	($chain:ident, $chain_prefix:ident) => {
42		bp_runtime::paste::item! {
43			#[doc = $chain " runtime version params."]
44			#[derive(Debug, PartialEq, Eq, Clone, Copy, Parser)]
45			pub struct [<$chain RuntimeVersionParams>] {
46				#[doc = "The type of runtime version for chain " $chain]
47				#[arg(long, default_value = "Bundle")]
48				pub [<$chain_prefix _version_mode>]: RuntimeVersionType,
49				#[doc = "The custom sepc_version for chain " $chain]
50				#[arg(long)]
51				pub [<$chain_prefix _spec_version>]: Option<u32>,
52				#[doc = "The custom transaction_version for chain " $chain]
53				#[arg(long)]
54				pub [<$chain_prefix _transaction_version>]: Option<u32>,
55			}
56
57			impl [<$chain RuntimeVersionParams>] {
58				/// Converts self into `ChainRuntimeVersion`.
59				pub fn into_runtime_version(
60					self,
61					bundle_runtime_version: Option<SimpleRuntimeVersion>,
62				) -> anyhow::Result<ChainRuntimeVersion> {
63					Ok(match self.[<$chain_prefix _version_mode>] {
64						RuntimeVersionType::Auto => ChainRuntimeVersion::Auto,
65						RuntimeVersionType::Custom => {
66							let custom_spec_version = self.[<$chain_prefix _spec_version>]
67								.ok_or_else(|| anyhow::Error::msg(format!("The {}-spec-version is required when choose custom mode", stringify!($chain_prefix))))?;
68							let custom_transaction_version = self.[<$chain_prefix _transaction_version>]
69								.ok_or_else(|| anyhow::Error::msg(format!("The {}-transaction-version is required when choose custom mode", stringify!($chain_prefix))))?;
70							ChainRuntimeVersion::Custom(
71								SimpleRuntimeVersion {
72									spec_version: custom_spec_version,
73									transaction_version: custom_transaction_version
74								}
75							)
76						},
77						RuntimeVersionType::Bundle => match bundle_runtime_version {
78							Some(runtime_version) => ChainRuntimeVersion::Custom(runtime_version),
79							None => {
80								return Err(anyhow::format_err!("Cannot use bundled runtime version of {}: it is not known to the relay", stringify!($chain_prefix)));
81							}
82						},
83					})
84				}
85			}
86		}
87	};
88}
89
90/// Create chain-specific set of runtime version parameters.
91#[macro_export]
92macro_rules! declare_chain_connection_params_cli_schema {
93	($chain:ident, $chain_prefix:ident) => {
94		bp_runtime::paste::item! {
95			// TODO: https://github.com/paritytech/parity-bridges-common/issues/2909
96			// remove all obsolete arguments (separate URI components)
97
98			#[doc = $chain " connection params."]
99			#[derive(Debug, PartialEq, Eq, Clone, Parser)]
100			pub struct [<$chain ConnectionParams>] {
101				#[doc = "WS endpoint of " $chain ": full URI."]
102				#[arg(long)]
103				pub [<$chain_prefix _uri>]: String,
104				#[doc = "Custom runtime version"]
105				#[command(flatten)]
106				pub [<$chain_prefix _runtime_version>]: [<$chain RuntimeVersionParams>],
107			}
108
109			impl [<$chain ConnectionParams>] {
110				/// Convert connection params into Substrate client.
111				#[allow(dead_code)]
112				pub async fn into_client<Chain: ChainWithRuntimeVersion>(
113					self,
114				) -> anyhow::Result<$crate::cli::DefaultClient<Chain>> {
115					let chain_runtime_version = self
116						.[<$chain_prefix _runtime_version>]
117						.into_runtime_version(Chain::RUNTIME_VERSION)?;
118					Ok(relay_substrate_client::new(relay_substrate_client::ConnectionParams {
119						uri: self.[<$chain_prefix _uri>],
120						chain_runtime_version,
121					})
122					.await
123					)
124				}
125			}
126		}
127	};
128}
129
130/// Create chain-specific set of signing parameters.
131#[macro_export]
132macro_rules! declare_chain_signing_params_cli_schema {
133	($chain:ident, $chain_prefix:ident) => {
134		bp_runtime::paste::item! {
135			#[doc = $chain " signing params."]
136			#[derive(Debug, PartialEq, Eq, Clone, Parser)]
137			pub struct [<$chain SigningParams>] {
138				#[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."]
139				#[arg(long)]
140				pub [<$chain_prefix _signer>]: Option<String>,
141				#[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."]
142				#[arg(long)]
143				pub [<$chain_prefix _signer_password>]: Option<String>,
144
145				#[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."]
146				#[arg(long)]
147				pub [<$chain_prefix _signer_file>]: Option<std::path::PathBuf>,
148				#[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."]
149				#[arg(long)]
150				pub [<$chain_prefix _signer_password_file>]: Option<std::path::PathBuf>,
151
152				#[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."]
153				#[arg(long)]
154				pub [<$chain_prefix _transactions_mortality>]: Option<u32>,
155			}
156
157			impl [<$chain SigningParams>] {
158				/// Return transactions mortality.
159				#[allow(dead_code)]
160				pub fn transactions_mortality(&self) -> anyhow::Result<Option<u32>> {
161					self.[<$chain_prefix _transactions_mortality>]
162						.map(|transactions_mortality| {
163							if !(4..=65536).contains(&transactions_mortality)
164								|| !transactions_mortality.is_power_of_two()
165							{
166								Err(anyhow::format_err!(
167									"Transactions mortality {} is not a power of two in a [4; 65536] range",
168									transactions_mortality,
169								))
170							} else {
171								Ok(transactions_mortality)
172							}
173						})
174						.transpose()
175				}
176
177				/// Parse signing params into chain-specific KeyPair.
178				#[allow(dead_code)]
179				pub fn to_keypair<Chain: ChainWithTransactions>(&self) -> anyhow::Result<AccountKeyPairOf<Chain>> {
180					let suri = match (self.[<$chain_prefix _signer>].as_ref(), self.[<$chain_prefix _signer_file>].as_ref()) {
181						(Some(suri), _) => suri.to_owned(),
182						(None, Some(suri_file)) => std::fs::read_to_string(suri_file)
183							.map_err(|err| anyhow::format_err!(
184								"Failed to read SURI from file {:?}: {}",
185								suri_file,
186								err,
187							))?,
188						(None, None) => return Err(anyhow::format_err!(
189							"One of options must be specified: '{}' or '{}'",
190							stringify!([<$chain_prefix _signer>]),
191							stringify!([<$chain_prefix _signer_file>]),
192						)),
193					};
194
195					let suri_password = match (
196						self.[<$chain_prefix _signer_password>].as_ref(),
197						self.[<$chain_prefix _signer_password_file>].as_ref(),
198					) {
199						(Some(suri_password), _) => Some(suri_password.to_owned()),
200						(None, Some(suri_password_file)) => std::fs::read_to_string(suri_password_file)
201							.map(Some)
202							.map_err(|err| anyhow::format_err!(
203								"Failed to read SURI password from file {:?}: {}",
204								suri_password_file,
205								err,
206							))?,
207						_ => None,
208					};
209
210					use sp_core::crypto::Pair;
211
212					AccountKeyPairOf::<Chain>::from_string(
213						&suri,
214						suri_password.as_deref()
215					).map_err(|e| anyhow::format_err!("{:?}", e))
216				}
217
218				/// Return transaction parameters.
219				#[allow(dead_code)]
220				pub fn transaction_params<Chain: ChainWithTransactions>(
221					&self,
222				) -> anyhow::Result<TransactionParams<AccountKeyPairOf<Chain>>> {
223					Ok(TransactionParams {
224						mortality: self.transactions_mortality()?,
225						signer: self.to_keypair::<Chain>()?,
226					})
227				}
228			}
229		}
230	};
231}
232
233/// Create chain-specific set of configuration objects: connection parameters,
234/// signing parameters and bridge initialization parameters.
235#[macro_export]
236macro_rules! declare_chain_cli_schema {
237	($chain:ident, $chain_prefix:ident) => {
238		$crate::declare_chain_runtime_version_params_cli_schema!($chain, $chain_prefix);
239		$crate::declare_chain_connection_params_cli_schema!($chain, $chain_prefix);
240		$crate::declare_chain_signing_params_cli_schema!($chain, $chain_prefix);
241	};
242}
243
244declare_chain_cli_schema!(Source, source);
245declare_chain_cli_schema!(Target, target);