Skip to content

test(blockassembly): fix TestHandleReorgWithInvalidBlock_Integration on main#714

Merged
freemans13 merged 1 commit into
bsv-blockchain:mainfrom
freemans13:fix/reorg-invalid-block-test-mined-set
Apr 16, 2026
Merged

test(blockassembly): fix TestHandleReorgWithInvalidBlock_Integration on main#714
freemans13 merged 1 commit into
bsv-blockchain:mainfrom
freemans13:fix/reorg-invalid-block-test-mined-set

Conversation

@freemans13

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #545. TestHandleReorgWithInvalidBlock_Integration has been failing on main consistently since the merge (see runs 24533201543, 24532254759, 24529039544).

Root cause

The test uses nodehelpers.NewBlockchainDaemon, which spins up the blockchain service but not BlockValidation. InvalidateBlock sets mined_set=false on the affected blocks and emits a BlockMinedUnset notification that BlockValidation normally consumes to re-set mined_set=true. With no BlockValidation running, nothing replies to the notification.

The reset() path added in #691 then blocks in waitForBlockMinedSet until its retry budget is exhausted — far longer than the test's 15s Eventually window — so the test times out before BA can settle on chain B.

Error from CI:

[Reset] context cancelled while waiting for invalid block mined_set -> UNKNOWN (0): context canceled

Fix

Capture the hashes returned by InvalidateBlock and call SetBlockMinedSet on each, simulating BlockValidation's async reaction. This is a test-only change — no production code touched.

Test plan

  • go test -count=1 -run '^TestHandleReorgWithInvalidBlock_Integration$' ./services/blockassembly/ passes 3/3 runs locally
  • go vet ./services/blockassembly/... clean
  • CI green on this PR

🤖 Generated with Claude Code

…ck test

TestHandleReorgWithInvalidBlock_Integration was failing on main after bsv-blockchain#545
landed. The test uses NewBlockchainDaemon which does not run the
BlockValidation service, so when InvalidateBlock sets mined_set=false on
the affected blocks, nothing reacts to the BlockMinedUnset notification
to re-set mined_set=true. BA's reset() then blocks in waitForBlockMinedSet
until its retry budget exhausts — well past the test's 15s Eventually
window — and the test times out.

Capture the hashes returned by InvalidateBlock and call SetBlockMinedSet
on each to simulate BlockValidation's async reaction. This unblocks
reset() and lets the test exercise what it's actually verifying: that
handleReorg returns ErrBlockAssemblyReset after fallback reset so BA
settles on the new chain tip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

🤖 Claude Code Review

Status: Complete

No issues found.

Summary

This test-only change correctly addresses the test timeout issue by simulating BlockValidation's async response to InvalidateBlock. The approach is accurate:

  1. Root cause correctly diagnosed: The test uses a partial daemon setup without BlockValidation running, so the BlockMinedUnset notification sent by InvalidateBlock has no consumer.

  2. Fix accurately simulates production behavior: BlockValidation normally processes BlockMinedUnset by calling setTxMinedStatus(unsetMined=true), which eventually calls SetBlockMinedSet (services/blockvalidation/BlockValidation.go:1015). The test now calls SetBlockMinedSet directly on each invalidated hash returned by InvalidateBlock, matching what BlockValidation would do.

  3. No production code touched: Changes are confined to the integration test.

The implementation correctly uses range iteration over the returned slice and passes pointer to each hash element to SetBlockMinedSet, matching the expected signature.

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Comparison Report

Baseline: main (unknown)

Current: PR-714 (dbb68e5)

Summary

  • Regressions: 0
  • Improvements: 0
  • Unchanged: 142
  • Significance level: p < 0.05
All benchmark results (sec/op)
Benchmark Baseline Current Change p-value
_NewBlockFromBytes-4 1.689µ 1.832µ ~ 0.100
SplitSyncedParentMap_SetIfNotExists/256_buckets-4 61.72n 61.62n ~ 0.400
SplitSyncedParentMap_SetIfNotExists/16_buckets-4 61.51n 61.54n ~ 0.400
SplitSyncedParentMap_SetIfNotExists/1_bucket-4 61.59n 61.49n ~ 0.100
SplitSyncedParentMap_ConcurrentSetIfNotExists/256_buckets... 30.59n 30.48n ~ 1.000
SplitSyncedParentMap_ConcurrentSetIfNotExists/16_buckets_... 51.99n 53.04n ~ 0.400
SplitSyncedParentMap_ConcurrentSetIfNotExists/1_bucket_pa... 107.2n 106.0n ~ 0.300
MiningCandidate_Stringify_Short-4 269.1n 268.3n ~ 0.700
MiningCandidate_Stringify_Long-4 1.898µ 1.909µ ~ 0.800
MiningSolution_Stringify-4 971.0n 978.0n ~ 0.200
BlockInfo_MarshalJSON-4 1.762µ 1.738µ ~ 0.100
NewFromBytes-4 135.2n 138.8n ~ 0.100
Mine_EasyDifficulty-4 64.72µ 64.69µ ~ 1.000
Mine_WithAddress-4 4.860µ 4.865µ ~ 0.500
BlockAssembler_AddTx-4 0.02790n 0.02776n ~ 0.700
AddNode-4 10.27 10.74 ~ 0.400
AddNodeWithMap-4 10.84 11.16 ~ 0.700
DiskTxMap_SetIfNotExists-4 3.329µ 3.305µ ~ 1.000
DiskTxMap_SetIfNotExists_Parallel-4 3.532µ 3.352µ ~ 1.000
DiskTxMap_ExistenceOnly-4 333.6n 305.3n ~ 0.700
Queue-4 148.8n 152.0n ~ 0.100
AtomicPointer-4 2.511n 2.517n ~ 1.000
ReorgOptimizations/DedupFilterPipeline/Old/10K-4 648.9µ 670.4µ ~ 0.100
ReorgOptimizations/DedupFilterPipeline/New/10K-4 642.1µ 654.7µ ~ 0.100
ReorgOptimizations/AllMarkFalse/Old/10K-4 89.49µ 101.47µ ~ 0.100
ReorgOptimizations/AllMarkFalse/New/10K-4 49.94µ 49.43µ ~ 0.200
ReorgOptimizations/HashSlicePool/Old/10K-4 45.20µ 55.12µ ~ 0.100
ReorgOptimizations/HashSlicePool/New/10K-4 8.759µ 8.657µ ~ 0.700
ReorgOptimizations/NodeFlags/Old/10K-4 3.861µ 4.444µ ~ 0.100
ReorgOptimizations/NodeFlags/New/10K-4 1.290µ 1.896µ ~ 0.100
ReorgOptimizations/DedupFilterPipeline/Old/100K-4 7.239m 7.782m ~ 0.100
ReorgOptimizations/DedupFilterPipeline/New/100K-4 7.819m 8.099m ~ 0.700
ReorgOptimizations/AllMarkFalse/Old/100K-4 898.7µ 944.0µ ~ 0.100
ReorgOptimizations/AllMarkFalse/New/100K-4 549.5µ 547.5µ ~ 0.400
ReorgOptimizations/HashSlicePool/Old/100K-4 495.0µ 500.7µ ~ 1.000
ReorgOptimizations/HashSlicePool/New/100K-4 199.9µ 200.0µ ~ 0.700
ReorgOptimizations/NodeFlags/Old/100K-4 41.08µ 40.86µ ~ 1.000
ReorgOptimizations/NodeFlags/New/100K-4 14.33µ 14.59µ ~ 0.100
TxMapSetIfNotExists-4 35.53n 35.99n ~ 0.200
TxMapSetIfNotExistsDuplicate-4 29.77n 30.17n ~ 0.100
ChannelSendReceive-4 453.8n 459.8n ~ 0.100
DirectSubtreeAdd/4_per_subtree-4 74.89n 74.48n ~ 1.000
DirectSubtreeAdd/64_per_subtree-4 41.07n 42.15n ~ 0.200
DirectSubtreeAdd/256_per_subtree-4 40.25n 40.03n ~ 0.100
DirectSubtreeAdd/1024_per_subtree-4 38.68n 38.64n ~ 0.400
DirectSubtreeAdd/2048_per_subtree-4 38.32n 38.22n ~ 0.700
SubtreeProcessorAdd/4_per_subtree-4 385.5n 381.1n ~ 0.400
SubtreeProcessorAdd/64_per_subtree-4 381.2n 386.5n ~ 0.100
SubtreeProcessorAdd/256_per_subtree-4 378.0n 384.7n ~ 0.400
SubtreeProcessorAdd/1024_per_subtree-4 385.5n 386.2n ~ 0.700
SubtreeProcessorAdd/2048_per_subtree-4 383.4n 383.5n ~ 0.700
SubtreeProcessorRotate/4_per_subtree-4 388.7n 387.7n ~ 0.700
SubtreeProcessorRotate/64_per_subtree-4 374.0n 365.8n ~ 0.100
SubtreeProcessorRotate/256_per_subtree-4 373.0n 364.4n ~ 0.100
SubtreeProcessorRotate/1024_per_subtree-4 379.6n 370.8n ~ 0.200
SubtreeNodeAddOnly/4_per_subtree-4 103.7n 103.0n ~ 0.700
SubtreeNodeAddOnly/64_per_subtree-4 70.97n 71.17n ~ 1.000
SubtreeNodeAddOnly/256_per_subtree-4 70.43n 70.09n ~ 0.700
SubtreeNodeAddOnly/1024_per_subtree-4 70.04n 69.88n ~ 0.700
SubtreeCreationOnly/4_per_subtree-4 206.0n 208.4n ~ 0.700
SubtreeCreationOnly/64_per_subtree-4 1.206µ 1.200µ ~ 1.000
SubtreeCreationOnly/256_per_subtree-4 2.986µ 2.816µ ~ 0.200
SubtreeCreationOnly/1024_per_subtree-4 8.143µ 8.143µ ~ 1.000
SubtreeCreationOnly/2048_per_subtree-4 12.24µ 11.86µ ~ 0.700
SubtreeProcessorOverheadBreakdown/64_per_subtree-4 382.4n 379.0n ~ 0.700
SubtreeProcessorOverheadBreakdown/1024_per_subtree-4 382.2n 382.4n ~ 1.000
ParallelGetAndSetIfNotExists/1k_nodes-4 1.088m 1.086m ~ 0.400
ParallelGetAndSetIfNotExists/10k_nodes-4 2.485m 2.521m ~ 0.400
ParallelGetAndSetIfNotExists/50k_nodes-4 10.93m 11.10m ~ 0.100
ParallelGetAndSetIfNotExists/100k_nodes-4 21.18m 21.53m ~ 0.200
SequentialGetAndSetIfNotExists/1k_nodes-4 844.9µ 846.1µ ~ 0.400
SequentialGetAndSetIfNotExists/10k_nodes-4 3.709m 3.696m ~ 0.700
SequentialGetAndSetIfNotExists/50k_nodes-4 13.06m 13.05m ~ 1.000
SequentialGetAndSetIfNotExists/100k_nodes-4 24.69m 24.89m ~ 0.400
ProcessOwnBlockSubtreeNodesParallel/1k_nodes-4 1.158m 1.170m ~ 0.400
ProcessOwnBlockSubtreeNodesParallel/10k_nodes-4 5.649m 5.661m ~ 0.400
ProcessOwnBlockSubtreeNodesParallel/100k_nodes-4 25.75m 26.11m ~ 0.400
ProcessOwnBlockSubtreeNodesSequential/1k_nodes-4 990.4µ 979.8µ ~ 0.200
ProcessOwnBlockSubtreeNodesSequential/10k_nodes-4 6.872m 6.866m ~ 1.000
ProcessOwnBlockSubtreeNodesSequential/100k_nodes-4 47.36m 48.10m ~ 0.700
CalcBlockWork-4 498.9n 503.6n ~ 0.100
CalculateWork-4 676.5n 705.0n ~ 0.700
BuildBlockLocatorString_Helpers/Size_10-4 1.289µ 1.279µ ~ 0.200
BuildBlockLocatorString_Helpers/Size_100-4 12.33µ 14.06µ ~ 0.400
BuildBlockLocatorString_Helpers/Size_1000-4 121.1µ 121.1µ ~ 1.000
CatchupWithHeaderCache-4 104.0m 103.8m ~ 0.200
SubtreeSizes/10k_tx_4_per_subtree-4 1.326m 1.296m ~ 0.200
SubtreeSizes/10k_tx_16_per_subtree-4 315.4µ 309.7µ ~ 1.000
SubtreeSizes/10k_tx_64_per_subtree-4 74.07µ 72.74µ ~ 0.100
SubtreeSizes/10k_tx_256_per_subtree-4 18.41µ 18.14µ ~ 0.400
SubtreeSizes/10k_tx_512_per_subtree-4 9.197µ 9.009µ ~ 0.400
SubtreeSizes/10k_tx_1024_per_subtree-4 4.564µ 4.509µ ~ 0.200
SubtreeSizes/10k_tx_2k_per_subtree-4 2.263µ 2.239µ ~ 0.700
BlockSizeScaling/10k_tx_64_per_subtree-4 72.04µ 72.12µ ~ 0.700
BlockSizeScaling/10k_tx_256_per_subtree-4 18.31µ 19.33µ ~ 0.100
BlockSizeScaling/10k_tx_1024_per_subtree-4 4.666µ 4.531µ ~ 0.100
BlockSizeScaling/50k_tx_64_per_subtree-4 379.1µ 383.0µ ~ 0.400
BlockSizeScaling/50k_tx_256_per_subtree-4 91.58µ 92.60µ ~ 0.400
BlockSizeScaling/50k_tx_1024_per_subtree-4 22.86µ 23.53µ ~ 0.400
SubtreeAllocations/small_subtrees_exists_check-4 154.0µ 151.8µ ~ 0.700
SubtreeAllocations/small_subtrees_data_fetch-4 160.8µ 160.1µ ~ 1.000
SubtreeAllocations/small_subtrees_full_validation-4 317.9µ 319.6µ ~ 0.700
SubtreeAllocations/medium_subtrees_exists_check-4 8.953µ 8.984µ ~ 1.000
SubtreeAllocations/medium_subtrees_data_fetch-4 9.330µ 9.326µ ~ 1.000
SubtreeAllocations/medium_subtrees_full_validation-4 18.62µ 18.36µ ~ 0.400
SubtreeAllocations/large_subtrees_exists_check-4 2.174µ 2.145µ ~ 0.100
SubtreeAllocations/large_subtrees_data_fetch-4 2.279µ 2.325µ ~ 0.100
SubtreeAllocations/large_subtrees_full_validation-4 4.603µ 4.571µ ~ 0.200
_BufferPoolAllocation/16KB-4 3.409µ 3.424µ ~ 1.000
_BufferPoolAllocation/32KB-4 7.668µ 7.754µ ~ 1.000
_BufferPoolAllocation/64KB-4 15.59µ 16.00µ ~ 0.400
_BufferPoolAllocation/128KB-4 29.37µ 31.92µ ~ 0.100
_BufferPoolAllocation/512KB-4 130.4µ 143.3µ ~ 0.100
_BufferPoolConcurrent/32KB-4 16.70µ 18.82µ ~ 0.100
_BufferPoolConcurrent/64KB-4 26.59µ 27.85µ ~ 0.100
_BufferPoolConcurrent/512KB-4 167.5µ 171.0µ ~ 0.400
_SubtreeDeserializationWithBufferSizes/16KB-4 683.1µ 718.8µ ~ 0.100
_SubtreeDeserializationWithBufferSizes/32KB-4 697.4µ 704.4µ ~ 1.000
_SubtreeDeserializationWithBufferSizes/64KB-4 719.7µ 701.8µ ~ 0.400
_SubtreeDeserializationWithBufferSizes/128KB-4 692.1µ 690.8µ ~ 0.400
_SubtreeDeserializationWithBufferSizes/512KB-4 684.7µ 706.1µ ~ 0.100
_SubtreeDataDeserializationWithBufferSizes/16KB-4 37.76m 37.47m ~ 0.100
_SubtreeDataDeserializationWithBufferSizes/32KB-4 37.71m 37.59m ~ 1.000
_SubtreeDataDeserializationWithBufferSizes/64KB-4 37.86m 37.75m ~ 0.700
_SubtreeDataDeserializationWithBufferSizes/128KB-4 37.78m 37.82m ~ 0.700
_SubtreeDataDeserializationWithBufferSizes/512KB-4 37.89m 37.67m ~ 0.100
_PooledVsNonPooled/Pooled-4 821.4n 825.1n ~ 0.100
_PooledVsNonPooled/NonPooled-4 6.356µ 7.401µ ~ 0.100
_MemoryFootprint/Current_512KB_32concurrent-4 9.038µ 8.534µ ~ 0.700
_MemoryFootprint/Proposed_32KB_32concurrent-4 11.00µ 12.09µ ~ 0.100
_MemoryFootprint/Alternative_64KB_32concurrent-4 10.57µ 10.92µ ~ 0.100
_prepareTxsPerLevel-4 376.3m 377.9m ~ 0.700
_prepareTxsPerLevelOrdered-4 3.716m 3.948m ~ 0.700
_prepareTxsPerLevel_Comparison/Original-4 381.6m 383.1m ~ 1.000
_prepareTxsPerLevel_Comparison/Optimized-4 3.767m 3.829m ~ 0.400
StoreBlock_Sequential/BelowCSVHeight-4 308.3µ 307.7µ ~ 0.400
StoreBlock_Sequential/AboveCSVHeight-4 311.0µ 311.1µ ~ 1.000
GetUtxoHashes-4 256.8n 257.9n ~ 1.000
GetUtxoHashes_ManyOutputs-4 44.62µ 46.24µ ~ 0.200
_NewMetaDataFromBytes-4 241.0n 240.1n ~ 0.700
_Bytes-4 619.7n 630.6n ~ 0.200
_MetaBytes-4 573.0n 569.7n ~ 0.400

Threshold: >10% with p < 0.05 | Generated: 2026-04-16 21:34 UTC

@freemans13 freemans13 self-assigned this Apr 16, 2026
freemans13 added a commit to freemans13/teranode that referenced this pull request Apr 16, 2026
…ck test

TestHandleReorgWithInvalidBlock_Integration (added in bsv-blockchain#545) fails on this
branch and on main because the test setup does not run the BlockValidation
service. InvalidateBlock sets mined_set=false and emits BlockMinedUnset,
which BlockValidation normally consumes to re-set mined_set=true. With no
BlockValidation running, BA's reset() blocks in waitForBlockMinedSet until
its retry budget exhausts — past the test's 15s Eventually window.

Capture the hashes returned by InvalidateBlock and call SetBlockMinedSet
on each to simulate BlockValidation's async reaction.

This change duplicates the fix on main in bsv-blockchain#714 to unblock CI on this PR
before bsv-blockchain#714 is merged and rolled forward.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@freemans13 freemans13 merged commit 7401c71 into bsv-blockchain:main Apr 16, 2026
27 of 28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants