gui: import xpubs from hws (wip)
This commit is contained in:
parent
8b129fe3e5
commit
8ad37f18a8
332
gui/Cargo.lock
generated
332
gui/Cargo.lock
generated
@ -2,6 +2,27 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "CoreFoundation-sys"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mach 0.1.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "IOKit-sys"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a"
|
||||
dependencies = [
|
||||
"CoreFoundation-sys",
|
||||
"libc",
|
||||
"mach 0.1.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ab_glyph"
|
||||
version = "0.2.15"
|
||||
@ -39,6 +60,12 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
@ -50,6 +77,15 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
version = "0.5.1"
|
||||
@ -86,6 +122,36 @@ dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-hwi"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/revault/async-hwi?branch=add-ledger#0eace00eca1ece4e4c49f2cd4d6fbffa2d527ce5"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64",
|
||||
"bitcoin",
|
||||
"futures",
|
||||
"hidapi",
|
||||
"ledger-apdu",
|
||||
"ledger-transport-hid",
|
||||
"ledger_bitcoin_client",
|
||||
"regex",
|
||||
"serialport",
|
||||
"tokio",
|
||||
"tokio-serial",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
@ -149,8 +215,11 @@ version = "0.29.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cb36de3b18ad25f396f9168302e36fb7e1e8923298ab3127da252d288d5af9d"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bech32",
|
||||
"bitcoin_hashes",
|
||||
"core2",
|
||||
"hashbrown 0.8.2",
|
||||
"secp256k1",
|
||||
"serde",
|
||||
]
|
||||
@ -161,6 +230,7 @@ version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4"
|
||||
dependencies = [
|
||||
"core2",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -208,6 +278,12 @@ version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
||||
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
version = "0.9.3"
|
||||
@ -431,6 +507,15 @@ dependencies = [
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
@ -590,6 +675,12 @@ dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.0"
|
||||
@ -940,13 +1031,23 @@ dependencies = [
|
||||
"svg_fmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25"
|
||||
dependencies = [
|
||||
"ahash 0.3.8",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ahash 0.7.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -964,6 +1065,12 @@ dependencies = [
|
||||
"hashbrown 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
@ -973,12 +1080,29 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hexf-parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||
|
||||
[[package]]
|
||||
name = "hidapi"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d26e1151deaab68f34fbfd16d491a2a0170cf98d69d3efa23873b567a4199e1"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced"
|
||||
version = "0.4.2"
|
||||
@ -1216,6 +1340,52 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "ledger-apdu"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe435806c197dfeaa5efcded5e623c4b8230fd28fdf1e91e7a86e40ef2acbf90"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"no-std-compat",
|
||||
"snafu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ledger-transport"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1117f2143d92c157197785bf57711d7b02f2cfa101e162f8ca7900fb7f976321"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"ledger-apdu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ledger-transport-hid"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45ba81a1f5f24396b37211478aff7fbcd605dd4544df8dbed07b9da3c2057aee"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"cfg-if 1.0.0",
|
||||
"hex",
|
||||
"hidapi",
|
||||
"ledger-transport",
|
||||
"libc",
|
||||
"log",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ledger_bitcoin_client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/edouardparis/app-bitcoin-new/?branch=bitcoin_client_rs#4f7951ce7c464db80f241ca8cd46c45770d6eec7"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bitcoin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
@ -1243,6 +1413,26 @@ dependencies = [
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libudev"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libudev-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libudev-sys"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
@ -1318,6 +1508,24 @@ dependencies = [
|
||||
"lyon_path",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
@ -1409,6 +1617,7 @@ dependencies = [
|
||||
name = "minisafe-gui"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"async-hwi",
|
||||
"backtrace",
|
||||
"chrono",
|
||||
"dirs",
|
||||
@ -1455,6 +1664,19 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio-serial"
|
||||
version = "5.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "531e0f6dc55a5aa7b8d320407c5c4ced464e23815c60f6a1e6d9e225d2b45905"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mio",
|
||||
"nix 0.23.1",
|
||||
"serialport",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mutate_once"
|
||||
version = "0.1.1"
|
||||
@ -1545,6 +1767,19 @@ dependencies = [
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.24.2"
|
||||
@ -1556,6 +1791,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "no-std-compat"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.1"
|
||||
@ -1946,6 +2187,23 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
@ -2116,6 +2374,23 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serialport"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aab92efb5cf60ad310548bc3f16fa6b0d950019cb7ed8ff41968c3d03721cf12"
|
||||
dependencies = [
|
||||
"CoreFoundation-sys",
|
||||
"IOKit-sys",
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"libudev",
|
||||
"mach 0.3.2",
|
||||
"nix 0.24.2",
|
||||
"regex",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sid"
|
||||
version = "0.6.1"
|
||||
@ -2220,6 +2495,38 @@ dependencies = [
|
||||
"wayland-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snafu"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a152ba99b054b22972ee794cf04e5ef572da1229e33b65f3c57abbff0525a454"
|
||||
dependencies = [
|
||||
"doc-comment",
|
||||
"snafu-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snafu-derive"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5e79cdebbabaebb06a9bdbaedc7f159b410461f63611d4d0e3fb0fab8fed850"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spirv"
|
||||
version = "0.2.0+1.5.4"
|
||||
@ -2350,17 +2657,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.20.1"
|
||||
version = "1.21.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581"
|
||||
checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"winapi",
|
||||
]
|
||||
@ -2376,6 +2685,19 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-serial"
|
||||
version = "5.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5488e0c75c70e880823aebc3ad4ac0a6da6f48d95bc1b9a52bd3200d6f1e724"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"futures",
|
||||
"log",
|
||||
"mio-serial",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.9"
|
||||
@ -2403,7 +2725,7 @@ version = "1.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"cfg-if 1.0.0",
|
||||
"rand 0.8.5",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
@ -14,13 +14,14 @@ name = "minisafe-gui"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
minisafe = { git = "https://github.com/revault/minisafe", branch = "master", default-features = false}
|
||||
async-hwi = { git = "https://github.com/revault/async-hwi", branch = "add-ledger" }
|
||||
minisafe = { git = "https://github.com/revault/minisafe", branch = "master", default-features = false }
|
||||
backtrace = "0.3"
|
||||
|
||||
iced = { version = "0.4", default-features= false, features = ["tokio", "wgpu", "svg", "qr_code", "pure"] }
|
||||
iced_native = "0.5"
|
||||
|
||||
tokio = {version = "1.9.0", features = ["signal"]}
|
||||
tokio = {version = "1.21.0", features = ["signal"]}
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
|
||||
76
gui/src/hw.rs
Normal file
76
gui/src/hw.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use async_hwi::{ledger, specter, DeviceKind, Error as HWIError, HWI};
|
||||
use log::debug;
|
||||
use minisafe::miniscript::bitcoin::util::bip32::Fingerprint;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HardwareWallet {
|
||||
pub device: Arc<dyn HWI + Send + Sync>,
|
||||
pub kind: DeviceKind,
|
||||
pub fingerprint: Fingerprint,
|
||||
}
|
||||
|
||||
impl HardwareWallet {
|
||||
async fn new(device: Arc<dyn HWI + Send + Sync>) -> Result<Self, HWIError> {
|
||||
let kind = device.device_kind();
|
||||
let fingerprint = device.get_master_fingerprint().await?;
|
||||
Ok(Self {
|
||||
device,
|
||||
kind,
|
||||
fingerprint,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_hardware_wallets() -> Vec<HardwareWallet> {
|
||||
let mut hws: Vec<HardwareWallet> = Vec::new();
|
||||
match specter::SpecterSimulator::try_connect().await {
|
||||
Ok(device) => match HardwareWallet::new(Arc::new(device)).await {
|
||||
Ok(hw) => hws.push(hw),
|
||||
Err(e) => {
|
||||
debug!("{}", e);
|
||||
}
|
||||
},
|
||||
Err(HWIError::DeviceNotFound) => {}
|
||||
Err(e) => {
|
||||
debug!("{}", e);
|
||||
}
|
||||
}
|
||||
match specter::Specter::try_connect_serial().await {
|
||||
Ok(device) => match HardwareWallet::new(Arc::new(device)).await {
|
||||
Ok(hw) => hws.push(hw),
|
||||
Err(e) => {
|
||||
debug!("{}", e);
|
||||
}
|
||||
},
|
||||
Err(HWIError::DeviceNotFound) => {}
|
||||
Err(e) => {
|
||||
debug!("{}", e);
|
||||
}
|
||||
}
|
||||
match ledger::LedgerSimulator::try_connect().await {
|
||||
Ok(device) => match HardwareWallet::new(Arc::new(device)).await {
|
||||
Ok(hw) => hws.push(hw),
|
||||
Err(e) => {
|
||||
debug!("{}", e);
|
||||
}
|
||||
},
|
||||
Err(HWIError::DeviceNotFound) => {}
|
||||
Err(e) => {
|
||||
debug!("{}", e);
|
||||
}
|
||||
}
|
||||
match ledger::Ledger::try_connect_hid() {
|
||||
Ok(device) => match HardwareWallet::new(Arc::new(device)).await {
|
||||
Ok(hw) => hws.push(hw),
|
||||
Err(e) => {
|
||||
debug!("{}", e);
|
||||
}
|
||||
},
|
||||
Err(HWIError::DeviceNotFound) => {}
|
||||
Err(e) => {
|
||||
debug!("{}", e);
|
||||
}
|
||||
}
|
||||
hws
|
||||
}
|
||||
@ -2,6 +2,7 @@ use minisafe::miniscript::bitcoin;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::Error;
|
||||
use crate::hw::HardwareWallet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
@ -10,10 +11,14 @@ pub enum Message {
|
||||
Next,
|
||||
Previous,
|
||||
Install,
|
||||
Close,
|
||||
Reload,
|
||||
Select(usize),
|
||||
Installed(Result<PathBuf, Error>),
|
||||
Network(bitcoin::Network),
|
||||
DefineBitcoind(DefineBitcoind),
|
||||
DefineDescriptor(DefineDescriptor),
|
||||
ConnectedHardwareWallets(Vec<HardwareWallet>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -25,6 +30,9 @@ pub enum DefineBitcoind {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DefineDescriptor {
|
||||
ImportDescriptor(String),
|
||||
ImportUserHWXpub,
|
||||
ImportHeirHWXpub,
|
||||
XpubImported(Result<String, Error>),
|
||||
UserXpubEdited(String),
|
||||
HeirXpubEdited(String),
|
||||
SequenceEdited(String),
|
||||
|
||||
@ -100,32 +100,32 @@ impl Installer {
|
||||
.expect("There is always a step");
|
||||
current_step.load_context(&self.context);
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
Message::Previous => {
|
||||
self.previous();
|
||||
Command::none()
|
||||
}
|
||||
Message::Install => {
|
||||
self.steps
|
||||
.get_mut(self.current)
|
||||
.expect("There is always a step")
|
||||
.update(message);
|
||||
return Command::perform(
|
||||
Command::perform(
|
||||
install(self.context.clone(), self.config.clone()),
|
||||
Message::Installed,
|
||||
);
|
||||
)
|
||||
}
|
||||
Message::Event(Event::Window(window::Event::CloseRequested)) => {
|
||||
self.stop();
|
||||
return Command::none();
|
||||
Command::none()
|
||||
}
|
||||
_ => {
|
||||
self.steps
|
||||
.get_mut(self.current)
|
||||
.expect("There is always a step")
|
||||
.update(message);
|
||||
}
|
||||
};
|
||||
Command::none()
|
||||
_ => self
|
||||
.steps
|
||||
.get_mut(self.current)
|
||||
.expect("There is always a step")
|
||||
.update(message),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view(&self) -> Element<Message> {
|
||||
@ -196,6 +196,13 @@ pub enum Error {
|
||||
CannotCreateFile(String),
|
||||
CannotWriteToFile(String),
|
||||
Unexpected(String),
|
||||
HardwareWallet(async_hwi::Error),
|
||||
}
|
||||
|
||||
impl From<async_hwi::Error> for Error {
|
||||
fn from(error: async_hwi::Error) -> Self {
|
||||
Error::HardwareWallet(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
@ -205,6 +212,7 @@ impl std::fmt::Display for Error {
|
||||
Self::CannotWriteToFile(e) => write!(f, "Failed to write to file: {}", e),
|
||||
Self::CannotCreateFile(e) => write!(f, "Failed to create file: {}", e),
|
||||
Self::Unexpected(e) => write!(f, "Unexpected: {}", e),
|
||||
Self::HardwareWallet(e) => write!(f, "Hardware Wallet: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
286
gui/src/installer/step/descriptor.rs
Normal file
286
gui/src/installer/step/descriptor.rs
Normal file
@ -0,0 +1,286 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use iced::{pure::Element, Command};
|
||||
use minisafe::{
|
||||
descriptors::InheritanceDescriptor,
|
||||
miniscript::{
|
||||
bitcoin::util::bip32::{DerivationPath, Fingerprint},
|
||||
descriptor::{Descriptor, DescriptorPublicKey, DescriptorXKey, Wildcard},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
hw::{list_hardware_wallets, HardwareWallet},
|
||||
installer::{
|
||||
config,
|
||||
message::{self, Message},
|
||||
step::{Context, Step},
|
||||
view, Error,
|
||||
},
|
||||
ui::component::form,
|
||||
};
|
||||
|
||||
pub struct DefineDescriptor {
|
||||
imported_descriptor: form::Value<String>,
|
||||
user_xpub: form::Value<String>,
|
||||
heir_xpub: form::Value<String>,
|
||||
sequence: form::Value<String>,
|
||||
modal: Option<GetHardwareWalletXpubModal>,
|
||||
|
||||
error: Option<String>,
|
||||
}
|
||||
|
||||
impl DefineDescriptor {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
imported_descriptor: form::Value::default(),
|
||||
user_xpub: form::Value::default(),
|
||||
heir_xpub: form::Value::default(),
|
||||
sequence: form::Value::default(),
|
||||
modal: None,
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for DefineDescriptor {
|
||||
// form value is set as valid each time it is edited.
|
||||
// Verification of the values is happening when the user click on Next button.
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::Close => {
|
||||
self.modal = None;
|
||||
}
|
||||
Message::DefineDescriptor(msg) => {
|
||||
match msg {
|
||||
message::DefineDescriptor::ImportDescriptor(desc) => {
|
||||
self.imported_descriptor.value = desc;
|
||||
self.imported_descriptor.valid = true;
|
||||
}
|
||||
message::DefineDescriptor::UserXpubEdited(xpub) => {
|
||||
self.user_xpub.value = xpub;
|
||||
self.user_xpub.valid = true;
|
||||
self.modal = None;
|
||||
}
|
||||
message::DefineDescriptor::HeirXpubEdited(xpub) => {
|
||||
self.heir_xpub.value = xpub;
|
||||
self.heir_xpub.valid = true;
|
||||
self.modal = None;
|
||||
}
|
||||
message::DefineDescriptor::SequenceEdited(seq) => {
|
||||
self.sequence.valid = true;
|
||||
if seq.is_empty() || seq.parse::<u16>().is_ok() {
|
||||
self.sequence.value = seq;
|
||||
}
|
||||
}
|
||||
message::DefineDescriptor::ImportUserHWXpub => {
|
||||
let modal = GetHardwareWalletXpubModal::new(false);
|
||||
let cmd = modal.load();
|
||||
self.modal = Some(modal);
|
||||
return cmd;
|
||||
}
|
||||
message::DefineDescriptor::ImportHeirHWXpub => {
|
||||
let modal = GetHardwareWalletXpubModal::new(true);
|
||||
let cmd = modal.load();
|
||||
self.modal = Some(modal);
|
||||
return cmd;
|
||||
}
|
||||
_ => {
|
||||
if let Some(modal) = &mut self.modal {
|
||||
return modal.update(Message::DefineDescriptor(msg));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
if let Some(modal) = &mut self.modal {
|
||||
return modal.update(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn apply(&mut self, _ctx: &mut Context, config: &mut config::Config) -> bool {
|
||||
// descriptor forms for import or creation cannot be both empty or filled.
|
||||
if self.imported_descriptor.value.is_empty()
|
||||
== (self.user_xpub.value.is_empty()
|
||||
|| self.heir_xpub.value.is_empty()
|
||||
|| self.sequence.value.is_empty())
|
||||
{
|
||||
if !self.user_xpub.value.is_empty() {
|
||||
self.user_xpub.valid = DescriptorPublicKey::from_str(&self.user_xpub.value).is_ok();
|
||||
}
|
||||
if !self.heir_xpub.value.is_empty() {
|
||||
self.heir_xpub.valid = DescriptorPublicKey::from_str(&self.heir_xpub.value).is_ok();
|
||||
}
|
||||
if !self.sequence.value.is_empty() {
|
||||
self.sequence.valid = self.sequence.value.parse::<u32>().is_ok();
|
||||
}
|
||||
if !self.imported_descriptor.value.is_empty() {
|
||||
self.imported_descriptor.valid =
|
||||
Descriptor::<DescriptorPublicKey>::from_str(&self.imported_descriptor.value)
|
||||
.is_ok();
|
||||
}
|
||||
false
|
||||
} else if !self.imported_descriptor.value.is_empty() {
|
||||
if let Ok(desc) = InheritanceDescriptor::from_str(&self.imported_descriptor.value) {
|
||||
config.main_descriptor = Some(desc);
|
||||
true
|
||||
} else {
|
||||
self.imported_descriptor.valid = false;
|
||||
false
|
||||
}
|
||||
} else {
|
||||
let user_key = DescriptorPublicKey::from_str(&self.user_xpub.value);
|
||||
self.user_xpub.valid = user_key.is_ok();
|
||||
|
||||
let heir_key = DescriptorPublicKey::from_str(&self.heir_xpub.value);
|
||||
self.user_xpub.valid = user_key.is_ok();
|
||||
|
||||
let sequence = self.sequence.value.parse::<u16>();
|
||||
self.sequence.valid = sequence.is_ok();
|
||||
|
||||
if !self.user_xpub.valid || !self.heir_xpub.valid || !self.sequence.valid {
|
||||
return false;
|
||||
}
|
||||
|
||||
match InheritanceDescriptor::new(
|
||||
user_key.unwrap(),
|
||||
heir_key.unwrap(),
|
||||
sequence.unwrap(),
|
||||
) {
|
||||
Ok(desc) => {
|
||||
config.main_descriptor = Some(desc);
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
self.error = Some(e.to_string());
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
if let Some(modal) = &self.modal {
|
||||
modal.view()
|
||||
} else {
|
||||
view::define_descriptor(
|
||||
&self.imported_descriptor,
|
||||
&self.user_xpub,
|
||||
&self.heir_xpub,
|
||||
&self.sequence,
|
||||
self.error.as_ref(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DefineDescriptor {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DefineDescriptor> for Box<dyn Step> {
|
||||
fn from(s: DefineDescriptor) -> Box<dyn Step> {
|
||||
Box::new(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GetHardwareWalletXpubModal {
|
||||
is_heir: bool,
|
||||
chosen_hw: Option<usize>,
|
||||
processing: bool,
|
||||
hws: Vec<HardwareWallet>,
|
||||
error: Option<Error>,
|
||||
}
|
||||
|
||||
impl GetHardwareWalletXpubModal {
|
||||
fn new(is_heir: bool) -> Self {
|
||||
Self {
|
||||
is_heir,
|
||||
chosen_hw: None,
|
||||
processing: false,
|
||||
hws: Vec::new(),
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
fn load(&self) -> Command<Message> {
|
||||
Command::perform(list_hardware_wallets(), Message::ConnectedHardwareWallets)
|
||||
}
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::Select(i) => {
|
||||
if let Some(hw) = self.hws.get(i) {
|
||||
let device = hw.device.clone();
|
||||
self.chosen_hw = Some(i);
|
||||
self.processing = true;
|
||||
return Command::perform(get_extended_pubkey(device, hw.fingerprint), |res| {
|
||||
Message::DefineDescriptor(message::DefineDescriptor::XpubImported(
|
||||
res.map(|key| key.to_string()),
|
||||
))
|
||||
});
|
||||
}
|
||||
}
|
||||
Message::ConnectedHardwareWallets(hws) => {
|
||||
self.hws = hws;
|
||||
}
|
||||
Message::Reload => {
|
||||
return self.load();
|
||||
}
|
||||
Message::DefineDescriptor(message::DefineDescriptor::XpubImported(res)) => {
|
||||
self.processing = false;
|
||||
match res {
|
||||
Ok(key) => {
|
||||
if self.is_heir {
|
||||
return Command::perform(
|
||||
async move { key },
|
||||
message::DefineDescriptor::HeirXpubEdited,
|
||||
)
|
||||
.map(Message::DefineDescriptor);
|
||||
} else {
|
||||
return Command::perform(
|
||||
async move { key },
|
||||
message::DefineDescriptor::UserXpubEdited,
|
||||
)
|
||||
.map(Message::DefineDescriptor);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
self.error = Some(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Command::none()
|
||||
}
|
||||
fn view(&self) -> Element<Message> {
|
||||
view::hardware_wallet_xpubs_modal(
|
||||
self.is_heir,
|
||||
&self.hws,
|
||||
self.error.as_ref(),
|
||||
self.processing,
|
||||
self.chosen_hw,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_extended_pubkey(
|
||||
hw: std::sync::Arc<dyn async_hwi::HWI + Send + Sync>,
|
||||
fingerprint: Fingerprint,
|
||||
) -> Result<DescriptorPublicKey, Error> {
|
||||
let derivation_path = DerivationPath::master();
|
||||
let xkey = hw
|
||||
.get_extended_pubkey(&derivation_path, true)
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
Ok(DescriptorPublicKey::XPub(DescriptorXKey {
|
||||
origin: Some((fingerprint, derivation_path)),
|
||||
derivation_path: DerivationPath::master(),
|
||||
xkey,
|
||||
wildcard: Wildcard::Unhardened,
|
||||
}))
|
||||
}
|
||||
@ -1,14 +1,11 @@
|
||||
mod descriptor;
|
||||
pub use descriptor::DefineDescriptor;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use iced::pure::Element;
|
||||
use minisafe::{
|
||||
descriptors::InheritanceDescriptor,
|
||||
miniscript::{
|
||||
bitcoin,
|
||||
descriptor::{Descriptor, DescriptorPublicKey},
|
||||
},
|
||||
};
|
||||
use iced::{pure::Element, Command};
|
||||
use minisafe::miniscript::bitcoin;
|
||||
|
||||
use crate::ui::component::form;
|
||||
|
||||
@ -19,7 +16,9 @@ use crate::installer::{
|
||||
};
|
||||
|
||||
pub trait Step {
|
||||
fn update(&mut self, message: Message);
|
||||
fn update(&mut self, _message: Message) -> Command<Message> {
|
||||
Command::none()
|
||||
}
|
||||
fn view(&self) -> Element<Message>;
|
||||
fn load_context(&mut self, _ctx: &Context) {}
|
||||
fn skip(&self, _ctx: &Context) -> bool {
|
||||
@ -58,10 +57,11 @@ impl Welcome {
|
||||
}
|
||||
|
||||
impl Step for Welcome {
|
||||
fn update(&mut self, message: Message) {
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
if let message::Message::Network(network) = message {
|
||||
self.network = network;
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
fn apply(&mut self, ctx: &mut Context, config: &mut config::Config) -> bool {
|
||||
ctx.network = self.network;
|
||||
@ -85,138 +85,6 @@ impl From<Welcome> for Box<dyn Step> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DefineDescriptor {
|
||||
imported_descriptor: form::Value<String>,
|
||||
user_xpub: form::Value<String>,
|
||||
heir_xpub: form::Value<String>,
|
||||
sequence: form::Value<String>,
|
||||
error: Option<String>,
|
||||
}
|
||||
|
||||
impl DefineDescriptor {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
imported_descriptor: form::Value::default(),
|
||||
user_xpub: form::Value::default(),
|
||||
heir_xpub: form::Value::default(),
|
||||
sequence: form::Value::default(),
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for DefineDescriptor {
|
||||
// form value is set as valid each time it is edited.
|
||||
// Verification of the values is happening when the user click on Next button.
|
||||
fn update(&mut self, message: Message) {
|
||||
if let Message::DefineDescriptor(msg) = message {
|
||||
match msg {
|
||||
message::DefineDescriptor::ImportDescriptor(desc) => {
|
||||
self.imported_descriptor.value = desc;
|
||||
self.imported_descriptor.valid = true;
|
||||
}
|
||||
message::DefineDescriptor::UserXpubEdited(xpub) => {
|
||||
self.user_xpub.value = xpub;
|
||||
self.user_xpub.valid = true;
|
||||
}
|
||||
message::DefineDescriptor::HeirXpubEdited(xpub) => {
|
||||
self.heir_xpub.value = xpub;
|
||||
self.heir_xpub.valid = true;
|
||||
}
|
||||
message::DefineDescriptor::SequenceEdited(seq) => {
|
||||
self.sequence.valid = true;
|
||||
if seq.is_empty() || seq.parse::<u16>().is_ok() {
|
||||
self.sequence.value = seq;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
fn apply(&mut self, _ctx: &mut Context, config: &mut config::Config) -> bool {
|
||||
// descriptor forms for import or creation cannot be both empty or filled.
|
||||
if self.imported_descriptor.value.is_empty()
|
||||
== (self.user_xpub.value.is_empty()
|
||||
|| self.heir_xpub.value.is_empty()
|
||||
|| self.sequence.value.is_empty())
|
||||
{
|
||||
if !self.user_xpub.value.is_empty() {
|
||||
self.user_xpub.valid = DescriptorPublicKey::from_str(&self.user_xpub.value).is_ok();
|
||||
}
|
||||
if !self.heir_xpub.value.is_empty() {
|
||||
self.heir_xpub.valid = DescriptorPublicKey::from_str(&self.heir_xpub.value).is_ok();
|
||||
}
|
||||
if !self.sequence.value.is_empty() {
|
||||
self.sequence.valid = self.sequence.value.parse::<u32>().is_ok();
|
||||
}
|
||||
if !self.imported_descriptor.value.is_empty() {
|
||||
self.imported_descriptor.valid =
|
||||
Descriptor::<DescriptorPublicKey>::from_str(&self.imported_descriptor.value)
|
||||
.is_ok();
|
||||
}
|
||||
false
|
||||
} else if !self.imported_descriptor.value.is_empty() {
|
||||
if let Ok(desc) = InheritanceDescriptor::from_str(&self.imported_descriptor.value) {
|
||||
config.main_descriptor = Some(desc);
|
||||
true
|
||||
} else {
|
||||
self.imported_descriptor.valid = false;
|
||||
false
|
||||
}
|
||||
} else {
|
||||
let user_key = DescriptorPublicKey::from_str(&self.user_xpub.value);
|
||||
self.user_xpub.valid = user_key.is_ok();
|
||||
|
||||
let heir_key = DescriptorPublicKey::from_str(&self.heir_xpub.value);
|
||||
self.user_xpub.valid = user_key.is_ok();
|
||||
|
||||
let sequence = self.sequence.value.parse::<u16>();
|
||||
self.sequence.valid = sequence.is_ok();
|
||||
|
||||
if !self.user_xpub.valid || !self.heir_xpub.valid || !self.sequence.valid {
|
||||
return false;
|
||||
}
|
||||
|
||||
match InheritanceDescriptor::new(
|
||||
user_key.unwrap(),
|
||||
heir_key.unwrap(),
|
||||
sequence.unwrap(),
|
||||
) {
|
||||
Ok(desc) => {
|
||||
config.main_descriptor = Some(desc);
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
self.error = Some(e.to_string());
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
view::define_descriptor(
|
||||
&self.imported_descriptor,
|
||||
&self.user_xpub,
|
||||
&self.heir_xpub,
|
||||
&self.sequence,
|
||||
self.error.as_ref(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DefineDescriptor {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DefineDescriptor> for Box<dyn Step> {
|
||||
fn from(s: DefineDescriptor) -> Box<dyn Step> {
|
||||
Box::new(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DefineBitcoind {
|
||||
cookie_path: form::Value<String>,
|
||||
address: form::Value<String>,
|
||||
@ -283,7 +151,7 @@ impl Step for DefineBitcoind {
|
||||
self.address.value = bitcoind_default_address(&ctx.network);
|
||||
}
|
||||
}
|
||||
fn update(&mut self, message: Message) {
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
if let Message::DefineBitcoind(msg) = message {
|
||||
match msg {
|
||||
message::DefineBitcoind::AddressEdited(address) => {
|
||||
@ -296,6 +164,7 @@ impl Step for DefineBitcoind {
|
||||
}
|
||||
};
|
||||
};
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn apply(&mut self, _ctx: &mut Context, config: &mut config::Config) -> bool {
|
||||
@ -358,7 +227,7 @@ impl Final {
|
||||
}
|
||||
|
||||
impl Step for Final {
|
||||
fn update(&mut self, message: Message) {
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::Installed(res) => {
|
||||
self.generating = false;
|
||||
@ -377,6 +246,7 @@ impl Step for Final {
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
|
||||
@ -1,18 +1,25 @@
|
||||
use iced::pure::{column, container, pick_list, row, scrollable, Element};
|
||||
use iced::pure::{column, container, pick_list, row, scrollable, widget, Element};
|
||||
use iced::{Alignment, Length};
|
||||
|
||||
use minisafe::miniscript::bitcoin;
|
||||
|
||||
use crate::ui::{
|
||||
component::{
|
||||
button, form,
|
||||
text::{text, Text},
|
||||
use crate::{
|
||||
hw::HardwareWallet,
|
||||
installer::{
|
||||
message::{self, Message},
|
||||
Error,
|
||||
},
|
||||
ui::{
|
||||
color,
|
||||
component::{
|
||||
button, card, form,
|
||||
text::{text, Text},
|
||||
},
|
||||
icon,
|
||||
util::Collection,
|
||||
},
|
||||
util::Collection,
|
||||
};
|
||||
|
||||
use crate::installer::message::{self, Message};
|
||||
|
||||
const NETWORKS: [bitcoin::Network; 4] = [
|
||||
bitcoin::Network::Bitcoin,
|
||||
bitcoin::Network::Testnet,
|
||||
@ -66,36 +73,55 @@ pub fn define_descriptor<'a>(
|
||||
let col_user_xpub = column()
|
||||
.push(text("Your xpub:").bold())
|
||||
.push(
|
||||
form::Form::new("Xpub", user_xpub, |msg| {
|
||||
Message::DefineDescriptor(message::DefineDescriptor::UserXpubEdited(msg))
|
||||
})
|
||||
.warning("Please enter correct xpub")
|
||||
.size(20)
|
||||
.padding(10),
|
||||
row()
|
||||
.push(
|
||||
form::Form::new("Xpub", user_xpub, |msg| {
|
||||
Message::DefineDescriptor(message::DefineDescriptor::UserXpubEdited(msg))
|
||||
})
|
||||
.warning("Please enter correct xpub")
|
||||
.size(20)
|
||||
.padding(10),
|
||||
)
|
||||
.push(button::primary(Some(icon::chip_icon()), "Import").on_press(
|
||||
Message::DefineDescriptor(message::DefineDescriptor::ImportUserHWXpub),
|
||||
))
|
||||
.spacing(5)
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
.spacing(10);
|
||||
|
||||
let col_heir_xpub = column()
|
||||
.push(text("Heir xpub:").bold())
|
||||
.push(
|
||||
form::Form::new("Xpub", heir_xpub, |msg| {
|
||||
Message::DefineDescriptor(message::DefineDescriptor::HeirXpubEdited(msg))
|
||||
})
|
||||
.warning("Please enter correct xpub")
|
||||
.size(20)
|
||||
.padding(10),
|
||||
row()
|
||||
.push(
|
||||
form::Form::new("Xpub", heir_xpub, |msg| {
|
||||
Message::DefineDescriptor(message::DefineDescriptor::HeirXpubEdited(msg))
|
||||
})
|
||||
.warning("Please enter correct xpub")
|
||||
.size(20)
|
||||
.padding(10),
|
||||
)
|
||||
.push(button::primary(Some(icon::chip_icon()), "Import").on_press(
|
||||
Message::DefineDescriptor(message::DefineDescriptor::ImportHeirHWXpub),
|
||||
))
|
||||
.spacing(5)
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
.spacing(10);
|
||||
|
||||
let col_sequence = column()
|
||||
.push(text("Number of block").bold())
|
||||
.push(text("Number of block:").bold())
|
||||
.push(
|
||||
form::Form::new("Number of block", sequence, |msg| {
|
||||
Message::DefineDescriptor(message::DefineDescriptor::SequenceEdited(msg))
|
||||
})
|
||||
.warning("Please enter correct block number")
|
||||
.size(20)
|
||||
.padding(10),
|
||||
container(
|
||||
form::Form::new("Number of block", sequence, |msg| {
|
||||
Message::DefineDescriptor(message::DefineDescriptor::SequenceEdited(msg))
|
||||
})
|
||||
.warning("Please enter correct block number")
|
||||
.size(20)
|
||||
.padding(10),
|
||||
)
|
||||
.width(Length::Units(150)),
|
||||
)
|
||||
.spacing(10);
|
||||
|
||||
@ -105,12 +131,8 @@ pub fn define_descriptor<'a>(
|
||||
.push(
|
||||
column()
|
||||
.push(col_user_xpub)
|
||||
.push(
|
||||
row()
|
||||
.push(col_sequence.width(Length::FillPortion(1)))
|
||||
.push(col_heir_xpub.width(Length::FillPortion(4)))
|
||||
.spacing(20),
|
||||
)
|
||||
.push(col_sequence)
|
||||
.push(col_heir_xpub)
|
||||
.spacing(20),
|
||||
)
|
||||
.push(text("or import it").bold().size(25))
|
||||
@ -128,7 +150,7 @@ pub fn define_descriptor<'a>(
|
||||
.on_press(Message::Next)
|
||||
},
|
||||
)
|
||||
.push_maybe(error.map(|e| text(e).size(15)))
|
||||
.push_maybe(error.map(|e| card::error("Failed to create descriptor", e)))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.padding(100)
|
||||
@ -233,6 +255,92 @@ pub fn install<'a>(
|
||||
layout(col)
|
||||
}
|
||||
|
||||
pub fn hardware_wallet_xpubs_modal<'a>(
|
||||
is_heir: bool,
|
||||
hws: &[HardwareWallet],
|
||||
error: Option<&Error>,
|
||||
processing: bool,
|
||||
chosen_hw: Option<usize>,
|
||||
) -> Element<'a, Message> {
|
||||
modal(
|
||||
column()
|
||||
.push(
|
||||
text(if is_heir {
|
||||
"Import the Heir xpub"
|
||||
} else {
|
||||
"Import the user xpub"
|
||||
})
|
||||
.bold()
|
||||
.size(50),
|
||||
)
|
||||
.push_maybe(error.map(|e| card::error("Failed to import xpub", &e.to_string())))
|
||||
.push(if !hws.is_empty() {
|
||||
column()
|
||||
.push(text(&format!("{} hardware wallets connected", hws.len())).bold())
|
||||
.spacing(10)
|
||||
.push(
|
||||
hws.iter()
|
||||
.enumerate()
|
||||
.fold(column().spacing(10), |col, (i, hw)| {
|
||||
col.push(hw_list_view(i, hw, Some(i) == chosen_hw, processing))
|
||||
}),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
} else {
|
||||
column().push(card::simple(
|
||||
column()
|
||||
.spacing(10)
|
||||
.push("Please connect a hardware wallet")
|
||||
.push(button::primary(None, "Refresh").on_press(Message::Reload))
|
||||
.align_items(Alignment::Center),
|
||||
))
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.padding(100)
|
||||
.spacing(50)
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
}
|
||||
|
||||
fn hw_list_view<'a>(
|
||||
i: usize,
|
||||
hw: &HardwareWallet,
|
||||
chosen: bool,
|
||||
processing: bool,
|
||||
) -> Element<'a, Message> {
|
||||
let mut bttn = iced::pure::button(
|
||||
row()
|
||||
.push(
|
||||
column()
|
||||
.push(text(&format!("{}", hw.kind)).bold())
|
||||
.push(text(&format!("fingerprint: {}", hw.fingerprint)).small())
|
||||
.spacing(5)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.push_maybe(if chosen && processing {
|
||||
Some(
|
||||
column()
|
||||
.push(text("Processing..."))
|
||||
.push(text("Please check your device").small()),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.padding(10)
|
||||
.style(button::Style::TransparentBorder)
|
||||
.width(Length::Fill);
|
||||
if !processing {
|
||||
bttn = bttn.on_press(Message::Select(i));
|
||||
}
|
||||
container(bttn)
|
||||
.width(Length::Fill)
|
||||
.style(card::SimpleCardStyle)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn layout<'a>(content: impl Into<Element<'a, Message>>) -> Element<'a, Message> {
|
||||
container(scrollable(
|
||||
column()
|
||||
@ -247,3 +355,33 @@ fn layout<'a>(content: impl Into<Element<'a, Message>>) -> Element<'a, Message>
|
||||
.width(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn modal<'a>(content: impl Into<Element<'a, Message>>) -> Element<'a, Message> {
|
||||
container(scrollable(
|
||||
column()
|
||||
.push(
|
||||
row().push(column().width(Length::Fill)).push(
|
||||
container(
|
||||
button::primary(Some(icon::cross_icon()), "Close").on_press(Message::Close),
|
||||
)
|
||||
.padding(10),
|
||||
),
|
||||
)
|
||||
.push(container(content).width(Length::Fill).center_x()),
|
||||
))
|
||||
.center_x()
|
||||
.height(Length::Fill)
|
||||
.width(Length::Fill)
|
||||
.style(ModalStyle)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub struct ModalStyle;
|
||||
impl widget::container::StyleSheet for ModalStyle {
|
||||
fn style(&self) -> widget::container::Style {
|
||||
widget::container::Style {
|
||||
background: color::BACKGROUND.into(),
|
||||
..widget::container::Style::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
pub mod app;
|
||||
pub mod daemon;
|
||||
pub mod hw;
|
||||
pub mod installer;
|
||||
pub mod loader;
|
||||
pub mod ui;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use iced::pure::{container, widget, Element};
|
||||
use iced::pure::{container, row, tooltip, widget, Element};
|
||||
|
||||
use crate::ui::color;
|
||||
use crate::ui::{color, component::text::text, icon};
|
||||
|
||||
pub fn simple<'a, T: 'a, C: Into<Element<'a, T>>>(content: C) -> widget::Container<'a, T> {
|
||||
container(content).padding(15).style(SimpleCardStyle)
|
||||
@ -16,3 +16,34 @@ impl widget::container::StyleSheet for SimpleCardStyle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// display an error card with the message and the error in a tooltip.
|
||||
pub fn error<'a, T: 'a>(message: &str, error: &str) -> widget::Container<'a, T> {
|
||||
container(
|
||||
tooltip(
|
||||
row()
|
||||
.spacing(20)
|
||||
.align_items(iced::Alignment::Center)
|
||||
.push(icon::block_icon().color(color::ALERT))
|
||||
.push(text(message).color(color::ALERT)),
|
||||
error,
|
||||
widget::tooltip::Position::Bottom,
|
||||
)
|
||||
.style(ErrorCardStyle),
|
||||
)
|
||||
.padding(15)
|
||||
.style(ErrorCardStyle)
|
||||
}
|
||||
|
||||
pub struct ErrorCardStyle;
|
||||
impl widget::container::StyleSheet for ErrorCardStyle {
|
||||
fn style(&self) -> widget::container::Style {
|
||||
widget::container::Style {
|
||||
border_radius: 10.0,
|
||||
border_color: color::ALERT,
|
||||
border_width: 1.5,
|
||||
background: color::FOREGROUND.into(),
|
||||
..widget::container::Style::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,6 +134,10 @@ pub fn warning_icon() -> Text {
|
||||
icon('\u{F33B}')
|
||||
}
|
||||
|
||||
pub fn chip_icon() -> Text {
|
||||
icon('\u{F2D6}')
|
||||
}
|
||||
|
||||
pub fn trash_icon() -> Text {
|
||||
icon('\u{F5DE}')
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user