partner_chains_node_commands/
lib.rs

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