Skip to content

Commit 9e0735e

Browse files
committed
Add pretaproot descriptors/remove macros
1 parent b827c26 commit 9e0735e

File tree

3 files changed

+291
-0
lines changed

3 files changed

+291
-0
lines changed

src/descriptor/mod.rs

Lines changed: 3 additions & 0 deletions
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;

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/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)