Merge #1199: Remove participate wallet process for share xpubs simple panel

6cbf81a3c5d8a7a4e1802581755c7c5d05766b66 Remove participate wallet process for share xpubs simple panel (edouardparis)

Pull request description:

  ![20240717_17h05m05s_grim](https://github.com/user-attachments/assets/91f0f608-d0d0-49a1-abd9-6d5cf01ca5ee)
  ![20240717_17h05m17s_grim](https://github.com/user-attachments/assets/b39f4a13-e581-45b7-a306-09381d303cf8)

ACKs for top commit:
  jp1ac4:
    Tested ACK 6cbf81a3c5.

Tree-SHA512: 4245513005e01b2eece4bf099c0b38b1aa6c23617b53bdd811f5d78c9800133c0dfbbed2f06b70e8ace8725482c9f0c9b0266d193c9b7947d8c5ae9ef8641ac9
This commit is contained in:
edouardparis 2024-07-18 17:28:40 +02:00
commit e81a2d3a9e
No known key found for this signature in database
GPG Key ID: E65F7A089C20DC8F
6 changed files with 250 additions and 252 deletions

View File

@ -12,7 +12,7 @@ use async_hwi::{DeviceKind, Version};
#[derive(Debug, Clone)]
pub enum Message {
CreateWallet,
ParticipateWallet,
ShareXpubs,
ImportWallet,
UserActionDone(bool),
Exit(PathBuf, Option<Bitcoind>),

View File

@ -27,8 +27,8 @@ use crate::{
pub use message::Message;
use step::{
BackupDescriptor, BackupMnemonic, DefineBitcoind, DefineDescriptor, Final, ImportDescriptor,
InternalBitcoindStep, ParticipateXpub, RecoverMnemonic, RegisterDescriptor,
SelectBitcoindTypeStep, Step, Welcome,
InternalBitcoindStep, RecoverMnemonic, RegisterDescriptor, SelectBitcoindTypeStep, ShareXpubs,
Step, Welcome,
};
pub struct Installer {
@ -155,17 +155,10 @@ impl Installer {
];
self.next()
}
Message::ParticipateWallet => {
Message::ShareXpubs => {
self.steps = vec![
Welcome::default().into(),
ParticipateXpub::new(self.network, self.signer.clone()).into(),
ImportDescriptor::new(self.network).into(),
BackupMnemonic::new(self.signer.clone()).into(),
RegisterDescriptor::new_import_wallet().into(),
SelectBitcoindTypeStep::new().into(),
InternalBitcoindStep::new(&self.context.data_dir).into(),
DefineBitcoind::new().into(),
Final::new().into(),
ShareXpubs::new(self.network, self.signer.clone()).into(),
];
self.next()
}

View File

@ -9,7 +9,7 @@ use liana::{
descriptors::{LianaDescriptor, LianaPolicy, PathInfo},
miniscript::{
bitcoin::{
bip32::{ChildNumber, DerivationPath, Fingerprint},
bip32::{DerivationPath, Fingerprint},
Network,
},
descriptor::{
@ -1064,7 +1064,7 @@ impl DescriptorEditModal for EditXpubModal {
}
}
fn default_derivation_path(network: Network) -> DerivationPath {
pub fn default_derivation_path(network: Network) -> DerivationPath {
DerivationPath::from_str({
if network == Network::Bitcoin {
"m/48'/0'/0'/2'"
@ -1077,7 +1077,7 @@ fn default_derivation_path(network: Network) -> DerivationPath {
/// LIANA_STANDARD_PATH: m/48'/0'/0'/2';
/// LIANA_TESTNET_STANDARD_PATH: m/48'/1'/0'/2';
async fn get_extended_pubkey(
pub async fn get_extended_pubkey(
hw: std::sync::Arc<dyn async_hwi::HWI + Send + Sync>,
fingerprint: Fingerprint,
network: Network,
@ -1095,178 +1095,6 @@ async fn get_extended_pubkey(
}))
}
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,
}
impl SignerXpubs {
fn new(signer: Arc<Mutex<Signer>>) -> Self {
Self {
signer,
xpubs: Vec::new(),
next_account: ChildNumber::from_hardened_idx(0).unwrap(),
}
}
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)
}
}
pub struct ParticipateXpub {
network: Network,
shared: bool,
hw_xpubs: Vec<HardwareWalletXpubs>,
xpubs_signer: SignerXpubs,
}
impl ParticipateXpub {
pub fn new(network: Network, signer: Arc<Mutex<Signer>>) -> Self {
Self {
network,
hw_xpubs: Vec::new(),
shared: false,
xpubs_signer: SignerXpubs::new(signer),
}
}
}
impl Step for ParticipateXpub {
// 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::UserActionDone(shared) => self.shared = shared,
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::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)) -> Element<Message> {
view::participate_xpub(
progress,
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(),
self.shared,
)
}
}
impl From<ParticipateXpub> for Box<dyn Step> {
fn from(s: ParticipateXpub) -> Box<dyn Step> {
Box::new(s)
}
}
pub struct ImportDescriptor {
network: Network,
imported_descriptor: form::Value<String>,

View File

@ -1,16 +1,16 @@
mod bitcoind;
mod descriptor;
mod mnemonic;
mod share_xpubs;
pub use bitcoind::{
DefineBitcoind, DownloadState, InstallState, InternalBitcoindStep, SelectBitcoindTypeStep,
};
pub use descriptor::{
BackupDescriptor, DefineDescriptor, ImportDescriptor, ParticipateXpub, RegisterDescriptor,
};
pub use descriptor::{BackupDescriptor, DefineDescriptor, ImportDescriptor, RegisterDescriptor};
pub use mnemonic::{BackupMnemonic, RecoverMnemonic};
pub use share_xpubs::ShareXpubs;
use std::path::PathBuf;

View File

@ -0,0 +1,192 @@
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::{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,
}
impl SignerXpubs {
fn new(signer: Arc<Mutex<Signer>>) -> Self {
Self {
signer,
xpubs: Vec::new(),
next_account: ChildNumber::from_hardened_idx(0).unwrap(),
}
}
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)
}
}
pub struct ShareXpubs {
network: Network,
shared: bool,
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(),
shared: false,
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::UserActionDone(shared) => self.shared = shared,
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::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)) -> Element<Message> {
view::share_xpubs(
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)
}
}

View File

@ -38,7 +38,32 @@ pub fn welcome<'a>() -> Element<'a, Message> {
Container::new(
Column::new()
.push(
Container::new(image::liana_brand_grey().width(Length::Fixed(200.0))).padding(100),
Container::new(
Row::new()
.align_items(Alignment::Center)
.push(
Container::new(image::liana_brand_grey().width(Length::Fixed(200.0)))
.width(Length::Fill),
)
.push(
Row::new()
.push(
button::secondary(
Some(icon::previous_icon()),
"Change network",
)
.width(Length::Fixed(200.0))
.on_press(Message::BackToLauncher),
)
.push(
button::secondary(None, "Share Xpubs")
.width(Length::Fixed(200.0))
.on_press(Message::ShareXpubs),
)
.spacing(20),
),
)
.padding(100),
)
.push(
Container::new(
@ -69,28 +94,6 @@ pub fn welcome<'a>() -> Element<'a, Message> {
)
.padding(20),
)
.push(
Container::new(
Column::new()
.spacing(20)
.align_items(Alignment::Center)
.push(
image::participate_in_new_wallet_icon()
.width(Length::Fixed(200.0)),
)
.push(
p1_regular("Participate in new wallet")
.style(color::GREY_3),
)
.push(
button::secondary(None, "Select")
.width(Length::Fixed(200.0))
.on_press(Message::ParticipateWallet),
)
.align_items(Alignment::Center),
)
.padding(20),
)
.push(
Container::new(
Column::new()
@ -101,7 +104,8 @@ pub fn welcome<'a>() -> Element<'a, Message> {
.width(Length::Fixed(100.0)),
)
.push(
p1_regular("Restore a wallet").style(color::GREY_3),
p1_regular("Add an existing wallet")
.style(color::GREY_3),
)
.push(
button::secondary(None, "Select")
@ -113,11 +117,6 @@ pub fn welcome<'a>() -> Element<'a, Message> {
.padding(20),
),
)
.push(
button::secondary(Some(icon::previous_icon()), "Change network")
.width(Length::Fixed(200.0))
.on_press(Message::BackToLauncher),
)
.push(Space::with_height(Length::Fixed(100.0)))
.spacing(50)
.align_items(Alignment::Center),
@ -566,42 +565,24 @@ pub fn hardware_wallet_xpubs<'a>(
.into()
}
pub fn participate_xpub<'a>(
progress: (usize, usize),
pub fn share_xpubs<'a>(
hws: Vec<Element<'a, Message>>,
signer: Element<'a, Message>,
shared: bool,
) -> Element<'a, Message> {
layout(
progress,
(0, 0),
"Share your public keys",
Column::new()
.push(
Column::new()
.push(
Container::new(
text("Generate an extended public key by selecting a signing device:")
.bold(),
)
.width(Length::Fill),
)
.spacing(10)
.push(Column::with_children(hws).spacing(10))
.push(signer)
.width(Length::Fill),
Container::new(
text("Generate an extended public key by selecting a signing device:").bold(),
)
.width(Length::Fill),
)
.push(
checkbox("I have shared my extended public key", shared)
.on_toggle(Message::UserActionDone),
)
.push(if shared {
button::primary(None, "Next")
.width(Length::Fixed(200.0))
.on_press(Message::Next)
} else {
button::primary(None, "Next").width(Length::Fixed(200.0))
})
.spacing(50),
.spacing(10)
.push(Column::with_children(hws).spacing(10))
.push(signer)
.width(Length::Fill),
true,
Some(Message::Previous),
)
@ -1951,11 +1932,15 @@ fn layout<'a>(
.center_x(),
)
.push(Container::new(h3(title)).width(Length::FillPortion(8)))
.push(
Container::new(text(format!("{} | {}", progress.0, progress.1)))
.width(Length::FillPortion(2))
.center_x(),
),
.push_maybe(if progress.1 > 0 {
Some(
Container::new(text(format!("{} | {}", progress.0, progress.1)))
.width(Length::FillPortion(2))
.center_x(),
)
} else {
None
}),
)
.push(
Row::new()