partner_chains_node_commands/
lib.rs

1//! This crate provides an enum type [PartnerChainsSubcommand] collecting all Partner Chains specific subcommands,
2//! and a [run] function for running these commands.
3//! [PartnerChainsSubcommand] is meant to be used by a command line argument parser library.
4#![deny(missing_docs)]
5use authority_selection_inherents::{
6	AuthoritySelectionDataSource, AuthoritySelectionInputs, CandidateValidationApi,
7};
8use clap::Parser;
9use cli_commands::address_association_signatures::AddressAssociationSignaturesCmd;
10use cli_commands::block_producer_metadata_signatures::BlockProducerMetadataSignatureCmd;
11use cli_commands::registration_signatures::RegistrationSignaturesCmd;
12use frame_support::sp_runtime::traits::NumberFor;
13use parity_scale_codec::{Decode, Encode};
14use partner_chains_cli::DefaultCmdRunContext;
15pub use partner_chains_cli::PartnerChainRuntime;
16use partner_chains_smart_contracts_commands::SmartContractsCmd;
17use sc_cli::{CliConfiguration, SharedParams, SubstrateCli};
18use sc_service::TaskManager;
19use sidechain_domain::*;
20use sp_api::ProvideRuntimeApi;
21use sp_blockchain::HeaderBackend;
22use sp_runtime::AccountId32;
23use sp_runtime::DeserializeOwned;
24use sp_runtime::Serialize;
25use sp_runtime::traits::Block as BlockT;
26use sp_session_validator_management::CommitteeMember as CommitteeMemberT;
27use sp_session_validator_management::SessionValidatorManagementApi;
28use sp_session_validator_management_query::SessionValidatorManagementQuery;
29use sp_session_validator_management_query::commands::*;
30#[allow(deprecated)]
31use sp_sidechain::{GetGenesisUtxo, GetSidechainStatus};
32use std::future::Future;
33use std::sync::Arc;
34
35#[derive(Debug, Clone, Parser)]
36/// Command line arguments for the `ariadne-parameters` command.
37pub struct AriadneParametersCmd {
38	#[arg(long)]
39	/// Main chain epoch number for which the parameters should be queried.
40	pub mc_epoch_number: McEpochNumber,
41	#[allow(missing_docs)]
42	#[clap(flatten)]
43	pub shared_params: SharedParams,
44}
45
46impl CliConfiguration for AriadneParametersCmd {
47	fn shared_params(&self) -> &SharedParams {
48		&self.shared_params
49	}
50}
51
52#[derive(Debug, Clone, Parser)]
53/// Command line arguments for the `sidechain-params` command.
54pub struct SidechainParamsCmd {
55	#[allow(missing_docs)]
56	#[clap(flatten)]
57	pub shared_params: SharedParams,
58}
59
60impl CliConfiguration for SidechainParamsCmd {
61	fn shared_params(&self) -> &SharedParams {
62		&self.shared_params
63	}
64}
65
66#[derive(Debug, Clone, Parser)]
67/// Command line arguments for the `registration-status` command.
68pub struct RegistrationStatusCmd {
69	#[arg(long)]
70	#[arg(long, alias = "mainchain-pub-key")]
71	/// Stake pool public key for which the registration status should be returned.
72	pub stake_pool_pub_key: StakePoolPublicKey,
73	#[arg(long)]
74	/// Mainchain epoch number for which the registration status should be returned.
75	pub mc_epoch_number: McEpochNumber,
76	#[allow(missing_docs)]
77	#[clap(flatten)]
78	pub shared_params: SharedParams,
79}
80
81impl CliConfiguration for RegistrationStatusCmd {
82	fn shared_params(&self) -> &SharedParams {
83		&self.shared_params
84	}
85}
86
87static REGISTRATION_STATUS_AFTER_HELP: once_cell::sync::Lazy<String> = once_cell::sync::Lazy::new(
88	|| {
89		fn get_node_exe_name() -> Option<String> {
90			let exe_path = std::env::current_exe().ok()?;
91			let exe_name_osstr = exe_path.file_name()?.to_os_string();
92			Some(exe_name_osstr.to_str()?.to_string())
93		}
94		format!(
95			"Example: {} registration-status --stake-pool-pub-key 0x702b81ab2e86cf73a87062af1eb0da666d451976d9d91c63a119ed94e6a33dc0 --mc-epoch-number 586",
96			get_node_exe_name().unwrap_or("node-bin".to_string())
97		)
98	},
99);
100
101#[derive(Clone, Debug, clap::Subcommand)]
102#[allow(clippy::large_enum_variant)]
103/// Entry point for all Partner Chains specific subcommand.
104pub enum PartnerChainsSubcommand<
105	RuntimeBindings: PartnerChainRuntime + Send + Sync,
106	PartnerchainAddress: Clone + Sync + Send + FromStrStdErr + 'static,
107> {
108	/// Returns sidechain parameters.
109	/// Requires --chain parameter that results in loading a properly configured chain spec.
110	SidechainParams(SidechainParamsCmd),
111
112	/// Returns registration status for a given stake pool public key and epoch number.
113	/// If registration has been included in Cardano block in epoch N, then it should be returned by this command if epoch greater than N+1 is provided.
114	/// If this command won't show your registration after a few minutes after it has been included in a cardano block, you can start debugging for unsuccessful registration.
115	/// Requires --chain parameter that results in loading a properly configured chain spec.
116	#[clap(
117		after_help = &*REGISTRATION_STATUS_AFTER_HELP
118	)]
119	RegistrationStatus(RegistrationStatusCmd),
120
121	/// Returns ariadne parameters effective at given mainchain epoch number.
122	/// Parameters are effective two epochs after the block their change is included in
123	/// Cardano.
124	/// Requires --chain parameter that results in loading a properly configured chain spec.
125	AriadneParameters(AriadneParametersCmd),
126
127	/// Generates registration signatures for partner chains committee candidates
128	RegistrationSignatures(RegistrationSignaturesCmd),
129
130	/// Signs address association
131	SignAddressAssociation(AddressAssociationSignaturesCmd<PartnerchainAddress>),
132
133	/// Signs block producer metadata for submitting to the runtime
134	#[command(subcommand)]
135	SignBlockProducerMetadata(BlockProducerMetadataSignatureCmd<AccountId32>),
136
137	/// Commands for interacting with Partner Chain smart contracts on Cardano
138	#[command(subcommand)]
139	SmartContracts(SmartContractsCmd),
140
141	/// Partner Chains text "wizards" for setting up chain
142	#[command(subcommand)]
143	Wizards(partner_chains_cli::Command<RuntimeBindings>),
144}
145
146#[allow(deprecated)]
147/// Runs a Partner Chains subcommand.
148pub fn run<
149	Cli,
150	Block,
151	CommitteeMember,
152	Client,
153	BlockProducerMetadata,
154	RuntimeBindings: PartnerChainRuntime + Send + Sync,
155	PartnerchainAddress,
156>(
157	cli: &Cli,
158	get_deps: impl FnOnce(
159		sc_service::Configuration,
160	) -> Result<
161		(Arc<Client>, TaskManager, Arc<dyn AuthoritySelectionDataSource + Send + Sync>),
162		sc_service::error::Error,
163	>,
164	cmd: PartnerChainsSubcommand<RuntimeBindings, PartnerchainAddress>,
165) -> sc_cli::Result<()>
166where
167	Cli: SubstrateCli,
168	Client: ProvideRuntimeApi<Block> + HeaderBackend<Block> + 'static,
169	Client::Api: GetGenesisUtxo<Block>
170		+ GetSidechainStatus<Block>
171		+ SessionValidatorManagementApi<
172			Block,
173			CommitteeMember,
174			AuthoritySelectionInputs,
175			ScEpochNumber,
176		> + CandidateValidationApi<Block>,
177	Block: BlockT,
178	NumberFor<Block>: From<u32> + Into<u32>,
179	CommitteeMember: CommitteeMemberT + Encode + Decode + Send + Sync + 'static,
180	CommitteeMember::AuthorityId: Decode + Encode + AsRef<[u8]> + Send + Sync + 'static,
181	CommitteeMember::AuthorityKeys: Decode + Encode,
182	BlockProducerMetadata: DeserializeOwned + Encode + Send + Sync,
183	PartnerchainAddress: Serialize + Clone + Sync + Send + FromStrStdErr + Encode + 'static,
184{
185	match cmd {
186		PartnerChainsSubcommand::SidechainParams(cmd) => {
187			let runner = cli.create_runner(&cmd)?;
188			runner.async_run(|config| {
189				let (client, task_manager, _) = get_deps(config)?;
190				Ok((print_result(cli_commands::get_genesis_utxo::execute(client)), task_manager))
191			})
192		},
193		PartnerChainsSubcommand::RegistrationStatus(cmd) => {
194			let runner = cli.create_runner(&cmd)?;
195			runner.async_run(move |config| {
196				let (client, task_manager, ds) = get_deps(config)?;
197				let query = SessionValidatorManagementQuery::new(client.clone(), ds.clone());
198				Ok((
199					print_result(cli_get_registration_status(
200						query,
201						cmd.mc_epoch_number,
202						cmd.stake_pool_pub_key.clone(),
203					)),
204					task_manager,
205				))
206			})
207		},
208		PartnerChainsSubcommand::AriadneParameters(cmd) => {
209			let runner = cli.create_runner(&cmd)?;
210			runner.async_run(move |config| {
211				let (client, task_manager, ds) = get_deps(config)?;
212				let query = SessionValidatorManagementQuery::new(client.clone(), ds.clone());
213				Ok((
214					print_result(cli_get_ariadne_parameters(query, cmd.mc_epoch_number)),
215					task_manager,
216				))
217			})
218		},
219		PartnerChainsSubcommand::RegistrationSignatures(cmd) => Ok(println!("{}", cmd.execute())),
220		PartnerChainsSubcommand::SignAddressAssociation(cmd) => {
221			cmd.execute().map_err(|e| sc_service::Error::Application(e.into()))?;
222			Ok(())
223		},
224		PartnerChainsSubcommand::SignBlockProducerMetadata(cmd) => {
225			cmd.execute::<BlockProducerMetadata>()
226				.map_err(|e| sc_service::Error::Application(e.into()))?;
227			Ok(())
228		},
229		PartnerChainsSubcommand::SmartContracts(cmd) => {
230			setup_log4rs()?;
231			Ok(cmd.execute_blocking()?)
232		},
233		PartnerChainsSubcommand::Wizards(cmd) => {
234			setup_log4rs()?;
235			Ok(cmd
236				.run(&DefaultCmdRunContext)
237				.map_err(|e| sc_cli::Error::Application(e.into()))?)
238		},
239	}
240}
241
242/// This sets logging to stderr, leaving stdout for smart-contracts JSON outputs.
243/// Ogmios interactions are logged to a file.
244fn setup_log4rs() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
245	let stderr = log4rs::append::console::ConsoleAppender::builder()
246		.target(log4rs::append::console::Target::Stderr)
247		.build();
248	let ogmios_log = log4rs::append::file::FileAppender::builder().build("ogmios_client.log")?;
249
250	let log_config = log4rs::config::Config::builder()
251		.appender(log4rs::config::Appender::builder().build("stderr", Box::new(stderr)))
252		.appender(log4rs::config::Appender::builder().build("ogmios-log", Box::new(ogmios_log)))
253		.logger(
254			log4rs::config::Logger::builder()
255				.appender("ogmios-log")
256				.additive(false)
257				.build("ogmios_client::jsonrpsee", log::LevelFilter::Debug),
258		)
259		.build(log4rs::config::Root::builder().appender("stderr").build(log::LevelFilter::Info))?;
260
261	log4rs::init_config(log_config)?;
262
263	Ok(())
264}
265
266async fn print_result<FIn>(command_future: FIn) -> Result<(), sc_cli::Error>
267where
268	FIn: Future<Output = Result<String, String>>,
269{
270	let result = command_future.await.unwrap_or_else(|e| e);
271	println!("{}", result);
272	Ok(())
273}
274
275#[cfg(test)]
276mod tests {
277
278	async fn some_err() -> Result<String, String> {
279		Err("some err".to_string())
280	}
281
282	#[tokio::test]
283	async fn print_async_doesnt_fail_if_result_is_error() {
284		let result = super::print_result(some_err()).await;
285		assert!(result.is_ok());
286	}
287}