Skip to content

ibd: connect archived blocks that spend a same-batch create - #1067

Merged
reardencode merged 3 commits into
reardencode:masterfrom
smolcars:ibd/archived-retry-same-batch-parent
Oct 18, 2026
Merged

reardencode merged 3 commits into
reardencode:masterfrom
smolcars:ibd/archived-retry-same-batch-parent

Conversation

@niteshbalusu11

@niteshbalusu11 niteshbalusu11 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

During IBD, a block can spend a tx created earlier in the same batch. When that batch is retried from its archived bodies (plan=None) with the IBD skeleton, the stamp only filled parent ranges when there was no skeleton. The same-batch parent had no range. Load failed with lookup stage miss (load parent without body_range denserels) twice, and IBD halted below valid blocks.

The retry happens in two cases:

  • an invalid block later in the same batch,
  • a restart after a crash between the Class A write and the tip advance.

Bitcoin Core connects the valid blocks in both cases.

 stamp_parent_pin_archived (plan=None)
   stamp external parents         # the skeleton carries their ranges
   stamp this batch's own creates # identity only
   if no skeleton
     fill missing ranges
+  else
+    fill missing ranges for this batch's own creates

Only an all-archived retry takes this branch. A normal IBD batch has a plan. The docs/invariants.md row notes the one loc read this adds.

Evidence

Invalid block in a batch. Same 140-block regtest chain sent to Core 31.1 and rbitcoin over IBD. Block 116 has a parent and its child. Block 118 is invalid.

Core 31.1 rbitcoin before rbitcoin after
tip 117 115, engine fault repeated @116 117

Before, this failed in 5 of 6 runs (it depends on how blocks split into batches). After, 5 of 5 matched Core.

Crash restart. kill -9 three times during a 1,500-block regtest IBD whose blocks spend same-block outputs, then restart:

Seed master with #1067 and #1068
1 stuck at 1128 (hole=, 0.0/s) 1500
3 halted at 254 (lookup stage miss) 1500
4 halted at 241 (lookup stage miss) 1500
5 stuck at 1378 (hole=, 0.0/s) 1500
6 stuck at 556 (hole=, 0.0/s) 1500

"halted" is this PR. "stuck" is #1068.

New test archived_retry_pins_a_same_block_parent (WireRig, one wave: valid block with a same-block spend, then a block whose coinbase overpays):

  • Before: Cascade, then EngineFault twice on the valid block, then halt.
  • After: the valid block connects and the bad block is marked invalid.

Merge Danger

Door: two-way

Blast Radius: small. Only the all-archived retry path changes. It is reachable on a live network after a crash during IBD, since blocks often spend outputs from the same batch.

🤖 Generated with Claude Code

@niteshbalusu11
niteshbalusu11 force-pushed the ibd/archived-retry-same-batch-parent branch from 198003f to 9c5222f Compare October 18, 2026 09:05
@niteshbalusu11
niteshbalusu11 force-pushed the ibd/archived-retry-same-batch-parent branch from 9c5222f to 7c4bdb4 Compare October 18, 2026 10:07
@niteshbalusu11 niteshbalusu11 changed the title ibd: connect the valid blocks of a rejected batch that spend a same-batch create ibd: connect archived blocks that spend a same-batch create Oct 18, 2026

@rearden-grok rearden-grok Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

An all-archived retry (plan=None, IBD skeleton present) used to stamp a same-batch create with a txid and no body_range. Load then failed closed with lookup stage miss (load parent without body_range denserels) and halted under blocks Core connects. The new branch loc-fills create.loc body, spent, and n_out for every tx fk in this stamp's metas. spend_edges_from_stamp still picks the in-batch fk. That is the right parent for a same-block spend and for A→B→C inside one load chunk. It does not run on plan=Some, and it does not loc-fill skeleton parents. There is no durable pin: the read dies with the batch, and Class A stays idempotent.

A kill -9 in one write era leaves a single load batch above the tip, and that batch fits one chunk, so this fill covers that crash. It does not cover a spend whose parent was only note_creates'd by an earlier load chunk of a larger archived wave.

Issue counts by severity

  • bugs: 0
  • suggestions: 2
  • nits: 1

Issues outside the diff

These findings reference lines that are not present in the diff and could not be posted as inline comments:

  • [nit] crates/rbitcoin-query/src/stamp.rs:9 -- The module contract still says IBD (skeleton = Some) never loc-by-fk, and that spent is pin loc, skeleton TipOnly loc, or unset. stamp_external_parents still obeys that. stamp_parent_pin_archived no longer does, for this batch's own creates. docs/invariants.md states the exception; this comment does not. A later edit that restores the module rule would re-break the retry.
    • Suggestion: Say that an all-archived retry may loc-fill its own creates, and that external IBD parents still must not.

Comment thread crates/rbitcoin-query/src/confirm_run/lookup.rs Outdated
Comment thread crates/rbitcoin-net/src/ibd/events/confirm_reject_tests.rs
@niteshbalusu11

niteshbalusu11 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor Author

@rearden-grok On the outside-diff nit (stamp.rs:9): done in 544a16f. The module doc now says the all-archived retry (stamp_parent_pin_archived) loc-fills its archived parents that have no body. External IBD parents still must not.

@niteshbalusu11

niteshbalusu11 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor Author

@rearden-grok please re-review the latest commit (544a16f).

@niteshbalusu11
niteshbalusu11 force-pushed the ibd/archived-retry-same-batch-parent branch from 32a11ca to 7f10c98 Compare October 18, 2026 14:31
niteshbalusu11 and others added 3 commits October 18, 2026 14:57
A batch with an invalid block is retried from its archived bodies. With
the IBD skeleton, the stamp gave no range to a parent created in that
batch. Load failed with "lookup stage miss" twice and IBD halted below
the valid blocks. Fill ranges for the batch's own creates on that retry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review nit: the test should fail if a batching change stops putting both
blocks in one wave.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review: a wave longer than one load batch notes chunk 1's archived creates
in flight with no outs. A spend of one in chunk 2 was not in that chunk's
metas, so it still had no range. Fill every parent with no body on the
archived retry: this batch's own creates and an earlier chunk's. Skeleton
parents keep their body and in-flight pins are still skipped.

Test: 145 archived blocks, the last spends a create from the first and
loads in the second chunk. The stamp.rs module note and the invariants row
name the exception.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@niteshbalusu11
niteshbalusu11 force-pushed the ibd/archived-retry-same-batch-parent branch from 7f10c98 to 544a16f Compare October 18, 2026 19:04
@niteshbalusu11

Copy link
Copy Markdown
Contributor Author

@rearden-grok I rebased this on master (after #1072) and fixed a conflict in docs/invariants.md. Please re-review the latest commit (544a16f).

@rearden-grok rearden-grok Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

The widened fill is the right scope. It runs only on an all-archived retry (plan=None with a skeleton). A parent that never got an ident still fails closed. Skeleton ranges are left alone, in-flight pins are still skipped, and what remains is this chunk's own creates plus an earlier chunk's no-out rows. archived_wave_pins_a_parent_from_the_previous_chunk fails if the extract is narrowed back to this stamp's metas. The stamp.rs note and the invariants row are the exception to "IBD never loc-by-fk", which is the counterintuitive contract.

Prior notes on the 144-block split and the cascade assert are addressed. The rebase onto current master kept that exception on the Lookup parent stamp row next to the BIP34 wording from #1072.

Issue counts by severity

  • bugs: 0
  • suggestions: 0
  • nits: 0

No further rebase. This head is ahead of master and mergeable. Merge this before #1068 so that PR's diff is only the tip-hole fetch; #1068 is already stacked on this tip.

@reardencode
reardencode merged commit ec661fe into reardencode:master Oct 18, 2026
18 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