@@ -232,6 +232,47 @@ size_t ComputeScriptLen(Fragment nodetype, Type sub0typ, size_t subsize, uint32_
232232// ! A helper sanitizer/checker for the output of CalcType.
233233Type SanitizeType (Type x);
234234
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+
235276} // namespace internal
236277
237278// ! A node in a miniscript expression.
@@ -249,6 +290,10 @@ struct Node {
249290 const std::vector<NodeRef<Key>> subs;
250291
251292private:
293+ // ! Cached ops counts.
294+ const internal::Ops ops;
295+ // ! Cached stack size bounds.
296+ const internal::StackSize ss;
252297 // ! Cached expression type (computed by CalcType and fed through SanitizeType).
253298 const Type typ;
254299 // ! Cached script length (computed by CalcScriptLen).
@@ -556,10 +601,148 @@ struct Node {
556601 return res.has_value ();
557602 }
558603
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+
559729public:
560730 // ! Return the size of the script for this expression (faster than ToScript().size()).
561731 size_t ScriptSize () const { return scriptlen; }
562732
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+
563746 // ! Return the expression type.
564747 Type GetType () const { return typ; }
565748
@@ -576,7 +759,7 @@ struct Node {
576759 bool NeedsSignature () const { return GetType () << " s" _mst; }
577760
578761 // ! Do all sanity checks.
579- bool IsSane () const { return IsValid () && GetType () << " mk" _mst; }
762+ bool IsSane () const { return IsValid () && GetType () << " mk" _mst && CheckOpsLimit () && CheckStackSize () ; }
580763
581764 // ! Check whether this node is safe as a script on its own.
582765 bool IsSaneTopLevel () const { return IsValidTopLevel () && IsSane () && NeedsSignature (); }
@@ -598,12 +781,12 @@ struct Node {
598781 }
599782
600783 // 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()) {}
607790};
608791
609792namespace internal {
0 commit comments