Skip to content

Commit c2cfb2f

Browse files
committed
Added ScriptContext to Miniscript
1 parent 5f8b7e5 commit c2cfb2f

File tree

19 files changed

+841
-387
lines changed

19 files changed

+841
-387
lines changed

examples/verify_tx.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ fn main() {
103103
0,
104104
0,
105105
);
106-
107106
println!("\nExample one");
108107
for elem in iter {
109108
match elem.expect("no evaluation error") {
@@ -129,7 +128,6 @@ fn main() {
129128
0,
130129
0,
131130
);
132-
133131
println!("\nExample two");
134132
for elem in iter {
135133
match elem.expect("no evaluation error") {
@@ -154,7 +152,6 @@ fn main() {
154152
0,
155153
0,
156154
);
157-
158155
println!("\nExample three");
159156
for elem in iter {
160157
let error = elem.expect_err("evaluation error");

fuzz/fuzz_targets/compile_descriptor.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
extern crate miniscript;
22

33
use miniscript::{policy, DummyKey, Miniscript};
4+
use miniscript::context::Segwitv0;
45
use policy::Liftable;
56

67
use std::str::FromStr;
78

8-
type DummyScript = Miniscript<DummyKey>;
9+
type DummyScript = Miniscript<DummyKey, Segwitv0>;
910
type DummyPolicy = policy::Concrete<DummyKey>;
1011

1112
fn do_test(data: &[u8]) {
1213
let data_str = String::from_utf8_lossy(data);
1314
if let Ok(pol) = DummyPolicy::from_str(&data_str) {
1415
// Compile
15-
if let Ok(desc) = pol.compile() {
16+
if let Ok(desc) = pol.compile::<Segwitv0>() {
1617
// Lift
1718
assert_eq!(desc.clone().lift(), pol.clone().lift());
1819
// Try to roundtrip the output of the compiler

fuzz/fuzz_targets/roundtrip_miniscript_script.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ extern crate miniscript;
33

44
use miniscript::Miniscript;
55
use miniscript::bitcoin::blockdata::script;
6+
use miniscript::context::Segwitv0;
67

78
fn do_test(data: &[u8]) {
89
// Try round-tripping as a script
910
let script = script::Script::from(data.to_owned());
1011

11-
if let Ok(pt) = Miniscript::parse(&script) {
12+
if let Ok(pt) = Miniscript::<_, Segwitv0>::parse(&script) {
1213
let output = pt.encode();
1314
assert_eq!(pt.script_size(), output.len());
1415
assert_eq!(output, script);

fuzz/fuzz_targets/roundtrip_miniscript_str.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ use std::str::FromStr;
66
use regex::Regex;
77

88
use miniscript::{DummyKey};
9+
use miniscript::context::Segwitv0;
910
use miniscript::Miniscript;
1011

1112
fn do_test(data: &[u8]) {
1213
let s = String::from_utf8_lossy(data);
13-
if let Ok(desc) = Miniscript::<DummyKey>::from_str(&s) {
14+
if let Ok(desc) = Miniscript::<DummyKey, Segwitv0>::from_str(&s) {
1415
let output = desc.to_string();
1516

1617
let multi_wrap_pk_re = Regex::new("([a-z]+)c:pk_k\\(").unwrap();

src/descriptor/create_descriptor.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use bitcoin::blockdata::script::Instruction;
99
use descriptor::satisfied_constraints::Error as IntError;
1010
use descriptor::satisfied_constraints::{Stack, StackElement};
1111
use descriptor::Descriptor;
12-
use miniscript::Miniscript;
12+
use miniscript::{Legacy, Miniscript, Segwitv0};
1313
use Error;
1414
use ToPublicKey;
1515

@@ -112,7 +112,7 @@ fn verify_wsh<'txin>(
112112
script_pubkey: &bitcoin::Script,
113113
script_sig: &bitcoin::Script,
114114
witness: &'txin [Vec<u8>],
115-
) -> Result<(Miniscript<bitcoin::PublicKey>, Stack<'txin>), Error> {
115+
) -> Result<(Miniscript<bitcoin::PublicKey, Segwitv0>, Stack<'txin>), Error> {
116116
if !script_sig.is_empty() {
117117
return Err(Error::NonEmptyScriptSig);
118118
}
@@ -121,7 +121,7 @@ fn verify_wsh<'txin>(
121121
if witness_script.to_v0_p2wsh() != *script_pubkey {
122122
return Err(Error::IncorrectScriptHash);
123123
}
124-
let ms = Miniscript::parse(&witness_script)?;
124+
let ms = Miniscript::<bitcoin::PublicKey, Segwitv0>::parse(&witness_script)?;
125125
//only iter till len -1 to not include the witness script
126126
let stack: Vec<StackElement> = witness
127127
.iter()
@@ -218,7 +218,7 @@ pub fn from_txin_with_witness_stack<'txin>(
218218
if !witness.is_empty() {
219219
return Err(Error::NonEmptyWitness);
220220
}
221-
let ms = Miniscript::parse(&redeem_script)?;
221+
let ms = Miniscript::<bitcoin::PublicKey, Legacy>::parse(&redeem_script)?;
222222
Ok((Descriptor::Sh(ms), stack))
223223
}
224224
} else {
@@ -230,7 +230,7 @@ pub fn from_txin_with_witness_stack<'txin>(
230230
if !witness.is_empty() {
231231
return Err(Error::NonEmptyWitness);
232232
}
233-
let ms = Miniscript::parse(script_pubkey)?;
233+
let ms = Miniscript::<bitcoin::PublicKey, Legacy>::parse(script_pubkey)?;
234234
Ok((Descriptor::Bare(ms), Stack(stack?)))
235235
}
236236
}

src/descriptor/mod.rs

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ use std::str::{self, FromStr};
3232

3333
use expression;
3434
use miniscript;
35-
use miniscript::Miniscript;
35+
use miniscript::context::ScriptContextError;
36+
use miniscript::{Legacy, Miniscript, Segwitv0};
3637
use Error;
3738
use MiniscriptKey;
3839
use Satisfier;
@@ -50,8 +51,8 @@ pub use self::satisfied_constraints::Stack;
5051
/// Script descriptor
5152
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
5253
pub enum Descriptor<Pk: MiniscriptKey> {
53-
/// A raw scriptpubkey (including pay-to-pubkey)
54-
Bare(Miniscript<Pk>),
54+
/// A raw scriptpubkey (including pay-to-pubkey) under Legacy context
55+
Bare(Miniscript<Pk, Legacy>),
5556
/// Pay-to-Pubkey
5657
Pk(Pk),
5758
/// Pay-to-PubKey-Hash
@@ -60,12 +61,12 @@ pub enum Descriptor<Pk: MiniscriptKey> {
6061
Wpkh(Pk),
6162
/// Pay-to-Witness-PubKey-Hash inside P2SH
6263
ShWpkh(Pk),
63-
/// Pay-to-ScriptHash
64-
Sh(Miniscript<Pk>),
65-
/// Pay-to-Witness-ScriptHash
66-
Wsh(Miniscript<Pk>),
67-
/// P2SH-P2WSH
68-
ShWsh(Miniscript<Pk>),
64+
/// Pay-to-ScriptHash with Legacy context
65+
Sh(Miniscript<Pk, Legacy>),
66+
/// Pay-to-Witness-ScriptHash with Segwitv0 context
67+
Wsh(Miniscript<Pk, Segwitv0>),
68+
/// P2SH-P2WSH with Segwitv0 context
69+
ShWsh(Miniscript<Pk, Segwitv0>),
6970
}
7071

7172
impl<Pk: MiniscriptKey> Descriptor<Pk> {
@@ -74,29 +75,43 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
7475
&self,
7576
mut translatefpk: Fpk,
7677
mut translatefpkh: Fpkh,
77-
) -> Result<Descriptor<Q>, E>
78+
) -> Result<Result<Descriptor<Q>, Error>, E>
7879
where
7980
Fpk: FnMut(&Pk) -> Result<Q, E>,
8081
Fpkh: FnMut(&Pk::Hash) -> Result<Q::Hash, E>,
8182
Q: MiniscriptKey,
8283
{
8384
match *self {
84-
Descriptor::Bare(ref ms) => Ok(Descriptor::Bare(
85-
ms.translate_pk(&mut translatefpk, &mut translatefpkh)?,
86-
)),
87-
Descriptor::Pk(ref pk) => translatefpk(pk).map(Descriptor::Pk),
88-
Descriptor::Pkh(ref pk) => translatefpk(pk).map(Descriptor::Pkh),
89-
Descriptor::Wpkh(ref pk) => translatefpk(pk).map(Descriptor::Wpkh),
90-
Descriptor::ShWpkh(ref pk) => translatefpk(pk).map(Descriptor::ShWpkh),
91-
Descriptor::Sh(ref ms) => Ok(Descriptor::Sh(
92-
ms.translate_pk(&mut translatefpk, &mut translatefpkh)?,
93-
)),
94-
Descriptor::Wsh(ref ms) => Ok(Descriptor::Wsh(
95-
ms.translate_pk(&mut translatefpk, &mut translatefpkh)?,
96-
)),
97-
Descriptor::ShWsh(ref ms) => Ok(Descriptor::ShWsh(
98-
ms.translate_pk(&mut translatefpk, &mut translatefpkh)?,
99-
)),
85+
Descriptor::Bare(ref ms) => Ok(ms
86+
.translate_pk(&mut translatefpk, &mut translatefpkh)?
87+
.map(Descriptor::Bare)),
88+
Descriptor::Pk(ref pk) => translatefpk(pk).map(Descriptor::Pk).map(Ok),
89+
Descriptor::Pkh(ref pk) => translatefpk(pk).map(Descriptor::Pkh).map(Ok),
90+
Descriptor::Wpkh(ref pk) => {
91+
//uncompressed pubkeys not allowed in segwit descriptors
92+
if pk.is_uncompressed() {
93+
Ok(Err(Error::ContextError(ScriptContextError::CompressedOnly)))
94+
} else {
95+
translatefpk(pk).map(Descriptor::Wpkh).map(Ok)
96+
}
97+
}
98+
Descriptor::ShWpkh(ref pk) => {
99+
//uncompressed pubkeys not allowed in segwit descriptors
100+
if pk.is_uncompressed() {
101+
Ok(Err(Error::ContextError(ScriptContextError::CompressedOnly)))
102+
} else {
103+
translatefpk(pk).map(Descriptor::ShWpkh).map(Ok)
104+
}
105+
}
106+
Descriptor::Sh(ref ms) => Ok(ms
107+
.translate_pk(&mut translatefpk, &mut translatefpkh)?
108+
.map(Descriptor::Sh)),
109+
Descriptor::Wsh(ref ms) => Ok(ms
110+
.translate_pk(&mut translatefpk, &mut translatefpkh)?
111+
.map(Descriptor::Wsh)),
112+
Descriptor::ShWsh(ref ms) => Ok(ms
113+
.translate_pk(&mut translatefpk, &mut translatefpkh)?
114+
.map(Descriptor::ShWsh)),
100115
}
101116
}
102117
}
@@ -201,7 +216,8 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
201216
let addr = bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin);
202217
addr.script_pubkey()
203218
}
204-
Descriptor::Sh(ref d) | Descriptor::Wsh(ref d) | Descriptor::ShWsh(ref d) => d.encode(),
219+
Descriptor::Sh(ref d) => d.encode(),
220+
Descriptor::Wsh(ref d) | Descriptor::ShWsh(ref d) => d.encode(),
205221
}
206222
}
207223

@@ -396,7 +412,12 @@ where
396412
expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Descriptor::Pkh))
397413
}
398414
("wpkh", 1) => {
399-
expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Descriptor::Wpkh))
415+
let wpkh = expression::terminal(&top.args[0], |pk| Pk::from_str(pk))?;
416+
if wpkh.is_uncompressed() {
417+
Err(Error::ContextError(ScriptContextError::CompressedOnly))
418+
} else {
419+
Ok(Descriptor::Wpkh(wpkh))
420+
}
400421
}
401422
("sh", 1) => {
402423
let newtop = &top.args[0];
@@ -409,9 +430,14 @@ where
409430
Ok(Descriptor::ShWsh(sub))
410431
}
411432
}
412-
("wpkh", 1) => expression::terminal(&newtop.args[0], |pk| {
413-
Pk::from_str(pk).map(Descriptor::ShWpkh)
414-
}),
433+
("wpkh", 1) => {
434+
let wpkh = expression::terminal(&newtop.args[0], |pk| Pk::from_str(pk))?;
435+
if wpkh.is_uncompressed() {
436+
Err(Error::ContextError(ScriptContextError::CompressedOnly))
437+
} else {
438+
Ok(Descriptor::ShWpkh(wpkh))
439+
}
440+
}
415441
_ => {
416442
let sub = Miniscript::from_tree(&top.args[0])?;
417443
if sub.ty.corr.base != miniscript::types::Base::B {
@@ -585,6 +611,18 @@ mod tests {
585611
StdDescriptor::from_str("nl:0").unwrap_err(); //issue 63
586612

587613
StdDescriptor::from_str(TEST_PK).unwrap();
614+
615+
let uncompressed_pk =
616+
"0414fc03b8df87cd7b872996810db8458d61da8448e531569c8517b469a119d267be5645686309c6e6736dbd93940707cc9143d3cf29f1b877ff340e2cb2d259cf";
617+
618+
// Context tests
619+
StdDescriptor::from_str(&format!("pk({})", uncompressed_pk)).unwrap();
620+
StdDescriptor::from_str(&format!("pkh({})", uncompressed_pk)).unwrap();
621+
StdDescriptor::from_str(&format!("sh(pk({}))", uncompressed_pk)).unwrap();
622+
StdDescriptor::from_str(&format!("wpkh({})", uncompressed_pk)).unwrap_err();
623+
StdDescriptor::from_str(&format!("sh(wpkh({}))", uncompressed_pk)).unwrap_err();
624+
StdDescriptor::from_str(&format!("wsh(pk{})", uncompressed_pk)).unwrap_err();
625+
StdDescriptor::from_str(&format!("sh(wsh(pk{}))", uncompressed_pk)).unwrap_err();
588626
}
589627

590628
#[test]
@@ -874,6 +912,8 @@ mod tests {
874912
);
875913
assert_eq!(sh.unsigned_script_sig(), bitcoin::Script::new());
876914

915+
let ms = ms_str!("c:pk_k({})", pk);
916+
877917
let wsh = Descriptor::Wsh(ms.clone());
878918
wsh.satisfy(&mut txin, &satisfier).expect("satisfaction");
879919
assert_eq!(

0 commit comments

Comments
 (0)