mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-25 10:43:19 -03:00
Use ScriptExecutionData to pass through annex hash
Instead of recomputing the annex hash every time a signature is verified, compute it once and cache it in a new ScriptExecutionData structure.
This commit is contained in:
parent
8bbed4b7ac
commit
330de894a9
3 changed files with 39 additions and 16 deletions
|
@ -371,7 +371,7 @@ 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)
|
||||
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);
|
||||
|
@ -1159,6 +1159,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 {
|
||||
|
||||
/**
|
||||
|
@ -1384,7 +1390,7 @@ static const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch");
|
|||
static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
|
||||
|
||||
template<typename T>
|
||||
bool SignatureHashSchnorr(uint256& hash_out, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache)
|
||||
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;
|
||||
switch (sigversion) {
|
||||
|
@ -1423,8 +1429,8 @@ bool SignatureHashSchnorr(uint256& hash_out, const T& tx_to, uint32_t in_pos, ui
|
|||
}
|
||||
|
||||
// Data about the input/prevout being spent
|
||||
const auto& witstack = tx_to.vin[in_pos].scriptWitness.stack;
|
||||
bool have_annex = witstack.size() > 1 && witstack.back().size() > 0 && witstack.back()[0] == ANNEX_TAG;
|
||||
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) {
|
||||
|
@ -1435,7 +1441,7 @@ bool SignatureHashSchnorr(uint256& hash_out, const T& tx_to, uint32_t in_pos, ui
|
|||
ss << in_pos;
|
||||
}
|
||||
if (have_annex) {
|
||||
ss << (CHashWriter(SER_GETHASH, 0) << witstack.back()).GetSHA256();
|
||||
ss << execdata.m_annex_hash;
|
||||
}
|
||||
|
||||
// Data about the output (if only one).
|
||||
|
@ -1553,7 +1559,7 @@ 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, ScriptError* serror) const
|
||||
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);
|
||||
// Schnorr signatures have 32-byte public keys. The caller is responsible for enforcing this.
|
||||
|
@ -1569,7 +1575,7 @@ bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const uns
|
|||
}
|
||||
uint256 sighash;
|
||||
assert(this->txdata);
|
||||
if (!SignatureHashSchnorr(sighash, *txTo, nIn, hashtype, sigversion, *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);
|
||||
|
@ -1664,7 +1670,7 @@ 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()};
|
||||
|
||||
|
@ -1674,7 +1680,7 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
|
|||
}
|
||||
|
||||
// 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);
|
||||
|
@ -1707,6 +1713,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
|
|||
{
|
||||
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) {
|
||||
|
@ -1721,14 +1728,14 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
|
|||
if (memcmp(hash_exec_script.begin(), program.data(), 32)) {
|
||||
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
|
||||
}
|
||||
return ExecuteWitnessScript(stack, exec_script, 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) {
|
||||
// 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
|
||||
}
|
||||
exec_script << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||
return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, serror);
|
||||
return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, serror);
|
||||
} else {
|
||||
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH);
|
||||
}
|
||||
|
@ -1738,11 +1745,16 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
|
|||
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)
|
||||
SpanPopBack(stack);
|
||||
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, serror)) {
|
||||
if (!checker.CheckSchnorrSignature(stack.front(), program, SigVersion::TAPROOT, execdata, serror)) {
|
||||
return false; // serror is set
|
||||
}
|
||||
return set_success(serror);
|
||||
|
|
|
@ -170,6 +170,16 @@ enum class SigVersion
|
|||
TAPROOT = 2, //!< Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, key path spending; see BIP 341
|
||||
};
|
||||
|
||||
struct ScriptExecutionData
|
||||
{
|
||||
//! 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;
|
||||
};
|
||||
|
||||
/** Signature hash sizes */
|
||||
static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32;
|
||||
static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20;
|
||||
|
@ -192,7 +202,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
virtual bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptError* serror = nullptr) const
|
||||
virtual bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -227,7 +237,7 @@ public:
|
|||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
|
||||
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, ScriptError* serror = nullptr) 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;
|
||||
};
|
||||
|
@ -235,6 +245,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);
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
return m_fuzzed_data_provider.ConsumeBool();
|
||||
}
|
||||
|
||||
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptError* serror = nullptr) const override
|
||||
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
|
||||
{
|
||||
return m_fuzzed_data_provider.ConsumeBool();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue