txscript: align empty CHECKSIG pubkey validation with Core
What changed, and why it matters
This change fixes a subtle mismatch between btcd and Bitcoin Core in how they handle a specific edge case in transaction script validation. When a signature is empty in a CHECKSIG operation, btcd previously skipped checking whether the provided public key was properly encoded and simply returned 'false'. Bitcoin Core, however, checks the public key encoding first and fails the script if the key is malformed. This patch makes btcd behave the same way, which is important for keeping all Bitcoin nodes in agreement about which transactions are valid.
Review and merge promptly, as consensus divergence between node implementations can lead to chain splits or transaction relay differences. Run the refreshed Bitcoin Core script reference tests to confirm alignment. Consider whether the same issue exists in related opcodes such as CHECKMULTISIG or CHECKSIGVERIFY.
Security signals we found
Consensus behavior divergence from reference implementation (Bitcoin Core)
Script validation edge case in CHECKSIG with empty signature
Potential for transaction validity disagreement between node implementations
Reference test suite update driving the change
Evidence from the diff
In txscript/opcode.go, opcodeCheckSig now calls vm.checkPubKeyEncoding(pkBytes) before pushing false to the stack when the full signature is empty and tapscript verification is not active. Previously, the empty-signature path bypassed pubkey encoding validation. This aligns btcd with refreshed Bitcoin Core script reference tests that expect the encoding check to run even when the signature is empty. The change is a consensus-compatibility fix rather than a new feature.
Changed components
txscript/opcode.goopcodeCheckSig functionnon-tapscript CHECKSIG validation pathInspect captured patch +4 / −0
diff --git a/txscript/opcode.go b/txscript/opcode.go
index 7326dab..fcd6803 100644
--- a/txscript/opcode.go
+++ b/txscript/opcode.go
@@ -1993,6 +1993,10 @@ func opcodeCheckSig(op *opcode, data []byte, vm *Engine) error {
// This only applies if tapscript verification isn't active, as this
// check is done within the sighash itself.
if vm.taprootCtx == nil && len(fullSigBytes) < 1 {
+ if err := vm.checkPubKeyEncoding(pkBytes); err != nil {
+ return err
+ }
+
vm.dstack.PushBool(false)
return nil
}
Why this scored 46/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.