Merge #1675: Disable signing devices that should not or cannot spend
74087c37cf0b0796e794f98bebb11167ca243969 refactor: map fingerprint only once (Thomas Ballivet) 86c4896534c1ac169a3352651b0e27170e8cb06f Disable signing devices that are not related to the actual spending path (Thomas Ballivet) Pull request description: Inspired by #1607 this shoud closes #1041. Notice that I added the wanted logic also for the recovery spending. That means you can only select a signing device related to the primary path for usual spend. But for recovery spending you can only select a signing device related to the recovery_path. You can see here both examples :   ACKs for top commit: jp1ac4: tACK 74087c37cf0b0796e794f98bebb11167ca243969. Tree-SHA512: 93e452ca9f56e214f308cbaf3cdc9ffb52301e64fded860d1a961e97df21270e18f994cef83358459981ddc3029c89183d23bc33bab863253cfa2a2c5a9d9554
This commit is contained in:
commit
ba17782604
@ -187,6 +187,7 @@ impl PsbtState {
|
||||
cache.datadir_path.clone(),
|
||||
cache.network,
|
||||
self.saved,
|
||||
self.tx.recovery_timelock(),
|
||||
);
|
||||
let cmd = modal.load(daemon);
|
||||
self.modal = Some(PsbtModal::Sign(modal));
|
||||
@ -438,6 +439,7 @@ pub struct SignModal {
|
||||
signed: HashSet<Fingerprint>,
|
||||
is_saved: bool,
|
||||
display_modal: bool,
|
||||
recovery_timelock: Option<u16>,
|
||||
}
|
||||
|
||||
impl SignModal {
|
||||
@ -447,6 +449,7 @@ impl SignModal {
|
||||
datadir_path: LianaDirectory,
|
||||
network: Network,
|
||||
is_saved: bool,
|
||||
recovery_timelock: Option<u16>,
|
||||
) -> Self {
|
||||
Self {
|
||||
signing: HashSet::new(),
|
||||
@ -456,6 +459,7 @@ impl SignModal {
|
||||
signed,
|
||||
is_saved,
|
||||
display_modal: true,
|
||||
recovery_timelock,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -565,6 +569,7 @@ impl Modal for SignModal {
|
||||
view::psbt::sign_action(
|
||||
self.error.as_ref(),
|
||||
&self.hws.list,
|
||||
&self.wallet.main_descriptor,
|
||||
self.wallet.signer.as_ref().map(|s| s.fingerprint()),
|
||||
self.wallet
|
||||
.signer
|
||||
@ -572,6 +577,7 @@ impl Modal for SignModal {
|
||||
.and_then(|signer| self.wallet.keys_aliases.get(&signer.fingerprint)),
|
||||
&self.signed,
|
||||
&self.signing,
|
||||
self.recovery_timelock,
|
||||
),
|
||||
)
|
||||
.on_blur(Some(view::Message::Spend(view::SpendTxMessage::Cancel)))
|
||||
|
||||
@ -13,6 +13,7 @@ pub fn hw_list_view(
|
||||
hw: &HardwareWallet,
|
||||
signed: bool,
|
||||
signing: bool,
|
||||
can_sign: bool,
|
||||
) -> Element<Message> {
|
||||
let mut bttn = Button::new(match hw {
|
||||
HardwareWallet::Supported {
|
||||
@ -40,6 +41,8 @@ pub fn hw_list_view(
|
||||
alias.as_ref(),
|
||||
"The wallet descriptor is not registered on the device.\n You can register it in the settings.",
|
||||
)
|
||||
} else if !can_sign {
|
||||
hw::disabled_hardware_wallet(kind, version.as_ref(), fingerprint, "This signing device is not part of this spending path.")
|
||||
} else {
|
||||
hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
|
||||
}
|
||||
@ -71,7 +74,7 @@ pub fn hw_list_view(
|
||||
})
|
||||
.style(theme::button::secondary)
|
||||
.width(Length::Fill);
|
||||
if !signing {
|
||||
if can_sign && !signing {
|
||||
if let HardwareWallet::Supported { registered, .. } = hw {
|
||||
if *registered != Some(false) {
|
||||
bttn = bttn.on_press(Message::SelectHardwareWallet(i));
|
||||
|
||||
@ -5,6 +5,7 @@ use iced::{
|
||||
Alignment, Length,
|
||||
};
|
||||
|
||||
use liana::descriptors::LianaDescriptor;
|
||||
use liana::{
|
||||
descriptors::{LianaPolicy, PathInfo, PathSpendInfo},
|
||||
miniscript::bitcoin::{
|
||||
@ -1014,13 +1015,16 @@ fn change_view(output: &TxOut, network: Network) -> Element<Message> {
|
||||
.into()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn sign_action<'a>(
|
||||
warning: Option<&Error>,
|
||||
hws: &'a [HardwareWallet],
|
||||
descriptor: &LianaDescriptor,
|
||||
signer: Option<Fingerprint>,
|
||||
signer_alias: Option<&'a String>,
|
||||
signed: &HashSet<Fingerprint>,
|
||||
signing: &HashSet<Fingerprint>,
|
||||
recovery_timelock: Option<u16>,
|
||||
) -> Element<'a, Message> {
|
||||
Column::new()
|
||||
.push_maybe(warning.map(|w| warn(Some(w))))
|
||||
@ -1037,29 +1041,37 @@ pub fn sign_action<'a>(
|
||||
.push(hws.iter().enumerate().fold(
|
||||
Column::new().spacing(10),
|
||||
|col, (i, hw)| {
|
||||
col.push(hw_list_view(
|
||||
i,
|
||||
hw,
|
||||
hw.fingerprint()
|
||||
.map(|f| signed.contains(&f))
|
||||
.unwrap_or(false),
|
||||
hw.fingerprint()
|
||||
.map(|f| signing.contains(&f))
|
||||
.unwrap_or(false),
|
||||
))
|
||||
let (signed, signing, can_sign) =
|
||||
hw.fingerprint().map_or((false, false, false), |f| {
|
||||
(
|
||||
signed.contains(&f),
|
||||
signing.contains(&f),
|
||||
descriptor
|
||||
.contains_fingerprint_in_path(f, recovery_timelock),
|
||||
)
|
||||
});
|
||||
col.push(hw_list_view(i, hw, signed, signing, can_sign))
|
||||
},
|
||||
))
|
||||
.push_maybe(signer.map(|fingerprint| {
|
||||
Button::new(if signed.contains(&fingerprint) {
|
||||
hw::sign_success_hot_signer(fingerprint, signer_alias)
|
||||
} else {
|
||||
hw::hot_signer(fingerprint, signer_alias)
|
||||
.push_maybe({
|
||||
signer.map(|fingerprint| {
|
||||
let can_sign = descriptor
|
||||
.contains_fingerprint_in_path(fingerprint, recovery_timelock);
|
||||
let btn = Button::new(if signed.contains(&fingerprint) {
|
||||
hw::sign_success_hot_signer(fingerprint, signer_alias)
|
||||
} else {
|
||||
hw::hot_signer(fingerprint, signer_alias, can_sign)
|
||||
})
|
||||
.padding(10)
|
||||
.style(theme::button::secondary)
|
||||
.width(Length::Fill);
|
||||
if can_sign {
|
||||
btn.on_press(Message::Spend(SpendTxMessage::SelectHotSigner))
|
||||
} else {
|
||||
btn
|
||||
}
|
||||
})
|
||||
.on_press(Message::Spend(SpendTxMessage::SelectHotSigner))
|
||||
.padding(10)
|
||||
.style(theme::button::secondary)
|
||||
.width(Length::Fill)
|
||||
}))
|
||||
})
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.spacing(20)
|
||||
|
||||
@ -205,6 +205,10 @@ impl SpendTx {
|
||||
.find(|&path| path.sigs_count >= path.threshold)
|
||||
}
|
||||
|
||||
pub fn recovery_timelock(&self) -> Option<u16> {
|
||||
self.sigs.recovery_paths().keys().max().cloned()
|
||||
}
|
||||
|
||||
pub fn signers(&self) -> HashSet<Fingerprint> {
|
||||
let mut signers = HashSet::new();
|
||||
for fg in self.sigs.primary_path().signed_pubkeys.keys() {
|
||||
|
||||
@ -125,10 +125,11 @@ pub fn unimplemented_method_hardware_wallet<'a, T: 'a, K: Display, V: Display, F
|
||||
.width(Length::Fill)
|
||||
}
|
||||
|
||||
pub fn unrelated_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
|
||||
pub fn disabled_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
|
||||
kind: K,
|
||||
version: Option<V>,
|
||||
fingerprint: F,
|
||||
label: &'static str,
|
||||
) -> Container<'a, T> {
|
||||
let key = column(vec![
|
||||
text::p1_regular(format!("#{}", fingerprint)).into(),
|
||||
@ -144,9 +145,7 @@ pub fn unrelated_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
|
||||
.push(key)
|
||||
.push(Space::with_width(15))
|
||||
.push(Space::with_width(Length::Fill))
|
||||
.push(text::text(
|
||||
"This signing device is not related to this Liana wallet.",
|
||||
))
|
||||
.push(text::text(label))
|
||||
.push(Space::with_width(Length::Fill))
|
||||
.align_y(Vertical::Center),
|
||||
)
|
||||
@ -157,6 +156,19 @@ pub fn unrelated_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
|
||||
.width(Length::Fill)
|
||||
}
|
||||
|
||||
pub fn unrelated_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
|
||||
kind: K,
|
||||
version: Option<V>,
|
||||
fingerprint: F,
|
||||
) -> Container<'a, T> {
|
||||
disabled_hardware_wallet(
|
||||
kind,
|
||||
version,
|
||||
fingerprint,
|
||||
"This signing device is not related to this Liana wallet.",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn processing_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
|
||||
kind: K,
|
||||
version: Option<V>,
|
||||
@ -484,20 +496,31 @@ pub fn unselected_hot_signer<'a, T: 'a, F: Display>(
|
||||
pub fn hot_signer<'a, T: 'a, F: Display>(
|
||||
fingerprint: F,
|
||||
alias: Option<impl Into<Cow<'a, str>> + Display>,
|
||||
can_sign: bool,
|
||||
) -> Container<'a, T> {
|
||||
Container::new(
|
||||
column(vec![
|
||||
Row::new()
|
||||
.spacing(5)
|
||||
.push_maybe(alias.map(|a| text::p1_bold(a)))
|
||||
.push(text::p1_regular(format!("#{}", fingerprint)))
|
||||
.into(),
|
||||
Row::new()
|
||||
.spacing(5)
|
||||
.push(text::caption("This computer"))
|
||||
.into(),
|
||||
])
|
||||
.width(Length::Fill),
|
||||
Row::new()
|
||||
.push(column(vec![
|
||||
Row::new()
|
||||
.spacing(5)
|
||||
.push_maybe(alias.map(|a| text::p1_bold(a)))
|
||||
.push(text::p1_regular(format!("#{}", fingerprint)))
|
||||
.into(),
|
||||
Row::new()
|
||||
.spacing(5)
|
||||
.push(text::caption("This computer"))
|
||||
.into(),
|
||||
]))
|
||||
.push(Space::with_width(Length::Fixed(20.0)))
|
||||
.push_maybe(if !can_sign {
|
||||
Some(text::text(
|
||||
"This hot signer is not part of this spending path.",
|
||||
))
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.push(Space::with_width(Length::Fill))
|
||||
.align_y(Vertical::Center),
|
||||
)
|
||||
.padding(10)
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ use miniscript::{
|
||||
RelLockTime, ScriptContext, Threshold,
|
||||
};
|
||||
|
||||
use miniscript::bitcoin::bip32::Fingerprint;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
convert::TryFrom,
|
||||
@ -368,6 +369,11 @@ impl PathInfo {
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
/// Determine whether the fingerprint is part of this path.
|
||||
pub fn contains_fingerprint(&self, fingerprint: Fingerprint) -> bool {
|
||||
self.thresh_origins().1.contains_key(&fingerprint)
|
||||
}
|
||||
}
|
||||
|
||||
// See
|
||||
|
||||
@ -217,6 +217,39 @@ impl LianaDescriptor {
|
||||
.for_any_key(|k| k.master_fingerprint() == fg)
|
||||
}
|
||||
|
||||
/// Determine whether the fingerprint is part of a specific path of this descriptor.
|
||||
/// If recovery_timelock is None, checks in the primary path.
|
||||
/// If recovery_timelock is Some(timelock), checks in the recovery path with specified timelock.
|
||||
pub fn contains_fingerprint_in_path(
|
||||
&self,
|
||||
fingerprint: Fingerprint,
|
||||
recovery_timelock: Option<u16>,
|
||||
) -> bool {
|
||||
match recovery_timelock {
|
||||
None => self.contains_fingerprint_in_primary_path(fingerprint),
|
||||
Some(timelock) => self.contains_fingerprint_in_recovery_path(fingerprint, timelock),
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine whether the fingerprint is part of the primary path of this descriptor.
|
||||
fn contains_fingerprint_in_primary_path(&self, fingerprint: Fingerprint) -> bool {
|
||||
self.policy().primary_path.contains_fingerprint(fingerprint)
|
||||
}
|
||||
|
||||
/// Determine whether the fingerprint is part of the recovery path of this descriptor for the
|
||||
/// specified timelock.
|
||||
fn contains_fingerprint_in_recovery_path(
|
||||
&self,
|
||||
fingerprint: Fingerprint,
|
||||
recovery_timelock: u16,
|
||||
) -> bool {
|
||||
self.policy()
|
||||
.recovery_paths
|
||||
.get(&recovery_timelock)
|
||||
.map(|path_info| path_info.contains_fingerprint(fingerprint))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Get the descriptor for receiving addresses.
|
||||
pub fn receive_descriptor(&self) -> &SinglePathLianaDesc {
|
||||
&self.receive_desc
|
||||
@ -751,9 +784,9 @@ impl DerivedSinglePathLianaDesc {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use bitcoin::{hashes::Hash, Sequence};
|
||||
|
||||
use crate::signer::HotSigner;
|
||||
use bitcoin::{hashes::Hash, Sequence};
|
||||
use miniscript::bitcoin::bip32::Fingerprint;
|
||||
|
||||
fn random_desc_key(
|
||||
secp: &secp256k1::Secp256k1<impl secp256k1::Signing>,
|
||||
@ -2210,13 +2243,120 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn descriptor_contains_fingerprint() {
|
||||
fn descriptor_contains_fingerprint_in_primary_path_multi() {
|
||||
let descr = LianaDescriptor::from_str("wsh(or_d(multi(3,[aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/0/<0;1>/*,[aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/0/<0;1>/*,[aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/0/<0;1>/*),and_v(v:thresh(2,pkh([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/1/<0;1>/*),a:pkh([aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/1/<0;1>/*),a:pkh([aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/1/<0;1>/*)),older(26352))))").unwrap();
|
||||
|
||||
assert!(descr.contains_fingerprint(Fingerprint::from_str("aabb0011").unwrap()));
|
||||
assert!(descr.contains_fingerprint(Fingerprint::from_str("aabb0012").unwrap()));
|
||||
assert!(descr.contains_fingerprint(Fingerprint::from_str("aabb0013").unwrap()));
|
||||
assert!(!descr.contains_fingerprint(Fingerprint::from_str("aabb0014").unwrap()));
|
||||
assert!(
|
||||
descr.contains_fingerprint_in_primary_path(Fingerprint::from_str("aabb0011").unwrap())
|
||||
);
|
||||
assert!(
|
||||
descr.contains_fingerprint_in_primary_path(Fingerprint::from_str("aabb0012").unwrap())
|
||||
);
|
||||
assert!(
|
||||
descr.contains_fingerprint_in_primary_path(Fingerprint::from_str("aabb0013").unwrap())
|
||||
);
|
||||
assert!(
|
||||
!descr.contains_fingerprint_in_primary_path(Fingerprint::from_str("aabb0014").unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn descriptor_contains_fingerprint_in_primary_path_single_key() {
|
||||
let descr = LianaDescriptor::from_str("wsh(or_d(pkh([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/0/<0;1>/*),and_v(v:thresh(1,pkh([bbcc2233/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/1/<0;1>/*)),older(26352))))").unwrap();
|
||||
|
||||
assert!(
|
||||
descr.contains_fingerprint_in_primary_path(Fingerprint::from_str("aabb0011").unwrap())
|
||||
);
|
||||
assert!(
|
||||
!descr.contains_fingerprint_in_primary_path(Fingerprint::from_str("bbcc2233").unwrap())
|
||||
);
|
||||
assert!(
|
||||
!descr.contains_fingerprint_in_primary_path(Fingerprint::from_str("ddeeff00").unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn descriptor_contains_fingerprint_in_recovery_path_multi() {
|
||||
let descr = LianaDescriptor::from_str("wsh(or_d(multi(3,[aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/0/<0;1>/*,[aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/0/<0;1>/*,[aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/0/<0;1>/*),and_v(v:thresh(2,pkh([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/1/<0;1>/*),a:pkh([aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/1/<0;1>/*),a:pkh([aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/1/<0;1>/*)),older(26352))))").unwrap();
|
||||
|
||||
assert!(descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("aabb0011").unwrap(),
|
||||
26352
|
||||
));
|
||||
assert!(descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("aabb0012").unwrap(),
|
||||
26352
|
||||
));
|
||||
assert!(descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("aabb0013").unwrap(),
|
||||
26352
|
||||
));
|
||||
assert!(!descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("aabb0013").unwrap(),
|
||||
1000
|
||||
));
|
||||
assert!(!descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("aabb0014").unwrap(),
|
||||
26352
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn descriptor_contains_fingerprint_in_recovery_path_single_key() {
|
||||
let descr = LianaDescriptor::from_str("wsh(or_d(pkh([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/0/<0;1>/*),and_v(v:thresh(1,pkh([bbcc2233/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/1/<0;1>/*)),older(26352))))").unwrap();
|
||||
|
||||
assert!(!descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("aabb0011").unwrap(),
|
||||
26352
|
||||
));
|
||||
assert!(descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("bbcc2233").unwrap(),
|
||||
26352
|
||||
));
|
||||
assert!(!descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("ddeeff00").unwrap(),
|
||||
26352
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn descriptor_contains_fingerprint_in_recovery_path_multiple_keys() {
|
||||
let descr = LianaDescriptor::from_str("wsh(or_d(c:or_i(and_v(v:older(38305),and_v(v:pkh([d6bba22a/84'/1'/0']tpubDCwMfgJWBGfJuZFmgTAP9qdSwJeC4fEKaYXQx7CNiTzsB5WrpVSmySdPFnKDu8ChWZweYNh7MoAoFsCNY7gTRFSGtDYbG9s6vAKNKzT1Hii/<0;1>/*),pk_h([e9e6c583/48'/1'/0'/2']tpubDEWn2LRKdyREaweKHxj7XzSjcxXGTVbFkL5Qi5AWsJzGvN28cKQwGqCND9TP6EPtPaE13eK9SnyuiQ4qsfy5UuGD3p32Ew36mWfKmYCJRcz/<0;1>/*))),pk_k([de8abde2/48'/1'/0'/2']tpubDES5ZQEwEuj7Fpe6d6wkwD8SdequEa2cqq57QHQ43pb1x2HxbLp6anHwutDNzrMhDAbx1YgxCFAbRi6EhWwQLaGMSSmxJRaAzCUgn6VwpVD/<0;1>/*)),and_v(v:thresh(1,pkh([d6bba22a/84'/1'/0']tpubDCwMfgJWBGfJuZFmgTAP9qdSwJeC4fEKaYXQx7CNiTzsB5WrpVSmySdPFnKDu8ChWZweYNh7MoAoFsCNY7gTRFSGtDYbG9s6vAKNKzT1Hii/<2;3>/*),a:pkh([e9e6c583/48'/1'/0'/2']tpubDEWn2LRKdyREaweKHxj7XzSjcxXGTVbFkL5Qi5AWsJzGvN28cKQwGqCND9TP6EPtPaE13eK9SnyuiQ4qsfy5UuGD3p32Ew36mWfKmYCJRcz/<2;3>/*)),older(52596))))").unwrap();
|
||||
|
||||
assert!(descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("d6bba22a").unwrap(),
|
||||
38305
|
||||
));
|
||||
assert!(descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("e9e6c583").unwrap(),
|
||||
38305
|
||||
));
|
||||
assert!(descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("d6bba22a").unwrap(),
|
||||
52596
|
||||
));
|
||||
assert!(descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("e9e6c583").unwrap(),
|
||||
52596
|
||||
));
|
||||
|
||||
assert!(!descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("d6bba22a").unwrap(),
|
||||
12345
|
||||
));
|
||||
assert!(!descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("e9e6c583").unwrap(),
|
||||
12345
|
||||
));
|
||||
|
||||
assert!(!descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("ffffffff").unwrap(),
|
||||
38305
|
||||
));
|
||||
assert!(!descr.contains_fingerprint_in_recovery_path(
|
||||
Fingerprint::from_str("ffffffff").unwrap(),
|
||||
52596
|
||||
));
|
||||
}
|
||||
|
||||
// TODO: test error conditions of deserialization.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user