Skip to content

Commit 0c49b1d

Browse files
committed
Added bare descriptor
Add utility function to verify checksum
1 parent 251f499 commit 0c49b1d

File tree

3 files changed

+366
-0
lines changed

3 files changed

+366
-0
lines changed

src/descriptor/bare.rs

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
// Miniscript
2+
// Written in 2020 by rust-miniscript developers
3+
//
4+
// To the extent possible under law, the author(s) have dedicated all
5+
// copyright and related and neighboring rights to this software to
6+
// the public domain worldwide. This software is distributed without
7+
// any warranty.
8+
//
9+
// You should have received a copy of the CC0 Public Domain Dedication
10+
// along with this software.
11+
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
12+
//
13+
14+
//! # Bare Output Descriptors
15+
//!
16+
//! Implementation of Bare Descriptors (i.e descriptors that are)
17+
//! wrapped inside wsh, or sh fragments.
18+
//! Also includes pk, and pkh descriptors
19+
//!
20+
21+
use std::{fmt, str::FromStr};
22+
23+
use bitcoin::{self, blockdata::script, Script};
24+
25+
use expression::{self, FromTree};
26+
use miniscript::context::ScriptContext;
27+
use policy::{semantic, Liftable};
28+
use util::{varint_len, witness_to_scriptsig};
29+
use {BareCtx, Error, Miniscript, MiniscriptKey, Satisfier, ToPublicKey};
30+
31+
use super::{
32+
checksum::{desc_checksum, verify_checksum},
33+
DescriptorTrait,
34+
};
35+
36+
/// Create a Bare Descriptor. That is descriptor that is
37+
/// not wrapped in sh or wsh. This covers the Pk descriptor
38+
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
39+
pub struct Bare<Pk: MiniscriptKey> {
40+
/// underlying miniscript
41+
ms: Miniscript<Pk, BareCtx>,
42+
}
43+
44+
impl<Pk: MiniscriptKey> Bare<Pk> {
45+
/// Create a new raw descriptor
46+
pub fn new(ms: Miniscript<Pk, BareCtx>) -> Result<Self, Error> {
47+
// do the top-level checks
48+
BareCtx::top_level_checks(&ms)?;
49+
Ok(Self { ms: ms })
50+
}
51+
52+
/// get the inner
53+
pub fn as_inner(&self) -> &Miniscript<Pk, BareCtx> {
54+
&self.ms
55+
}
56+
}
57+
58+
impl<Pk: MiniscriptKey> fmt::Debug for Bare<Pk> {
59+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60+
write!(f, "{:?}", self.ms)
61+
}
62+
}
63+
64+
impl<Pk: MiniscriptKey> fmt::Display for Bare<Pk> {
65+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66+
let desc = format!("{}", self.ms);
67+
let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?;
68+
write!(f, "{}#{}", &desc, &checksum)
69+
}
70+
}
71+
72+
impl<Pk: MiniscriptKey> Liftable<Pk> for Bare<Pk> {
73+
fn lift(&self) -> Result<semantic::Policy<Pk>, Error> {
74+
self.ms.lift()
75+
}
76+
}
77+
78+
impl<Pk: MiniscriptKey> FromTree for Bare<Pk>
79+
where
80+
<Pk as FromStr>::Err: ToString,
81+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
82+
{
83+
fn from_tree(top: &expression::Tree) -> Result<Self, Error> {
84+
let sub = Miniscript::<Pk, BareCtx>::from_tree(&top)?;
85+
BareCtx::top_level_checks(&sub)?;
86+
Bare::new(sub)
87+
}
88+
}
89+
90+
impl<Pk: MiniscriptKey> FromStr for Bare<Pk>
91+
where
92+
<Pk as FromStr>::Err: ToString,
93+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
94+
{
95+
type Err = Error;
96+
97+
fn from_str(s: &str) -> Result<Self, Self::Err> {
98+
let desc_str = verify_checksum(s)?;
99+
let top = expression::Tree::from_str(desc_str)?;
100+
Self::from_tree(&top)
101+
}
102+
}
103+
104+
impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Bare<Pk>
105+
where
106+
<Pk as FromStr>::Err: ToString,
107+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
108+
{
109+
fn sanity_check(&self) -> Result<(), Error> {
110+
self.ms.sanity_check()?;
111+
Ok(())
112+
}
113+
114+
fn address<ToPkCtx: Copy>(
115+
&self,
116+
_to_pk_ctx: ToPkCtx,
117+
_network: bitcoin::Network,
118+
) -> Option<bitcoin::Address>
119+
where
120+
Pk: ToPublicKey<ToPkCtx>,
121+
{
122+
None
123+
}
124+
125+
fn script_pubkey<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> Script
126+
where
127+
Pk: ToPublicKey<ToPkCtx>,
128+
{
129+
self.ms.encode(to_pk_ctx)
130+
}
131+
132+
fn unsigned_script_sig<ToPkCtx: Copy>(&self, _to_pk_ctx: ToPkCtx) -> Script
133+
where
134+
Pk: ToPublicKey<ToPkCtx>,
135+
{
136+
Script::new()
137+
}
138+
139+
fn witness_script<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> Script
140+
where
141+
Pk: ToPublicKey<ToPkCtx>,
142+
{
143+
self.ms.encode(to_pk_ctx)
144+
}
145+
146+
fn get_satisfaction<ToPkCtx, S>(
147+
&self,
148+
satisfier: S,
149+
to_pk_ctx: ToPkCtx,
150+
) -> Result<(Vec<Vec<u8>>, Script), Error>
151+
where
152+
ToPkCtx: Copy,
153+
Pk: ToPublicKey<ToPkCtx>,
154+
S: Satisfier<ToPkCtx, Pk>,
155+
{
156+
let ms = self.ms.satisfy(satisfier, to_pk_ctx)?;
157+
let script_sig = witness_to_scriptsig(&ms);
158+
let witness = vec![];
159+
Ok((witness, script_sig))
160+
}
161+
162+
fn max_satisfaction_weight<ToPkCtx: Copy>(&self) -> Option<usize>
163+
where
164+
Pk: ToPublicKey<ToPkCtx>,
165+
{
166+
let scriptsig_len = self.ms.max_satisfaction_size()?;
167+
Some(4 * (varint_len(scriptsig_len) + scriptsig_len))
168+
}
169+
170+
fn script_code<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> Script
171+
where
172+
Pk: ToPublicKey<ToPkCtx>,
173+
{
174+
self.script_pubkey(to_pk_ctx)
175+
}
176+
}
177+
178+
/// A bare PkH descriptor at top level
179+
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
180+
pub struct Pkh<Pk: MiniscriptKey> {
181+
/// underlying publickey
182+
pk: Pk,
183+
}
184+
185+
impl<Pk: MiniscriptKey> Pkh<Pk> {
186+
/// Create a new Pkh descriptor
187+
pub fn new(pk: Pk) -> Self {
188+
// do the top-level checks
189+
Self { pk: pk }
190+
}
191+
192+
/// Get the inner key
193+
pub fn as_inner(&self) -> &Pk {
194+
&self.pk
195+
}
196+
}
197+
198+
impl<Pk: MiniscriptKey> fmt::Debug for Pkh<Pk> {
199+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200+
write!(f, "pkh({:?})", self.pk)
201+
}
202+
}
203+
204+
impl<Pk: MiniscriptKey> fmt::Display for Pkh<Pk> {
205+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206+
let desc = format!("pkh({})", self.pk);
207+
let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?;
208+
write!(f, "{}#{}", &desc, &checksum)
209+
}
210+
}
211+
212+
impl<Pk: MiniscriptKey> Liftable<Pk> for Pkh<Pk> {
213+
fn lift(&self) -> Result<semantic::Policy<Pk>, Error> {
214+
Ok(semantic::Policy::KeyHash(self.pk.to_pubkeyhash()))
215+
}
216+
}
217+
218+
impl<Pk: MiniscriptKey> FromTree for Pkh<Pk>
219+
where
220+
<Pk as FromStr>::Err: ToString,
221+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
222+
{
223+
fn from_tree(top: &expression::Tree) -> Result<Self, Error> {
224+
if top.name == "pkh" && top.args.len() == 1 {
225+
Ok(Pkh::new(expression::terminal(&top.args[0], |pk| {
226+
Pk::from_str(pk)
227+
})?))
228+
} else {
229+
Err(Error::Unexpected(format!(
230+
"{}({} args) while parsing pkh descriptor",
231+
top.name,
232+
top.args.len(),
233+
)))
234+
}
235+
}
236+
}
237+
238+
impl<Pk: MiniscriptKey> FromStr for Pkh<Pk>
239+
where
240+
<Pk as FromStr>::Err: ToString,
241+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
242+
{
243+
type Err = Error;
244+
245+
fn from_str(s: &str) -> Result<Self, Self::Err> {
246+
let desc_str = verify_checksum(s)?;
247+
let top = expression::Tree::from_str(desc_str)?;
248+
Self::from_tree(&top)
249+
}
250+
}
251+
252+
impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Pkh<Pk>
253+
where
254+
<Pk as FromStr>::Err: ToString,
255+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
256+
{
257+
fn sanity_check(&self) -> Result<(), Error> {
258+
Ok(())
259+
}
260+
261+
fn address<ToPkCtx: Copy>(
262+
&self,
263+
to_pk_ctx: ToPkCtx,
264+
network: bitcoin::Network,
265+
) -> Option<bitcoin::Address>
266+
where
267+
Pk: ToPublicKey<ToPkCtx>,
268+
{
269+
Some(bitcoin::Address::p2pkh(
270+
&self.pk.to_public_key(to_pk_ctx),
271+
network,
272+
))
273+
}
274+
275+
fn script_pubkey<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> Script
276+
where
277+
Pk: ToPublicKey<ToPkCtx>,
278+
{
279+
let addr =
280+
bitcoin::Address::p2pkh(&self.pk.to_public_key(to_pk_ctx), bitcoin::Network::Bitcoin);
281+
addr.script_pubkey()
282+
}
283+
284+
fn unsigned_script_sig<ToPkCtx: Copy>(&self, _to_pk_ctx: ToPkCtx) -> Script
285+
where
286+
Pk: ToPublicKey<ToPkCtx>,
287+
{
288+
Script::new()
289+
}
290+
291+
fn witness_script<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> Script
292+
where
293+
Pk: ToPublicKey<ToPkCtx>,
294+
{
295+
self.script_pubkey(to_pk_ctx)
296+
}
297+
298+
fn get_satisfaction<ToPkCtx, S>(
299+
&self,
300+
satisfier: S,
301+
to_pk_ctx: ToPkCtx,
302+
) -> Result<(Vec<Vec<u8>>, Script), Error>
303+
where
304+
ToPkCtx: Copy,
305+
Pk: ToPublicKey<ToPkCtx>,
306+
S: Satisfier<ToPkCtx, Pk>,
307+
{
308+
if let Some(sig) = satisfier.lookup_sig(&self.pk, to_pk_ctx) {
309+
let mut sig_vec = sig.0.serialize_der().to_vec();
310+
sig_vec.push(sig.1.as_u32() as u8);
311+
let script_sig = script::Builder::new()
312+
.push_slice(&sig_vec[..])
313+
.push_key(&self.pk.to_public_key(to_pk_ctx))
314+
.into_script();
315+
let witness = vec![];
316+
Ok((witness, script_sig))
317+
} else {
318+
Err(Error::MissingSig(self.pk.to_public_key(to_pk_ctx)))
319+
}
320+
}
321+
322+
fn max_satisfaction_weight<ToPkCtx: Copy>(&self) -> Option<usize>
323+
where
324+
Pk: ToPublicKey<ToPkCtx>,
325+
{
326+
Some(4 * (1 + 73 + self.pk.serialized_len()))
327+
}
328+
329+
fn script_code<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> Script
330+
where
331+
Pk: ToPublicKey<ToPkCtx>,
332+
{
333+
self.script_pubkey(to_pk_ctx)
334+
}
335+
}

src/descriptor/checksum.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ fn poly_mod(mut c: u64, val: u64) -> u64 {
3434
}
3535

3636
/// Compute the checksum of a descriptor
37+
/// Note that this function does not check if the
38+
/// descriptor string is syntactically correct or not.
39+
/// This only computes the checksum
3740
pub fn desc_checksum(desc: &str) -> Result<String, Error> {
3841
let mut c = 1;
3942
let mut cls = 0;
@@ -72,6 +75,30 @@ pub fn desc_checksum(desc: &str) -> Result<String, Error> {
7275
Ok(String::from_iter(chars))
7376
}
7477

78+
/// Helper function for FromStr for various
79+
/// descriptor types. Checks and verifies the checksum
80+
/// if it is present and returns the descriptor string
81+
/// without the checksum
82+
pub(super) fn verify_checksum(s: &str) -> Result<&str, Error> {
83+
for ch in s.as_bytes() {
84+
if *ch < 20 || *ch > 127 {
85+
return Err(Error::Unprintable(*ch));
86+
}
87+
}
88+
89+
let mut parts = s.splitn(2, '#');
90+
let desc_str = parts.next().unwrap();
91+
if let Some(checksum_str) = parts.next() {
92+
let expected_sum = desc_checksum(desc_str)?;
93+
if checksum_str != expected_sum {
94+
return Err(Error::BadDescriptor(format!(
95+
"Invalid checksum '{}', expected '{}'",
96+
checksum_str, expected_sum
97+
)));
98+
}
99+
}
100+
Ok(desc_str)
101+
}
75102
#[cfg(test)]
76103
mod test {
77104
use super::*;

src/descriptor/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ use MiniscriptKey;
5050
use Satisfier;
5151
use ToPublicKey;
5252

53+
mod bare;
54+
// Descriptor Exports
55+
pub use self::bare::{Bare, Pkh};
56+
5357
mod checksum;
5458
mod key;
5559
use self::checksum::desc_checksum;

0 commit comments

Comments
 (0)