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