Repository navigation
net: reconsiderblock activates the most-work chain - #1089
Conversation
Core's reconsiderblock clears the marks, then ActivateBestChain picks from every candidate. rbitcoin re-applied each restored invalidate path on its own. Paths that outweighed the tip only together stayed inactive, and a restored block that lost was forgotten as a fallback. Reconsider now ranks one branch per candidate tip: restored path ends, the reconsidered hash, fork tips, held bodies, and precious. It connects the most-work branch and tries the next untried tip when a block fails. Then each restored leaf that lost becomes a fork tip if every block to it was connected before: it came off the active chain (each recorded path now says so), or it is at or under a fork tip. A leaf under a fork tip or another kept leaf is skipped. A held child that never connected does not hide one. Restored blocks are walked past the 10,000-block cap. A store fault while ranking is returned. invalidateblock uses the same branch builder and ranking. Fixes reardencode#1080. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
The happy path matches the cases in #1080. I do not think this is ready to merge: a failed reconsiderblock throws away the path records the new fallback depends on, and the 10,000-block exemption does not survive into the later invalidateblock that is supposed to use the parked leaf.
What holds up:
- Related paths are joined by the parent link, then one branch is ranked. That is the fix for repro 1 (
[a8,a9]plus[a10]againstb9). connectedis set only for a path taken off the active chain. Parking stops at an existing fork tip, so a held child that never connected does not become avalid-forkand does not hide the leaf under it. Equal-work precious ordering matchesheld_branch_beats/accept_branch_weaker.- A connect failure with a kept heavier prefix (
Accepted+Some) tries the next untried tip. A full accept orIgnoredWeakerstops, which is right once candidates are ordered by total chain work. - qc, test, windows, macos, and coverage are green on
db2c43b.
Two change requests are on the lines below.
Not blocking, and not a reason to expand this PR:
branch parent not on chainbreaks the retry loop instead of moving on to the next tip.try_apply_helddoes the same, andassemble_side_branchonly returns a branch whose parentis_connected, so this is hard to hit. If it does fire, every lighter candidate is skipped and the RPC still returns success.- A side
invalidateblockstill marks only that hash. A descendant parked by this PR stays infork_tips, sogetchaintipscan keep sayingvalid-forkwhile the ancestor is invalid. The branch is filtered out of activation, so the tip is right and the label is not. The same gap exists for any fork tip; parking makes more of them. reconsider_skips_a_branch_that_failsholds the tip body before corrupting the store, and only checksis_local_fault. That hold is what productioninvalidateblockremoves, so the test does not show the lost sibling.assert_ne!(bad5_status, Some("valid-fork"))also passes for any other status.- The limits you already list (equal work stays on the current tip, fork-tip cap 64, interior blocks not kept, one extra ancestor tip, quadratic rebuild) match the code.
A store fault while ranking returned before the losing restored leaves were parked. Their invalidate paths were already gone, so nothing named them any more. A branch whose work cannot be summed is now skipped, the first store fault is kept, the leaves are parked, and then the fault is returned. invalidateblock skips such a branch again, as before this PR. A branch whose parent is not on the chain no longer stops the retry loop; the next untried tip is tried. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@rearden-grok please re-review the latest commit (59b8b04). |
There was a problem hiding this comment.
Approved. 59b8b040 fixes both blocking notes from the last review, and I do not see a new one.
Store faults while ranking no longer discard a winner that was already scored, and they no longer return before the restored leaves are parked. invalidateblock still skips a branch whose work cannot be summed, as it did before this PR. A branch parent not on chain failure tries the next untried tip instead of stopping the loop. The 10,000-block exemption is now described as reconsider-only: the changelog and the known limits both say a later invalidateblock keeps the usual cap.
Re-checked the activation rules against the #1080 cases. Related paths are still one ranked branch. A losing leaf is parked only when the blocks above an existing fork tip were on the active chain, so a held child that never connected is not a valid-fork and does not hide the leaf. A kept heavier prefix still tries the next tip; a full accept or IgnoredWeaker still stops. qc, test, windows, macos, and coverage are green on this commit.
What I am accepting, not asking to change:
- A store fault on a losing candidate still fails the RPC after the winner is activated and the leaves are parked. That is the fault being returned, not the winner being dropped.
- A local fault from
accept_branch_prefixstill stops the retry. The leaves are parked first, so a sick store does not forget them. - A parked leaf deeper than 10,000 blocks is invisible to the next
invalidateblock, same as any other fork tip. That is now what the changelog says. - A side
invalidateblockstill marks only that hash, sogetchaintipscan keep a parked descendant asvalid-forkwhile an ancestor is invalid. Activation filters that branch. - The store-fault test still holds the body before truncating
header.body. The newfork_tipsassertion does prove the leaf is parked after the error.
|
@rearden-grok please re-review the latest commit (b33787a). It changes only the test: the h/i beat used to submit h5 with |
The beat submitted h5 with submitblock while its parent h4 was invalidated, and relied on submitblock holding it. Core refuses that block as `bad-prevblk`, so the beat should not depend on rbitcoin holding it there. Accept it through the hub instead, as a peer's block arrives, so the held child still sits over the restored h4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@rearden-grok correction: the test-only commit is now 8c1d3c5. I only reworded the message: there is no separate submitblock PR. The beat still stops relying on |
b33787a to
8c1d3c5
Compare
There was a problem hiding this comment.
Approved. b33787a2 is test-only, and it still plants the held child the journey is about.
submitblock of h5 while h4 is invalidated is the case a follow-up will reject as bad-prevblk. accept_received_block is the peer path: h4 is off the active chain, so the body is held and UnknownParent does not run connect. While h2 and h3 are still invalid, best_held_branch skips that walk, so the coinbase failure is not marked yet. Reconsider then ranks h5 above h4, fails it, and still parks h4. The later invalidateblock of h5 is what keeps this from passing if the body never landed. qc, test, windows, and macos are green on this commit; coverage was still running.
No production path changed. The limits from the previous approval are unchanged.
Fixes #1080. Follows the plan from the issue thread, with a few changes from review, listed at the end.
Summary
reconsiderblockused to re-apply each restoredinvalidateblockpath on its own. It now picks the most-work branch the wayinvalidateblockdoes, and keeps losing restored leaves as forks.connectedflag.invalidateblockskips the branch, as before.try_apply_after_invalidateuses the same branch builder and ranking. Tip-followbest_held_branchis unchanged.Evidence
The same RPC steps, run on bitcoind 31.1, master, and this branch:
getchaintipsalso matches Core for the restored leaves:valid-forkfor the kept leaf, and blocks under it are not listed.Tests:
chain_ops_reconsider_most_work(beats added to the mature-chain RPC journey) covers the rows above. Before: fails at the first reconsider. After: passes.reconsider_skips_a_branch_that_failscovers:valid-fork;chain_edges_pin_work_headers_and_reopen: reconsider does not turn a held block into avalid-fork.connectedflag ignored;Merge Danger
Door: two-way
No schema or on-disk change. Revert restores the old per-path reconsider.
Blast Radius: operator RPCs
invalidateblock/reconsiderblockonly. Tip-follow held-branch selection is unchanged.Known limits, unchanged or not covered:
COMPAT.md"Equal-work fork choice" difference.fork_tipsis still capped at 64 (FIFO). A kept leaf takes a slot like any reorg loser, so a full cache can evict an older fallback.getchaintipslists an older fork tip under a kept leaf too. Core lists only the leaf; master lists only the older tip.invalidateblockcalls.invalidateblockdoes, like any other fork tip.Changes from the plan in the thread, from review:
🤖 Generated with Claude Code