Run random satisfaction tests. */
| 345 | struct MiniScriptTest : BasicTestingSetup { |
| 346 | /** Run random satisfaction tests. */ |
| 347 | void TestSatisfy(const KeyConverter& converter, const Node& node) |
| 348 | { |
| 349 | auto script = node.ToScript(converter); |
| 350 | const auto challenges{FindChallenges(node)}; // Find all challenges in the generated miniscript. |
| 351 | std::vector<Challenge> challist(challenges.begin(), challenges.end()); |
| 352 | for (int iter = 0; iter < 3; ++iter) { |
| 353 | std::shuffle(challist.begin(), challist.end(), m_rng); |
| 354 | Satisfier satisfier(converter.MsContext()); |
| 355 | TestSignatureChecker checker(satisfier); |
| 356 | bool prev_mal_success = false, prev_nonmal_success = false; |
| 357 | // Go over all challenges involved in this miniscript in random order. |
| 358 | for (int add = -1; add < (int)challist.size(); ++add) { |
| 359 | if (add >= 0) satisfier.supported.insert(challist[add]); // The first iteration does not add anything |
| 360 | |
| 361 | // Get the ScriptPubKey for this script, filling spend data if it's Taproot. |
| 362 | TaprootBuilder builder; |
| 363 | const CScript script_pubkey{ScriptPubKey(converter.MsContext(), script, builder)}; |
| 364 | |
| 365 | // Run malleable satisfaction algorithm. |
| 366 | CScriptWitness witness_mal; |
| 367 | const bool mal_success = node.Satisfy(satisfier, witness_mal.stack, false) == miniscript::Availability::YES; |
| 368 | SatisfactionToWitness(converter.MsContext(), witness_mal, script, builder); |
| 369 | |
| 370 | // Run non-malleable satisfaction algorithm. |
| 371 | CScriptWitness witness_nonmal; |
| 372 | const bool nonmal_success = node.Satisfy(satisfier, witness_nonmal.stack, true) == miniscript::Availability::YES; |
| 373 | // Compute witness size (excluding script push, control block, and witness count encoding). |
| 374 | const uint64_t wit_size{GetSerializeSize(witness_nonmal.stack) - GetSizeOfCompactSize(witness_nonmal.stack.size())}; |
| 375 | SatisfactionToWitness(converter.MsContext(), witness_nonmal, script, builder); |
| 376 | |
| 377 | if (nonmal_success) { |
| 378 | // Non-malleable satisfactions are bounded by the satisfaction size plus: |
| 379 | // - For P2WSH spends, the witness script |
| 380 | // - For Tapscript spends, both the witness script and the control block |
| 381 | const size_t max_stack_size{*node.GetStackSize() + 1 + miniscript::IsTapscript(converter.MsContext())}; |
| 382 | BOOST_CHECK(witness_nonmal.stack.size() <= max_stack_size); |
| 383 | // If a non-malleable satisfaction exists, the malleable one must also exist, and be identical to it. |
| 384 | BOOST_CHECK(mal_success); |
| 385 | BOOST_CHECK(witness_nonmal.stack == witness_mal.stack); |
| 386 | assert(wit_size <= *node.GetWitnessSize()); |
| 387 | |
| 388 | // Test non-malleable satisfaction. |
| 389 | ScriptError serror; |
| 390 | bool res = VerifyScript(CScript(), script_pubkey, &witness_nonmal, STANDARD_SCRIPT_VERIFY_FLAGS, checker, &serror); |
| 391 | // Non-malleable satisfactions are guaranteed to be valid if ValidSatisfactions(). |
| 392 | if (node.ValidSatisfactions()) BOOST_CHECK(res); |
| 393 | // More detailed: non-malleable satisfactions must be valid, or could fail with ops count error (if CheckOpsLimit failed), |
| 394 | // or with a stack size error (if CheckStackSize check fails). |
| 395 | BOOST_CHECK(res || |
| 396 | (!node.CheckOpsLimit() && serror == ScriptError::SCRIPT_ERR_OP_COUNT) || |
| 397 | (!node.CheckStackSize() && serror == ScriptError::SCRIPT_ERR_STACK_SIZE)); |
| 398 | } |
| 399 | |
| 400 | if (mal_success && (!nonmal_success || witness_mal.stack != witness_nonmal.stack)) { |
| 401 | // Test malleable satisfaction only if it's different from the non-malleable one. |
| 402 | ScriptError serror; |
| 403 | bool res = VerifyScript(CScript(), script_pubkey, &witness_mal, STANDARD_SCRIPT_VERIFY_FLAGS, checker, &serror); |
| 404 | // Malleable satisfactions are not guaranteed to be valid under any conditions, but they can only |
nothing calls this directly
no test coverage detected