{
 "number": 36702,
 "input_hash": "b5c3e657d50fd3a9",
 "model": "ledger",
 "prompt_hash": null,
 "created": "2026-11-01T01:17:29+00:00",
 "result": {
  "summary": "Prevent an assertion crash on startup during AssumeUTXO background validation if the node restarts while holding the snapshot block without its immediate ancestors. On startup, `LoadBlockIndex()` now adds the snapshot block to `m_blocks_unlinked` if its parent lacks transaction count data, and `PopulateBlockIndexCandidates()` skips registering it as a tip candidate until its ancestors are connected.",
  "problem": "During AssumeUTXO background sync, blocks up to the snapshot block can arrive out of order, placing the snapshot block into memory before its ancestors. If the node restarts in this window, `LoadBlockIndex()` sets the hardcoded chain transaction count for the snapshot block without restoring it to `m_blocks_unlinked`, leading `FindMostWorkChain()` to walk back to an unlinked parent lacking transaction counts and crash on an assertion on every boot. The only manual recovery is `-reindex` or `-reindex-chainstate`, which completely wipes the snapshot chainstate.",
  "card": "Fixes an assertion failure loop on startup for nodes using AssumeUTXO that restart while the snapshot block has been downloaded ahead of its ancestor blocks. In that state, startup assigns the snapshot block a hardcoded transaction count and treats it as a background chain tip candidate, causing FindMostWorkChain to walk back into an unlinked parent and assert fail on every boot. This PR adds the unlinked snapshot block back into m_blocks_unlinked on startup and excludes it from candidate tips until its ancestors arrive. The crash was also independently reported during review testing of #36452 and otherwise requires a full reindex that destroys the snapshot chainstate.",
  "evidence": [
   "Author description: 'Assertion failed: (pindexTest->HaveNumChainTxs() || pindexTest->nHeight == 0), function FindMostWorkChain, file validation.cpp, line 3217.'",
   "Author notes reproducing it over P2P using a test peer withholding the snapshot block's parent, and reports that cyb3ralbert encountered the same assertion during testing of #36452 (referencing PR #36452 comment)."
  ],
  "scope_notes": "Small, targeted change touching `LoadBlockIndex` in `blockstorage.cpp` and `PopulateBlockIndexCandidates` in `validation.cpp`, with an added regression test in `feature_assumeutxo.py`.",
  "changed_since_previous": "Now includes the complete diff and patch ID; the logic and scope remain consistent with the prior description.",
  "thread": {
   "last_event_at": "2026-10-31T09:41:21Z",
   "events": 1,
   "head_sha": "80f6ddd6cbba7c8104dc0b9dabb451af13cc4a7d",
   "schema_floor": 2,
   "schema": 2
  },
  "waiting_on": [
   {
    "at": "2026-10-31",
    "id": "c:6541811854",
    "on": "reviewer",
    "url": "https://github.com/bitcoin/bitcoin/pull/36702#issuecomment-6541811854",
    "what": "code review"
   }
  ],
  "risk": {
   "level": "medium",
   "touches": [
    "validation"
   ],
   "why": "A logic error in block index loading or candidate tip selection could stall background AssumeUTXO sync or cause improper chainstate initialization."
  },
  "discussion": {
   "author_status": "last statement processed 2026-10-31",
   "open_concerns": [],
   "resolved_concerns": []
  },
  "reviewability": {
   "state": "Ready",
   "label": "Ready",
   "reason": "Nothing known that would invalidate a review now."
  },
  "agreement": {
   "state": "Strong",
   "summary": "Strong: substantive support, no open objection (ValeraFinebits)",
   "reason": "Initial record. ValeraFinebits verified the bug and fix by testing feature_assumeutxo.py on Windows x64. / Initial record. ValeraFinebits verified the reproduction and fix via testing on Windows x64.",
   "evidence": [
    "ValeraFinebits: support \u2014 passes with the fix on Windows x64; reverting the C++ changes reproduces the startup assertion in FindMostWorkChain()"
   ],
   "objections": [],
   "support": [
    {
     "reviewer": "ValeraFinebits",
     "reason": "passes with the fix on Windows x64; reverting the C++ changes reproduces the startup assertion in FindMostWorkChain()",
     "substantive": true,
     "verdict": "",
     "id": "c:6541811854",
     "url": "https://github.com/bitcoin/bitcoin/pull/36702#issuecomment-6541811854",
     "at": "2026-10-31",
     "association": "NONE",
     "evidence": "tested",
     "areas": [
      "feature_assumeutxo.py"
     ]
    }
   ],
   "participants": [
    {
     "login": "ValeraFinebits",
     "stance": "support",
     "note": "tested that the fix resolves the startup assertion in feature_assumeutxo.py",
     "association": "NONE",
     "comments": 1,
     "first": "2026-10-31",
     "last": "2026-10-31"
    }
   ],
   "derivation": "substantive support, no open objection (ValeraFinebits)",
   "model_state": null,
   "corrections": [],
   "notes": "Initial record. ValeraFinebits verified the bug and fix by testing feature_assumeutxo.py on Windows x64. / Initial record. ValeraFinebits verified the reproduction and fix via testing on Windows x64."
  },
  "dependencies": {
   "depends_on": [],
   "enables": []
  },
  "categories": [
   {
    "name": "mempool",
    "member": false,
    "evidence": "Matches the 'ancestor' keyword and 'src/validation.cpp' path, but deals exclusively with block index loading and AssumeUTXO chainstate on startup.",
    "band": "Unranked",
    "reason_tag": "bug fix",
    "score": 0.0,
    "factors": {
     "bug_severity": 2,
     "leverage": 0,
     "performance": 0,
     "security_stability": 2,
     "user_value": 1
    },
    "rationale": "Does not touch transaction pool acceptance, eviction, standardness, or relay."
   },
   {
    "name": "validation",
    "member": true,
    "evidence": "Labeled 'Validation' and modifies `LoadBlockIndex()` in `src/node/blockstorage.cpp` and `PopulateBlockIndexCandidates()` in `src/validation.cpp` to correctly handle AssumeUTXO block index linkage.",
    "band": "P1",
    "reason_tag": "bug fix",
    "score": 0.8,
    "factors": {
     "bug_severity": 2,
     "leverage": 0,
     "performance": 0,
     "security_stability": 2,
     "user_value": 1
    },
    "rationale": "Fixes a startup crash loop in `FindMostWorkChain()` that permanently bricks startup without `-reindex-chainstate`. If a node running AssumeUTXO background validation restarts while holding the snapshot block without its parent, `LoadBlockIndex()` fails to add it to `m_blocks_unlinked`, triggering an assertion failure on every subsequent boot."
   }
  ],
  "confidence": "high",
  "uncertainties": [],
  "needs": []
 },
 "cost_usd": 0.0,
 "source": {
  "record": "2026-11-01T01:17:29+00:00",
  "code": "2026-11-01T01:20:07+00:00",
  "judgments": 2
 }
}