While reviewing spec coverage of src/test/data/script_tests.json against the script interpreter, I found two gaps that are testable within this file's harness but were never covered:
1. OP_CHECKSIGVERIFY / OP_CHECKMULTISIGVERIFY failure paths
OP_CHECKSIGVERIFY never appears anywhere in the file, and no vector expects the CHECKSIGVERIFY or CHECKMULTISIGVERIFY script errors, so the VERIFY tail of both opcodes (interpreter.cpp, case OP_CHECKSIGVERIFY) is untested here. This commit adds static vectors that fail the signature check with an empty signature and a valid pubkey, so each opcode returns its opcode-specific error code. The success paths require real signatures and remain covered by the auto-generated tests and functional tests.
2. CHECKLOCKTIMEVERIFY (BIP65) failure paths
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY is never set by any vector: CHECKLOCKTIMEVERIFY only appears as an unflagged NOP, so none of the BIP65 semantics are exercised, while the equivalent CHECKSEQUENCEVERIFY section has existed since #7994. This commit adds a section mirroring the CSV tests, covering every failure path reachable in this harness:
- empty stack →
INVALID_STACK_OPERATION - negative operand →
NEGATIVE_LOCKTIME - negative zero (
0x80), evaluated as 0 byCScriptNum→UNSATISFIED_LOCKTIMErather thanNEGATIVE_LOCKTIME - non-minimal encoding under MINIMALDATA →
SCRIPTNUM - final input nSequence (lock time requirement itself satisfied) →
UNSATISFIED_LOCKTIME - operand greater than the tx nLockTime →
UNSATISFIED_LOCKTIME - height/time type mismatch →
UNSATISFIED_LOCKTIME - 5-byte operand (2^32) accepted by the parser, then failing the type check →
UNSATISFIED_LOCKTIME
Unlike CSV (where an operand with bit 31 set makes the opcode pass without calling CheckSequence), the CLTV success path cannot be expressed in this file, because the test harness spends with nLockTime=0 and a final nSequence; it is covered by tx_valid.json and functional tests instead. A comment in the JSON notes this.
3. Negative zero vector for the existing CHECKSEQUENCEVERIFY section
Following review feedback, the third commit adds the same negative-zero vector to the existing CSV section: the footgun is identical there (a re-implementation treating any operand with the sign bit set as negative would return NEGATIVE_LOCKTIME instead of reaching CheckSequence), and it keeps the two sections mirrored.