From a8bf3f4e6d3cf592cfd6fbefd86e77b6b3de8933 Mon Sep 17 00:00:00 2001 From: Armin Sabouri Date: Sat, 18 May 2024 17:40:30 -0400 Subject: [PATCH] tests: add unit tests for OP_CAT This commit introduces unit tests for opcat in script_tests.json. Additionally, it 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. --- src/test/data/script_tests.json | 337 ++++++++++++++++++++++++++++++++ src/test/script_tests.cpp | 99 +++++++++- 2 files changed, 434 insertions(+), 2 deletions(-) diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json index ad05240369b315..15abf3d650d9db 100644 --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -2527,6 +2527,343 @@ ["0", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "UNSATISFIED_LOCKTIME", "CSV fails if stack top bit 1 << 31 is set and the tx version < 2"], ["0x050000000001", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "UNSATISFIED_LOCKTIME", "CSV fails if stack top bit 1 << 31 is not set, and tx version < 2"], +["OP_CAT tests"], +[ + [ + "#SCRIPT# CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT", + "OK", + "TAPSCRIPT (CAT) Test of OP_CAT flag by calling CAT on an empty stack. This does not error because no TAPSCRIPT_OP_CAT flag is set so CAT is OP_SUCCESS" +], +[ + [ + "#SCRIPT# CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "INVALID_STACK_OPERATION", + "TAPSCRIPT Test of OP_CAT flag by calling CAT on an empty stack. This throws an error because TAPSCRIPT_OP_CAT flag is set so CAT is executed" +], +[ + [ + "aa", + "bb", + "#SCRIPT# CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "OK", + "TAPSCRIPT Test of OP_CAT flag by calling CAT on two elements. TAPSCRIPT_OP_CAT flag is set so CAT is executed." +], +[ + [ + "78a11a1260c1101260", + "78a11a1260", + "c1101260", + "#SCRIPT# CAT EQUAL", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "OK", + "TAPSCRIPT CATs 78a11a1260 and c1101260 together and checks it is EQUAL to stack element 78a11a1260c1101260" +], +[ + [ + "78a11a1260c1101260", + "78a11a1260", + "c1101260", + "#SCRIPT# CAT EQUAL", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT", + "OK", + "TAPSCRIPT Test of OP_CAT flag, CATs 78a11a1260 and c1101260 together and checks it is EQUAL to stack element 78a11a1260c1101260. No TAPSCRIPT_OP_CAT set so CAT should be OP_SUCCESS." +], +[ + [ + "", + "78a11a1260", + "c1101260", + "#SCRIPT# CAT EQUAL", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "EVAL_FALSE", + "TAPSCRIPT CATs 78a11a1260 and c1101260 together and checks it is EQUAL to the empty stack element" +], +[ + [ + "51", + "78a11a1260c1101260", + "78a11a1260", + "c1101260", + "#SCRIPT# CAT EQUALVERIFY", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "OK", + "TAPSCRIPT CATS 78a11a1260 and c1101260 together and checks it is EQUALVERIFY to stack element 78a11a1260c1101260" +], +[ + [ + "51", + "c110126078a11a1260", + "78a11a1260", + "c1101260", + "#SCRIPT# CAT EQUALVERIFY", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "EQUALVERIFY", + "TAPSCRIPT CATs 78a11a1260 and c1101260 together and checks it is EQUALVERIFY to stack element c110126078a11a1260" +], +[ + [ + "aa", + "bb", + "#SCRIPT# CAT 0x4c 0x02 0xaabb EQUAL", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "OK", + "TAPSCRIPT CATs aa and bb together and checks EQUAL to aabb" +], +[ + [ + "eeffeeff", + "aa", + "bbff", + "#SCRIPT# CAT CAT DUP DROP 0x4c 0x07 0xeeffeeffaabbff EQUAL", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "OK", + "TAPSCRIPT CATs aa and bbcc and eeffeff together and checks EQUAL to eeffeeffaabbcc" +], +[ + [ + "c24f2c1e363e09a5dd56f0", + "89a0385490a11b6dc6740f3513", + "#SCRIPT# CAT 0x4c 0x18 0xc24f2c1e363e09a5dd56f089a0385490a11b6dc6740f3513 EQUAL", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "OK", + "TAPSCRIPT Tests CAT on different sized random stack elements and compares the result." +], +[ + [ + "f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93", + "f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93", + "#SCRIPT# CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "OK", + "TAPSCRIPT Tests CAT on two hash outputs" +], +[ + [ + "51", + "bbbb", + "01", + "#SCRIPT# IF CAT ELSE DROP ENDIF", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "OK", + "TAPSCRIPT Tests CAT inside of an IF ELSE conditional (true IF)" +], +[ + [ + "51", + "", + "#SCRIPT# IF CAT ELSE DROP ENDIF", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "CLEANSTACK", + "TAPSCRIPT Tests CAT inside of an IF ELSE conditional (false IF)" +], +[ + [ + "1a1a", + "#SCRIPT# DUP CAT DUP CAT DUP CAT DUP CAT DUP CAT DUP CAT DUP CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "OK", + "TAPSCRIPT Runs DUP CAT seven times on 1a1a" +], +[ + [ + "1a1a1a1a1a1a1a", + "#SCRIPT# DUP CAT DUP CAT DUP CAT DUP CAT DUP CAT DUP CAT DUP CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "PUSH_SIZE", + "TAPSCRIPT Runs DUP CAT seven times on 1a1a1a1a1a1a1a triggering a stack size error as the result is larger than max stack element size" +], +[ + [ + "1ffe1234567890", + "00", + "#SCRIPT# HASH256 DUP SHA1 CAT DUP CAT TOALTSTACK HASH256 DUP CAT TOALTSTACK FROMALTSTACK", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "OK", + "TAPSCRIPT Tests CAT with a melange of other opcodes including FROMALTSTACK. " +], +[ + [ + "#SCRIPT# CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "INVALID_STACK_OPERATION", + "TAPSCRIPT ([], CAT) Tests CAT fails on empty stack" +], +[ + [ + "09ca7009ca7009ca7009ca7009ca70", + "#SCRIPT# CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "INVALID_STACK_OPERATION", + "TAPSCRIPT ([09ca7009ca7009ca7009ca7009ca70], CAT) Tests CAT fails on a stack of only one element" +], +[ + [ + "", + "09ca7009ca7009ca7009ca7009ca70", + "#SCRIPT# CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "OK", + "TAPSCRIPT (['', 09ca7009ca7009ca7009ca7009ca70], CAT) Tests CAT succeeds when one of the two values to concatenate is of size zero" +], +[ + [ + "f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93", + "0102030405060708", + "#SCRIPT# CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "OK", + "TAPSCRIPT ([512 byte element, 09ca7009ca7009ca7009ca7009ca70], CAT) Tests edge case where concatenated value is exactly max stack element size (520 bytes)" +], +[ + [ + "f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b930102030405060708", + "01", + "#SCRIPT# CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "PUSH_SIZE", + "TAPSCRIPT ([520 byte element, 01], CAT) Tests edge case where concatenated value is one byte larger than max stack element size (520 bytes)" +], +[ + [ + "f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b930102030405060708", + "f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b930102030405060708", + "#SCRIPT# CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "PUSH_SIZE", + "TAPSCRIPT ([520 byte element, 520 byte element], CAT) Tests case where each element to concatenate is exactly max stack element size (520 bytes)" +], +[ + [ + "f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b930102030405060708", + "f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93f821125522f9490bcd108cdd0effbb002d45c6e66e6b48aeb51c865743796b93010203040506070809", + "#SCRIPT# CAT", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT,OP_CAT", + "PUSH_SIZE", + "TAPSCRIPT ([520 byte element, 521 byte element], CAT) Tests edge case where one of the elements to concatenate is one byte larger than max stack element size (520 bytes)" +], ["MINIMALIF tests"], ["MINIMALIF is not applied to non-segwit scripts"], diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 531ea268ef19f2..92e84d65f6e4f1 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -915,16 +915,34 @@ BOOST_AUTO_TEST_CASE(script_json_test) // amount (nValue) to use in the crediting tx UniValue tests = read_json(json_tests::script_tests); + const KeyData keys; for (unsigned int idx = 0; idx < tests.size(); idx++) { const UniValue& test = tests[idx]; std::string strTest = test.write(); CScriptWitness witness; + TaprootBuilder taprootBuilder; CAmount nValue = 0; unsigned int pos = 0; if (test.size() > 0 && test[pos].isArray()) { unsigned int i=0; for (i = 0; i < test[pos].size()-1; i++) { - witness.stack.push_back(ParseHex(test[pos][i].get_str())); + auto element = test[pos][i].get_str(); + // We use #SCRIPT# to flag a non-hex script that we can read using ParseScript + // Taproot script must be third from the last element in witness stack + std::string scriptFlag = std::string("#SCRIPT#"); + if (element.find(scriptFlag) == 0) { + CScript script = ParseScript(element.substr(scriptFlag.size())); + witness.stack.push_back(ToByteVector(script)); + } else if (strcmp(element.c_str(), "#CONTROLBLOCK#") == 0) { + // Taproot script control block - second from the last element in witness stack + // If #CONTROLBLOCK# we auto-generate the control block + taprootBuilder.Add(/*depth=*/0, witness.stack.back(), TAPROOT_LEAF_TAPSCRIPT, /*track=*/true); + taprootBuilder.Finalize(XOnlyPubKey(keys.key0.GetPubKey())); + auto controlblocks = taprootBuilder.GetSpendData().scripts[{witness.stack.back(), TAPROOT_LEAF_TAPSCRIPT}]; + witness.stack.push_back(*(controlblocks.begin())); + } else { + witness.stack.push_back(ParseHex(element)); + } } nValue = AmountFromValue(test[pos][i]); pos++; @@ -939,7 +957,14 @@ BOOST_AUTO_TEST_CASE(script_json_test) std::string scriptSigString = test[pos++].get_str(); CScript scriptSig = ParseScript(scriptSigString); std::string scriptPubKeyString = test[pos++].get_str(); - CScript scriptPubKey = ParseScript(scriptPubKeyString); + CScript scriptPubKey; + // If requested, auto-generate the taproot output + if (strcmp(scriptPubKeyString.c_str(), "0x51 0x20 #TAPROOTOUTPUT#")== 0) { + BOOST_CHECK_MESSAGE(taprootBuilder.IsComplete(), "Failed to autogenerate Tapscript Script PubKey"); + scriptPubKey = CScript() << OP_1 << ToByteVector(taprootBuilder.GetOutput()); + } else { + scriptPubKey = ParseScript(scriptPubKeyString); + } unsigned int scriptflags = ParseScriptFlags(test[pos++].get_str()); int scriptError = ParseScriptError(test[pos++].get_str()); @@ -1710,4 +1735,74 @@ BOOST_AUTO_TEST_CASE(compute_tapleaf) BOOST_CHECK_EQUAL(ComputeTapleafHash(0xc2, Span(script)), tlc2); } +void DoTapscriptTest(std::vector witVerifyScript, std::vector> witData, const std::string& message, int scriptError) +{ + const KeyData keys; + TaprootBuilder builder; + builder.Add(/*depth=*/0, witVerifyScript, TAPROOT_LEAF_TAPSCRIPT, /*track=*/true); + builder.Finalize(XOnlyPubKey(keys.key0.GetPubKey())); + + CScriptWitness witness; + witness.stack.insert(witness.stack.begin(), witData.begin(), witData.end()); + witness.stack.push_back(witVerifyScript); + auto controlblock = *(builder.GetSpendData().scripts[{witVerifyScript, TAPROOT_LEAF_TAPSCRIPT}].begin()); + witness.stack.push_back(controlblock); + + uint32_t flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_TAPROOT | SCRIPT_VERIFY_OP_CAT; + CScript scriptPubKey = CScript() << OP_1 << ToByteVector(builder.GetOutput()); + CScript scriptSig = CScript(); // Script sig is always size 0 and empty in tapscript + DoTest(scriptPubKey, scriptSig, witness, flags, message, scriptError, /*nValue=*/1); +} + +BOOST_AUTO_TEST_CASE(cat_simple) +{ + std::vector> witData; + witData.emplace_back(ParseHex("aa")); + witData.emplace_back(ParseHex("bbbb")); + std::vector witVerifyScript = {OP_CAT, OP_PUSHDATA1, 0x03, 0xaa, 0xbb, 0xbb, OP_EQUAL}; + DoTapscriptTest(witVerifyScript, witData, "Simple CAT", SCRIPT_ERR_OK); +} + + +BOOST_AUTO_TEST_CASE(cat_empty_stack) +{ + // Ensures that OP_CAT successfully handles concatenating two empty stack elements + std::vector> witData; + witData.emplace_back(); + witData.emplace_back(); + std::vector witVerifyScript = {OP_CAT, OP_PUSHDATA1, 0x00,OP_EQUAL}; + DoTapscriptTest(witVerifyScript, witData, "CAT empty stack", SCRIPT_ERR_OK); +} + +BOOST_AUTO_TEST_CASE(cat_dup_test) +{ + // CAT DUP exhaustion attacks using all element sizes from 1 to 522 + // with CAT DUP repetitions up to 10. Ensures the correct error is thrown + // or not thrown, as appropriate. + unsigned int maxElementSize = 522; + unsigned int maxDupsToCheck = 10; + + std::vector> witData; + witData.emplace_back(); + for (unsigned int elementSize = 1; elementSize <= maxElementSize; elementSize++) { + std::vector witVerifyScript; + // increase the size of stack element by one byte + witData.at(0).emplace_back(0x1A); + for (unsigned int dups = 1; dups <= maxDupsToCheck; dups++) { + witVerifyScript.emplace_back(OP_DUP); + witVerifyScript.emplace_back(OP_CAT); + int expectedErr = SCRIPT_ERR_OK; + unsigned int catedStackElementSize = witData.at(0).size()< MAX_SCRIPT_ELEMENT_SIZE || elementSize > MAX_SCRIPT_ELEMENT_SIZE){ + expectedErr = SCRIPT_ERR_PUSH_SIZE; + break; + } + DoTapscriptTest(witVerifyScript, witData, "CAT DUP test", expectedErr); + // Once we hit the stack element size limit, break + if (expectedErr == SCRIPT_ERR_OK) + break; + } + } +} + BOOST_AUTO_TEST_SUITE_END()