Skip to content
1 change: 1 addition & 0 deletions src/addresstype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ enum DeploymentPos : uint16_t {
DEPLOYMENT_TESTDUMMY,
DEPLOYMENT_TAPROOT, // Deployment of Schnorr/Taproot (BIPs 340-342)
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp
DEPLOYMENT_CTV, // Deployment of CHECKTEMPLATEVERIFY (BIP 119) (regtest only)
MAX_VERSION_BITS_DEPLOYMENTS
};
constexpr bool ValidDeployment(DeploymentPos dep) { return dep < MAX_VERSION_BITS_DEPLOYMENTS; }
Expand Down
4 changes: 4 additions & 0 deletions src/deploymentinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B
/*.name =*/ "taproot",
/*.gbt_force =*/ true,
},
{
/*.name =*/ "ctv",
/*.gbt_force =*/ true,
},
};

std::string DeploymentName(Consensus::BuriedDeployment dep)
Expand Down
5 changes: 5 additions & 0 deletions src/kernel/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,11 @@ class CRegTestParams : public CChainParams
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay

consensus.vDeployments[Consensus::DEPLOYMENT_CTV].bit = 1;
consensus.vDeployments[Consensus::DEPLOYMENT_CTV].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_CTV].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_CTV].min_activation_height = 0; // No activation delay

consensus.nMinimumChainWork = uint256{};
consensus.defaultAssumeValid = uint256{};

Expand Down
5 changes: 5 additions & 0 deletions src/policy/policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs,
if (subscript.GetSigOpCount(true) > MAX_P2SH_SIGOPS) {
MaybeReject("scriptcheck-sigops");
}
} 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;
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ 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};

/** 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};
Expand Down
4 changes: 4 additions & 0 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,10 @@ UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager&
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_SEGWIT);
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TESTDUMMY);
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TAPROOT);

if (chainman.GetParams().GetChainType() == ChainType::REGTEST) {
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CTV);
}
return softforks;
}
} // anon namespace
Expand Down
4 changes: 4 additions & 0 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,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
Expand Down Expand Up @@ -616,6 +618,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
Expand Down
141 changes: 134 additions & 7 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,42 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
break;
}

case OP_NOP1: case OP_NOP4: case OP_NOP5:
case OP_CHECKTEMPLATEVERIFY:
{
if (flags & SCRIPT_VERIFY_DISCOURAGE_CHECKTEMPLATEVERIFY) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
}

// if flags not enabled; treat as a NOP4
if (!(flags & SCRIPT_VERIFY_CHECKTEMPLATEVERIFY)) {
break;
}

if (stack.size() < 1) {
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
}

// If the argument was not 32 bytes, treat as OP_NOP4:
switch (stack.back().size()) {
case 32:
{
const std::span<const unsigned char> hash{stack.back()};
if (!checker.CheckDefaultCheckTemplateVerifyHash(hash)) {
return set_error(serror, SCRIPT_ERR_TEMPLATE_MISMATCH);
}
break;
}
default:
// future upgrade can add semantics for this opcode with different length args
// so discourage use when applicable
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_CHECKTEMPLATEVERIFY) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
}
}
}
break;

case OP_NOP1: case OP_NOP5:
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
{
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
Expand Down Expand Up @@ -1377,6 +1412,18 @@ uint256 GetSpentAmountsSHA256(const std::vector<CTxOut>& outputs_spent)
HashWriter ss{};
for (const auto& txout : outputs_spent) {
ss << txout.nValue;

}
return ss.GetSHA256();
}

/** Compute the (single) SHA256 of the concatenation of all scriptSigs in a tx. */
template <class T>
uint256 GetScriptSigsSHA256(const T& txTo)
{
HashWriter ss{};
for (const auto& in : txTo.vin) {
ss << in.scriptSig;
}
return ss.GetSHA256();
}
Expand All @@ -1391,9 +1438,66 @@ uint256 GetSpentScriptsSHA256(const std::vector<CTxOut>& outputs_spent)
return ss.GetSHA256();
}

template<typename TxType>
uint256 GetDefaultCheckTemplateVerifyHashWithScript(
const TxType& tx, const uint256& outputs_hash, const uint256& sequences_hash,
const uint256& scriptSig_hash, const uint32_t input_index)
{
auto h = HashWriter{}
<< tx.version
<< tx.nLockTime
<< scriptSig_hash
<< uint32_t(tx.vin.size())
<< sequences_hash
<< uint32_t(tx.vout.size())
<< outputs_hash
<< input_index;
return h.GetSHA256();
}

template<typename TxType>
uint256 GetDefaultCheckTemplateVerifyHashEmptyScript(
const TxType& tx, const uint256& outputs_hash, const uint256& sequences_hash,
const uint32_t input_index)
{
auto h = HashWriter{}
<< tx.version
<< tx.nLockTime
<< uint32_t(tx.vin.size())
<< sequences_hash
<< uint32_t(tx.vout.size())
<< outputs_hash
<< input_index;
return h.GetSHA256();
}

} // namespace

template<typename TxType>
uint256 GetDefaultCheckTemplateVerifyHash(const TxType& tx, uint32_t input_index) {
return GetDefaultCheckTemplateVerifyHash(tx, GetOutputsSHA256(tx), GetSequencesSHA256(tx), input_index);
}

template<typename TxType>
static bool NoScriptSigs(const TxType& tx)
{
return std::all_of(tx.vin.begin(), tx.vin.end(), [](const CTxIn& c) { return c.scriptSig.empty(); });
}

template<typename TxType>
uint256 GetDefaultCheckTemplateVerifyHash(
const TxType& tx, const uint256& outputs_hash, const uint256& sequences_hash, const uint32_t input_index) {
return NoScriptSigs(tx) ? GetDefaultCheckTemplateVerifyHashEmptyScript(tx, outputs_hash, sequences_hash, input_index) :
GetDefaultCheckTemplateVerifyHashWithScript(tx, outputs_hash, sequences_hash, GetScriptSigsSHA256(tx), input_index);
}

template
uint256 GetDefaultCheckTemplateVerifyHash(
const CTransaction& tx, const uint256& outputs_hash, const uint256& sequences_hash, const uint32_t input_index);
template
uint256 GetDefaultCheckTemplateVerifyHash(
const CMutableTransaction& tx, const uint256& outputs_hash, const uint256& sequences_hash, const uint32_t input_index);

template <class T>
void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs, bool force)
{
Expand Down Expand Up @@ -1427,12 +1531,19 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent
if (uses_bip341_taproot && uses_bip143_segwit) break; // No need to scan further if we already need all.
}

if (uses_bip143_segwit || uses_bip341_taproot) {
// Computations shared between both sighash schemes.
m_prevouts_single_hash = GetPrevoutsSHA256(txTo);
m_sequences_single_hash = GetSequencesSHA256(txTo);
m_outputs_single_hash = GetOutputsSHA256(txTo);
}
// Each of these computations is always required for CHECKTEMPLATEVERIFY, and sometimes
// required for any segwit/taproot evaluation.
m_prevouts_single_hash = GetPrevoutsSHA256(txTo);
m_sequences_single_hash = GetSequencesSHA256(txTo);
m_outputs_single_hash = GetOutputsSHA256(txTo);

// Only required for CHECKTEMPLATEVERIFY.
//
// The empty hash is used to signal whether or not we should skip scriptSigs
// when re-computing for different indexes.
m_scriptSigs_single_hash = NoScriptSigs(txTo) ? uint256{} : GetScriptSigsSHA256(txTo);
m_bip119_ctv_ready = true;

if (uses_bip143_segwit) {
hashPrevouts = SHA256Uint256(m_prevouts_single_hash);
hashSequence = SHA256Uint256(m_sequences_single_hash);
Expand Down Expand Up @@ -1788,6 +1899,22 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
return true;
}

template <class T>
bool GenericTransactionSignatureChecker<T>::CheckDefaultCheckTemplateVerifyHash(const std::span<const unsigned char>& hash) const
{
// Should already be checked before calling...
assert(hash.size() == 32);
if (txdata && txdata->m_bip119_ctv_ready) {
assert(txTo != nullptr);
uint256 hash_tmpl = txdata->m_scriptSigs_single_hash.IsNull() ?
GetDefaultCheckTemplateVerifyHashEmptyScript(*txTo, txdata->m_outputs_single_hash, txdata->m_sequences_single_hash, nIn) :
GetDefaultCheckTemplateVerifyHashWithScript(*txTo, txdata->m_outputs_single_hash, txdata->m_sequences_single_hash,
txdata->m_scriptSigs_single_hash, nIn);
return std::equal(hash_tmpl.begin(), hash_tmpl.end(), hash.data());
} else {
return HandleMissingData(m_mdb);
}
}
// explicit instantiation
template class GenericTransactionSignatureChecker<CTransaction>;
template class GenericTransactionSignatureChecker<CMutableTransaction>;
Expand Down
49 changes: 43 additions & 6 deletions src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ enum : uint32_t {
// Making unknown public key versions (in BIP 342 scripts) non-standard
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20),

// CHECKTEMPLATEVERIFY validation (BIP-119)
SCRIPT_VERIFY_CHECKTEMPLATEVERIFY = (1U << 21),

// discourage upgradable OP_CHECKTEMPLATEVERIFY hashes
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_CHECKTEMPLATEVERIFY = (1U << 22),

// discourage OP_CHECKTEMPLATEVERIFY
SCRIPT_VERIFY_DISCOURAGE_CHECKTEMPLATEVERIFY = (1U << 23),

// Constants to point to the highest flag in use. Add new flags above this line.
//
SCRIPT_VERIFY_END_MARKER
Expand All @@ -152,22 +161,35 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i

struct PrecomputedTransactionData
{
// Order of fields is packed below (uint256 is 32 bytes, vector is 24 bytes
// (3 ptrs), ready flags (1 byte each).

// BIP341 precomputed data.
// These are single-SHA256, see https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-16.
uint256 m_prevouts_single_hash;
uint256 m_sequences_single_hash;
uint256 m_outputs_single_hash;
uint256 m_spent_amounts_single_hash;
uint256 m_spent_scripts_single_hash;
//! Whether the 5 fields above are initialized.
bool m_bip341_taproot_ready = false;

// BIP119 precomputed data (single SHA256).
uint256 m_scriptSigs_single_hash;

// BIP143 precomputed data (double-SHA256).
uint256 hashPrevouts, hashSequence, hashOutputs;
//! Whether the 3 fields above are initialized.
bool m_bip143_segwit_ready = false;

// BIP341 cached outputs.
std::vector<CTxOut> m_spent_outputs;

//! Whether the bip341 fields above are initialized.
bool m_bip341_taproot_ready = false;

//! Whether the bip119 fields above are initialized.
bool m_bip119_ctv_ready = false;

//! Whether the bip143 fields above are initialized.
bool m_bip143_segwit_ready = false;

//! Whether m_spent_outputs is initialized.
bool m_spent_outputs_ready = false;

Expand All @@ -187,6 +209,17 @@ struct PrecomputedTransactionData
explicit PrecomputedTransactionData(const T& tx);
};

/**
* Compute the default template hash for OP_CHECKTEMPLATEVERIFY using some precomputed
* hash inputs.
*
* (This exported interface is only used in tests.)
*/
template<typename TxType>
uint256 GetDefaultCheckTemplateVerifyHash(
const TxType& tx, const uint256& outputs_hash, const uint256& sequences_hash,
const uint32_t input_index);

enum class SigVersion
{
BASE = 0, //!< Bare scripts and BIP16 P2SH-wrapped redeemscripts
Expand Down Expand Up @@ -265,6 +298,11 @@ class BaseSignatureChecker
return false;
}

virtual bool CheckDefaultCheckTemplateVerifyHash(const std::span<const unsigned char>& hash) const
{
return false;
}

virtual ~BaseSignatureChecker() = default;
};

Expand Down Expand Up @@ -301,8 +339,7 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
bool CheckLockTime(const CScriptNum& nLockTime) const override;
bool CheckSequence(const CScriptNum& nSequence) const override;

bool m_require_sighash_all{false};
bool CheckDefaultCheckTemplateVerifyHash(const std::span<const unsigned char>& hash) const override;
};

using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
Expand Down
10 changes: 9 additions & 1 deletion src/script/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ std::string GetOpName(opcodetype opcode)
case OP_NOP1 : return "OP_NOP1";
case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY";
case OP_CHECKSEQUENCEVERIFY : return "OP_CHECKSEQUENCEVERIFY";
case OP_NOP4 : return "OP_NOP4";
case OP_CHECKTEMPLATEVERIFY : return "OP_CHECKTEMPLATEVERIFY";
case OP_NOP5 : return "OP_NOP5";
case OP_NOP6 : return "OP_NOP6";
case OP_NOP7 : return "OP_NOP7";
Expand Down Expand Up @@ -222,6 +222,14 @@ bool CScript::IsPayToAnchor(int version, const std::vector<unsigned char>& 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:
Expand Down
Loading