Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ constexpr bool ValidDeployment(BuriedDeployment dep) { return dep <= DEPLOYMENT_

enum DeploymentPos : uint16_t {
DEPLOYMENT_TESTDUMMY,
DEPLOYMENT_TEMPLATEHASH,
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp
// Removing an entry may require bumping MinBIP9WarningHeight.
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 @@ -13,6 +13,10 @@ const std::array<VBDeploymentInfo,Consensus::MAX_VERSION_BITS_DEPLOYMENTS> Versi
.name = "testdummy",
.gbt_optional_rule = true,
},
VBDeploymentInfo{
.name = "templatehash",
.gbt_optional_rule = true,
}
};

std::string DeploymentName(Consensus::BuriedDeployment dep)
Expand Down
32 changes: 32 additions & 0 deletions src/kernel/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ class CMainParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 1815; // 90%
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 2016;

// Deployment of OP_TEMPLATEHASH
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].bit = 3;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].min_activation_height = 0;

ApplyDeploymentOptions(opts.dep_opts);

consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000001128750f82f4c366153a3a030"};
Expand Down Expand Up @@ -261,6 +267,12 @@ class CTestNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 1512; // 75%
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 2016;

// Deployment of OP_TEMPLATEHASH
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].bit = 3;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].min_activation_height = 0;

ApplyDeploymentOptions(opts.dep_opts);

consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000017dde1c649f3708d14b6"};
Expand Down Expand Up @@ -363,6 +375,12 @@ class CTestNet4Params : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 1512; // 75%
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 2016;

// Deployment of OP_TEMPLATEHASH
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].bit = 3;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].min_activation_height = 0;

ApplyDeploymentOptions(opts.dep_opts);

consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000009a0fe15d0177d086304"};
Expand Down Expand Up @@ -508,6 +526,12 @@ class SigNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 1815; // 90%
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 2016;

// Deployment of OP_TEMPLATEHASH
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].bit = 3;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].min_activation_height = 0;

ApplyDeploymentOptions(options.dep_opts);

// message start is defined as the first 4 bytes of the sha256d of the block script
Expand Down Expand Up @@ -592,6 +616,14 @@ class CRegTestParams : public CChainParams
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 108; // 75%
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 144; // Faster than normal for regtest (144 instead of 2016)

// Deployment of OP_TEMPLATEHASH
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].bit = 3;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].min_activation_height = 0; // No activation delay
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].threshold = 108; // 75%
consensus.vDeployments[Consensus::DEPLOYMENT_TEMPLATEHASH].period = 144;

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

Expand Down
6 changes: 4 additions & 2 deletions src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ static constexpr script_verify_flags MANDATORY_SCRIPT_VERIFY_FLAGS{SCRIPT_VERIFY
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY |
SCRIPT_VERIFY_WITNESS |
SCRIPT_VERIFY_TAPROOT};
SCRIPT_VERIFY_TAPROOT |
SCRIPT_VERIFY_TEMPLATEHASH};

/**
* Standard script verification flags that standard transactions will comply
Expand All @@ -128,7 +129,8 @@ static constexpr script_verify_flags STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRI
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_TEMPLATEHASH};

/** For convenience, standard but not mandatory verify flags. */
static constexpr script_verify_flags STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS};
Expand Down
4 changes: 2 additions & 2 deletions src/pubkey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,12 +233,12 @@ bool XOnlyPubKey::IsFullyValid() const
return secp256k1_xonly_pubkey_parse(secp256k1_context_static, &pubkey, m_keydata.data());
}

bool XOnlyPubKey::VerifySchnorr(const uint256& msg, std::span<const unsigned char> sigbytes) const
bool XOnlyPubKey::VerifySchnorr(std::span<const unsigned char> msg, std::span<const unsigned char> sigbytes) const
{
assert(sigbytes.size() == 64);
secp256k1_xonly_pubkey pubkey;
if (!secp256k1_xonly_pubkey_parse(secp256k1_context_static, &pubkey, m_keydata.data())) return false;
return secp256k1_schnorrsig_verify(secp256k1_context_static, sigbytes.data(), msg.begin(), 32, &pubkey);
return secp256k1_schnorrsig_verify(secp256k1_context_static, sigbytes.data(), msg.data(), msg.size(), &pubkey);
}

static const HashWriter HASHER_TAPTWEAK{TaggedHash("TapTweak")};
Expand Down
5 changes: 3 additions & 2 deletions src/pubkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,10 @@ class XOnlyPubKey

/** Verify a Schnorr signature against this public key.
*
* sigbytes must be exactly 64 bytes.
* msg may be of any length: per BIP340 the message is not required to be a
* 32-byte hash. sigbytes must be exactly 64 bytes.
*/
bool VerifySchnorr(const uint256& msg, std::span<const unsigned char> sigbytes) const;
bool VerifySchnorr(std::span<const unsigned char> msg, std::span<const unsigned char> sigbytes) const;

/** Compute the Taproot tweak as specified in BIP341, with *this as internal
* key:
Expand Down
1 change: 1 addition & 0 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1516,6 +1516,7 @@ UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager&
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CSV);
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_SEGWIT);
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TESTDUMMY);
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TEMPLATEHASH);
return softforks;
}
} // anon namespace
Expand Down
145 changes: 145 additions & 0 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,57 @@ static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::con
assert(false);
}

/** Helper for OP_CHECKSIGFROMSTACK (BIP-348).
*
* Returns false when script execution must immediately fail. On success, success_out indicates
* whether the signature check itself succeeded (false only for an empty signature).
*/
static bool EvalChecksigFromStack(const valtype& sig, const valtype& msg, const valtype& pubkey_in, ScriptExecutionData& execdata, script_verify_flags flags, SigVersion sigversion, ScriptError* serror, bool& success_out)
{
assert(sigversion == SigVersion::TAPSCRIPT);

/*
* The following validation sequence is consensus critical. Please note how --
* upgradable public key versions precede other rules;
* the script execution fails when using empty signature with invalid public key;
* the script execution fails when using non-empty invalid signature.
*/
success_out = !sig.empty();
if (success_out) {
// Implement the sigops/witnesssize ratio test.
// Passing with an upgradable public key version is also counted.
assert(execdata.m_validation_weight_left_init);
execdata.m_validation_weight_left -= VALIDATION_WEIGHT_PER_SIGOP_PASSED;
if (execdata.m_validation_weight_left < 0) {
return set_error(serror, SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT);
}
}
if (pubkey_in.size() == 0) {
return set_error(serror, SCRIPT_ERR_TAPSCRIPT_EMPTY_PUBKEY);
} else if (pubkey_in.size() == 32) {
if (success_out) {
if (sig.size() != 64) {
return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_SIZE);
}
XOnlyPubKey pubkey{pubkey_in};
if (!pubkey.VerifySchnorr(msg, sig)) {
return set_error(serror, SCRIPT_ERR_SCHNORR_SIG);
}
}
} else {
/*
* New public key version softforks should be defined before this `else` block.
* Generally, the new code should not do anything but failing the script execution. To avoid
* consensus bugs, it should not modify any existing values (including `success_out`).
*/
if ((flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE) != 0) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE);
}
}

return true;
}

bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror)
{
static const CScriptNum bnZero(0);
Expand Down Expand Up @@ -1224,6 +1275,64 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
}
break;

case OP_TEMPLATEHASH:
{
// OP_TEMPLATEHASH is only available in Tapscript. Note this is the exact same error
// returned in the default case, which would be the one hit by OP_SUCCESS206 before
// the introduction of OP_TEMPLATEHASH.
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) {
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
}

const uint256 template_hash{checker.GetTemplateHash(execdata)};
stack.emplace_back(template_hash.begin(), template_hash.end());
}
break;

case OP_CHECKSIGFROMSTACK:
{
// DISCOURAGE/activation for OP_CHECKSIGFROMSTACK is handled in OP_SUCCESS handling.
// OP_CHECKSIGFROMSTACK is only available in Tapscript.
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) {
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
}

// If fewer than 3 elements are on the stack, the script MUST fail and terminate immediately.
if (stack.size() < 3) {
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
}

// The public key (top element), message (second to top element), and
// signature (third from top element) are read from the stack.
const valtype& pubkey = stacktop(-1);
const valtype& msg = stacktop(-2);
const valtype& sig = stacktop(-3);

bool push_success{true};
if (!EvalChecksigFromStack(sig, msg, pubkey, execdata, flags, sigversion, serror, push_success)) {
return false; // serror set by EvalChecksigFromStack
}

popstack(stack);
popstack(stack);
popstack(stack);

stack.push_back(push_success ? vchTrue : vchFalse);
}
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);
}
// The internal key is always set when executing Tapscript (see VerifyWitnessProgram).
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);
}
Expand Down Expand Up @@ -1476,6 +1585,7 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTr
const HashWriter HASHER_TAPSIGHASH{TaggedHash("TapSighash")};
const HashWriter HASHER_TAPLEAF{TaggedHash("TapLeaf")};
const HashWriter HASHER_TAPBRANCH{TaggedHash("TapBranch")};
const HashWriter HASHER_TEMPLATEHASH{TaggedHash("TemplateHash")};

static bool HandleMissingData(MissingDataBehavior mdb)
{
Expand Down Expand Up @@ -1835,6 +1945,27 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
return true;
}

template<class T>
uint256 GenericTransactionSignatureChecker<T>::GetTemplateHash(ScriptExecutionData& execdata) const
{
assert(txdata && txdata->m_bip341_taproot_ready);
assert(execdata.m_annex_init);

HashWriter ss{HASHER_TEMPLATEHASH};
ss << txTo->version;
ss << txTo->nLockTime;
ss << txdata->m_sequences_single_hash;
ss << txdata->m_outputs_single_hash;
const uint8_t annex_present = (execdata.m_annex_present ? 1 : 0);
ss << annex_present;
ss << nIn;
if (execdata.m_annex_present) {
ss << execdata.m_annex_hash;
}

return ss.GetSHA256();
}

// explicit instantiation
template class GenericTransactionSignatureChecker<CTransaction>;
template class GenericTransactionSignatureChecker<CMutableTransaction>;
Expand All @@ -1854,6 +1985,16 @@ static bool ExecuteWitnessScript(const std::span<const valtype>& stack_span, con
}
// New opcodes will be listed here. May use a different sigversion to modify existing opcodes.
if (IsOpSuccess(opcode)) {
// Do not return early success on the BIP-448 opcodes (OP_TEMPLATEHASH,
// OP_CHECKSIGFROMSTACK, OP_INTERNALKEY) once they are active. They are non-standard until then.
if (opcode == OP_TEMPLATEHASH || opcode == OP_CHECKSIGFROMSTACK || opcode == OP_INTERNALKEY) {
if (flags & SCRIPT_VERIFY_DISCOURAGE_TEMPLATEHASH) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_TEMPLATEHASH);
}
if (flags & SCRIPT_VERIFY_TEMPLATEHASH) {
continue;
}
}
if (flags & SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS);
}
Expand Down Expand Up @@ -1990,6 +2131,8 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
exec_script = CScript(script.begin(), script.end());
execdata.m_validation_weight_left = ::GetSerializeSize(witness.stack) + VALIDATION_WEIGHT_OFFSET;
execdata.m_validation_weight_left_init = true;
// Expose the verified 32-byte internal key (control[1:33]) for OP_INTERNALKEY.
execdata.m_internal_key = uint256{std::span{control}.subspan(1, 32)};
return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::TAPSCRIPT, checker, execdata, serror);
}
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION) {
Expand Down Expand Up @@ -2200,6 +2343,8 @@ const std::map<std::string, script_verify_flag_name>& ScriptFlagNamesToEnum()
FLAG_NAME(DISCOURAGE_UPGRADABLE_PUBKEYTYPE),
FLAG_NAME(DISCOURAGE_OP_SUCCESS),
FLAG_NAME(DISCOURAGE_UPGRADABLE_TAPROOT_VERSION),
FLAG_NAME(TEMPLATEHASH),
FLAG_NAME(DISCOURAGE_TEMPLATEHASH),
};
#undef FLAG_NAME
return g_names_to_enum;
Expand Down
20 changes: 20 additions & 0 deletions src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ enum class script_verify_flag_name : uint8_t {
// Making unknown public key versions (in BIP 342 scripts) non-standard
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE,

// OP_TEMPLATEHASH validation (BIP 446)
SCRIPT_VERIFY_TEMPLATEHASH,

// Make OP_TEMPLATEHASH spend non-standard before activation.
SCRIPT_VERIFY_DISCOURAGE_TEMPLATEHASH,

// Constants to point to the highest flag in use. Add new flags above this line.
//
SCRIPT_VERIFY_END_MARKER
Expand Down Expand Up @@ -232,6 +238,9 @@ struct ScriptExecutionData

//! The hash of the corresponding output
std::optional<uint256> m_output_hash;

//! The 32-byte x-only Taproot internal key, set for Tapscript spends (for OP_INTERNALKEY).
std::optional<uint256> m_internal_key;
};

/** Signature hash sizes */
Expand Down Expand Up @@ -295,6 +304,11 @@ class BaseSignatureChecker
return false;
}

virtual uint256 GetTemplateHash(ScriptExecutionData& execdata) const
{
return {};
}

virtual ~BaseSignatureChecker() = default;
};

Expand Down Expand Up @@ -332,6 +346,7 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker
bool CheckSchnorrSignature(std::span<const unsigned char> sig, std::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;
uint256 GetTemplateHash(ScriptExecutionData& execdata) const override;
};

using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
Expand Down Expand Up @@ -363,6 +378,11 @@ class DeferringSignatureChecker : public BaseSignatureChecker
{
return m_checker.CheckSequence(nSequence);
}

uint256 GetTemplateHash(ScriptExecutionData& execdata) const override
{
return m_checker.GetTemplateHash(execdata);
}
};

/** Compute the BIP341 tapleaf hash from leaf version & script. */
Expand Down
Loading