Skip to content

Commit e3d7533

Browse files
EthanHeilmanjanb84
andcommitted
test: improves tapscript unit tests
This commit creates new test utilities for future Taproot script tests within script_tests.json. The key features of this commit are the addition of three new tags: `#SCRIPT#`, `#CONTROLBLOCK#`, and `#TAPROOTOUTPUT#`. These tags streamline the test creation process by eliminating the need to manually generate these components outside the test suite. * `#SCRIPT#`: Parses Tapscript and outputs a byte string of opcodes. * `#CONTROLBLOCK#`: Automatically generates the control block for a given Taproot output. * `#TAPROOTOUTPUT#`: Generates the final Taproot scriptPubKey. Update src/test/script_tests.cpp Co-authored-by: Jan B <[email protected]>
1 parent 3e16708 commit e3d7533

File tree

2 files changed

+86
-5
lines changed

2 files changed

+86
-5
lines changed

src/test/data/script_tests.json

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,6 +2610,63 @@
26102610
[["645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "UNBALANCED_CONDITIONAL"],
26112611
[["645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "UNBALANCED_CONDITIONAL"],
26122612

2613+
["Tapscript tests"],
2614+
[
2615+
[
2616+
"1ffe1234567890",
2617+
"00",
2618+
"#SCRIPT# HASH256 DUP SHA1 DROP DUP DROP TOALTSTACK HASH256 DUP DROP TOALTSTACK FROMALTSTACK",
2619+
"#CONTROLBLOCK#",
2620+
0.00000001
2621+
],
2622+
"",
2623+
"0x51 0x20 #TAPROOTOUTPUT#",
2624+
"P2SH,WITNESS,TAPROOT",
2625+
"OK",
2626+
"TAPSCRIPT Tests testing tapscript with many different op codes including ALTSTACK interactions"
2627+
],
2628+
[
2629+
[
2630+
"abcdef",
2631+
"#SCRIPT# 1 IF SHA256 ENDIF SIZE SWAP DROP 32 EQUAL",
2632+
"#CONTROLBLOCK#",
2633+
0.00000001
2634+
],
2635+
"",
2636+
"0x51 0x20 #TAPROOTOUTPUT#",
2637+
"P2SH,WITNESS,TAPROOT",
2638+
"OK",
2639+
"TAPSCRIPT Test IF conditional when true"
2640+
],
2641+
[
2642+
[
2643+
"abcdef",
2644+
"#SCRIPT# 0 IF SHA256 ENDIF SIZE SWAP DROP 32 EQUAL",
2645+
"#CONTROLBLOCK#",
2646+
0.00000001
2647+
],
2648+
"",
2649+
"0x51 0x20 #TAPROOTOUTPUT#",
2650+
"P2SH,WITNESS,TAPROOT",
2651+
"EVAL_FALSE",
2652+
"TAPSCRIPT Test IF conditional when false"
2653+
],
2654+
[
2655+
[
2656+
"aa",
2657+
"bb",
2658+
"cc",
2659+
"#SCRIPT# EQUAL IF DROP DROP ENDIF",
2660+
"#CONTROLBLOCK#",
2661+
0.00000001
2662+
],
2663+
"",
2664+
"0x51 0x20 #TAPROOTOUTPUT#",
2665+
"P2SH,WITNESS,TAPROOT",
2666+
"OK",
2667+
"TAPSCRIPT Test that DROP operations do not execute inside of a false IF conditional"
2668+
],
2669+
26132670
["NULLFAIL should cover all signatures and signatures only"],
26142671
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66 and NULLFAIL-compliant"],
26152672
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "OK", "BIP66 and NULLFAIL-compliant"],

src/test/script_tests.cpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -916,21 +916,38 @@ BOOST_AUTO_TEST_CASE(script_json_test)
916916
// amount (nValue) to use in the crediting tx
917917
UniValue tests = read_json(json_tests::script_tests);
918918

919+
const KeyData keys;
919920
for (unsigned int idx = 0; idx < tests.size(); idx++) {
920921
const UniValue& test = tests[idx];
921922
std::string strTest = test.write();
922923
CScriptWitness witness;
924+
TaprootBuilder taprootBuilder;
923925
CAmount nValue = 0;
924926
unsigned int pos = 0;
925927
if (test.size() > 0 && test[pos].isArray()) {
926928
unsigned int i=0;
927929
for (i = 0; i < test[pos].size()-1; i++) {
928930
auto element = test[pos][i].get_str();
929-
const auto witness_value{TryParseHex<unsigned char>(element)};
930-
if (!witness_value.has_value()) {
931-
BOOST_ERROR("Bad witness in test: " << strTest << " witness is not hex: " << element);
931+
// We use #SCRIPT# to flag a non-hex script that we can read using ParseScript
932+
// Taproot script must be third from the last element in witness stack
933+
static const std::string SCRIPT_FLAG{"#SCRIPT#"};
934+
if (element.starts_with(SCRIPT_FLAG)) {
935+
CScript script = ParseScript(element.substr(SCRIPT_FLAG.size()));
936+
witness.stack.push_back(ToByteVector(script));
937+
} else if (element == "#CONTROLBLOCK#") {
938+
// Taproot script control block - second from the last element in witness stack
939+
// If #CONTROLBLOCK# we auto-generate the control block
940+
taprootBuilder.Add(/*depth=*/0, witness.stack.back(), TAPROOT_LEAF_TAPSCRIPT, /*track=*/true);
941+
taprootBuilder.Finalize(XOnlyPubKey(keys.key0.GetPubKey()));
942+
auto controlblocks = taprootBuilder.GetSpendData().scripts[{witness.stack.back(), TAPROOT_LEAF_TAPSCRIPT}];
943+
witness.stack.push_back(*(controlblocks.begin()));
944+
} else {
945+
const auto witness_value{TryParseHex<unsigned char>(element)};
946+
if (!witness_value.has_value()) {
947+
BOOST_ERROR("Bad witness in test: " << strTest << " witness is not hex: " << element);
948+
}
949+
witness.stack.push_back(witness_value.value());
932950
}
933-
witness.stack.push_back(witness_value.value());
934951
}
935952
nValue = AmountFromValue(test[pos][i]);
936953
pos++;
@@ -945,7 +962,14 @@ BOOST_AUTO_TEST_CASE(script_json_test)
945962
std::string scriptSigString = test[pos++].get_str();
946963
CScript scriptSig = ParseScript(scriptSigString);
947964
std::string scriptPubKeyString = test[pos++].get_str();
948-
CScript scriptPubKey = ParseScript(scriptPubKeyString);
965+
CScript scriptPubKey;
966+
// If requested, auto-generate the taproot output
967+
if (scriptPubKeyString == "0x51 0x20 #TAPROOTOUTPUT#") {
968+
BOOST_CHECK_MESSAGE(taprootBuilder.IsComplete(), "Failed to autogenerate Tapscript output key");
969+
scriptPubKey = CScript() << OP_1 << ToByteVector(taprootBuilder.GetOutput());
970+
} else {
971+
scriptPubKey = ParseScript(scriptPubKeyString);
972+
}
949973
unsigned int scriptflags = ParseScriptFlags(test[pos++].get_str());
950974
int scriptError = ParseScriptError(test[pos++].get_str());
951975

0 commit comments

Comments
 (0)