diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index f9c58c0db19db0..8423459fb4af3c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1248,6 +1248,17 @@ bool EvalScript(std::vector >& stack, const CScript& } break; + case OP_INTERNALKEY: { + // OP_INTERNALKEY is only available in Tapscript + if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE); + if (flags & SCRIPT_VERIFY_DISCOURAGE_LNHANCE) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS); + } + + stack.emplace_back(execdata.m_internal_key.begin(), execdata.m_internal_key.end()); + break; + } + default: return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } @@ -1922,6 +1933,9 @@ static bool ExecuteWitnessScript(const Span& stack_span, const CS // Note how this condition would not be reached if an unknown OP_SUCCESSx was found return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } + if ((flags & SCRIPT_VERIFY_LNHANCE) && opcode == OP_INTERNALKEY) { + continue; + } // New opcodes will be listed here. May use a different sigversion to modify existing opcodes. if (IsOpSuccess(opcode)) { if (flags & SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS) { @@ -1982,7 +1996,6 @@ uint256 ComputeTaprootMerkleRoot(Span control, const uint25 static bool VerifyTaprootCommitment(const std::vector& control, const std::vector& program, const uint256& tapleaf_hash) { - assert(control.size() >= TAPROOT_CONTROL_BASE_SIZE); assert(program.size() >= uint256::size()); //! The internal pubkey (x-only, so no Y coordinate parity). const XOnlyPubKey p{Span{control}.subspan(1, TAPROOT_CONTROL_BASE_SIZE - 1)}; @@ -2050,6 +2063,9 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, if (control.size() < TAPROOT_CONTROL_BASE_SIZE || control.size() > TAPROOT_CONTROL_MAX_SIZE || ((control.size() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE) != 0) { return set_error(serror, SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE); } + assert(control.size() >= TAPROOT_CONTROL_BASE_SIZE); + execdata.m_internal_key = uint256{Span{control}.subspan(1, TAPROOT_CONTROL_BASE_SIZE - 1)}; + execdata.m_internal_key_init = true; execdata.m_tapleaf_hash = ComputeTapleafHash(control[0] & TAPROOT_LEAF_MASK, script); if (!VerifyTaprootCommitment(control, program, execdata.m_tapleaf_hash)) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 34a4e9355ce8a1..881844b6a8fed0 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -227,6 +227,10 @@ struct ScriptExecutionData //! The tapleaf hash. uint256 m_tapleaf_hash; + //! Whether m_internal_key is initialized. + bool m_internal_key_init = false; + uint256 m_internal_key; + //! Whether m_codeseparator_pos is initialized. bool m_codeseparator_pos_init = false; //! Opcode position of the last executed OP_CODESEPARATOR (or 0xFFFFFFFF if none executed). diff --git a/src/script/script.cpp b/src/script/script.cpp index d9533762930395..bd1f66fab0dc28 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -146,6 +146,8 @@ std::string GetOpName(opcodetype opcode) // Opcode added by BIP 342 (Tapscript) case OP_CHECKSIGADD : return "OP_CHECKSIGADD"; + case OP_INTERNALKEY : return "OP_INTERNALKEY"; + case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; default: diff --git a/src/script/script.h b/src/script/script.h index a1c56fc7251814..b65189c0e6cc3d 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -207,6 +207,7 @@ enum opcodetype // Opcode added by BIP 342 (Tapscript) OP_CHECKSIGADD = 0xba, + OP_INTERNALKEY = 0xbb, OP_INVALIDOPCODE = 0xff, }; diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index 21ef1fab61b893..f931a7613097ab 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -668,5 +668,13 @@ "0200000002d58631133c4d4f6188abbd0fa0a7aa64bfde05ce4297e3349b38599ceebaf2e20000000001510000000082b77fb944a40756a6341ee425c85d5850f2acbe6d51a7bcd211929a764b8ec8000000000100000000000ae80300000000000017a914d63e77972529f4db5b32efaa4e06f66ae0b5dc0987d00700000000000017a91488b6705f8c9568c52b55ed712c257f84f64a49f587b80b00000000000017a9142be57e9a179f8d9ff8f33a788d4b54512ea9e36087a00f00000000000017a91429261b4f65796f618908de9f51669014e2e2e04f87881300000000000017a914e3a1e1d24cbba3ca9369248082988bad3ceafcfb87701700000000000017a91403801b0a9591f3b5a00a5ea60fb34dc12b4a691187581b00000000000017a91465248bc2c732db2d88db0b0d677c1514b101025b87401f00000000000017a91420021e3dc4e80c7192c1a3cd04026d22d0f8d38287282300000000000017a914df27596dbff2028791bd7692846e65d16d8fed0d87102700000000000017a9142ed128e911cab04d3277d3635f79d5e3d7e6f4c48700000000", "CLEANSTACK,DISCOURAGE_LNHANCE"], + ["Test OP_INTERNALKEY"], + [[["e2f2baee9c59389b34e39742ce05debf64aaa7a00fbdab88614f4d3c133186d5", + 0, + "1 0x20 0x15887b8f5cc1e8d98ffa7af368d0159cfbee6de780d012f7379ba12d363900e3", + 155000]], +"02000000000101d58631133c4d4f6188abbd0fa0a7aa64bfde05ce4297e3349b38599ceebaf2e20000000000ffffffff01f0490200000000002251202ca3bc76489a54904ad2507005789afc1e6b362b451be89f69de39ddf9ba8abf0223bb2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac08721c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000", + "DISCOURAGE_LNHANCE"], + ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 644eaa26c4fc8c..e1ffec17fe735c 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -151,6 +151,9 @@ bool CheckTxScripts(const CTransaction& tx, const std::map& unsigned int TrimFlags(unsigned int flags) { + // !LNHANCE requires !DISCOURGE_OP_SUCCESS + if (!(flags & SCRIPT_VERIFY_LNHANCE)) flags &= ~(unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS; + // WITNESS requires P2SH if (!(flags & SCRIPT_VERIFY_P2SH)) flags &= ~(unsigned int)SCRIPT_VERIFY_WITNESS; diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index a0bbede2f020d0..6706d37f0766ff 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -925,4 +925,4 @@ def taproot_construct(pubkey, scripts=None, treat_internal_as_infinity=False): return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves, h, tweaked) def is_op_success(o): - return o == 0x50 or o == 0x62 or o == 0x89 or o == 0x8a or o == 0x8d or o == 0x8e or (o >= 0x7e and o <= 0x81) or (o >= 0x83 and o <= 0x86) or (o >= 0x95 and o <= 0x99) or (o >= 0xbb and o <= 0xfe) + return o == 0x50 or o == 0x62 or o == 0x89 or o == 0x8a or o == 0x8d or o == 0x8e or (o >= 0x7e and o <= 0x81) or (o >= 0x83 and o <= 0x86) or (o >= 0x95 and o <= 0x99) or (o >= 0xbc and o <= 0xfe)