kaspa_cli_lib/modules/
export.rs1use crate::imports::*;
2use kaspa_wallet_core::account::{multisig::MultiSig, Account, BIP32_ACCOUNT_KIND, MULTISIG_ACCOUNT_KIND};
3
4#[derive(Default, Handler)]
5#[help("Export transactions, a wallet or a private key")]
6pub struct Export;
7
8impl Export {
9 async fn main(self: Arc<Self>, ctx: &Arc<dyn Context>, argv: Vec<String>, _cmd: &str) -> Result<()> {
10 let ctx = ctx.clone().downcast_arc::<KaspaCli>()?;
11
12 if argv.is_empty() || argv.first() == Some(&"help".to_string()) {
13 tprintln!(ctx, "usage: export [mnemonic]");
14 return Ok(());
15 }
16
17 let what = argv.first().unwrap();
18 match what.as_str() {
19 "mnemonic" => {
20 let account = ctx.account().await?;
21 if account.account_kind() == MULTISIG_ACCOUNT_KIND {
22 let account = account.downcast_arc::<MultiSig>()?;
23 export_multisig_account(ctx, account).await
24 } else {
25 export_single_key_account(ctx, account).await
26 }
27 }
28 _ => Err(format!("Invalid argument: {}", what).into()),
29 }
30 }
31}
32
33async fn export_multisig_account(ctx: Arc<KaspaCli>, account: Arc<MultiSig>) -> Result<()> {
34 match &account.prv_key_data_ids() {
35 None => Err(Error::WatchOnlyAccountNoKeyData),
36 Some(v) if v.is_empty() => Err(Error::WatchOnlyAccountNoKeyData),
37 Some(prv_key_data_ids) => {
38 let wallet_secret = Secret::new(ctx.term().ask(true, "Enter wallet password: ").await?.trim().as_bytes().to_vec());
39 if wallet_secret.as_ref().is_empty() {
40 return Err(Error::WalletSecretRequired);
41 }
42
43 tprintln!(ctx, "required signatures: {}", account.minimum_signatures());
44 tprintln!(ctx, "");
45
46 let prv_key_data_store = ctx.store().as_prv_key_data_store()?;
47 let mut generated_xpub_keys = Vec::with_capacity(prv_key_data_ids.len());
48
49 for (id, prv_key_data_id) in prv_key_data_ids.iter().enumerate() {
50 let prv_key_data = prv_key_data_store.load_key_data(&wallet_secret, prv_key_data_id).await?.unwrap();
51 let mnemonic = prv_key_data.as_mnemonic(None).unwrap().unwrap();
52
53 let xpub_key: kaspa_bip32::ExtendedPublicKey<kaspa_bip32::secp256k1::PublicKey> =
54 prv_key_data.create_xpub(None, MULTISIG_ACCOUNT_KIND.into(), 0).await?; tprintln!(ctx, "");
57 tprintln!(ctx, "extended public key {}:", id + 1);
58 tprintln!(ctx, "");
59 tprintln!(ctx, "{}", ctx.wallet().network_format_xpub(&xpub_key));
60 tprintln!(ctx, "");
61
62 tprintln!(ctx, "mnemonic {}:", id + 1);
63 tprintln!(ctx, "");
64 tprintln!(ctx, "{}", mnemonic.phrase());
65 tprintln!(ctx, "");
66
67 generated_xpub_keys.push(xpub_key);
68 }
69 let test = account.xpub_keys();
70
71 if let Some(keys) = test {
72 let additional = keys.iter().filter(|item| !generated_xpub_keys.contains(item));
73 additional.enumerate().for_each(|(idx, xpub)| {
74 if idx == 0 {
75 tprintln!(ctx, "additional xpubs: ");
76 }
77 tprintln!(ctx, "{}", ctx.wallet().network_format_xpub(xpub));
78 });
79 }
80 Ok(())
81 }
82 }
83}
84
85async fn export_single_key_account(ctx: Arc<KaspaCli>, account: Arc<dyn Account>) -> Result<()> {
86 let prv_key_data_id = account.prv_key_data_id()?;
87
88 let wallet_secret = Secret::new(ctx.term().ask(true, "Enter wallet password: ").await?.trim().as_bytes().to_vec());
89 if wallet_secret.as_ref().is_empty() {
90 return Err(Error::WalletSecretRequired);
91 }
92
93 let prv_key_data = ctx.store().as_prv_key_data_store()?.load_key_data(&wallet_secret, prv_key_data_id).await?;
94 let Some(keydata) = prv_key_data else { return Err(Error::KeyDataNotFound) };
95 let payment_secret = if keydata.payload.is_encrypted() {
96 let payment_secret = Secret::new(ctx.term().ask(true, "Enter payment password: ").await?.trim().as_bytes().to_vec());
97 if payment_secret.as_ref().is_empty() {
98 return Err(Error::PaymentSecretRequired);
99 } else {
100 Some(payment_secret)
101 }
102 } else {
103 None
104 };
105
106 let prv_key_data = keydata.payload.decrypt(payment_secret.as_ref())?;
107 let mnemonic = prv_key_data.as_ref().as_mnemonic()?;
108
109 let xpub_key = keydata.create_xpub(None, BIP32_ACCOUNT_KIND.into(), 0).await?; tprintln!(ctx, "extended public key:");
112 tprintln!(ctx, "");
113 tprintln!(ctx, "{}", ctx.wallet().network_format_xpub(&xpub_key));
114 tprintln!(ctx, "");
115
116 match mnemonic {
117 None => {
118 tprintln!(ctx, "mnemonic is not available for this private key");
119 }
120 Some(mnemonic) if payment_secret.is_none() => {
121 tprintln!(ctx, "mnemonic:");
122 tprintln!(ctx, "");
123 tprintln!(ctx, "{}", mnemonic.phrase());
124 tprintln!(ctx, "");
125 }
126 Some(mnemonic) => {
127 tpara!(
128 ctx,
129 "\
130 IMPORTANT: to recover your private key using this mnemonic in the future \
131 you will need your payment password. Your payment password is permanently associated with \
132 this mnemonic.",
133 );
134 tprintln!(ctx, "");
135 tprintln!(ctx, "mnemonic:");
136 tprintln!(ctx, "");
137 tprintln!(ctx, "{}", mnemonic.phrase());
138 tprintln!(ctx, "");
139 }
140 };
141
142 Ok(())
143}