quantus_cli/cli/
high_security.rs

1use crate::{
2	chain::quantus_subxt,
3	cli::{address_format::QuantusSS58, progress_spinner::wait_for_tx_confirmation},
4	log_error, log_print, log_success, log_verbose,
5};
6use clap::Subcommand;
7use colored::Colorize;
8use sp_core::crypto::{AccountId32 as SpAccountId32, Ss58Codec};
9
10/// High-Security (Reversible) commands
11#[derive(Subcommand, Debug)]
12pub enum HighSecurityCommands {
13	/// Check High-Security status for an account
14	Status {
15		/// Account address to check (SS58 or wallet name)
16		#[arg(long)]
17		account: String,
18	},
19
20	/// Set High-Security (reversibility) for an account
21	Set {
22		/// Interceptor account (SS58 or wallet name)
23		#[arg(long)]
24		interceptor: String,
25
26		/// Delay in blocks (mutually exclusive with --delay-seconds)
27		#[arg(long, conflicts_with = "delay_seconds")]
28		delay_blocks: Option<u32>,
29
30		/// Delay in seconds (mutually exclusive with --delay-blocks)
31		#[arg(long, conflicts_with = "delay_blocks")]
32		delay_seconds: Option<u64>,
33
34		/// Wallet name to sign with
35		#[arg(short, long)]
36		from: String,
37
38		/// Password for the wallet
39		#[arg(short, long)]
40		password: Option<String>,
41
42		/// Read password from file (for scripting)
43		#[arg(long)]
44		password_file: Option<String>,
45	},
46}
47
48/// Handle high security commands
49pub async fn handle_high_security_command(
50	command: HighSecurityCommands,
51	node_url: &str,
52) -> crate::error::Result<()> {
53	let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
54
55	match command {
56		HighSecurityCommands::Status { account } => {
57			log_print!("🔍 Checking High Security Status");
58
59			// Resolve account address
60			let resolved_account = crate::cli::common::resolve_address(&account)?;
61			let account_id_sp = SpAccountId32::from_ss58check(&resolved_account).map_err(|e| {
62				crate::error::QuantusError::Generic(format!(
63					"Invalid account address '{resolved_account}': {e:?}"
64				))
65			})?;
66			let account_id_bytes: [u8; 32] = *account_id_sp.as_ref();
67			let account_id = subxt::ext::subxt_core::utils::AccountId32::from(account_id_bytes);
68
69			// Convert account to Quantus SS58 format
70			let account_ss58 = account_id.to_quantus_ss58();
71
72			// Query storage
73			let storage_addr = quantus_subxt::api::storage()
74				.reversible_transfers()
75				.high_security_accounts(account_id);
76			let latest = quantus_client.get_latest_block().await?;
77			let value = quantus_client
78				.client()
79				.storage()
80				.at(latest)
81				.fetch(&storage_addr)
82				.await
83				.map_err(|e| {
84					crate::error::QuantusError::NetworkError(format!("Fetch error: {e:?}"))
85				})?;
86
87			log_print!("📋 Account: {}", account_ss58.bright_cyan());
88
89			if let Some(high_security_data) = value {
90				log_success!("✅ High Security: ENABLED");
91
92				// Convert interceptor to Quantus SS58 format
93				let interceptor_ss58 = high_security_data.interceptor.to_quantus_ss58();
94
95				log_print!("🛡️  Guardian/Interceptor: {}", interceptor_ss58.bright_green());
96
97				// Format delay display
98				match high_security_data.delay {
99					quantus_subxt::api::runtime_types::qp_scheduler::BlockNumberOrTimestamp::BlockNumber(blocks) => {
100						log_print!("⏱️  Delay: {} blocks", blocks.to_string().bright_yellow());
101					},
102					quantus_subxt::api::runtime_types::qp_scheduler::BlockNumberOrTimestamp::Timestamp(ms) => {
103						let seconds = ms / 1000;
104						log_print!("⏱️  Delay: {} seconds", seconds.to_string().bright_yellow());
105					},
106				}
107			} else {
108				log_print!("❌ High Security: DISABLED");
109				log_print!("💡 This account does not have high-security reversibility enabled.");
110			}
111
112			Ok(())
113		},
114
115		HighSecurityCommands::Set {
116			interceptor,
117			delay_blocks,
118			delay_seconds,
119			from,
120			password,
121			password_file,
122		} => {
123			log_print!("🛡️  Set High Security");
124			log_verbose!("📦 Using wallet: {}", from.bright_blue().bold());
125			let keypair = crate::wallet::load_keypair_from_wallet(&from, password, password_file)?;
126
127			// Resolve interceptor: allow wallet name or SS58 address
128			let interceptor_resolved = crate::cli::common::resolve_address(&interceptor)?;
129			let interceptor_sp =
130				SpAccountId32::from_ss58check(&interceptor_resolved).map_err(|e| {
131					crate::error::QuantusError::Generic(format!(
132						"Invalid interceptor address '{interceptor_resolved}': {e:?}"
133					))
134				})?;
135			let interceptor_bytes: [u8; 32] = *interceptor_sp.as_ref();
136			let interceptor_subxt =
137				subxt::ext::subxt_core::utils::AccountId32::from(interceptor_bytes);
138
139			// Build delay enum for set_high_security
140			use quantus_subxt::api::reversible_transfers::calls::types::set_high_security::Delay as HsDelay;
141			let delay_value = match (delay_blocks, delay_seconds) {
142				(Some(blocks), None) => HsDelay::BlockNumber(blocks),
143				(None, Some(seconds)) => HsDelay::Timestamp(seconds * 1000), /* Convert seconds */
144				// to milliseconds
145				(None, None) => {
146					log_error!("❌ You must specify either --delay-blocks or --delay-seconds");
147					return Err(crate::error::QuantusError::Generic(
148						"Missing delay parameter".to_string(),
149					));
150				},
151				(Some(_), Some(_)) => {
152					unreachable!("clap conflicts_with ensures these are mutually exclusive")
153				},
154			};
155
156			log_verbose!("✍️  Creating set_high_security extrinsic...");
157
158			// Current generated metadata expects (delay, interceptor).
159			let tx_call = quantus_subxt::api::tx()
160				.reversible_transfers()
161				.set_high_security(delay_value, interceptor_subxt);
162
163			let tx_hash =
164				crate::cli::common::submit_transaction(&quantus_client, &keypair, tx_call, None)
165					.await?;
166
167			log_success!("✅ SUCCESS High security set! Hash: 0x{}", hex::encode(tx_hash.as_ref()));
168
169			let success = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
170			if success {
171				log_success!("🎉 FINISHED High security configuration confirmed on-chain");
172			} else {
173				log_error!("❌ Transaction failed or not included");
174			}
175
176			Ok(())
177		},
178	}
179}