Skip to content

Commit 35adb67

Browse files
Add plan capabilities to miniscript
Add a `plan` module that contains utilities to calculate the cheapest spending path given an AssetProvider (that could keys, preimages, or timelocks). Adds a `get_plan` method on the various descriptor types. Co-authored-by: Daniela Brozzoni <danielabrozzoni@protonmail.com>
1 parent 2eff886 commit 35adb67

File tree

13 files changed

+1450
-396
lines changed

13 files changed

+1450
-396
lines changed

src/descriptor/bare.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ use bitcoin::script::{self, PushBytes};
1414
use bitcoin::{Address, Network, ScriptBuf};
1515

1616
use super::checksum::{self, verify_checksum};
17+
use crate::descriptor::DefiniteDescriptorKey;
1718
use crate::expression::{self, FromTree};
1819
use crate::miniscript::context::{ScriptContext, ScriptContextError};
20+
use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness};
21+
use crate::plan::AssetProvider;
1922
use crate::policy::{semantic, Liftable};
2023
use crate::prelude::*;
2124
use crate::util::{varint_len, witness_to_scriptsig};
@@ -135,6 +138,30 @@ impl<Pk: MiniscriptKey + ToPublicKey> Bare<Pk> {
135138
}
136139
}
137140

141+
impl Bare<DefiniteDescriptorKey> {
142+
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
143+
pub fn get_plan_satisfaction<P>(
144+
&self,
145+
provider: &P,
146+
) -> Satisfaction<Placeholder<DefiniteDescriptorKey>>
147+
where
148+
P: AssetProvider<DefiniteDescriptorKey>,
149+
{
150+
self.ms.build_template(provider)
151+
}
152+
153+
/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
154+
pub fn get_plan_satisfaction_mall<P>(
155+
&self,
156+
provider: &P,
157+
) -> Satisfaction<Placeholder<DefiniteDescriptorKey>>
158+
where
159+
P: AssetProvider<DefiniteDescriptorKey>,
160+
{
161+
self.ms.build_template_mall(provider)
162+
}
163+
}
164+
138165
impl<Pk: MiniscriptKey> fmt::Debug for Bare<Pk> {
139166
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140167
write!(f, "{:?}", self.ms)
@@ -312,6 +339,45 @@ impl<Pk: MiniscriptKey + ToPublicKey> Pkh<Pk> {
312339
}
313340
}
314341

342+
impl Pkh<DefiniteDescriptorKey> {
343+
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
344+
pub fn get_plan_satisfaction<P>(
345+
&self,
346+
provider: &P,
347+
) -> Satisfaction<Placeholder<DefiniteDescriptorKey>>
348+
where
349+
P: AssetProvider<DefiniteDescriptorKey>,
350+
{
351+
let stack = if provider.lookup_ecdsa_sig(&self.pk) {
352+
let stack = vec![
353+
Placeholder::EcdsaSigPk(self.pk.clone()),
354+
Placeholder::Pubkey(self.pk.clone(), BareCtx::pk_len(&self.pk)),
355+
];
356+
Witness::Stack(stack)
357+
} else {
358+
Witness::Unavailable
359+
};
360+
361+
Satisfaction {
362+
stack,
363+
has_sig: true,
364+
relative_timelock: None,
365+
absolute_timelock: None,
366+
}
367+
}
368+
369+
/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
370+
pub fn get_plan_satisfaction_mall<P>(
371+
&self,
372+
provider: &P,
373+
) -> Satisfaction<Placeholder<DefiniteDescriptorKey>>
374+
where
375+
P: AssetProvider<DefiniteDescriptorKey>,
376+
{
377+
self.get_plan_satisfaction(provider)
378+
}
379+
}
380+
315381
impl<Pk: MiniscriptKey> fmt::Debug for Pkh<Pk> {
316382
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317383
write!(f, "pkh({:?})", self.pk)

src/descriptor/key.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,41 @@ impl DescriptorPublicKey {
721721
}
722722
}
723723
}
724+
725+
/// Whether this key is the "parent" of a [`DefiniteDescriptorKey`]
726+
///
727+
/// The key is considered "parent" if it represents the non-derived version of a definite key,
728+
/// meaning it contains a wildcard where the definite key has a definite derivation number.
729+
///
730+
/// If `self` is a single key or doesn't contain any wildcards, the definite key will have to
731+
/// be exactly the same.
732+
///
733+
/// Returns the derivation path to apply to `self` to obtain the definite key.
734+
pub fn is_parent(&self, definite_key: &DefiniteDescriptorKey) -> Option<bip32::DerivationPath> {
735+
// If the key is `Single` or it's an `XPub` with no wildcard it will match the definite key
736+
// exactly, so we try this check first
737+
if self == &definite_key.0 {
738+
return Some(bip32::DerivationPath::default());
739+
}
740+
741+
match (self, &definite_key.0) {
742+
(DescriptorPublicKey::XPub(self_xkey), DescriptorPublicKey::XPub(definite_xkey))
743+
if definite_xkey.derivation_path.len() > 0 =>
744+
{
745+
let definite_path_len = definite_xkey.derivation_path.len();
746+
if self_xkey.origin == definite_xkey.origin
747+
&& self_xkey.xkey == definite_xkey.xkey
748+
&& self_xkey.derivation_path.as_ref()
749+
== &definite_xkey.derivation_path[..(definite_path_len - 1)]
750+
{
751+
Some(vec![definite_xkey.derivation_path[definite_path_len - 1]].into())
752+
} else {
753+
None
754+
}
755+
}
756+
_ => None,
757+
}
758+
}
724759
}
725760

726761
impl FromStr for DescriptorSecretKey {

src/descriptor/mod.rs

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ use bitcoin::{secp256k1, Address, Network, Script, ScriptBuf, TxIn, Witness};
2222
use sync::Arc;
2323

2424
use self::checksum::verify_checksum;
25-
use crate::miniscript::{Legacy, Miniscript, Segwitv0};
25+
use crate::miniscript::{satisfy, Legacy, Miniscript, Segwitv0};
26+
use crate::plan::{AssetProvider, Plan};
2627
use crate::prelude::*;
2728
use crate::{
2829
expression, hash256, miniscript, BareCtx, Error, ForEachKey, MiniscriptKey, Satisfier,
@@ -476,7 +477,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
476477
Descriptor::Wpkh(ref wpkh) => wpkh.get_satisfaction(satisfier),
477478
Descriptor::Wsh(ref wsh) => wsh.get_satisfaction(satisfier),
478479
Descriptor::Sh(ref sh) => sh.get_satisfaction(satisfier),
479-
Descriptor::Tr(ref tr) => tr.get_satisfaction(satisfier),
480+
Descriptor::Tr(ref tr) => tr.get_satisfaction(&satisfier),
480481
}
481482
}
482483

@@ -493,7 +494,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
493494
Descriptor::Wpkh(ref wpkh) => wpkh.get_satisfaction_mall(satisfier),
494495
Descriptor::Wsh(ref wsh) => wsh.get_satisfaction_mall(satisfier),
495496
Descriptor::Sh(ref sh) => sh.get_satisfaction_mall(satisfier),
496-
Descriptor::Tr(ref tr) => tr.get_satisfaction_mall(satisfier),
497+
Descriptor::Tr(ref tr) => tr.get_satisfaction_mall(&satisfier),
497498
}
498499
}
499500

@@ -511,6 +512,60 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
511512
}
512513
}
513514

515+
impl Descriptor<DefiniteDescriptorKey> {
516+
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
517+
pub fn get_plan<P>(self, provider: &P) -> Option<Plan>
518+
where
519+
P: AssetProvider<DefiniteDescriptorKey>,
520+
{
521+
let satisfaction = match self {
522+
Descriptor::Bare(ref bare) => bare.get_plan_satisfaction(provider),
523+
Descriptor::Pkh(ref pkh) => pkh.get_plan_satisfaction(provider),
524+
Descriptor::Wpkh(ref wpkh) => wpkh.get_plan_satisfaction(provider),
525+
Descriptor::Wsh(ref wsh) => wsh.get_plan_satisfaction(provider),
526+
Descriptor::Sh(ref sh) => sh.get_plan_satisfaction(provider),
527+
Descriptor::Tr(ref tr) => tr.get_plan_satisfaction(provider),
528+
};
529+
530+
if let satisfy::Witness::Stack(stack) = satisfaction.stack {
531+
Some(Plan {
532+
descriptor: self,
533+
template: stack,
534+
absolute_timelock: satisfaction.absolute_timelock.map(Into::into),
535+
relative_timelock: satisfaction.relative_timelock,
536+
})
537+
} else {
538+
None
539+
}
540+
}
541+
542+
/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
543+
pub fn get_plan_mall<P>(self, provider: &P) -> Option<Plan>
544+
where
545+
P: AssetProvider<DefiniteDescriptorKey>,
546+
{
547+
let satisfaction = match self {
548+
Descriptor::Bare(ref bare) => bare.get_plan_satisfaction_mall(provider),
549+
Descriptor::Pkh(ref pkh) => pkh.get_plan_satisfaction_mall(provider),
550+
Descriptor::Wpkh(ref wpkh) => wpkh.get_plan_satisfaction_mall(provider),
551+
Descriptor::Wsh(ref wsh) => wsh.get_plan_satisfaction_mall(provider),
552+
Descriptor::Sh(ref sh) => sh.get_plan_satisfaction_mall(provider),
553+
Descriptor::Tr(ref tr) => tr.get_plan_satisfaction_mall(provider),
554+
};
555+
556+
if let satisfy::Witness::Stack(stack) = satisfaction.stack {
557+
Some(Plan {
558+
descriptor: self,
559+
template: stack,
560+
absolute_timelock: satisfaction.absolute_timelock.map(Into::into),
561+
relative_timelock: satisfaction.relative_timelock,
562+
})
563+
} else {
564+
None
565+
}
566+
}
567+
}
568+
514569
impl<P, Q> TranslatePk<P, Q> for Descriptor<P>
515570
where
516571
P: MiniscriptKey,

src/descriptor/segwitv0.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ use bitcoin::{Address, Network, ScriptBuf};
1212

1313
use super::checksum::{self, verify_checksum};
1414
use super::SortedMultiVec;
15+
use crate::descriptor::DefiniteDescriptorKey;
1516
use crate::expression::{self, FromTree};
1617
use crate::miniscript::context::{ScriptContext, ScriptContextError};
18+
use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness};
19+
use crate::plan::AssetProvider;
1720
use crate::policy::{semantic, Liftable};
1821
use crate::prelude::*;
1922
use crate::util::varint_len;
@@ -192,6 +195,36 @@ impl<Pk: MiniscriptKey + ToPublicKey> Wsh<Pk> {
192195
}
193196
}
194197

198+
impl Wsh<DefiniteDescriptorKey> {
199+
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
200+
pub fn get_plan_satisfaction<P>(
201+
&self,
202+
provider: &P,
203+
) -> Satisfaction<Placeholder<DefiniteDescriptorKey>>
204+
where
205+
P: AssetProvider<DefiniteDescriptorKey>,
206+
{
207+
match &self.inner {
208+
WshInner::SortedMulti(sm) => sm.build_template(provider),
209+
WshInner::Ms(ms) => ms.build_template(provider),
210+
}
211+
}
212+
213+
/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
214+
pub fn get_plan_satisfaction_mall<P>(
215+
&self,
216+
provider: &P,
217+
) -> Satisfaction<Placeholder<DefiniteDescriptorKey>>
218+
where
219+
P: AssetProvider<DefiniteDescriptorKey>,
220+
{
221+
match &self.inner {
222+
WshInner::SortedMulti(sm) => sm.build_template(provider),
223+
WshInner::Ms(ms) => ms.build_template_mall(provider),
224+
}
225+
}
226+
}
227+
195228
/// Wsh Inner
196229
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
197230
pub enum WshInner<Pk: MiniscriptKey> {
@@ -419,6 +452,45 @@ impl<Pk: MiniscriptKey + ToPublicKey> Wpkh<Pk> {
419452
}
420453
}
421454

455+
impl Wpkh<DefiniteDescriptorKey> {
456+
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
457+
pub fn get_plan_satisfaction<P>(
458+
&self,
459+
provider: &P,
460+
) -> Satisfaction<Placeholder<DefiniteDescriptorKey>>
461+
where
462+
P: AssetProvider<DefiniteDescriptorKey>,
463+
{
464+
let stack = if provider.lookup_ecdsa_sig(&self.pk) {
465+
let stack = vec![
466+
Placeholder::EcdsaSigPk(self.pk.clone()),
467+
Placeholder::Pubkey(self.pk.clone(), Segwitv0::pk_len(&self.pk)),
468+
];
469+
Witness::Stack(stack)
470+
} else {
471+
Witness::Unavailable
472+
};
473+
474+
Satisfaction {
475+
stack,
476+
has_sig: true,
477+
relative_timelock: None,
478+
absolute_timelock: None,
479+
}
480+
}
481+
482+
/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
483+
pub fn get_plan_satisfaction_mall<P>(
484+
&self,
485+
provider: &P,
486+
) -> Satisfaction<Placeholder<DefiniteDescriptorKey>>
487+
where
488+
P: AssetProvider<DefiniteDescriptorKey>,
489+
{
490+
self.get_plan_satisfaction(provider)
491+
}
492+
}
493+
422494
impl<Pk: MiniscriptKey> fmt::Debug for Wpkh<Pk> {
423495
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
424496
write!(f, "wpkh({:?})", self.pk)

src/descriptor/sh.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ use bitcoin::{script, Address, Network, ScriptBuf};
1616

1717
use super::checksum::{self, verify_checksum};
1818
use super::{SortedMultiVec, Wpkh, Wsh};
19+
use crate::descriptor::DefiniteDescriptorKey;
1920
use crate::expression::{self, FromTree};
2021
use crate::miniscript::context::ScriptContext;
22+
use crate::miniscript::satisfy::{Placeholder, Satisfaction};
23+
use crate::plan::AssetProvider;
2124
use crate::policy::{semantic, Liftable};
2225
use crate::prelude::*;
2326
use crate::util::{varint_len, witness_to_scriptsig};
@@ -419,6 +422,39 @@ impl<Pk: MiniscriptKey + ToPublicKey> Sh<Pk> {
419422
}
420423
}
421424

425+
impl Sh<DefiniteDescriptorKey> {
426+
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
427+
pub fn get_plan_satisfaction<P>(
428+
&self,
429+
provider: &P,
430+
) -> Satisfaction<Placeholder<DefiniteDescriptorKey>>
431+
where
432+
P: AssetProvider<DefiniteDescriptorKey>,
433+
{
434+
match &self.inner {
435+
ShInner::Wsh(ref wsh) => wsh.get_plan_satisfaction(provider),
436+
ShInner::Wpkh(ref wpkh) => wpkh.get_plan_satisfaction(provider),
437+
ShInner::SortedMulti(ref smv) => smv.build_template(provider),
438+
ShInner::Ms(ref ms) => ms.build_template(provider),
439+
}
440+
}
441+
442+
/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
443+
pub fn get_plan_satisfaction_mall<P>(
444+
&self,
445+
provider: &P,
446+
) -> Satisfaction<Placeholder<DefiniteDescriptorKey>>
447+
where
448+
P: AssetProvider<DefiniteDescriptorKey>,
449+
{
450+
match &self.inner {
451+
ShInner::Wsh(ref wsh) => wsh.get_plan_satisfaction_mall(provider),
452+
ShInner::Ms(ref ms) => ms.build_template_mall(provider),
453+
_ => self.get_plan_satisfaction(provider),
454+
}
455+
}
456+
}
457+
422458
impl<Pk: MiniscriptKey> ForEachKey<Pk> for Sh<Pk> {
423459
fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: F) -> bool {
424460
match self.inner {

src/descriptor/sortedmulti.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use bitcoin::script;
1515
use crate::miniscript::context::ScriptContext;
1616
use crate::miniscript::decode::Terminal;
1717
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
18+
use crate::miniscript::satisfy::{Placeholder, Satisfaction};
19+
use crate::plan::AssetProvider;
1820
use crate::prelude::*;
1921
use crate::{
2022
errstr, expression, miniscript, policy, script_num_size, Error, ForEachKey, Miniscript,
@@ -155,6 +157,16 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
155157
ms.satisfy(satisfier)
156158
}
157159

160+
/// Attempt to produce a witness template given the assets available
161+
pub fn build_template<P>(&self, provider: &P) -> Satisfaction<Placeholder<Pk>>
162+
where
163+
Pk: ToPublicKey,
164+
P: AssetProvider<Pk>,
165+
{
166+
let ms = Miniscript::from_ast(self.sorted_node()).expect("Multi node typecheck");
167+
ms.build_template(provider)
168+
}
169+
158170
/// Size, in bytes of the script-pubkey. If this Miniscript is used outside
159171
/// of segwit (e.g. in a bare or P2SH descriptor), this quantity should be
160172
/// multiplied by 4 to compute the weight.

0 commit comments

Comments
 (0)