qa: simple differential fuzzing for sighash with/without caching
What changed, and why it matters
This commit adds a new automated test (a fuzz test) that checks Bitcoin Core's signature hash calculation behaves identically whether or not a performance cache is used. It does not change production code, fix a bug, or introduce any user-facing behavior. It is purely a quality-assurance addition.
No action required. This is a benign test-only addition. Reviewers may optionally verify the fuzz target compiles and the differential assertion is sound.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch adds a new FUZZ_TARGET named sighash_cache in src/test/fuzz/script_interpreter.cpp. It generates random transaction/script inputs and compares SignatureHash(...) outputs with and without a SigHashCache object across 100 random hash types, asserting equality. It includes util/check.h for the Assert macro. No production code is modified.
Changed components
src/test/fuzz/script_interpreter.cppInspect captured patch +25 / −0
diff --git a/src/test/fuzz/script_interpreter.cpp b/src/test/fuzz/script_interpreter.cpp
index 9e3ad02b..2c2ce855 100644
--- a/src/test/fuzz/script_interpreter.cpp
+++ b/src/test/fuzz/script_interpreter.cpp
@@ -7,6 +7,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <util/check.h>
#include <cstdint>
#include <optional>
@@ -45,3 +46,27 @@ FUZZ_TARGET(script_interpreter)
(void)CastToBool(ConsumeRandomLengthByteVector(fuzzed_data_provider));
}
}
+
+/** Differential fuzzing for SignatureHash with and without cache. */
+FUZZ_TARGET(sighash_cache)
+{
+ FuzzedDataProvider provider(buffer.data(), buffer.size());
+
+ // Get inputs to the sighash function that won't change across types.
+ const auto scriptcode{ConsumeScript(provider)};
+ const auto tx{ConsumeTransaction(provider, std::nullopt)};
+ if (tx.vin.empty()) return;
+ const auto in_index{provider.ConsumeIntegralInRange<uint32_t>(0, tx.vin.size() - 1)};
+ const auto amount{ConsumeMoney(provider)};
+ const auto sigversion{(SigVersion)provider.ConsumeIntegralInRange(0, 1)};
+
+ // Check the sighash function will give the same result for 100 fuzzer-generated hash types whether or not a cache is
+ // provided. The cache is conserved across types to exercise cache hits.
+ SigHashCache sighash_cache{};
+ for (int i{0}; i < 100; ++i) {
+ const auto hash_type{((i & 2) == 0) ? provider.ConsumeIntegral<int8_t>() : provider.ConsumeIntegral<int32_t>()};
+ const auto nocache_res{SignatureHash(scriptcode, tx, in_index, hash_type, amount, sigversion)};
+ const auto cache_res{SignatureHash(scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, &sighash_cache)};
+ Assert(nocache_res == cache_res);
+ }
+}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.