{
 "number": 36346,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/36346",
 "title": "validation: don't accept txs to the mempool while loading a snapshot",
 "author": "0xShadowX",
 "author_association": "FIRST_TIME_CONTRIBUTOR",
 "created_at": "2026-09-26T17:16:20Z",
 "updated_at": "2026-11-01T01:29:39Z",
 "description_hash": "ac48b1f43aab6051",
 "age_days": 36,
 "draft": false,
 "labels": [
  "Validation"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "ca21d8f5fbc9961f2a9d0622c0cf4e23f2a82b82",
 "head_ref": "fix-loadtxoutset-mempool-recheck",
 "head_repo": "0xShadowX/bitcoin",
 "head_history": [
  {
   "t": "2026-09-27T07:55:08Z",
   "sha": "ca21d8f5fbc9961f2a9d0622c0cf4e23f2a82b82"
  }
 ],
 "additions": 82,
 "deletions": 0,
 "changed_files": 3,
 "commit_count": 1,
 "size_bucket": "S",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {},
   "conflicts": []
  }
 },
 "acks_parsed": {},
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 0,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "fjahr"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-09-27T07:55:46Z",
  "last_reviewer_activity": "2026-09-26T22:07:39Z",
  "last_reviewer": "fjahr",
  "author_silent_days": 35,
  "waiting_on_author_days": 0,
  "days_since_update": 0
 },
 "refs": {
  "mentioned": [],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [],
  "conflicts": []
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [],
 "body": "`ActivateSnapshot` checks that the mempool is empty before it starts, but `PopulateAndValidateSnapshot` then runs without `cs_main`, which takes minutes on mainnet. A transaction entering the mempool in that window (e.g. through `sendrawtransaction`) hits the assert in `AddChainstate`:\n\n```\nAssertion failed: (!prev_chainstate.m_mempool || prev_chainstate.m_mempool->size() == 0), function AddChainstate, file validation.cpp, line 6201.\n```\n\nBy then the base blockhash file has been written, so the node needs `-reindex` to start again.\n\nThis sets a flag on `ChainstateManager` while `ActivateSnapshot` runs, and `AcceptToMemoryPool` and `ProcessNewPackage` reject with `snapshot-loading` while it's set. `testmempoolaccept` still works. A second `loadtxoutset` started while one is running is also rejected.\n\n`test_mempool_blocked_during_load` in `feature_assumeutxo.py` pauses the load through a FIFO, checks that `sendrawtransaction` and `submitpackage` are rejected and the mempool stays empty, then lets the load finish. On master the transaction is accepted and the node aborts. The test is skipped where `os.mkfifo` isn't available.",
 "commits": [
  {
   "sha": "ca21d8f5fbc9961f2a9d0622c0cf4e23f2a82b82",
   "date": "2026-09-27T07:54:59Z",
   "message": "validation: don't accept txs to the mempool while loading a snapshot\n\nActivateSnapshot checks that the mempool is empty, then releases cs_main\nwhile PopulateAndValidateSnapshot runs, which can take minutes. A\ntransaction added to the mempool in that window (e.g. through\nsendrawtransaction) made AddChainstate hit its empty-mempool assert after\nthe base blockhash file was already written, and the node then needed\n-reindex to start again.\n\nSet a flag on ChainstateManager for the duration of ActivateSnapshot and\nreject transactions and packages in AcceptToMemoryPool and\nProcessNewPackage while it is set. test_accept calls still work since\nthey don't add anything to the mempool. A second concurrent loadtxoutset\nis rejected while the flag is set."
  }
 ],
 "timeline": [
  {
   "t": "2026-09-26T22:07:39Z",
   "kind": "comment",
   "who": "fjahr",
   "assoc": "MEMBER",
   "text": "I haven't looked at the code or verified the issue but it seems to me the correct fix would be to prevent the user from putting a tx into the mempool when we don't want one to get there instead of letting them do it and then fail, even if it's a graceful failure with this change.",
   "id": "c:5850316714",
   "url": "https://github.com/bitcoin/bitcoin/pull/36346#issuecomment-5850316714",
   "hash": "df31df9e49ab3bc1",
   "edited": null
  },
  {
   "t": "2026-09-27T07:55:08Z",
   "kind": "force_push",
   "who": "0xShadowX",
   "commit": "ca21d8f5fbc9961f2a9d0622c0cf4e23f2a82b82",
   "id": "push:ca21d8f5fbc9961f2a9d0622c0cf4e23f2a82b82"
  },
  {
   "t": "2026-09-27T07:55:46Z",
   "kind": "comment",
   "who": "0xShadowX",
   "assoc": "NONE",
   "text": "Makes sense, changed it to that. While a snapshot is loading, AcceptToMemoryPool and ProcessNewPackage now reject with \"snapshot-loading\" (test_accept still works), so the load no longer fails because of the mempool. Updated the description and the test.",
   "id": "c:5853990694",
   "url": "https://github.com/bitcoin/bitcoin/pull/36346#issuecomment-5853990694",
   "hash": "feb3d733864d7de6",
   "edited": null
  }
 ],
 "labels_log": [
  {
   "t": "2026-09-26T17:16:24Z",
   "action": "labeled",
   "label": "Validation",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-28T12:23:03Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-29T20:17:27Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-09-27T07:55:19Z",
   "kind": "renamed",
   "who": "0xShadowX",
   "from": "validation: recheck mempool before activating a snapshot",
   "to": "validation: don't accept txs to the mempool while loading a snapshot"
  }
 ],
 "text_chars": 2496,
 "text_tokens_estimate": 624,
 "changed_paths": [
  "src/validation.cpp",
  "src/validation.h",
  "test/functional/feature_assumeutxo.py"
 ],
 "files": [
  {
   "path": "src/validation.cpp",
   "add": 23,
   "del": 0
  },
  {
   "path": "src/validation.h",
   "add": 5,
   "del": 0
  },
  {
   "path": "test/functional/feature_assumeutxo.py",
   "add": 54,
   "del": 0
  }
 ],
 "test_lines": 54,
 "git": {
  "head": "ca21d8f5fbc9961f2a9d0622c0cf4e23f2a82b82",
  "head_matches_backup": true,
  "base": "ed7dd7cf4e1561a97edf72eba29a67b14e28c717",
  "patch_id": "018e824a9cb11b31",
  "commits": [
   {
    "sha": "ca21d8f5fb",
    "subject": "validation: don't accept txs to the mempool while loading a snapshot",
    "files": 3,
    "add": 82,
    "del": 0
   }
  ],
  "patch_truncated": false
 },
 "patch_id": "018e824a9cb11b31",
 "input_hash": "f2d3419fd1d7971f",
 "extracted_at": "2026-11-02T01:09:49+00:00"
}