script: (refactor) prepare for introducing sighash midstate cache
What changed, and why it matters
This commit is a code cleanup in Bitcoin Core's signature hashing function. It moves the invalid SIGHASH_SINGLE check earlier and consolidates the final hashing step so both code paths use the same HashWriter. There is no security bug being fixed here; it is preparation for a future performance improvement (a sighash midstate cache).
No security action required. Treat as normal refactoring commit; review the follow-up cache introduction commit when it appears.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The refactor restructures SignatureHash() in src/script/interpreter.cpp. The SIGHASH_SINGLE out-of-range check is hoisted before the sigversion branch, and the HashWriter is instantiated once at the top. The WITNESS_V0 branch no longer creates its own HashWriter, and the non-witness branch now serializes into the shared ss before appending nHashType. Behavior is preserved; the change is purely structural to ease a later cache introduction.
Changed components
src/script/interpreter.cppSignatureHash()SegWit signature hashingInspect captured patch +22 / −22
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 61ea7f45..765c415e 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1569,6 +1569,18 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
{
assert(nIn < txTo.vin.size());
+ if (sigversion != SigVersion::WITNESS_V0) {
+ // Check for invalid use of SIGHASH_SINGLE
+ if ((nHashType & 0x1f) == SIGHASH_SINGLE) {
+ if (nIn >= txTo.vout.size()) {
+ // nOut out of range
+ return uint256::ONE;
+ }
+ }
+ }
+
+ HashWriter ss{};
+
if (sigversion == SigVersion::WITNESS_V0) {
uint256 hashPrevouts;
uint256 hashSequence;
@@ -1583,16 +1595,14 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
hashSequence = cacheready ? cache->hashSequence : SHA256Uint256(GetSequencesSHA256(txTo));
}
-
if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
hashOutputs = cacheready ? cache->hashOutputs : SHA256Uint256(GetOutputsSHA256(txTo));
} else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
- HashWriter ss{};
- ss << txTo.vout[nIn];
- hashOutputs = ss.GetHash();
+ HashWriter inner_ss{};
+ inner_ss << txTo.vout[nIn];
+ hashOutputs = inner_ss.GetHash();
}
- HashWriter ss{};
// Version
ss << txTo.version;
// Input prevouts/nSequence (none/all, depending on flags)
@@ -1609,26 +1619,16 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
ss << hashOutputs;
// Locktime
ss << txTo.nLockTime;
- // Sighash type
- ss << nHashType;
-
- return ss.GetHash();
- }
+ } else {
+ // Wrapper to serialize only the necessary parts of the transaction being signed
+ CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, nHashType);
- // Check for invalid use of SIGHASH_SINGLE
- if ((nHashType & 0x1f) == SIGHASH_SINGLE) {
- if (nIn >= txTo.vout.size()) {
- // nOut out of range
- return uint256::ONE;
- }
+ // Serialize
+ ss << txTmp;
}
- // Wrapper to serialize only the necessary parts of the transaction being signed
- CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, nHashType);
-
- // Serialize and hash
- HashWriter ss{};
- ss << txTmp << nHashType;
+ // Add sighash type and hash.
+ ss << nHashType;
return ss.GetHash();
}
Why this scored 12/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.