202 lines
6.1 KiB
Rust
202 lines
6.1 KiB
Rust
use std::sync::{Arc, Mutex};
|
|
|
|
use iced::{Command, Subscription};
|
|
use liana::miniscript::bitcoin::{
|
|
bip32::{ChildNumber, Fingerprint},
|
|
Network,
|
|
};
|
|
|
|
use liana_ui::widget::Element;
|
|
|
|
use crate::{
|
|
hw::{HardwareWallet, HardwareWallets},
|
|
installer::{
|
|
message::Message,
|
|
step::{
|
|
descriptor::editor::key::{default_derivation_path, get_extended_pubkey},
|
|
Context, Step,
|
|
},
|
|
view, Error,
|
|
},
|
|
signer::Signer,
|
|
};
|
|
|
|
pub struct HardwareWalletXpubs {
|
|
fingerprint: Fingerprint,
|
|
xpubs: Vec<String>,
|
|
processing: bool,
|
|
error: Option<Error>,
|
|
}
|
|
|
|
pub struct SignerXpubs {
|
|
signer: Arc<Mutex<Signer>>,
|
|
xpubs: Vec<String>,
|
|
next_account: ChildNumber,
|
|
words: [&'static str; 12],
|
|
did_backup: bool,
|
|
}
|
|
|
|
impl SignerXpubs {
|
|
fn new(signer: Arc<Mutex<Signer>>) -> Self {
|
|
let words = { signer.lock().unwrap().mnemonic() };
|
|
Self {
|
|
words,
|
|
signer,
|
|
xpubs: Vec::new(),
|
|
next_account: ChildNumber::from_hardened_idx(0).unwrap(),
|
|
did_backup: false,
|
|
}
|
|
}
|
|
|
|
fn select(&mut self, network: Network) {
|
|
self.next_account = self.next_account.increment().unwrap();
|
|
let signer = self.signer.lock().unwrap();
|
|
let derivation_path = default_derivation_path(network);
|
|
// We keep only one for the moment.
|
|
self.xpubs = vec![format!(
|
|
"[{}{}]{}",
|
|
signer.fingerprint(),
|
|
derivation_path.to_string().trim_start_matches('m'),
|
|
signer.get_extended_pubkey(&derivation_path)
|
|
)];
|
|
}
|
|
|
|
pub fn view(&self) -> Element<Message> {
|
|
view::signer_xpubs(&self.xpubs, &self.words, self.did_backup)
|
|
}
|
|
}
|
|
|
|
pub struct ShareXpubs {
|
|
network: Network,
|
|
hw_xpubs: Vec<HardwareWalletXpubs>,
|
|
xpubs_signer: SignerXpubs,
|
|
}
|
|
|
|
impl ShareXpubs {
|
|
pub fn new(network: Network, signer: Arc<Mutex<Signer>>) -> Self {
|
|
Self {
|
|
network,
|
|
hw_xpubs: Vec::new(),
|
|
xpubs_signer: SignerXpubs::new(signer),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Step for ShareXpubs {
|
|
// 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, hws: &mut HardwareWallets, message: Message) -> Command<Message> {
|
|
match message {
|
|
Message::ImportXpub(fg, res) => {
|
|
if let Some(hw_xpubs) = self.hw_xpubs.iter_mut().find(|x| x.fingerprint == fg) {
|
|
hw_xpubs.processing = false;
|
|
match res {
|
|
Err(e) => {
|
|
hw_xpubs.error = e.into();
|
|
}
|
|
Ok(xpub) => {
|
|
hw_xpubs.error = None;
|
|
// We keep only one for the moment.
|
|
hw_xpubs.xpubs = vec![xpub.to_string()];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Message::UseHotSigner => {
|
|
self.xpubs_signer.select(self.network);
|
|
}
|
|
Message::UserActionDone(done) => {
|
|
self.xpubs_signer.did_backup = done;
|
|
}
|
|
Message::Select(i) => {
|
|
if let Some(HardwareWallet::Supported {
|
|
device,
|
|
fingerprint,
|
|
..
|
|
}) = hws.list.get(i)
|
|
{
|
|
let device = device.clone();
|
|
let fingerprint = *fingerprint;
|
|
let network = self.network;
|
|
if let Some(hw_xpubs) = self
|
|
.hw_xpubs
|
|
.iter_mut()
|
|
.find(|x| x.fingerprint == fingerprint)
|
|
{
|
|
hw_xpubs.processing = true;
|
|
hw_xpubs.error = None;
|
|
} else {
|
|
self.hw_xpubs.push(HardwareWalletXpubs {
|
|
fingerprint,
|
|
xpubs: Vec::new(),
|
|
processing: true,
|
|
error: None,
|
|
});
|
|
}
|
|
return Command::perform(
|
|
async move {
|
|
(
|
|
fingerprint,
|
|
get_extended_pubkey(device, fingerprint, network).await,
|
|
)
|
|
},
|
|
|(fingerprint, res)| Message::ImportXpub(fingerprint, res),
|
|
);
|
|
}
|
|
}
|
|
_ => {}
|
|
};
|
|
Command::none()
|
|
}
|
|
|
|
fn subscription(&self, hws: &HardwareWallets) -> Subscription<Message> {
|
|
hws.refresh().map(Message::HardwareWallets)
|
|
}
|
|
|
|
fn apply(&mut self, ctx: &mut Context) -> bool {
|
|
ctx.bitcoin_config.network = self.network;
|
|
// Drop connections to hardware wallets.
|
|
self.hw_xpubs = Vec::new();
|
|
true
|
|
}
|
|
|
|
fn view<'a>(
|
|
&'a self,
|
|
hws: &'a HardwareWallets,
|
|
_progress: (usize, usize),
|
|
email: Option<&'a str>,
|
|
) -> Element<Message> {
|
|
view::share_xpubs(
|
|
email,
|
|
hws.list
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, hw)| {
|
|
if let Some(hw_xpubs) = self
|
|
.hw_xpubs
|
|
.iter()
|
|
.find(|h| hw.fingerprint() == Some(h.fingerprint))
|
|
{
|
|
view::hardware_wallet_xpubs(
|
|
i,
|
|
hw,
|
|
Some(&hw_xpubs.xpubs),
|
|
hw_xpubs.processing,
|
|
hw_xpubs.error.as_ref(),
|
|
)
|
|
} else {
|
|
view::hardware_wallet_xpubs(i, hw, None, false, None)
|
|
}
|
|
})
|
|
.collect(),
|
|
self.xpubs_signer.view(),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl From<ShareXpubs> for Box<dyn Step> {
|
|
fn from(s: ShareXpubs) -> Box<dyn Step> {
|
|
Box::new(s)
|
|
}
|
|
}
|