@@ -373,13 +373,112 @@ struct Ops {
373
373
Ops (uint32_t in_count, MaxInt<uint32_t > in_sat, MaxInt<uint32_t > in_dsat) : count(in_count), sat(in_sat), dsat(in_dsat) {};
374
374
};
375
375
376
+ /* * A data structure to help the calculation of stack size limits.
377
+ *
378
+ * Conceptually, every SatInfo object corresponds to a (possibly empty) set of script execution
379
+ * traces (sequences of opcodes).
380
+ * - SatInfo{} corresponds to the empty set.
381
+ * - SatInfo{n, e} corresponds to a single trace whose net effect is removing n elements from the
382
+ * stack (may be negative for a net increase), and reaches a maximum of e stack elements more
383
+ * than it ends with.
384
+ * - operator| is the union operation: (a | b) corresponds to the union of the traces in a and the
385
+ * traces in b.
386
+ * - operator+ is the concatenation operator: (a + b) corresponds to the set of traces formed by
387
+ * concatenating any trace in a with any trace in b.
388
+ *
389
+ * Its fields are:
390
+ * - valid is true if the set is non-empty.
391
+ * - netdiff (if valid) is the largest difference between stack size at the beginning and at the
392
+ * end of the script across all traces in the set.
393
+ * - exec (if valid) is the largest difference between stack size anywhere during execution and at
394
+ * the end of the script, across all traces in the set (note that this is not necessarily due
395
+ * to the same trace as the one that resulted in the value for netdiff).
396
+ *
397
+ * This allows us to build up stack size limits for any script efficiently, by starting from the
398
+ * individual opcodes miniscripts correspond to, using concatenation to construct scripts, and
399
+ * using the union operation to choose between execution branches. Since any top-level script
400
+ * satisfaction ends with a single stack element, we know that for a full script:
401
+ * - netdiff+1 is the maximal initial stack size (relevant for P2WSH stack limits).
402
+ * - exec+1 is the maximal stack size reached during execution (relevant for P2TR stack limits).
403
+ *
404
+ * Mathematically, SatInfo forms a semiring:
405
+ * - operator| is the semiring addition operator, with identity SatInfo{}, and which is commutative
406
+ * and associative.
407
+ * - operator+ is the semiring multiplication operator, with identity SatInfo{0}, and which is
408
+ * associative.
409
+ * - operator+ is distributive over operator|, so (a + (b | c)) = (a+b | a+c). This means we do not
410
+ * need to actually materialize all possible full execution traces over the whole script (which
411
+ * may be exponential in the length of the script); instead we can use the union operation at the
412
+ * individual subexpression level, and concatenate the result with subexpressions before and
413
+ * after it.
414
+ * - It is not a commutative semiring, because a+b can differ from b+a. For example, "OP_1 OP_DROP"
415
+ * has exec=1, while "OP_DROP OP_1" has exec=0.
416
+ */
417
+ struct SatInfo {
418
+ // ! Whether a canonical satisfaction/dissatisfaction is possible at all.
419
+ const bool valid;
420
+ // ! How much higher the stack size at start of execution can be compared to at the end.
421
+ const int32_t netdiff;
422
+ // ! Mow much higher the stack size can be during execution compared to at the end.
423
+ const int32_t exec;
424
+
425
+ /* * Empty script set. */
426
+ constexpr SatInfo () noexcept : valid(false ), netdiff(0 ), exec(0 ) {}
427
+
428
+ /* * Script set with a single script in it, with specified netdiff and exec. */
429
+ constexpr SatInfo (int32_t in_netdiff, int32_t in_exec) noexcept :
430
+ valid{true }, netdiff{in_netdiff}, exec{in_exec} {}
431
+
432
+ /* * Script set union. */
433
+ constexpr friend SatInfo operator |(const SatInfo& a, const SatInfo& b) noexcept
434
+ {
435
+ // Union with an empty set is itself.
436
+ if (!a.valid ) return b;
437
+ if (!b.valid ) return a;
438
+ // Otherwise the netdiff and exec of the union is the maximum of the individual values.
439
+ return {std::max (a.netdiff , b.netdiff ), std::max (a.exec , b.exec )};
440
+ }
441
+
442
+ /* * Script set concatenation. */
443
+ constexpr friend SatInfo operator +(const SatInfo& a, const SatInfo& b) noexcept
444
+ {
445
+ // Concatenation with an empty set yields an empty set.
446
+ if (!a.valid || !b.valid ) return {};
447
+ // Otherwise, the maximum stack size difference for the combined scripts is the sum of the
448
+ // netdiffs, and the maximum stack size difference anywhere is either b.exec (if the
449
+ // maximum occurred in b) or b.netdiff+a.exec (if the maximum occurred in a).
450
+ return {a.netdiff + b.netdiff , std::max (b.exec , b.netdiff + a.exec )};
451
+ }
452
+
453
+ /* * The empty script. */
454
+ static constexpr SatInfo Empty () noexcept { return {0 , 0 }; }
455
+ /* * A script consisting of a single push opcode. */
456
+ static constexpr SatInfo Push () noexcept { return {-1 , 0 }; }
457
+ /* * A script consisting of a single hash opcode. */
458
+ static constexpr SatInfo Hash () noexcept { return {0 , 0 }; }
459
+ /* * A script consisting of just a repurposed nop (OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY). */
460
+ static constexpr SatInfo Nop () noexcept { return {0 , 0 }; }
461
+ /* * A script consisting of just OP_IF or OP_NOTIF. Note that OP_ELSE and OP_ENDIF have no stack effect. */
462
+ static constexpr SatInfo If () noexcept { return {1 , 1 }; }
463
+ /* * A script consisting of just a binary operator (OP_BOOLAND, OP_BOOLOR, OP_ADD). */
464
+ static constexpr SatInfo BinaryOp () noexcept { return {1 , 1 }; }
465
+
466
+ // Scripts for specific individual opcodes.
467
+ static constexpr SatInfo OP_DUP () noexcept { return {-1 , 0 }; }
468
+ static constexpr SatInfo OP_IFDUP (bool nonzero) noexcept { return {nonzero ? -1 : 0 , 0 }; }
469
+ static constexpr SatInfo OP_EQUALVERIFY () noexcept { return {2 , 2 }; }
470
+ static constexpr SatInfo OP_EQUAL () noexcept { return {1 , 1 }; }
471
+ static constexpr SatInfo OP_SIZE () noexcept { return {-1 , 0 }; }
472
+ static constexpr SatInfo OP_CHECKSIG () noexcept { return {1 , 1 }; }
473
+ static constexpr SatInfo OP_0NOTEQUAL () noexcept { return {0 , 0 }; }
474
+ static constexpr SatInfo OP_VERIFY () noexcept { return {1 , 1 }; }
475
+ };
476
+
376
477
struct StackSize {
377
- // ! Maximum stack size to satisfy;
378
- MaxInt<uint32_t > sat;
379
- // ! Maximum stack size to dissatisfy;
380
- MaxInt<uint32_t > dsat;
478
+ const SatInfo sat, dsat;
381
479
382
- StackSize (MaxInt<uint32_t > in_sat, MaxInt<uint32_t > in_dsat) : sat(in_sat), dsat(in_dsat) {};
480
+ constexpr StackSize (SatInfo in_sat, SatInfo in_dsat) noexcept : sat(in_sat), dsat(in_dsat) {};
481
+ constexpr StackSize (SatInfo in_both) noexcept : sat(in_both), dsat(in_both) {};
383
482
};
384
483
385
484
struct WitnessSize {
@@ -878,51 +977,115 @@ struct Node {
878
977
}
879
978
880
979
internal::StackSize CalcStackSize () const {
980
+ using namespace internal ;
881
981
switch (fragment) {
882
- case Fragment::JUST_0: return {{}, 0 };
883
- case Fragment::JUST_1:
982
+ case Fragment::JUST_0: return {{}, SatInfo::Push () };
983
+ case Fragment::JUST_1: return { SatInfo::Push (), {}};
884
984
case Fragment::OLDER:
885
- case Fragment::AFTER: return {0 , {}};
886
- case Fragment::PK_K: return {0 , 0 };
887
- case Fragment::PK_H: return {1 , 1 };
985
+ case Fragment::AFTER: return {SatInfo::Push () + SatInfo::Nop () , {}};
986
+ case Fragment::PK_K: return {SatInfo::Push () };
987
+ case Fragment::PK_H: return {SatInfo::OP_DUP () + SatInfo::Hash () + SatInfo::Push () + SatInfo::OP_EQUALVERIFY () };
888
988
case Fragment::SHA256:
889
989
case Fragment::RIPEMD160:
890
990
case Fragment::HASH256:
891
- case Fragment::HASH160: return {1 , {}};
991
+ case Fragment::HASH160: return {
992
+ SatInfo::OP_SIZE () + SatInfo::Push () + SatInfo::OP_EQUALVERIFY () + SatInfo::Hash () + SatInfo::Push () + SatInfo::OP_EQUAL (),
993
+ {}
994
+ };
892
995
case Fragment::ANDOR: {
893
- const auto sat{(subs[0 ]->ss .sat + subs[1 ]->ss .sat ) | (subs[0 ]->ss .dsat + subs[2 ]->ss .sat )};
894
- const auto dsat{subs[0 ]->ss .dsat + subs[2 ]->ss .dsat };
895
- return {sat, dsat};
996
+ const auto & x{subs[0 ]->ss };
997
+ const auto & y{subs[1 ]->ss };
998
+ const auto & z{subs[2 ]->ss };
999
+ return {
1000
+ (x.sat + SatInfo::If () + y.sat ) | (x.dsat + SatInfo::If () + z.sat ),
1001
+ x.dsat + SatInfo::If () + z.dsat
1002
+ };
1003
+ }
1004
+ case Fragment::AND_V: {
1005
+ const auto & x{subs[0 ]->ss };
1006
+ const auto & y{subs[1 ]->ss };
1007
+ return {x.sat + y.sat , {}};
1008
+ }
1009
+ case Fragment::AND_B: {
1010
+ const auto & x{subs[0 ]->ss };
1011
+ const auto & y{subs[1 ]->ss };
1012
+ return {x.sat + y.sat + SatInfo::BinaryOp (), x.dsat + y.dsat + SatInfo::BinaryOp ()};
896
1013
}
897
- case Fragment::AND_V: return {subs[0 ]->ss .sat + subs[1 ]->ss .sat , {}};
898
- case Fragment::AND_B: return {subs[0 ]->ss .sat + subs[1 ]->ss .sat , subs[0 ]->ss .dsat + subs[1 ]->ss .dsat };
899
1014
case Fragment::OR_B: {
900
- const auto sat{(subs[0 ]->ss .dsat + subs[1 ]->ss .sat ) | (subs[0 ]->ss .sat + subs[1 ]->ss .dsat )};
901
- const auto dsat{subs[0 ]->ss .dsat + subs[1 ]->ss .dsat };
902
- return {sat, dsat};
1015
+ const auto & x{subs[0 ]->ss };
1016
+ const auto & y{subs[1 ]->ss };
1017
+ return {
1018
+ ((x.sat + y.dsat ) | (x.dsat + y.sat )) + SatInfo::BinaryOp (),
1019
+ x.dsat + y.dsat + SatInfo::BinaryOp ()
1020
+ };
1021
+ }
1022
+ case Fragment::OR_C: {
1023
+ const auto & x{subs[0 ]->ss };
1024
+ const auto & y{subs[1 ]->ss };
1025
+ return {(x.sat + SatInfo::If ()) | (x.dsat + SatInfo::If () + y.sat ), {}};
1026
+ }
1027
+ case Fragment::OR_D: {
1028
+ const auto & x{subs[0 ]->ss };
1029
+ const auto & y{subs[1 ]->ss };
1030
+ return {
1031
+ (x.sat + SatInfo::OP_IFDUP (true ) + SatInfo::If ()) | (x.dsat + SatInfo::OP_IFDUP (false ) + SatInfo::If () + y.sat ),
1032
+ x.dsat + SatInfo::OP_IFDUP (false ) + SatInfo::If () + y.dsat
1033
+ };
1034
+ }
1035
+ case Fragment::OR_I: {
1036
+ const auto & x{subs[0 ]->ss };
1037
+ const auto & y{subs[1 ]->ss };
1038
+ return {SatInfo::If () + (x.sat | y.sat ), SatInfo::If () + (x.dsat | y.dsat )};
903
1039
}
904
- case Fragment::OR_C: return {subs[0 ]->ss .sat | (subs[0 ]->ss .dsat + subs[1 ]->ss .sat ), {}};
905
- case Fragment::OR_D: return {subs[0 ]->ss .sat | (subs[0 ]->ss .dsat + subs[1 ]->ss .sat ), subs[0 ]->ss .dsat + subs[1 ]->ss .dsat };
906
- case Fragment::OR_I: return {(subs[0 ]->ss .sat + 1 ) | (subs[1 ]->ss .sat + 1 ), (subs[0 ]->ss .dsat + 1 ) | (subs[1 ]->ss .dsat + 1 )};
907
- case Fragment::MULTI: return {k + 1 , k + 1 };
908
- case Fragment::MULTI_A: return {keys.size (), keys.size ()};
1040
+ // multi(k, key1, key2, ..., key_n) starts off with k+1 stack elements (a 0, plus k
1041
+ // signatures), then reaches n+k+3 stack elements after pushing the n keys, plus k and
1042
+ // n itself, and ends with 1 stack element (success or failure). Thus, it net removes
1043
+ // k elements (from k+1 to 1), while reaching k+n+2 more than it ends with.
1044
+ case Fragment::MULTI: return {SatInfo (k, k + keys.size () + 2 )};
1045
+ // multi_a(k, key1, key2, ..., key_n) starts off with n stack elements (the
1046
+ // signatures), reaches 1 more (after the first key push), and ends with 1. Thus it net
1047
+ // removes n-1 elements (from n to 1) while reaching n more than it ends with.
1048
+ case Fragment::MULTI_A: return {SatInfo (keys.size () - 1 , keys.size ())};
909
1049
case Fragment::WRAP_A:
910
1050
case Fragment::WRAP_N:
911
1051
case Fragment::WRAP_S: return subs[0 ]->ss ;
912
- case Fragment::WRAP_C: return {subs[0 ]->ss .sat + 1 , subs[0 ]->ss .dsat + 1 };
913
- case Fragment::WRAP_D: return {1 + subs[0 ]->ss .sat , 1 };
914
- case Fragment::WRAP_V: return {subs[0 ]->ss .sat , {}};
915
- case Fragment::WRAP_J: return {subs[0 ]->ss .sat , 1 };
1052
+ case Fragment::WRAP_C: return {
1053
+ subs[0 ]->ss .sat + SatInfo::OP_CHECKSIG (),
1054
+ subs[0 ]->ss .dsat + SatInfo::OP_CHECKSIG ()
1055
+ };
1056
+ case Fragment::WRAP_D: return {
1057
+ SatInfo::OP_DUP () + SatInfo::If () + subs[0 ]->ss .sat ,
1058
+ SatInfo::OP_DUP () + SatInfo::If ()
1059
+ };
1060
+ case Fragment::WRAP_V: return {subs[0 ]->ss .sat + SatInfo::OP_VERIFY (), {}};
1061
+ case Fragment::WRAP_J: return {
1062
+ SatInfo::OP_SIZE () + SatInfo::OP_0NOTEQUAL () + SatInfo::If () + subs[0 ]->ss .sat ,
1063
+ SatInfo::OP_SIZE () + SatInfo::OP_0NOTEQUAL () + SatInfo::If ()
1064
+ };
916
1065
case Fragment::THRESH: {
917
- auto sats = Vector (internal::MaxInt<uint32_t >(0 ));
918
- for (const auto & sub : subs) {
919
- auto next_sats = Vector (sats[0 ] + sub->ss .dsat );
920
- for (size_t j = 1 ; j < sats.size (); ++j) next_sats.push_back ((sats[j] + sub->ss .dsat ) | (sats[j - 1 ] + sub->ss .sat ));
921
- next_sats.push_back (sats[sats.size () - 1 ] + sub->ss .sat );
1066
+ // sats[j] is the SatInfo corresponding to all traces reaching j satisfactions.
1067
+ auto sats = Vector (SatInfo::Empty ());
1068
+ for (size_t i = 0 ; i < subs.size (); ++i) {
1069
+ // Loop over the subexpressions, processing them one by one. After adding
1070
+ // element i we need to add OP_ADD (if i>0).
1071
+ auto add = i ? SatInfo::BinaryOp () : SatInfo::Empty ();
1072
+ // Construct a variable that will become the next sats, starting with index 0.
1073
+ auto next_sats = Vector (sats[0 ] + subs[i]->ss .dsat + add);
1074
+ // Then loop to construct next_sats[1..i].
1075
+ for (size_t j = 1 ; j < sats.size (); ++j) {
1076
+ next_sats.push_back (((sats[j] + subs[i]->ss .dsat ) | (sats[j - 1 ] + subs[i]->ss .sat )) + add);
1077
+ }
1078
+ // Finally construct next_sats[i+1].
1079
+ next_sats.push_back (sats[sats.size () - 1 ] + subs[i]->ss .sat + add);
1080
+ // Switch over.
922
1081
sats = std::move (next_sats);
923
1082
}
924
- assert (k <= sats.size ());
925
- return {sats[k], sats[0 ]};
1083
+ // To satisfy thresh we need k satisfactions; to dissatisfy we need 0. In both
1084
+ // cases a push of k and an OP_EQUAL follow.
1085
+ return {
1086
+ sats[k] + SatInfo::Push () + SatInfo::OP_EQUAL (),
1087
+ sats[0 ] + SatInfo::Push () + SatInfo::OP_EQUAL ()
1088
+ };
926
1089
}
927
1090
}
928
1091
assert (false );
@@ -1310,17 +1473,32 @@ struct Node {
1310
1473
return true ;
1311
1474
}
1312
1475
1476
+ /* * Whether this node is of type B, K or W. (That is, anything but V.) */
1477
+ bool IsBKW () const {
1478
+ return !((GetType () & " BKW" _mst) == " " _mst);
1479
+ }
1480
+
1313
1481
/* * Return the maximum number of stack elements needed to satisfy this script non-malleably.
1314
1482
* This does not account for the P2WSH script push. */
1315
1483
std::optional<uint32_t > GetStackSize () const {
1316
1484
if (!ss.sat .valid ) return {};
1317
- return ss.sat .value ;
1485
+ return ss.sat .netdiff + static_cast <int32_t >(IsBKW ());
1486
+ }
1487
+
1488
+ // ! Return the maximum size of the stack during execution of this script.
1489
+ std::optional<uint32_t > GetExecStackSize () const {
1490
+ if (!ss.sat .valid ) return {};
1491
+ return ss.sat .exec + static_cast <int32_t >(IsBKW ());
1318
1492
}
1319
1493
1320
1494
// ! Check the maximum stack size for this script against the policy limit.
1321
1495
bool CheckStackSize () const {
1322
- // TODO: MAX_STACK_SIZE during script execution under Tapscript.
1323
- if (IsTapscript (m_script_ctx)) return true ;
1496
+ // Since in Tapscript there is no standardness limit on the script and witness sizes, we may run
1497
+ // into the maximum stack size while executing the script. Make sure it doesn't happen.
1498
+ if (IsTapscript (m_script_ctx)) {
1499
+ if (const auto exec_ss = GetExecStackSize ()) return exec_ss <= MAX_STACK_SIZE;
1500
+ return true ;
1501
+ }
1324
1502
if (const auto ss = GetStackSize ()) return *ss <= MAX_STANDARD_P2WSH_STACK_ITEMS;
1325
1503
return true ;
1326
1504
}
0 commit comments