Skip to content

Commit bf0d59a

Browse files
miniscript: Descriptor field accessors
1 parent e3fd3d6 commit bf0d59a

File tree

3 files changed

+80
-9
lines changed

3 files changed

+80
-9
lines changed

src/runtime/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ impl FieldAccess for Value {
252252
Value::Array(x) => x.get_field(field),
253253
Value::Psbt(x) => x.get_field(field),
254254
Value::Transaction(x) => x.get_field(field),
255+
Value::Descriptor(x) => x.get_field(field),
255256
Value::TapInfo(x) => x.get_field(field),
256257
_ => None,
257258
}

src/stdlib/miniscript.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::convert::{TryFrom, TryInto};
22
use std::{fmt, sync::Arc};
33

4-
use miniscript::descriptor::{ShInner, WshInner};
4+
use miniscript::descriptor::{DescriptorType, ShInner, WshInner};
55
use miniscript::{ScriptContext, Threshold};
66

77
use crate::runtime::scope::{Mutable, ScopeRef};
8-
use crate::runtime::{Array, Error, Evaluate, ExprRepr, Result, Value};
9-
use crate::stdlib::btc::WshScript;
8+
use crate::runtime::{Array, Error, Evaluate, ExprRepr, FieldAccess, Result, Value};
9+
use crate::stdlib::{btc::WshScript, taproot::tap_scripts_to_val};
1010
use crate::util::{DescriptorExt, DescriptorSecretKeyExt, MiniscriptExt};
1111
use crate::{ast, DescriptorDpk as Descriptor, MiniscriptDpk as Miniscript, PolicyDpk as Policy};
1212

@@ -245,6 +245,39 @@ pub mod fns {
245245
}
246246
}
247247

248+
// Descriptor fields accessors
249+
impl FieldAccess for Descriptor {
250+
fn get_field(self, field: &Value) -> Option<Value> {
251+
Some(match field.as_str()? {
252+
"descriptor_type" => self.desc_type().into(),
253+
"max_weight" => self.max_weight_to_satisfy().ok()?.into(),
254+
"singles" => self.into_single_descriptors().ok()?.into(),
255+
"is_safe" => self.sanity_check().is_ok().into(),
256+
"is_multipath" => self.is_multipath().into(),
257+
"is_wildcard" => self.has_wildcard().into(),
258+
"is_definite" => (!self.has_wildcard() && !self.is_multipath()).into(),
259+
260+
// Only available for definite descriptors (non-multi-path and no underived wildcards)
261+
"script_pubkey" | "scriptPubKey" => self.to_script_pubkey().ok()?.into(),
262+
"explicit_script" | "explicitScript" => self.to_explicit_script().ok()?.into(),
263+
// Only available for definite segwit descriptors
264+
"witness_program" => self.witness_program().ok()??.into(),
265+
266+
// Only available for taproot descriptors (similar fields mirrored on TaprootSpendInfo)
267+
"internal_key" => self.tr()?.internal_key().clone().into(),
268+
// Only available for definite taproot descriptors
269+
"merkle_root" => self.tap_info().ok()??.merkle_root()?.into(),
270+
"output_key" => self.tap_info().ok()??.output_key().into(),
271+
"output_key_parity" => self.tap_info().ok()??.output_key_parity().into(),
272+
"scripts" => tap_scripts_to_val(&*self.tap_info().ok()??),
273+
_ => {
274+
return None;
275+
}
276+
// TODO address_type
277+
})
278+
}
279+
}
280+
248281
fn into_policies(values: Vec<Value>) -> Result<Vec<Arc<Policy>>> {
249282
values
250283
.into_iter()
@@ -267,6 +300,8 @@ fn into_prob_policies(values: Vec<Value>) -> Result<Vec<(usize, Arc<Policy>)>> {
267300
.collect()
268301
}
269302

303+
impl_simple_to_value!(DescriptorType, t, format!("{:?}", t));
304+
270305
// Convert from Value to Miniscript types
271306

272307
impl_simple_into_variant!(Descriptor, Descriptor, into_descriptor, NotDescriptor);

src/util.rs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use std::fmt;
22
use std::marker::PhantomData;
3+
use std::sync::Arc;
34

45
use bitcoin::bip32::{self, ChildNumber, DerivationPath, IntoDerivationPath};
56
use bitcoin::hashes::{sha256, Hash};
67
use bitcoin::hex::DisplayHex;
7-
use bitcoin::{psbt, secp256k1, taproot, PublicKey};
8+
use bitcoin::taproot::TaprootSpendInfo;
9+
use bitcoin::{psbt, secp256k1, PublicKey};
810
use miniscript::descriptor::{
9-
DerivPaths, DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, Wildcard,
11+
self, DerivPaths, DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, Wildcard,
1012
};
1113
use miniscript::{
1214
bitcoin, DefiniteDescriptorKey, Descriptor, ForEachKey, MiniscriptKey, TranslatePk, Translator,
@@ -26,18 +28,18 @@ pub trait TapInfoExt {
2628
bitcoin::ScriptBuf::new_witness_program(&self.witness_program())
2729
}
2830
}
29-
impl TapInfoExt for taproot::TaprootSpendInfo {
31+
impl TapInfoExt for TaprootSpendInfo {
3032
fn witness_program(&self) -> bitcoin::WitnessProgram {
3133
bitcoin::WitnessProgram::p2tr_tweaked(self.output_key())
3234
}
3335
}
3436

3537
pub trait PsbtTaprootExt {
3638
/// Update PSBT fields using the TaprootSpendInfo
37-
fn update_with_taproot(&mut self, tapinfo: &taproot::TaprootSpendInfo) -> Result<()>;
39+
fn update_with_taproot(&mut self, tapinfo: &TaprootSpendInfo) -> Result<()>;
3840
}
3941
impl PsbtTaprootExt for psbt::Input {
40-
fn update_with_taproot(&mut self, tapinfo: &taproot::TaprootSpendInfo) -> Result<()> {
42+
fn update_with_taproot(&mut self, tapinfo: &TaprootSpendInfo) -> Result<()> {
4143
self.tap_merkle_root = tapinfo.merkle_root();
4244
self.tap_internal_key = Some(tapinfo.internal_key());
4345
self.tap_scripts = tapinfo
@@ -53,7 +55,7 @@ impl PsbtTaprootExt for psbt::Input {
5355
}
5456
}
5557
impl PsbtTaprootExt for psbt::Output {
56-
fn update_with_taproot(&mut self, tapinfo: &taproot::TaprootSpendInfo) -> Result<()> {
58+
fn update_with_taproot(&mut self, tapinfo: &TaprootSpendInfo) -> Result<()> {
5759
self.tap_internal_key = Some(tapinfo.internal_key());
5860
// `tap_key_origins` needs to be filled in manually
5961
// TODO autofill `tap_tree`
@@ -101,6 +103,21 @@ pub trait DescriptorExt {
101103
fn to_address(&self, network: bitcoin::Network) -> Result<bitcoin::Address> {
102104
Ok(self.derive_definite()?.address(network)?)
103105
}
106+
107+
/// Get the witness program. Errors if the descriptor contains underived wildcards or multi-path derivations.
108+
fn witness_program(&self) -> Result<Option<bitcoin::WitnessProgram>> {
109+
Ok(self
110+
.to_address(bitcoin::Network::Bitcoin)?
111+
.witness_program())
112+
}
113+
114+
/// Get the inner Tr, if it is a Tr descriptors
115+
fn tr(&self) -> Option<&descriptor::Tr<DescriptorPublicKey>>;
116+
117+
/// Get a TaprootSpendInfo representation of this Tr descriptor
118+
/// Returna an Ok(None) for non-Taproot descriptors, or an Err for Taproot
119+
/// descriptors that are not definite (contain underived wildcards).
120+
fn tap_info(&self) -> Result<Option<Arc<TaprootSpendInfo>>>;
104121
}
105122

106123
impl DescriptorExt for Descriptor<DescriptorPublicKey> {
@@ -115,6 +132,24 @@ impl DescriptorExt for Descriptor<DescriptorPublicKey> {
115132
);
116133
Ok(self.at_derivation_index(0).expect("index is valid"))
117134
}
135+
136+
fn tr(&self) -> Option<&descriptor::Tr<DescriptorPublicKey>> {
137+
match self {
138+
Descriptor::Tr(tr) => Some(tr),
139+
_ => None,
140+
}
141+
}
142+
143+
fn tap_info(&self) -> Result<Option<Arc<TaprootSpendInfo>>> {
144+
if matches!(self, Descriptor::Tr(_)) {
145+
Ok(match self.definite()? {
146+
Descriptor::Tr(tr) => Some(tr.spend_info().clone()),
147+
_ => unreachable!(),
148+
})
149+
} else {
150+
Ok(None)
151+
}
152+
}
118153
}
119154

120155
// BIP32 derivation utilities

0 commit comments

Comments
 (0)