@@ -232,6 +232,47 @@ size_t ComputeScriptLen(Fragment nodetype, Type sub0typ, size_t subsize, uint32_
232
232
// ! A helper sanitizer/checker for the output of CalcType.
233
233
Type SanitizeType (Type x);
234
234
235
+ // ! Class whose objects represent the maximum of a list of integers.
236
+ template <typename I>
237
+ struct MaxInt {
238
+ const bool valid;
239
+ const I value;
240
+
241
+ MaxInt () : valid(false ), value(0 ) {}
242
+ MaxInt (I val) : valid(true ), value(val) {}
243
+
244
+ friend MaxInt<I> operator +(const MaxInt<I>& a, const MaxInt<I>& b) {
245
+ if (!a.valid || !b.valid ) return {};
246
+ return a.value + b.value ;
247
+ }
248
+
249
+ friend MaxInt<I> operator |(const MaxInt<I>& a, const MaxInt<I>& b) {
250
+ if (!a.valid ) return b;
251
+ if (!b.valid ) return a;
252
+ return std::max (a.value , b.value );
253
+ }
254
+ };
255
+
256
+ struct Ops {
257
+ // ! Non-push opcodes.
258
+ uint32_t count;
259
+ // ! Number of keys in possibly executed OP_CHECKMULTISIG(VERIFY)s to satisfy.
260
+ MaxInt<uint32_t > sat;
261
+ // ! Number of keys in possibly executed OP_CHECKMULTISIG(VERIFY)s to dissatisfy.
262
+ MaxInt<uint32_t > dsat;
263
+
264
+ Ops (uint32_t in_count, MaxInt<uint32_t > in_sat, MaxInt<uint32_t > in_dsat) : count(in_count), sat(in_sat), dsat(in_dsat) {};
265
+ };
266
+
267
+ struct StackSize {
268
+ // ! Maximum stack size to satisfy;
269
+ MaxInt<uint32_t > sat;
270
+ // ! Maximum stack size to dissatisfy;
271
+ MaxInt<uint32_t > dsat;
272
+
273
+ StackSize (MaxInt<uint32_t > in_sat, MaxInt<uint32_t > in_dsat) : sat(in_sat), dsat(in_dsat) {};
274
+ };
275
+
235
276
} // namespace internal
236
277
237
278
// ! A node in a miniscript expression.
@@ -249,6 +290,10 @@ struct Node {
249
290
const std::vector<NodeRef<Key>> subs;
250
291
251
292
private:
293
+ // ! Cached ops counts.
294
+ const internal::Ops ops;
295
+ // ! Cached stack size bounds.
296
+ const internal::StackSize ss;
252
297
// ! Cached expression type (computed by CalcType and fed through SanitizeType).
253
298
const Type typ;
254
299
// ! Cached script length (computed by CalcScriptLen).
@@ -556,10 +601,148 @@ struct Node {
556
601
return res.has_value ();
557
602
}
558
603
604
+ internal::Ops CalcOps () const {
605
+ switch (nodetype) {
606
+ case Fragment::JUST_1: return {0 , 0 , {}};
607
+ case Fragment::JUST_0: return {0 , {}, 0 };
608
+ case Fragment::PK_K: return {0 , 0 , 0 };
609
+ case Fragment::PK_H: return {3 , 0 , 0 };
610
+ case Fragment::OLDER:
611
+ case Fragment::AFTER: return {1 , 0 , {}};
612
+ case Fragment::SHA256:
613
+ case Fragment::RIPEMD160:
614
+ case Fragment::HASH256:
615
+ case Fragment::HASH160: return {4 , 0 , {}};
616
+ case Fragment::AND_V: return {subs[0 ]->ops .count + subs[1 ]->ops .count , subs[0 ]->ops .sat + subs[1 ]->ops .sat , {}};
617
+ case Fragment::AND_B: {
618
+ const auto count{1 + subs[0 ]->ops .count + subs[1 ]->ops .count };
619
+ const auto sat{subs[0 ]->ops .sat + subs[1 ]->ops .sat };
620
+ const auto dsat{subs[0 ]->ops .dsat + subs[1 ]->ops .dsat };
621
+ return {count, sat, dsat};
622
+ }
623
+ case Fragment::OR_B: {
624
+ const auto count{1 + subs[0 ]->ops .count + subs[1 ]->ops .count };
625
+ const auto sat{(subs[0 ]->ops .sat + subs[1 ]->ops .dsat ) | (subs[1 ]->ops .sat + subs[0 ]->ops .dsat )};
626
+ const auto dsat{subs[0 ]->ops .dsat + subs[1 ]->ops .dsat };
627
+ return {count, sat, dsat};
628
+ }
629
+ case Fragment::OR_D: {
630
+ const auto count{3 + subs[0 ]->ops .count + subs[1 ]->ops .count };
631
+ const auto sat{subs[0 ]->ops .sat | (subs[1 ]->ops .sat + subs[0 ]->ops .dsat )};
632
+ const auto dsat{subs[0 ]->ops .dsat + subs[1 ]->ops .dsat };
633
+ return {count, sat, dsat};
634
+ }
635
+ case Fragment::OR_C: {
636
+ const auto count{2 + subs[0 ]->ops .count + subs[1 ]->ops .count };
637
+ const auto sat{subs[0 ]->ops .sat | (subs[1 ]->ops .sat + subs[0 ]->ops .dsat )};
638
+ return {count, sat, {}};
639
+ }
640
+ case Fragment::OR_I: {
641
+ const auto count{3 + subs[0 ]->ops .count + subs[1 ]->ops .count };
642
+ const auto sat{subs[0 ]->ops .sat | subs[1 ]->ops .sat };
643
+ const auto dsat{subs[0 ]->ops .dsat | subs[1 ]->ops .dsat };
644
+ return {count, sat, dsat};
645
+ }
646
+ case Fragment::ANDOR: {
647
+ const auto count{3 + subs[0 ]->ops .count + subs[1 ]->ops .count + subs[2 ]->ops .count };
648
+ const auto sat{(subs[1 ]->ops .sat + subs[0 ]->ops .sat ) | (subs[0 ]->ops .dsat + subs[2 ]->ops .sat )};
649
+ const auto dsat{subs[0 ]->ops .dsat + subs[2 ]->ops .dsat };
650
+ return {count, sat, dsat};
651
+ }
652
+ case Fragment::MULTI: return {1 , (uint32_t )keys.size (), (uint32_t )keys.size ()};
653
+ case Fragment::WRAP_S:
654
+ case Fragment::WRAP_C:
655
+ case Fragment::WRAP_N: return {1 + subs[0 ]->ops .count , subs[0 ]->ops .sat , subs[0 ]->ops .dsat };
656
+ case Fragment::WRAP_A: return {2 + subs[0 ]->ops .count , subs[0 ]->ops .sat , subs[0 ]->ops .dsat };
657
+ case Fragment::WRAP_D: return {3 + subs[0 ]->ops .count , subs[0 ]->ops .sat , 0 };
658
+ case Fragment::WRAP_J: return {4 + subs[0 ]->ops .count , subs[0 ]->ops .sat , 0 };
659
+ case Fragment::WRAP_V: return {subs[0 ]->ops .count + (subs[0 ]->GetType () << " x" _mst), subs[0 ]->ops .sat , {}};
660
+ case Fragment::THRESH: {
661
+ uint32_t count = 0 ;
662
+ auto sats = Vector (internal::MaxInt<uint32_t >(0 ));
663
+ for (const auto & sub : subs) {
664
+ count += sub->ops .count + 1 ;
665
+ auto next_sats = Vector (sats[0 ] + sub->ops .dsat );
666
+ for (size_t j = 1 ; j < sats.size (); ++j) next_sats.push_back ((sats[j] + sub->ops .dsat ) | (sats[j - 1 ] + sub->ops .sat ));
667
+ next_sats.push_back (sats[sats.size () - 1 ] + sub->ops .sat );
668
+ sats = std::move (next_sats);
669
+ }
670
+ assert (k <= sats.size ());
671
+ return {count, sats[k], sats[0 ]};
672
+ }
673
+ }
674
+ assert (false );
675
+ return {0 , {}, {}};
676
+ }
677
+
678
+ internal::StackSize CalcStackSize () const {
679
+ switch (nodetype) {
680
+ case Fragment::JUST_0: return {{}, 0 };
681
+ case Fragment::JUST_1:
682
+ case Fragment::OLDER:
683
+ case Fragment::AFTER: return {0 , {}};
684
+ case Fragment::PK_K: return {1 , 1 };
685
+ case Fragment::PK_H: return {2 , 2 };
686
+ case Fragment::SHA256:
687
+ case Fragment::RIPEMD160:
688
+ case Fragment::HASH256:
689
+ case Fragment::HASH160: return {1 , {}};
690
+ case Fragment::ANDOR: {
691
+ const auto sat{(subs[0 ]->ss .sat + subs[1 ]->ss .sat ) | (subs[0 ]->ss .dsat + subs[2 ]->ss .sat )};
692
+ const auto dsat{subs[0 ]->ss .dsat + subs[2 ]->ss .dsat };
693
+ return {sat, dsat};
694
+ }
695
+ case Fragment::AND_V: return {subs[0 ]->ss .sat + subs[1 ]->ss .sat , {}};
696
+ case Fragment::AND_B: return {subs[0 ]->ss .sat + subs[1 ]->ss .sat , subs[0 ]->ss .dsat + subs[1 ]->ss .dsat };
697
+ case Fragment::OR_B: {
698
+ const auto sat{(subs[0 ]->ss .dsat + subs[1 ]->ss .sat ) | (subs[0 ]->ss .sat + subs[1 ]->ss .dsat )};
699
+ const auto dsat{subs[0 ]->ss .dsat + subs[1 ]->ss .dsat };
700
+ return {sat, dsat};
701
+ }
702
+ case Fragment::OR_C: return {subs[0 ]->ss .sat | (subs[0 ]->ss .dsat + subs[1 ]->ss .sat ), {}};
703
+ 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 };
704
+ 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 )};
705
+ case Fragment::MULTI: return {k + 1 , k + 1 };
706
+ case Fragment::WRAP_A:
707
+ case Fragment::WRAP_N:
708
+ case Fragment::WRAP_S:
709
+ case Fragment::WRAP_C: return subs[0 ]->ss ;
710
+ case Fragment::WRAP_D: return {1 + subs[0 ]->ss .sat , 1 };
711
+ case Fragment::WRAP_V: return {subs[0 ]->ss .sat , {}};
712
+ case Fragment::WRAP_J: return {subs[0 ]->ss .sat , 1 };
713
+ case Fragment::THRESH: {
714
+ auto sats = Vector (internal::MaxInt<uint32_t >(0 ));
715
+ for (const auto & sub : subs) {
716
+ auto next_sats = Vector (sats[0 ] + sub->ss .dsat );
717
+ for (size_t j = 1 ; j < sats.size (); ++j) next_sats.push_back ((sats[j] + sub->ss .dsat ) | (sats[j - 1 ] + sub->ss .sat ));
718
+ next_sats.push_back (sats[sats.size () - 1 ] + sub->ss .sat );
719
+ sats = std::move (next_sats);
720
+ }
721
+ assert (k <= sats.size ());
722
+ return {sats[k], sats[0 ]};
723
+ }
724
+ }
725
+ assert (false );
726
+ return {{}, {}};
727
+ }
728
+
559
729
public:
560
730
// ! Return the size of the script for this expression (faster than ToScript().size()).
561
731
size_t ScriptSize () const { return scriptlen; }
562
732
733
+ // ! Return the maximum number of ops needed to satisfy this script non-malleably.
734
+ uint32_t GetOps () const { return ops.count + ops.sat .value ; }
735
+
736
+ // ! Check the ops limit of this script against the consensus limit.
737
+ bool CheckOpsLimit () const { return GetOps () <= MAX_OPS_PER_SCRIPT; }
738
+
739
+ /* * Return the maximum number of stack elements needed to satisfy this script non-malleably, including
740
+ * the script push. */
741
+ uint32_t GetStackSize () const { return ss.sat .value + 1 ; }
742
+
743
+ // ! Check the maximum stack size for this script against the policy limit.
744
+ bool CheckStackSize () const { return GetStackSize () - 1 <= MAX_STANDARD_P2WSH_STACK_ITEMS; }
745
+
563
746
// ! Return the expression type.
564
747
Type GetType () const { return typ; }
565
748
@@ -576,7 +759,7 @@ struct Node {
576
759
bool NeedsSignature () const { return GetType () << " s" _mst; }
577
760
578
761
// ! Do all sanity checks.
579
- bool IsSane () const { return IsValid () && GetType () << " mk" _mst; }
762
+ bool IsSane () const { return IsValid () && GetType () << " mk" _mst && CheckOpsLimit () && CheckStackSize () ; }
580
763
581
764
// ! Check whether this node is safe as a script on its own.
582
765
bool IsSaneTopLevel () const { return IsValidTopLevel () && IsSane () && NeedsSignature (); }
@@ -598,12 +781,12 @@ struct Node {
598
781
}
599
782
600
783
// Constructors with various argument combinations.
601
- Node (Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char > arg, uint32_t val = 0 ) : nodetype(nt), k(val), data(std::move(arg)), subs(std::move(sub)), typ(CalcType()), scriptlen(CalcScriptLen()) {}
602
- Node (Fragment nt, std::vector<unsigned char > arg, uint32_t val = 0 ) : nodetype(nt), k(val), data(std::move(arg)), typ(CalcType()), scriptlen(CalcScriptLen()) {}
603
- Node (Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0 ) : nodetype(nt), k(val), keys(std::move(key)), subs(std::move(sub)), typ(CalcType()), scriptlen(CalcScriptLen()) {}
604
- Node (Fragment nt, std::vector<Key> key, uint32_t val = 0 ) : nodetype(nt), k(val), keys(std::move(key)), typ(CalcType()), scriptlen(CalcScriptLen()) {}
605
- Node (Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0 ) : nodetype(nt), k(val), subs(std::move(sub)), typ(CalcType()), scriptlen(CalcScriptLen()) {}
606
- Node (Fragment nt, uint32_t val = 0 ) : nodetype(nt), k(val), typ(CalcType()), scriptlen(CalcScriptLen()) {}
784
+ Node (Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char > arg, uint32_t val = 0 ) : nodetype(nt), k(val), data(std::move(arg)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
785
+ Node (Fragment nt, std::vector<unsigned char > arg, uint32_t val = 0 ) : nodetype(nt), k(val), data(std::move(arg)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
786
+ Node (Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0 ) : nodetype(nt), k(val), keys(std::move(key)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
787
+ Node (Fragment nt, std::vector<Key> key, uint32_t val = 0 ) : nodetype(nt), k(val), keys(std::move(key)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
788
+ Node (Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0 ) : nodetype(nt), k(val), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
789
+ Node (Fragment nt, uint32_t val = 0 ) : nodetype(nt), k(val), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}
607
790
};
608
791
609
792
namespace internal {
0 commit comments