diff --git a/src/consensus/params.h b/src/consensus/params.h index 628fb858c2f6..fee284014ed4 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -35,6 +35,7 @@ enum DeploymentPos : uint16_t { DEPLOYMENT_CHECKTEMPLATEVERIFY, // Deployment of CTV (BIP 119) DEPLOYMENT_ANYPREVOUT, DEPLOYMENT_OP_CAT, + DEPLOYMENT_INTERNALKEY, // NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp MAX_VERSION_BITS_DEPLOYMENTS }; diff --git a/src/deploymentinfo.cpp b/src/deploymentinfo.cpp index 8b5736f17b18..bc7d0e7987f3 100644 --- a/src/deploymentinfo.cpp +++ b/src/deploymentinfo.cpp @@ -27,6 +27,10 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B /*.name =*/ "opcat", /*.gbt_force =*/ true, }, + { + /*.name =*/ "internalkey", + /*.gbt_force =*/ true, + }, }; std::string DeploymentName(Consensus::BuriedDeployment dep) @@ -91,6 +95,8 @@ const std::map g_verify_flag_names{ FLAG_NAME(DEFAULT_CHECK_TEMPLATE_VERIFY_HASH), FLAG_NAME(DISCOURAGE_UPGRADABLE_CHECK_TEMPLATE_VERIFY_HASH), FLAG_NAME(DISCOURAGE_CHECK_TEMPLATE_VERIFY_HASH), + FLAG_NAME(DISCOURAGE_INTERNALKEY), + FLAG_NAME(INTERNALKEY), FLAG_NAME(ANYPREVOUT), FLAG_NAME(DISCOURAGE_ANYPREVOUT), FLAG_NAME(OP_CAT), diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 71dcbe30db6d..caf49cd564ce 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -145,6 +145,7 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY] = SetupDeployment{.activate = 0x60007700, .abandon = 0x40007700, .never = true}; consensus.vDeployments[Consensus::DEPLOYMENT_ANYPREVOUT] = SetupDeployment{.activate = 0x60007600, .abandon = 0x40007600, .never = true}; consensus.vDeployments[Consensus::DEPLOYMENT_OP_CAT] = SetupDeployment{.activate = 0x62000100, .abandon = 0x42000100, .never = true}; + consensus.vDeployments[Consensus::DEPLOYMENT_INTERNALKEY] = SetupDeployment{.activate = 0x62000100, .abandon = 0x42000100, .never = true}; consensus.nMinimumChainWork = uint256{"000000000000000000000000000000000000000088e186b70e0862c193ec44d6"}; consensus.defaultAssumeValid = uint256{"000000000000000000011c5890365bdbe5d25b97ce0057589acaef4f1a57263f"}; // 856760 @@ -264,6 +265,7 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY] = SetupDeployment{.activate = 0x60007700, .abandon = 0x40007700, .never = true}; consensus.vDeployments[Consensus::DEPLOYMENT_ANYPREVOUT] = SetupDeployment{.activate = 0x60007600, .abandon = 0x40007600, .never = true}; consensus.vDeployments[Consensus::DEPLOYMENT_OP_CAT] = SetupDeployment{.activate = 0x62000100, .abandon = 0x42000100, .never = true}; +consensus.vDeployments[Consensus::DEPLOYMENT_INTERNALKEY] = SetupDeployment{.activate = 0x62000100, .abandon = 0x42000100, .never = true}; consensus.nMinimumChainWork = uint256{"000000000000000000000000000000000000000000000f209695166be8b61fa9"}; consensus.defaultAssumeValid = uint256{"000000000000000465b1a66c9f386308e8c75acef9201f3f577811da09fc90ad"}; // 2873500 @@ -508,6 +510,7 @@ class SigNetParams : public CChainParams { .activate = 0x62000100, .abandon = 0x42000100, }; + consensus.vDeployments[Consensus::DEPLOYMENT_INTERNALKEY] = SetupDeployment{.activate = 0x62000100, .abandon = 0x42000100, .never = true}; RenounceDeployments(options.renounce, consensus.vDeployments); @@ -584,6 +587,7 @@ class CRegTestParams : public CChainParams consensus.vDeployments[Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY] = SetupDeployment{.activate = 0x60007700, .abandon = 0x40007700, .always = true}; consensus.vDeployments[Consensus::DEPLOYMENT_ANYPREVOUT] = SetupDeployment{.activate = 0x60007600, .abandon = 0x40007600, .always = true}; consensus.vDeployments[Consensus::DEPLOYMENT_OP_CAT] = SetupDeployment{.activate = 0x62000100, .abandon = 0x42000100, .always = true}; +consensus.vDeployments[Consensus::DEPLOYMENT_INTERNALKEY] = SetupDeployment{.activate = 0x62000100, .abandon = 0x42000100, .always = true}; consensus.nMinimumChainWork = uint256{}; consensus.defaultAssumeValid = uint256{}; diff --git a/src/policy/policy.h b/src/policy/policy.h index ee93555f8e1d..4ff4643e1b9c 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -121,6 +121,7 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERI SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_CHECK_TEMPLATE_VERIFY_HASH | SCRIPT_VERIFY_DEFAULT_CHECK_TEMPLATE_VERIFY_HASH | SCRIPT_VERIFY_ANYPREVOUT | + SCRIPT_VERIFY_INTERNALKEY | SCRIPT_VERIFY_OP_CAT }; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index ef05d4c3d2d2..a2e2a1fd765b 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1396,6 +1396,7 @@ UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager& SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY); SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_ANYPREVOUT); SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_OP_CAT); + SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_INTERNALKEY); return softforks; } } // anon namespace diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index b113efb54d55..fab6b219b0b6 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1283,6 +1283,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); + } + // Always present in Tapscript + assert(execdata.m_internal_key); + stack.emplace_back(execdata.m_internal_key->begin(), execdata.m_internal_key->end()); + break; + } + default: return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } @@ -1988,6 +1999,12 @@ std::optional CheckTapscriptOpSuccess(const CScript& exec_script, unsigned } else if (!(flags & SCRIPT_VERIFY_OP_CAT)) { return set_success(serror); } + } else if (opcode == OP_INTERNALKEY) { + if (flags & SCRIPT_VERIFY_DISCOURAGE_INTERNALKEY) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS); + } else if (!(flags & SCRIPT_VERIFY_INTERNALKEY)) { + return set_success(serror); + } } else { // OP_SUCCESS behaviour if (flags & SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS) { diff --git a/src/script/interpreter.h b/src/script/interpreter.h index ff041ca611ee..4e31354392d2 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -163,6 +163,12 @@ enum : uint32_t { SCRIPT_VERIFY_OP_CAT = (1U << 26), SCRIPT_VERIFY_DISCOURAGE_OP_CAT = (1U << 27), + // Executing OP_INTERNALKEY + SCRIPT_VERIFY_INTERNALKEY = (1U << 30), + + // Making OP_INTERNALKEY non-standard + SCRIPT_VERIFY_DISCOURAGE_INTERNALKEY = (1U << 31), + // Constants to point to the highest flag in use. Add new flags above this line. // SCRIPT_VERIFY_END_MARKER diff --git a/src/script/script.cpp b/src/script/script.cpp index fc77d70a9e9e..fb63ff45fa42 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -149,6 +149,9 @@ std::string GetOpName(opcodetype opcode) // Opcode added by BIP 342 (Tapscript) case OP_CHECKSIGADD : return "OP_CHECKSIGADD"; + // Opcode added by BIP 349 + 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 1bd615c6e816..ce6db09292a0 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -209,6 +209,9 @@ enum opcodetype // Opcode added by BIP 342 (Tapscript) OP_CHECKSIGADD = 0xba, + // Opcode added by BIP 349 + OP_INTERNALKEY = 0xcb, + OP_INVALIDOPCODE = 0xff, }; diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index e087557925c9..f4bb33133ebc 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -672,5 +672,13 @@ "0200000002d58631133c4d4f6188abbd0fa0a7aa64bfde05ce4297e3349b38599ceebaf2e20000000001510000000082b77fb944a40756a6341ee425c85d5850f2acbe6d51a7bcd211929a764b8ec8000000000100000000000ae80300000000000017a914d63e77972529f4db5b32efaa4e06f66ae0b5dc0987d00700000000000017a91488b6705f8c9568c52b55ed712c257f84f64a49f587b80b00000000000017a9142be57e9a179f8d9ff8f33a788d4b54512ea9e36087a00f00000000000017a91429261b4f65796f618908de9f51669014e2e2e04f87881300000000000017a914e3a1e1d24cbba3ca9369248082988bad3ceafcfb87701700000000000017a91403801b0a9591f3b5a00a5ea60fb34dc12b4a691187581b00000000000017a91465248bc2c732db2d88db0b0d677c1514b101025b87401f00000000000017a91420021e3dc4e80c7192c1a3cd04026d22d0f8d38287282300000000000017a914df27596dbff2028791bd7692846e65d16d8fed0d87102700000000000017a9142ed128e911cab04d3277d3635f79d5e3d7e6f4c48700000000", "CLEANSTACK,DISCOURAGE_CHECK_TEMPLATE_VERIFY_HASH"], + ["Test OP_INTERNALKEY"], + [[["e2f2baee9c59389b34e39742ce05debf64aaa7a00fbdab88614f4d3c133186d5", + 0, + "1 0x20 0xa9e62de0f9782710f702214fc81c0f0f90fb3537987b3685caad6d52db305447", + 155000]], +"02000000000101d58631133c4d4f6188abbd0fa0a7aa64bfde05ce4297e3349b38599ceebaf2e20000000000ffffffff01f0490200000000002251202ca3bc76489a54904ad2507005789afc1e6b362b451be89f69de39ddf9ba8abf0223cb2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac08721c050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000", + "DISCOURAGE_INTERNALKEY"], + ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 40b4830c2f94..bcbad4756176 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1747,7 +1747,7 @@ BOOST_AUTO_TEST_CASE(formatscriptflags) { // quick check that FormatScriptFlags reports any unknown/unexpected bits BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_P2SH), "P2SH"); - BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_P2SH | (1u<<31)), "P2SH,0x80000000"); + BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_P2SH | (1u<<29)), "P2SH,0x20000000"); BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_TAPROOT | (1u<<28)), "TAPROOT,0x10000000"); BOOST_CHECK_EQUAL(FormatScriptFlags(1u<<28), "0x10000000"); } diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index af36a9569315..06717fc044f9 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -130,7 +130,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail TxValidationState state; // Randomly selects flag combinations - uint32_t test_flags = (uint32_t) insecure_rand.randrange((SCRIPT_VERIFY_END_MARKER - 1) << 1); + uint32_t test_flags = (uint32_t) insecure_rand.randrange((uint64_t)(SCRIPT_VERIFY_END_MARKER - 1) << 1); // Filter out incompatible flag choices if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) { diff --git a/src/validation.cpp b/src/validation.cpp index 261549219d35..eb5138bc0cd0 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1255,6 +1255,7 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws) { Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY, SCRIPT_VERIFY_DISCOURAGE_CHECK_TEMPLATE_VERIFY_HASH }, { Consensus::DEPLOYMENT_ANYPREVOUT, SCRIPT_VERIFY_DISCOURAGE_ANYPREVOUT }, { Consensus::DEPLOYMENT_OP_CAT, SCRIPT_VERIFY_DISCOURAGE_OP_CAT }, + { Consensus::DEPLOYMENT_INTERNALKEY, SCRIPT_VERIFY_DISCOURAGE_INTERNALKEY }, }); // Check input scripts and signatures. @@ -2429,6 +2430,11 @@ unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Chainstat flags |= SCRIPT_VERIFY_OP_CAT; } + // Enforce OP_INTERNALKEY (BIP349) + if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_INTERNALKEY)) { + flags |= SCRIPT_VERIFY_INTERNALKEY; + } + return flags; } diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py index f45a11dd6c59..bcbc600765cd 100755 --- a/test/functional/feature_taproot.py +++ b/test/functional/feature_taproot.py @@ -65,6 +65,7 @@ OP_EQUAL, OP_EQUALVERIFY, OP_IF, + OP_INTERNALKEY, OP_NOP, OP_NOT, OP_NOTIF, @@ -660,6 +661,22 @@ def byte_popper(expr): # === Actual test cases === +def spenders_internalkey_active(): + + secs = [generate_privkey() for _ in range(8)] + pubs = [compute_xonly_pubkey(sec)[0] for sec in secs] + + spenders = [] + + scripts = [ + ("ik", CScript([OP_INTERNALKEY, OP_EQUAL])), + ] + + tap = taproot_construct(pubs[0], scripts) + + add_spender(spenders, "ik/success", tap=tap, leaf="ik", inputs=[pubs[0]], failure={"inputs": [pubs[1]]}) + + return spenders def spenders_taproot_active(): """Return a list of Spenders for testing post-Taproot activation behavior.""" @@ -1784,7 +1801,7 @@ def run_test(self): self.gen_test_vectors() self.log.info("Post-activation tests...") - self.test_spenders(self.nodes[0], spenders_taproot_active(), input_counts=[1, 2, 2, 2, 2, 3]) + self.test_spenders(self.nodes[0], spenders_taproot_active() + spenders_internalkey_active(), input_counts=[1, 2, 2, 2, 2, 3]) # Run each test twice; once in isolation, and once combined with others. Testing in isolation # means that the standardness is verified in every test (as combined transactions are only standard # when all their inputs are standard). diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index f54fd0851b1d..cad8ad6537f9 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -196,7 +196,7 @@ def check_signalling_deploymentinfo_result(self, gdi_result, height, blockhash): assert_equal(gdi_result, { "hash": blockhash, "height": height, - "script_flags": ["ANYPREVOUT","CHECKLOCKTIMEVERIFY","CHECKSEQUENCEVERIFY","DEFAULT_CHECK_TEMPLATE_VERIFY_HASH","DERSIG","NULLDUMMY","OP_CAT","P2SH","TAPROOT","WITNESS"], + "script_flags": ["ANYPREVOUT","CHECKLOCKTIMEVERIFY","CHECKSEQUENCEVERIFY","DEFAULT_CHECK_TEMPLATE_VERIFY_HASH","DERSIG", "INTERNALKEY", "NULLDUMMY","OP_CAT","P2SH","TAPROOT","WITNESS"], "deployments": { 'bip34': {'type': 'buried', 'active': True, 'height': 2}, 'bip66': {'type': 'buried', 'active': True, 'height': 3}, @@ -261,7 +261,21 @@ def check_signalling_deploymentinfo_result(self, gdi_result, height, blockhash): 'height': 0, 'active': True, }, - } + "internalkey": { + "type": "heretical", + "height": 0, + "active": True, + "heretical": { + "binana-id": "BIN-2024-0001-000", + "start_time": -1, + "timeout": 9223372036854775807, + "period": 144, + "status": "active", + "since": 0, + "status_next": "active" + } + } + } }) def _test_getdeploymentinfo(self): diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index b386c452f77a..5081b6bbf8b1 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -254,6 +254,8 @@ def __new__(cls, n): # BIP 342 opcodes (Tapscript) OP_CHECKSIGADD = CScriptOp(0xba) +OP_INTERNALKEY = CScriptOp(0xcb) + OP_INVALIDOPCODE = CScriptOp(0xff) OPCODE_NAMES.update({ @@ -969,4 +971,6 @@ def is_op_success(o): # assume OP_CAT is activated in tests if o == OP_CAT: return False + if o == OP_INTERNALKEY: + return False 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)