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.
This commit is contained in:
Johnson Lau 2020-09-11 14:34:02 -07:00 committed by Pieter Wuille
parent 330de894a9
commit 72422ce396
6 changed files with 234 additions and 19 deletions

View file

@ -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);
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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

View file

@ -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:

View file

@ -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,