Skip to content

Commit 7be767c

Browse files
committed
satisfy: clean up a bunch of satisfaction code
There is a bunch of essentially-repeated "combine two satisfactions" code throughout satisfy.rs. It's hard to tell at a glance what order the concatenantions are in, even though this is essential, or whether the combination is actually done correctly. (In particular, we routinely use the opposite order for Witness::combine and for ||'ing the has_sig, and we routinely assume that timelock conditions are None for dissatisfactions, which is true, but it's still a lot to keep in your head.)
1 parent 8efce62 commit 7be767c

File tree

1 file changed

+67
-101
lines changed

1 file changed

+67
-101
lines changed

src/miniscript/satisfy.rs

Lines changed: 67 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,34 @@ pub struct Satisfaction<T> {
889889
}
890890

891891
impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
892+
/// The empty satisfaction.
893+
///
894+
/// This has the property that, when concatenated on either side with another satisfaction
895+
/// X, the result will be X.
896+
fn empty() -> Self {
897+
Satisfaction {
898+
has_sig: false,
899+
relative_timelock: None,
900+
absolute_timelock: None,
901+
stack: Witness::Stack(vec![]),
902+
}
903+
}
904+
905+
/// Forms a satisfaction which is the concatenation of two satisfactions, with `other`'s
906+
/// stack before `self`'s.
907+
///
908+
/// This order allows callers to write `left.concatenate_rev(right)` which feels more
909+
/// natural than the opposite order, and more importantly, allows this method to be
910+
/// used when folding over an iterator of multiple satisfactions.
911+
fn concatenate_rev(self, other: Self) -> Self {
912+
Satisfaction {
913+
has_sig: self.has_sig || other.has_sig,
914+
relative_timelock: cmp::max(self.relative_timelock, other.relative_timelock),
915+
absolute_timelock: cmp::max(self.absolute_timelock, other.absolute_timelock),
916+
stack: Witness::combine(other.stack, self.stack),
917+
}
918+
}
919+
892920
pub(crate) fn build_template<P, Ctx>(
893921
term: &Terminal<Pk, Ctx>,
894922
provider: &P,
@@ -1041,20 +1069,9 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
10411069
}
10421070
} else {
10431071
// Otherwise flatten everything out
1044-
Satisfaction {
1045-
has_sig: ret_stack.iter().any(|sat| sat.has_sig),
1046-
relative_timelock: ret_stack
1047-
.iter()
1048-
.filter_map(|sat| sat.relative_timelock)
1049-
.max(),
1050-
absolute_timelock: ret_stack
1051-
.iter()
1052-
.filter_map(|sat| sat.absolute_timelock)
1053-
.max(),
1054-
stack: ret_stack
1055-
.into_iter()
1056-
.fold(Witness::empty(), |acc, next| Witness::combine(next.stack, acc)),
1057-
}
1072+
ret_stack
1073+
.into_iter()
1074+
.fold(Satisfaction::empty(), Satisfaction::concatenate_rev)
10581075
}
10591076
}
10601077

@@ -1125,20 +1142,9 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
11251142

11261143
// combine the witness
11271144
// no non-malleability checks needed
1128-
Satisfaction {
1129-
has_sig: ret_stack.iter().any(|sat| sat.has_sig),
1130-
relative_timelock: ret_stack
1131-
.iter()
1132-
.filter_map(|sat| sat.relative_timelock)
1133-
.max(),
1134-
absolute_timelock: ret_stack
1135-
.iter()
1136-
.filter_map(|sat| sat.absolute_timelock)
1137-
.max(),
1138-
stack: ret_stack
1139-
.into_iter()
1140-
.fold(Witness::empty(), |acc, next| Witness::combine(next.stack, acc)),
1141-
}
1145+
ret_stack
1146+
.into_iter()
1147+
.fold(Satisfaction::empty(), Satisfaction::concatenate_rev)
11421148
}
11431149

11441150
fn minimum(sat1: Self, sat2: Self) -> Self {
@@ -1360,12 +1366,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
13601366
Self::satisfy_helper(&l.node, stfr, root_has_sig, leaf_hash, min_fn, thresh_fn);
13611367
let r_sat =
13621368
Self::satisfy_helper(&r.node, stfr, root_has_sig, leaf_hash, min_fn, thresh_fn);
1363-
Satisfaction {
1364-
stack: Witness::combine(r_sat.stack, l_sat.stack),
1365-
has_sig: l_sat.has_sig || r_sat.has_sig,
1366-
relative_timelock: cmp::max(l_sat.relative_timelock, r_sat.relative_timelock),
1367-
absolute_timelock: cmp::max(l_sat.absolute_timelock, r_sat.absolute_timelock),
1368-
}
1369+
l_sat.concatenate_rev(r_sat)
13691370
}
13701371
Terminal::AndOr(ref a, ref b, ref c) => {
13711372
let a_sat =
@@ -1383,27 +1384,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
13831384
let c_sat =
13841385
Self::satisfy_helper(&c.node, stfr, root_has_sig, leaf_hash, min_fn, thresh_fn);
13851386

1386-
min_fn(
1387-
Satisfaction {
1388-
stack: Witness::combine(b_sat.stack, a_sat.stack),
1389-
has_sig: a_sat.has_sig || b_sat.has_sig,
1390-
relative_timelock: cmp::max(
1391-
a_sat.relative_timelock,
1392-
b_sat.relative_timelock,
1393-
),
1394-
absolute_timelock: cmp::max(
1395-
a_sat.absolute_timelock,
1396-
b_sat.absolute_timelock,
1397-
),
1398-
},
1399-
Satisfaction {
1400-
stack: Witness::combine(c_sat.stack, a_nsat.stack),
1401-
has_sig: a_nsat.has_sig || c_sat.has_sig,
1402-
// timelocks can't be dissatisfied, so here we ignore a_nsat and only consider c_sat
1403-
relative_timelock: c_sat.relative_timelock,
1404-
absolute_timelock: c_sat.absolute_timelock,
1405-
},
1406-
)
1387+
min_fn(a_sat.concatenate_rev(b_sat), a_nsat.concatenate_rev(c_sat))
14071388
}
14081389
Terminal::OrB(ref l, ref r) => {
14091390
let l_sat =
@@ -1431,18 +1412,8 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
14311412
assert!(!r_nsat.has_sig);
14321413

14331414
min_fn(
1434-
Satisfaction {
1435-
stack: Witness::combine(r_sat.stack, l_nsat.stack),
1436-
has_sig: r_sat.has_sig,
1437-
relative_timelock: r_sat.relative_timelock,
1438-
absolute_timelock: r_sat.absolute_timelock,
1439-
},
1440-
Satisfaction {
1441-
stack: Witness::combine(r_nsat.stack, l_sat.stack),
1442-
has_sig: l_sat.has_sig,
1443-
relative_timelock: l_sat.relative_timelock,
1444-
absolute_timelock: l_sat.absolute_timelock,
1445-
},
1415+
Satisfaction::concatenate_rev(l_nsat, r_sat),
1416+
Satisfaction::concatenate_rev(l_sat, r_nsat),
14461417
)
14471418
}
14481419
Terminal::OrD(ref l, ref r) | Terminal::OrC(ref l, ref r) => {
@@ -1461,15 +1432,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
14611432

14621433
assert!(!l_nsat.has_sig);
14631434

1464-
min_fn(
1465-
l_sat,
1466-
Satisfaction {
1467-
stack: Witness::combine(r_sat.stack, l_nsat.stack),
1468-
has_sig: r_sat.has_sig,
1469-
relative_timelock: r_sat.relative_timelock,
1470-
absolute_timelock: r_sat.absolute_timelock,
1471-
},
1472-
)
1435+
min_fn(l_sat, Satisfaction::concatenate_rev(l_nsat, r_sat))
14731436
}
14741437
Terminal::OrI(ref l, ref r) => {
14751438
let l_sat =
@@ -1492,7 +1455,24 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
14921455
)
14931456
}
14941457
Terminal::Thresh(ref thresh) => {
1495-
thresh_fn(thresh, stfr, root_has_sig, leaf_hash, min_fn)
1458+
if thresh.k() == thresh.n() {
1459+
// this is just an and
1460+
thresh
1461+
.iter()
1462+
.map(|s| {
1463+
Self::satisfy_helper(
1464+
&s.node,
1465+
stfr,
1466+
root_has_sig,
1467+
leaf_hash,
1468+
min_fn,
1469+
thresh_fn,
1470+
)
1471+
})
1472+
.fold(Satisfaction::empty(), Satisfaction::concatenate_rev)
1473+
} else {
1474+
thresh_fn(thresh, stfr, root_has_sig, leaf_hash, min_fn)
1475+
}
14961476
}
14971477
Terminal::Multi(ref thresh) => {
14981478
// Collect all available signatures
@@ -1682,12 +1662,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
16821662
min_fn,
16831663
thresh_fn,
16841664
);
1685-
Satisfaction {
1686-
stack: Witness::combine(odissat.stack, vsat.stack),
1687-
has_sig: vsat.has_sig || odissat.has_sig,
1688-
relative_timelock: None,
1689-
absolute_timelock: None,
1690-
}
1665+
vsat.concatenate_rev(odissat)
16911666
}
16921667
Terminal::AndB(ref l, ref r)
16931668
| Terminal::OrB(ref l, ref r)
@@ -1709,12 +1684,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
17091684
min_fn,
17101685
thresh_fn,
17111686
);
1712-
Satisfaction {
1713-
stack: Witness::combine(rnsat.stack, lnsat.stack),
1714-
has_sig: rnsat.has_sig || lnsat.has_sig,
1715-
relative_timelock: None,
1716-
absolute_timelock: None,
1717-
}
1687+
lnsat.concatenate_rev(rnsat)
17181688
}
17191689
Terminal::OrI(ref l, ref r) => {
17201690
let lnsat = Self::dissatisfy_helper(
@@ -1750,23 +1720,19 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
17501720
// Dissatisfactions don't need to non-malleable. Use minimum_mall always
17511721
Satisfaction::minimum_mall(dissat_1, dissat_2)
17521722
}
1753-
Terminal::Thresh(ref thresh) => Satisfaction {
1754-
stack: thresh.iter().fold(Witness::empty(), |acc, sub| {
1755-
let nsat = Self::dissatisfy_helper(
1756-
&sub.node,
1723+
Terminal::Thresh(ref thresh) => thresh
1724+
.iter()
1725+
.map(|s| {
1726+
Self::dissatisfy_helper(
1727+
&s.node,
17571728
stfr,
17581729
root_has_sig,
17591730
leaf_hash,
17601731
min_fn,
17611732
thresh_fn,
1762-
);
1763-
assert!(!nsat.has_sig);
1764-
Witness::combine(nsat.stack, acc)
1765-
}),
1766-
has_sig: false,
1767-
relative_timelock: None,
1768-
absolute_timelock: None,
1769-
},
1733+
)
1734+
})
1735+
.fold(Satisfaction::empty(), Satisfaction::concatenate_rev),
17701736
Terminal::Multi(ref thresh) => Satisfaction {
17711737
stack: Witness::Stack(vec![Placeholder::PushZero; thresh.k() + 1]),
17721738
has_sig: false,

0 commit comments

Comments
 (0)