Merge ff0194a7ce9dabf1b31b64ca584e45840dce8141 into 5f4422d68dc3530c353af1f87499de1c864b60ad

This commit is contained in:
Antoine Poinsot 2025-03-17 03:54:22 +01:00 committed by GitHub
commit 6dd8f44eda
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 54 additions and 51 deletions

View File

@ -19,20 +19,20 @@ namespace internal {
Type SanitizeType(Type e) { Type SanitizeType(Type e) {
int num_types = (e << "K"_mst) + (e << "V"_mst) + (e << "B"_mst) + (e << "W"_mst); int num_types = (e << "K"_mst) + (e << "V"_mst) + (e << "B"_mst) + (e << "W"_mst);
if (num_types == 0) return ""_mst; // No valid type, don't care about the rest if (num_types == 0) return ""_mst; // No valid type, don't care about the rest
assert(num_types == 1); // K, V, B, W all conflict with each other CHECK_NONFATAL(num_types == 1); // K, V, B, W all conflict with each other
assert(!(e << "z"_mst) || !(e << "o"_mst)); // z conflicts with o CHECK_NONFATAL(!(e << "z"_mst) || !(e << "o"_mst)); // z conflicts with o
assert(!(e << "n"_mst) || !(e << "z"_mst)); // n conflicts with z CHECK_NONFATAL(!(e << "n"_mst) || !(e << "z"_mst)); // n conflicts with z
assert(!(e << "n"_mst) || !(e << "W"_mst)); // n conflicts with W CHECK_NONFATAL(!(e << "n"_mst) || !(e << "W"_mst)); // n conflicts with W
assert(!(e << "V"_mst) || !(e << "d"_mst)); // V conflicts with d CHECK_NONFATAL(!(e << "V"_mst) || !(e << "d"_mst)); // V conflicts with d
assert(!(e << "K"_mst) || (e << "u"_mst)); // K implies u CHECK_NONFATAL(!(e << "K"_mst) || (e << "u"_mst)); // K implies u
assert(!(e << "V"_mst) || !(e << "u"_mst)); // V conflicts with u CHECK_NONFATAL(!(e << "V"_mst) || !(e << "u"_mst)); // V conflicts with u
assert(!(e << "e"_mst) || !(e << "f"_mst)); // e conflicts with f CHECK_NONFATAL(!(e << "e"_mst) || !(e << "f"_mst)); // e conflicts with f
assert(!(e << "e"_mst) || (e << "d"_mst)); // e implies d CHECK_NONFATAL(!(e << "e"_mst) || (e << "d"_mst)); // e implies d
assert(!(e << "V"_mst) || !(e << "e"_mst)); // V conflicts with e CHECK_NONFATAL(!(e << "V"_mst) || !(e << "e"_mst)); // V conflicts with e
assert(!(e << "d"_mst) || !(e << "f"_mst)); // d conflicts with f CHECK_NONFATAL(!(e << "d"_mst) || !(e << "f"_mst)); // d conflicts with f
assert(!(e << "V"_mst) || (e << "f"_mst)); // V implies f CHECK_NONFATAL(!(e << "V"_mst) || (e << "f"_mst)); // V implies f
assert(!(e << "K"_mst) || (e << "s"_mst)); // K implies s CHECK_NONFATAL(!(e << "K"_mst) || (e << "s"_mst)); // K implies s
assert(!(e << "z"_mst) || (e << "m"_mst)); // z implies m CHECK_NONFATAL(!(e << "z"_mst) || (e << "m"_mst)); // z implies m
return e; return e;
} }
@ -40,46 +40,46 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty
size_t data_size, size_t n_subs, size_t n_keys, MiniscriptContext ms_ctx) { size_t data_size, size_t n_subs, size_t n_keys, MiniscriptContext ms_ctx) {
// Sanity check on data // Sanity check on data
if (fragment == Fragment::SHA256 || fragment == Fragment::HASH256) { if (fragment == Fragment::SHA256 || fragment == Fragment::HASH256) {
assert(data_size == 32); CHECK_NONFATAL(data_size == 32);
} else if (fragment == Fragment::RIPEMD160 || fragment == Fragment::HASH160) { } else if (fragment == Fragment::RIPEMD160 || fragment == Fragment::HASH160) {
assert(data_size == 20); CHECK_NONFATAL(data_size == 20);
} else { } else {
assert(data_size == 0); CHECK_NONFATAL(data_size == 0);
} }
// Sanity check on k // Sanity check on k
if (fragment == Fragment::OLDER || fragment == Fragment::AFTER) { if (fragment == Fragment::OLDER || fragment == Fragment::AFTER) {
assert(k >= 1 && k < 0x80000000UL); CHECK_NONFATAL(k >= 1 && k < 0x80000000UL);
} else if (fragment == Fragment::MULTI || fragment == Fragment::MULTI_A) { } else if (fragment == Fragment::MULTI || fragment == Fragment::MULTI_A) {
assert(k >= 1 && k <= n_keys); CHECK_NONFATAL(k >= 1 && k <= n_keys);
} else if (fragment == Fragment::THRESH) { } else if (fragment == Fragment::THRESH) {
assert(k >= 1 && k <= n_subs); CHECK_NONFATAL(k >= 1 && k <= n_subs);
} else { } else {
assert(k == 0); CHECK_NONFATAL(k == 0);
} }
// Sanity check on subs // Sanity check on subs
if (fragment == Fragment::AND_V || fragment == Fragment::AND_B || fragment == Fragment::OR_B || if (fragment == Fragment::AND_V || fragment == Fragment::AND_B || fragment == Fragment::OR_B ||
fragment == Fragment::OR_C || fragment == Fragment::OR_I || fragment == Fragment::OR_D) { fragment == Fragment::OR_C || fragment == Fragment::OR_I || fragment == Fragment::OR_D) {
assert(n_subs == 2); CHECK_NONFATAL(n_subs == 2);
} else if (fragment == Fragment::ANDOR) { } else if (fragment == Fragment::ANDOR) {
assert(n_subs == 3); CHECK_NONFATAL(n_subs == 3);
} else if (fragment == Fragment::WRAP_A || fragment == Fragment::WRAP_S || fragment == Fragment::WRAP_C || } else if (fragment == Fragment::WRAP_A || fragment == Fragment::WRAP_S || fragment == Fragment::WRAP_C ||
fragment == Fragment::WRAP_D || fragment == Fragment::WRAP_V || fragment == Fragment::WRAP_J || fragment == Fragment::WRAP_D || fragment == Fragment::WRAP_V || fragment == Fragment::WRAP_J ||
fragment == Fragment::WRAP_N) { fragment == Fragment::WRAP_N) {
assert(n_subs == 1); CHECK_NONFATAL(n_subs == 1);
} else if (fragment != Fragment::THRESH) { } else if (fragment != Fragment::THRESH) {
assert(n_subs == 0); CHECK_NONFATAL(n_subs == 0);
} }
// Sanity check on keys // Sanity check on keys
if (fragment == Fragment::PK_K || fragment == Fragment::PK_H) { if (fragment == Fragment::PK_K || fragment == Fragment::PK_H) {
assert(n_keys == 1); CHECK_NONFATAL(n_keys == 1);
} else if (fragment == Fragment::MULTI) { } else if (fragment == Fragment::MULTI) {
assert(n_keys >= 1 && n_keys <= MAX_PUBKEYS_PER_MULTISIG); CHECK_NONFATAL(n_keys >= 1 && n_keys <= MAX_PUBKEYS_PER_MULTISIG);
assert(!IsTapscript(ms_ctx)); CHECK_NONFATAL(!IsTapscript(ms_ctx));
} else if (fragment == Fragment::MULTI_A) { } else if (fragment == Fragment::MULTI_A) {
assert(n_keys >= 1 && n_keys <= MAX_PUBKEYS_PER_MULTI_A); CHECK_NONFATAL(n_keys >= 1 && n_keys <= MAX_PUBKEYS_PER_MULTI_A);
assert(IsTapscript(ms_ctx)); CHECK_NONFATAL(IsTapscript(ms_ctx));
} else { } else {
assert(n_keys == 0); CHECK_NONFATAL(n_keys == 0);
} }
// Below is the per-fragment logic for computing the expression types. // Below is the per-fragment logic for computing the expression types.

View File

@ -659,7 +659,8 @@ private:
stack.pop_back(); stack.pop_back();
} }
// The final remaining results element is the root result, return it. // The final remaining results element is the root result, return it.
assert(results.size() == 1); assert(results.size() >= 1);
CHECK_NONFATAL(results.size() == 1);
return std::move(results[0]); return std::move(results[0]);
} }
@ -1225,7 +1226,7 @@ private:
// The dissatisfaction consists of as many empty vectors as there are keys, which is the same as // The dissatisfaction consists of as many empty vectors as there are keys, which is the same as
// satisfying 0 keys. // satisfying 0 keys.
auto& nsat{sats[0]}; auto& nsat{sats[0]};
assert(node.k != 0); CHECK_NONFATAL(node.k != 0);
assert(node.k <= sats.size()); assert(node.k <= sats.size());
return {std::move(nsat), std::move(sats[node.k])}; return {std::move(nsat), std::move(sats[node.k])};
} }
@ -1392,38 +1393,38 @@ private:
// (the actual satisfaction code in ProduceInputHelper does not use GetType) // (the actual satisfaction code in ProduceInputHelper does not use GetType)
// For 'z' nodes, available satisfactions/dissatisfactions must have stack size 0. // For 'z' nodes, available satisfactions/dissatisfactions must have stack size 0.
if (node.GetType() << "z"_mst && ret.nsat.available != Availability::NO) assert(ret.nsat.stack.size() == 0); if (node.GetType() << "z"_mst && ret.nsat.available != Availability::NO) CHECK_NONFATAL(ret.nsat.stack.size() == 0);
if (node.GetType() << "z"_mst && ret.sat.available != Availability::NO) assert(ret.sat.stack.size() == 0); if (node.GetType() << "z"_mst && ret.sat.available != Availability::NO) CHECK_NONFATAL(ret.sat.stack.size() == 0);
// For 'o' nodes, available satisfactions/dissatisfactions must have stack size 1. // For 'o' nodes, available satisfactions/dissatisfactions must have stack size 1.
if (node.GetType() << "o"_mst && ret.nsat.available != Availability::NO) assert(ret.nsat.stack.size() == 1); if (node.GetType() << "o"_mst && ret.nsat.available != Availability::NO) CHECK_NONFATAL(ret.nsat.stack.size() == 1);
if (node.GetType() << "o"_mst && ret.sat.available != Availability::NO) assert(ret.sat.stack.size() == 1); if (node.GetType() << "o"_mst && ret.sat.available != Availability::NO) CHECK_NONFATAL(ret.sat.stack.size() == 1);
// For 'n' nodes, available satisfactions/dissatisfactions must have stack size 1 or larger. For satisfactions, // For 'n' nodes, available satisfactions/dissatisfactions must have stack size 1 or larger. For satisfactions,
// the top element cannot be 0. // the top element cannot be 0.
if (node.GetType() << "n"_mst && ret.sat.available != Availability::NO) assert(ret.sat.stack.size() >= 1); if (node.GetType() << "n"_mst && ret.sat.available != Availability::NO) CHECK_NONFATAL(ret.sat.stack.size() >= 1);
if (node.GetType() << "n"_mst && ret.nsat.available != Availability::NO) assert(ret.nsat.stack.size() >= 1); if (node.GetType() << "n"_mst && ret.nsat.available != Availability::NO) CHECK_NONFATAL(ret.nsat.stack.size() >= 1);
if (node.GetType() << "n"_mst && ret.sat.available != Availability::NO) assert(!ret.sat.stack.back().empty()); if (node.GetType() << "n"_mst && ret.sat.available != Availability::NO) CHECK_NONFATAL(!ret.sat.stack.back().empty());
// For 'd' nodes, a dissatisfaction must exist, and they must not need a signature. If it is non-malleable, // For 'd' nodes, a dissatisfaction must exist, and they must not need a signature. If it is non-malleable,
// it must be canonical. // it must be canonical.
if (node.GetType() << "d"_mst) assert(ret.nsat.available != Availability::NO); if (node.GetType() << "d"_mst) CHECK_NONFATAL(ret.nsat.available != Availability::NO);
if (node.GetType() << "d"_mst) assert(!ret.nsat.has_sig); if (node.GetType() << "d"_mst) CHECK_NONFATAL(!ret.nsat.has_sig);
if (node.GetType() << "d"_mst && !ret.nsat.malleable) assert(!ret.nsat.non_canon); if (node.GetType() << "d"_mst && !ret.nsat.malleable) CHECK_NONFATAL(!ret.nsat.non_canon);
// For 'f'/'s' nodes, dissatisfactions/satisfactions must have a signature. // For 'f'/'s' nodes, dissatisfactions/satisfactions must have a signature.
if (node.GetType() << "f"_mst && ret.nsat.available != Availability::NO) assert(ret.nsat.has_sig); if (node.GetType() << "f"_mst && ret.nsat.available != Availability::NO) CHECK_NONFATAL(ret.nsat.has_sig);
if (node.GetType() << "s"_mst && ret.sat.available != Availability::NO) assert(ret.sat.has_sig); if (node.GetType() << "s"_mst && ret.sat.available != Availability::NO) CHECK_NONFATAL(ret.sat.has_sig);
// For non-malleable 'e' nodes, a non-malleable dissatisfaction must exist. // For non-malleable 'e' nodes, a non-malleable dissatisfaction must exist.
if (node.GetType() << "me"_mst) assert(ret.nsat.available != Availability::NO); if (node.GetType() << "me"_mst) CHECK_NONFATAL(ret.nsat.available != Availability::NO);
if (node.GetType() << "me"_mst) assert(!ret.nsat.malleable); if (node.GetType() << "me"_mst) CHECK_NONFATAL(!ret.nsat.malleable);
// For 'm' nodes, if a satisfaction exists, it must be non-malleable. // For 'm' nodes, if a satisfaction exists, it must be non-malleable.
if (node.GetType() << "m"_mst && ret.sat.available != Availability::NO) assert(!ret.sat.malleable); if (node.GetType() << "m"_mst && ret.sat.available != Availability::NO) CHECK_NONFATAL(!ret.sat.malleable);
// If a non-malleable satisfaction exists, it must be canonical. // If a non-malleable satisfaction exists, it must be canonical.
if (ret.sat.available != Availability::NO && !ret.sat.malleable) assert(!ret.sat.non_canon); if (ret.sat.available != Availability::NO && !ret.sat.malleable) CHECK_NONFATAL(!ret.sat.non_canon);
return ret; return ret;
}; };
@ -1604,7 +1605,8 @@ public:
case Fragment::THRESH: case Fragment::THRESH:
return static_cast<uint32_t>(std::count(subs.begin(), subs.end(), true)) >= node.k; return static_cast<uint32_t>(std::count(subs.begin(), subs.end(), true)) >= node.k;
default: // wrappers default: // wrappers
assert(subs.size() == 1); assert(subs.size() >= 1);
CHECK_NONFATAL(subs.size() == 1);
return subs[0]; return subs[0];
} }
}); });
@ -2157,7 +2159,8 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
} }
// Sanity checks on the produced miniscript // Sanity checks on the produced miniscript
assert(constructed.size() == 1); assert(constructed.size() >= 1);
CHECK_NONFATAL(constructed.size() == 1);
assert(constructed[0]->ScriptSize() == script_size); assert(constructed[0]->ScriptSize() == script_size);
if (in.size() > 0) return {}; if (in.size() > 0) return {};
NodeRef<Key> tl_node = std::move(constructed.front()); NodeRef<Key> tl_node = std::move(constructed.front());