gui: update liana with multipath support
This commit is contained in:
parent
045182e7ea
commit
35dbb47bc1
1092
gui/Cargo.lock
generated
1092
gui/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -71,13 +71,13 @@ impl App {
|
||||
menu::Menu::Home => Home::new(self.wallet.clone(), &self.cache.coins).into(),
|
||||
menu::Menu::Coins => CoinsPanel::new(
|
||||
&self.cache.coins,
|
||||
self.wallet.main_descriptor.timelock_value(),
|
||||
self.wallet.main_descriptor.first_timelock_value(),
|
||||
)
|
||||
.into(),
|
||||
menu::Menu::Recovery => RecoveryPanel::new(
|
||||
self.wallet.clone(),
|
||||
&self.cache.coins,
|
||||
self.wallet.main_descriptor.timelock_value(),
|
||||
self.wallet.main_descriptor.first_timelock_value(),
|
||||
self.cache.blockheight as u32,
|
||||
)
|
||||
.into(),
|
||||
|
||||
@ -15,11 +15,11 @@ pub struct CoinsPanel {
|
||||
selected: Vec<usize>,
|
||||
warning: Option<Error>,
|
||||
/// timelock value to pass for the heir to consume a coin.
|
||||
timelock: u32,
|
||||
timelock: u16,
|
||||
}
|
||||
|
||||
impl CoinsPanel {
|
||||
pub fn new(coins: &[Coin], timelock: u32) -> Self {
|
||||
pub fn new(coins: &[Coin], timelock: u16) -> Self {
|
||||
let mut panel = Self {
|
||||
coins: Vec::new(),
|
||||
selected: Vec::new(),
|
||||
|
||||
@ -124,12 +124,12 @@ impl State for Home {
|
||||
for coin in coins {
|
||||
if coin.spend_info.is_none() && coin.block_height.is_some() {
|
||||
self.balance += coin.amount;
|
||||
let timelock = self.wallet.main_descriptor.timelock_value();
|
||||
let timelock = self.wallet.main_descriptor.first_timelock_value();
|
||||
let seq = remaining_sequence(&coin, cache.blockheight as u32, timelock);
|
||||
if seq == 0 {
|
||||
recovery_alert.0 += coin.amount;
|
||||
recovery_alert.1 += 1;
|
||||
} else if seq < timelock * 10 / 100 {
|
||||
} else if seq < timelock as u32 * 10 / 100 {
|
||||
recovery_warning.0 += coin.amount;
|
||||
recovery_warning.1 += 1;
|
||||
}
|
||||
|
||||
@ -33,11 +33,11 @@ pub struct RecoveryPanel {
|
||||
recipient: form::Value<String>,
|
||||
generated: Option<detail::SpendTxState>,
|
||||
/// timelock value to pass for the heir to consume a coin.
|
||||
timelock: u32,
|
||||
timelock: u16,
|
||||
}
|
||||
|
||||
impl RecoveryPanel {
|
||||
pub fn new(wallet: Arc<Wallet>, coins: &[Coin], timelock: u32, blockheight: u32) -> Self {
|
||||
pub fn new(wallet: Arc<Wallet>, coins: &[Coin], timelock: u16, blockheight: u32) -> Self {
|
||||
let mut locked_coins = (0, Amount::from_sat(0));
|
||||
let mut recoverable_coins = (0, Amount::from_sat(0));
|
||||
for coin in coins {
|
||||
|
||||
@ -302,13 +302,13 @@ impl Setting for RescanSetting {
|
||||
}
|
||||
}
|
||||
view::SettingsEditMessage::Confirm => {
|
||||
let date_time = NaiveDate::from_ymd(
|
||||
let date_time = NaiveDate::from_ymd_opt(
|
||||
i32::from_str(&self.year.value).unwrap_or(1),
|
||||
u32::from_str(&self.month.value).unwrap_or(1),
|
||||
u32::from_str(&self.day.value).unwrap_or(1),
|
||||
)
|
||||
.and_hms(0, 0, 0);
|
||||
let t = date_time.timestamp() as u32;
|
||||
.unwrap();
|
||||
let t = date_time.and_hms_opt(0, 0, 0).unwrap().timestamp() as u32;
|
||||
self.processing = true;
|
||||
info!("Asking deamon to rescan with timestamp: {}", t);
|
||||
return Command::perform(
|
||||
|
||||
@ -138,7 +138,7 @@ pub struct CreateSpendPanel {
|
||||
impl CreateSpendPanel {
|
||||
pub fn new(wallet: Arc<Wallet>, coins: &[Coin], blockheight: u32) -> Self {
|
||||
let descriptor = wallet.main_descriptor.clone();
|
||||
let timelock = descriptor.timelock_value();
|
||||
let timelock = descriptor.first_timelock_value();
|
||||
Self {
|
||||
draft: step::TransactionDraft::default(),
|
||||
current: 0,
|
||||
|
||||
@ -224,7 +224,7 @@ impl Recipient {
|
||||
|
||||
pub struct ChooseCoins {
|
||||
descriptor: LianaDescriptor,
|
||||
timelock: u32,
|
||||
timelock: u16,
|
||||
coins: Vec<(Coin, bool)>,
|
||||
recipients: Vec<(Address, Amount)>,
|
||||
|
||||
@ -238,7 +238,7 @@ impl ChooseCoins {
|
||||
pub fn new(
|
||||
descriptor: LianaDescriptor,
|
||||
coins: Vec<Coin>,
|
||||
timelock: u32,
|
||||
timelock: u16,
|
||||
blockheight: u32,
|
||||
) -> Self {
|
||||
let mut coins: Vec<(Coin, bool)> = coins
|
||||
|
||||
@ -19,7 +19,7 @@ use crate::{
|
||||
pub fn coins_view<'a>(
|
||||
cache: &Cache,
|
||||
coins: &'a [Coin],
|
||||
timelock: u32,
|
||||
timelock: u16,
|
||||
selected: &[usize],
|
||||
) -> Element<'a, Message> {
|
||||
Column::new()
|
||||
@ -55,7 +55,7 @@ pub fn coins_view<'a>(
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
fn coin_list_view(
|
||||
coin: &Coin,
|
||||
timelock: u32,
|
||||
timelock: u16,
|
||||
blockheight: u32,
|
||||
index: usize,
|
||||
collapsed: bool,
|
||||
@ -84,7 +84,7 @@ fn coin_list_view(
|
||||
)
|
||||
.align_items(Alignment::Center),
|
||||
))
|
||||
} else if seq < timelock * 10 / 100 {
|
||||
} else if seq < timelock as u32 * 10 / 100 {
|
||||
Some(Container::new(
|
||||
Row::new()
|
||||
.spacing(5)
|
||||
@ -138,7 +138,7 @@ fn coin_list_view(
|
||||
.spacing(5)
|
||||
.push_maybe(if coin.spend_info.is_none() {
|
||||
if let Some(b) = coin.block_height {
|
||||
if blockheight > b as u32 + timelock {
|
||||
if blockheight > b as u32 + timelock as u32 {
|
||||
Some(Container::new(
|
||||
text("The recovery path is available")
|
||||
.bold()
|
||||
@ -147,7 +147,7 @@ fn coin_list_view(
|
||||
))
|
||||
} else {
|
||||
Some(Container::new(
|
||||
text(format!("The recovery path will be available in {} blocks", b as u32 + timelock - blockheight))
|
||||
text(format!("The recovery path will be available in {} blocks", b as u32 + timelock as u32 - blockheight))
|
||||
.bold()
|
||||
.small(),
|
||||
))
|
||||
|
||||
@ -127,8 +127,11 @@ fn event_list_view<'a>(i: usize, event: &HistoryTransaction) -> Element<'a, Mess
|
||||
})
|
||||
.push(if let Some(t) = event.time {
|
||||
Container::new(
|
||||
text(format!("{}", NaiveDateTime::from_timestamp(t as i64, 0)))
|
||||
.small(),
|
||||
text(format!(
|
||||
"{}",
|
||||
NaiveDateTime::from_timestamp_opt(t as i64, 0).unwrap(),
|
||||
))
|
||||
.small(),
|
||||
)
|
||||
} else {
|
||||
badge::unconfirmed()
|
||||
@ -186,7 +189,7 @@ pub fn event_view<'a>(cache: &Cache, event: &'a HistoryTransaction) -> Element<'
|
||||
.push(card::simple(
|
||||
Column::new()
|
||||
.push_maybe(event.time.map(|t| {
|
||||
let date = NaiveDateTime::from_timestamp(t as i64, 0);
|
||||
let date = NaiveDateTime::from_timestamp_opt(t as i64, 0).unwrap();
|
||||
Row::new()
|
||||
.width(Length::Fill)
|
||||
.push(Container::new(text("Date:").bold()).width(Length::Fill))
|
||||
|
||||
@ -203,7 +203,7 @@ fn spend_header<'a>(tx: &SpendTx) -> Element<'a, Message> {
|
||||
.push(
|
||||
Row::new()
|
||||
.push(badge::Badge::new(icon::send_icon()).style(theme::Badge::Standard))
|
||||
.push(if tx.sigs.recovery_path().is_some() {
|
||||
.push(if !tx.sigs.recovery_paths().is_empty() {
|
||||
text("Recovery").bold()
|
||||
} else {
|
||||
text("Spend").bold()
|
||||
@ -363,8 +363,8 @@ pub fn signatures<'a>(
|
||||
Column::new()
|
||||
.padding(15)
|
||||
.spacing(10)
|
||||
.push(text(if tx.sigs.recovery_path().is_some() {
|
||||
"2 spending paths available. Finalizing this transaction requires either:"
|
||||
.push(text(if !tx.sigs.recovery_paths().is_empty() {
|
||||
"Multiple spending paths available. Finalizing this transaction requires either:"
|
||||
} else {
|
||||
"1 spending path available. Finalizing this transaction requires:"
|
||||
}))
|
||||
@ -373,9 +373,9 @@ pub fn signatures<'a>(
|
||||
tx.sigs.primary_path(),
|
||||
keys_aliases,
|
||||
))
|
||||
.push_maybe(tx.sigs.recovery_path().as_ref().map(|path| {
|
||||
let (_, keys) = desc_info.recovery_path();
|
||||
path_view(keys, path, keys_aliases)
|
||||
.push(tx.sigs.recovery_paths().iter().fold(Column::new().spacing(10), |col, (seq, path)| {
|
||||
let keys = &desc_info.recovery_paths()[seq];
|
||||
col.push(path_view(keys, path, keys_aliases))
|
||||
})),
|
||||
),
|
||||
)
|
||||
|
||||
@ -110,30 +110,12 @@ fn spend_tx_list_view<'a>(i: usize, tx: &SpendTx) -> Element<'a, Message> {
|
||||
.push(
|
||||
Row::new()
|
||||
.push(badge::spend())
|
||||
.push(if let Some(sigs) = tx.sigs.recovery_path() {
|
||||
Row::new()
|
||||
.spacing(10)
|
||||
.align_items(Alignment::Center)
|
||||
.push(
|
||||
Row::new()
|
||||
.spacing(5)
|
||||
.align_items(Alignment::Center)
|
||||
.push(text(format!(
|
||||
"{}/{}",
|
||||
if sigs.sigs_count <= sigs.threshold {
|
||||
sigs.sigs_count
|
||||
} else {
|
||||
sigs.threshold
|
||||
},
|
||||
sigs.threshold
|
||||
)))
|
||||
.push(icon::key_icon()),
|
||||
)
|
||||
.push(
|
||||
Container::new(text(" Recovery ").small())
|
||||
.padding(3)
|
||||
.style(theme::Container::Pill(theme::Pill::Simple)),
|
||||
)
|
||||
.push(if !tx.sigs.recovery_paths().is_empty() {
|
||||
Row::new().push(
|
||||
Container::new(text(" Recovery ").small())
|
||||
.padding(3)
|
||||
.style(theme::Container::Pill(theme::Pill::Simple)),
|
||||
)
|
||||
} else {
|
||||
let sigs = tx.sigs.primary_path();
|
||||
Row::new()
|
||||
|
||||
@ -120,7 +120,7 @@ pub fn recipient_view<'a>(
|
||||
|
||||
pub fn choose_coins_view<'a>(
|
||||
cache: &Cache,
|
||||
timelock: u32,
|
||||
timelock: u16,
|
||||
coins: &[(Coin, bool)],
|
||||
amount_left: Option<&Amount>,
|
||||
feerate: &form::Value<String>,
|
||||
@ -192,7 +192,7 @@ pub fn choose_coins_view<'a>(
|
||||
fn coin_list_view<'a>(
|
||||
i: usize,
|
||||
coin: &Coin,
|
||||
timelock: u32,
|
||||
timelock: u16,
|
||||
blockheight: u32,
|
||||
selected: bool,
|
||||
) -> Element<'a, Message> {
|
||||
@ -223,7 +223,7 @@ fn coin_list_view<'a>(
|
||||
)
|
||||
.align_items(Alignment::Center),
|
||||
))
|
||||
} else if seq < timelock * 10 / 100 {
|
||||
} else if seq < timelock as u32 * 10 / 100 {
|
||||
Some(Container::new(
|
||||
Row::new()
|
||||
.spacing(5)
|
||||
|
||||
@ -55,8 +55,10 @@ impl Wallet {
|
||||
for (fingerprint, _) in info.primary_path().thresh_origins().1.iter() {
|
||||
descriptor_keys.insert(*fingerprint);
|
||||
}
|
||||
for (fingerprint, _) in info.recovery_path().1.thresh_origins().1.iter() {
|
||||
descriptor_keys.insert(*fingerprint);
|
||||
for path in info.recovery_paths().values() {
|
||||
for (fingerprint, _) in path.thresh_origins().1.iter() {
|
||||
descriptor_keys.insert(*fingerprint);
|
||||
}
|
||||
}
|
||||
descriptor_keys
|
||||
}
|
||||
|
||||
@ -201,7 +201,7 @@ impl Daemon for EmbeddedDaemon {
|
||||
.read()
|
||||
.unwrap()
|
||||
.control
|
||||
.create_recovery(address, feerate_vb)
|
||||
.create_recovery(address, feerate_vb, None)
|
||||
.map_err(|e| DaemonError::Unexpected(e.to_string()))
|
||||
.map(|res| res.psbt)
|
||||
}
|
||||
|
||||
@ -9,15 +9,15 @@ pub use liana::{
|
||||
|
||||
pub type Coin = ListCoinsEntry;
|
||||
|
||||
pub fn remaining_sequence(coin: &Coin, blockheight: u32, timelock: u32) -> u32 {
|
||||
pub fn remaining_sequence(coin: &Coin, blockheight: u32, timelock: u16) -> u32 {
|
||||
if let Some(coin_blockheight) = coin.block_height {
|
||||
if blockheight > coin_blockheight as u32 + timelock {
|
||||
if blockheight > coin_blockheight as u32 + timelock as u32 {
|
||||
0
|
||||
} else {
|
||||
coin_blockheight as u32 + timelock - blockheight
|
||||
coin_blockheight as u32 + timelock as u32 - blockheight
|
||||
}
|
||||
} else {
|
||||
timelock
|
||||
timelock as u32
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,12 +89,10 @@ impl SpendTx {
|
||||
if path.sigs_count >= path.threshold {
|
||||
return Some(path);
|
||||
}
|
||||
if let Some(path) = self.sigs.recovery_path() {
|
||||
if path.sigs_count >= path.threshold {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
None
|
||||
self.sigs
|
||||
.recovery_paths()
|
||||
.values()
|
||||
.find(|&path| path.sigs_count >= path.threshold)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
@ -424,7 +424,10 @@ impl Step for DefineDescriptor {
|
||||
PathInfo::Multi(self.recovery_threshold, recovery_keys)
|
||||
};
|
||||
|
||||
let policy = match LianaPolicy::new(spending_keys, recovery_keys, sequence.unwrap()) {
|
||||
let mut recovery_paths = BTreeMap::new();
|
||||
recovery_paths.insert(sequence.unwrap(), recovery_keys);
|
||||
|
||||
let policy = match LianaPolicy::new(spending_keys, recovery_paths) {
|
||||
Ok(policy) => policy,
|
||||
Err(e) => {
|
||||
self.error = Some(e.to_string());
|
||||
|
||||
@ -136,8 +136,10 @@ impl Step for RecoverMnemonic {
|
||||
for (fingerprint, _) in info.primary_path().thresh_origins().1.iter() {
|
||||
descriptor_keys.insert(*fingerprint);
|
||||
}
|
||||
for (fingerprint, _) in info.recovery_path().1.thresh_origins().1.iter() {
|
||||
descriptor_keys.insert(*fingerprint);
|
||||
for (_, path) in info.recovery_paths().iter() {
|
||||
for (fingerprint, _) in path.thresh_origins().1.iter() {
|
||||
descriptor_keys.insert(*fingerprint);
|
||||
}
|
||||
}
|
||||
if !descriptor_keys.contains(&fingerprint) {
|
||||
self.error =
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user