Skip to content

Commit 5de4df8

Browse files
committed
Merge #828: Various cleanups, mostly around the compiler
bf10e45 compiler: pull non-binary argument errors from PolicyError into CompilerError (Andrew Poelstra) 3bc6c44 compiler: use Miniscript terminal constructors directly (Andrew Poelstra) b8d8910 miniscript: add infallible multi() and multi_a() constructors (Andrew Poelstra) fcbcf4e compiler: revert malleability check to simpler version (Andrew Poelstra) f98f263 compiler: forbid fragment probabilities of 0 (Andrew Poelstra) 16b4d60 fuzz/generate-files: support jujutsu (Andrew Poelstra) e000146 tr: cache TrSpendInfo when computing it (Andrew Poelstra) 2284858 tr: add `into_control_block` accessor to TrSpendInfoIterItem (Andrew Poelstra) Pull request description: This PR is a grab back of prepatory commits for larger changes. It includes a couple of followups to #815 in the first commits. Then it cleans up some error types and paths. I don't believe anything in here will be tricky or controversial, though there are API breaks because of the cleaned up error types. ACKs for top commit: sanket1729: ACK bf10e45 Tree-SHA512: 8a6c9006fb6473187a2ba69f0311fc29756c5f99544e35ab499658910e8f82a10eff740a19634b5d0c6807c583b8cd79fdc9fbf8a59b78ab96d5b387759db2b7
2 parents ea636ff + bf10e45 commit 5de4df8

File tree

14 files changed

+211
-67
lines changed

14 files changed

+211
-67
lines changed

.github/workflows/cron-daily-fuzz.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ parse_descriptor,
2525
parse_descriptor_definite,
2626
parse_descriptor_priv,
2727
parse_descriptor_secret,
28+
regression_compiler,
2829
regression_descriptor_parse,
2930
regression_taptree,
3031
roundtrip_concrete,

fuzz/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ honggfuzz = { version = "0.5.56", default-features = false }
1414
# We shouldn't need an explicit version on the next line, but Andrew's tools
1515
# choke on it otherwise. See https://github.com/nix-community/crate2nix/issues/373
1616
miniscript = { path = "..", features = [ "compiler" ], version = "13.0" }
17-
old_miniscript = { package = "miniscript", version = "12.3" }
17+
old_miniscript = { package = "miniscript", features = [ "compiler" ], version = "12.3" }
1818

1919
regex = "1.0"
2020

@@ -46,6 +46,10 @@ path = "fuzz_targets/parse_descriptor_priv.rs"
4646
name = "parse_descriptor_secret"
4747
path = "fuzz_targets/parse_descriptor_secret.rs"
4848

49+
[[bin]]
50+
name = "regression_compiler"
51+
path = "fuzz_targets/regression_compiler.rs"
52+
4953
[[bin]]
5054
name = "regression_descriptor_parse"
5155
path = "fuzz_targets/regression_descriptor_parse.rs"

fuzz/cycle.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# For hfuzz options see https://github.com/google/honggfuzz/blob/master/docs/USAGE.md
77

88
set -e
9-
REPO_DIR=$(git rev-parse --show-toplevel)
9+
REPO_DIR=$(git rev-parse --show-toplevel || jj workspace root)
1010
# can't find the file because of the ENV var
1111
# shellcheck source=/dev/null
1212
source "$REPO_DIR/fuzz/fuzz-util.sh"

fuzz/fuzz-util.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env bash
22

3-
REPO_DIR=$(git rev-parse --show-toplevel)
3+
REPO_DIR=$(git rev-parse --show-toplevel || jj workspace root)
44

55
# Sort order is effected by locale. See `man sort`.
66
# > Set LC_ALL=C to get the traditional sort order that uses native byte values.

fuzz/fuzz.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env bash
22
set -ex
33

4-
REPO_DIR=$(git rev-parse --show-toplevel)
4+
REPO_DIR=$(git rev-parse --show-toplevel || jj workspace show)
55

66
# can't find the file because of the ENV var
77
# shellcheck source=/dev/null
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use descriptor_fuzz::FuzzPk;
2+
use honggfuzz::fuzz;
3+
use miniscript::{policy, ParseError, ParseNumError};
4+
use old_miniscript::policy as old_policy;
5+
6+
type Policy = policy::Concrete<FuzzPk>;
7+
type OldPolicy = old_policy::Concrete<FuzzPk>;
8+
9+
fn do_test(data: &[u8]) {
10+
let data_str = String::from_utf8_lossy(data);
11+
match (data_str.parse::<Policy>(), data_str.parse::<OldPolicy>()) {
12+
(Err(_), Err(_)) => {}
13+
(Ok(x), Err(e)) => panic!("new logic parses {} as {:?}, old fails with {}", data_str, x, e),
14+
// These is anew parse error
15+
(
16+
Err(miniscript::Error::Parse(ParseError::Num(ParseNumError::IllegalZero { .. }))),
17+
Ok(_),
18+
) => {}
19+
(Err(e), Ok(x)) => {
20+
panic!("old logic parses {} as {:?}, new fails with {:?}", data_str, x, e)
21+
}
22+
(Ok(new), Ok(old)) => {
23+
assert_eq!(
24+
old.to_string(),
25+
new.to_string(),
26+
"input {} (left is old, right is new)",
27+
data_str
28+
);
29+
30+
let comp = new.compile::<miniscript::Legacy>();
31+
let old_comp = old.compile::<old_miniscript::Legacy>();
32+
33+
match (comp, old_comp) {
34+
(Err(_), Err(_)) => {}
35+
(Ok(x), Err(e)) => {
36+
panic!("new logic compiles {} as {:?}, old fails with {}", data_str, x, e)
37+
}
38+
(Err(e), Ok(x)) => {
39+
panic!("old logic compiles {} as {:?}, new fails with {}", data_str, x, e)
40+
}
41+
(Ok(new), Ok(old)) => {
42+
assert_eq!(
43+
old.to_string(),
44+
new.to_string(),
45+
"input {} (left is old, right is new)",
46+
data_str
47+
);
48+
}
49+
}
50+
}
51+
}
52+
}
53+
54+
fn main() {
55+
loop {
56+
fuzz!(|data| {
57+
do_test(data);
58+
});
59+
}
60+
}
61+
62+
#[cfg(test)]
63+
mod tests {
64+
#[test]
65+
fn duplicate_crash() { crate::do_test(b"or(0@pk(09),0@TRIVIAL)") }
66+
}

fuzz/generate-files.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
set -e
44

5-
REPO_DIR=$(git rev-parse --show-toplevel)
5+
REPO_DIR=$(git rev-parse --show-toplevel || jj workspace root)
66

77
# can't find the file because of the ENV var
88
# shellcheck source=/dev/null
@@ -26,7 +26,7 @@ honggfuzz = { version = "0.5.56", default-features = false }
2626
# We shouldn't need an explicit version on the next line, but Andrew's tools
2727
# choke on it otherwise. See https://github.com/nix-community/crate2nix/issues/373
2828
miniscript = { path = "..", features = [ "compiler" ], version = "13.0" }
29-
old_miniscript = { package = "miniscript", version = "12.3" }
29+
old_miniscript = { package = "miniscript", features = [ "compiler" ], version = "12.3" }
3030
3131
regex = "1.0"
3232
EOF

src/descriptor/tr/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,19 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
125125
/// This data is needed to compute the Taproot output, so this method is implicitly
126126
/// called through [`Self::script_pubkey`], [`Self::address`], etc. It is also needed
127127
/// to compute the hash needed to sign the output.
128-
pub fn spend_info(&self) -> TrSpendInfo<Pk>
128+
pub fn spend_info(&self) -> Arc<TrSpendInfo<Pk>>
129129
where
130130
Pk: ToPublicKey,
131131
{
132-
TrSpendInfo::from_tr(self)
132+
let mut lock = self.spend_info.lock().unwrap();
133+
match *lock {
134+
Some(ref res) => Arc::clone(res),
135+
None => {
136+
let arc = Arc::new(TrSpendInfo::from_tr(self));
137+
*lock = Some(Arc::clone(&arc));
138+
arc
139+
}
140+
}
133141
}
134142

135143
/// Checks whether the descriptor is safe.

src/descriptor/tr/spend_info.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,11 +339,19 @@ impl<'sp, Pk: MiniscriptKey> TrSpendInfoIterItem<'sp, Pk> {
339339
/// The control block of this leaf.
340340
///
341341
/// Unlike the other data obtainable from [`TrSpendInfoIterItem`], this one is computed
342-
/// dynamically during iteration and therefore will not outlive the iterator item. If
343-
/// you need access to multiple control blocks at once, will likely need to clone and
344-
/// store them separately.
342+
/// dynamically during iteration and therefore will not outlive the iterator item. See
343+
/// [`Self::into_control_block`], which consumes the iterator item but will give you an
344+
/// owned copy of the control block.
345+
///
346+
/// If you need access to multiple control blocks at once, you may need to `clone` the
347+
/// return value of this method, or call [`Self::into_control_block`], and store the
348+
/// result in a separate container.
345349
#[inline]
346350
pub fn control_block(&self) -> &ControlBlock { &self.control_block }
351+
352+
/// Extract the control block of this leaf, consuming `self`.
353+
#[inline]
354+
pub fn into_control_block(self) -> ControlBlock { self.control_block }
347355
}
348356

349357
#[cfg(test)]

src/expression/error.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ pub enum ParseNumError {
197197
StdParse(num::ParseIntError),
198198
/// Number had a leading zero, + or -.
199199
InvalidLeadingDigit(char),
200+
/// Number was 0 in a context where zero is not allowed.
201+
///
202+
/// Be aware that locktimes have their own error types and do not use this variant.
203+
IllegalZero {
204+
/// A description of the location where 0 was not allowed.
205+
context: &'static str,
206+
},
200207
}
201208

202209
impl fmt::Display for ParseNumError {
@@ -206,6 +213,9 @@ impl fmt::Display for ParseNumError {
206213
ParseNumError::InvalidLeadingDigit(ch) => {
207214
write!(f, "numbers must start with 1-9, not {}", ch)
208215
}
216+
ParseNumError::IllegalZero { context } => {
217+
write!(f, "{} may not be 0", context)
218+
}
209219
}
210220
}
211221
}
@@ -216,6 +226,7 @@ impl std::error::Error for ParseNumError {
216226
match self {
217227
ParseNumError::StdParse(ref e) => Some(e),
218228
ParseNumError::InvalidLeadingDigit(..) => None,
229+
ParseNumError::IllegalZero { .. } => None,
219230
}
220231
}
221232
}

0 commit comments

Comments
 (0)