aboutsummaryrefslogtreecommitdiff
path: root/src/script/interpreter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/script/interpreter.cpp')
-rw-r--r--src/script/interpreter.cpp467
1 files changed, 420 insertions, 47 deletions
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 50a6192476..5735e7df66 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -342,13 +342,10 @@ public:
};
}
-/** Helper for OP_CHECKSIG and OP_CHECKSIGVERIFY
- *
- * A return value of false means the script fails entirely. When true is returned, the
- * fSuccess variable indicates whether the signature check itself succeeded.
- */
-static bool EvalChecksig(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
+static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
{
+ assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
+
// Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend);
@@ -363,7 +360,7 @@ static bool EvalChecksig(const valtype& vchSig, const valtype& vchPubKey, CScrip
//serror is set
return false;
}
- fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);
+ fSuccess = checker.CheckECDSASignature(vchSig, vchPubKey, scriptCode, sigversion);
if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size())
return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL);
@@ -371,7 +368,67 @@ static bool EvalChecksig(const valtype& vchSig, const valtype& vchPubKey, CScrip
return true;
}
-bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
+static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
+{
+ 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 = !sig.empty();
+ if (success) {
+ // 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.size() == 0) {
+ return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
+ } else if (pubkey.size() == 32) {
+ if (success && !checker.CheckSchnorrSignature(sig, pubkey, sigversion, execdata, serror)) {
+ return false; // serror is set
+ }
+ } 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`).
+ */
+ if ((flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE) != 0) {
+ return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE);
+ }
+ }
+
+ return true;
+}
+
+/** Helper for OP_CHECKSIG, OP_CHECKSIGVERIFY, and (in Tapscript) OP_CHECKSIGADD.
+ *
+ * A return value of false means the script fails entirely. When true is returned, the
+ * success variable indicates whether the signature check itself succeeded.
+ */
+static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
+{
+ switch (sigversion) {
+ case SigVersion::BASE:
+ case SigVersion::WITNESS_V0:
+ return EvalChecksigPreTapscript(sig, pubkey, pbegincodehash, pend, flags, checker, sigversion, serror, success);
+ case SigVersion::TAPSCRIPT:
+ return EvalChecksigTapscript(sig, pubkey, execdata, flags, checker, sigversion, serror, success);
+ case SigVersion::TAPROOT:
+ // Key path spending in Taproot has no script, so this is unreachable.
+ break;
+ }
+ assert(false);
+}
+
+bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror)
{
static const CScriptNum bnZero(0);
static const CScriptNum bnOne(1);
@@ -381,6 +438,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
// static const valtype vchZero(0);
static const valtype vchTrue(1, 1);
+ // sigversion cannot be TAPROOT here, as it admits no script execution.
+ assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0 || sigversion == SigVersion::TAPSCRIPT);
+
CScript::const_iterator pc = script.begin();
CScript::const_iterator pend = script.end();
CScript::const_iterator pbegincodehash = script.begin();
@@ -389,15 +449,18 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
ConditionStack vfExec;
std::vector<valtype> altstack;
set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
- if (script.size() > MAX_SCRIPT_SIZE)
+ if ((sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) && script.size() > MAX_SCRIPT_SIZE) {
return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE);
+ }
int nOpCount = 0;
bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0;
+ uint32_t opcode_pos = 0;
+ execdata.m_codeseparator_pos = 0xFFFFFFFFUL;
+ execdata.m_codeseparator_pos_init = true;
try
{
- while (pc < pend)
- {
+ for (; pc < pend; ++opcode_pos) {
bool fExec = vfExec.all_true();
//
@@ -408,9 +471,12 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE)
return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
- // Note how OP_RESERVED does not count towards the opcode limit.
- if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT)
- return set_error(serror, SCRIPT_ERR_OP_COUNT);
+ if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) {
+ // Note how OP_RESERVED does not count towards the opcode limit.
+ if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT) {
+ return set_error(serror, SCRIPT_ERR_OP_COUNT);
+ }
+ }
if (opcode == OP_CAT ||
opcode == OP_SUBSTR ||
@@ -568,6 +634,15 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
valtype& vch = stacktop(-1);
+ // Tapscript requires minimal IF/NOTIF inputs as a consensus rule.
+ if (sigversion == SigVersion::TAPSCRIPT) {
+ // The input argument to the OP_IF and OP_NOTIF opcodes must be either
+ // exactly 0 (the empty vector) or exactly 1 (the one-byte vector with value 1).
+ if (vch.size() > 1 || (vch.size() == 1 && vch[0] != 1)) {
+ return set_error(serror, SCRIPT_ERR_TAPSCRIPT_MINIMALIF);
+ }
+ }
+ // Under witness v0 rules it is only a policy rule, enabled through SCRIPT_VERIFY_MINIMALIF.
if (sigversion == SigVersion::WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) {
if (vch.size() > 1)
return set_error(serror, SCRIPT_ERR_MINIMALIF);
@@ -1001,6 +1076,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
// Hash starts after the code separator
pbegincodehash = pc;
+ execdata.m_codeseparator_pos = opcode_pos;
}
break;
@@ -1015,7 +1091,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
valtype& vchPubKey = stacktop(-1);
bool fSuccess = true;
- if (!EvalChecksig(vchSig, vchPubKey, pbegincodehash, pend, flags, checker, sigversion, serror, fSuccess)) return false;
+ if (!EvalChecksig(vchSig, vchPubKey, pbegincodehash, pend, execdata, flags, checker, sigversion, serror, fSuccess)) return false;
popstack(stack);
popstack(stack);
stack.push_back(fSuccess ? vchTrue : vchFalse);
@@ -1029,9 +1105,32 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
}
break;
+ case OP_CHECKSIGADD:
+ {
+ // OP_CHECKSIGADD is only available in Tapscript
+ if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
+
+ // (sig num pubkey -- num)
+ if (stack.size() < 3) return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
+
+ const valtype& sig = stacktop(-3);
+ const CScriptNum num(stacktop(-2), fRequireMinimal);
+ const valtype& pubkey = stacktop(-1);
+
+ bool success = true;
+ if (!EvalChecksig(sig, pubkey, pbegincodehash, pend, execdata, flags, checker, sigversion, serror, success)) return false;
+ popstack(stack);
+ popstack(stack);
+ popstack(stack);
+ stack.push_back((num + (success ? 1 : 0)).getvch());
+ }
+ break;
+
case OP_CHECKMULTISIG:
case OP_CHECKMULTISIGVERIFY:
{
+ if (sigversion == SigVersion::TAPSCRIPT) return set_error(serror, SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG);
+
// ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)
int i = 1;
@@ -1089,7 +1188,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
}
// Check signature
- bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);
+ bool fOk = checker.CheckECDSASignature(vchSig, vchPubKey, scriptCode, sigversion);
if (fOk) {
isig++;
@@ -1159,6 +1258,12 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
return set_success(serror);
}
+bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
+{
+ ScriptExecutionData execdata;
+ return EvalScript(stack, script, flags, checker, sigversion, execdata, serror);
+}
+
namespace {
/**
@@ -1291,35 +1396,183 @@ uint256 GetOutputsSHA256(const T& txTo)
return ss.GetSHA256();
}
+/** Compute the (single) SHA256 of the concatenation of all amounts spent by a tx. */
+uint256 GetSpentAmountsSHA256(const std::vector<CTxOut>& outputs_spent)
+{
+ CHashWriter ss(SER_GETHASH, 0);
+ for (const auto& txout : outputs_spent) {
+ ss << txout.nValue;
+ }
+ return ss.GetSHA256();
+}
+
+/** Compute the (single) SHA256 of the concatenation of all scriptPubKeys spent by a tx. */
+uint256 GetSpentScriptsSHA256(const std::vector<CTxOut>& outputs_spent)
+{
+ CHashWriter ss(SER_GETHASH, 0);
+ for (const auto& txout : outputs_spent) {
+ ss << txout.scriptPubKey;
+ }
+ return ss.GetSHA256();
+}
+
+
} // namespace
template <class T>
-void PrecomputedTransactionData::Init(const T& txTo)
+void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs)
{
- assert(!m_ready);
+ assert(!m_spent_outputs_ready);
- // Cache is calculated only for transactions with witness
- if (txTo.HasWitness()) {
- hashPrevouts = SHA256Uint256(GetPrevoutsSHA256(txTo));
- hashSequence = SHA256Uint256(GetSequencesSHA256(txTo));
- hashOutputs = SHA256Uint256(GetOutputsSHA256(txTo));
+ m_spent_outputs = std::move(spent_outputs);
+ if (!m_spent_outputs.empty()) {
+ assert(m_spent_outputs.size() == txTo.vin.size());
+ m_spent_outputs_ready = true;
}
- m_ready = true;
+ // Determine which precomputation-impacting features this transaction uses.
+ bool uses_bip143_segwit = false;
+ bool uses_bip341_taproot = false;
+ for (size_t inpos = 0; inpos < txTo.vin.size(); ++inpos) {
+ if (!txTo.vin[inpos].scriptWitness.IsNull()) {
+ if (m_spent_outputs_ready && m_spent_outputs[inpos].scriptPubKey.size() == 2 + WITNESS_V1_TAPROOT_SIZE &&
+ m_spent_outputs[inpos].scriptPubKey[0] == OP_1) {
+ // Treat every witness-bearing spend with 34-byte scriptPubKey that starts with OP_1 as a Taproot
+ // spend. This only works if spent_outputs was provided as well, but if it wasn't, actual validation
+ // will fail anyway. Note that this branch may trigger for scriptPubKeys that aren't actually segwit
+ // but in that case validation will fail as SCRIPT_ERR_WITNESS_UNEXPECTED anyway.
+ uses_bip341_taproot = true;
+ } else {
+ // Treat every spend that's not known to native witness v1 as a Witness v0 spend. This branch may
+ // also be taken for unknown witness versions, but it is harmless, and being precise would require
+ // P2SH evaluation to find the redeemScript.
+ uses_bip143_segwit = true;
+ }
+ }
+ 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);
+ }
+ if (uses_bip143_segwit) {
+ hashPrevouts = SHA256Uint256(m_prevouts_single_hash);
+ hashSequence = SHA256Uint256(m_sequences_single_hash);
+ hashOutputs = SHA256Uint256(m_outputs_single_hash);
+ m_bip143_segwit_ready = true;
+ }
+ if (uses_bip341_taproot) {
+ m_spent_amounts_single_hash = GetSpentAmountsSHA256(m_spent_outputs);
+ m_spent_scripts_single_hash = GetSpentScriptsSHA256(m_spent_outputs);
+ m_bip341_taproot_ready = true;
+ }
}
template <class T>
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
{
- Init(txTo);
+ Init(txTo, {});
}
// explicit instantiation
-template void PrecomputedTransactionData::Init(const CTransaction& txTo);
-template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo);
+template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
+template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
+static const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash");
+static const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf");
+static const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch");
+static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
+
+template<typename T>
+bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache)
+{
+ uint8_t ext_flag, key_version;
+ switch (sigversion) {
+ case SigVersion::TAPROOT:
+ ext_flag = 0;
+ // key_version is not used and left uninitialized.
+ break;
+ case SigVersion::TAPSCRIPT:
+ ext_flag = 1;
+ // key_version must be 0 for now, representing the current version of
+ // 32-byte public keys in the tapscript signature opcode execution.
+ // An upgradable public key version (with a size not 32-byte) may
+ // request a different key_version with a new sigversion.
+ key_version = 0;
+ break;
+ default:
+ assert(false);
+ }
+ assert(in_pos < tx_to.vin.size());
+ assert(cache.m_bip341_taproot_ready && cache.m_spent_outputs_ready);
+
+ CHashWriter ss = HASHER_TAPSIGHASH;
+
+ // Epoch
+ static constexpr uint8_t EPOCH = 0;
+ ss << EPOCH;
+
+ // Hash type
+ const uint8_t output_type = (hash_type == SIGHASH_DEFAULT) ? SIGHASH_ALL : (hash_type & SIGHASH_OUTPUT_MASK); // Default (no sighash byte) is equivalent to SIGHASH_ALL
+ const uint8_t input_type = hash_type & SIGHASH_INPUT_MASK;
+ if (!(hash_type <= 0x03 || (hash_type >= 0x81 && hash_type <= 0x83))) return false;
+ ss << hash_type;
+
+ // Transaction level data
+ ss << tx_to.nVersion;
+ ss << tx_to.nLockTime;
+ if (input_type != SIGHASH_ANYONECANPAY) {
+ ss << cache.m_prevouts_single_hash;
+ ss << cache.m_spent_amounts_single_hash;
+ ss << cache.m_spent_scripts_single_hash;
+ ss << cache.m_sequences_single_hash;
+ }
+ if (output_type == SIGHASH_ALL) {
+ ss << cache.m_outputs_single_hash;
+ }
+
+ // Data about the input/prevout being spent
+ assert(execdata.m_annex_init);
+ const bool have_annex = execdata.m_annex_present;
+ const uint8_t spend_type = (ext_flag << 1) + (have_annex ? 1 : 0); // The low bit indicates whether an annex is present.
+ ss << spend_type;
+ if (input_type == SIGHASH_ANYONECANPAY) {
+ ss << tx_to.vin[in_pos].prevout;
+ ss << cache.m_spent_outputs[in_pos];
+ ss << tx_to.vin[in_pos].nSequence;
+ } else {
+ ss << in_pos;
+ }
+ if (have_annex) {
+ ss << execdata.m_annex_hash;
+ }
+
+ // Data about the output (if only one).
+ if (output_type == SIGHASH_SINGLE) {
+ if (in_pos >= tx_to.vout.size()) return false;
+ CHashWriter sha_single_output(SER_GETHASH, 0);
+ sha_single_output << tx_to.vout[in_pos];
+ ss << sha_single_output.GetSHA256();
+ }
+
+ // Additional data for BIP 342 signatures
+ if (sigversion == SigVersion::TAPSCRIPT) {
+ assert(execdata.m_tapleaf_hash_init);
+ ss << execdata.m_tapleaf_hash;
+ ss << key_version;
+ assert(execdata.m_codeseparator_pos_init);
+ ss << execdata.m_codeseparator_pos;
+ }
+
+ hash_out = ss.GetSHA256();
+ return true;
+}
+
template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
@@ -1329,7 +1582,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
uint256 hashPrevouts;
uint256 hashSequence;
uint256 hashOutputs;
- const bool cacheready = cache && cache->m_ready;
+ const bool cacheready = cache && cache->m_bip143_segwit_ready;
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
hashPrevouts = cacheready ? cache->hashPrevouts : SHA256Uint256(GetPrevoutsSHA256(txTo));
@@ -1389,13 +1642,19 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
}
template <class T>
-bool GenericTransactionSignatureChecker<T>::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
+bool GenericTransactionSignatureChecker<T>::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
return pubkey.Verify(sighash, vchSig);
}
template <class T>
-bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
+bool GenericTransactionSignatureChecker<T>::VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
+{
+ return pubkey.VerifySchnorr(sighash, sig);
+}
+
+template <class T>
+bool GenericTransactionSignatureChecker<T>::CheckECDSASignature(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
{
CPubKey pubkey(vchPubKey);
if (!pubkey.IsValid())
@@ -1410,13 +1669,41 @@ bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata);
- if (!VerifySignature(vchSig, pubkey, sighash))
+ if (!VerifyECDSASignature(vchSig, pubkey, sighash))
return false;
return true;
}
template <class T>
+bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey_in, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror) const
+{
+ assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
+ // Schnorr signatures have 32-byte public keys. The caller is responsible for enforcing this.
+ assert(pubkey_in.size() == 32);
+ // Note that in Tapscript evaluation, empty signatures are treated specially (invalid signature that does not
+ // abort script execution). This is implemented in EvalChecksigTapscript, which won't invoke
+ // CheckSchnorrSignature in that case. In other contexts, they are invalid like every other signature with
+ // size different from 64 or 65.
+ if (sig.size() != 64 && sig.size() != 65) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_SIZE);
+
+ XOnlyPubKey pubkey{pubkey_in};
+
+ uint8_t hashtype = SIGHASH_DEFAULT;
+ if (sig.size() == 65) {
+ hashtype = SpanPopBack(sig);
+ if (hashtype == SIGHASH_DEFAULT) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
+ }
+ uint256 sighash;
+ assert(this->txdata);
+ if (!SignatureHashSchnorr(sighash, execdata, *txTo, nIn, hashtype, sigversion, *this->txdata)) {
+ return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
+ }
+ if (!VerifySchnorrSignature(sig, pubkey, sighash)) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG);
+ return true;
+}
+
+template <class T>
bool GenericTransactionSignatureChecker<T>::CheckLockTime(const CScriptNum& nLockTime) const
{
// There are two kinds of nLockTime: lock-by-blockheight
@@ -1504,17 +1791,39 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
template class GenericTransactionSignatureChecker<CTransaction>;
template class GenericTransactionSignatureChecker<CMutableTransaction>;
-static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CScript& scriptPubKey, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptError* serror)
+static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CScript& scriptPubKey, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
{
std::vector<valtype> stack{stack_span.begin(), stack_span.end()};
+ if (sigversion == SigVersion::TAPSCRIPT) {
+ // OP_SUCCESSx processing overrides everything, including stack element size limits
+ CScript::const_iterator pc = scriptPubKey.begin();
+ while (pc < scriptPubKey.end()) {
+ opcodetype opcode;
+ if (!scriptPubKey.GetOp(pc, opcode)) {
+ // Note how this condition would not be reached if an unknown OP_SUCCESSx was found
+ return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
+ }
+ // 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) {
+ return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS);
+ }
+ return set_success(serror);
+ }
+ }
+
+ // Tapscript enforces initial stack size limits (altstack is empty here)
+ if (stack.size() > MAX_STACK_SIZE) return set_error(serror, SCRIPT_ERR_STACK_SIZE);
+ }
+
// Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack
for (const valtype& elem : stack) {
if (elem.size() > MAX_SCRIPT_ELEMENT_SIZE) return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
}
// Run the script interpreter.
- if (!EvalScript(stack, scriptPubKey, flags, checker, sigversion, serror)) return false;
+ if (!EvalScript(stack, scriptPubKey, flags, checker, sigversion, execdata, serror)) return false;
// Scripts inside witness implicitly require cleanstack behaviour
if (stack.size() != 1) return set_error(serror, SCRIPT_ERR_CLEANSTACK);
@@ -1522,40 +1831,104 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
return true;
}
-static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
+static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const CScript& script, uint256& tapleaf_hash)
{
- CScript scriptPubKey;
+ const int path_len = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
+ const XOnlyPubKey p{uint256(std::vector<unsigned char>(control.begin() + 1, control.begin() + TAPROOT_CONTROL_BASE_SIZE))};
+ const XOnlyPubKey q{uint256(program)};
+ tapleaf_hash = (CHashWriter(HASHER_TAPLEAF) << uint8_t(control[0] & TAPROOT_LEAF_MASK) << script).GetSHA256();
+ uint256 k = tapleaf_hash;
+ for (int i = 0; i < path_len; ++i) {
+ CHashWriter ss_branch{HASHER_TAPBRANCH};
+ Span<const unsigned char> node(control.data() + TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * i, TAPROOT_CONTROL_NODE_SIZE);
+ if (std::lexicographical_compare(k.begin(), k.end(), node.begin(), node.end())) {
+ ss_branch << k << node;
+ } else {
+ ss_branch << node << k;
+ }
+ k = ss_branch.GetSHA256();
+ }
+ k = (CHashWriter(HASHER_TAPTWEAK) << MakeSpan(p) << k).GetSHA256();
+ return q.CheckPayToContract(p, k, control[0] & 1);
+}
+
+static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
+{
+ CScript exec_script; //!< Actually executed script (last stack item in P2WSH; implied P2PKH script in P2WPKH; leaf script in P2TR)
Span<const valtype> stack{witness.stack};
+ ScriptExecutionData execdata;
if (witversion == 0) {
if (program.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
- // Version 0 segregated witness program: SHA256(CScript) inside the program, CScript + inputs in witness
+ // BIP141 P2WSH: 32-byte witness v0 program (which encodes SHA256(script))
if (stack.size() == 0) {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY);
}
const valtype& script_bytes = SpanPopBack(stack);
- scriptPubKey = CScript(script_bytes.begin(), script_bytes.end());
- uint256 hashScriptPubKey;
- CSHA256().Write(&scriptPubKey[0], scriptPubKey.size()).Finalize(hashScriptPubKey.begin());
- if (memcmp(hashScriptPubKey.begin(), program.data(), 32)) {
+ exec_script = CScript(script_bytes.begin(), script_bytes.end());
+ uint256 hash_exec_script;
+ CSHA256().Write(&exec_script[0], exec_script.size()).Finalize(hash_exec_script.begin());
+ if (memcmp(hash_exec_script.begin(), program.data(), 32)) {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
}
- return ExecuteWitnessScript(stack, scriptPubKey, flags, SigVersion::WITNESS_V0, checker, serror);
+ return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, serror);
} else if (program.size() == WITNESS_V0_KEYHASH_SIZE) {
- // Special case for pay-to-pubkeyhash; signature + pubkey in witness
+ // BIP141 P2WPKH: 20-byte witness v0 program (which encodes Hash160(pubkey))
if (stack.size() != 2) {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness
}
- scriptPubKey << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG;
- return ExecuteWitnessScript(stack, scriptPubKey, flags, SigVersion::WITNESS_V0, checker, serror);
+ exec_script << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG;
+ return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, serror);
} else {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH);
}
+ } else if (witversion == 1 && program.size() == WITNESS_V1_TAPROOT_SIZE && !is_p2sh) {
+ // BIP341 Taproot: 32-byte non-P2SH witness v1 program (which encodes a P2C-tweaked pubkey)
+ if (!(flags & SCRIPT_VERIFY_TAPROOT)) return set_success(serror);
+ if (stack.size() == 0) return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY);
+ if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) {
+ // Drop annex (this is non-standard; see IsWitnessStandard)
+ const valtype& annex = SpanPopBack(stack);
+ execdata.m_annex_hash = (CHashWriter(SER_GETHASH, 0) << annex).GetSHA256();
+ execdata.m_annex_present = true;
+ } else {
+ execdata.m_annex_present = false;
+ }
+ execdata.m_annex_init = true;
+ if (stack.size() == 1) {
+ // Key path spending (stack size is 1 after removing optional annex)
+ if (!checker.CheckSchnorrSignature(stack.front(), program, SigVersion::TAPROOT, execdata, serror)) {
+ return false; // serror is set
+ }
+ return set_success(serror);
+ } else {
+ // Script path spending (stack size is >1 after removing optional annex)
+ const valtype& control = SpanPopBack(stack);
+ const valtype& script_bytes = SpanPopBack(stack);
+ exec_script = CScript(script_bytes.begin(), script_bytes.end());
+ 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);
+ }
+ if (!VerifyTaprootCommitment(control, program, exec_script, execdata.m_tapleaf_hash)) {
+ return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
+ }
+ execdata.m_tapleaf_hash_init = true;
+ if ((control[0] & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSCRIPT) {
+ // Tapscript (leaf version 0xc0)
+ execdata.m_validation_weight_left = ::GetSerializeSize(witness.stack, PROTOCOL_VERSION) + VALIDATION_WEIGHT_OFFSET;
+ execdata.m_validation_weight_left_init = true;
+ return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::TAPSCRIPT, checker, execdata, serror);
+ }
+ if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION) {
+ return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION);
+ }
+ return set_success(serror);
+ }
} else {
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM);
}
- // Higher version witness scripts return true for future softfork compatibility
+ // Other version/size/p2sh combinations return true for future softfork compatibility
return true;
}
// There is intentionally no return statement here, to be able to use "control reaches end of non-void function" warnings to detect gaps in the logic above.
@@ -1601,7 +1974,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
// The scriptSig must be _exactly_ CScript(), otherwise we reintroduce malleability.
return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED);
}
- if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) {
+ if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror, /* is_p2sh */ false)) {
return false;
}
// Bypass the cleanstack check at the end. The actual stack is obviously not clean
@@ -1646,7 +2019,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
// reintroduce malleability.
return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED_P2SH);
}
- if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) {
+ if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror, /* is_p2sh */ true)) {
return false;
}
// Bypass the cleanstack check at the end. The actual stack is obviously not clean