Skip to content

Commit d4d9f1a

Browse files
committed
sh added
1 parent 36dbf23 commit d4d9f1a

File tree

2 files changed

+341
-1
lines changed

2 files changed

+341
-1
lines changed

src/descriptor/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@ use ToPublicKey;
5252

5353
mod bare;
5454
mod segwitv0;
55+
mod sh;
5556
// Descriptor Exports
5657
pub use self::bare::{Bare, Pkh};
5758
pub use self::segwitv0::{Wpkh, Wsh};
58-
//
59+
pub use self::sh::Sh;
60+
5961
mod checksum;
6062
mod key;
6163
use self::checksum::desc_checksum;

src/descriptor/sh.rs

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
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+
//! # P2SH Descriptors
15+
//!
16+
//! Implementation of p2sh descriptors. Contains the implementation
17+
//! of sh, wrapped fragments for sh which include wsh, sortedmulti
18+
//! sh(miniscript), and sh(wpkh)
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 push_opcode_size;
29+
use util::{varint_len, witness_to_scriptsig};
30+
use {Error, Legacy, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey};
31+
32+
use super::{
33+
checksum::{desc_checksum, verify_checksum},
34+
DescriptorTrait, SortedMultiVec, Wpkh, Wsh,
35+
};
36+
37+
/// A Legacy p2sh Descriptor
38+
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
39+
pub struct Sh<Pk: MiniscriptKey> {
40+
/// underlying miniscript
41+
inner: ShInner<Pk>,
42+
}
43+
44+
/// Sh Inner
45+
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
46+
enum ShInner<Pk: MiniscriptKey> {
47+
/// Nested Wsh
48+
Wsh(Wsh<Pk>),
49+
/// Nested Wpkh
50+
Wpkh(Wpkh<Pk>),
51+
/// Inner Sorted Multi
52+
SortedMulti(SortedMultiVec<Pk, Legacy>),
53+
/// p2sh miniscript
54+
Ms(Miniscript<Pk, Legacy>),
55+
}
56+
57+
impl<Pk: MiniscriptKey> Liftable<Pk> for Sh<Pk> {
58+
fn lift(&self) -> Result<semantic::Policy<Pk>, Error> {
59+
match self.inner {
60+
ShInner::Wsh(ref wsh) => wsh.lift(),
61+
ShInner::Wpkh(ref pk) => Ok(semantic::Policy::KeyHash(pk.as_inner().to_pubkeyhash())),
62+
ShInner::SortedMulti(ref smv) => smv.lift(),
63+
ShInner::Ms(ref ms) => ms.lift(),
64+
}
65+
}
66+
}
67+
68+
impl<Pk: MiniscriptKey> fmt::Debug for Sh<Pk> {
69+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70+
match self.inner {
71+
ShInner::Wsh(ref wsh_inner) => write!(f, "sh({:?})", wsh_inner),
72+
ShInner::Wpkh(ref pk) => write!(f, "sh({:?})", pk),
73+
ShInner::SortedMulti(ref smv) => write!(f, "sh({:?})", smv),
74+
ShInner::Ms(ref ms) => write!(f, "sh({:?})", ms),
75+
}
76+
}
77+
}
78+
79+
impl<Pk: MiniscriptKey> fmt::Display for Sh<Pk> {
80+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81+
let desc = match self.inner {
82+
ShInner::Wsh(ref wsh_inner) => format!("sh({})", wsh_inner),
83+
ShInner::Wpkh(ref pk) => format!("sh({})", pk),
84+
ShInner::SortedMulti(ref smv) => format!("sh({})", smv),
85+
ShInner::Ms(ref ms) => format!("sh({})", ms),
86+
};
87+
let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?;
88+
write!(f, "{}#{}", &desc, &checksum)
89+
}
90+
}
91+
92+
impl<Pk: MiniscriptKey> FromTree for Sh<Pk>
93+
where
94+
<Pk as FromStr>::Err: ToString,
95+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
96+
{
97+
fn from_tree(top: &expression::Tree) -> Result<Self, Error> {
98+
if top.name == "sh" && top.args.len() == 1 {
99+
let top = &top.args[0];
100+
let inner = match top.name {
101+
"wsh" => ShInner::Wsh(Wsh::from_tree(&top)?),
102+
"wpkh" => ShInner::Wpkh(Wpkh::from_tree(&top)?),
103+
"sortedmulti" => ShInner::SortedMulti(SortedMultiVec::from_tree(&top)?),
104+
_ => {
105+
let sub = Miniscript::from_tree(&top)?;
106+
Legacy::top_level_checks(&sub)?;
107+
ShInner::Ms(sub)
108+
}
109+
};
110+
Ok(Sh { inner: inner })
111+
} else {
112+
Err(Error::Unexpected(format!(
113+
"{}({} args) while parsing sh descriptor",
114+
top.name,
115+
top.args.len(),
116+
)))
117+
}
118+
}
119+
}
120+
121+
impl<Pk: MiniscriptKey> FromStr for Sh<Pk>
122+
where
123+
<Pk as FromStr>::Err: ToString,
124+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
125+
{
126+
type Err = Error;
127+
fn from_str(s: &str) -> Result<Self, Self::Err> {
128+
let desc_str = verify_checksum(s)?;
129+
let top = expression::Tree::from_str(desc_str)?;
130+
Self::from_tree(&top)
131+
}
132+
}
133+
134+
impl<Pk: MiniscriptKey> Sh<Pk> {
135+
/// Create a new p2sh descriptor with the raw miniscript
136+
pub fn new(ms: Miniscript<Pk, Legacy>) -> Result<Self, Error> {
137+
// do the top-level checks
138+
Legacy::top_level_checks(&ms)?;
139+
Ok(Self {
140+
inner: ShInner::Ms(ms),
141+
})
142+
}
143+
144+
/// Create a new p2sh sortedmulti descriptor with threshold `k`
145+
/// and Vec of `pks`.
146+
pub fn new_sortedmulti(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
147+
// The context checks will be carried out inside new function for
148+
// sortedMultiVec
149+
Ok(Self {
150+
inner: ShInner::SortedMulti(SortedMultiVec::new(k, pks)?),
151+
})
152+
}
153+
154+
/// Create a new p2sh wrapped wsh descriptor with the raw miniscript
155+
pub fn new_nested_wsh(ms: Miniscript<Pk, Segwitv0>) -> Result<Self, Error> {
156+
Ok(Self {
157+
inner: ShInner::Wsh(Wsh::new(ms)?),
158+
})
159+
}
160+
161+
/// Create a new p2sh wrapped wsh sortedmulti descriptor from threshold
162+
/// `k` and Vec of `pks`
163+
pub fn new_nested_wsh_sortedmulti(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
164+
// The context checks will be carried out inside new function for
165+
// sortedMultiVec
166+
Ok(Self {
167+
inner: ShInner::Wsh(Wsh::new_sortedmulti(k, pks)?),
168+
})
169+
}
170+
171+
/// Create a new p2sh wrapped wpkh from `Pk`
172+
pub fn new_nested_wpkh(pk: Pk) -> Result<Self, Error> {
173+
Ok(Self {
174+
inner: ShInner::Wpkh(Wpkh::new(pk)?),
175+
})
176+
}
177+
}
178+
179+
impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Sh<Pk>
180+
where
181+
<Pk as FromStr>::Err: ToString,
182+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
183+
{
184+
fn sanity_check(&self) -> Result<(), Error> {
185+
match self.inner {
186+
ShInner::Wsh(ref wsh) => wsh.sanity_check()?,
187+
ShInner::Wpkh(ref wpkh) => wpkh.sanity_check()?,
188+
ShInner::SortedMulti(ref smv) => smv.sanity_check()?,
189+
ShInner::Ms(ref ms) => ms.sanity_check()?,
190+
}
191+
Ok(())
192+
}
193+
194+
fn address<ToPkCtx: Copy>(
195+
&self,
196+
to_pk_ctx: ToPkCtx,
197+
network: bitcoin::Network,
198+
) -> Option<bitcoin::Address>
199+
where
200+
Pk: ToPublicKey<ToPkCtx>,
201+
{
202+
match self.inner {
203+
ShInner::Wsh(ref wsh) => Some(bitcoin::Address::p2sh(
204+
&wsh.script_pubkey(to_pk_ctx),
205+
network,
206+
)),
207+
ShInner::Wpkh(ref wpkh) => Some(bitcoin::Address::p2sh(
208+
&wpkh.script_pubkey(to_pk_ctx),
209+
network,
210+
)),
211+
ShInner::SortedMulti(ref smv) => {
212+
Some(bitcoin::Address::p2sh(&smv.encode(to_pk_ctx), network))
213+
}
214+
ShInner::Ms(ref ms) => Some(bitcoin::Address::p2sh(&ms.encode(to_pk_ctx), network)),
215+
}
216+
}
217+
218+
fn script_pubkey<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> Script
219+
where
220+
Pk: ToPublicKey<ToPkCtx>,
221+
{
222+
match self.inner {
223+
ShInner::Wsh(ref wsh) => wsh.script_pubkey(to_pk_ctx).to_p2sh(),
224+
ShInner::Wpkh(ref wpkh) => wpkh.script_pubkey(to_pk_ctx).to_p2sh(),
225+
ShInner::SortedMulti(ref smv) => smv.encode(to_pk_ctx).to_p2sh(),
226+
ShInner::Ms(ref ms) => ms.encode(to_pk_ctx).to_p2sh(),
227+
}
228+
}
229+
230+
fn unsigned_script_sig<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> Script
231+
where
232+
Pk: ToPublicKey<ToPkCtx>,
233+
{
234+
match self.inner {
235+
ShInner::Wsh(ref wsh) => {
236+
let witness_script = wsh.witness_script(to_pk_ctx);
237+
script::Builder::new()
238+
.push_slice(&witness_script.to_v0_p2wsh()[..])
239+
.into_script()
240+
}
241+
ShInner::Wpkh(ref wpkh) => {
242+
let redeem_script = wpkh.script_pubkey(to_pk_ctx);
243+
script::Builder::new()
244+
.push_slice(&redeem_script[..])
245+
.into_script()
246+
}
247+
ShInner::SortedMulti(..) | ShInner::Ms(..) => Script::new(),
248+
}
249+
}
250+
251+
fn witness_script<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> Script
252+
where
253+
Pk: ToPublicKey<ToPkCtx>,
254+
{
255+
match self.inner {
256+
ShInner::Wsh(ref wsh) => wsh.witness_script(to_pk_ctx),
257+
ShInner::Wpkh(ref wpkh) => wpkh.script_pubkey(to_pk_ctx),
258+
ShInner::SortedMulti(ref smv) => smv.encode(to_pk_ctx),
259+
ShInner::Ms(ref ms) => ms.encode(to_pk_ctx),
260+
}
261+
}
262+
263+
fn get_satisfaction<ToPkCtx, S>(
264+
&self,
265+
satisfier: S,
266+
to_pk_ctx: ToPkCtx,
267+
) -> Result<(Vec<Vec<u8>>, Script), Error>
268+
where
269+
ToPkCtx: Copy,
270+
Pk: ToPublicKey<ToPkCtx>,
271+
S: Satisfier<ToPkCtx, Pk>,
272+
{
273+
let script_sig = self.unsigned_script_sig(to_pk_ctx);
274+
match self.inner {
275+
ShInner::Wsh(ref wsh) => {
276+
let (witness, _) = wsh.get_satisfaction(satisfier, to_pk_ctx)?;
277+
Ok((witness, script_sig))
278+
}
279+
ShInner::Wpkh(ref wpkh) => {
280+
let (witness, _) = wpkh.get_satisfaction(satisfier, to_pk_ctx)?;
281+
Ok((witness, script_sig))
282+
}
283+
ShInner::SortedMulti(ref smv) => {
284+
let mut script_witness = smv.satisfy(satisfier, to_pk_ctx)?;
285+
script_witness.push(smv.encode(to_pk_ctx).into_bytes());
286+
let script_sig = witness_to_scriptsig(&script_witness);
287+
let witness = vec![];
288+
Ok((witness, script_sig))
289+
}
290+
ShInner::Ms(ref ms) => {
291+
let mut script_witness = ms.satisfy(satisfier, to_pk_ctx)?;
292+
script_witness.push(ms.encode(to_pk_ctx).into_bytes());
293+
let script_sig = witness_to_scriptsig(&script_witness);
294+
let witness = vec![];
295+
Ok((witness, script_sig))
296+
}
297+
}
298+
}
299+
300+
fn max_satisfaction_weight<ToPkCtx: Copy>(&self) -> Option<usize>
301+
where
302+
Pk: ToPublicKey<ToPkCtx>,
303+
{
304+
Some(match self.inner {
305+
// add weighted script sig, len byte stays the same
306+
ShInner::Wsh(ref wsh) => 4 * 35 + wsh.max_satisfaction_weight()?,
307+
ShInner::SortedMulti(ref smv) => {
308+
let ss = smv.script_size();
309+
let ps = push_opcode_size(ss);
310+
let scriptsig_len = ps + ss + smv.max_satisfaction_size(1);
311+
4 * (varint_len(scriptsig_len) + scriptsig_len)
312+
}
313+
// add weighted script sig, len byte stays the same
314+
ShInner::Wpkh(ref wpkh) => 4 * 23 + wpkh.max_satisfaction_weight()?,
315+
ShInner::Ms(ref ms) => {
316+
let ss = ms.script_size();
317+
let ps = push_opcode_size(ss);
318+
let scriptsig_len = ps + ss + ms.max_satisfaction_size()?;
319+
4 * (varint_len(scriptsig_len) + scriptsig_len)
320+
}
321+
})
322+
}
323+
324+
fn script_code<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> Script
325+
where
326+
Pk: ToPublicKey<ToPkCtx>,
327+
{
328+
match self.inner {
329+
// - For P2WSH witness program, if the witnessScript does not contain any `OP_CODESEPARATOR`,
330+
// the `scriptCode` is the `witnessScript` serialized as scripts inside CTxOut.
331+
ShInner::Wsh(ref wsh) => wsh.script_code(to_pk_ctx),
332+
ShInner::SortedMulti(ref smv) => smv.encode(to_pk_ctx),
333+
ShInner::Wpkh(ref wpkh) => wpkh.script_code(to_pk_ctx),
334+
// For "legacy" P2SH outputs, it is defined as the txo's redeemScript.
335+
ShInner::Ms(ref ms) => ms.encode(to_pk_ctx),
336+
}
337+
}
338+
}

0 commit comments

Comments
 (0)