Skip to content

Commit bdba766

Browse files
committed
miniscript: introduce a helper to get the maximum witness size
Similarly to how we compute the maximum stack size. Also note how it would be quite expensive to recompute it recursively by accounting for different ECDSA signature sizes. So we just assume high-R everywhere. It's only a trivial difference anyways.
1 parent 4ab382c commit bdba766

File tree

2 files changed

+107
-38
lines changed

2 files changed

+107
-38
lines changed

src/script/miniscript.h

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,15 @@ struct StackSize {
337337
StackSize(MaxInt<uint32_t> in_sat, MaxInt<uint32_t> in_dsat) : sat(in_sat), dsat(in_dsat) {};
338338
};
339339

340+
struct WitnessSize {
341+
//! Maximum witness size to satisfy;
342+
MaxInt<uint32_t> sat;
343+
//! Maximum witness size to dissatisfy;
344+
MaxInt<uint32_t> dsat;
345+
346+
WitnessSize(MaxInt<uint32_t> in_sat, MaxInt<uint32_t> in_dsat) : sat(in_sat), dsat(in_dsat) {};
347+
};
348+
340349
struct NoDupCheck {};
341350

342351
} // namespace internal
@@ -360,6 +369,8 @@ struct Node {
360369
const internal::Ops ops;
361370
//! Cached stack size bounds.
362371
const internal::StackSize ss;
372+
//! Cached witness size bounds.
373+
const internal::WitnessSize ws;
363374
//! Cached expression type (computed by CalcType and fed through SanitizeType).
364375
const Type typ;
365376
//! Cached script length (computed by CalcScriptLen).
@@ -846,6 +857,56 @@ struct Node {
846857
assert(false);
847858
}
848859

860+
internal::WitnessSize CalcWitnessSize() const {
861+
switch (fragment) {
862+
case Fragment::JUST_0: return {{}, 0};
863+
case Fragment::JUST_1:
864+
case Fragment::OLDER:
865+
case Fragment::AFTER: return {0, {}};
866+
case Fragment::PK_K: return {1 + 72, 1};
867+
case Fragment::PK_H: return {1 + 72 + 1 + 33, 1 + 1 + 33};
868+
case Fragment::SHA256:
869+
case Fragment::RIPEMD160:
870+
case Fragment::HASH256:
871+
case Fragment::HASH160: return {1 + 32, {}};
872+
case Fragment::ANDOR: {
873+
const auto sat{(subs[0]->ws.sat + subs[1]->ws.sat) | (subs[0]->ws.dsat + subs[2]->ws.sat)};
874+
const auto dsat{subs[0]->ws.dsat + subs[2]->ws.dsat};
875+
return {sat, dsat};
876+
}
877+
case Fragment::AND_V: return {subs[0]->ws.sat + subs[1]->ws.sat, {}};
878+
case Fragment::AND_B: return {subs[0]->ws.sat + subs[1]->ws.sat, subs[0]->ws.dsat + subs[1]->ws.dsat};
879+
case Fragment::OR_B: {
880+
const auto sat{(subs[0]->ws.dsat + subs[1]->ws.sat) | (subs[0]->ws.sat + subs[1]->ws.dsat)};
881+
const auto dsat{subs[0]->ws.dsat + subs[1]->ws.dsat};
882+
return {sat, dsat};
883+
}
884+
case Fragment::OR_C: return {subs[0]->ws.sat | (subs[0]->ws.dsat + subs[1]->ws.sat), {}};
885+
case Fragment::OR_D: return {subs[0]->ws.sat | (subs[0]->ws.dsat + subs[1]->ws.sat), subs[0]->ws.dsat + subs[1]->ws.dsat};
886+
case Fragment::OR_I: return {(subs[0]->ws.sat + 1 + 1) | (subs[1]->ws.sat + 1), (subs[0]->ws.dsat + 1 + 1) | (subs[1]->ws.dsat + 1)};
887+
case Fragment::MULTI: return {k * (1 + 72) + 1, k + 1};
888+
case Fragment::WRAP_A:
889+
case Fragment::WRAP_N:
890+
case Fragment::WRAP_S:
891+
case Fragment::WRAP_C: return subs[0]->ws;
892+
case Fragment::WRAP_D: return {1 + 1 + subs[0]->ws.sat, 1};
893+
case Fragment::WRAP_V: return {subs[0]->ws.sat, {}};
894+
case Fragment::WRAP_J: return {subs[0]->ws.sat, 1};
895+
case Fragment::THRESH: {
896+
auto sats = Vector(internal::MaxInt<uint32_t>(0));
897+
for (const auto& sub : subs) {
898+
auto next_sats = Vector(sats[0] + sub->ws.dsat);
899+
for (size_t j = 1; j < sats.size(); ++j) next_sats.push_back((sats[j] + sub->ws.dsat) | (sats[j - 1] + sub->ws.sat));
900+
next_sats.push_back(sats[sats.size() - 1] + sub->ws.sat);
901+
sats = std::move(next_sats);
902+
}
903+
assert(k <= sats.size());
904+
return {sats[k], sats[0]};
905+
}
906+
}
907+
assert(false);
908+
}
909+
849910
template<typename Ctx>
850911
internal::InputResult ProduceInput(const Ctx& ctx) const {
851912
using namespace internal;
@@ -1164,6 +1225,13 @@ struct Node {
11641225
//! Whether no satisfaction exists for this node.
11651226
bool IsNotSatisfiable() const { return !GetStackSize(); }
11661227

1228+
/** Return the maximum size in bytes of a witness to satisfy this script non-malleably. Note this does
1229+
* not include the witness script push. */
1230+
std::optional<uint32_t> GetWitnessSize() const {
1231+
if (!ws.sat.valid) return {};
1232+
return ws.sat.value;
1233+
}
1234+
11671235
//! Return the expression type.
11681236
Type GetType() const { return typ; }
11691237

@@ -1260,12 +1328,12 @@ struct Node {
12601328
bool operator==(const Node<Key>& arg) const { return Compare(*this, arg) == 0; }
12611329

12621330
// Constructors with various argument combinations, which bypass the duplicate key check.
1263-
Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
1264-
Node(internal::NoDupCheck, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
1265-
Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
1266-
Node(internal::NoDupCheck, Fragment nt, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
1267-
Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : fragment(nt), k(val), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
1268-
Node(internal::NoDupCheck, Fragment nt, uint32_t val = 0) : fragment(nt), k(val), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
1331+
Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
1332+
Node(internal::NoDupCheck, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
1333+
Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
1334+
Node(internal::NoDupCheck, Fragment nt, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
1335+
Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : fragment(nt), k(val), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
1336+
Node(internal::NoDupCheck, Fragment nt, uint32_t val = 0) : fragment(nt), k(val), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
12691337

12701338
// Constructors with various argument combinations, which do perform the duplicate key check.
12711339
template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, std::move(sub), std::move(arg), val) { DuplicateKeyCheck(ctx); }

0 commit comments

Comments
 (0)