Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/descriptor/bare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ use bitcoin::blockdata::script;
use bitcoin::{Address, Network, Script};

use super::checksum::{self, verify_checksum};
use crate::descriptor::{DefiniteDescriptorKey, DescriptorType};
use crate::expression::{self, FromTree};
use crate::miniscript::context::ScriptContext;
use crate::miniscript::satisfy::{Placeholder, WitnessTemplate};
use crate::plan::{AssetProvider, Plan};
use crate::policy::{semantic, Liftable};
use crate::prelude::*;
use crate::util::{varint_len, witness_to_scriptsig};
Expand Down Expand Up @@ -124,6 +127,28 @@ impl<Pk: MiniscriptKey + ToPublicKey> Bare<Pk> {
}
}

impl Bare<DefiniteDescriptorKey> {
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
pub fn get_plan<P>(&self, provider: &P) -> Option<Plan>
where
P: AssetProvider<DefiniteDescriptorKey>,
{
self.ms
.build_template(provider)
.into_plan(DescriptorType::Bare)
}

/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
pub fn get_plan_mall<P>(&self, provider: &P) -> Option<Plan>
where
P: AssetProvider<DefiniteDescriptorKey>,
{
self.ms
.build_template_mall(provider)
.into_plan(DescriptorType::Bare)
}
}

impl<Pk: MiniscriptKey> fmt::Debug for Bare<Pk> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.ms)
Expand Down Expand Up @@ -278,6 +303,37 @@ impl<Pk: MiniscriptKey + ToPublicKey> Pkh<Pk> {
}
}

impl Pkh<DefiniteDescriptorKey> {
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
pub fn get_plan<P>(&self, provider: &P) -> Option<Plan>
where
P: AssetProvider<DefiniteDescriptorKey>,
{
if provider.lookup_ecdsa_sig(&self.pk) {
let stack = vec![
Placeholder::EcdsaSigPk(self.pk.clone()),
Placeholder::Pubkey(self.pk.clone(), BareCtx::pk_len(&self.pk)),
];
Some(Plan {
relative_timelock: None,
absolute_timelock: None,
desc_type: DescriptorType::Pkh,
template: WitnessTemplate::from_placeholder_stack(stack),
})
} else {
None
}
}

/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
pub fn get_plan_mall<P>(&self, provider: &P) -> Option<Plan>
where
P: AssetProvider<DefiniteDescriptorKey>,
{
self.get_plan(provider)
}
}

impl<Pk: MiniscriptKey> fmt::Debug for Pkh<Pk> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "pkh({:?})", self.pk)
Expand Down
2 changes: 1 addition & 1 deletion src/descriptor/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ pub enum SinglePubKey {

/// A [`DescriptorPublicKey`] without any wildcards.
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
pub struct DefiniteDescriptorKey(DescriptorPublicKey);
pub struct DefiniteDescriptorKey(pub(super) DescriptorPublicKey);

impl fmt::Display for DescriptorSecretKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Expand Down
77 changes: 75 additions & 2 deletions src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use sync::Arc;

use self::checksum::verify_checksum;
use crate::miniscript::{Legacy, Miniscript, Segwitv0};
use crate::plan::{AssetProvider, Assets, IntoAssets, Plan};
use crate::prelude::*;
use crate::{
expression, hash256, miniscript, BareCtx, Error, ForEachKey, MiniscriptKey, Satisfier,
Expand Down Expand Up @@ -435,7 +436,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
Descriptor::Wpkh(ref wpkh) => wpkh.get_satisfaction(satisfier),
Descriptor::Wsh(ref wsh) => wsh.get_satisfaction(satisfier),
Descriptor::Sh(ref sh) => sh.get_satisfaction(satisfier),
Descriptor::Tr(ref tr) => tr.get_satisfaction(satisfier),
Descriptor::Tr(ref tr) => tr.get_satisfaction(&satisfier),
}
}

Expand All @@ -452,7 +453,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
Descriptor::Wpkh(ref wpkh) => wpkh.get_satisfaction_mall(satisfier),
Descriptor::Wsh(ref wsh) => wsh.get_satisfaction_mall(satisfier),
Descriptor::Sh(ref sh) => sh.get_satisfaction_mall(satisfier),
Descriptor::Tr(ref tr) => tr.get_satisfaction_mall(satisfier),
Descriptor::Tr(ref tr) => tr.get_satisfaction_mall(&satisfier),
}
}

Expand All @@ -470,6 +471,38 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
}
}

impl Descriptor<DefiniteDescriptorKey> {
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
pub fn get_plan<P>(&self, provider: &P) -> Option<Plan>
where
P: AssetProvider<DefiniteDescriptorKey>,
{
match *self {
Descriptor::Bare(ref bare) => bare.get_plan(provider),
Descriptor::Pkh(ref pkh) => pkh.get_plan(provider),
Descriptor::Wpkh(ref wpkh) => wpkh.get_plan(provider),
Descriptor::Wsh(ref wsh) => wsh.get_plan(provider),
Descriptor::Sh(ref sh) => sh.get_plan(provider),
Descriptor::Tr(ref tr) => tr.get_plan(provider),
}
}

/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
pub fn get_plan_mall<P>(&self, provider: &P) -> Option<Plan>
where
P: AssetProvider<DefiniteDescriptorKey>,
{
match *self {
Descriptor::Bare(ref bare) => bare.get_plan_mall(provider),
Descriptor::Pkh(ref pkh) => pkh.get_plan_mall(provider),
Descriptor::Wpkh(ref wpkh) => wpkh.get_plan_mall(provider),
Descriptor::Wsh(ref wsh) => wsh.get_plan_mall(provider),
Descriptor::Sh(ref sh) => sh.get_plan_mall(provider),
Descriptor::Tr(ref tr) => tr.get_plan_mall(provider),
}
}
}

impl<P, Q> TranslatePk<P, Q> for Descriptor<P>
where
P: MiniscriptKey,
Expand Down Expand Up @@ -771,6 +804,46 @@ impl Descriptor<DefiniteDescriptorKey> {
let derived = self.translate_pk(&mut Derivator(secp))?;
Ok(derived)
}

/// Returns the set of keys which are available in the [`KeyMap`] in form of [`Assets`]
pub fn available_keys(&self, key_map: &KeyMap) -> Assets {
let mut available_keys = vec![];

self.for_each_key(|pk| {
let found = match &pk.0 {
s @ DescriptorPublicKey::Single(_) => key_map.contains_key(&s),
DescriptorPublicKey::XPub(xkey) => {
if key_map.contains_key(&DescriptorPublicKey::XPub(xkey.clone())) {
true
} else if xkey.derivation_path.len() > 0 {
let unwind_wildcard = DescriptorXKey {
origin: xkey.origin.clone(),
xkey: xkey.xkey,
wildcard: Wildcard::Unhardened,
derivation_path: xkey
.derivation_path
.into_iter()
.take(xkey.derivation_path.len() - 1)
.cloned()
.collect::<Vec<_>>()
.into(),
};
key_map.contains_key(&DescriptorPublicKey::XPub(unwind_wildcard))
} else {
false
}
}
};

if found {
available_keys.push(pk.clone());
}

true
});

available_keys.into_assets()
}
}

impl_from_tree!(
Expand Down
60 changes: 60 additions & 0 deletions src/descriptor/segwitv0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ use bitcoin::{self, Address, Network, Script};

use super::checksum::{self, verify_checksum};
use super::SortedMultiVec;
use crate::descriptor::{DefiniteDescriptorKey, DescriptorType};
use crate::expression::{self, FromTree};
use crate::miniscript::context::{ScriptContext, ScriptContextError};
use crate::miniscript::satisfy::{Placeholder, WitnessTemplate};
use crate::plan::{AssetProvider, Plan};
use crate::policy::{semantic, Liftable};
use crate::prelude::*;
use crate::util::varint_len;
Expand Down Expand Up @@ -173,6 +176,32 @@ impl<Pk: MiniscriptKey + ToPublicKey> Wsh<Pk> {
}
}

impl Wsh<DefiniteDescriptorKey> {
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
pub fn get_plan<P>(&self, provider: &P) -> Option<Plan>
where
P: AssetProvider<DefiniteDescriptorKey>,
{
match &self.inner {
WshInner::SortedMulti(sm) => sm.build_template(provider).into_plan(DescriptorType::Wsh),
WshInner::Ms(ms) => ms.build_template(provider).into_plan(DescriptorType::Wsh),
}
}

/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
pub fn get_plan_mall<P>(&self, provider: &P) -> Option<Plan>
where
P: AssetProvider<DefiniteDescriptorKey>,
{
match &self.inner {
WshInner::SortedMulti(sm) => sm.build_template(provider).into_plan(DescriptorType::Wsh),
WshInner::Ms(ms) => ms
.build_template_mall(provider)
.into_plan(DescriptorType::Wsh),
}
}
}

/// Wsh Inner
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum WshInner<Pk: MiniscriptKey> {
Expand Down Expand Up @@ -393,6 +422,37 @@ impl<Pk: MiniscriptKey + ToPublicKey> Wpkh<Pk> {
}
}

impl Wpkh<DefiniteDescriptorKey> {
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
pub fn get_plan<P>(&self, provider: &P) -> Option<Plan>
where
P: AssetProvider<DefiniteDescriptorKey>,
{
if provider.lookup_ecdsa_sig(&self.pk) {
let stack = vec![
Placeholder::EcdsaSigPk(self.pk.clone()),
Placeholder::Pubkey(self.pk.clone(), Segwitv0::pk_len(&self.pk)),
];
Some(Plan {
relative_timelock: None,
absolute_timelock: None,
desc_type: DescriptorType::Wpkh,
template: WitnessTemplate::from_placeholder_stack(stack),
})
} else {
None
}
}

/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
pub fn get_plan_mall<P>(&self, provider: &P) -> Option<Plan>
where
P: AssetProvider<DefiniteDescriptorKey>,
{
self.get_plan(provider)
}
}

impl<Pk: MiniscriptKey> fmt::Debug for Wpkh<Pk> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "wpkh({:?})", self.pk)
Expand Down
40 changes: 40 additions & 0 deletions src/descriptor/sh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ use bitcoin::{Address, Network, Script};

use super::checksum::{self, verify_checksum};
use super::{SortedMultiVec, Wpkh, Wsh};
use crate::descriptor::{DefiniteDescriptorKey, DescriptorType};
use crate::expression::{self, FromTree};
use crate::miniscript::context::ScriptContext;
use crate::plan::{AssetProvider, Plan};
use crate::policy::{semantic, Liftable};
use crate::prelude::*;
use crate::util::{varint_len, witness_to_scriptsig};
Expand Down Expand Up @@ -377,6 +379,44 @@ impl<Pk: MiniscriptKey + ToPublicKey> Sh<Pk> {
}
}

impl Sh<DefiniteDescriptorKey> {
/// Returns a plan if the provided assets are sufficient to produce a non-malleable satisfaction
pub fn get_plan<P>(&self, provider: &P) -> Option<Plan>
where
P: AssetProvider<DefiniteDescriptorKey>,
{
match &self.inner {
ShInner::Wsh(ref wsh) => wsh.get_plan(provider).map(|mut plan| {
plan.desc_type = DescriptorType::ShWsh;
plan
}),
ShInner::Wpkh(ref wpkh) => wpkh.get_plan(provider).map(|mut plan| {
plan.desc_type = DescriptorType::ShWpkh;
plan
}),
ShInner::SortedMulti(ref _smv) => todo!(),
ShInner::Ms(ref ms) => ms.build_template(provider).into_plan(DescriptorType::Sh),
}
}

/// Returns a plan if the provided assets are sufficient to produce a malleable satisfaction
pub fn get_plan_mall<P>(&self, provider: &P) -> Option<Plan>
where
P: AssetProvider<DefiniteDescriptorKey>,
{
match &self.inner {
ShInner::Wsh(ref wsh) => wsh.get_plan_mall(provider).map(|mut plan| {
plan.desc_type = DescriptorType::ShWsh;
plan
}),
ShInner::Ms(ref ms) => ms
.build_template_mall(provider)
.into_plan(DescriptorType::Sh),
_ => self.get_plan(provider),
}
}
}

impl<Pk: MiniscriptKey> ForEachKey<Pk> for Sh<Pk> {
fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: F) -> bool
where
Expand Down
12 changes: 12 additions & 0 deletions src/descriptor/sortedmulti.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use bitcoin::blockdata::script;
use crate::miniscript::context::ScriptContext;
use crate::miniscript::decode::Terminal;
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
use crate::miniscript::satisfy::{Placeholder, Satisfaction};
use crate::plan::AssetProvider;
use crate::prelude::*;
use crate::{
errstr, expression, miniscript, policy, script_num_size, Error, ForEachKey, Miniscript,
Expand Down Expand Up @@ -171,6 +173,16 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
ms.satisfy(satisfier)
}

/// Attempt to produce a witness template given the assets available
pub fn build_template<P>(&self, provider: &P) -> Satisfaction<Placeholder<Pk>>
where
Pk: ToPublicKey,
P: AssetProvider<Pk>,
{
let ms = Miniscript::from_ast(self.sorted_node()).expect("Multi node typecheck");
ms.build_template(provider)
}

/// Size, in bytes of the script-pubkey. If this Miniscript is used outside
/// of segwit (e.g. in a bare or P2SH descriptor), this quantity should be
/// multiplied by 4 to compute the weight.
Expand Down
Loading