Skip to content

Commit 5048df6

Browse files
committed
Merge #297: Add pretaproot descriptor and addressabledescriptor
121e0b4 Add an API to convert Descriptor into a pre-taproot descriptor (sanket1729) 9e0735e Add pretaproot descriptors/remove macros (sanket1729) b827c26 Remove comment about Tr descriptor (sanket1729) 79b2659 Change from arc.clone() to Arc::clone() (sanket1729) Pull request description: Still not completely sure if this is what you wanted. I think we might also want new descriptor enums for `PreTaprootDescriptor` and `AddressableDescriptor` ACKs for top commit: apoelstra: ACK 121e0b4 Tree-SHA512: 890cf9e8cf2869f02c46a6ce051e27cc1bb6d2cc425c4fccb24d45d115922eff5afe5939d7afa6c8f112510f30b81c17e64715afc3fa70cc0e05e059dd94a42d
2 parents 9d67260 + 121e0b4 commit 5048df6

File tree

4 files changed

+337
-4
lines changed

4 files changed

+337
-4
lines changed

src/descriptor/mod.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ use {
4141
TranslatePk2,
4242
};
4343

44+
// Directly export from lib.rs, exporting the trait here causes conflicts in this file
45+
pub(crate) mod pretaproot;
46+
4447
mod bare;
4548
mod segwitv0;
4649
mod sh;
@@ -91,7 +94,6 @@ pub trait DescriptorTrait<Pk: MiniscriptKey> {
9194
/// Some descriptors like pk() don't have any address.
9295
/// Errors:
9396
/// - On raw/bare descriptors that don't have any address
94-
/// - In Tr descriptors where the precomputed spend data is not available
9597
fn address(&self, network: bitcoin::Network) -> Result<bitcoin::Address, Error>
9698
where
9799
Pk: ToPublicKey;
@@ -339,6 +341,43 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
339341
Descriptor::Tr(ref _tr) => DescriptorType::Tr,
340342
}
341343
}
344+
345+
/// .
346+
/// Convert a Descriptor into [`pretaproot::PreTaprootDescriptor`]
347+
/// # Examples
348+
///
349+
/// ```
350+
/// use std::str::FromStr;
351+
/// use miniscript::descriptor::Descriptor;
352+
/// use miniscript::{PreTaprootDescriptor, PreTaprootDescriptorTrait};
353+
/// use miniscript::bitcoin;
354+
///
355+
/// // A descriptor with a string generic
356+
/// let desc = Descriptor::<bitcoin::PublicKey>::from_str("wpkh(02e18f242c8b0b589bfffeac30e1baa80a60933a649c7fb0f1103e78fbf58aa0ed)")
357+
/// .expect("Valid segwitv0 descriptor");
358+
/// let pre_tap_desc = desc.into_pre_taproot_desc().expect("Wsh is pre taproot");
359+
///
360+
/// // Now the script code and explicit script no longer fail on longer fail
361+
/// // on PreTaprootDescriptor using PreTaprootDescriptorTrait
362+
/// let script_code = pre_tap_desc.script_code();
363+
/// assert_eq!(script_code.to_string(),
364+
/// "Script(OP_DUP OP_HASH160 OP_PUSHBYTES_20 62107d047e8818b594303fe0657388cc4fc8771f OP_EQUALVERIFY OP_CHECKSIG)"
365+
/// );
366+
/// ```
367+
///
368+
/// # Errors
369+
///
370+
/// This function will return an error if descriptor is not a pre taproot descriptor.
371+
pub fn into_pre_taproot_desc(self) -> Result<pretaproot::PreTaprootDescriptor<Pk>, Self> {
372+
match self {
373+
Descriptor::Bare(bare) => Ok(pretaproot::PreTaprootDescriptor::Bare(bare)),
374+
Descriptor::Pkh(pkh) => Ok(pretaproot::PreTaprootDescriptor::Pkh(pkh)),
375+
Descriptor::Wpkh(wpkh) => Ok(pretaproot::PreTaprootDescriptor::Wpkh(wpkh)),
376+
Descriptor::Sh(sh) => Ok(pretaproot::PreTaprootDescriptor::Sh(sh)),
377+
Descriptor::Wsh(wsh) => Ok(pretaproot::PreTaprootDescriptor::Wsh(wsh)),
378+
Descriptor::Tr(tr) => Err(Descriptor::Tr(tr)),
379+
}
380+
}
342381
}
343382

344383
impl<P: MiniscriptKey, Q: MiniscriptKey> TranslatePk<P, Q> for Descriptor<P> {

src/descriptor/pretaproot.rs

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
use std::{
2+
fmt,
3+
str::{self, FromStr},
4+
};
5+
6+
use bitcoin::{self, Script};
7+
8+
use super::{checksum::verify_checksum, Bare, Pkh, Sh, Wpkh, Wsh};
9+
use {expression, DescriptorTrait, Error, MiniscriptKey, Satisfier, ToPublicKey};
10+
11+
/// Script descriptor
12+
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
13+
pub enum PreTaprootDescriptor<Pk: MiniscriptKey> {
14+
/// Bare descriptor
15+
Bare(Bare<Pk>),
16+
/// Pay-to-PubKey-Hash
17+
Pkh(Pkh<Pk>),
18+
/// Pay-to-Witness-PubKey-Hash
19+
Wpkh(Wpkh<Pk>),
20+
/// Pay-to-ScriptHash(includes nested wsh/wpkh/sorted multi)
21+
Sh(Sh<Pk>),
22+
/// Pay-to-Witness-ScriptHash with Segwitv0 context
23+
Wsh(Wsh<Pk>),
24+
}
25+
26+
impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for PreTaprootDescriptor<Pk> {
27+
/// Whether the descriptor is safe
28+
/// Checks whether all the spend paths in the descriptor are possible
29+
/// on the bitcoin network under the current standardness and consensus rules
30+
/// Also checks whether the descriptor requires signauture on all spend paths
31+
/// And whether the script is malleable.
32+
/// In general, all the guarantees of miniscript hold only for safe scripts.
33+
/// All the analysis guarantees of miniscript only hold safe scripts.
34+
/// The signer may not be able to find satisfactions even if one exists
35+
fn sanity_check(&self) -> Result<(), Error> {
36+
match *self {
37+
PreTaprootDescriptor::Bare(ref bare) => bare.sanity_check(),
38+
PreTaprootDescriptor::Pkh(ref pkh) => pkh.sanity_check(),
39+
PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.sanity_check(),
40+
PreTaprootDescriptor::Wsh(ref wsh) => wsh.sanity_check(),
41+
PreTaprootDescriptor::Sh(ref sh) => sh.sanity_check(),
42+
}
43+
}
44+
/// Computes the Bitcoin address of the descriptor, if one exists
45+
fn address(&self, network: bitcoin::Network) -> Result<bitcoin::Address, Error>
46+
where
47+
Pk: ToPublicKey,
48+
{
49+
match *self {
50+
PreTaprootDescriptor::Bare(ref bare) => bare.address(network),
51+
PreTaprootDescriptor::Pkh(ref pkh) => pkh.address(network),
52+
PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.address(network),
53+
PreTaprootDescriptor::Wsh(ref wsh) => wsh.address(network),
54+
PreTaprootDescriptor::Sh(ref sh) => sh.address(network),
55+
}
56+
}
57+
58+
/// Computes the scriptpubkey of the descriptor
59+
fn script_pubkey(&self) -> Script
60+
where
61+
Pk: ToPublicKey,
62+
{
63+
match *self {
64+
PreTaprootDescriptor::Bare(ref bare) => bare.script_pubkey(),
65+
PreTaprootDescriptor::Pkh(ref pkh) => pkh.script_pubkey(),
66+
PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.script_pubkey(),
67+
PreTaprootDescriptor::Wsh(ref wsh) => wsh.script_pubkey(),
68+
PreTaprootDescriptor::Sh(ref sh) => sh.script_pubkey(),
69+
}
70+
}
71+
72+
/// Computes the scriptSig that will be in place for an unsigned
73+
/// input spending an output with this descriptor. For pre-segwit
74+
/// descriptors, which use the scriptSig for signatures, this
75+
/// returns the empty script.
76+
///
77+
/// This is used in Segwit transactions to produce an unsigned
78+
/// transaction whose txid will not change during signing (since
79+
/// only the witness data will change).
80+
fn unsigned_script_sig(&self) -> Script
81+
where
82+
Pk: ToPublicKey,
83+
{
84+
match *self {
85+
PreTaprootDescriptor::Bare(ref bare) => bare.unsigned_script_sig(),
86+
PreTaprootDescriptor::Pkh(ref pkh) => pkh.unsigned_script_sig(),
87+
PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.unsigned_script_sig(),
88+
PreTaprootDescriptor::Wsh(ref wsh) => wsh.unsigned_script_sig(),
89+
PreTaprootDescriptor::Sh(ref sh) => sh.unsigned_script_sig(),
90+
}
91+
}
92+
93+
/// Computes the "witness script" of the descriptor, i.e. the underlying
94+
/// script before any hashing is done. For `Bare`, `Pkh` and `Wpkh` this
95+
/// is the scriptPubkey; for `ShWpkh` and `Sh` this is the redeemScript;
96+
/// for the others it is the witness script.
97+
/// Errors:
98+
/// - When the descriptor is Tr
99+
fn explicit_script(&self) -> Result<Script, Error>
100+
where
101+
Pk: ToPublicKey,
102+
{
103+
match *self {
104+
PreTaprootDescriptor::Bare(ref bare) => bare.explicit_script(),
105+
PreTaprootDescriptor::Pkh(ref pkh) => pkh.explicit_script(),
106+
PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.explicit_script(),
107+
PreTaprootDescriptor::Wsh(ref wsh) => wsh.explicit_script(),
108+
PreTaprootDescriptor::Sh(ref sh) => sh.explicit_script(),
109+
}
110+
}
111+
112+
/// Returns satisfying non-malleable witness and scriptSig to spend an
113+
/// output controlled by the given descriptor if it possible to
114+
/// construct one using the satisfier S.
115+
fn get_satisfaction<S>(&self, satisfier: S) -> Result<(Vec<Vec<u8>>, Script), Error>
116+
where
117+
Pk: ToPublicKey,
118+
S: Satisfier<Pk>,
119+
{
120+
match *self {
121+
PreTaprootDescriptor::Bare(ref bare) => bare.get_satisfaction(satisfier),
122+
PreTaprootDescriptor::Pkh(ref pkh) => pkh.get_satisfaction(satisfier),
123+
PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.get_satisfaction(satisfier),
124+
PreTaprootDescriptor::Wsh(ref wsh) => wsh.get_satisfaction(satisfier),
125+
PreTaprootDescriptor::Sh(ref sh) => sh.get_satisfaction(satisfier),
126+
}
127+
}
128+
129+
/// Returns a possilbly mallable satisfying non-malleable witness and scriptSig to spend an
130+
/// output controlled by the given descriptor if it possible to
131+
/// construct one using the satisfier S.
132+
fn get_satisfaction_mall<S>(&self, satisfier: S) -> Result<(Vec<Vec<u8>>, Script), Error>
133+
where
134+
Pk: ToPublicKey,
135+
S: Satisfier<Pk>,
136+
{
137+
match *self {
138+
PreTaprootDescriptor::Bare(ref bare) => bare.get_satisfaction_mall(satisfier),
139+
PreTaprootDescriptor::Pkh(ref pkh) => pkh.get_satisfaction_mall(satisfier),
140+
PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.get_satisfaction_mall(satisfier),
141+
PreTaprootDescriptor::Wsh(ref wsh) => wsh.get_satisfaction_mall(satisfier),
142+
PreTaprootDescriptor::Sh(ref sh) => sh.get_satisfaction_mall(satisfier),
143+
}
144+
}
145+
146+
/// Computes an upper bound on the weight of a satisfying witness to the
147+
/// transaction. Assumes all signatures are 73 bytes, including push opcode
148+
/// and sighash suffix. Includes the weight of the VarInts encoding the
149+
/// scriptSig and witness stack length.
150+
fn max_satisfaction_weight(&self) -> Result<usize, Error> {
151+
match *self {
152+
PreTaprootDescriptor::Bare(ref bare) => bare.max_satisfaction_weight(),
153+
PreTaprootDescriptor::Pkh(ref pkh) => pkh.max_satisfaction_weight(),
154+
PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.max_satisfaction_weight(),
155+
PreTaprootDescriptor::Wsh(ref wsh) => wsh.max_satisfaction_weight(),
156+
PreTaprootDescriptor::Sh(ref sh) => sh.max_satisfaction_weight(),
157+
}
158+
}
159+
160+
/// Get the `scriptCode` of a transaction output.
161+
///
162+
/// The `scriptCode` is the Script of the previous transaction output being serialized in the
163+
/// sighash when evaluating a `CHECKSIG` & co. OP code.
164+
/// Returns Error for Tr descriptors
165+
fn script_code(&self) -> Result<Script, Error>
166+
where
167+
Pk: ToPublicKey,
168+
{
169+
match *self {
170+
PreTaprootDescriptor::Bare(ref bare) => bare.script_code(),
171+
PreTaprootDescriptor::Pkh(ref pkh) => pkh.script_code(),
172+
PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.script_code(),
173+
PreTaprootDescriptor::Wsh(ref wsh) => wsh.script_code(),
174+
PreTaprootDescriptor::Sh(ref sh) => sh.script_code(),
175+
}
176+
}
177+
}
178+
179+
impl<Pk> expression::FromTree for PreTaprootDescriptor<Pk>
180+
where
181+
Pk: MiniscriptKey + str::FromStr,
182+
Pk::Hash: str::FromStr,
183+
<Pk as FromStr>::Err: ToString,
184+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
185+
{
186+
/// Parse an expression tree into a descriptor
187+
fn from_tree(top: &expression::Tree) -> Result<PreTaprootDescriptor<Pk>, Error> {
188+
Ok(match (top.name, top.args.len() as u32) {
189+
("pkh", 1) => PreTaprootDescriptor::Pkh(Pkh::from_tree(top)?),
190+
("wpkh", 1) => PreTaprootDescriptor::Wpkh(Wpkh::from_tree(top)?),
191+
("sh", 1) => PreTaprootDescriptor::Sh(Sh::from_tree(top)?),
192+
("wsh", 1) => PreTaprootDescriptor::Wsh(Wsh::from_tree(top)?),
193+
_ => PreTaprootDescriptor::Bare(Bare::from_tree(top)?),
194+
})
195+
}
196+
}
197+
198+
impl<Pk> FromStr for PreTaprootDescriptor<Pk>
199+
where
200+
Pk: MiniscriptKey + str::FromStr,
201+
Pk::Hash: str::FromStr,
202+
<Pk as FromStr>::Err: ToString,
203+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
204+
{
205+
type Err = Error;
206+
207+
fn from_str(s: &str) -> Result<PreTaprootDescriptor<Pk>, Error> {
208+
let desc_str = verify_checksum(s)?;
209+
let top = expression::Tree::from_str(desc_str)?;
210+
expression::FromTree::from_tree(&top)
211+
}
212+
}
213+
214+
impl<Pk: MiniscriptKey> fmt::Debug for PreTaprootDescriptor<Pk> {
215+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
216+
match *self {
217+
PreTaprootDescriptor::Bare(ref sub) => write!(f, "{:?}", sub),
218+
PreTaprootDescriptor::Pkh(ref pkh) => write!(f, "{:?}", pkh),
219+
PreTaprootDescriptor::Wpkh(ref wpkh) => write!(f, "{:?}", wpkh),
220+
PreTaprootDescriptor::Sh(ref sub) => write!(f, "{:?}", sub),
221+
PreTaprootDescriptor::Wsh(ref sub) => write!(f, "{:?}", sub),
222+
}
223+
}
224+
}
225+
226+
impl<Pk: MiniscriptKey> fmt::Display for PreTaprootDescriptor<Pk> {
227+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
228+
match *self {
229+
PreTaprootDescriptor::Bare(ref sub) => write!(f, "{}", sub),
230+
PreTaprootDescriptor::Pkh(ref pkh) => write!(f, "{}", pkh),
231+
PreTaprootDescriptor::Wpkh(ref wpkh) => write!(f, "{}", wpkh),
232+
PreTaprootDescriptor::Sh(ref sub) => write!(f, "{}", sub),
233+
PreTaprootDescriptor::Wsh(ref sub) => write!(f, "{}", sub),
234+
}
235+
}
236+
}
237+
238+
serde_string_impl_pk!(PreTaprootDescriptor, "a pre-taproot script descriptor");
239+
240+
// Have the trait in a separate module to avoid conflicts
241+
pub(crate) mod traits {
242+
use bitcoin::Script;
243+
244+
use {
245+
descriptor::{Pkh, Sh, Wpkh, Wsh},
246+
DescriptorTrait, MiniscriptKey, ToPublicKey,
247+
};
248+
249+
use super::PreTaprootDescriptor;
250+
251+
/// A general trait for Pre taproot bitcoin descriptor.
252+
/// Similar to [`DescriptorTrait`], but `explicit_script` and `script_code` methods cannot fail
253+
pub trait PreTaprootDescriptorTrait<Pk: MiniscriptKey>: DescriptorTrait<Pk> {
254+
/// Same as [`DescriptorTrait::explicit_script`], but a non failing version.
255+
/// All PreTaproot descriptors have a unique explicit script
256+
fn explicit_script(&self) -> Script
257+
where
258+
Pk: ToPublicKey,
259+
{
260+
// This expect can technically be avoided if we implement this for types, but
261+
// having this expect saves lots of LoC because of default implementation
262+
<Self as DescriptorTrait<Pk>>::explicit_script(&self)
263+
.expect("Pre taproot descriptor have explicit script")
264+
}
265+
266+
/// Same as [`DescriptorTrait::script_code`], but a non failing version.
267+
/// All PreTaproot descriptors have a script code
268+
fn script_code(&self) -> Script
269+
where
270+
Pk: ToPublicKey,
271+
{
272+
<Self as DescriptorTrait<Pk>>::script_code(&self)
273+
.expect("Pre taproot descriptor have non-failing script code")
274+
}
275+
}
276+
277+
impl<Pk: MiniscriptKey> PreTaprootDescriptorTrait<Pk> for Pkh<Pk> {}
278+
279+
impl<Pk: MiniscriptKey> PreTaprootDescriptorTrait<Pk> for Sh<Pk> {}
280+
281+
impl<Pk: MiniscriptKey> PreTaprootDescriptorTrait<Pk> for Wpkh<Pk> {}
282+
283+
impl<Pk: MiniscriptKey> PreTaprootDescriptorTrait<Pk> for Wsh<Pk> {}
284+
285+
impl<Pk: MiniscriptKey> PreTaprootDescriptorTrait<Pk> for PreTaprootDescriptor<Pk> {}
286+
}

src/descriptor/tr.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,13 @@ impl<Pk: MiniscriptKey> Clone for Tr<Pk> {
6060
Self {
6161
internal_key: self.internal_key.clone(),
6262
tree: self.tree.clone(),
63-
spend_info: Mutex::new(self.spend_info.lock().expect("Lock poisoned").clone()),
63+
spend_info: Mutex::new(
64+
self.spend_info
65+
.lock()
66+
.expect("Lock poisoned")
67+
.as_ref()
68+
.map(Arc::clone),
69+
),
6470
}
6571
}
6672
}
@@ -216,7 +222,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
216222
// read only panics if the lock is poisoned (meaning other thread having a lock panicked)
217223
let read_lock = self.spend_info.lock().expect("Lock poisoned");
218224
if let Some(ref spend_info) = *read_lock {
219-
return spend_info.clone();
225+
return Arc::clone(spend_info);
220226
}
221227
drop(read_lock);
222228

@@ -260,7 +266,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
260266
}
261267
};
262268
let spend_info = Arc::new(data);
263-
*self.spend_info.lock().expect("Lock poisoned") = Some(spend_info.clone());
269+
*self.spend_info.lock().expect("Lock poisoned") = Some(Arc::clone(&spend_info));
264270
spend_info
265271
}
266272
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ pub use miniscript::decode::Terminal;
129129
pub use miniscript::satisfy::{Preimage32, Satisfier};
130130
pub use miniscript::Miniscript;
131131

132+
pub use descriptor::pretaproot::{traits::PreTaprootDescriptorTrait, PreTaprootDescriptor};
133+
132134
///Public key trait which can be converted to Hash type
133135
pub trait MiniscriptKey: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash {
134136
/// Check if the publicKey is uncompressed. The default

0 commit comments

Comments
 (0)