/ src / test / miniscript_tests.cpp
miniscript_tests.cpp
  1  // Copyright (c) 2019-2022 The Bitcoin Core developers
  2  // Distributed under the MIT software license, see the accompanying
  3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4  
  5  #include <stdint.h>
  6  #include <string>
  7  #include <vector>
  8  
  9  #include <test/util/random.h>
 10  #include <test/util/setup_common.h>
 11  #include <boost/test/unit_test.hpp>
 12  
 13  #include <addresstype.h>
 14  #include <core_io.h>
 15  #include <hash.h>
 16  #include <pubkey.h>
 17  #include <uint256.h>
 18  #include <crypto/ripemd160.h>
 19  #include <crypto/sha256.h>
 20  #include <script/interpreter.h>
 21  #include <script/miniscript.h>
 22  #include <script/script_error.h>
 23  #include <script/signingprovider.h>
 24  
 25  namespace {
 26  
 27  /** TestData groups various kinds of precomputed data necessary in this test. */
 28  struct TestData {
 29      //! The only public keys used in this test.
 30      std::vector<CPubKey> pubkeys;
 31      //! A map from the public keys to their CKeyIDs (faster than hashing every time).
 32      std::map<CPubKey, CKeyID> pkhashes;
 33      std::map<CKeyID, CPubKey> pkmap;
 34      std::map<XOnlyPubKey, CKeyID> xonly_pkhashes;
 35      std::map<CPubKey, std::vector<unsigned char>> signatures;
 36      std::map<XOnlyPubKey, std::vector<unsigned char>> schnorr_signatures;
 37  
 38      // Various precomputed hashes
 39      std::vector<std::vector<unsigned char>> sha256;
 40      std::vector<std::vector<unsigned char>> ripemd160;
 41      std::vector<std::vector<unsigned char>> hash256;
 42      std::vector<std::vector<unsigned char>> hash160;
 43      std::map<std::vector<unsigned char>, std::vector<unsigned char>> sha256_preimages;
 44      std::map<std::vector<unsigned char>, std::vector<unsigned char>> ripemd160_preimages;
 45      std::map<std::vector<unsigned char>, std::vector<unsigned char>> hash256_preimages;
 46      std::map<std::vector<unsigned char>, std::vector<unsigned char>> hash160_preimages;
 47  
 48      TestData()
 49      {
 50          // All our signatures sign (and are required to sign) this constant message.
 51          auto const MESSAGE_HASH = uint256S("f5cd94e18b6fe77dd7aca9e35c2b0c9cbd86356c80a71065");
 52          // We don't pass additional randomness when creating a schnorr signature.
 53          auto const EMPTY_AUX{uint256S("")};
 54  
 55          // We generate 255 public keys and 255 hashes of each type.
 56          for (int i = 1; i <= 255; ++i) {
 57              // This 32-byte array functions as both private key data and hash preimage (31 zero bytes plus any nonzero byte).
 58              unsigned char keydata[32] = {0};
 59              keydata[31] = i;
 60  
 61              // Compute CPubkey and CKeyID
 62              CKey key;
 63              key.Set(keydata, keydata + 32, true);
 64              CPubKey pubkey = key.GetPubKey();
 65              CKeyID keyid = pubkey.GetID();
 66              pubkeys.push_back(pubkey);
 67              pkhashes.emplace(pubkey, keyid);
 68              pkmap.emplace(keyid, pubkey);
 69              XOnlyPubKey xonly_pubkey{pubkey};
 70              uint160 xonly_hash{Hash160(xonly_pubkey)};
 71              xonly_pkhashes.emplace(xonly_pubkey, xonly_hash);
 72              pkmap.emplace(xonly_hash, pubkey);
 73  
 74              // Compute ECDSA signatures on MESSAGE_HASH with the private keys.
 75              std::vector<unsigned char> sig, schnorr_sig(64);
 76              BOOST_CHECK(key.Sign(MESSAGE_HASH, sig));
 77              sig.push_back(1); // sighash byte
 78              signatures.emplace(pubkey, sig);
 79              BOOST_CHECK(key.SignSchnorr(MESSAGE_HASH, schnorr_sig, nullptr, EMPTY_AUX));
 80              schnorr_sig.push_back(1); // Maximally sized Schnorr sigs have a sighash byte.
 81              schnorr_signatures.emplace(XOnlyPubKey{pubkey}, schnorr_sig);
 82  
 83              // Compute various hashes
 84              std::vector<unsigned char> hash;
 85              hash.resize(32);
 86              CSHA256().Write(keydata, 32).Finalize(hash.data());
 87              sha256.push_back(hash);
 88              sha256_preimages[hash] = std::vector<unsigned char>(keydata, keydata + 32);
 89              CHash256().Write(keydata).Finalize(hash);
 90              hash256.push_back(hash);
 91              hash256_preimages[hash] = std::vector<unsigned char>(keydata, keydata + 32);
 92              hash.resize(20);
 93              CRIPEMD160().Write(keydata, 32).Finalize(hash.data());
 94              ripemd160.push_back(hash);
 95              ripemd160_preimages[hash] = std::vector<unsigned char>(keydata, keydata + 32);
 96              CHash160().Write(keydata).Finalize(hash);
 97              hash160.push_back(hash);
 98              hash160_preimages[hash] = std::vector<unsigned char>(keydata, keydata + 32);
 99          }
100      }
101  };
102  
103  //! Global TestData object
104  std::unique_ptr<const TestData> g_testdata;
105  
106  //! A classification of leaf conditions in miniscripts (excluding true/false).
107  enum class ChallengeType {
108      SHA256,
109      RIPEMD160,
110      HASH256,
111      HASH160,
112      OLDER,
113      AFTER,
114      PK
115  };
116  
117  /* With each leaf condition we associate a challenge number.
118   * For hashes it's just the first 4 bytes of the hash. For pubkeys, it's the last 4 bytes.
119   */
120  uint32_t ChallengeNumber(const CPubKey& pubkey) { return ReadLE32(pubkey.data() + 29); }
121  uint32_t ChallengeNumber(const std::vector<unsigned char>& hash) { return ReadLE32(hash.data()); }
122  
123  //! A Challenge is a combination of type of leaf condition and its challenge number.
124  typedef std::pair<ChallengeType, uint32_t> Challenge;
125  
126  /** A class encapulating conversion routing for CPubKey. */
127  struct KeyConverter {
128      typedef CPubKey Key;
129  
130      const miniscript::MiniscriptContext m_script_ctx;
131  
132      constexpr KeyConverter(miniscript::MiniscriptContext ctx) noexcept : m_script_ctx{ctx} {}
133  
134      bool KeyCompare(const Key& a, const Key& b) const {
135          return a < b;
136      }
137  
138      //! Convert a public key to bytes.
139      std::vector<unsigned char> ToPKBytes(const CPubKey& key) const {
140          if (!miniscript::IsTapscript(m_script_ctx)) {
141              return {key.begin(), key.end()};
142          }
143          const XOnlyPubKey xonly_pubkey{key};
144          return {xonly_pubkey.begin(), xonly_pubkey.end()};
145      }
146  
147      //! Convert a public key to its Hash160 bytes (precomputed).
148      std::vector<unsigned char> ToPKHBytes(const CPubKey& key) const {
149          if (!miniscript::IsTapscript(m_script_ctx)) {
150              auto hash = g_testdata->pkhashes.at(key);
151              return {hash.begin(), hash.end()};
152          }
153          const XOnlyPubKey xonly_key{key};
154          auto hash = g_testdata->xonly_pkhashes.at(xonly_key);
155          return {hash.begin(), hash.end()};
156      }
157  
158      //! Parse a public key from a range of hex characters.
159      template<typename I>
160      std::optional<Key> FromString(I first, I last) const {
161          auto bytes = ParseHex(std::string(first, last));
162          Key key{bytes.begin(), bytes.end()};
163          if (key.IsValid()) return key;
164          return {};
165      }
166  
167      template<typename I>
168      std::optional<Key> FromPKBytes(I first, I last) const {
169          if (!miniscript::IsTapscript(m_script_ctx)) {
170              Key key{first, last};
171              if (key.IsValid()) return key;
172              return {};
173          }
174          if (last - first != 32) return {};
175          XOnlyPubKey xonly_pubkey;
176          std::copy(first, last, xonly_pubkey.begin());
177          return xonly_pubkey.GetEvenCorrespondingCPubKey();
178      }
179  
180      template<typename I>
181      std::optional<Key> FromPKHBytes(I first, I last) const {
182          assert(last - first == 20);
183          CKeyID keyid;
184          std::copy(first, last, keyid.begin());
185          return g_testdata->pkmap.at(keyid);
186      }
187  
188      std::optional<std::string> ToString(const Key& key) const {
189          return HexStr(ToPKBytes(key));
190      }
191  
192      miniscript::MiniscriptContext MsContext() const {
193          return m_script_ctx;
194      }
195  };
196  
197  /** A class that encapsulates all signing/hash revealing operations. */
198  struct Satisfier : public KeyConverter {
199  
200      Satisfier(miniscript::MiniscriptContext ctx) noexcept : KeyConverter{ctx} {}
201  
202      //! Which keys/timelocks/hash preimages are available.
203      std::set<Challenge> supported;
204  
205      //! Implement simplified CLTV logic: stack value must exactly match an entry in `supported`.
206      bool CheckAfter(uint32_t value) const {
207          return supported.count(Challenge(ChallengeType::AFTER, value));
208      }
209  
210      //! Implement simplified CSV logic: stack value must exactly match an entry in `supported`.
211      bool CheckOlder(uint32_t value) const {
212          return supported.count(Challenge(ChallengeType::OLDER, value));
213      }
214  
215      //! Produce a signature for the given key.
216      miniscript::Availability Sign(const CPubKey& key, std::vector<unsigned char>& sig) const {
217          if (supported.count(Challenge(ChallengeType::PK, ChallengeNumber(key)))) {
218              if (!miniscript::IsTapscript(m_script_ctx)) {
219                  auto it = g_testdata->signatures.find(key);
220                  if (it == g_testdata->signatures.end()) return miniscript::Availability::NO;
221                  sig = it->second;
222              } else {
223                  auto it = g_testdata->schnorr_signatures.find(XOnlyPubKey{key});
224                  if (it == g_testdata->schnorr_signatures.end()) return miniscript::Availability::NO;
225                  sig = it->second;
226              }
227              return miniscript::Availability::YES;
228          }
229          return miniscript::Availability::NO;
230      }
231  
232      //! Helper function for the various hash based satisfactions.
233      miniscript::Availability SatHash(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage, ChallengeType chtype) const {
234          if (!supported.count(Challenge(chtype, ChallengeNumber(hash)))) return miniscript::Availability::NO;
235          const auto& m =
236              chtype == ChallengeType::SHA256 ? g_testdata->sha256_preimages :
237              chtype == ChallengeType::HASH256 ? g_testdata->hash256_preimages :
238              chtype == ChallengeType::RIPEMD160 ? g_testdata->ripemd160_preimages :
239              g_testdata->hash160_preimages;
240          auto it = m.find(hash);
241          if (it == m.end()) return miniscript::Availability::NO;
242          preimage = it->second;
243          return miniscript::Availability::YES;
244      }
245  
246      // Functions that produce the preimage for hashes of various types.
247      miniscript::Availability SatSHA256(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { return SatHash(hash, preimage, ChallengeType::SHA256); }
248      miniscript::Availability SatRIPEMD160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { return SatHash(hash, preimage, ChallengeType::RIPEMD160); }
249      miniscript::Availability SatHASH256(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { return SatHash(hash, preimage, ChallengeType::HASH256); }
250      miniscript::Availability SatHASH160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const { return SatHash(hash, preimage, ChallengeType::HASH160); }
251  };
252  
253  /** Mocking signature/timelock checker.
254   *
255   * It holds a pointer to a Satisfier object, to determine which timelocks are supposed to be available.
256   */
257  class TestSignatureChecker : public BaseSignatureChecker {
258      const Satisfier& ctx;
259  
260  public:
261      TestSignatureChecker(const Satisfier& in_ctx LIFETIMEBOUND) : ctx(in_ctx) {}
262  
263      bool CheckECDSASignature(const std::vector<unsigned char>& sig, const std::vector<unsigned char>& pubkey, const CScript& scriptcode, SigVersion sigversion) const override {
264          CPubKey pk(pubkey);
265          if (!pk.IsValid()) return false;
266          // Instead of actually running signature validation, check if the signature matches the precomputed one for this key.
267          auto it = g_testdata->signatures.find(pk);
268          if (it == g_testdata->signatures.end()) return false;
269          return sig == it->second;
270      }
271  
272      bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion,
273                                 ScriptExecutionData&, ScriptError*) const override {
274          XOnlyPubKey pk{pubkey};
275          auto it = g_testdata->schnorr_signatures.find(pk);
276          if (it == g_testdata->schnorr_signatures.end()) return false;
277          return sig == it->second;
278      }
279  
280      bool CheckLockTime(const CScriptNum& locktime) const override {
281          // Delegate to Satisfier.
282          return ctx.CheckAfter(locktime.GetInt64());
283      }
284  
285      bool CheckSequence(const CScriptNum& sequence) const override {
286          // Delegate to Satisfier.
287          return ctx.CheckOlder(sequence.GetInt64());
288      }
289  };
290  
291  //! Public key to be used as internal key for dummy Taproot spends.
292  const std::vector<unsigned char> NUMS_PK{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")};
293  
294  using Fragment = miniscript::Fragment;
295  using NodeRef = miniscript::NodeRef<CPubKey>;
296  using miniscript::operator"" _mst;
297  using Node = miniscript::Node<CPubKey>;
298  
299  /** Compute all challenges (pubkeys, hashes, timelocks) that occur in a given Miniscript. */
300  std::set<Challenge> FindChallenges(const NodeRef& ref) {
301      std::set<Challenge> chal;
302      for (const auto& key : ref->keys) {
303          chal.emplace(ChallengeType::PK, ChallengeNumber(key));
304      }
305      if (ref->fragment == miniscript::Fragment::OLDER) {
306          chal.emplace(ChallengeType::OLDER, ref->k);
307      } else if (ref->fragment == miniscript::Fragment::AFTER) {
308          chal.emplace(ChallengeType::AFTER, ref->k);
309      } else if (ref->fragment == miniscript::Fragment::SHA256) {
310          chal.emplace(ChallengeType::SHA256, ChallengeNumber(ref->data));
311      } else if (ref->fragment == miniscript::Fragment::RIPEMD160) {
312          chal.emplace(ChallengeType::RIPEMD160, ChallengeNumber(ref->data));
313      } else if (ref->fragment == miniscript::Fragment::HASH256) {
314          chal.emplace(ChallengeType::HASH256, ChallengeNumber(ref->data));
315      } else if (ref->fragment == miniscript::Fragment::HASH160) {
316          chal.emplace(ChallengeType::HASH160, ChallengeNumber(ref->data));
317      }
318      for (const auto& sub : ref->subs) {
319          auto sub_chal = FindChallenges(sub);
320          chal.insert(sub_chal.begin(), sub_chal.end());
321      }
322      return chal;
323  }
324  
325  //! The spk for this script under the given context. If it's a Taproot output also record the spend data.
326  CScript ScriptPubKey(miniscript::MiniscriptContext ctx, const CScript& script, TaprootBuilder& builder)
327  {
328      if (!miniscript::IsTapscript(ctx)) return CScript() << OP_0 << WitnessV0ScriptHash(script);
329  
330      // For Taproot outputs we always use a tree with a single script and a dummy internal key.
331      builder.Add(0, script, TAPROOT_LEAF_TAPSCRIPT);
332      builder.Finalize(XOnlyPubKey{NUMS_PK});
333      return GetScriptForDestination(builder.GetOutput());
334  }
335  
336  //! Fill the witness with the data additional to the script satisfaction.
337  void SatisfactionToWitness(miniscript::MiniscriptContext ctx, CScriptWitness& witness, const CScript& script, TaprootBuilder& builder) {
338      // For P2WSH, it's only the witness script.
339      witness.stack.emplace_back(script.begin(), script.end());
340      if (!miniscript::IsTapscript(ctx)) return;
341      // For Tapscript we also need the control block.
342      witness.stack.push_back(*builder.GetSpendData().scripts.begin()->second.begin());
343  }
344  
345  /** Run random satisfaction tests. */
346  void TestSatisfy(const KeyConverter& converter, const std::string& testcase, const NodeRef& node) {
347      auto script = node->ToScript(converter);
348      auto challenges = FindChallenges(node); // Find all challenges in the generated miniscript.
349      std::vector<Challenge> challist(challenges.begin(), challenges.end());
350      for (int iter = 0; iter < 3; ++iter) {
351          Shuffle(challist.begin(), challist.end(), g_insecure_rand_ctx);
352          Satisfier satisfier(converter.MsContext());
353          TestSignatureChecker checker(satisfier);
354          bool prev_mal_success = false, prev_nonmal_success = false;
355          // Go over all challenges involved in this miniscript in random order.
356          for (int add = -1; add < (int)challist.size(); ++add) {
357              if (add >= 0) satisfier.supported.insert(challist[add]); // The first iteration does not add anything
358  
359              // Get the ScriptPubKey for this script, filling spend data if it's Taproot.
360              TaprootBuilder builder;
361              const CScript script_pubkey{ScriptPubKey(converter.MsContext(), script, builder)};
362  
363              // Run malleable satisfaction algorithm.
364              CScriptWitness witness_mal;
365              const bool mal_success = node->Satisfy(satisfier, witness_mal.stack, false) == miniscript::Availability::YES;
366              SatisfactionToWitness(converter.MsContext(), witness_mal, script, builder);
367  
368              // Run non-malleable satisfaction algorithm.
369              CScriptWitness witness_nonmal;
370              const bool nonmal_success = node->Satisfy(satisfier, witness_nonmal.stack, true) == miniscript::Availability::YES;
371              // Compute witness size (excluding script push, control block, and witness count encoding).
372              const size_t wit_size = GetSerializeSize(witness_nonmal.stack) - GetSizeOfCompactSize(witness_nonmal.stack.size());
373              SatisfactionToWitness(converter.MsContext(), witness_nonmal, script, builder);
374  
375              if (nonmal_success) {
376                  // Non-malleable satisfactions are bounded by the satisfaction size plus:
377                  // - For P2WSH spends, the witness script
378                  // - For Tapscript spends, both the witness script and the control block
379                  const size_t max_stack_size{*node->GetStackSize() + 1 + miniscript::IsTapscript(converter.MsContext())};
380                  BOOST_CHECK(witness_nonmal.stack.size() <= max_stack_size);
381                  // If a non-malleable satisfaction exists, the malleable one must also exist, and be identical to it.
382                  BOOST_CHECK(mal_success);
383                  BOOST_CHECK(witness_nonmal.stack == witness_mal.stack);
384                  assert(wit_size <= *node->GetWitnessSize());
385  
386                  // Test non-malleable satisfaction.
387                  ScriptError serror;
388                  bool res = VerifyScript(CScript(), script_pubkey, &witness_nonmal, STANDARD_SCRIPT_VERIFY_FLAGS, checker, &serror);
389                  // Non-malleable satisfactions are guaranteed to be valid if ValidSatisfactions().
390                  if (node->ValidSatisfactions()) BOOST_CHECK(res);
391                  // More detailed: non-malleable satisfactions must be valid, or could fail with ops count error (if CheckOpsLimit failed),
392                  // or with a stack size error (if CheckStackSize check fails).
393                  BOOST_CHECK(res ||
394                              (!node->CheckOpsLimit() && serror == ScriptError::SCRIPT_ERR_OP_COUNT) ||
395                              (!node->CheckStackSize() && serror == ScriptError::SCRIPT_ERR_STACK_SIZE));
396              }
397  
398              if (mal_success && (!nonmal_success || witness_mal.stack != witness_nonmal.stack)) {
399                  // Test malleable satisfaction only if it's different from the non-malleable one.
400                  ScriptError serror;
401                  bool res = VerifyScript(CScript(), script_pubkey, &witness_mal, STANDARD_SCRIPT_VERIFY_FLAGS, checker, &serror);
402                  // Malleable satisfactions are not guaranteed to be valid under any conditions, but they can only
403                  // fail due to stack or ops limits.
404                  BOOST_CHECK(res || serror == ScriptError::SCRIPT_ERR_OP_COUNT || serror == ScriptError::SCRIPT_ERR_STACK_SIZE);
405              }
406  
407              if (node->IsSane()) {
408                  // For sane nodes, the two algorithms behave identically.
409                  BOOST_CHECK_EQUAL(mal_success, nonmal_success);
410              }
411  
412              // Adding more satisfied conditions can never remove our ability to produce a satisfaction.
413              BOOST_CHECK(mal_success >= prev_mal_success);
414              // For nonmalleable solutions this is only true if the added condition is PK;
415              // for other conditions, adding one may make an valid satisfaction become malleable. If the script
416              // is sane, this cannot happen however.
417              if (node->IsSane() || add < 0 || challist[add].first == ChallengeType::PK) {
418                  BOOST_CHECK(nonmal_success >= prev_nonmal_success);
419              }
420              // Remember results for the next added challenge.
421              prev_mal_success = mal_success;
422              prev_nonmal_success = nonmal_success;
423          }
424  
425          bool satisfiable = node->IsSatisfiable([](const Node&) { return true; });
426          // If the miniscript was satisfiable at all, a satisfaction must be found after all conditions are added.
427          BOOST_CHECK_EQUAL(prev_mal_success, satisfiable);
428          // If the miniscript is sane and satisfiable, a nonmalleable satisfaction must eventually be found.
429          if (node->IsSane()) BOOST_CHECK_EQUAL(prev_nonmal_success, satisfiable);
430      }
431  }
432  
433  enum TestMode : int {
434      //! Invalid under any context
435      TESTMODE_INVALID = 0,
436      //! Valid under any context unless overridden
437      TESTMODE_VALID = 1,
438      TESTMODE_NONMAL = 2,
439      TESTMODE_NEEDSIG = 4,
440      TESTMODE_TIMELOCKMIX = 8,
441      //! Invalid only under P2WSH context
442      TESTMODE_P2WSH_INVALID = 16,
443      //! Invalid only under Tapscript context
444      TESTMODE_TAPSCRIPT_INVALID = 32,
445  };
446  
447  void Test(const std::string& ms, const std::string& hexscript, int mode, const KeyConverter& converter,
448            int opslimit = -1, int stacklimit = -1, std::optional<uint32_t> max_wit_size = std::nullopt,
449            std::optional<uint32_t> stack_exec = {})
450  {
451      auto node = miniscript::FromString(ms, converter);
452      const bool is_tapscript{miniscript::IsTapscript(converter.MsContext())};
453      if (mode == TESTMODE_INVALID || ((mode & TESTMODE_P2WSH_INVALID) && !is_tapscript) || ((mode & TESTMODE_TAPSCRIPT_INVALID) && is_tapscript)) {
454          BOOST_CHECK_MESSAGE(!node || !node->IsValid(), "Unexpectedly valid: " + ms);
455      } else {
456          BOOST_CHECK_MESSAGE(node, "Unparseable: " + ms);
457          BOOST_CHECK_MESSAGE(node->IsValid(), "Invalid: " + ms);
458          BOOST_CHECK_MESSAGE(node->IsValidTopLevel(), "Invalid top level: " + ms);
459          auto computed_script = node->ToScript(converter);
460          BOOST_CHECK_MESSAGE(node->ScriptSize() == computed_script.size(), "Script size mismatch: " + ms);
461          if (hexscript != "?") BOOST_CHECK_MESSAGE(HexStr(computed_script) == hexscript, "Script mismatch: " + ms + " (" + HexStr(computed_script) + " vs " + hexscript + ")");
462          BOOST_CHECK_MESSAGE(node->IsNonMalleable() == !!(mode & TESTMODE_NONMAL), "Malleability mismatch: " + ms);
463          BOOST_CHECK_MESSAGE(node->NeedsSignature() == !!(mode & TESTMODE_NEEDSIG), "Signature necessity mismatch: " + ms);
464          BOOST_CHECK_MESSAGE((node->GetType() << "k"_mst) == !(mode & TESTMODE_TIMELOCKMIX), "Timelock mix mismatch: " + ms);
465          auto inferred_miniscript = miniscript::FromScript(computed_script, converter);
466          BOOST_CHECK_MESSAGE(inferred_miniscript, "Cannot infer miniscript from script: " + ms);
467          BOOST_CHECK_MESSAGE(inferred_miniscript->ToScript(converter) == computed_script, "Roundtrip failure: miniscript->script != miniscript->script->miniscript->script: " + ms);
468          if (opslimit != -1) BOOST_CHECK_MESSAGE((int)*node->GetOps() == opslimit, "Ops limit mismatch: " << ms << " (" << *node->GetOps() << " vs " << opslimit << ")");
469          if (stacklimit != -1) BOOST_CHECK_MESSAGE((int)*node->GetStackSize() == stacklimit, "Stack limit mismatch: " << ms << " (" << *node->GetStackSize() << " vs " << stacklimit << ")");
470          if (max_wit_size) BOOST_CHECK_MESSAGE(*node->GetWitnessSize() == *max_wit_size, "Witness size limit mismatch: " << ms << " (" << *node->GetWitnessSize() << " vs " << *max_wit_size << ")");
471          if (stack_exec) BOOST_CHECK_MESSAGE(*node->GetExecStackSize() == *stack_exec, "Stack execution limit mismatch: " << ms << " (" << *node->GetExecStackSize() << " vs " << *stack_exec << ")");
472          TestSatisfy(converter, ms, node);
473      }
474  }
475  
476  void Test(const std::string& ms, const std::string& hexscript, const std::string& hextapscript, int mode,
477            int opslimit, int stacklimit, std::optional<uint32_t> max_wit_size,
478            std::optional<uint32_t> max_tap_wit_size,
479            std::optional<uint32_t> stack_exec)
480  {
481      KeyConverter wsh_converter(miniscript::MiniscriptContext::P2WSH);
482      Test(ms, hexscript, mode, wsh_converter, opslimit, stacklimit, max_wit_size, stack_exec);
483      KeyConverter tap_converter(miniscript::MiniscriptContext::TAPSCRIPT);
484      Test(ms, hextapscript == "=" ? hexscript : hextapscript, mode, tap_converter, opslimit, stacklimit, max_tap_wit_size, stack_exec);
485  }
486  
487  void Test(const std::string& ms, const std::string& hexscript, const std::string& hextapscript, int mode)
488  {
489      Test(ms, hexscript, hextapscript, mode,
490           /*opslimit=*/-1, /*stacklimit=*/-1,
491           /*max_wit_size=*/std::nullopt, /*max_tap_wit_size=*/std::nullopt, /*stack_exec=*/std::nullopt);
492  }
493  
494  } // namespace
495  
496  BOOST_FIXTURE_TEST_SUITE(miniscript_tests, BasicTestingSetup)
497  
498  BOOST_AUTO_TEST_CASE(fixed_tests)
499  {
500      g_testdata.reset(new TestData());
501  
502      // Validity rules
503      Test("l:older(1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // older(1): valid
504      Test("l:older(0)", "?", "?", TESTMODE_INVALID); // older(0): k must be at least 1
505      Test("l:older(2147483647)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // older(2147483647): valid
506      Test("l:older(2147483648)", "?", "?", TESTMODE_INVALID); // older(2147483648): k must be below 2^31
507      Test("u:after(1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // after(1): valid
508      Test("u:after(0)", "?", "?", TESTMODE_INVALID); // after(0): k must be at least 1
509      Test("u:after(2147483647)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // after(2147483647): valid
510      Test("u:after(2147483648)", "?", "?", TESTMODE_INVALID); // after(2147483648): k must be below 2^31
511      Test("andor(0,1,1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // andor(Bdu,B,B): valid
512      Test("andor(a:0,1,1)", "?", "?", TESTMODE_INVALID); // andor(Wdu,B,B): X must be B
513      Test("andor(0,a:1,a:1)", "?", "?", TESTMODE_INVALID); // andor(Bdu,W,W): Y and Z must be B/V/K
514      Test("andor(1,1,1)", "?", "?", TESTMODE_INVALID); // andor(Bu,B,B): X must be d
515      Test("andor(n:or_i(0,after(1)),1,1)", "?", "?", TESTMODE_VALID); // andor(Bdu,B,B): valid
516      Test("andor(or_i(0,after(1)),1,1)", "?", "?", TESTMODE_INVALID); // andor(Bd,B,B): X must be u
517      Test("c:andor(0,pk_k(03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7),pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // andor(Bdu,K,K): valid
518      Test("t:andor(0,v:1,v:1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // andor(Bdu,V,V): valid
519      Test("and_v(v:1,1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_v(V,B): valid
520      Test("t:and_v(v:1,v:1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_v(V,V): valid
521      Test("c:and_v(v:1,pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // and_v(V,K): valid
522      Test("and_v(1,1)", "?", "?", TESTMODE_INVALID); // and_v(B,B): X must be V
523      Test("and_v(pk_k(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),1)", "?", "?", TESTMODE_INVALID); // and_v(K,B): X must be V
524      Test("and_v(v:1,a:1)", "?", "?", TESTMODE_INVALID); // and_v(K,W): Y must be B/V/K
525      Test("and_b(1,a:1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // and_b(B,W): valid
526      Test("and_b(1,1)", "?", "?", TESTMODE_INVALID); // and_b(B,B): Y must W
527      Test("and_b(v:1,a:1)", "?", "?", TESTMODE_INVALID); // and_b(V,W): X must be B
528      Test("and_b(a:1,a:1)", "?", "?", TESTMODE_INVALID); // and_b(W,W): X must be B
529      Test("and_b(pk_k(025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),a:1)", "?", "?", TESTMODE_INVALID); // and_b(K,W): X must be B
530      Test("or_b(0,a:0)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // or_b(Bd,Wd): valid
531      Test("or_b(1,a:0)", "?", "?", TESTMODE_INVALID); // or_b(B,Wd): X must be d
532      Test("or_b(0,a:1)", "?", "?", TESTMODE_INVALID); // or_b(Bd,W): Y must be d
533      Test("or_b(0,0)", "?", "?", TESTMODE_INVALID); // or_b(Bd,Bd): Y must W
534      Test("or_b(v:0,a:0)", "?", "?", TESTMODE_INVALID); // or_b(V,Wd): X must be B
535      Test("or_b(a:0,a:0)", "?", "?", TESTMODE_INVALID); // or_b(Wd,Wd): X must be B
536      Test("or_b(pk_k(025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),a:0)", "?", "?", TESTMODE_INVALID); // or_b(Kd,Wd): X must be B
537      Test("t:or_c(0,v:1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // or_c(Bdu,V): valid
538      Test("t:or_c(a:0,v:1)", "?", "?", TESTMODE_INVALID); // or_c(Wdu,V): X must be B
539      Test("t:or_c(1,v:1)", "?", "?", TESTMODE_INVALID); // or_c(Bu,V): X must be d
540      Test("t:or_c(n:or_i(0,after(1)),v:1)", "?", "?", TESTMODE_VALID); // or_c(Bdu,V): valid
541      Test("t:or_c(or_i(0,after(1)),v:1)", "?", "?", TESTMODE_INVALID); // or_c(Bd,V): X must be u
542      Test("t:or_c(0,1)", "?", "?", TESTMODE_INVALID); // or_c(Bdu,B): Y must be V
543      Test("or_d(0,1)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // or_d(Bdu,B): valid
544      Test("or_d(a:0,1)", "?", "?", TESTMODE_INVALID); // or_d(Wdu,B): X must be B
545      Test("or_d(1,1)", "?", "?", TESTMODE_INVALID); // or_d(Bu,B): X must be d
546      Test("or_d(n:or_i(0,after(1)),1)", "?", "?", TESTMODE_VALID); // or_d(Bdu,B): valid
547      Test("or_d(or_i(0,after(1)),1)", "?", "?", TESTMODE_INVALID); // or_d(Bd,B): X must be u
548      Test("or_d(0,v:1)", "?", "?", TESTMODE_INVALID); // or_d(Bdu,V): Y must be B
549      Test("or_i(1,1)", "?", "?", TESTMODE_VALID); // or_i(B,B): valid
550      Test("t:or_i(v:1,v:1)", "?", "?", TESTMODE_VALID); // or_i(V,V): valid
551      Test("c:or_i(pk_k(03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7),pk_k(036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // or_i(K,K): valid
552      Test("or_i(a:1,a:1)", "?", "?", TESTMODE_INVALID); // or_i(W,W): X and Y must be B/V/K
553      Test("or_b(l:after(100),al:after(1000000000))", "?", "?", TESTMODE_VALID); // or_b(timelock, heighlock) valid
554      Test("and_b(after(100),a:after(1000000000))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX); // and_b(timelock, heighlock) invalid
555      Test("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // alias to c:pk_k
556      Test("pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", "76a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac", "76a914fd1690c37fa3b0f04395ddc9415b220ab1ccc59588ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // alias to c:pk_h
557  
558      // Randomly generated test set that covers the majority of type and node type combinations
559      Test("lltvln:after(1231488000)", "6300676300676300670400046749b1926869516868", "=", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 3, 3, 3);
560      Test("uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "6363829263522103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a21025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc52af0400046749b168670068670068", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 14, 5, 2 + 2 + 1 + 2 * 73, 0, 7);
561      Test("or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))", "63522103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee872921024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae926700686b63006760b2686c9b", "?", TESTMODE_VALID | TESTMODE_TAPSCRIPT_INVALID, 14, 5, 2 + 1 + 2 * 73 + 2, 0, 8);
562      Test("j:and_v(vdv:after(1567547623),older(2016))", "829263766304e7e06e5db169686902e007b268", "=", TESTMODE_VALID | TESTMODE_NONMAL, 11, 1, 2, 2, 2);
563      Test("t:and_v(vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5))", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 2 + 33 + 33, 2 + 33 + 33, 4);
564      Test("t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))", "532102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975562102e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1353ae6482012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2886703010040b2696851", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TAPSCRIPT_INVALID, 13, 5, 1 + 3 * 73, 0, 10);
565      Test("or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))", "512102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f951ae73645321022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a0121032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f2103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a53ae7c630320a107b16700689b68", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TAPSCRIPT_INVALID, 15, 7, 2 + 1 + 3 * 73 + 1, 0, 10);
566      Test("or_d(sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6),and_n(un:after(499999999),older(4194305)))", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", TESTMODE_VALID, 16, 1, 33, 33, 3);
567      Test("and_v(or_i(v:multi(2,02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb),v:multi(2,03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)),sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68))", "63522102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee52103774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb52af67522103e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a21025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc52af6882012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c6887", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 11, 5, 2 + 1 + 2 * 73 + 33, 0, 8);
568      Test("j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))", "82926352210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae7c6351b26703e2e440b2689a68", "?", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 14, 4, 1 + 2 * 73 + 2, 0, 8);
569      Test("and_b(older(16),s:or_d(sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),n:after(1567547623)))", "60b27c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87736404e7e06e5db192689a", "=", TESTMODE_VALID, 12, 1, 33, 33, 4);
570      Test("j:and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))", "82926382012088a91420195b5a3d650c17f0f29f91c33f8f6335193d078882012088a82096de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c4787736460b26868", "=", TESTMODE_VALID, 16, 2, 33 + 33, 33 + 33, 4);
571      Test("and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))", "82012088aa2032ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac876b82012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876b51b26c9a6c9a", "=", TESTMODE_VALID | TESTMODE_NONMAL, 15, 2, 33 + 33, 33 + 33, 4);
572      Test("thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))", "522103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c721036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0052ae6b5121036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0051ae6c936b21022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ac6c935287", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 13, 6, 1 + 2 * 73 + 1 + 73 + 1, 0, 10);
573      Test("and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))", "82012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68876400676303e2e440b26967029000b269685168", "=", TESTMODE_VALID, 14, 2, 33 + 2, 33 + 2, 4);
574      Test("or_d(nd:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))", "766303e2e440b26903e2e440b2696892736482012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68768", "=", TESTMODE_VALID, 15, 2, 1 + 33, 1 + 33, 3);
575      Test("c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764512102c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db51af682103acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeac", "?", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 8, 2, 33 + 73, 0, 4);
576      Test("c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "5221036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a002102352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d552ae6482012088a6141b0f3c404d12075c68c938f9f60ebea4f74941a088682103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 10, 5, 1 + 2 * 73 + 73, 0, 9);
577      Test("and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))", "82012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b2587640350c300b2696782012088aa20939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735886804ff64cd1db1", "=", TESTMODE_VALID, 14, 2, 33 + 33, 33 + 33, 4);
578      Test("andor(hash256(5f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040),j:and_v(v:hash160(3a2bff0da9d96868e66abc4427bea4691cf61ccd),older(4194305)),ripemd160(44d90e2d3714c8663b632fcf0f9d5f22192cc4c8))", "82012088aa205f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040876482012088a61444d90e2d3714c8663b632fcf0f9d5f22192cc4c8876782926382012088a9143a2bff0da9d96868e66abc4427bea4691cf61ccd8803010040b26868", "=", TESTMODE_VALID, 20, 2, 33 + 33, 33 + 33, 4);
579      Test("or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f946))", "630320a107b1692102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", "630320a107b16920c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", TESTMODE_VALID | TESTMODE_NONMAL, 10, 2, 2 + 73, 2 + 66, 3);
580      Test("thresh(2,c:pk_h(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),a:hash160(dd69735817e0e3f6f826a9238dc2e291184f0131))", "76a9145dedfbf9ea599dd4e3ca6a80b333c472fd0b3f6988ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", "76a9141a7ac36cfa8431ab2395d701b0050045ae4a37d188ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", TESTMODE_VALID, 18, 4, 1 + 34 + 33 + 33, 1 + 33 + 33 + 33, 6);
581      Test("and_n(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),uc:and_v(v:older(144),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b2692103fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b26920fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", TESTMODE_VALID | TESTMODE_NEEDSIG, 13, 3, 33 + 2 + 73, 33 + 2 + 66, 5);
582      Test("and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))", "2103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", "20daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TIMELOCKMIX, 12, 2, 73 + 1, 66 + 1, 3);
583      Test("c:or_i(and_v(v:older(16),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)),pk_h(026a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4))", "6360b26976a9149fc5dbe5efdce10374a4dd4053c93af540211718886776a9142fbd32c8dd59ee7c17e66cb6ebea7e9846c3040f8868ac", "6360b26976a9144d4421361c3289bdad06441ffaee8be8e786f1ad886776a91460d4a7bcbd08f58e58bd208d1069837d7adb16ae8868ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 12, 3, 2 + 34 + 73, 2 + 33 + 66, 4);
584      Test("or_d(c:pk_h(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),andor(c:pk_k(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),older(2016),after(1567547623)))", "76a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac736421024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", "76a91421ab1a140d0d305b8ff62bdb887d9fef82c9899e88ac7364204ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", TESTMODE_VALID | TESTMODE_NONMAL, 13, 3, 1 + 34 + 73, 1 + 33 + 66, 5);
585      Test("c:andor(ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e),and_v(v:hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),pk_h(03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a)))", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914dd100be7d9aea5721158ebde6d6a1fd8fff93bb1886776a9149fc5dbe5efdce10374a4dd4053c93af5402117188868ac", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914a63d1e4d2ed109246c600ec8c19cce546b65b1cc886776a9144d4421361c3289bdad06441ffaee8be8e786f1ad8868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 18, 3, 33 + 34 + 73, 33 + 33 + 66, 5);
586      Test("c:andor(u:ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),or_i(pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)))", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a9149652d86bedf43ad264362e6e6eba6eb764508127886776a914751e76e8199196d454941c45d1b3a323f1433bd688686776a91420d637c1a6404d2227f3561fdbaff5a680dba6488868ac", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a914ceedcb44b38bdbcb614d872223964fd3dca8a434886776a914f678d9b79045452c8c64e9309d0f0046056e26c588686776a914a2a75e1819afa208f6c89ae0da43021116dfcb0c8868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 23, 4, 2 + 33 + 34 + 73, 2 + 33 + 33 + 66, 5);
587      Test("c:or_i(andor(c:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),pk_k(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))", "6376a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac6476a91406afd46bcdfd22ef94ac122aa11f241244a37ecc886776a9149652d86bedf43ad264362e6e6eba6eb7645081278868672102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", "6376a914fd1690c37fa3b0f04395ddc9415b220ab1ccc59588ac6476a9149b652a14674a506079f574d20ca7daef6f9a66bb886776a914ceedcb44b38bdbcb614d872223964fd3dca8a43488686720d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 17, 5, 2 + 34 + 73 + 34 + 73, 2 + 33 + 66 + 33 + 66, 6);
588      Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670400ca9a3bb16951686c936b6300670164b16951686c935187", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670400ca9a3bb16951686c936b6300670164b16951686c935187", TESTMODE_VALID, 18, 3, 73 + 2 + 2, 66 + 2 + 2, 4);
589      Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac6c936b6300670400ca9a3bb16951686c936b6300670164b16951686c935287", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac6c936b6300670400ca9a3bb16951686c936b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX, 22, 4, 73 + 73 + 2 + 2, 66 + 66 + 2 + 2, 5);
590  
591      // Additional Tapscript-related tests
592      // Edge cases when parsing multi_a from script:
593      //  - no pubkey at all
594      //  - no pubkey before a CHECKSIGADD
595      //  - no pubkey before the CHECKSIG
596      constexpr KeyConverter tap_converter{miniscript::MiniscriptContext::TAPSCRIPT};
597      constexpr KeyConverter wsh_converter{miniscript::MiniscriptContext::P2WSH};
598      const auto no_pubkey{ParseHex("ac519c")};
599      BOOST_CHECK(miniscript::FromScript({no_pubkey.begin(), no_pubkey.end()}, tap_converter) == nullptr);
600      const auto incomplete_multi_a{ParseHex("ba20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba519c")};
601      BOOST_CHECK(miniscript::FromScript({incomplete_multi_a.begin(), incomplete_multi_a.end()}, tap_converter) == nullptr);
602      const auto incomplete_multi_a_2{ParseHex("ac2079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba519c")};
603      BOOST_CHECK(miniscript::FromScript({incomplete_multi_a_2.begin(), incomplete_multi_a_2.end()}, tap_converter) == nullptr);
604      // Can use multi_a under Tapscript but not P2WSH.
605      Test("and_v(v:multi_a(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "?", "20d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85aac205601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7ccba529d0400046749b1", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4, 2, {}, {}, 3);
606      // Can use more than 20 keys in a multi_a.
607      std::string ms_str_multi_a{"multi_a(1,"};
608      for (size_t i = 0; i < 21; ++i) {
609          ms_str_multi_a += HexStr(g_testdata->pubkeys[i]);
610          if (i < 20) ms_str_multi_a += ",";
611      }
612      ms_str_multi_a += ")";
613      Test(ms_str_multi_a, "?", "2079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba20f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9ba20e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13ba202f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4ba20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ba205cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bcba202f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ba20acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeba20a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7ba20774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cbba20d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85aba20f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8ba20499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4ba20d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080eba20e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0aba20defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34ba205601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7ccba202b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6cba204ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ba20352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5ba519c", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 22, 21, {}, {}, 22);
614      // Since 'd:' is 'u' we can use it directly inside a thresh. But we can't under P2WSH.
615      Test("thresh(2,dv:older(42),s:pk(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", "?", "7663012ab269687c205cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bcac937c20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 12, 3, {}, {}, 4);
616      // We can have a script that has more than 201 ops (n = 99), that needs a stack size > 100 (n = 110), or has a
617      // script that is larger than 3600 bytes (n = 200). All that can't be under P2WSH.
618      for (const auto pk_count: {99, 110, 200}) {
619          std::string ms_str_large;
620          for (auto i = 0; i < pk_count - 1; ++i) {
621              ms_str_large += "and_b(pk(" + HexStr(g_testdata->pubkeys[i]) + "),a:";
622          }
623          ms_str_large += "pk(" + HexStr(g_testdata->pubkeys[pk_count - 1]) + ")";
624          ms_str_large.insert(ms_str_large.end(), pk_count - 1, ')');
625          Test(ms_str_large, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, pk_count + (pk_count - 1) * 3, pk_count, {}, {}, pk_count + 1);
626      }
627      // We can have a script that reaches a stack size of 1000 during execution.
628      std::string ms_stack_limit;
629      auto count{998};
630      for (auto i = 0; i < count; ++i) {
631          ms_stack_limit += "and_b(older(1),a:";
632      }
633      ms_stack_limit += "pk(" + HexStr(g_testdata->pubkeys[0]) + ")";
634      ms_stack_limit.insert(ms_stack_limit.end(), count, ')');
635      const auto ms_stack_ok{miniscript::FromString(ms_stack_limit, tap_converter)};
636      BOOST_CHECK(ms_stack_ok && ms_stack_ok->CheckStackSize());
637      Test(ms_stack_limit, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4 * count + 1, 1, {}, {}, 1 + count + 1);
638      // But one more element on the stack during execution will make it fail. And we'd detect that.
639      count++;
640      ms_stack_limit = "and_b(older(1),a:" + ms_stack_limit + ")";
641      const auto ms_stack_nok{miniscript::FromString(ms_stack_limit, tap_converter)};
642      BOOST_CHECK(ms_stack_nok && !ms_stack_nok->CheckStackSize());
643      Test(ms_stack_limit, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4 * count + 1, 1, {}, {}, 1 + count + 1);
644  
645      // Misc unit tests
646      // A Script with a non minimal push is invalid
647      std::vector<unsigned char> nonminpush = ParseHex("0000210232780000feff00ffffffffffff21ff005f00ae21ae00000000060602060406564c2102320000060900fe00005f00ae21ae00100000060606060606000000000000000000000000000000000000000000000000000000000000000000");
648      const CScript nonminpush_script(nonminpush.begin(), nonminpush.end());
649      BOOST_CHECK(miniscript::FromScript(nonminpush_script, wsh_converter) == nullptr);
650      BOOST_CHECK(miniscript::FromScript(nonminpush_script, tap_converter) == nullptr);
651      // A non-minimal VERIFY (<key> CHECKSIG VERIFY 1)
652      std::vector<unsigned char> nonminverify = ParseHex("2103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7ac6951");
653      const CScript nonminverify_script(nonminverify.begin(), nonminverify.end());
654      BOOST_CHECK(miniscript::FromScript(nonminverify_script, wsh_converter) == nullptr);
655      BOOST_CHECK(miniscript::FromScript(nonminverify_script, tap_converter) == nullptr);
656      // A threshold as large as the number of subs is valid.
657      Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670164b16951686c935287", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_NONMAL);
658      // A threshold of 1 is valid.
659      Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_NONMAL);
660      // A threshold with a k larger than the number of subs is invalid
661      Test("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", "=", TESTMODE_INVALID);
662      // A threshold with a k null is invalid
663      Test("thresh(0,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", "=", TESTMODE_INVALID);
664      // For CHECKMULTISIG the OP cost is the number of keys, but the stack size is the number of sigs (+1)
665      const auto ms_multi = miniscript::FromString("multi(1,03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", wsh_converter);
666      BOOST_CHECK(ms_multi);
667      BOOST_CHECK_EQUAL(*ms_multi->GetOps(), 4); // 3 pubkeys + CMS
668      BOOST_CHECK_EQUAL(*ms_multi->GetStackSize(), 2); // 1 sig + dummy elem
669      // The 'd:' wrapper leaves on the stack what was DUP'ed at the beginning of its execution.
670      // Since it contains an OP_IF just after on the same element, we can make sure that the element
671      // in question must be OP_1 if OP_IF enforces that its argument must only be OP_1 or the empty
672      // vector (since otherwise the execution would immediately fail). This is the MINIMALIF rule.
673      // Unfortunately, this rule is consensus for Taproot but only policy for P2WSH. Therefore we can't
674      // (for now) have 'd:' be 'u'. This tests we can't use a 'd:' wrapper for a thresh, which requires
675      // its subs to all be 'u' (taken from https://github.com/rust-bitcoin/rust-miniscript/discussions/341).
676      const auto ms_minimalif = miniscript::FromString("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),sc:pk_k(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798),sdv:older(32))", wsh_converter);
677      BOOST_CHECK(ms_minimalif && !ms_minimalif->IsValid());
678      // A Miniscript with duplicate keys is not sane
679      const auto ms_dup1 = miniscript::FromString("and_v(v:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", wsh_converter);
680      BOOST_CHECK(ms_dup1);
681      BOOST_CHECK(!ms_dup1->IsSane() && !ms_dup1->CheckDuplicateKey());
682      // Same with a disjunction, and different key nodes (pk and pkh)
683      const auto ms_dup2 = miniscript::FromString("or_b(c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", wsh_converter);
684      BOOST_CHECK(ms_dup2 && !ms_dup2->IsSane() && !ms_dup2->CheckDuplicateKey());
685      // Same when the duplicates are leaves or a larger tree
686      const auto ms_dup3 = miniscript::FromString("or_i(and_b(pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556)),and_b(older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", wsh_converter);
687      BOOST_CHECK(ms_dup3 && !ms_dup3->IsSane() && !ms_dup3->CheckDuplicateKey());
688      // Same when the duplicates are on different levels in the tree
689      const auto ms_dup4 = miniscript::FromString("thresh(2,pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),a:and_b(dv:older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", wsh_converter);
690      BOOST_CHECK(ms_dup4 && !ms_dup4->IsSane() && !ms_dup4->CheckDuplicateKey());
691      // Sanity check the opposite is true, too. An otherwise sane Miniscript with no duplicate keys is sane.
692      const auto ms_nondup = miniscript::FromString("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", wsh_converter);
693      BOOST_CHECK(ms_nondup && ms_nondup->CheckDuplicateKey() && ms_nondup->IsSane());
694      // Test we find the first insane sub closer to be a leaf node. This fragment is insane for two reasons:
695      // 1. It can be spent without a signature
696      // 2. It contains timelock mixes
697      // We'll report the timelock mix error, as it's "deeper" (closer to be a leaf node) than the "no 's' property"
698      // error is.
699      const auto ms_ins = miniscript::FromString("or_i(and_b(after(1),a:after(1000000000)),pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204))", wsh_converter);
700      BOOST_CHECK(ms_ins && ms_ins->IsValid() && !ms_ins->IsSane());
701      const auto insane_sub = ms_ins->FindInsaneSub();
702      BOOST_CHECK(insane_sub && *insane_sub->ToString(wsh_converter) == "and_b(after(1),a:after(1000000000))");
703  
704      // Timelock tests
705      Test("after(100)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock
706      Test("after(1000000000)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only timelock
707      Test("or_b(l:after(100),al:after(1000000000))", "?", "?", TESTMODE_VALID); // or_b(timelock, heighlock) valid
708      Test("and_b(after(100),a:after(1000000000))", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX); // and_b(timelock, heighlock) invalid
709      /* This is correctly detected as non-malleable but for the wrong reason. The type system assumes that branches 1 and 2
710         can be spent together to create a non-malleble witness, but because of mixing of timelocks they cannot be spent together.
711         But since exactly one of the two after's can be satisfied, the witness involving the key cannot be malleated.
712      */
713      Test("thresh(2,ltv:after(1000000000),altv:after(100),a:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", "?", "?", TESTMODE_VALID | TESTMODE_TIMELOCKMIX | TESTMODE_NONMAL); // thresh with k = 2
714      // This is actually non-malleable in practice, but we cannot detect it in type system. See above rationale
715      Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(1000000000),altv:after(100))", "?", "?", TESTMODE_VALID); // thresh with k = 1
716  
717      g_testdata.reset();
718  }
719  
720  BOOST_AUTO_TEST_SUITE_END()