Problem
txMeta.BlockIDs and txMeta.BlockHeights are parallel arrays of every block a tx has been observed in, including orphans. Several call sites pick index [0] without checking main-chain membership. Under fork conditions, index 0 can be an orphan block.
The /merkle_proof instance is tracked separately. This issue is to audit the rest.
Call sites to review
services/validator/Validator.go:886 — utxoHeights[idx] = txMeta.BlockHeights[0]. Parent tx height used for UTXO maturity (coinbase 100-block rule) and locktime evaluation. If the parent crossed a fork boundary, height could be off-by-one or wrong-fork. Likely bug — coinbase maturity is consensus-critical.
services/blockvalidation/quick_validate.go:399, 400, 522, 523, 651, 652 — existingBlockID = uint64(existingMeta.BlockIDs[0]) and block.ID = existingMeta.BlockIDs[0] during retry-resume. If existing meta has multiple block IDs, wrong fork's ID may be adopted.
model/Block.go:969 — parentTxMeta.BlockIDs[0] == GenesisBlockID. Genesis check; probably fine since genesis cannot be in a fork, but worth a comment.
Action
For each call site, determine whether the index-0 assumption is safe (e.g., genesis special case) or needs a main-chain filter. Add a regression test per site touched.
Out of scope
stores/utxo writes that just track all blocks where a tx appears — these are correct as-is, no main-chain restriction wanted.
Problem
txMeta.BlockIDsandtxMeta.BlockHeightsare parallel arrays of every block a tx has been observed in, including orphans. Several call sites pick index[0]without checking main-chain membership. Under fork conditions, index 0 can be an orphan block.The
/merkle_proofinstance is tracked separately. This issue is to audit the rest.Call sites to review
services/validator/Validator.go:886—utxoHeights[idx] = txMeta.BlockHeights[0]. Parent tx height used for UTXO maturity (coinbase 100-block rule) and locktime evaluation. If the parent crossed a fork boundary, height could be off-by-one or wrong-fork. Likely bug — coinbase maturity is consensus-critical.services/blockvalidation/quick_validate.go:399, 400, 522, 523, 651, 652—existingBlockID = uint64(existingMeta.BlockIDs[0])andblock.ID = existingMeta.BlockIDs[0]during retry-resume. If existing meta has multiple block IDs, wrong fork's ID may be adopted.model/Block.go:969—parentTxMeta.BlockIDs[0] == GenesisBlockID. Genesis check; probably fine since genesis cannot be in a fork, but worth a comment.Action
For each call site, determine whether the index-0 assumption is safe (e.g., genesis special case) or needs a main-chain filter. Add a regression test per site touched.
Out of scope
stores/utxowrites that just track all blocks where a tx appears — these are correct as-is, no main-chain restriction wanted.