diff --git a/src/addresstype.cpp b/src/addresstype.cpp index 67e643943d4..66d6c47ff9a 100644 --- a/src/addresstype.cpp +++ b/src/addresstype.cpp @@ -98,6 +98,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) case TxoutType::MULTISIG: case TxoutType::NULL_DATA: case TxoutType::NONSTANDARD: + case TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY: addressRet = CNoDestination(scriptPubKey); return false; } // no default case, so the compiler can warn about missing cases diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 32a31ea653c..b2c0abf12b8 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -212,6 +212,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) if (subscript.GetSigOpCount(true) > MAX_P2SH_SIGOPS) { return false; } + } else if (whichType == TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY) { + // after activation, only allow bare with no scriptsig. + // pre-activation disallowing enforced via discouraged logic in the + // interpreter. + if (tx.vin[i].scriptSig.size() != 0) return false; } } diff --git a/src/policy/policy.h b/src/policy/policy.h index 2151ec13dd0..f4fe5e33af1 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -124,7 +124,10 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERI SCRIPT_VERIFY_CONST_SCRIPTCODE | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION | SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS | - SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE}; + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE | + SCRIPT_VERIFY_DISCOURAGE_CHECKTEMPLATEVERIFY | + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_CHECKTEMPLATEVERIFY | + SCRIPT_VERIFY_CHECKTEMPLATEVERIFY}; /** For convenience, standard but not mandatory verify flags. */ static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS}; diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 950c7e04004..1c5e4399bae 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -544,6 +544,8 @@ static RPCHelpMan decodescript() case TxoutType::WITNESS_UNKNOWN: case TxoutType::WITNESS_V1_TAPROOT: case TxoutType::ANCHOR: + // don't wrap CTV because P2SH CTV is a hash cycle + case TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY: // Should not be wrapped return false; } // no default case, so the compiler can warn about missing cases @@ -587,6 +589,8 @@ static RPCHelpMan decodescript() case TxoutType::WITNESS_V0_SCRIPTHASH: case TxoutType::WITNESS_V1_TAPROOT: case TxoutType::ANCHOR: + // don't wrap CTV because P2SH CTV is a hash cycle + case TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY: // Should not be wrapped return false; } // no default case, so the compiler can warn about missing cases diff --git a/src/script/script.cpp b/src/script/script.cpp index 4c8fbeeef44..fc77d70a9e9 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -221,6 +221,14 @@ bool CScript::IsPayToAnchor(int version, const std::vector& progr program[1] == 0x73; } +bool CScript::IsPayToBareDefaultCheckTemplateVerifyHash() const +{ + // Extra-fast test for pay-to-bare-default-check-template-verify-hash CScripts: + return (this->size() == 34 && + (*this)[0] == 0x20 && + (*this)[33] == OP_CHECKTEMPLATEVERIFY); +} + bool CScript::IsPayToScriptHash() const { // Extra-fast test for pay-to-script-hash CScripts: diff --git a/src/script/script.h b/src/script/script.h index 5ac07f55296..f42c31caa04 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -553,6 +553,8 @@ public: */ static bool IsPayToAnchor(int version, const std::vector& program); + bool IsPayToBareDefaultCheckTemplateVerifyHash() const; + bool IsPayToScriptHash() const; bool IsPayToWitnessScriptHash() const; bool IsWitnessProgram(int& version, std::vector& program) const; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 33cbc38be41..25793793764 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -412,6 +412,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator case TxoutType::NONSTANDARD: case TxoutType::NULL_DATA: case TxoutType::WITNESS_UNKNOWN: + case TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY: return false; case TxoutType::PUBKEY: if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false; diff --git a/src/script/solver.cpp b/src/script/solver.cpp index 783baf07089..9cd6d34f277 100644 --- a/src/script/solver.cpp +++ b/src/script/solver.cpp @@ -29,6 +29,7 @@ std::string GetTxnOutputType(TxoutType t) case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot"; case TxoutType::WITNESS_UNKNOWN: return "witness_unknown"; + case TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY: return "bare_default_ctv_hash"; } // no default case, so the compiler can warn about missing cases assert(false); } @@ -151,6 +152,10 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector witnessprogram; if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { diff --git a/src/script/solver.h b/src/script/solver.h index d2b7fb88814..2fd7ced47a5 100644 --- a/src/script/solver.h +++ b/src/script/solver.h @@ -31,6 +31,7 @@ enum class TxoutType { WITNESS_V0_SCRIPTHASH, WITNESS_V0_KEYHASH, WITNESS_V1_TAPROOT, + TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY, WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above }; diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp index 07a49e039fb..4b74abd1278 100644 --- a/src/test/fuzz/script.cpp +++ b/src/test/fuzz/script.cpp @@ -76,6 +76,7 @@ FUZZ_TARGET(script, .init = initialize_script) assert(which_type == TxoutType::PUBKEY || which_type == TxoutType::NONSTANDARD || which_type == TxoutType::NULL_DATA || + which_type == TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY || which_type == TxoutType::MULTISIG); } if (which_type == TxoutType::NONSTANDARD || diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 1375672a418..60b6dec957a 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -70,6 +70,9 @@ static std::map mapFlagNames = { {std::string("DISCOURAGE_UPGRADABLE_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE}, {std::string("DISCOURAGE_OP_SUCCESS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS}, {std::string("DISCOURAGE_UPGRADABLE_TAPROOT_VERSION"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION}, + {std::string("DISCOURAGE_UPGRADABLE_CHECKTEMPLATEVERIFY"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_CHECKTEMPLATEVERIFY}, + {std::string("DISCOURAGE_CHECKTEMPLATEVERIFY"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_CHECKTEMPLATEVERIFY}, + {std::string("CHECKTEMPLATEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKTEMPLATEVERIFY}, }; unsigned int ParseScriptFlags(std::string strFlags) diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp index ac23b092d40..49e5e40209e 100644 --- a/src/wallet/rpc/backup.cpp +++ b/src/wallet/rpc/backup.cpp @@ -912,6 +912,8 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d case TxoutType::WITNESS_V1_TAPROOT: case TxoutType::ANCHOR: return "unrecognized script"; + case TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY: + return "bare default CheckTemplateVerify hash"; } // no default case, so the compiler can warn about missing cases NONFATAL_UNREACHABLE(); } diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 04287581f5a..f0865d6b0e2 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -208,6 +208,11 @@ IsMineResult IsMineInner(const LegacyDataSPKM& keystore, const CScript& scriptPu } break; } + case TxoutType::TX_BARE_DEFAULT_CHECKTEMPLATEVERIFY: + { + ret = IsMineResult::NO; + break; + } } // no default case, so the compiler can warn about missing cases if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) {