Skip to content

Commit

Permalink
feat: ledger key manager interface (#5644)
Browse files Browse the repository at this point in the history
Description
---
This PR expands the console wallet and key manager interfaces to support
hardware wallets. It *does not* introduce functionality to communicate
with the hardware wallet, simply the interface to support it. Another PR
will be opened swapping out the placeholder software key manager for the
hardware key manager where appropriate.

Motivation and Context
---
So people can use their most excellent hardware wallets with the
MinoTari console wallet.

How Has This Been Tested?
---
Manually on osx

What process can a PR reviewer use to test or verify this change?
---

<!-- Checklist -->
<!-- 1. Is the title of your PR in the form that would make nice release
notes? The title, excluding the conventional commit
tag, will be included exactly as is in the CHANGELOG, so please think
about it carefully. -->


Breaking Changes
---

- [x] None
- [ ] Requires data directory on base node to be deleted
- [ ] Requires hard fork
- [ ] Other - Please specify

<!-- Does this include a breaking change? If so, include this line as a
footer -->
<!-- BREAKING CHANGE: Description what the user should do, e.g. delete a
database, resync the chain -->

---------

Co-authored-by: C.Lee Taylor <[email protected]>
  • Loading branch information
brianp and leet4tari authored Mar 26, 2024
1 parent 2867454 commit 0d66126
Show file tree
Hide file tree
Showing 35 changed files with 1,530 additions and 37 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@ pie/
integration_tests/cucumber-output-junit.xml

integration_tests/log/

# Ignore MinoTari Ledger Wallet
app_nanosplus.json
app_nanos.json
68 changes: 68 additions & 0 deletions Cargo.lock

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

32 changes: 17 additions & 15 deletions applications/minotari_console_wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ tari_script = { path = "../../infrastructure/tari_script" }
tari_shutdown = { path = "../../infrastructure/shutdown" }
tari_utilities = { version = "0.7" }
minotari_wallet = { path = "../../base_layer/wallet", features = [
"bundled_sqlite",
"bundled_sqlite",
] }
tari_hashing = { path = "../../hashing" }

Expand All @@ -31,26 +31,28 @@ console-subscriber = "0.1.8"
# Uncomment for normal use (non tokio-console tracing)
tokio = { version = "1.36", features = ["signal"] }

blake2 = "0.10"
chrono = { version = "0.4.19", default-features = false }
clap = { version = "3.2", features = ["derive", "env"] }
config = "0.13.0"
crossterm = { version = "0.25.0" }
digest = "0.10"
futures = { version = "^0.3.16", default-features = false, features = [
"alloc",
"alloc",
] }
ledger-transport-hid = { git = "https://github.com/Zondax/ledger-rs", rev = "20e2a20", optional = true }
log = { version = "0.4.8", features = ["std"] }
log4rs = { version = "1.3.0", default_features = false, features = [
"config_parsing",
"threshold_filter",
"yaml_format",
"console_appender",
"rolling_file_appender",
"compound_policy",
"size_trigger",
"fixed_window_roller",
"delete_roller",
"config_parsing",
"threshold_filter",
"yaml_format",
"console_appender",
"rolling_file_appender",
"compound_policy",
"size_trigger",
"fixed_window_roller",
"delete_roller",
] }
log = { version = "0.4.8", features = ["std"] }
qrcode = { version = "0.12" }
rand = "0.8"
regex = "1.5.4"
Expand All @@ -66,10 +68,9 @@ thiserror = "1.0.26"
tonic = "0.8.3"
unicode-segmentation = "1.6.0"
unicode-width = "0.1"
url = "2.3.1"
zeroize = "1"
zxcvbn = "2"
url = "2.3.1"
blake2 = "0.10"

[dependencies.tari_core]
path = "../../base_layer/core"
Expand All @@ -86,8 +87,9 @@ tari_features = { path = "../../common/tari_features", version = "1.0.0-pre.11a"

[features]
default = ["libtor"]
libtor = ["tari_libtor"]
grpc = []
ledger = ["ledger-transport-hid"]
libtor = ["tari_libtor"]

[package.metadata.cargo-machete]
# We need to specify extra features for log4rs even though it is not used directly in this crate
Expand Down
65 changes: 63 additions & 2 deletions applications/minotari_console_wallet/src/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@

#![allow(dead_code, unused)]

use std::{fs, path::PathBuf, str::FromStr, sync::Arc, time::Instant};
use std::{fs, io, path::PathBuf, str::FromStr, sync::Arc, time::Instant};

#[cfg(feature = "ledger")]
use ledger_transport_hid::{hidapi::HidApi, TransportNativeHID};
use log::*;
use minotari_app_utilities::identity_management::setup_node_identity;
use minotari_wallet::{
Expand All @@ -33,7 +35,7 @@ use minotari_wallet::{
database::{WalletBackend, WalletDatabase},
sqlite_utilities::initialize_sqlite_database_backends,
},
wallet::{derive_comms_secret_key, read_or_create_master_seed},
wallet::{derive_comms_secret_key, read_or_create_master_seed, read_or_create_wallet_type},
Wallet,
WalletConfig,
WalletSqlite,
Expand All @@ -48,6 +50,7 @@ use tari_common::{
},
exit_codes::{ExitCode, ExitError},
};
use tari_common_types::wallet_types::WalletType;
use tari_comms::{
multiaddr::Multiaddr,
peer_manager::{Peer, PeerFeatures, PeerQuery},
Expand Down Expand Up @@ -248,6 +251,7 @@ pub async fn change_password(
None,
shutdown_signal,
non_interactive_mode,
None,
)
.await?;

Expand Down Expand Up @@ -379,6 +383,7 @@ pub async fn init_wallet(
recovery_seed: Option<CipherSeed>,
shutdown_signal: ShutdownSignal,
non_interactive_mode: bool,
wallet_type: Option<WalletType>,
) -> Result<WalletSqlite, ExitError> {
fs::create_dir_all(
config
Expand Down Expand Up @@ -414,6 +419,7 @@ pub async fn init_wallet(
};

let master_seed = read_or_create_master_seed(recovery_seed.clone(), &wallet_db)?;
let wallet_type = read_or_create_wallet_type(wallet_type, &wallet_db);

let node_identity = match config.wallet.identity_file.as_ref() {
Some(identity_file) => {
Expand Down Expand Up @@ -459,6 +465,7 @@ pub async fn init_wallet(
key_manager_backend,
shutdown_signal,
master_seed,
wallet_type.unwrap(),
)
.await
.map_err(|e| match e {
Expand Down Expand Up @@ -804,6 +811,60 @@ pub(crate) fn boot_with_password(
Ok((boot_mode, password))
}

pub fn prompt_wallet_type(
boot_mode: WalletBoot,
wallet_config: &WalletConfig,
non_interactive: bool,
) -> Option<WalletType> {
if non_interactive {
return Some(WalletType::Software);
}

if wallet_config.wallet_type.is_some() {
return wallet_config.wallet_type;
}

match boot_mode {
WalletBoot::New => {
#[cfg(not(feature = "ledger"))]
return Some(WalletType::Software);

#[cfg(feature = "ledger")]
{
if prompt("\r\nWould you like to use a connected hardware wallet? (Supported types: Ledger)") {
print!("Scanning for connected Ledger hardware device... ");
let err = "No connected device was found. Please make sure the device is plugged in before
continuing.";
match TransportNativeHID::new(&HidApi::new().expect(err)) {
Ok(_) => {
println!("Device found.");
let account = prompt_ledger_account().expect("An account value");
Some(WalletType::Ledger(account))
},
Err(e) => panic!("{}", e),
}
} else {
Some(WalletType::Software)
}
}
},
_ => None,
}
}

pub fn prompt_ledger_account() -> Option<usize> {
let question =
"\r\nPlease enter an account number for your ledger. A simple 1-9, easily remembered numbers are suggested.";
println!("{}", question);
let mut input = "".to_string();
io::stdin().read_line(&mut input).unwrap();
let input = input.trim();
match input.parse() {
Ok(num) => Some(num),
Err(_e) => Some(1),
}
}

#[cfg(test)]
mod test {
use tari_utilities::SafePassword;
Expand Down
6 changes: 5 additions & 1 deletion applications/minotari_console_wallet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ use tokio::runtime::Runtime;
use wallet_modes::{command_mode, grpc_mode, recovery_mode, script_mode, tui_mode, WalletMode};

pub use crate::config::ApplicationConfig;
use crate::init::{boot_with_password, confirm_direct_only_send, confirm_seed_words, wallet_mode};
use crate::init::{boot_with_password, confirm_direct_only_send, confirm_seed_words, prompt_wallet_type, wallet_mode};

pub const LOG_TARGET: &str = "wallet::console_wallet::main";

Expand Down Expand Up @@ -128,6 +128,9 @@ pub fn run_wallet_with_cli(

let recovery_seed = get_recovery_seed(boot_mode, &cli)?;

// This is deactivated at the moment as full support is not yet complete
let wallet_type = prompt_wallet_type(boot_mode, &config.wallet, cli.non_interactive_mode);

// get command line password if provided
let seed_words_file_name = cli.seed_words_file_name.clone();

Expand Down Expand Up @@ -167,6 +170,7 @@ pub fn run_wallet_with_cli(
recovery_seed,
shutdown_signal,
cli.non_interactive_mode,
wallet_type,
))?;

if !cli.non_interactive_mode &&
Expand Down
Loading

0 comments on commit 0d66126

Please sign in to comment.