From ee86a1bd5ebbda39885f1e8869bf8ae03552d682 Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Thu, 18 Aug 2022 11:57:52 +0200 Subject: [PATCH] descriptors: add a helper to get the value of the timelock --- src/descriptors.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/descriptors.rs b/src/descriptors.rs index aa6c1dc2..d58e5dbe 100644 --- a/src/descriptors.rs +++ b/src/descriptors.rs @@ -335,6 +335,43 @@ impl InheritanceDescriptor { .expect("All pubkeys are derived, no wildcard."); DerivedInheritanceDescriptor(desc) } + + /// Get the value (in blocks) of the relative timelock for the heir's spending path. + pub fn timelock_value(&self) -> u32 { + let wsh_desc = match &self.0 { + descriptor::Descriptor::Wsh(desc) => desc, + _ => unreachable!(), + }; + let ms = match wsh_desc.as_inner() { + descriptor::WshInner::Ms(ms) => ms, + _ => unreachable!(), + }; + + let policy = ms + .lift() + .expect("Lifting can't fail on a Miniscript") + .normalized(); + let subs = match policy { + SemanticPolicy::Threshold(1, subs) => subs, + _ => unreachable!(), + }; + let heir_subs = subs + .iter() + .find_map(|s| match s { + SemanticPolicy::Threshold(2, subs) => Some(subs), + _ => None, + }) + .expect("Always present"); + let csv = heir_subs + .iter() + .find_map(|s| match s { + SemanticPolicy::Older(csv) => Some(csv), + _ => None, + }) + .expect("Always present"); + + *csv + } } impl DerivedInheritanceDescriptor { @@ -391,5 +428,17 @@ mod tests { ); } + #[test] + fn inheritance_descriptor_tl_value() { + let desc = InheritanceDescriptor::from_str("wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/*),older(1),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/*)))").unwrap(); + assert_eq!(desc.timelock_value(), 1); + + let desc = InheritanceDescriptor::from_str("wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/*),older(42000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/*)))").unwrap(); + assert_eq!(desc.timelock_value(), 42000); + + let desc = InheritanceDescriptor::from_str("wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/*),older(65535),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/*)))").unwrap(); + assert_eq!(desc.timelock_value(), 0xffff); + } + // TODO: test error conditions of deserialization. }