Skip to content

fix: block assembly reset skips intermediate block finalization and handleReorg returns nil after fallback reset#545

Open
freemans13 wants to merge 1 commit intobsv-blockchain:mainfrom
freemans13:fix/block-assembly-reset-bugs
Open

fix: block assembly reset skips intermediate block finalization and handleReorg returns nil after fallback reset#545
freemans13 wants to merge 1 commit intobsv-blockchain:mainfrom
freemans13:fix/block-assembly-reset-bugs

Conversation

@freemans13
Copy link
Collaborator

Summary

Fix two bugs in block assembly's reset logic that cause intermediate blocks to be improperly processed during reorgs/resets:

  • Bug 1: SubtreeProcessor.reset() only called finalizeBlockProcessing() (and thus SetBlockProcessedAt) for the last moveForward block. Intermediate blocks never got processed_at set, meaning they were not recognized as fully processed.
  • Bug 2: handleReorg() returned nil after fallback reset (triggered by invalid block or failed Reorg), allowing processNewBlockAnnouncement to overwrite the reset's setBestBlockHeader with a potentially stale value captured before the reset ran.

Changes

Bug 1 Fix (SubtreeProcessor.go)

  • Non-legacy path: Added finalizeBlockProcessing(ctx, block) inside the per-block moveForward loop so each block gets SetBlockProcessedAt called individually.
  • Legacy sync path: Replaced single currentBlockHeader.Store() + updatePrecomputedMiningData() with per-block finalizeBlockProcessing() loop after concurrent coinbase processing.
  • Changed post-loop conditional to only handle the moveBack-only case (no moveForward blocks).

Bug 2 Fix (BlockAssembler.go)

  • After fallback reset() succeeds in handleReorg(), return errors.NewBlockAssemblyResetError(...) instead of nil. This matches the large-reorg path which already correctly returns ErrBlockAssemblyReset, preventing processNewBlockAnnouncement from overwriting the reset's state.

Tests

  • Unit tests (reset_bug_test.go): Two tests that prove both bugs exist and pass after fixes.
  • Integration tests (blockassembly_system_test.go): Two tests using full blockchain daemon (gRPC, real stores) that exercise the reset and reorg-with-invalid-block paths end-to-end.

Testing

All tests pass with no regressions:

ok  github.com/bsv-blockchain/teranode/services/blockassembly         18.460s
ok  github.com/bsv-blockchain/teranode/services/blockassembly/mining   3.961s
ok  github.com/bsv-blockchain/teranode/services/blockassembly/subtreeprocessor  210.212s

Related issue: https://github.com/orgs/bitcoin-sv/projects/5/views/6?pane=issue&itemId=161301562&issue=bitcoin-sv%7Cteranode%7C4507

…andleReorg returns nil after fallback reset

Fix two bugs in block assembly's reset logic:

Bug 1: SubtreeProcessor.reset() only called finalizeBlockProcessing (and thus
SetBlockProcessedAt) for the LAST moveForward block. Intermediate blocks never
got processed_at set, meaning they were not recognized as fully processed.
Fix: call finalizeBlockProcessing for each moveForward block individually in
both the legacy sync and non-legacy paths.

Bug 2: handleReorg() returned nil after fallback reset (triggered by invalid
block or failed Reorg), allowing processNewBlockAnnouncement to overwrite the
reset's setBestBlockHeader with a potentially stale value captured before the
reset ran. Fix: return ErrBlockAssemblyReset after fallback reset, matching
the large-reorg path which already returns this error.

Includes unit tests proving both bugs and integration tests using full
blockchain daemon with gRPC and real stores.
@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

🤖 Claude Code Review

Status: Complete

Two critical bug fixes in block assembly reset logic, both properly addressed with comprehensive tests:

Bug 1 Fix (SubtreeProcessor.go): Fixed intermediate block finalization during reset

  • Previously only the last moveForward block got SetBlockProcessedAt called
  • Now each block is finalized individually in both legacy and non-legacy paths
  • Critical for block processing state consistency

Bug 2 Fix (BlockAssembler.go): Fixed handleReorg return value after fallback reset

  • Now returns ErrBlockAssemblyReset instead of nil after fallback reset
  • Prevents processNewBlockAnnouncement from overwriting reset state at line 609
  • Matches the large-reorg path behavior (line 1163)

Test Coverage: Excellent

  • Unit tests prove both bugs exist and pass after fixes
  • Integration tests exercise full service stack with real stores
  • Tests cover both reset-ahead and reorg-with-invalid-block scenarios

No issues found. The fixes are well-reasoned, properly implemented, and thoroughly tested.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 2, 2026

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.

1 participant