Skip to content

Commit d29c298

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 <[email protected]>
1 parent fc20eb0 commit d29c298

File tree

11 files changed

+1596
-189
lines changed

11 files changed

+1596
-189
lines changed

src/descriptor/bare.rs

+66
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ use bitcoin::script::{self, PushBytes};
1313
use bitcoin::{Address, Network, ScriptBuf};
1414

1515
use super::checksum::{self, verify_checksum};
16+
use crate::descriptor::DefiniteDescriptorKey;
1617
use crate::expression::{self, FromTree};
1718
use crate::miniscript::context::{ScriptContext, ScriptContextError};
19+
use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness};
20+
use crate::plan::AssetProvider;
1821
use crate::policy::{semantic, Liftable};
1922
use crate::prelude::*;
2023
use crate::util::{varint_len, witness_to_scriptsig};
@@ -134,6 +137,30 @@ impl<Pk: MiniscriptKey + ToPublicKey> Bare<Pk> {
134137
}
135138
}
136139

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

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

src/descriptor/mod.rs

+62-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ use sync::Arc;
2222

2323
use self::checksum::verify_checksum;
2424
use crate::miniscript::decode::Terminal;
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, BareCtx, Error, ForEachKey, MiniscriptKey, Satisfier, ToPublicKey,
@@ -474,7 +475,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
474475
Descriptor::Wpkh(ref wpkh) => wpkh.get_satisfaction(satisfier),
475476
Descriptor::Wsh(ref wsh) => wsh.get_satisfaction(satisfier),
476477
Descriptor::Sh(ref sh) => sh.get_satisfaction(satisfier),
477-
Descriptor::Tr(ref tr) => tr.get_satisfaction(satisfier),
478+
Descriptor::Tr(ref tr) => tr.get_satisfaction(&satisfier),
478479
}
479480
}
480481

@@ -491,7 +492,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
491492
Descriptor::Wpkh(ref wpkh) => wpkh.get_satisfaction_mall(satisfier),
492493
Descriptor::Wsh(ref wsh) => wsh.get_satisfaction_mall(satisfier),
493494
Descriptor::Sh(ref sh) => sh.get_satisfaction_mall(satisfier),
494-
Descriptor::Tr(ref tr) => tr.get_satisfaction_mall(satisfier),
495+
Descriptor::Tr(ref tr) => tr.get_satisfaction_mall(&satisfier),
495496
}
496497
}
497498

@@ -509,6 +510,64 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
509510
}
510511
}
511512

513+
impl Descriptor<DefiniteDescriptorKey> {
514+
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
515+
///
516+
/// If the assets aren't sufficient for generating a Plan, the descriptor is returned
517+
pub fn plan<P>(self, provider: &P) -> Result<Plan, Self>
518+
where
519+
P: AssetProvider<DefiniteDescriptorKey>,
520+
{
521+
let satisfaction = match self {
522+
Descriptor::Bare(ref bare) => bare.plan_satisfaction(provider),
523+
Descriptor::Pkh(ref pkh) => pkh.plan_satisfaction(provider),
524+
Descriptor::Wpkh(ref wpkh) => wpkh.plan_satisfaction(provider),
525+
Descriptor::Wsh(ref wsh) => wsh.plan_satisfaction(provider),
526+
Descriptor::Sh(ref sh) => sh.plan_satisfaction(provider),
527+
Descriptor::Tr(ref tr) => tr.plan_satisfaction(provider),
528+
};
529+
530+
if let satisfy::Witness::Stack(stack) = satisfaction.stack {
531+
Ok(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+
Err(self)
539+
}
540+
}
541+
542+
/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
543+
///
544+
/// If the assets aren't sufficient for generating a Plan, the descriptor is returned
545+
pub fn plan_mall<P>(self, provider: &P) -> Result<Plan, Self>
546+
where
547+
P: AssetProvider<DefiniteDescriptorKey>,
548+
{
549+
let satisfaction = match self {
550+
Descriptor::Bare(ref bare) => bare.plan_satisfaction_mall(provider),
551+
Descriptor::Pkh(ref pkh) => pkh.plan_satisfaction_mall(provider),
552+
Descriptor::Wpkh(ref wpkh) => wpkh.plan_satisfaction_mall(provider),
553+
Descriptor::Wsh(ref wsh) => wsh.plan_satisfaction_mall(provider),
554+
Descriptor::Sh(ref sh) => sh.plan_satisfaction_mall(provider),
555+
Descriptor::Tr(ref tr) => tr.plan_satisfaction_mall(provider),
556+
};
557+
558+
if let satisfy::Witness::Stack(stack) = satisfaction.stack {
559+
Ok(Plan {
560+
descriptor: self,
561+
template: stack,
562+
absolute_timelock: satisfaction.absolute_timelock.map(Into::into),
563+
relative_timelock: satisfaction.relative_timelock,
564+
})
565+
} else {
566+
Err(self)
567+
}
568+
}
569+
}
570+
512571
impl<P, Q> TranslatePk<P, Q> for Descriptor<P>
513572
where
514573
P: MiniscriptKey,

src/descriptor/segwitv0.rs

+72
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ use bitcoin::{Address, Network, ScriptBuf};
1111

1212
use super::checksum::{self, verify_checksum};
1313
use super::SortedMultiVec;
14+
use crate::descriptor::DefiniteDescriptorKey;
1415
use crate::expression::{self, FromTree};
1516
use crate::miniscript::context::{ScriptContext, ScriptContextError};
17+
use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness};
18+
use crate::plan::AssetProvider;
1619
use crate::policy::{semantic, Liftable};
1720
use crate::prelude::*;
1821
use crate::util::varint_len;
@@ -191,6 +194,36 @@ impl<Pk: MiniscriptKey + ToPublicKey> Wsh<Pk> {
191194
}
192195
}
193196

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

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

src/descriptor/sh.rs

+36
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ use bitcoin::{script, Address, Network, ScriptBuf};
1515

1616
use super::checksum::{self, verify_checksum};
1717
use super::{SortedMultiVec, Wpkh, Wsh};
18+
use crate::descriptor::DefiniteDescriptorKey;
1819
use crate::expression::{self, FromTree};
1920
use crate::miniscript::context::ScriptContext;
21+
use crate::miniscript::satisfy::{Placeholder, Satisfaction};
22+
use crate::plan::AssetProvider;
2023
use crate::policy::{semantic, Liftable};
2124
use crate::prelude::*;
2225
use crate::util::{varint_len, witness_to_scriptsig};
@@ -418,6 +421,39 @@ impl<Pk: MiniscriptKey + ToPublicKey> Sh<Pk> {
418421
}
419422
}
420423

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

src/descriptor/sortedmulti.rs

+12
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use bitcoin::script;
1414
use crate::miniscript::context::ScriptContext;
1515
use crate::miniscript::decode::Terminal;
1616
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
17+
use crate::miniscript::satisfy::{Placeholder, Satisfaction};
18+
use crate::plan::AssetProvider;
1719
use crate::prelude::*;
1820
use crate::{
1921
errstr, expression, policy, script_num_size, Error, ForEachKey, Miniscript, MiniscriptKey,
@@ -154,6 +156,16 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
154156
ms.satisfy(satisfier)
155157
}
156158

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

0 commit comments

Comments
 (0)