hw: load wallet from config

This commit is contained in:
edouard 2022-10-31 14:49:39 +01:00
parent 6500381059
commit 258aeb57fe
8 changed files with 110 additions and 26 deletions

9
gui/Cargo.lock generated
View File

@ -125,7 +125,7 @@ dependencies = [
[[package]]
name = "async-hwi"
version = "0.0.1"
source = "git+https://github.com/revault/async-hwi?branch=add-ledger#de5615ba2e2e2d3ded6a058f0193f8787d3873dc"
source = "git+https://github.com/revault/async-hwi?branch=master#14b29f820910132d9b8f799d030bfed69ac67239"
dependencies = [
"async-trait",
"base64",
@ -1380,7 +1380,7 @@ dependencies = [
[[package]]
name = "ledger_bitcoin_client"
version = "0.1.0"
source = "git+https://github.com/edouardparis/app-bitcoin-new/?branch=bitcoin_client_rs#95ec68b546d49a77eee274880d2450fdb08f840c"
source = "git+https://github.com/edouardparis/app-bitcoin-new/?branch=bitcoin_client_rs#efe26ed9fcfe0676c33141ad4777fd3786f7d539"
dependencies = [
"async-trait",
"bitcoin",
@ -1597,7 +1597,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "minisafe"
version = "0.0.1"
source = "git+https://github.com/revault/minisafe?branch=master#790d283e77063c019f54690eb1c483562e12338c"
source = "git+https://github.com/revault/minisafe?branch=master#8b129fe3e51ac41a78c282a018e70bd86e2ab24f"
dependencies = [
"backtrace",
"base64",
@ -1636,8 +1636,7 @@ dependencies = [
[[package]]
name = "miniscript"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4975078076f0b7b914a3044ad7432d2a7fcec38edb855afdc672e24ca35b69"
source = "git+https://github.com/darosior/rust-miniscript?branch=multipath_descriptors_on_8.0#a63d5a263a9006b4d29342012133a3bc919765ba"
dependencies = [
"bitcoin",
"serde",

View File

@ -14,7 +14,7 @@ name = "minisafe-gui"
path = "src/main.rs"
[dependencies]
async-hwi = { git = "https://github.com/revault/async-hwi", branch = "add-ledger" }
async-hwi = { git = "https://github.com/revault/async-hwi", branch = "master" }
minisafe = { git = "https://github.com/revault/minisafe", branch = "master", default-features = false }
backtrace = "0.3"

View File

@ -1,3 +1,4 @@
use crate::hw::HardwareWalletConfig;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
@ -9,16 +10,23 @@ pub struct Config {
pub log_level: Option<String>,
/// Use iced debug feature if true.
pub debug: Option<bool>,
/// hardware wallets config.
#[serde(default)]
pub hardware_wallets: Vec<HardwareWalletConfig>,
}
pub const DEFAULT_FILE_NAME: &str = "gui.toml";
impl Config {
pub fn new(minisafed_config_path: PathBuf) -> Self {
pub fn new(
minisafed_config_path: PathBuf,
hardware_wallets: Vec<HardwareWalletConfig>,
) -> Self {
Self {
minisafed_config_path,
log_level: None,
debug: None,
hardware_wallets,
}
}

View File

@ -1,7 +1,12 @@
use std::sync::Arc;
use async_hwi::{ledger, specter, DeviceKind, Error as HWIError, HWI};
use log::debug;
use minisafe::miniscript::bitcoin::util::bip32::Fingerprint;
use std::sync::Arc;
use minisafe::miniscript::bitcoin::{
hashes::hex::{FromHex, ToHex},
util::bip32::Fingerprint,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
pub struct HardwareWallet {
@ -22,7 +27,33 @@ impl HardwareWallet {
}
}
pub async fn list_hardware_wallets() -> Vec<HardwareWallet> {
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct HardwareWalletConfig {
pub kind: String,
pub fingerprint: String,
pub token: String,
}
impl HardwareWalletConfig {
pub fn new(kind: &async_hwi::DeviceKind, fingerprint: &Fingerprint, token: &[u8; 32]) -> Self {
Self {
kind: kind.to_string(),
fingerprint: fingerprint.to_string(),
token: token.to_hex(),
}
}
fn token(&self) -> [u8; 32] {
let mut res = [0x00; 32];
res.copy_from_slice(&Vec::from_hex(&self.token).unwrap());
res
}
}
pub async fn list_hardware_wallets(
cfg: &[HardwareWalletConfig],
wallet: Option<(&str, &str)>,
) -> Vec<HardwareWallet> {
let mut hws: Vec<HardwareWallet> = Vec::new();
match specter::SpecterSimulator::try_connect().await {
Ok(device) => match HardwareWallet::new(Arc::new(device)).await {
@ -49,8 +80,26 @@ pub async fn list_hardware_wallets() -> Vec<HardwareWallet> {
}
}
match ledger::LedgerSimulator::try_connect().await {
Ok(device) => match HardwareWallet::new(Arc::new(device)).await {
Ok(hw) => hws.push(hw),
Ok(mut device) => match device.get_master_fingerprint().await {
Ok(fingerprint) => {
if let Some((name, descriptor)) = wallet {
device
.load_wallet(
name,
descriptor,
cfg.iter()
.find(|cfg| cfg.fingerprint == fingerprint.to_string())
.map(|cfg| cfg.token()),
)
.expect("Configuration must be correct");
}
hws.push(HardwareWallet {
kind: device.device_kind(),
fingerprint,
device: Arc::new(device),
});
}
Err(e) => {
debug!("{}", e);
}

View File

@ -12,7 +12,9 @@ use std::convert::TryInto;
use std::io::Write;
use std::path::PathBuf;
use crate::{app::config as gui_config, installer::config::DEFAULT_FILE_NAME};
use crate::{
app::config as gui_config, hw::HardwareWalletConfig, installer::config::DEFAULT_FILE_NAME,
};
pub use message::Message;
use step::{Context, DefineBitcoind, DefineDescriptor, Final, RegisterDescriptor, Step, Welcome};
@ -133,6 +135,12 @@ impl Installer {
}
pub async fn install(ctx: Context) -> Result<PathBuf, Error> {
let hardware_wallets = ctx
.hw_tokens
.iter()
.map(|(kind, fingerprint, token)| HardwareWalletConfig::new(kind, fingerprint, token))
.collect();
let mut cfg: minisafe::config::Config = ctx
.try_into()
.expect("Everything should be checked at this point");
@ -179,6 +187,7 @@ pub async fn install(ctx: Context) -> Result<PathBuf, Error> {
e
))
})?,
hardware_wallets,
))
.unwrap()
.as_bytes(),

View File

@ -2,10 +2,10 @@ use std::str::FromStr;
use iced::{pure::Element, Command};
use minisafe::{
descriptors::InheritanceDescriptor,
descriptors::MultipathDescriptor,
miniscript::{
bitcoin::util::bip32::{DerivationPath, Fingerprint},
descriptor::{Descriptor, DescriptorPublicKey, DescriptorXKey, Wildcard},
descriptor::{Descriptor, DescriptorMultiXKey, DescriptorPublicKey, Wildcard},
},
};
@ -123,7 +123,7 @@ impl Step for DefineDescriptor {
}
false
} else if !self.imported_descriptor.value.is_empty() {
if let Ok(desc) = InheritanceDescriptor::from_str(&self.imported_descriptor.value) {
if let Ok(desc) = MultipathDescriptor::from_str(&self.imported_descriptor.value) {
ctx.descriptor = Some(desc);
true
} else {
@ -144,7 +144,7 @@ impl Step for DefineDescriptor {
return false;
}
let desc = match InheritanceDescriptor::new(
let desc = match MultipathDescriptor::new(
user_key.unwrap(),
heir_key.unwrap(),
sequence.unwrap(),
@ -207,7 +207,10 @@ impl GetHardwareWalletXpubModal {
}
}
fn load(&self) -> Command<Message> {
Command::perform(list_hardware_wallets(), Message::ConnectedHardwareWallets)
Command::perform(
list_hardware_wallets(&[], None),
Message::ConnectedHardwareWallets,
)
}
fn update(&mut self, message: Message) -> Command<Message> {
match message {
@ -276,9 +279,12 @@ async fn get_extended_pubkey(
.get_extended_pubkey(&derivation_path, true)
.await
.map_err(Error::from)?;
Ok(DescriptorPublicKey::XPub(DescriptorXKey {
Ok(DescriptorPublicKey::MultiXPub(DescriptorMultiXKey {
origin: Some((fingerprint, derivation_path)),
derivation_path: DerivationPath::master(),
derivation_paths: vec![
DerivationPath::from_str("m/0").unwrap(),
DerivationPath::from_str("m/1").unwrap(),
],
xkey,
wildcard: Wildcard::Unhardened,
}))
@ -286,7 +292,7 @@ async fn get_extended_pubkey(
#[derive(Default)]
pub struct RegisterDescriptor {
descriptor: Option<InheritanceDescriptor>,
descriptor: Option<MultipathDescriptor>,
processing: bool,
chosen_hw: Option<usize>,
hws: Vec<(HardwareWallet, Option<[u8; 32]>)>,
@ -348,11 +354,21 @@ impl Step for RegisterDescriptor {
};
Command::none()
}
fn apply(&mut self, _ctx: &mut Context) -> bool {
fn apply(&mut self, ctx: &mut Context) -> bool {
for (hw, token) in &self.hws {
if let Some(token) = token {
if *token != [0x00; 32] {
ctx.hw_tokens.push((hw.kind, hw.fingerprint, *token));
}
}
}
true
}
fn load(&self) -> Command<Message> {
Command::perform(list_hardware_wallets(), Message::ConnectedHardwareWallets)
Command::perform(
list_hardware_wallets(&[], None),
Message::ConnectedHardwareWallets,
)
}
fn view(&self) -> Element<Message> {
let desc = self.descriptor.as_ref().unwrap();

View File

@ -5,10 +5,11 @@ use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
use async_hwi::DeviceKind;
use iced::{pure::Element, Command};
use minisafe::{
config::{BitcoinConfig, BitcoindConfig},
descriptors::InheritanceDescriptor,
descriptors::MultipathDescriptor,
miniscript::bitcoin,
};
@ -40,7 +41,8 @@ pub trait Step {
pub struct Context {
pub bitcoin_config: BitcoinConfig,
pub bitcoind_config: Option<BitcoindConfig>,
pub descriptor: Option<InheritanceDescriptor>,
pub descriptor: Option<MultipathDescriptor>,
pub hw_tokens: Vec<(DeviceKind, bitcoin::util::bip32::Fingerprint, [u8; 32])>,
pub data_dir: Option<PathBuf>,
}
@ -51,6 +53,7 @@ impl Context {
network,
poll_interval_secs: Duration::from_secs(30),
},
hw_tokens: Vec::new(),
bitcoind_config: None,
descriptor: None,
data_dir,

View File

@ -81,7 +81,7 @@ pub fn fake_daemon_config() -> Config {
toml::from_str(
r#"
data_dir = "/home/edouard/code/revault/demo/minisafe/datadir"
main_descriptor = "wsh(or_d(pk(tpubDCbK3Ysvk8HjcF6mPyrgMu3KgLiaaP19RjKpNezd8GrbAbNg6v5BtWLaCt8FNm6QkLseopKLf5MNYQFtochDTKHdfgG6iqJ8cqnLNAwtXuP/*),and_v(v:pkh(tpubDDtb2WPYwEWw2WWDV7reLV348iJHw2HmhzvPysKKrJw3hYmvrd4jasyoioVPdKGQqjyaBMEvTn1HvHWDSVqQ6amyyxRZ5YjpPBBGjJ8yu8S/*),older(100))))#459t6xxr"
main_descriptor = "wsh(or_d(pk(tpubDCbK3Ysvk8HjcF6mPyrgMu3KgLiaaP19RjKpNezd8GrbAbNg6v5BtWLaCt8FNm6QkLseopKLf5MNYQFtochDTKHdfgG6iqJ8cqnLNAwtXuP/<0;1>/*),and_v(v:pkh(tpubDDtb2WPYwEWw2WWDV7reLV348iJHw2HmhzvPysKKrJw3hYmvrd4jasyoioVPdKGQqjyaBMEvTn1HvHWDSVqQ6amyyxRZ5YjpPBBGjJ8yu8S/<0;1>/*),older(100))))#9sx3g3pv"
[bitcoin_config]
network = "regtest"