Skip to content

Commit

Permalink
upstream sync from rusty-kaspa
Browse files Browse the repository at this point in the history
  • Loading branch information
smartgoo committed Oct 29, 2023
2 parents 47f6b72 + ae7dae3 commit b2b9f49
Show file tree
Hide file tree
Showing 61 changed files with 1,590 additions and 407 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
target
web-root
/.idea/*
**/.idea/
/rust-toolchain
/.vscode/
**/db-*
Expand Down
52 changes: 51 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ kaspa-wrpc-server = { version = "0.1.7", path = "rpc/wrpc/server" }
kaspa-wrpc-wasm = { version = "0.1.7", path = "rpc/wrpc/wasm" }
kaspad = { version = "0.1.7", path = "kaspad" }
kaspa-perf-monitor = { path = "metrics/perf_monitor" }

# external
thiserror = "1"
faster-hex = "0.6"
Expand Down
5 changes: 2 additions & 3 deletions cli/src/modules/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,9 @@ impl Account {
};

let prv_key_data_info = ctx.select_private_key().await?;
let prv_key_data_id = prv_key_data_info.id;

let account_name = account_name.as_deref();
wizards::account::create(&ctx, prv_key_data_id, account_kind, account_name).await?;
wizards::account::create(&ctx, prv_key_data_info, account_kind, account_name).await?;
}
"scan" => {
let extent = if argv.is_empty() {
Expand Down Expand Up @@ -100,7 +99,7 @@ impl Account {
async fn display_help(self: Arc<Self>, ctx: Arc<KaspaCli>, _argv: Vec<String>) -> Result<()> {
ctx.term().help(
&[
("create [<type>] [<name>]", "Create a new account (types: 'bip32' (default), 'legacy')"),
("create [<type>] [<name>]", "Create a new account (types: 'bip32' (default), 'legacy', 'multisig')"),
// ("import", "Import a private key using 24 or 12 word mnemonic"),
("name <name>", "Name or rename the selected account (use 'remove' to remove the name"),
("scan [<derivations>]", "Scan extended address derivation chain (legacy accounts)"),
Expand Down
149 changes: 100 additions & 49 deletions cli/src/modules/export.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::imports::*;
use kaspa_wallet_core::runtime::{Account, MultiSig};

#[derive(Default, Handler)]
#[help("Export transactions, a wallet or a private key")]
Expand All @@ -17,60 +18,110 @@ impl Export {
match what.as_str() {
"mnemonic" => {
let account = ctx.account().await?;
let prv_key_data_id = account.prv_key_data_id()?;
if matches!(account.account_kind(), AccountKind::MultiSig) {
let account = account.downcast_arc::<MultiSig>()?;
export_multisig_account(ctx, account).await
} else {
export_single_key_account(ctx, account).await
}
}
_ => Err(format!("Invalid argument: {}", what).into()),
}
}
}

async fn export_multisig_account(ctx: Arc<KaspaCli>, account: Arc<MultiSig>) -> Result<()> {
match &account.prv_key_data_ids {
None => Err(Error::KeyDataNotFound),
Some(v) if v.is_empty() => Err(Error::KeyDataNotFound),
Some(prv_key_data_ids) => {
let wallet_secret = Secret::new(ctx.term().ask(true, "Enter wallet password: ").await?.trim().as_bytes().to_vec());
if wallet_secret.as_ref().is_empty() {
return Err(Error::WalletSecretRequired);
}

tprintln!(ctx, "required signatures: {}", account.minimum_signatures);
tprintln!(ctx, "");

let access_ctx: Arc<dyn AccessContextT> = Arc::new(AccessContext::new(wallet_secret));
let prv_key_data_store = ctx.store().as_prv_key_data_store()?;
let mut generated_xpub_keys = Vec::with_capacity(prv_key_data_ids.len());
for (id, prv_key_data_id) in prv_key_data_ids.iter().enumerate() {
let prv_key_data = prv_key_data_store.load_key_data(&access_ctx, prv_key_data_id).await?.unwrap();
let mnemonic = prv_key_data.as_mnemonic(None).unwrap().unwrap();

tprintln!(ctx, "mnemonic {}:", id + 1);
tprintln!(ctx, "");
tprintln!(ctx, "{}", mnemonic.phrase());
tprintln!(ctx, "");

let wallet_secret = Secret::new(ctx.term().ask(true, "Enter wallet password: ").await?.trim().as_bytes().to_vec());
if wallet_secret.as_ref().is_empty() {
return Err(Error::WalletSecretRequired);
let xpub_key = prv_key_data.create_xpub(None, AccountKind::MultiSig, 0).await?; // todo it can be done concurrently
let xpub_prefix = kaspa_bip32::Prefix::XPUB;
generated_xpub_keys.push(xpub_key.to_string(Some(xpub_prefix)));
}

let additional = account.xpub_keys.iter().filter(|xpub| !generated_xpub_keys.contains(xpub));
additional.enumerate().for_each(|(idx, xpub)| {
if idx == 0 {
tprintln!(ctx, "additional xpubs: ");
}
tprintln!(ctx, "{xpub}");
});
Ok(())
}
}
}

async fn export_single_key_account(ctx: Arc<KaspaCli>, account: Arc<dyn Account>) -> Result<()> {
let prv_key_data_id = account.prv_key_data_id()?;

let wallet_secret = Secret::new(ctx.term().ask(true, "Enter wallet password: ").await?.trim().as_bytes().to_vec());
if wallet_secret.as_ref().is_empty() {
return Err(Error::WalletSecretRequired);
}

let access_ctx: Arc<dyn AccessContextT> = Arc::new(AccessContext::new(wallet_secret));
let prv_key_data = ctx.store().as_prv_key_data_store()?.load_key_data(&access_ctx, prv_key_data_id).await?;
if let Some(keydata) = prv_key_data {
let payment_secret = if keydata.payload.is_encrypted() {
let payment_secret =
Secret::new(ctx.term().ask(true, "Enter payment password: ").await?.trim().as_bytes().to_vec());
if payment_secret.as_ref().is_empty() {
return Err(Error::PaymentSecretRequired);
} else {
Some(payment_secret)
}
} else {
None
};

let prv_key_data = keydata.payload.decrypt(payment_secret.as_ref())?;
let mnemonic = prv_key_data.as_ref().as_mnemonic()?;
if let Some(mnemonic) = mnemonic {
if payment_secret.is_none() {
tprintln!(ctx, "mnemonic:");
tprintln!(ctx, "");
tprintln!(ctx, "{}", mnemonic.phrase());
tprintln!(ctx, "");
} else {
tpara!(
ctx,
"\
let access_ctx: Arc<dyn AccessContextT> = Arc::new(AccessContext::new(wallet_secret));
let prv_key_data = ctx.store().as_prv_key_data_store()?.load_key_data(&access_ctx, prv_key_data_id).await?;
let Some(keydata) = prv_key_data else { return Err(Error::KeyDataNotFound) };
let payment_secret = if keydata.payload.is_encrypted() {
let payment_secret = Secret::new(ctx.term().ask(true, "Enter payment password: ").await?.trim().as_bytes().to_vec());
if payment_secret.as_ref().is_empty() {
return Err(Error::PaymentSecretRequired);
} else {
Some(payment_secret)
}
} else {
None
};

let prv_key_data = keydata.payload.decrypt(payment_secret.as_ref())?;
let mnemonic = prv_key_data.as_ref().as_mnemonic()?;

match mnemonic {
None => {
tprintln!(ctx, "mnemonic is not available for this private key");
}
Some(mnemonic) if payment_secret.is_none() => {
tprintln!(ctx, "mnemonic:");
tprintln!(ctx, "");
tprintln!(ctx, "{}", mnemonic.phrase());
tprintln!(ctx, "");
}
Some(mnemonic) => {
tpara!(
ctx,
"\
IMPORTANT: to recover your private key using this mnemonic in the future \
you will need your payment password. Your payment password is permanently associated with \
this mnemonic.",
);
tprintln!(ctx, "");
tprintln!(ctx, "mnemonic:");
tprintln!(ctx, "");
tprintln!(ctx, "{}", mnemonic.phrase());
tprintln!(ctx, "");
}
} else {
tprintln!(ctx, "mnemonic is not available for this private key");
}

Ok(())
} else {
Err(Error::KeyDataNotFound)
}
}
_ => Err(format!("Invalid argument: {}", what).into()),
);
tprintln!(ctx, "");
tprintln!(ctx, "mnemonic:");
tprintln!(ctx, "");
tprintln!(ctx, "{}", mnemonic.phrase());
tprintln!(ctx, "");
}
}
};

Ok(())
}
14 changes: 12 additions & 2 deletions cli/src/modules/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ impl Import {
let what = argv.get(0).unwrap();
match what.as_str() {
"mnemonic" => {
crate::wizards::import::import_with_mnemonic(&ctx).await?;
let account_kind =
if let Some(account_kind) = argv.get(1) { account_kind.parse::<AccountKind>()? } else { AccountKind::Bip32 };
if argv.len() > 1 {
crate::wizards::import::import_with_mnemonic(&ctx, account_kind, &argv[2..]).await?;
} else {
crate::wizards::import::import_with_mnemonic(&ctx, account_kind, &[]).await?;
}
}
"legacy" => {
if exists_legacy_v0_keydata().await? {
Expand All @@ -32,6 +38,7 @@ impl Import {
return Err("KDX/kaspanet keydata file not found".into());
}
}
// todo "read-only" => {}
// "core" => {}
v => {
tprintln!(ctx, "unknown command: '{v}'\r\n");
Expand All @@ -45,7 +52,10 @@ impl Import {
async fn display_help(self: Arc<Self>, ctx: Arc<KaspaCli>) -> Result<()> {
ctx.term().help(
&[
("mnemonic", "Import a 24 or 12 word mnemonic"),
(
"mnemonic [<type>] [<additional xpub keys>] ",
"Import a 24 or 12 word mnemonic (types: 'bip32' (default), 'legacy', 'multisig'), ",
),
("legacy", "Import a legacy (local KDX) wallet"),
// ("purge", "Purge an account from the wallet"),
],
Expand Down
Loading

0 comments on commit b2b9f49

Please sign in to comment.