Skip to content

Commit ea36951

Browse files
committed
Added Script Context
1 parent b4e33e5 commit ea36951

File tree

22 files changed

+861
-393
lines changed

22 files changed

+861
-393
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ fuzz/hfuzz_workspace
1414

1515
#emacs
1616
*~
17+
18+
#VsCode files
19+
.vscode

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ matrix:
99
env: DO_FUZZ=true DO_LINT=true
1010
- rust: beta
1111
- rust: nightly
12-
env: DO_BENCH=true
12+
env: DO_BENCH=true DO_MIRI=true
1313
- rust: 1.22.0
1414

1515
script:

contrib/test.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ then
4343
)
4444
fi
4545

46+
# Miri Checks if told to
47+
# Only supported in nightly
48+
if [ "$DO_MIRI" = true ]
49+
then
50+
(
51+
MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)
52+
echo "Installing latest nightly with Miri: $MIRI_NIGHTLY"
53+
rustup set profile minimal
54+
rustup default "$MIRI_NIGHTLY"
55+
rustup component add rustfmt
56+
cargo miri test -- -- any_unsafe_transmute_miri_test
57+
58+
# Change back to latest nightly possibly without Miri
59+
rustup default nightly
60+
)
61+
fi
62+
4663
# Bench if told to
4764
if [ "$DO_BENCH" = true ]
4865
then

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
@@ -4,11 +4,12 @@ extern crate miniscript;
44
use std::str::FromStr;
55

66
use miniscript::{DummyKey};
7+
use miniscript::context::Segwitv0;
78
use miniscript::Miniscript;
89

910
fn do_test(data: &[u8]) {
1011
let s = String::from_utf8_lossy(data);
11-
if let Ok(desc) = Miniscript::<DummyKey>::from_str(&s) {
12+
if let Ok(desc) = Miniscript::<DummyKey, Segwitv0>::from_str(&s) {
1213
let output = desc.to_string();
1314
assert_eq!(s, output);
1415
}

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: 73 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ use std::fmt;
3131
use std::str::{self, FromStr};
3232

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

7071
impl<Pk: MiniscriptKey> Descriptor<Pk> {
@@ -73,29 +74,43 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
7374
&self,
7475
mut translatefpk: Fpk,
7576
mut translatefpkh: Fpkh,
76-
) -> Result<Descriptor<Q>, E>
77+
) -> Result<Result<Descriptor<Q>, Error>, E>
7778
where
7879
Fpk: FnMut(&Pk) -> Result<Q, E>,
7980
Fpkh: FnMut(&Pk::Hash) -> Result<Q::Hash, E>,
8081
Q: MiniscriptKey,
8182
{
8283
match *self {
83-
Descriptor::Bare(ref ms) => Ok(Descriptor::Bare(
84-
ms.translate_pk(&mut translatefpk, &mut translatefpkh)?,
85-
)),
86-
Descriptor::Pk(ref pk) => translatefpk(pk).map(Descriptor::Pk),
87-
Descriptor::Pkh(ref pk) => translatefpk(pk).map(Descriptor::Pkh),
88-
Descriptor::Wpkh(ref pk) => translatefpk(pk).map(Descriptor::Wpkh),
89-
Descriptor::ShWpkh(ref pk) => translatefpk(pk).map(Descriptor::ShWpkh),
90-
Descriptor::Sh(ref ms) => Ok(Descriptor::Sh(
91-
ms.translate_pk(&mut translatefpk, &mut translatefpkh)?,
92-
)),
93-
Descriptor::Wsh(ref ms) => Ok(Descriptor::Wsh(
94-
ms.translate_pk(&mut translatefpk, &mut translatefpkh)?,
95-
)),
96-
Descriptor::ShWsh(ref ms) => Ok(Descriptor::ShWsh(
97-
ms.translate_pk(&mut translatefpk, &mut translatefpkh)?,
98-
)),
84+
Descriptor::Bare(ref ms) => Ok(ms
85+
.translate_pk(&mut translatefpk, &mut translatefpkh)?
86+
.map(Descriptor::Bare)),
87+
Descriptor::Pk(ref pk) => translatefpk(pk).map(Descriptor::Pk).map(Ok),
88+
Descriptor::Pkh(ref pk) => translatefpk(pk).map(Descriptor::Pkh).map(Ok),
89+
Descriptor::Wpkh(ref pk) => {
90+
//uncompressed pubkeys not allowed in segwit descriptors
91+
if pk.is_uncompressed() {
92+
Ok(Err(Error::ContextError(ScriptContextError::CompressedOnly)))
93+
} else {
94+
translatefpk(pk).map(Descriptor::Wpkh).map(Ok)
95+
}
96+
}
97+
Descriptor::ShWpkh(ref pk) => {
98+
//uncompressed pubkeys not allowed in segwit descriptors
99+
if pk.is_uncompressed() {
100+
Ok(Err(Error::ContextError(ScriptContextError::CompressedOnly)))
101+
} else {
102+
translatefpk(pk).map(Descriptor::ShWpkh).map(Ok)
103+
}
104+
}
105+
Descriptor::Sh(ref ms) => Ok(ms
106+
.translate_pk(&mut translatefpk, &mut translatefpkh)?
107+
.map(Descriptor::Sh)),
108+
Descriptor::Wsh(ref ms) => Ok(ms
109+
.translate_pk(&mut translatefpk, &mut translatefpkh)?
110+
.map(Descriptor::Wsh)),
111+
Descriptor::ShWsh(ref ms) => Ok(ms
112+
.translate_pk(&mut translatefpk, &mut translatefpkh)?
113+
.map(Descriptor::ShWsh)),
99114
}
100115
}
101116
}
@@ -200,7 +215,8 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
200215
let addr = bitcoin::Address::p2wpkh(&pk.to_public_key(), bitcoin::Network::Bitcoin);
201216
addr.script_pubkey()
202217
}
203-
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(),
204220
}
205221
}
206222

@@ -395,25 +411,29 @@ where
395411
expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Descriptor::Pkh))
396412
}
397413
("wpkh", 1) => {
398-
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+
}
399420
}
400421
("sh", 1) => {
401422
let newtop = &top.args[0];
402423
match (newtop.name, newtop.args.len()) {
403-
("wsh", 1) => {
404-
let sub = Miniscript::from_tree(&newtop.args[0])?;
405-
Ok(Descriptor::ShWsh(sub))
406-
}
407-
("wpkh", 1) => expression::terminal(&newtop.args[0], |pk| {
408-
Pk::from_str(pk).map(Descriptor::ShWpkh)
409-
}),
410-
_ => {
411-
let sub = Miniscript::from_tree(&top.args[0])?;
412-
Ok(Descriptor::Sh(sub))
424+
("wsh", 1) => Miniscript::from_tree(&newtop.args[0]).map(Descriptor::ShWsh),
425+
("wpkh", 1) => {
426+
let wpkh = expression::terminal(&newtop.args[0], |pk| Pk::from_str(pk))?;
427+
if wpkh.is_uncompressed() {
428+
Err(Error::ContextError(ScriptContextError::CompressedOnly))
429+
} else {
430+
Ok(Descriptor::ShWpkh(wpkh))
431+
}
413432
}
433+
_ => Miniscript::from_tree(&top.args[0]).map(Descriptor::Sh),
414434
}
415435
}
416-
("wsh", 1) => expression::unary(top, Descriptor::Wsh),
436+
("wsh", 1) => Miniscript::from_tree(&top.args[0]).map(Descriptor::Wsh),
417437
_ => {
418438
let sub = expression::FromTree::from_tree(&top)?;
419439
Ok(Descriptor::Bare(sub))
@@ -563,6 +583,18 @@ mod tests {
563583
StdDescriptor::from_str("pk()").unwrap_err();
564584

565585
StdDescriptor::from_str(TEST_PK).unwrap();
586+
587+
let uncompressed_pk =
588+
"0414fc03b8df87cd7b872996810db8458d61da8448e531569c8517b469a119d267be5645686309c6e6736dbd93940707cc9143d3cf29f1b877ff340e2cb2d259cf";
589+
590+
// Context tests
591+
StdDescriptor::from_str(&format!("pk({})", uncompressed_pk)).unwrap();
592+
StdDescriptor::from_str(&format!("pkh({})", uncompressed_pk)).unwrap();
593+
StdDescriptor::from_str(&format!("sh(pk({}))", uncompressed_pk)).unwrap();
594+
StdDescriptor::from_str(&format!("wpkh({})", uncompressed_pk)).unwrap_err();
595+
StdDescriptor::from_str(&format!("sh(wpkh({}))", uncompressed_pk)).unwrap_err();
596+
StdDescriptor::from_str(&format!("wsh(pk{})", uncompressed_pk)).unwrap_err();
597+
StdDescriptor::from_str(&format!("sh(wsh(pk{}))", uncompressed_pk)).unwrap_err();
566598
}
567599

568600
#[test]
@@ -852,6 +884,8 @@ mod tests {
852884
);
853885
assert_eq!(sh.unsigned_script_sig(), bitcoin::Script::new());
854886

887+
let ms = ms_str!("c:pk_k({})", pk);
888+
855889
let wsh = Descriptor::Wsh(ms.clone());
856890
wsh.satisfy(&mut txin, &satisfier).expect("satisfaction");
857891
assert_eq!(

0 commit comments

Comments
 (0)