Skip to content

Commit bd482b3

Browse files
author
MarcoFalke
committed
Merge bitcoin/bitcoin#24105: Optimize CHECKSIGADD Script Validation
cfa5752 Optimize CHECKSIGADD Script Validation (Jeremy Rubin) Pull request description: This is a mild validation improvement that improves performance by caching some signature data when you have a Taproot script fragment that uses CHECKSIGADD Multisignatures with sighash single. In some basic testing I showed this to have about a 0.6% speedup during block validation for a block with a lot of CHECKSIGADDs, but that was with the entirety of block validation so the specific impact on the script interpreter performance should be a bit more once you subtract things like coin fetching. If desired I can produce a more specific/sharable bench for this, the code I used to test was just monkey patching the existing taproot tests since generating valid spends is kinda tricky. But it's sort of an obvious win so I'm not sure it needs a rigorous bench, but I will tinker on one of those while the code is being reviewed for correctness. The overhead of this approach is that: 1. ScriptExecutionData is no longer const 2. around 32 bytes of extra stack space 3. zero extra hashing since we only cache on first use ACKs for top commit: sipa: utACK cfa5752 MarcoFalke: review ACK cfa5752 jonatack: ACK cfa5752 theStack: Code-review ACK cfa5752 Tree-SHA512: d5938773724bb9c97b6fd623ef7efdf7f522af52dc0903ecb88c38a518b628d7915b7eae6a774f7be653dc6bcd92e9abc4dd5e8b11f3a995e01e0102d2113d09
2 parents 69ef0a1 + cfa5752 commit bd482b3

File tree

4 files changed

+18
-11
lines changed

4 files changed

+18
-11
lines changed

src/script/interpreter.cpp

+8-5
Original file line numberDiff line numberDiff line change
@@ -1500,7 +1500,7 @@ static bool HandleMissingData(MissingDataBehavior mdb)
15001500
}
15011501

15021502
template<typename T>
1503-
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, MissingDataBehavior mdb)
1503+
bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb)
15041504
{
15051505
uint8_t ext_flag, key_version;
15061506
switch (sigversion) {
@@ -1568,9 +1568,12 @@ bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata
15681568
// Data about the output (if only one).
15691569
if (output_type == SIGHASH_SINGLE) {
15701570
if (in_pos >= tx_to.vout.size()) return false;
1571-
CHashWriter sha_single_output(SER_GETHASH, 0);
1572-
sha_single_output << tx_to.vout[in_pos];
1573-
ss << sha_single_output.GetSHA256();
1571+
if (!execdata.m_output_hash) {
1572+
CHashWriter sha_single_output(SER_GETHASH, 0);
1573+
sha_single_output << tx_to.vout[in_pos];
1574+
execdata.m_output_hash = sha_single_output.GetSHA256();
1575+
}
1576+
ss << execdata.m_output_hash.value();
15741577
}
15751578

15761579
// Additional data for BIP 342 signatures
@@ -1692,7 +1695,7 @@ bool GenericTransactionSignatureChecker<T>::CheckECDSASignature(const std::vecto
16921695
}
16931696

16941697
template <class T>
1695-
bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey_in, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror) const
1698+
bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey_in, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) const
16961699
{
16971700
assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
16981701
// Schnorr signatures have 32-byte public keys. The caller is responsible for enforcing this.

src/script/interpreter.h

+8-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <span.h>
1212
#include <primitives/transaction.h>
1313

14+
#include <optional>
1415
#include <vector>
1516
#include <stdint.h>
1617

@@ -215,6 +216,9 @@ struct ScriptExecutionData
215216
bool m_validation_weight_left_init = false;
216217
//! How much validation weight is left (decremented for every successful non-empty signature check).
217218
int64_t m_validation_weight_left;
219+
220+
//! The hash of the corresponding output
221+
std::optional<uint256> m_output_hash;
218222
};
219223

220224
/** Signature hash sizes */
@@ -244,7 +248,7 @@ class BaseSignatureChecker
244248
return false;
245249
}
246250

247-
virtual bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const
251+
virtual bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const
248252
{
249253
return false;
250254
}
@@ -272,7 +276,7 @@ enum class MissingDataBehavior
272276
};
273277

274278
template<typename T>
275-
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, MissingDataBehavior mdb);
279+
bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb);
276280

277281
template <class T>
278282
class GenericTransactionSignatureChecker : public BaseSignatureChecker
@@ -292,7 +296,7 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker
292296
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, MissingDataBehavior mdb) : txTo(txToIn), m_mdb(mdb), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
293297
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn, MissingDataBehavior mdb) : txTo(txToIn), m_mdb(mdb), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
294298
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
295-
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
299+
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
296300
bool CheckLockTime(const CScriptNum& nLockTime) const override;
297301
bool CheckSequence(const CScriptNum& nSequence) const override;
298302
};
@@ -313,7 +317,7 @@ class DeferringSignatureChecker : public BaseSignatureChecker
313317
return m_checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion);
314318
}
315319

316-
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
320+
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
317321
{
318322
return m_checker.CheckSchnorrSignature(sig, pubkey, sigversion, execdata, serror);
319323
}

src/script/sign.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ class DummySignatureChecker final : public BaseSignatureChecker
542542
public:
543543
DummySignatureChecker() {}
544544
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
545-
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror) const override { return true; }
545+
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) const override { return true; }
546546
};
547547
const DummySignatureChecker DUMMY_CHECKER;
548548

src/test/fuzz/signature_checker.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class FuzzedSignatureChecker : public BaseSignatureChecker
3434
return m_fuzzed_data_provider.ConsumeBool();
3535
}
3636

37-
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
37+
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
3838
{
3939
return m_fuzzed_data_provider.ConsumeBool();
4040
}

0 commit comments

Comments
 (0)