Perform various applicable tests on a miniscript Node. */
| 1028 | |
| 1029 | /** Perform various applicable tests on a miniscript Node. */ |
| 1030 | void TestNode(const MsCtx script_ctx, const std::optional<Node>& node, FuzzedDataProvider& provider) |
| 1031 | { |
| 1032 | if (!node) return; |
| 1033 | |
| 1034 | // Check that it roundtrips to text representation |
| 1035 | const ParserContext parser_ctx{script_ctx}; |
| 1036 | std::optional<std::string> str{node->ToString(parser_ctx)}; |
| 1037 | assert(str); |
| 1038 | auto parsed = miniscript::FromString(*str, parser_ctx); |
| 1039 | assert(parsed); |
| 1040 | assert(*parsed == *node); |
| 1041 | |
| 1042 | // Check consistency between script size estimation and real size. |
| 1043 | auto script = node->ToScript(parser_ctx); |
| 1044 | assert(node->ScriptSize() == script.size()); |
| 1045 | |
| 1046 | // Check consistency of "x" property with the script (type K is excluded, because it can end |
| 1047 | // with a push of a key, which could match these opcodes). |
| 1048 | if (!(node->GetType() << "K"_mst)) { |
| 1049 | bool ends_in_verify = !(node->GetType() << "x"_mst); |
| 1050 | assert(ends_in_verify == (script.back() == OP_CHECKSIG || script.back() == OP_CHECKMULTISIG || script.back() == OP_EQUAL || script.back() == OP_NUMEQUAL)); |
| 1051 | } |
| 1052 | |
| 1053 | // The rest of the checks only apply when testing a valid top-level script. |
| 1054 | if (!node->IsValidTopLevel()) return; |
| 1055 | |
| 1056 | // Check roundtrip to script |
| 1057 | auto decoded = miniscript::FromScript(script, parser_ctx); |
| 1058 | assert(decoded); |
| 1059 | // Note we can't use *decoded == *node because the miniscript representation may differ, so we check that: |
| 1060 | // - The script corresponding to that decoded form matches exactly |
| 1061 | // - The type matches exactly |
| 1062 | assert(decoded->ToScript(parser_ctx) == script); |
| 1063 | assert(decoded->GetType() == node->GetType()); |
| 1064 | |
| 1065 | // Optionally pad the script or the witness in order to increase the sensitivity of the tests of |
| 1066 | // the resources limits logic. |
| 1067 | CScriptWitness witness_mal, witness_nonmal; |
| 1068 | if (provider.ConsumeBool()) { |
| 1069 | // Under P2WSH, optionally pad the script with OP_NOPs to max op the ops limit of the constructed script. |
| 1070 | // This makes the script obviously not actually miniscript-compatible anymore, but the |
| 1071 | // signatures constructed in this test don't commit to the script anyway, so the same |
| 1072 | // miniscript satisfier will work. This increases the sensitivity of the test to the ops |
| 1073 | // counting logic being too low, especially for simple scripts. |
| 1074 | // Do this optionally because we're not solely interested in cases where the number of ops is |
| 1075 | // maximal. |
| 1076 | // Do not pad more than what would cause MAX_STANDARD_P2WSH_SCRIPT_SIZE to be reached, however, |
| 1077 | // as that also invalidates scripts. |
| 1078 | const auto node_ops{node->GetOps()}; |
| 1079 | if (!IsTapscript(script_ctx) && node_ops && *node_ops < MAX_OPS_PER_SCRIPT |
| 1080 | && node->ScriptSize() < MAX_STANDARD_P2WSH_SCRIPT_SIZE) { |
| 1081 | int add = std::min<int>( |
| 1082 | MAX_OPS_PER_SCRIPT - *node_ops, |
| 1083 | MAX_STANDARD_P2WSH_SCRIPT_SIZE - node->ScriptSize()); |
| 1084 | for (int i = 0; i < add; ++i) script.push_back(OP_NOP); |
| 1085 | } |
| 1086 | |
| 1087 | // Under Tapscript, optionally pad the stack up to the limit minus the calculated maximum execution stack |
no test coverage detected