aboutsummaryrefslogtreecommitdiff
path: root/src/script
diff options
context:
space:
mode:
authorJohnson Lau <jl2012@xbt.hk>2020-09-11 14:34:02 -0700
committerPieter Wuille <pieter@wuille.net>2020-10-12 17:18:24 -0700
commit72422ce396b8eba7b1a72c171c2f07dae691d1b5 (patch)
treed2aa2a75ed54579ed6ddac8f858cf3ca67605b48 /src/script
parent330de894a9a48515d9a473448b6c67adc3d188be (diff)
downloadbitcoin-72422ce396b8eba7b1a72c171c2f07dae691d1b5.tar.xz
Implement Tapscript script validation rules (BIP 342)
This adds a new `SigVersion::TAPSCRIPT`, makes the necessary interpreter changes to make it implement BIP342, and uses them for leaf version 0xc0 in Taproot script path spends.
Diffstat (limited to 'src/script')
-rw-r--r--src/script/interpreter.cpp186
-rw-r--r--src/script/interpreter.h29
-rw-r--r--src/script/script.cpp11
-rw-r--r--src/script/script.h12
-rw-r--r--src/script/script_error.cpp10
-rw-r--r--src/script/script_error.h5
6 files changed, 234 insertions, 19 deletions
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index a9d28c25d2..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);
@@ -371,6 +368,66 @@ static bool EvalChecksig(const valtype& vchSig, const valtype& vchPubKey, CScrip
return true;
}
+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);
@@ -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;
@@ -1392,10 +1491,19 @@ 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;
+ 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);
@@ -1452,6 +1560,15 @@ bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata
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;
}
@@ -1561,9 +1678,13 @@ bool GenericTransactionSignatureChecker<T>::CheckECDSASignature(const std::vecto
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);
+ 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};
@@ -1674,6 +1795,28 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
{
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);
@@ -1688,12 +1831,12 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
return true;
}
-static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const CScript& script)
+static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const CScript& script, uint256& tapleaf_hash)
{
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)};
- uint256 tapleaf_hash = (CHashWriter(HASHER_TAPLEAF) << uint8_t(control[0] & TAPROOT_LEAF_MASK) << script).GetSHA256();
+ 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};
@@ -1766,9 +1909,16 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
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)) {
+ 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);
}
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 55aa1f230b..c0c2b012c6 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -86,6 +86,8 @@ enum
// "Exactly one stack element must remain, and when interpreted as a boolean, it must be true".
// (BIP62 rule 6)
// Note: CLEANSTACK should never be used without P2SH or WITNESS.
+ // Note: WITNESS_V0 and TAPSCRIPT script execution have behavior similar to CLEANSTACK as part of their
+ // consensus rules. It is automatic there and does not need this flag.
SCRIPT_VERIFY_CLEANSTACK = (1U << 8),
// Verify CHECKLOCKTIMEVERIFY
@@ -108,6 +110,8 @@ enum
// Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
//
+ // Note: TAPSCRIPT script execution has behavior similar to MINIMALIF as part of its consensus
+ // rules. It is automatic there and does not depend on this flag.
SCRIPT_VERIFY_MINIMALIF = (1U << 13),
// Signature(s) must be empty vector if a CHECK(MULTI)SIG operation failed
@@ -122,13 +126,19 @@ enum
//
SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16),
- // Taproot validation (BIP 341)
+ // Taproot/Tapscript validation (BIPs 341 & 342)
//
SCRIPT_VERIFY_TAPROOT = (1U << 17),
// Making unknown Taproot leaf versions non-standard
//
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = (1U << 18),
+
+ // Making unknown OP_SUCCESS non-standard
+ SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS = (1U << 19),
+
+ // Making unknown public key versions (in BIP 342 scripts) non-standard
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20),
};
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
@@ -168,16 +178,32 @@ enum class SigVersion
BASE = 0, //!< Bare scripts and BIP16 P2SH-wrapped redeemscripts
WITNESS_V0 = 1, //!< Witness v0 (P2WPKH and P2WSH); see BIP 141
TAPROOT = 2, //!< Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, key path spending; see BIP 341
+ TAPSCRIPT = 3, //!< Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, script path spending, leaf version 0xc0; see BIP 342
};
struct ScriptExecutionData
{
+ //! Whether m_tapleaf_hash is initialized.
+ bool m_tapleaf_hash_init = false;
+ //! The tapleaf hash.
+ uint256 m_tapleaf_hash;
+
+ //! Whether m_codeseparator_pos is initialized.
+ bool m_codeseparator_pos_init = false;
+ //! Opcode position of the last executed OP_CODESEPARATOR (or 0xFFFFFFFF if none executed).
+ uint32_t m_codeseparator_pos;
+
//! Whether m_annex_present and (when needed) m_annex_hash are initialized.
bool m_annex_init = false;
//! Whether an annex is present.
bool m_annex_present;
//! Hash of the annex data.
uint256 m_annex_hash;
+
+ //! Whether m_validation_weight_left is initialized.
+ bool m_validation_weight_left_init = false;
+ //! How much validation weight is left (decremented for every successful non-empty signature check).
+ int64_t m_validation_weight_left;
};
/** Signature hash sizes */
@@ -186,6 +212,7 @@ static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20;
static constexpr size_t WITNESS_V1_TAPROOT_SIZE = 32;
static constexpr uint8_t TAPROOT_LEAF_MASK = 0xfe;
+static constexpr uint8_t TAPROOT_LEAF_TAPSCRIPT = 0xc0;
static constexpr size_t TAPROOT_CONTROL_BASE_SIZE = 33;
static constexpr size_t TAPROOT_CONTROL_NODE_SIZE = 32;
static constexpr size_t TAPROOT_CONTROL_MAX_NODE_COUNT = 128;
diff --git a/src/script/script.cpp b/src/script/script.cpp
index 92c6fe7785..f31472e42d 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -140,6 +140,9 @@ std::string GetOpName(opcodetype opcode)
case OP_NOP9 : return "OP_NOP9";
case OP_NOP10 : return "OP_NOP10";
+ // Opcode added by BIP 342 (Tapscript)
+ case OP_CHECKSIGADD : return "OP_CHECKSIGADD";
+
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
default:
@@ -328,3 +331,11 @@ bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator en
opcodeRet = static_cast<opcodetype>(opcode);
return true;
}
+
+bool IsOpSuccess(const opcodetype& opcode)
+{
+ return opcode == 80 || opcode == 98 || (opcode >= 126 && opcode <= 129) ||
+ (opcode >= 131 && opcode <= 134) || (opcode >= 137 && opcode <= 138) ||
+ (opcode >= 141 && opcode <= 142) || (opcode >= 149 && opcode <= 153) ||
+ (opcode >= 187 && opcode <= 254);
+}
diff --git a/src/script/script.h b/src/script/script.h
index fcf3e29362..974cde4984 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -49,6 +49,12 @@ static const uint32_t LOCKTIME_MAX = 0xFFFFFFFFU;
// has meanings independent of the script
static constexpr unsigned int ANNEX_TAG = 0x50;
+// Validation weight per passing signature (Tapscript only, see BIP 342).
+static constexpr uint64_t VALIDATION_WEIGHT_PER_SIGOP_PASSED = 50;
+
+// How much weight budget is added to the witness size (Tapscript only, see BIP 342).
+static constexpr uint64_t VALIDATION_WEIGHT_OFFSET = 50;
+
template <typename T>
std::vector<unsigned char> ToByteVector(const T& in)
{
@@ -192,6 +198,9 @@ enum opcodetype
OP_NOP9 = 0xb8,
OP_NOP10 = 0xb9,
+ // Opcode added by BIP 342 (Tapscript)
+ OP_CHECKSIGADD = 0xba,
+
OP_INVALIDOPCODE = 0xff,
};
@@ -560,4 +569,7 @@ struct CScriptWitness
std::string ToString() const;
};
+/** Test for OP_SUCCESSx opcodes as defined by BIP342. */
+bool IsOpSuccess(const opcodetype& opcode);
+
#endif // BITCOIN_SCRIPT_SCRIPT_H
diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp
index 77add5194c..fadc04262c 100644
--- a/src/script/script_error.cpp
+++ b/src/script/script_error.cpp
@@ -75,6 +75,10 @@ std::string ScriptErrorString(const ScriptError serror)
return "Witness version reserved for soft-fork upgrades";
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION:
return "Taproot version reserved for soft-fork upgrades";
+ case SCRIPT_ERR_DISCOURAGE_OP_SUCCESS:
+ return "OP_SUCCESSx reserved for soft-fork upgrades";
+ case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE:
+ return "Public key version reserved for soft-fork upgrades";
case SCRIPT_ERR_PUBKEYTYPE:
return "Public key is neither compressed or uncompressed";
case SCRIPT_ERR_CLEANSTACK:
@@ -101,6 +105,12 @@ std::string ScriptErrorString(const ScriptError serror)
return "Invalid Schnorr signature";
case SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE:
return "Invalid Taproot control block size";
+ case SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT:
+ return "Too much signature validation relative to witness weight";
+ case SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG:
+ return "OP_CHECKMULTISIG(VERIFY) is not available in tapscript";
+ case SCRIPT_ERR_TAPSCRIPT_MINIMALIF:
+ return "OP_IF/NOTIF argument must be minimal in tapscript";
case SCRIPT_ERR_OP_CODESEPARATOR:
return "Using OP_CODESEPARATOR in non-witness script";
case SCRIPT_ERR_SIG_FINDANDDELETE:
diff --git a/src/script/script_error.h b/src/script/script_error.h
index 59d3e565a5..b071681613 100644
--- a/src/script/script_error.h
+++ b/src/script/script_error.h
@@ -57,6 +57,8 @@ typedef enum ScriptError_t
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS,
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM,
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION,
+ SCRIPT_ERR_DISCOURAGE_OP_SUCCESS,
+ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE,
/* segregated witness */
SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH,
@@ -72,6 +74,9 @@ typedef enum ScriptError_t
SCRIPT_ERR_SCHNORR_SIG_HASHTYPE,
SCRIPT_ERR_SCHNORR_SIG,
SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE,
+ SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT,
+ SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG,
+ SCRIPT_ERR_TAPSCRIPT_MINIMALIF,
/* Constant scriptCode */
SCRIPT_ERR_OP_CODESEPARATOR,