Skip to content

Commit 4fb4101

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 a1ca42a commit 4fb4101

1 file changed

Lines changed: 62 additions & 101 deletions

File tree

src/miniscript/satisfy.rs

Lines changed: 62 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,29 @@ pub struct Satisfaction<T> {
892892
}
893893

894894
impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
895+
/// The empty satisfaction.
896+
///
897+
/// This has the property that, when concatenated on either side with another satisfaction
898+
/// X, the result will be X.
899+
fn empty() -> Self {
900+
Satisfaction {
901+
has_sig: false,
902+
relative_timelock: None,
903+
absolute_timelock: None,
904+
stack: Witness::Stack(vec![]),
905+
}
906+
}
907+
908+
/// Forms a satisfaction which is the concatenation of two satisfactions.
909+
fn concatenate(self, other: Self) -> Self {
910+
Satisfaction {
911+
has_sig: self.has_sig || other.has_sig,
912+
relative_timelock: cmp::max(self.relative_timelock, other.relative_timelock),
913+
absolute_timelock: cmp::max(self.absolute_timelock, other.absolute_timelock),
914+
stack: Witness::combine(self.stack, other.stack),
915+
}
916+
}
917+
895918
pub(crate) fn build_template<P, Ctx>(
896919
term: &Terminal<Pk, Ctx>,
897920
provider: &P,
@@ -1044,20 +1067,9 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
10441067
}
10451068
} else {
10461069
// Otherwise flatten everything out
1047-
Satisfaction {
1048-
has_sig: ret_stack.iter().any(|sat| sat.has_sig),
1049-
relative_timelock: ret_stack
1050-
.iter()
1051-
.filter_map(|sat| sat.relative_timelock)
1052-
.max(),
1053-
absolute_timelock: ret_stack
1054-
.iter()
1055-
.filter_map(|sat| sat.absolute_timelock)
1056-
.max(),
1057-
stack: ret_stack
1058-
.into_iter()
1059-
.fold(Witness::empty(), |acc, next| Witness::combine(next.stack, acc)),
1060-
}
1070+
ret_stack
1071+
.into_iter()
1072+
.fold(Satisfaction::empty(), Satisfaction::concatenate)
10611073
}
10621074
}
10631075

@@ -1128,20 +1140,9 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
11281140

11291141
// combine the witness
11301142
// no non-malleability checks needed
1131-
Satisfaction {
1132-
has_sig: ret_stack.iter().any(|sat| sat.has_sig),
1133-
relative_timelock: ret_stack
1134-
.iter()
1135-
.filter_map(|sat| sat.relative_timelock)
1136-
.max(),
1137-
absolute_timelock: ret_stack
1138-
.iter()
1139-
.filter_map(|sat| sat.absolute_timelock)
1140-
.max(),
1141-
stack: ret_stack
1142-
.into_iter()
1143-
.fold(Witness::empty(), |acc, next| Witness::combine(next.stack, acc)),
1144-
}
1143+
ret_stack
1144+
.into_iter()
1145+
.fold(Satisfaction::empty(), Satisfaction::concatenate)
11451146
}
11461147

11471148
fn minimum(sat1: Self, sat2: Self) -> Self {
@@ -1363,12 +1364,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
13631364
Self::satisfy_helper(&l.node, stfr, root_has_sig, leaf_hash, min_fn, thresh_fn);
13641365
let r_sat =
13651366
Self::satisfy_helper(&r.node, stfr, root_has_sig, leaf_hash, min_fn, thresh_fn);
1366-
Satisfaction {
1367-
stack: Witness::combine(r_sat.stack, l_sat.stack),
1368-
has_sig: l_sat.has_sig || r_sat.has_sig,
1369-
relative_timelock: cmp::max(l_sat.relative_timelock, r_sat.relative_timelock),
1370-
absolute_timelock: cmp::max(l_sat.absolute_timelock, r_sat.absolute_timelock),
1371-
}
1367+
r_sat.concatenate(l_sat)
13721368
}
13731369
Terminal::AndOr(ref a, ref b, ref c) => {
13741370
let a_sat =
@@ -1386,27 +1382,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
13861382
let c_sat =
13871383
Self::satisfy_helper(&c.node, stfr, root_has_sig, leaf_hash, min_fn, thresh_fn);
13881384

1389-
min_fn(
1390-
Satisfaction {
1391-
stack: Witness::combine(b_sat.stack, a_sat.stack),
1392-
has_sig: a_sat.has_sig || b_sat.has_sig,
1393-
relative_timelock: cmp::max(
1394-
a_sat.relative_timelock,
1395-
b_sat.relative_timelock,
1396-
),
1397-
absolute_timelock: cmp::max(
1398-
a_sat.absolute_timelock,
1399-
b_sat.absolute_timelock,
1400-
),
1401-
},
1402-
Satisfaction {
1403-
stack: Witness::combine(c_sat.stack, a_nsat.stack),
1404-
has_sig: a_nsat.has_sig || c_sat.has_sig,
1405-
// timelocks can't be dissatisfied, so here we ignore a_nsat and only consider c_sat
1406-
relative_timelock: c_sat.relative_timelock,
1407-
absolute_timelock: c_sat.absolute_timelock,
1408-
},
1409-
)
1385+
min_fn(b_sat.concatenate(a_sat), c_sat.concatenate(a_nsat))
14101386
}
14111387
Terminal::OrB(ref l, ref r) => {
14121388
let l_sat =
@@ -1434,18 +1410,8 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
14341410
assert!(!r_nsat.has_sig);
14351411

14361412
min_fn(
1437-
Satisfaction {
1438-
stack: Witness::combine(r_sat.stack, l_nsat.stack),
1439-
has_sig: r_sat.has_sig,
1440-
relative_timelock: r_sat.relative_timelock,
1441-
absolute_timelock: r_sat.absolute_timelock,
1442-
},
1443-
Satisfaction {
1444-
stack: Witness::combine(r_nsat.stack, l_sat.stack),
1445-
has_sig: l_sat.has_sig,
1446-
relative_timelock: l_sat.relative_timelock,
1447-
absolute_timelock: l_sat.absolute_timelock,
1448-
},
1413+
Satisfaction::concatenate(r_sat, l_nsat),
1414+
Satisfaction::concatenate(r_nsat, l_sat),
14491415
)
14501416
}
14511417
Terminal::OrD(ref l, ref r) | Terminal::OrC(ref l, ref r) => {
@@ -1464,15 +1430,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
14641430

14651431
assert!(!l_nsat.has_sig);
14661432

1467-
min_fn(
1468-
l_sat,
1469-
Satisfaction {
1470-
stack: Witness::combine(r_sat.stack, l_nsat.stack),
1471-
has_sig: r_sat.has_sig,
1472-
relative_timelock: r_sat.relative_timelock,
1473-
absolute_timelock: r_sat.absolute_timelock,
1474-
},
1475-
)
1433+
min_fn(l_sat, Satisfaction::concatenate(r_sat, l_nsat))
14761434
}
14771435
Terminal::OrI(ref l, ref r) => {
14781436
let l_sat =
@@ -1495,7 +1453,24 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
14951453
)
14961454
}
14971455
Terminal::Thresh(ref thresh) => {
1498-
thresh_fn(thresh, stfr, root_has_sig, leaf_hash, min_fn)
1456+
if thresh.k() == thresh.n() {
1457+
// this is just an and
1458+
thresh
1459+
.iter()
1460+
.map(|s| {
1461+
Self::satisfy_helper(
1462+
&s.node,
1463+
stfr,
1464+
root_has_sig,
1465+
leaf_hash,
1466+
min_fn,
1467+
thresh_fn,
1468+
)
1469+
})
1470+
.fold(Satisfaction::empty(), Satisfaction::concatenate)
1471+
} else {
1472+
thresh_fn(thresh, stfr, root_has_sig, leaf_hash, min_fn)
1473+
}
14991474
}
15001475
Terminal::Multi(ref thresh) => {
15011476
// Collect all available signatures
@@ -1685,12 +1660,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
16851660
min_fn,
16861661
thresh_fn,
16871662
);
1688-
Satisfaction {
1689-
stack: Witness::combine(odissat.stack, vsat.stack),
1690-
has_sig: vsat.has_sig || odissat.has_sig,
1691-
relative_timelock: None,
1692-
absolute_timelock: None,
1693-
}
1663+
odissat.concatenate(vsat)
16941664
}
16951665
Terminal::AndB(ref l, ref r)
16961666
| Terminal::OrB(ref l, ref r)
@@ -1712,12 +1682,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
17121682
min_fn,
17131683
thresh_fn,
17141684
);
1715-
Satisfaction {
1716-
stack: Witness::combine(rnsat.stack, lnsat.stack),
1717-
has_sig: rnsat.has_sig || lnsat.has_sig,
1718-
relative_timelock: None,
1719-
absolute_timelock: None,
1720-
}
1685+
rnsat.concatenate(lnsat)
17211686
}
17221687
Terminal::OrI(ref l, ref r) => {
17231688
let lnsat = Self::dissatisfy_helper(
@@ -1753,23 +1718,19 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
17531718
// Dissatisfactions don't need to non-malleable. Use minimum_mall always
17541719
Satisfaction::minimum_mall(dissat_1, dissat_2)
17551720
}
1756-
Terminal::Thresh(ref thresh) => Satisfaction {
1757-
stack: thresh.iter().fold(Witness::empty(), |acc, sub| {
1758-
let nsat = Self::dissatisfy_helper(
1759-
&sub.node,
1721+
Terminal::Thresh(ref thresh) => thresh
1722+
.iter()
1723+
.map(|s| {
1724+
Self::dissatisfy_helper(
1725+
&s.node,
17601726
stfr,
17611727
root_has_sig,
17621728
leaf_hash,
17631729
min_fn,
17641730
thresh_fn,
1765-
);
1766-
assert!(!nsat.has_sig);
1767-
Witness::combine(nsat.stack, acc)
1768-
}),
1769-
has_sig: false,
1770-
relative_timelock: None,
1771-
absolute_timelock: None,
1772-
},
1731+
)
1732+
})
1733+
.fold(Satisfaction::empty(), Satisfaction::concatenate),
17731734
Terminal::Multi(ref thresh) => Satisfaction {
17741735
stack: Witness::Stack(vec![Placeholder::PushZero; thresh.k() + 1]),
17751736
has_sig: false,

0 commit comments

Comments
 (0)