From 330de894a9a48515d9a473448b6c67adc3d188be Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 11 Sep 2020 14:33:52 -0700 Subject: [PATCH] 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. --- src/script/interpreter.cpp | 38 +++++++++++++++++++---------- src/script/interpreter.h | 15 ++++++++++-- src/test/fuzz/signature_checker.cpp | 2 +- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 07cc525f5..a9d28c25d 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -371,7 +371,7 @@ static bool EvalChecksig(const valtype& vchSig, const valtype& vchPubKey, CScrip return true; } -bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror) +bool EvalScript(std::vector >& 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 >& stack, const CScript& return set_success(serror); } +bool EvalScript(std::vector >& 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 -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::CheckECDSASignature(const std::vecto } template -bool GenericTransactionSignatureChecker::CheckSchnorrSignature(Span sig, Span pubkey_in, SigVersion sigversion, ScriptError* serror) const +bool GenericTransactionSignatureChecker::CheckSchnorrSignature(Span sig, Span 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::CheckSchnorrSignature(Spantxdata); - 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::CheckSequence(const CScriptNum& nSeq template class GenericTransactionSignatureChecker; template class GenericTransactionSignatureChecker; -static bool ExecuteWitnessScript(const Span& stack_span, const CScript& scriptPubKey, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptError* serror) +static bool ExecuteWitnessScript(const Span& stack_span, const CScript& scriptPubKey, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror) { std::vector stack{stack_span.begin(), stack_span.end()}; @@ -1674,7 +1680,7 @@ static bool ExecuteWitnessScript(const Span& 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 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); diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 2ffe18dd9..55aa1f230 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -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 sig, Span pubkey, SigVersion sigversion, ScriptError* serror = nullptr) const + virtual bool CheckSchnorrSignature(Span sig, Span 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& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override; - bool CheckSchnorrSignature(Span sig, Span pubkey, SigVersion sigversion, ScriptError* serror = nullptr) const override; + bool CheckSchnorrSignature(Span sig, Span 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; using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker; +bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr); bool EvalScript(std::vector >& 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); diff --git a/src/test/fuzz/signature_checker.cpp b/src/test/fuzz/signature_checker.cpp index f538c9272..e121c8966 100644 --- a/src/test/fuzz/signature_checker.cpp +++ b/src/test/fuzz/signature_checker.cpp @@ -33,7 +33,7 @@ public: return m_fuzzed_data_provider.ConsumeBool(); } - bool CheckSchnorrSignature(Span sig, Span pubkey, SigVersion sigversion, ScriptError* serror = nullptr) const override + bool CheckSchnorrSignature(Span sig, Span pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override { return m_fuzzed_data_provider.ConsumeBool(); }