Skip to content

Commit 7ea3555

Browse files
committed
miniscript: don't use malleable satisfier, even for or_i dissatisfactions
...unless the user has explicitly requested a malleable satisfaction. Fixes #976
1 parent 668a2ae commit 7ea3555

2 files changed

Lines changed: 8 additions & 15 deletions

File tree

src/miniscript/satisfy/mod.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ mod tests {
651651
//
652652
// Since the first branch's dissatisfaction is HASSIG but the second branch's is not,
653653
// the satisfier is required to dissatisfy the second branch to avoid malleability.
654-
// However, because of #976, it does not.
654+
// But if we use `into_plan_mall` we can see the bug.
655655
//
656656
// Itstead, it takes both the after(144) and after(50) branches, and the resulting
657657
// plan should show after(144) since it's the higher one. However, prior to #895,
@@ -664,9 +664,12 @@ mod tests {
664664
// Need DefiniteDescriptorKey https://github.com/rust-bitcoin/rust-miniscript/issues/927
665665
let descriptor =
666666
Descriptor::<crate::DefiniteDescriptorKey>::from_str(&descriptor_str).unwrap();
667-
// Compute plan and confirm the timelock is correct.
668-
let plan = descriptor.into_plan(&satisfier).unwrap();
667+
// Compute plan and confirm the timelock is correct -- 144 for a malleable transaction
668+
let plan = descriptor.clone().into_plan_mall(&satisfier).unwrap();
669669
assert_eq!(plan.absolute_timelock, Some(absolute::LockTime::from_height(144).unwrap()),);
670+
// ...and 50 for a non-malleable one (since take the expensive_threshold alternate)
671+
let plan = descriptor.into_plan(&satisfier).unwrap();
672+
assert_eq!(plan.absolute_timelock, Some(absolute::LockTime::from_height(50).unwrap()),);
670673

671674
// Same descriptor as above, except that now we use a time-based timelock rather than a
672675
// lower height-based one.
@@ -690,7 +693,7 @@ mod tests {
690693
let descriptor =
691694
Descriptor::<crate::DefiniteDescriptorKey>::from_str(&descriptor_str).unwrap();
692695

693-
let plan = descriptor.into_plan(&satisfier).unwrap();
696+
let plan = descriptor.into_plan_mall(&satisfier).unwrap();
694697
assert_eq!(
695698
plan.absolute_timelock,
696699
Some(absolute::LockTime::from_time(1000000000).unwrap()),

src/miniscript/satisfy/sat_dissat.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -442,18 +442,8 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
442442
Terminal::OrI(_, _) => {
443443
let SatDissat { dissat: r_dis, sat: r_sat } = stack.pop().unwrap();
444444
let SatDissat { dissat: l_dis, sat: l_sat } = stack.pop().unwrap();
445-
// Regarding use of `minimum_mall` for dissatisfactions: this is correct, because
446-
// dissatisfactions for individual fragments are not required to be unique. (We
447-
// track this property using the `ty.malleability.dissat` field.)
448-
//
449-
// This is because usually dissatisfactions are eventually dropped, e.g. by the
450-
// `j:` wrapper or because at the top-level a dissatisfaction is simply invalid.
451-
// In cases where non-unique dissatisfactions would cause malleability, e.g. in
452-
// the `or_c` or `or_b` fragments which use dissatisfactions as part of their
453-
// satisfactions, we set `ty.malleability.non_malleable` appropriately (and
454-
// potentially reject the whole script at creation time).
455445
SatDissat {
456-
dissat: Self::minimum_mall(
446+
dissat: min_fn(
457447
Self {
458448
stack: Witness::combine(l_dis.stack, Witness::push_1()),
459449
..l_dis

0 commit comments

Comments
 (0)