Skip to content

Commit c93bdef

Browse files
committed
Added ScriptContext to Miniscript
1 parent e3746e2 commit c93bdef

22 files changed

+711
-316
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: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
extern crate miniscript;
22

3+
use miniscript::Segwitv0;
34
use miniscript::{policy, DummyKey, Miniscript};
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
@@ -37,7 +38,8 @@ fn main() {
3738
}
3839

3940
#[cfg(feature = "honggfuzz")]
40-
#[macro_use] extern crate honggfuzz;
41+
#[macro_use]
42+
extern crate honggfuzz;
4143
#[cfg(feature = "honggfuzz")]
4244
fn main() {
4345
loop {

fuzz/fuzz_targets/roundtrip_concrete.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
21
extern crate miniscript;
32
extern crate regex;
4-
use std::str::FromStr;
53
use miniscript::{policy, DummyKey};
64
use regex::Regex;
5+
use std::str::FromStr;
76

87
type DummyPolicy = policy::Concrete<DummyKey>;
98

@@ -29,7 +28,8 @@ fn main() {
2928
}
3029

3130
#[cfg(feature = "honggfuzz")]
32-
#[macro_use] extern crate honggfuzz;
31+
#[macro_use]
32+
extern crate honggfuzz;
3333
#[cfg(feature = "honggfuzz")]
3434
fn main() {
3535
loop {

fuzz/fuzz_targets/roundtrip_descriptor.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
extern crate miniscript;
32
extern crate regex;
43

@@ -10,13 +9,15 @@ fn do_test(data: &[u8]) {
109
let s = String::from_utf8_lossy(data);
1110
if let Ok(desc) = Descriptor::<DummyKey>::from_str(&s) {
1211
let output = desc.to_string();
13-
12+
1413
let multi_wrap_pk_re = Regex::new("([a-z]+)c:pk_k\\(").unwrap();
1514
let multi_wrap_pkh_re = Regex::new("([a-z]+)c:pk_h\\(").unwrap();
1615

1716
let normalize_aliases = multi_wrap_pk_re.replace_all(&s, "$1:pk(");
1817
let normalize_aliases = multi_wrap_pkh_re.replace_all(&normalize_aliases, "$1:pkh(");
19-
let normalize_aliases = normalize_aliases.replace("c:pk_k(", "pk(").replace("c:pk_h(", "pkh(");
18+
let normalize_aliases = normalize_aliases
19+
.replace("c:pk_k(", "pk(")
20+
.replace("c:pk_h(", "pkh(");
2021

2122
assert_eq!(normalize_aliases.to_lowercase(), output.to_lowercase());
2223
}
@@ -32,7 +33,8 @@ fn main() {
3233
}
3334

3435
#[cfg(feature = "honggfuzz")]
35-
#[macro_use] extern crate honggfuzz;
36+
#[macro_use]
37+
extern crate honggfuzz;
3638
#[cfg(feature = "honggfuzz")]
3739
fn main() {
3840
loop {

fuzz/fuzz_targets/roundtrip_miniscript_script.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
21
extern crate miniscript;
32

4-
use miniscript::Miniscript;
53
use miniscript::bitcoin::blockdata::script;
4+
use miniscript::Miniscript;
5+
use miniscript::Segwitv0;
66

77
fn do_test(data: &[u8]) {
88
// Try round-tripping as a script
99
let script = script::Script::from(data.to_owned());
1010

11-
if let Ok(pt) = Miniscript::parse(&script) {
11+
if let Ok(pt) = Miniscript::<_, Segwitv0>::parse(&script) {
1212
let output = pt.encode();
1313
assert_eq!(pt.script_size(), output.len());
1414
assert_eq!(output, script);
@@ -25,7 +25,8 @@ fn main() {
2525
}
2626

2727
#[cfg(feature = "honggfuzz")]
28-
#[macro_use] extern crate honggfuzz;
28+
#[macro_use]
29+
extern crate honggfuzz;
2930
#[cfg(feature = "honggfuzz")]
3031
fn main() {
3132
loop {

fuzz/fuzz_targets/roundtrip_miniscript_str.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
1-
21
extern crate miniscript;
32
extern crate regex;
43

5-
use std::str::FromStr;
64
use regex::Regex;
5+
use std::str::FromStr;
76

8-
use miniscript::{DummyKey};
7+
use miniscript::DummyKey;
98
use miniscript::Miniscript;
9+
use miniscript::Segwitv0;
1010

1111
fn do_test(data: &[u8]) {
1212
let s = String::from_utf8_lossy(data);
13-
if let Ok(desc) = Miniscript::<DummyKey>::from_str(&s) {
13+
if let Ok(desc) = Miniscript::<DummyKey, Segwitv0>::from_str(&s) {
1414
let output = desc.to_string();
15-
15+
1616
let multi_wrap_pk_re = Regex::new("([a-z]+)c:pk_k\\(").unwrap();
1717
let multi_wrap_pkh_re = Regex::new("([a-z]+)c:pk_h\\(").unwrap();
1818

1919
let normalize_aliases = multi_wrap_pk_re.replace_all(&s, "$1:pk(");
2020
let normalize_aliases = multi_wrap_pkh_re.replace_all(&normalize_aliases, "$1:pkh(");
21-
let normalize_aliases = normalize_aliases.replace("c:pk_k(", "pk(").replace("c:pk_h(", "pkh(");
21+
let normalize_aliases = normalize_aliases
22+
.replace("c:pk_k(", "pk(")
23+
.replace("c:pk_h(", "pkh(");
2224

2325
assert_eq!(normalize_aliases.to_lowercase(), output.to_lowercase());
24-
2526
}
2627
}
2728

@@ -35,7 +36,8 @@ fn main() {
3536
}
3637

3738
#[cfg(feature = "honggfuzz")]
38-
#[macro_use] extern crate honggfuzz;
39+
#[macro_use]
40+
extern crate honggfuzz;
3941
#[cfg(feature = "honggfuzz")]
4042
fn main() {
4143
loop {

fuzz/fuzz_targets/roundtrip_semantic.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
21
extern crate miniscript;
32

4-
use std::str::FromStr;
53
use miniscript::{policy, DummyKey};
4+
use std::str::FromStr;
65

76
type DummyPolicy = policy::Semantic<DummyKey>;
87

@@ -24,12 +23,13 @@ fn main() {
2423
}
2524

2625
#[cfg(feature = "honggfuzz")]
27-
#[macro_use] extern crate honggfuzz;
26+
#[macro_use]
27+
extern crate honggfuzz;
2828
#[cfg(feature = "honggfuzz")]
2929
fn main() {
3030
loop {
3131
fuzz!(|data| {
3232
do_test(data);
3333
});
3434
}
35-
}
35+
}

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: 55 additions & 16 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,16 +61,19 @@ 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> {
7273
/// Convert a descriptor using abstract keys to one using specific keys
74+
/// This will panic if translatefpk returns an uncompressed key when
75+
/// converting to a Segwit descriptor. To prevent this panic, ensure
76+
/// translatefpk returns an error in this case instead.
7377
pub fn translate_pk<Fpk, Fpkh, Q, E>(
7478
&self,
7579
mut translatefpk: Fpk,
@@ -86,8 +90,18 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
8690
)),
8791
Descriptor::Pk(ref pk) => translatefpk(pk).map(Descriptor::Pk),
8892
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),
93+
Descriptor::Wpkh(ref pk) => {
94+
if pk.is_uncompressed() {
95+
panic!("Uncompressed pubkeys are not allowed in segwit v0 scripts");
96+
}
97+
translatefpk(pk).map(Descriptor::Wpkh)
98+
}
99+
Descriptor::ShWpkh(ref pk) => {
100+
if pk.is_uncompressed() {
101+
panic!("Uncompressed pubkeys are not allowed in segwit v0 scripts");
102+
}
103+
translatefpk(pk).map(Descriptor::ShWpkh)
104+
}
91105
Descriptor::Sh(ref ms) => Ok(Descriptor::Sh(
92106
ms.translate_pk(&mut translatefpk, &mut translatefpkh)?,
93107
)),
@@ -201,7 +215,8 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
201215
let addr = bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin);
202216
addr.script_pubkey()
203217
}
204-
Descriptor::Sh(ref d) | Descriptor::Wsh(ref d) | Descriptor::ShWsh(ref d) => d.encode(),
218+
Descriptor::Sh(ref d) => d.encode(),
219+
Descriptor::Wsh(ref d) | Descriptor::ShWsh(ref d) => d.encode(),
205220
}
206221
}
207222

@@ -396,7 +411,12 @@ where
396411
expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Descriptor::Pkh))
397412
}
398413
("wpkh", 1) => {
399-
expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Descriptor::Wpkh))
414+
let wpkh = expression::terminal(&top.args[0], |pk| Pk::from_str(pk))?;
415+
if wpkh.is_uncompressed() {
416+
Err(Error::ContextError(ScriptContextError::CompressedOnly))
417+
} else {
418+
Ok(Descriptor::Wpkh(wpkh))
419+
}
400420
}
401421
("sh", 1) => {
402422
let newtop = &top.args[0];
@@ -409,9 +429,14 @@ where
409429
Ok(Descriptor::ShWsh(sub))
410430
}
411431
}
412-
("wpkh", 1) => expression::terminal(&newtop.args[0], |pk| {
413-
Pk::from_str(pk).map(Descriptor::ShWpkh)
414-
}),
432+
("wpkh", 1) => {
433+
let wpkh = expression::terminal(&newtop.args[0], |pk| Pk::from_str(pk))?;
434+
if wpkh.is_uncompressed() {
435+
Err(Error::ContextError(ScriptContextError::CompressedOnly))
436+
} else {
437+
Ok(Descriptor::ShWpkh(wpkh))
438+
}
439+
}
415440
_ => {
416441
let sub = Miniscript::from_tree(&top.args[0])?;
417442
if sub.ty.corr.base != miniscript::types::Base::B {
@@ -586,6 +611,18 @@ mod tests {
586611
StdDescriptor::from_str("nl:0").unwrap_err(); //issue 63
587612

588613
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();
589626
}
590627

591628
#[test]
@@ -875,6 +912,8 @@ mod tests {
875912
);
876913
assert_eq!(sh.unsigned_script_sig(), bitcoin::Script::new());
877914

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

0 commit comments

Comments
 (0)