mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-17 13:07:54 +02:00
commit
e765fc93ea
45 changed files with 3034 additions and 139 deletions
|
|
@ -19,7 +19,7 @@ public:
|
|||
bool sighash_byte;
|
||||
|
||||
SimpleSignatureChecker(const uint256& hashIn, bool sighash_byte_in) : hash(hashIn), sighash_byte(sighash_byte_in) {};
|
||||
bool CheckSig(const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
{
|
||||
std::vector<unsigned char> vchSigCopy(vchSig);
|
||||
CPubKey pubkey(vchPubKey);
|
||||
|
|
|
|||
|
|
@ -350,13 +350,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,7 +368,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);
|
||||
|
|
@ -379,7 +376,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);
|
||||
|
|
@ -389,6 +446,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();
|
||||
|
|
@ -397,15 +457,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();
|
||||
|
||||
//
|
||||
|
|
@ -416,9 +479,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);
|
||||
}
|
||||
}
|
||||
|
||||
// ELEMENTS:
|
||||
// commented out opcodes are re-enabled in Elements
|
||||
|
|
@ -580,6 +646,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);
|
||||
|
|
@ -1279,6 +1354,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;
|
||||
|
||||
|
|
@ -1293,7 +1369,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);
|
||||
|
|
@ -1307,9 +1383,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;
|
||||
|
|
@ -1367,7 +1466,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++;
|
||||
|
|
@ -1528,6 +1627,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 {
|
||||
|
||||
/**
|
||||
|
|
@ -1678,36 +1783,184 @@ 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));
|
||||
hashIssuance = SHA256Uint256(GetIssuanceSHA256(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 (inpos < txTo.witness.vtxinwit.size() && !txTo.witness.vtxinwit[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);
|
||||
hashIssuance = SHA256Uint256(GetIssuanceSHA256(txTo));
|
||||
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 CConfidentialValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
|
||||
{
|
||||
|
|
@ -1718,7 +1971,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
|
|||
uint256 hashSequence;
|
||||
uint256 hashIssuance;
|
||||
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));
|
||||
|
|
@ -1792,13 +2045,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())
|
||||
|
|
@ -1813,12 +2072,40 @@ 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
|
||||
{
|
||||
|
|
@ -1907,17 +2194,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);
|
||||
|
|
@ -1925,40 +2234,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.
|
||||
|
|
@ -2004,7 +2377,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
|
||||
|
|
@ -2049,7 +2422,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
|
||||
|
|
|
|||
|
|
@ -7,14 +7,17 @@
|
|||
#define BITCOIN_SCRIPT_INTERPRETER_H
|
||||
|
||||
#include <script/script_error.h>
|
||||
#include <span.h>
|
||||
#include <primitives/transaction.h>
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
class CPubKey;
|
||||
class XOnlyPubKey;
|
||||
class CScript;
|
||||
class CTransaction;
|
||||
class CTxOut;
|
||||
class uint256;
|
||||
|
||||
/** Signature hash types/flags */
|
||||
|
|
@ -24,6 +27,10 @@ enum
|
|||
SIGHASH_NONE = 2,
|
||||
SIGHASH_SINGLE = 3,
|
||||
SIGHASH_ANYONECANPAY = 0x80,
|
||||
|
||||
SIGHASH_DEFAULT = 0, //!< Taproot only; implied when sighash byte is missing, and equivalent to SIGHASH_ALL
|
||||
SIGHASH_OUTPUT_MASK = 3,
|
||||
SIGHASH_INPUT_MASK = 0x80,
|
||||
};
|
||||
|
||||
/** Script verification flags.
|
||||
|
|
@ -79,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
|
||||
|
|
@ -101,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
|
||||
|
|
@ -115,22 +126,52 @@ enum
|
|||
//
|
||||
SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16),
|
||||
|
||||
// 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),
|
||||
|
||||
// Signature checking assumes no sighash byte after the DER signature
|
||||
//
|
||||
SCRIPT_NO_SIGHASH_BYTE = (1U << 17),
|
||||
SCRIPT_NO_SIGHASH_BYTE = (1U << 21),
|
||||
};
|
||||
|
||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
|
||||
|
||||
struct PrecomputedTransactionData
|
||||
{
|
||||
// BIP341 precomputed data.
|
||||
// These are single-SHA256, see https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-15.
|
||||
uint256 m_prevouts_single_hash;
|
||||
uint256 m_sequences_single_hash;
|
||||
uint256 m_outputs_single_hash;
|
||||
uint256 m_spent_amounts_single_hash;
|
||||
uint256 m_spent_scripts_single_hash;
|
||||
//! Whether the 5 fields above are initialized.
|
||||
bool m_bip341_taproot_ready = false;
|
||||
|
||||
// BIP143 precomputed data (double-SHA256).
|
||||
uint256 hashPrevouts, hashSequence, hashOutputs, hashIssuance;
|
||||
bool m_ready = false;
|
||||
//! Whether the 3 fields above are initialized.
|
||||
bool m_bip143_segwit_ready = false;
|
||||
|
||||
std::vector<CTxOut> m_spent_outputs;
|
||||
//! Whether m_spent_outputs is initialized.
|
||||
bool m_spent_outputs_ready = false;
|
||||
|
||||
PrecomputedTransactionData() = default;
|
||||
|
||||
template <class T>
|
||||
void Init(const T& tx);
|
||||
void Init(const T& tx, std::vector<CTxOut>&& spent_outputs);
|
||||
|
||||
template <class T>
|
||||
explicit PrecomputedTransactionData(const T& tx);
|
||||
|
|
@ -138,13 +179,48 @@ struct PrecomputedTransactionData
|
|||
|
||||
enum class SigVersion
|
||||
{
|
||||
BASE = 0,
|
||||
WITNESS_V0 = 1,
|
||||
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 */
|
||||
static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32;
|
||||
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;
|
||||
static constexpr size_t TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT;
|
||||
|
||||
template <class T>
|
||||
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
|
||||
|
|
@ -152,7 +228,12 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
|
|||
class BaseSignatureChecker
|
||||
{
|
||||
public:
|
||||
virtual bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
|
||||
virtual bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -180,12 +261,14 @@ private:
|
|||
const PrecomputedTransactionData* txdata;
|
||||
|
||||
protected:
|
||||
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
||||
virtual bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
||||
virtual bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const;
|
||||
|
||||
public:
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
|
||||
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
|
||||
bool CheckLockTime(const CScriptNum& nLockTime) const override;
|
||||
bool CheckSequence(const CScriptNum& nSequence) const override;
|
||||
};
|
||||
|
|
@ -193,6 +276,7 @@ public:
|
|||
using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
|
||||
using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker<CMutableTransaction>;
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr);
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
|
||||
|
||||
|
|
|
|||
|
|
@ -144,6 +144,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:
|
||||
|
|
@ -405,3 +408,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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,17 @@ static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20
|
|||
// SEQUENCE_FINAL).
|
||||
static const uint32_t LOCKTIME_MAX = 0xFFFFFFFFU;
|
||||
|
||||
// Tag for input annex. If there are at least two witness elements for a transaction input,
|
||||
// and the first byte of the last element is 0x50, this last element is called annex, and
|
||||
// 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;
|
||||
|
||||
// ELEMENTS:
|
||||
// Number of confirms on parent chain required to confirm on sidechain.
|
||||
static const unsigned int DEFAULT_PEGIN_CONFIRMATION_DEPTH = 8;
|
||||
|
|
@ -199,6 +210,9 @@ enum opcodetype
|
|||
OP_NOP9 = 0xb8,
|
||||
OP_NOP10 = 0xb9,
|
||||
|
||||
// Opcode added by BIP 342 (Tapscript)
|
||||
OP_CHECKSIGADD = 0xba,
|
||||
|
||||
OP_INVALIDOPCODE = 0xff,
|
||||
};
|
||||
|
||||
|
|
@ -581,4 +595,7 @@ struct CScriptWitness
|
|||
uint32_t GetSerializedSize() const;
|
||||
};
|
||||
|
||||
/** Test for OP_SUCCESSx opcodes as defined by BIP342. */
|
||||
bool IsOpSuccess(const opcodetype& opcode);
|
||||
|
||||
#endif // BITCOIN_SCRIPT_SCRIPT_H
|
||||
|
|
|
|||
|
|
@ -73,6 +73,12 @@ std::string ScriptErrorString(const ScriptError serror)
|
|||
return "NOPx reserved for soft-fork upgrades";
|
||||
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM:
|
||||
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:
|
||||
|
|
@ -91,6 +97,20 @@ std::string ScriptErrorString(const ScriptError serror)
|
|||
return "Witness provided for non-witness script";
|
||||
case SCRIPT_ERR_WITNESS_PUBKEYTYPE:
|
||||
return "Using non-compressed keys in segwit";
|
||||
case SCRIPT_ERR_SCHNORR_SIG_SIZE:
|
||||
return "Invalid Schnorr signature size";
|
||||
case SCRIPT_ERR_SCHNORR_SIG_HASHTYPE:
|
||||
return "Invalid Schnorr signature hash type";
|
||||
case SCRIPT_ERR_SCHNORR_SIG:
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ typedef enum ScriptError_t
|
|||
/* softfork safeness */
|
||||
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,
|
||||
|
|
@ -66,6 +69,15 @@ typedef enum ScriptError_t
|
|||
SCRIPT_ERR_WITNESS_UNEXPECTED,
|
||||
SCRIPT_ERR_WITNESS_PUBKEYTYPE,
|
||||
|
||||
/* Taproot */
|
||||
SCRIPT_ERR_SCHNORR_SIG_SIZE,
|
||||
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,
|
||||
SCRIPT_ERR_SIG_FINDANDDELETE,
|
||||
|
|
|
|||
|
|
@ -22,8 +22,11 @@ namespace {
|
|||
class CSignatureCache
|
||||
{
|
||||
private:
|
||||
//! Entries are SHA256(nonce || signature hash || public key || signature):
|
||||
CSHA256 m_salted_hasher;
|
||||
//! Entries are SHA256(nonce || 'E' or 'S' || 31 zero bytes || signature hash || public key || signature):
|
||||
CSHA256 m_salted_hasher_ecdsa;
|
||||
CSHA256 m_salted_hasher_schnorr;
|
||||
CSHA256 m_salted_hasher_range_proof;
|
||||
CSHA256 m_salted_hasher_surjection_proof;
|
||||
typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
|
||||
map_type setValid;
|
||||
boost::shared_mutex cs_sigcache;
|
||||
|
|
@ -34,25 +37,43 @@ public:
|
|||
uint256 nonce = GetRandHash();
|
||||
// We want the nonce to be 64 bytes long to force the hasher to process
|
||||
// this chunk, which makes later hash computations more efficient. We
|
||||
// just write our 32-byte entropy twice to fill the 64 bytes.
|
||||
m_salted_hasher.Write(nonce.begin(), 32);
|
||||
m_salted_hasher.Write(nonce.begin(), 32);
|
||||
// just write our 32-byte entropy, and then pad with 'E' for ECDSA and
|
||||
// 'S' for Schnorr (followed by 0 bytes).
|
||||
static constexpr unsigned char PADDING_ECDSA[32] = {'E'};
|
||||
static constexpr unsigned char PADDING_SCHNORR[32] = {'S'};
|
||||
static constexpr unsigned char PADDING_RANGE_PROOF[32] = {'r'};
|
||||
static constexpr unsigned char PADDING_SURJECTION_PROOF[32] = {'s'};
|
||||
m_salted_hasher_ecdsa.Write(nonce.begin(), 32);
|
||||
m_salted_hasher_ecdsa.Write(PADDING_ECDSA, 32);
|
||||
m_salted_hasher_schnorr.Write(nonce.begin(), 32);
|
||||
m_salted_hasher_schnorr.Write(PADDING_SCHNORR, 32);
|
||||
m_salted_hasher_range_proof.Write(nonce.begin(), 32);
|
||||
m_salted_hasher_range_proof.Write(PADDING_RANGE_PROOF, 32);
|
||||
m_salted_hasher_surjection_proof.Write(nonce.begin(), 32);
|
||||
m_salted_hasher_surjection_proof.Write(PADDING_SURJECTION_PROOF, 32);
|
||||
}
|
||||
|
||||
void
|
||||
ComputeEntry(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey)
|
||||
ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey)
|
||||
{
|
||||
CSHA256 hasher = m_salted_hasher;
|
||||
CSHA256 hasher = m_salted_hasher_ecdsa;
|
||||
hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
|
||||
}
|
||||
|
||||
void
|
||||
ComputeEntrySchnorr(uint256& entry, const uint256 &hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey)
|
||||
{
|
||||
CSHA256 hasher = m_salted_hasher_schnorr;
|
||||
hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin());
|
||||
}
|
||||
|
||||
// ELEMENTS:
|
||||
void ComputeEntry(uint256& entry, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) {
|
||||
CSHA256 hasher = m_salted_hasher;
|
||||
void ComputeEntryRangeProof(uint256& entry, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) {
|
||||
CSHA256 hasher = m_salted_hasher_range_proof;
|
||||
hasher.Write(proof.data(), proof.size()).Write(commitment.data(), commitment.size()).Finalize(entry.begin());
|
||||
}
|
||||
void ComputeEntry(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) {
|
||||
CSHA256 hasher = m_salted_hasher;
|
||||
void ComputeEntrySurjectionProof(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) {
|
||||
CSHA256 hasher = m_salted_hasher_surjection_proof;
|
||||
hasher.Write(hash.begin(), 32).Write(proof.data(), proof.size()).Write(commitment.data(), commitment.size()).Finalize(entry.begin());
|
||||
}
|
||||
|
||||
|
|
@ -100,19 +121,29 @@ void InitSignatureCache()
|
|||
(nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
|
||||
}
|
||||
|
||||
bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
|
||||
bool CachingTransactionSignatureChecker::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
|
||||
{
|
||||
uint256 entry;
|
||||
signatureCache.ComputeEntry(entry, sighash, vchSig, pubkey);
|
||||
signatureCache.ComputeEntryECDSA(entry, sighash, vchSig, pubkey);
|
||||
if (signatureCache.Get(entry, !store))
|
||||
return true;
|
||||
if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash))
|
||||
if (!TransactionSignatureChecker::VerifyECDSASignature(vchSig, pubkey, sighash))
|
||||
return false;
|
||||
if (store)
|
||||
signatureCache.Set(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CachingTransactionSignatureChecker::VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
|
||||
{
|
||||
uint256 entry;
|
||||
signatureCache.ComputeEntrySchnorr(entry, sighash, sig, pubkey);
|
||||
if (signatureCache.Get(entry, !store)) return true;
|
||||
if (!TransactionSignatureChecker::VerifySchnorrSignature(sig, pubkey, sighash)) return false;
|
||||
if (store) signatureCache.Set(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// ELEMENTS CACHES
|
||||
|
||||
|
|
@ -141,7 +172,7 @@ void InitSurjectionproofCache()
|
|||
bool CachingRangeProofChecker::VerifyRangeProof(const std::vector<unsigned char>& vchRangeProof, const std::vector<unsigned char>& vchValueCommitment, const std::vector<unsigned char>& vchAssetCommitment, const CScript& scriptPubKey, const secp256k1_context* secp256k1_ctx_verify_amounts) const
|
||||
{
|
||||
uint256 entry;
|
||||
rangeProofCache.ComputeEntry(entry, vchRangeProof, vchValueCommitment);
|
||||
rangeProofCache.ComputeEntryRangeProof(entry, vchRangeProof, vchValueCommitment);
|
||||
|
||||
if (rangeProofCache.Get(entry, !store)) {
|
||||
return true;
|
||||
|
|
@ -192,7 +223,7 @@ bool CachingSurjectionProofChecker::VerifySurjectionProof(secp256k1_surjectionpr
|
|||
// wtxid commits to all data including surj targets
|
||||
// we need to specify the proof and output asset point to be unique
|
||||
uint256 entry;
|
||||
surjectionProofCache.ComputeEntry(entry, wtxid, vchproof, std::vector<unsigned char>(std::begin(gen.data), std::end(gen.data)));
|
||||
surjectionProofCache.ComputeEntrySurjectionProof(entry, wtxid, vchproof, std::vector<unsigned char>(std::begin(gen.data), std::end(gen.data)));
|
||||
|
||||
if (surjectionProofCache.Get(entry, !store)) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#define BITCOIN_SCRIPT_SIGCACHE_H
|
||||
|
||||
#include <script/interpreter.h>
|
||||
#include <span.h>
|
||||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_rangeproof.h>
|
||||
|
|
@ -51,7 +52,8 @@ private:
|
|||
public:
|
||||
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {}
|
||||
|
||||
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
|
||||
bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
|
||||
bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const override;
|
||||
};
|
||||
|
||||
void InitSignatureCache();
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
|
|||
case TxoutType::NONSTANDARD:
|
||||
case TxoutType::NULL_DATA:
|
||||
case TxoutType::WITNESS_UNKNOWN:
|
||||
case TxoutType::WITNESS_V1_TAPROOT:
|
||||
return false;
|
||||
case TxoutType::PUBKEY:
|
||||
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
|
||||
|
|
@ -267,9 +268,9 @@ private:
|
|||
|
||||
public:
|
||||
SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {}
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
{
|
||||
if (checker.CheckSig(scriptSig, vchPubKey, scriptCode, sigversion)) {
|
||||
if (checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion)) {
|
||||
CPubKey pubkey(vchPubKey);
|
||||
sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig));
|
||||
return true;
|
||||
|
|
@ -346,7 +347,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
|
|||
for (unsigned int i = last_success_key; i < num_pubkeys; ++i) {
|
||||
const valtype& pubkey = solutions[i+1];
|
||||
// We either have a signature for this pubkey, or we have found a signature and it is valid
|
||||
if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckSig(sig, pubkey, next_script, sigversion)) {
|
||||
if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckECDSASignature(sig, pubkey, next_script, sigversion)) {
|
||||
last_success_key = i + 1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -411,7 +412,7 @@ class DummySignatureChecker final : public BaseSignatureChecker
|
|||
{
|
||||
public:
|
||||
DummySignatureChecker() {}
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
|
||||
};
|
||||
const DummySignatureChecker DUMMY_CHECKER;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <pubkey.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <script/keyorigin.h>
|
||||
#include <span.h>
|
||||
#include <streams.h>
|
||||
|
||||
class CKey;
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ std::string GetTxnOutputType(TxoutType t)
|
|||
case TxoutType::NULL_DATA: return "nulldata";
|
||||
case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
|
||||
case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
|
||||
case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";
|
||||
case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
|
||||
case TxoutType::TRUE: return "true";
|
||||
case TxoutType::FEE: return "fee";
|
||||
|
|
@ -153,6 +154,11 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
|
|||
vSolutionsRet.push_back(witnessprogram);
|
||||
return TxoutType::WITNESS_V0_SCRIPTHASH;
|
||||
}
|
||||
if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
|
||||
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
|
||||
vSolutionsRet.push_back(std::move(witnessprogram));
|
||||
return TxoutType::WITNESS_V1_TAPROOT;
|
||||
}
|
||||
if (witnessversion != 0) {
|
||||
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
|
||||
vSolutionsRet.push_back(std::move(witnessprogram));
|
||||
|
|
@ -231,7 +237,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
|
|||
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
|
||||
addressRet = hash;
|
||||
return true;
|
||||
} else if (whichType == TxoutType::WITNESS_UNKNOWN) {
|
||||
} else if (whichType == TxoutType::WITNESS_UNKNOWN || whichType == TxoutType::WITNESS_V1_TAPROOT) {
|
||||
WitnessUnknown unk;
|
||||
unk.version = vSolutions[0][0];
|
||||
std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ enum class TxoutType {
|
|||
NULL_DATA, //!< unspendable OP_RETURN script that carries data
|
||||
WITNESS_V0_SCRIPTHASH,
|
||||
WITNESS_V0_KEYHASH,
|
||||
WITNESS_V1_TAPROOT,
|
||||
WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
|
||||
TRUE, // For testing purposes only
|
||||
// ELEMENTS:
|
||||
|
|
@ -266,7 +267,8 @@ public:
|
|||
* * ScriptHash: TxoutType::SCRIPTHASH destination (P2SH)
|
||||
* * WitnessV0ScriptHash: TxoutType::WITNESS_V0_SCRIPTHASH destination (P2WSH)
|
||||
* * WitnessV0KeyHash: TxoutType::WITNESS_V0_KEYHASH destination (P2WPKH)
|
||||
* * WitnessUnknown: TxoutType::WITNESS_UNKNOWN destination (P2W???)
|
||||
* * WitnessUnknown: TxoutType::WITNESS_UNKNOWN/WITNESS_V1_TAPROOT destination (P2W???)
|
||||
* (taproot outputs do not require their own type as long as no wallet support exists)
|
||||
* * NullData: TxoutType::NULL_DATA destination (OP_RETURN)
|
||||
* A CTxDestination is the internal data type encoded in a bitcoin address
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue