This PR fixes a potential null pointer dereference crash in the CalculateSequenceLocks function when processing time-based sequence locks with invalid input heights.
Problem
The CalculateSequenceLocks function in src/consensus/tx_verify.cpp was using Assert(block.GetAncestor(std::max(nCoinHeight - 1, 0)))->GetMedianTimePast() which could return a null pointer when the ancestor block doesn’t exist (e.g., when input height is greater than current block height), leading to a crash when attempting to dereference it.
Solution
Modified the code to safely check if the ancestor block exists before attempting to access its median time past. When the ancestor lookup fails, the time-based sequence lock calculation is skipped for that input, preventing the crash while maintaining correct behavior.
Changes
- src/consensus/tx_verify.cpp: Added null pointer check in
CalculateSequenceLocksbefore accessing ancestor block for time-based sequence locks - src/test/transaction_tests.cpp: Added comprehensive unit tests covering edge cases for invalid heights, valid heights, and empty transactions
- test/functional/feature_bip68_sequence.py: Added functional regression test to verify the fix works in real transaction processing
Testing
The fix includes:
- Unit tests for
CalculateSequenceLockswith invalid input heights (regression test) - Unit tests for normal operation with valid heights
- Unit tests for edge cases like empty transactions
- Functional test that creates and processes a transaction with time-based sequence locks to ensure no crash occurs
All tests pass and the change maintains backward compatibility while preventing the crash scenario.