{
 "number": 35675,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/35675",
 "title": "mining: add block template manager",
 "author": "ismaelsadeeq",
 "author_association": "MEMBER",
 "created_at": "2026-07-07T11:05:56Z",
 "updated_at": "2026-09-16T21:45:20Z",
 "age_days": 72,
 "draft": false,
 "labels": [
  "Mining"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "e8b283c35a656f808250e4b32d3494f49a000b08",
 "head_ref": "07-2026-block-template-man-skeleton",
 "head_repo": "ismaelsadeeq/bitcoin",
 "head_history": [
  {
   "t": "2026-07-08T09:38:52Z",
   "sha": "149baad2200157c4b5772d2027c364642e98e17a"
  },
  {
   "t": "2026-07-10T16:28:13Z",
   "sha": "82dc581ae80a5ab39b39d44cac1daef8c6232ba8"
  },
  {
   "t": "2026-07-16T13:20:24Z",
   "sha": "cd83dfd9a6a6ab8404519d775dd10a7aa4067795"
  },
  {
   "t": "2026-07-17T15:58:09Z",
   "sha": "1881848e0c99e1e55f9739d2a097bfdad6e3873c"
  },
  {
   "t": "2026-07-20T11:43:08Z",
   "sha": "e7f0008848cf39fee548178850eff3e5abb771c9"
  },
  {
   "t": "2026-07-20T12:28:09Z",
   "sha": "d36b3cd0e2f0ccee652c5f34d6379901b02e8abf"
  },
  {
   "t": "2026-07-20T15:40:17Z",
   "sha": "c2dde7de2c2f77b89eb888211249c3b21410eb59"
  },
  {
   "t": "2026-07-23T12:51:57Z",
   "sha": "5ff6e9a81a100f14eaab0711fcf325e46ae1b21f"
  },
  {
   "t": "2026-07-23T13:59:02Z",
   "sha": "c5864dbae4e525162fd075c2d986e1553459eaab"
  },
  {
   "t": "2026-07-24T13:09:19Z",
   "sha": "a87d81548be53ed0af747adf5b8fd22e0d6b8289"
  },
  {
   "t": "2026-07-29T08:56:16Z",
   "sha": "ba196b7f99d762d1efe43248c3069efa938761b5"
  },
  {
   "t": "2026-07-29T10:02:06Z",
   "sha": "dad787680358adeba552802c152343a7d9ddfbd0"
  },
  {
   "t": "2026-08-05T12:28:14Z",
   "sha": "af294d84f56b2a9a5f57ac974d03a47cf89c752f"
  },
  {
   "t": "2026-08-14T22:07:32Z",
   "sha": "6fea30c82cf7e66a3d35cb1ca70735d4ef282806"
  },
  {
   "t": "2026-08-14T22:07:33Z",
   "sha": "6fea30c82cf7e66a3d35cb1ca70735d4ef282806"
  },
  {
   "t": "2026-09-08T11:05:44Z",
   "sha": "5f01715a32c98ca6fd6dc8301287045be0c8104c"
  },
  {
   "t": "2026-09-08T12:49:18Z",
   "sha": "9a663e7032257b92d7e52c510d7bf57c43049bde"
  },
  {
   "t": "2026-09-13T11:11:35Z",
   "sha": "40d21b36782ba99d4cfac9ac136cd1e4c6eb285f"
  },
  {
   "t": "2026-09-13T12:45:24Z",
   "sha": "e8b283c35a656f808250e4b32d3494f49a000b08"
  }
 ],
 "additions": 891,
 "deletions": 449,
 "changed_files": 28,
 "commit_count": 15,
 "size_bucket": "XL",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "ack": [
     {
      "login": "Sjors",
      "url": "https://github.com/bitcoin/bitcoin/pull/35675#issuecomment-5662194889"
     },
     {
      "login": "pablomartin4btc",
      "url": "https://github.com/bitcoin/bitcoin/pull/35675#pullrequestreview-5199333367"
     },
     {
      "login": "w0xlt",
      "url": "https://github.com/bitcoin/bitcoin/pull/35675#pullrequestreview-5203760349"
     },
     {
      "login": "enirox001",
      "url": "https://github.com/bitcoin/bitcoin/pull/35675#pullrequestreview-5225189828"
     }
    ],
    "stale_ack": [
     {
      "login": "marcofleon",
      "url": "https://github.com/bitcoin/bitcoin/pull/35675#pullrequestreview-5225346799"
     }
    ]
   },
   "conflicts": [
    {
     "number": 36244,
     "title": "validation, net: Process blocks asynchronously and reduce cs_main contention",
     "author": "w0xlt"
    },
    {
     "number": 36188,
     "title": "crypto: plug hardware optimized SHA256 into libsecp256k1's context",
     "author": "furszy"
    },
    {
     "number": 36109,
     "title": "test: harden arbitrary-parent block creation",
     "author": "l0rinc"
    },
    {
     "number": 36097,
     "title": "mining: replace interrupt methods with cancellation arguments",
     "author": "xyzconstant"
    },
    {
     "number": 36047,
     "title": "rpc: handle createNewBlock() returning nullptr during shutdown",
     "author": "chriszeng1010"
    },
    {
     "number": 36000,
     "title": "validation: prefetch blocks while connecting",
     "author": "l0rinc"
    },
    {
     "number": 35671,
     "title": "mining: add TxCollection to bandwidth-efficiently validate external block templates",
     "author": "Sjors"
    },
    {
     "number": 35646,
     "title": "RFC: Separate out runtime errors from BlockValidationState using `util::Expected`",
     "author": "yuvicc"
    },
    {
     "number": 35569,
     "title": "Encapsulation for CTransaction",
     "author": "purpleKarrot"
    },
    {
     "number": 35511,
     "title": "RFC: consensus: Make `CAmount` a class",
     "author": "hodlinator"
    },
    {
     "number": 32468,
     "title": "rpc: generateblock to allow multiple outputs",
     "author": "polespinasa"
    },
    {
     "number": 31117,
     "title": "miner: Reorg Testnet4 minimum difficulty blocks",
     "author": "fjahr"
    },
    {
     "number": 29700,
     "title": "kernel, refactor: return error status on all fatal errors",
     "author": "ryanofsky"
    }
   ]
  }
 },
 "acks_parsed": {
  "Sjors": {
   "kind": "ack",
   "hash": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "t": "2026-09-14T09:56:26Z",
   "stale": false
  },
  "pablomartin4btc": {
   "kind": "ack",
   "hash": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "t": "2026-09-14T15:03:04Z",
   "stale": false
  },
  "enirox001": {
   "kind": "ack",
   "hash": null,
   "t": "2026-09-16T16:06:12Z",
   "stale": false
  },
  "w0xlt": {
   "kind": "ack",
   "hash": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "t": "2026-09-14T23:09:52Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 4,
  "stale_ack": 0,
  "concept_ack": 0,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 1,
  "changes_requested": 0,
  "distinct_reviewers": [
   "Sjors",
   "enirox001",
   "marcofleon",
   "pablomartin4btc",
   "w0xlt"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-09-13T12:45:24Z",
  "last_reviewer_activity": "2026-09-16T17:26:23Z",
  "last_reviewer": "marcofleon",
  "author_silent_days": 4,
  "waiting_on_author_days": 0,
  "days_since_update": 0
 },
 "refs": {
  "mentioned": [
   32547,
   33922,
   34020,
   34672,
   34803,
   35129,
   35581
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 32547,
    "type": "pull",
    "state": "closed",
    "merged": false,
    "merged_at": null,
    "title": "Mining: Avoid copying template CBlocks"
   },
   {
    "number": 33922,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "mining: add getMemoryLoad() and track template non-mempool memory footprint"
   },
   {
    "number": 34020,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-07-07",
    "title": "mining: add getTransactions(ByWitnessID) IPC methods"
   },
   {
    "number": 34672,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-07-22",
    "title": "mining: add reason/debug to `submitSolution` and unify with `submitBlock`"
   },
   {
    "number": 34803,
    "type": "pull",
    "state": "closed",
    "merged": false,
    "merged_at": null,
    "title": "mempool: asynchronous mempool fee rate diagram updates via validation interface"
   },
   {
    "number": 35129,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-06-30",
    "title": "test: add fuzz test for private broadcast"
   },
   {
    "number": 35581,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "node: add block template manager and track waitNext fee inflow"
   }
  ],
  "conflicts": [
   36244,
   36188,
   36109,
   36097,
   36047,
   36000,
   35671,
   35646,
   35569,
   35511,
   32468,
   31117,
   29700
  ]
 },
 "stack": {
  "shares_commits_with": [
   33922,
   35581
  ],
  "based_on": [],
  "base_for": [
   33922,
   35581
  ]
 },
 "review_paths": [
  "src/init.cpp",
  "src/node/block_template_manager.cpp",
  "src/node/block_template_manager.h",
  "src/node/context.h",
  "src/node/interfaces.cpp",
  "src/rpc/mining.cpp",
  "src/test/fuzz/block_template_manager.cpp",
  "src/test/fuzz/cmpctblock.cpp",
  "src/test/util/setup_common.cpp"
 ],
 "body": "This PR introduces `node::BlockTemplateManager` and moves block template\ncreation, submission, and mining wait helpers behind it.\n\n#### Motivation\n\nInstead of keeping template-related state and helper functions spread across\n`NodeContext`, `miner.cpp`, the mining interface, RPC, and tests, the manager\n now owns the node's init-time mining options and exposes methods needed by callers.\n\nThis also prevents some redundant copies previously done when using the mining\ninterface to create a block template and then retrieve the template data.\n\nThis keeps the IPC `Mining` interface focused on IPC-facing mining objects, while RPC\nand tests use the block template manager directly.\n\n#### Changes\n\n- `NodeContext` no longer stores `BlockCreateOptions` directly.\n- `BlockTemplateManager` stores the parsed init-time mining options and applies\n  them to unset per-call options before creating templates.\n- Block submission through the mining interface is routed through\n  `BlockTemplateManager::SubmitBlock()`, preserving the existing\n  `BlockChecked` state-capture behavior.\n- Tip lookup, tip waiting, cooldown, and `waitNext()` template creation helpers\n  are moved from miner helper functions into `BlockTemplateManager`.\n- In-process RPC and tests create raw `CBlockTemplate` objects directly through\n  `BlockTemplateManager`, avoiding cached `BlockTemplateImpl` objects that hold\n  `NodeContext` references during shutdown.\n- A fuzz target is added for `BlockTemplateManager::CreateNewTemplate()` with\n  fuzzed mempool contents and mining options.\n\n#### Note: This change is intended to be a pure refactor that preserves behavior.",
 "commits": [
  {
   "sha": "482a5d27124c4241c27ed323659914cd38409650",
   "date": "2026-09-13T10:33:47Z",
   "message": "node: introduce BlockTemplateManager\n\nAdd BlockTemplateManager, a wrapper around\nBlockAssembler::CreateNewBlock(), and store it in NodeContext.\n\nWire it into node init and test setup so it is reset before its\nmempool/chainman dependencies, and update tests and fuzz setups that\nrebuild chainman or mempool. Add a unit test that verifies a block\ntemplate can be created through the manager."
  },
  {
   "sha": "bcdadf2b7111a061d2bbeba350233db7b258a787",
   "date": "2026-09-13T11:28:23Z",
   "message": "node: move mining_args to BlockTemplateManager\n\nPass the parsed mining args to BlockTemplateManager at construction\nand expose them via BlockCreateArgs(), so the manager owns the\ninit-time block create options instead of NodeContext."
  },
  {
   "sha": "07964c525d4769d00004b9942225a2cb1306e09d",
   "date": "2026-09-13T11:28:23Z",
   "message": "miner: move SubmitBlock into BlockTemplateManager\n\nMove SubmitBlockStateCatcher and SubmitBlock from miner.cpp into\nBlockTemplateManager as a member function. This groups block submission\nwith block creation in the same class. The function uses m_chainman\ndirectly instead of taking it as a parameter."
  },
  {
   "sha": "037d3519f2ff9937aa82060a42939aafab4179f1",
   "date": "2026-09-13T11:28:25Z",
   "message": "node: move tip and wait helpers into BlockTemplateManager\n\nMove the mining tip lookup and block-template waiting helpers (GetTip,\nWaitTipChanged, WaitAndCreateNewBlock, InterruptWait,\nCooldownIfHeadersAhead) into BlockTemplateManager so the manager owns\nthe template waiting flow. The manager now takes KernelNotifications\nat construction."
  },
  {
   "sha": "400ac328c91b653e793bd140b89e8fabfb42e3ac",
   "date": "2026-09-13T11:28:26Z",
   "message": "interfaces: create block template via BlockTemplateManager\n\nRoute the Mining interface's createNewBlock() through\nBlockTemplateManager::CreateNewTemplate() instead of constructing a\nBlockAssembler directly. Merging the init-time defaults into unset\noptions now happens inside CreateNewTemplate(), so every caller gets\nthem applied."
  },
  {
   "sha": "011cbfbcd2e3a3282ae7e5afdfd923bc444d1845",
   "date": "2026-09-13T11:28:27Z",
   "message": "rpc: route getblocktemplate internals through node\n\nAdd EnsureBlockTemplateManager() and use it in getblocktemplate for tip\nlookup and longpoll waiting, while using ChainstateManager directly for\ntest-chain and IBD checks."
  },
  {
   "sha": "0998c0bbdfcc3c3f7c105a1fb7223bace529556a",
   "date": "2026-09-13T11:28:28Z",
   "message": "rpc: build getblocktemplate via BlockTemplateManager"
  },
  {
   "sha": "951fac807d37302e7de5e289c0ab05f8dfcb1e1c",
   "date": "2026-09-13T11:28:29Z",
   "message": "rpc: do not copy template data in getblocktemplate\n\nThe transactions, fees and sigops costs, and the coinbase outputs\nare only read, so reference them from the template instead of copying\nthem out of it."
  },
  {
   "sha": "38556f817e060187b48cd31490f578cdf63134ee",
   "date": "2026-09-13T11:28:29Z",
   "message": "rpc: only copy the header in getblocktemplate\n\nThe block copy exists solely to apply header adjustments (time, nonce,\nversion bits) that must not mutate the cached template. Now that the\ntransactions are read directly from the template, copy only the header\ninstead of the entire block."
  },
  {
   "sha": "8372721f20e2ea2c15bae38a046476736169d801",
   "date": "2026-09-13T11:28:30Z",
   "message": "rpc: build generation templates via BlockTemplateManager\n\ngenerateblock is no longer safe for fuzzing: it previously threw at\nEnsureMining (node.mining is never set in the fuzz setup), but with\nEnsureBlockTemplateManager it now executes, mining and submitting a\nreal block. Submission mutates the chain state shared across fuzz\niterations and writes the block to disk."
  },
  {
   "sha": "ec4f24ef181a7987bcea72a21e507f59dc1b8126",
   "date": "2026-09-13T11:28:31Z",
   "message": "rpc: wait for tips via BlockTemplateManager\n\nThe waitforblock, waitforblockheight and waitfornewblock RPCs are no\nlonger safe for fuzzing: they previously threw at EnsureMining\n(node.mining is never set in the fuzz setup), but with\nEnsureBlockTemplateManager they now really wait. The tip never changes\nduring fuzzing and nothing interrupts the wait, so a call without a\ntimeout blocks forever."
  },
  {
   "sha": "6c71166bcfa4339d5019f2b27d477eb45ce7fe86",
   "date": "2026-09-13T11:28:32Z",
   "message": "test: create templates via BlockTemplateManager"
  },
  {
   "sha": "ffe3a631f38b1224db8b0ea246a3c976f358c59e",
   "date": "2026-09-13T11:28:33Z",
   "message": "node: remove NodeContext::mining and EnsureMining"
  },
  {
   "sha": "3d7d190b6995b3c0adb6528c2d9eac81a8f839d9",
   "date": "2026-09-13T11:28:34Z",
   "message": "ci: enforce iwyu for block template manager"
  },
  {
   "sha": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "date": "2026-09-13T11:28:35Z",
   "message": "test: fuzz BlockTemplateManager"
  }
 ],
 "timeline": [
  {
   "t": "2026-07-07T11:06:33Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "CC @Sjors @pablomartin4btc @w0xlt  as discussed in #35581"
  },
  {
   "t": "2026-07-07T15:49:15Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Concept ACK\n\nI updated https://github.com/Sjors/bitcoin/pull/120 to build on this branch.\n\nWill review later."
  },
  {
   "t": "2026-07-07T16:47:17Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Please rebase, so I can make #33922 cleanly based on this (it conflicts with #35129 and #34020)."
  },
  {
   "t": "2026-07-08T09:38:52Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "149baad2200157c4b5772d2027c364642e98e17a"
  },
  {
   "t": "2026-07-08T11:37:05Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nDone."
  },
  {
   "t": "2026-07-08T14:11:11Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Commit 231fa5ad4f71e652c3c7a52a88f540d47dad29b7 _rpc, test: build block templates via BlockTemplateManager_ is rather large. Maybe have one commit per RPC method, or at least do the critical `getblocktemplate` in a separate commit.\n\nAfter this change, does `node.mining` still do anything?\n\nConsider adding the new files to `FILES_WITH_ENFORCED_IWYU` since their ancestors were enforced too."
  },
  {
   "t": "2026-07-08T15:23:48Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/node/block_template_manager.h",
   "commit": "1aa695a6f08a6824f5577ce8de3e5fe6578daa3b",
   "in_reply_to": null,
   "text": "In 1aa695a6f08a6824f5577ce8de3e5fe6578daa3b _node: introduce block template manager_: in this PR the block template *manager* doesn't really live up to its name. We should clarify what it does, e.g.:\n\n[quoted text omitted]"
  },
  {
   "t": "2026-07-08T15:28:02Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/cmpctblock.cpp",
   "commit": "1aa695a6f08a6824f5577ce8de3e5fe6578daa3b",
   "in_reply_to": null,
   "text": "In 1aa695a6f08a6824f5577ce8de3e5fe6578daa3b _node: introduce block template manager_: not sure if this alias adds much value, is does add churn."
  },
  {
   "t": "2026-07-08T15:36:22Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/node/block_template_manager.h",
   "commit": "610d5d704c35c8aeda860640b599e9ffa5e99d92",
   "in_reply_to": null,
   "text": "In 610d5d704c35c8aeda860640b599e9ffa5e99d92 _node: move mining_args to block template manager_: maybe call this `m_block_create_args` and then document that they're set during `Init`."
  },
  {
   "t": "2026-07-08T15:37:36Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/node/block_template_manager.h",
   "commit": "610d5d704c35c8aeda860640b599e9ffa5e99d92",
   "in_reply_to": null,
   "text": "In 610d5d704c35c8aeda860640b599e9ffa5e99d92 _node: move mining_args to block template manager_: in line with my above comment, I'd call this `BlockCreateArgs()` (dropping `Get` makes the call sites more readable imo, the method is already marked `const`)\n\nnit: `const BlockCreateOptions&`"
  },
  {
   "t": "2026-07-08T16:15:24Z",
   "kind": "review",
   "who": "Sjors",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "149baad2200157c4b5772d2027c364642e98e17a",
   "text": "Reviewed up to 00e3b6717204937272f2d81074b429e7c13430a7 _miner: move SubmitBlock into BlockTemplateManager_. Be careful when rebasing after #34672."
  },
  {
   "t": "2026-07-08T16:55:59Z",
   "kind": "review_comment",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "path": "src/node/interfaces.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "nit: `m_interrupt_wait` is missing the comment that `m_interrupt_mining` in `MinerImpl` has:\n```suggestion\n    // Treat as if guarded by notifications().m_tip_block_mutex\n    bool m_interrupt_wait{false};\n```\nI think both fields are used identically \u2014 written under the mutex via `InterruptWait()`, read inside lambda predicates holding the mutex. The missing comment on `m_interrupt_wait` seems inconsistent."
  },
  {
   "t": "2026-07-08T17:15:15Z",
   "kind": "review",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "149baad2200157c4b5772d2027c364642e98e17a",
   "text": "Concept ACK\n\nThanks for taking the [suggestion](https://github.com/bitcoin/bitcoin/pull/35581#discussion_r3535875551) from the previous PR.\n\nGeneral first pass review at 149baad220.\n\nThis PR is not based on #34803. #35675 targets master directly. `IsStale`, `TemplateSnapshot`, `m_template_snapshots`, and any `MempoolUpdated` subscription are absent \u2014 this is a pure refactor. The fee inflow tracking from #35581 will follow as a separate PR once this (or #34803) merges.\n\nKey differences from #35581:\n- `WaitAndCreateNewBlock` has no cheap staleness check \u2014 it rebuilds a full template to detect fee increases. The original comment (`// The latter check is expensive so we only run it once per second.`) is accurate again here.\n- No `template_id` / tracked-vs-untracked template distinction.\n- rpc/mining.cpp calls `BlockTemplateManager::CreateNewTemplate()` directly (returning `CBlockTemplate`). `BlockTemplateImpl` is only ever instantiated in node/interfaces.cpp (the IPC layer) \u2014 both on initial creation via `MinerImpl::createNewBlock` and on subsequent calls via `waitNext` \u2014 never from the RPC path. This removes the NodeContext-reference-during-shutdown concern that was present in #35581.\n\nRe Sjors' question about `node.mining`: it appears to be unused after this PR. On master it was read via `EnsureMining()` in `rpc/server_util.cpp`, but that's now replaced by `EnsureBlockTemplateManager()`. The field could likely be removed.\n\nI plan to follow up with a more detailed commit-by-commit review. Please let me know if any of my observations above are off.\n\nThanks for splitting this out from #35581 \u2014 the separation between the pure refactor and the fee-inflow tracking makes both easier to review."
  },
  {
   "t": "2026-07-10T16:28:13Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "82dc581ae80a5ab39b39d44cac1daef8c6232ba8"
  },
  {
   "t": "2026-07-10T16:32:22Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "Forced pushed from 149baad220 to 82dc581ae8 149baad220...82dc581ae8\n\n- https://github.com/bitcoin/bitcoin/pull/35675#issuecomment-4915691730 Fixed.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3545208567 Taken but with modification, I did not mention the tracking, we may change approach as I told you IRL.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3545242072 Removed.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3545299899 Taken.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3545308920 Fixed.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3545821698 Fixed.\n\nThanks for the review @Sjors @pablomartin4btc"
  },
  {
   "t": "2026-07-15T05:40:22Z",
   "kind": "review",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "82dc581ae80a5ab39b39d44cac1daef8c6232ba8",
   "text": "Concept ACK\n\nHaving a dedicated template manager is helpful. In a recent [PR](33966), the mining options were also disentangled from startup defaults, which helped clarify and improve consistency. Nice to see this approach at a centralized block template manager"
  },
  {
   "t": "2026-07-16T03:51:09Z",
   "kind": "review_comment",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "path": "src/rpc/mining.cpp",
   "commit": "82dc581ae80a5ab39b39d44cac1daef8c6232ba8",
   "in_reply_to": null,
   "text": "In 370d6cdb, shouldn't be `EnsureBlockTemplateManager(node)` instead of `CHECK_NONFATAL`?\n```suggestion\n    const auto mining_options{node::FlattenMiningOptions(EnsureBlockTemplateManager(node).BlockCreateArgs())};\n```"
  },
  {
   "t": "2026-07-16T04:43:04Z",
   "kind": "review_comment",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "path": "src/test/util/setup_common.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "In 9038cc7e, `CreateBlockTemplateManager()` is being called before chainstate is activated (before `LoadVerifyActivateChainstate()` runs. The manager is created before `ActiveChainstate()` is ready \u2014 `CreateNewBlock()` asserts `pindexPrev != nullptr` and would crash if called at this point).\n\nEvery fuzz reset path added in the same commit gets the order right: reset() \u2192 m_make_chainman() \u2192 `LoadVerifyActivateChainstate()` \u2192 `CreateBlockTemplateManager()`. The production path in `init.cpp` also creates the manager only after `VerifyLoadedChainstate()` succeeds. The constructor is inconsistent with both.\n\nSo `CreateBlockTemplateManager()` could be removed from ChainTestingSetup's constructor and call it at the end of `LoadVerifyActivateChainstate()` instead. The four explicit `CreateBlockTemplateManager()` calls in the fuzz reset paths become redundant and can be removed. Tests that never call `LoadVerifyActivateChainstate()` have no loaded chainstate and no use for the manager anyway."
  },
  {
   "t": "2026-07-16T05:05:29Z",
   "kind": "review_comment",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "path": "src/node/interfaces.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "minor nit (naming clarity only). The behaviour is correct.\n\n```suggestion\n    const BlockCreateOptions m_caller_options;\n```\n\nThe concern is in `waitNext()` at line 931-932:\n\n```\n  block_template_manager().WaitAndCreateNewBlock(\n      m_block_template, options, m_create_options, m_interrupt_wait);\n  if (new_template) return std::make_unique<BlockTemplateImpl>(m_create_options, ...);\n```\nA reader seeing `m_create_options` passed there might think \"these are the fully resolved options that were used to build the template\" \u2014 but they're not, they're the raw caller options that will get merged again inside `CreateNewTemplate()`. Perhaps `m_caller_options` makes that clear."
  },
  {
   "t": "2026-07-16T05:14:00Z",
   "kind": "review_comment",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "tiny nit: In 82dc581a \u2014 fuzz file is named `blocktemplatemanager.cpp` but `FUZZ_TARGET(block_template_manager)`. Every other fuzz target uses the same name for both (e.g.\n ` block_index.cpp` \u2192 `FUZZ_TARGET(block_index)`). Shouldn't be named `block_template_manager.cpp`?"
  },
  {
   "t": "2026-07-16T05:17:53Z",
   "kind": "review_comment",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "path": "src/node/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "In b520d18b, `AssertLockHeld()` isn't missing as in `WaitAndCreateNewBlock()`?\n```suggestion\n                AssertLockHeld(m_notifications.m_tip_block_mutex);\n                const auto tip_block = m_notifications.TipBlock();\n```"
  },
  {
   "t": "2026-07-16T05:19:32Z",
   "kind": "review",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "82dc581ae80a5ab39b39d44cac1daef8c6232ba8",
   "text": "ACK 82dc581ae80a5ab39b39d44cac1daef8c6232ba8\n\nReviewed commit by commit. Clean refactor overall \u2014 the consolidation of block creation, submission, and tip-waiting into `BlockTemplateManager` makes the mining boundary explicit and enables the removal of `node.mining` from `NodeContext` (b8ae3fc6).\n\nLeft some comments."
  },
  {
   "t": "2026-07-16T13:20:24Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "cd83dfd9a6a6ab8404519d775dd10a7aa4067795"
  },
  {
   "t": "2026-07-16T13:25:46Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/node/interfaces.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": 3592765987,
   "text": "I think I did not touch that line in this PR, and we flatten in master as well, so making it explicit like u suggested will be nice but beyond the scope of this PR."
  },
  {
   "t": "2026-07-16T13:25:55Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "Forced pushed 82dc581ae80a5ab39b39d44cac1daef8c6232ba8 to cd83dfd9a6a6ab8404519d775dd10a7aa4067795 https://github.com/bitcoin/bitcoin/compare/82dc581ae80a5ab39b39d44cac1daef8c6232ba8..cd83dfd9a6a6ab8404519d775dd10a7aa4067795\n\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3592512990 Fixed.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3592688570 Hmm, not sure, that will fix some, but will not fix the issue when the user constructs the block template manager directly. I agree it is implicit now. I added a comment to make it explicit.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3592797241 Fixed.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3592811170 Fixed."
  },
  {
   "t": "2026-07-16T13:39:11Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "47653818150745f452e01d204a240a45162e4b85",
   "in_reply_to": null,
   "text": "In 47653818150745f452e01d204a240a45162e4b85 _node: introduce BlockTemplateManager_: maybe document that the order is important here:\n\n[quoted text omitted]"
  },
  {
   "t": "2026-07-16T14:18:36Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/node/block_template_manager.h",
   "commit": "9a0be305a2c4a9dd50e4b51520189c6173939715",
   "in_reply_to": null,
   "text": "In 9a0be305a2c4a9dd50e4b51520189c6173939715 _node: move tip and wait helpers into BlockTemplateManager_: maybe ~example~ expand this an the above comment a bit:\n\n```diff\ndiff --git a/src/node/block_template_manager.h b/src/node/block_template_manager.h\nindex 3386bbf65a..02fadaf75c 100644\n--- a/src/node/block_template_manager.h\n+++ b/src/node/block_template_manager.h\n@@ -50,15 +50,18 @@ public:\n     std::unique_ptr<CBlockTemplate> CreateNewTemplate(const BlockCreateOptions& options);\n\n     /** Submit a block via ProcessNewBlock and capture validation state. */\n     bool SubmitBlock(const std::shared_ptr<const CBlock>& block, bool* new_block, std::string& reason, std::string& debug);\n\n-    /** @return the active chain tip, or nullopt if none exists. */\n+    /** Locks cs_main.\n+     *  @return the active chain tip, or nullopt if none exists. */\n     std::optional<interfaces::BlockRef> GetTip();\n\n     /** Wait for the tip to differ from @p current_tip or timeout.\n-     *  Waits indefinitely during startup for a non-null tip. */\n+     *  Waits indefinitely during startup for a non-null tip.\n+     *  @return the current tip, or nullopt if the node is shutting down or\n+     *  interrupt is set (not when the timeout is reached). */\n     std::optional<interfaces::BlockRef> WaitTipChanged(const uint256& current_tip, MillisecondsDouble& timeout, bool& interrupt);\n\n     /**\n      * Wait while the best known header extends the current chain tip AND at\n      * least one block is being added to the tip every 3 seconds. If the tip is\n```"
  },
  {
   "t": "2026-07-16T14:38:40Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/rpc/mining.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "In c6f05147f9024c02ea7e3a07eddbd57720695c86 _rpc: build getblocktemplate via BlockTemplateManager_: perhaps worth a separate commit that stops routing `getblocktemplate` internals through the Mining interface.\n\nIt was originally done for test coverage, but `interface_ipc_mining.py` covers all (?) these methods now."
  },
  {
   "t": "2026-07-16T14:40:08Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/rpc/mining.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "In c6f05147f9024c02ea7e3a07eddbd57720695c86 _rpc: build getblocktemplate via BlockTemplateManager_: the basically does what #32547 wanted?\n\nIt's a more important change than the others in this commit, hence probably worth separating."
  },
  {
   "t": "2026-07-16T14:48:03Z",
   "kind": "review",
   "who": "Sjors",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "cd83dfd9a6a6ab8404519d775dd10a7aa4067795",
   "text": "Code review cd83dfd9a6a6ab8404519d775dd10a7aa4067795.\n\nUpdate: maybe widen the range of fuzzed `block_reserved_weight`, `block_max_weight`, etc?\n\n_Maybe_ useful extra fuzz coverage...:\n\nClaude suggestion\n\n```diff\ndiff --git a/src/test/fuzz/block_template_manager.cpp b/src/test/fuzz/block_template_manager.cpp\nindex 658ddc3b16..af8fb95a79 100644\n--- a/src/test/fuzz/block_template_manager.cpp\n+++ b/src/test/fuzz/block_template_manager.cpp\n@@ -6,8 +6,10 @@\n #include <consensus/consensus.h>\n #include <consensus/validation.h>\n+#include <interfaces/types.h>\n #include <kernel/mempool_entry.h>\n #include <node/block_template_manager.h>\n #include <node/miner.h>\n #include <node/mining_args.h>\n+#include <node/mining_types.h>\n #include <policy/feerate.h>\n #include <policy/policy.h>\n@@ -21,8 +23,12 @@\n #include <test/util/txmempool.h>\n #include <txmempool.h>\n+#include <uint256.h>\n+#include <util/time.h>\n\n #include <cassert>\n+#include <chrono>\n #include <cstddef>\n #include <cstdint>\n+#include <numeric>\n #include <optional>\n\n@@ -123,4 +129,64 @@ FUZZ_TARGET(block_template_manager, .init = initialize_block_template_manager)\n             assert(fee >= 0);\n         if (!resolved.use_mempool) assert(block.vtx.size() == 1);\n+\n+        // Exercise the tip-wait helpers. Every wait below uses a zero\n+        // timeout, so each predicate is checked exactly once and no call\n+        // blocks: the tip cannot change during this test.\n+        const uint256 tip_hash{block.hashPrevBlock};\n+        {\n+            // WaitTipChanged() is what getblocktemplate's longpoll blocks on.\n+            // A current_tip equal to the actual tip times out and returns the\n+            // unchanged tip; any other value returns immediately. An interrupt\n+            // wins over both and is reset by the call.\n+            const bool interrupted{fuzzed_data_provider.ConsumeBool()};\n+            bool interrupt{interrupted};\n+            const uint256 current_tip{fuzzed_data_provider.ConsumeBool() ? tip_hash : ConsumeUInt256(fuzzed_data_provider)};\n+            MillisecondsDouble timeout{0};\n+            const std::optional<interfaces::BlockRef> wait_tip{block_template_manager.WaitTipChanged(current_tip, timeout, interrupt)};\n+            assert(!interrupt);\n+            if (interrupted) {\n+                assert(!wait_tip);\n+            } else {\n+                assert(wait_tip && wait_tip->hash == tip_hash);\n+            }\n+        }\n+        {\n+            // WaitAndCreateNewBlock() backs the Mining interface's waitNext(),\n+            // the IPC template-update mechanism (unrelated to the RPC\n+            // longpoll). Its wait loop runs exactly one iteration on a zero\n+            // timeout. Fresh transactions may raise the next template's fees,\n+            // and mock time more than 20 minutes past the tip forces a\n+            // template through the test-network min-difficulty path.\n+            if (fuzzed_data_provider.ConsumeBool()) {\n+                PopulateRandTransactionsToMempool(fuzzed_data_provider, mempool, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, DEFAULT_BLOCK_MAX_WEIGHT));\n+            }\n+            const std::chrono::seconds mock_offset{fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 30 * 60)};\n+            SetMockTime(WITH_LOCK(node.chainman->GetMutex(),\n+                                  return node.chainman->ActiveTip()->Time()) + mock_offset);\n+            const bool min_difficulty_window{mock_offset > std::chrono::minutes{20}};\n+            node::BlockWaitOptions wait_options;\n+            wait_options.timeout = MillisecondsDouble{0};\n+            if (fuzzed_data_provider.ConsumeBool()) {\n+                wait_options.fee_threshold = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, MAX_MONEY);\n+            }\n+            const bool interrupted{fuzzed_data_provider.ConsumeBool()};\n+            bool interrupt{interrupted};\n+            const auto next_template{block_template_manager.WaitAndCreateNewBlock(block_template, wait_options, options, interrupt)};\n+            assert(!interrupt);\n+            if (interrupted) {\n+                assert(!next_template);\n+            } else if (min_difficulty_window) {\n+                // A new template is returned regardless of its fees.\n+                assert(next_template && next_template->block.hashPrevBlock == tip_hash);\n+            } else if (next_template) {\n+                // Without a tip change the only way out before the deadline is\n+                // the fee threshold.\n+                assert(next_template->block.hashPrevBlock == tip_hash);\n+                const CAmount old_fees{std::accumulate(block_template->vTxFees.begin(), block_template->vTxFees.end(), CAmount{0})};\n+                const CAmount new_fees{std::accumulate(next_template->vTxFees.begin(), next_template->vTxFees.end(), CAmount{0})};\n+                assert(wait_options.fee_threshold < MAX_MONEY);\n+                assert(new_fees >= old_fees + wait_options.fee_threshold);\n+            }\n+        }\n     }\n }\n```"
  },
  {
   "t": "2026-07-16T14:53:54Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/rpc/mining.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": 3596407312,
   "text": "Yes, I added that in the PR description."
  },
  {
   "t": "2026-07-16T19:51:51Z",
   "kind": "review_comment",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "path": "src/init.cpp",
   "commit": "47653818150745f452e01d204a240a45162e4b85",
   "in_reply_to": null,
   "text": "In 4765381815 _\"node: introduce BlockTemplateManager\"_\n\nnit:\n\nI see a pattern in the code where a null assertion is used to check if it has been initialized before initializing a node object, such as `node.mempool`, `node.chainman`, `node.scheduler`. It seems this happens because these can be initialized in a different places, so when you try to initialize again, you should check whether it has already been initialized.\n\nI think there might be no use for this for the block template manager. As it is only ever initialized here, I think to keep things consistent would be good to assert before initializing as well\n\n```diff --git a/src/init.cpp b/src/init.cpp\nindex 73281cab8a..6199b120af 100644\n--- a/src/init.cpp\n+++ b/src/init.cpp\n@@ -1431,6 +1431,7 @@ static ChainstateLoadResult InitAndLoadChainstate(\n             LogInfo(\"Block index and chainstate loaded\");\n             auto mining_args{node::ReadMiningArgs(args)};\n             Assert(mining_args); // no error can happen, already checked in AppIni\ntParameterInteraction\n+            assert(!node.block_template_manager);\n             node.block_template_manager = std::make_unique<node::BlockTemplateMana\nger>(*node.mempool, chainman, *node.notifications, std::move(*mining_args));\n             node.notifications->setChainstateLoaded(true);\n         }\n```"
  },
  {
   "t": "2026-07-16T20:38:15Z",
   "kind": "review_comment",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/util/setup_common.cpp",
   "commit": "47653818150745f452e01d204a240a45162e4b85",
   "in_reply_to": null,
   "text": "In 4765381815  _\"node: introduce BlockTemplateManager\"_\n\nnit: asserting that it is non initialized here could be nice as well\n\n```diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp\nindex eecd0a60cf..6282a77928 100644\n--- a/src/test/util/setup_common.cpp\n+++ b/src/test/util/setup_common.cpp\n@@ -335,6 +335,7 @@ void ChainTestingSetup::CreateBlockTemplateManager()\n {\n     auto mining_args{node::ReadMiningArgs(*Assert(m_node.args))};\n     Assert(mining_args);\n+    Assert(!m_node.block_template_manager);\n     m_node.block_template_manager = std::make_unique<node::BlockTemplateManager>(*\nm_node.mempool, *m_node.chainman, *Assert(m_node.notifications), std::move(*mining_\nargs));\n }\n```"
  },
  {
   "t": "2026-07-17T12:10:17Z",
   "kind": "review_comment",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "path": "src/node/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "In https://github.com/bitcoin/bitcoin/pull/35675/changes/b1c0f2f682414ee728d27a1739bd4d56130a4532 _\"miner: move SubmitBlock into BlockTemplateManager\"_\n\nI have some gripes with this. The `SubmitBlockStateCatcher` is duplicated in different places. This moved one in the template manager, another in the `rpc/mining.cpp` as `submitblock_StateCatcher` and `BlockValidationStateCatcher` in util/mining.cpp. I think they more or less do the same thing. This may not be a good fit for this PR, but we could move this to `mining_types.h` and use it where it applies. Maybe in a separate PR"
  },
  {
   "t": "2026-07-17T15:58:09Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "1881848e0c99e1e55f9739d2a097bfdad6e3873c"
  },
  {
   "t": "2026-07-17T16:08:35Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "Forced pushed from cd83dfd9a6a6ab8404519d775dd10a7aa4067795 to 1881848e0c99e1e55f9739d2a097bfdad6e3873c [cd83dfd9a6...1881848e0c](https://github.com/bitcoin/bitcoin/compare/cd83dfd9a6a6ab8404519d775dd10a7aa4067795..1881848e0c99e1e55f9739d2a097bfdad6e3873c)\n\n- https://github.com/bitcoin/bitcoin/pull/35675#pullrequestreview-4714280522 Indeed, I take your suggestion but tweak a bit and added other fuzz harnesses.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3595930927 Added.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3596236525 Taken.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3596394421 Done."
  },
  {
   "t": "2026-07-18T13:17:31Z",
   "kind": "review_comment",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "path": "src/node/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "In 52b09a4538 _\"node: move tip and wait helpers into BlockTemplateManager\"_\n\nWould it be cleaner for `WaitAndCreateNewBlock()` to accept `const CBlockTemplate&`? I don't think the method depends on ownership. It also assumes the template is non-null, so i think an object reference would retain the intended behaviour without using the `unique_ptr`"
  },
  {
   "t": "2026-07-18T13:46:11Z",
   "kind": "review_comment",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "path": "src/node/block_template_manager.cpp",
   "commit": "c6faba7d8ee9e93e0d75d076e0debee3d8baa54f",
   "in_reply_to": null,
   "text": "In c6faba7d8e: _\"interfaces create block template via BlockTemplateManager\"_\n\nnon-blocking nit\n\nWhile checking for places where `MergeMiningOptions` is still used, I saw the `miner_tests` using it. It appears to use the BlockAssembler for what CreateNewTemplate does, and with the changes in this commit, this is no longer necessary. I think this can be implemented as\n\n```diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp\nindex 2a7a0ff9eb..ee5417a4de 100644\n--- a/src/test/miner_tests.cpp\n+++ b/src/test/miner_tests.cpp\n@@ -219,11 +219,7 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const\n\n     // Test the inclusion of package feerates in the block template and ensure they are sequential.\n     // Can't use the Mining interface because it needs access to m_package_feerates.\n-    const auto block_package_feerates = BlockAssembler{\n-        m_node.chainman->ActiveChainstate(),\n-        &tx_mempool,\n-        MergeMiningOptions(options, Assert(m_node.block_template_manager)->BlockCreateArgs()),\n-    }.CreateNewBlock()->m_package_feerates;\n+    const auto block_package_feerates = Assert(m_node.block_template_manager)->CreateNewTemplate(options)->m_package_feerates;\n     BOOST_CHECK(block_package_feerates.size() == 2);\n\n     // parent_tx and high_fee_tx are added to the block as a package.\n```"
  },
  {
   "t": "2026-07-18T15:08:48Z",
   "kind": "review_comment",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "path": "src/node/context.h",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "In https://github.com/bitcoin/bitcoin/pull/35675/changes/e19d9f67a56af76105ee7524feb63fed59fa5558: _\"node: remove NodeContext::mining and EnsureMining\"_\n\nMinign has been removed from NodeContext header, but the import is still in `context.cpp`. THis should be removed\n\n```diff --git a/src/node/context.cpp b/src/node/context.cpp\nindex 05e6039aac..550ada748a 100644\n--- a/src/node/context.cpp\n+++ b/src/node/context.cpp\n@@ -7,7 +7,6 @@\n #include <addrman.h>\n #include <banman.h>\n #include <interfaces/chain.h>\n-#include <interfaces/mining.h>\n #include <kernel/context.h>\n #include <key.h>\n #include <net.h>\n```"
  },
  {
   "t": "2026-07-18T15:37:14Z",
   "kind": "review_comment",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "In 1881848e0c: _\"test: fuzz BlockTemplateManager\"_\n\nThe fuzz target looks good, but i do not see where this exercises the non-default BlockCreateArgs default arguments. I think this prevents it from exercising the merging cases.\n\nI think perhpas we could construct a local BlockTemplateManager with the init-time options for each iteration, this would cover the options behavior\n\ncodex suggested this\n\n```diff --git a/src/test/fuzz/block_template_manager.cpp b/src/test/fuzz/block_template_manager.cpp\nindex 297713d620..332a3a3ba1 100644\n--- a/src/test/fuzz/block_template_manager.cpp\n+++ b/src/test/fuzz/block_template_manager.cpp\n@@ -159,8 +159,9 @@ FUZZ_TARGET(block_template_manager, .init = initialize_block_template_manager)\n {\n     FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};\n     const auto& node = g_setup->m_node;\n-    auto& block_template_manager = *Assert(node.block_template_manager);\n     auto& mempool = *Assert(node.mempool);\n+    auto& chainman = *Assert(node.chainman);\n+    auto& notifications = *Assert(node.notifications);\n     SeedRandomStateForTest(SeedRand::ZEROS);\n     SetMockTime(WITH_LOCK(node.chainman->GetMutex(),\n                           return node.chainman->ActiveTip()->Time()));\n@@ -179,6 +180,26 @@ FUZZ_TARGET(block_template_manager, .init = initialize_block_template_manager)\n     }\n     LIMITED_WHILE(fuzzed_data_provider.remaining_bytes() > 0, 10)\n     {\n+        BlockCreateOptions init_options;\n+        if (fuzzed_data_provider.ConsumeBool()) {\n+            const CAmount fee_amount = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, COIN);\n+            const int32_t fee_size = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(1, MAX_STANDARD_TX_WEIGHT / WITNESS_SCALE_FACTOR);\n+            init_options.block_min_fee_rate = CFeeRate(fee_amount, fee_size);\n+        }\n+        if (fuzzed_data_provider.ConsumeBool()) {\n+            init_options.print_modified_fee = fuzzed_data_provider.ConsumeBool();\n+        }\n+        if (fuzzed_data_provider.ConsumeBool()) {\n+            init_options.block_reserved_weight = fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(\n+                MINIMUM_BLOCK_RESERVED_WEIGHT, MAX_BLOCK_WEIGHT);\n+        }\n+        if (fuzzed_data_provider.ConsumeBool()) {\n+            init_options.block_max_weight = fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(\n+                MINIMUM_BLOCK_RESERVED_WEIGHT, MAX_BLOCK_WEIGHT);\n+        }\n+        // Match ReadMiningArgs(), which rejects invalid init-time options.\n+        if (!node::CheckMiningOptions(init_options, /*use_argnames=*/false)) continue;\n+\n         BlockCreateOptions options;\n         options.test_block_validity = use_valid_transactions;\n         if (fuzzed_data_provider.ConsumeBool()) {\n@@ -207,9 +228,14 @@ FUZZ_TARGET(block_template_manager, .init = initialize_block_template_manager)\n         if (!use_valid_transactions && fuzzed_data_provider.ConsumeBool()) {\n             options.coinbase_output_script = ConsumeScript(fuzzed_data_provider);\n         }\n+        const auto resolved{node::FlattenMiningOptions(node::MergeMiningOptions(options, init_options))};\n+        // Per-call options must also result in a valid effective configuration.\n+        if (!node::CheckMiningOptions(resolved, /*use_argnames=*/false)) continue;\n+\n+        node::BlockTemplateManager block_template_manager{\n+            mempool, chainman, notifications, std::move(init_options)};\n         auto block_template = block_template_manager.CreateNewTemplate(options);\n         assert(block_template);\n-        const auto resolved{node::FlattenMiningOptions(node::MergeMiningOptions(options, block_template_manager.BlockCreateArgs()))};\n         const CBlock& block{block_template->block};\n         // Coinbase is first; the per-tx vectors exclude it and track the block.\n         assert(!block.vtx.empty() && block.vtx[0]->IsCoinBase());\n```"
  },
  {
   "t": "2026-07-18T15:41:37Z",
   "kind": "review",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "1881848e0c99e1e55f9739d2a097bfdad6e3873c",
   "text": "Code Review 1881848e0c\n\nMost changes here are refactors (move), it preserves the code paths that it moves into the block template manager and also the fuzz test coverage is good as well.\n\nLeft some questions and suggestions, mostly non blocking"
  },
  {
   "t": "2026-07-20T09:01:05Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/node/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": 3603018165,
   "text": "See #34672"
  },
  {
   "t": "2026-07-20T11:43:08Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "e7f0008848cf39fee548178850eff3e5abb771c9"
  },
  {
   "t": "2026-07-20T11:45:19Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/node/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": 3603018165,
   "text": "Doing this is beyond the scope of this PR, I think."
  },
  {
   "t": "2026-07-20T11:46:14Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/node/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": 3608430390,
   "text": "This is a move-only commit and aim to reserve call site arguments, changing the parameter types is beyond the scope of this PR."
  },
  {
   "t": "2026-07-20T11:50:55Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "Forced pushed from 1881848e0c99e1e55f9739d2a097bfdad6e3873c to e7f0008848cf39fee548178850eff3e5abb771c9 1881848e0c...e7f0008848\n\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3598491053 Used the same pattern as mempool and chainman but moved the assert to the same location just below them.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3598757258 Fixed.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3608471266 Fixed, note: you use the wrong commit hash and title.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3608594864 Fixed, thanks.\n- https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3608652851 The aim is to cover block assembler and the manager, using custom block create args won't add much because merely having a custom block create option gives us the same hit. But I use the diff as inspiration to extend the harness to support non-standard options and add further fuzz, so that we ensure incorrect options are checked as well, and when it throws due to that, we expect a match btw the check we do manually and the internal check done by the block assembler."
  },
  {
   "t": "2026-07-20T12:25:20Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "CI is unhappy :-(\n\n@ismaelsadeeq I find it easier to follow inline comments if you (also) reply there instead of (only) in the main thread."
  },
  {
   "t": "2026-07-20T12:28:09Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "d36b3cd0e2f0ccee652c5f34d6379901b02e8abf"
  },
  {
   "t": "2026-07-20T12:30:57Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nFixed it's a stale using declaration in the unit test, after the recent push. Fixed.\n\n[quoted text omitted]\nNoted, but github u.i makes the PR review much harder after just a few comments; that's why I usually batch them with a link (I guess you are okay with a few clicks to expand :))."
  },
  {
   "t": "2026-07-20T13:42:09Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "@ismaelsadeeq yes, I expand them all anyway"
  },
  {
   "t": "2026-07-20T14:20:56Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/rpc/mining.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "In 4872a562104fb81c6aad2a528408f492bd5f5306 _rpc: build getblocktemplate via BlockTemplateManager_: there's now an opportunity to avoid copies, like #32547 tried to do:\n\n```diff\ndiff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp\nindex a86e5dc54e..20c6ed928a 100644\n--- a/src/rpc/mining.cpp\n+++ b/src/rpc/mining.cpp\n@@ -911,5 +911,5 @@ static RPCMethod getblocktemplate()\n     }\n     CHECK_NONFATAL(pindexPrev);\n-    CBlock block{block_template->block};\n+    CBlock& block{block_template->block};\n\n     // Update nTime\n@@ -924,6 +924,6 @@ static RPCMethod getblocktemplate()\n     UniValue transactions(UniValue::VARR);\n     std::map<Txid, int64_t> setTxIndex;\n-    std::vector<CAmount> tx_fees{block_template->vTxFees};\n-    std::vector<int64_t> tx_sigops{block_template->vTxSigOpsCost};\n+    const std::vector<CAmount>& tx_fees{block_template->vTxFees};\n+    const std::vector<int64_t>& tx_sigops{block_template->vTxSigOpsCost};\n\n     int i = 0;\n@@ -1053,5 +1053,5 @@ static RPCMethod getblocktemplate()\n     }\n\n-    if (auto coinbase{block_template->m_coinbase_tx}; coinbase.required_outputs.size() > 0) {\n+    if (const auto& coinbase{block_template->m_coinbase_tx}; coinbase.required_outputs.size() > 0) {\n         CHECK_NONFATAL(coinbase.required_outputs.size() == 1); // Only one output is currently expected\n         result.pushKV(\"default_witness_commitment\", HexStr(coinbase.required_outputs[0].scriptPubKey));\n```\n\nThis can be made robust against using multiple clients (which we don't really support), per Claude:\n\n[quoted text omitted]"
  },
  {
   "t": "2026-07-20T14:21:16Z",
   "kind": "review",
   "who": "Sjors",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "d36b3cd0e2f0ccee652c5f34d6379901b02e8abf",
   "text": "ACK d36b3cd0e2f0ccee652c5f34d6379901b02e8abf"
  },
  {
   "t": "2026-07-20T15:40:17Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "c2dde7de2c2f77b89eb888211249c3b21410eb59"
  },
  {
   "t": "2026-07-20T15:40:52Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/rpc/mining.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": 3614979888,
   "text": "Good idea, done in d36b3cd0e2...c2dde7de2c"
  },
  {
   "t": "2026-07-23T12:51:57Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "5ff6e9a81a100f14eaab0711fcf325e46ae1b21f"
  },
  {
   "t": "2026-07-23T13:59:02Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "c5864dbae4e525162fd075c2d986e1553459eaab"
  },
  {
   "t": "2026-07-23T15:00:15Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "Rebased C. I failure in https://github.com/bitcoin/bitcoin/actions/runs/30013677040/job/89227984564?pr=35675 seems unrelated, it is a private broadcast test failure."
  },
  {
   "t": "2026-07-23T17:18:23Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/block_template_manager.cpp",
   "commit": "c5864dbae4e525162fd075c2d986e1553459eaab",
   "in_reply_to": null,
   "text": "In c5864dbae4e525162fd075c2d986e1553459eaab _test: fuzz BlockTemplateManager_ nit: indentation seems a bit excessive."
  },
  {
   "t": "2026-07-23T17:20:19Z",
   "kind": "review",
   "who": "Sjors",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "c5864dbae4e525162fd075c2d986e1553459eaab",
   "text": "Code review c5864dbae4e525162fd075c2d986e1553459eaab\n\nI think your rebase after https://github.com/bitcoin/bitcoin/pull/34672 reverted some of the changes from that PR.\n\nOther changes since my last review:\n- added 5284840786f7c440b11f2add76d03a06beb5f38a and 8faf339323d8e3dc1cd148ec1a08c973b4ab7d09 after my suggestion in https://github.com/bitcoin/bitcoin/pull/35675#discussion_r3614979888"
  },
  {
   "t": "2026-07-23T22:38:04Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "LGTM, but the current code is really reverting https://github.com/bitcoin/bitcoin/pull/34672.\n\nSuggestion:\n\n```diff\ndiff --git a/src/node/block_template_manager.cpp b/src/node/block_template_manager.cpp\nindex 376e030a91..a4d2d03ddf 100644\n--- a/src/node/block_template_manager.cpp\n+++ b/src/node/block_template_manager.cpp\n@@ -71,7 +71,7 @@ protected:\n };\n } // namespace\n\n-bool BlockTemplateManager::SubmitBlock(const std::shared_ptr<const CBlock>& block, bool* new_block, std::string& reason, std::string& debug)\n+bool BlockTemplateManager::SubmitBlock(const std::shared_ptr<const CBlock>& block, std::string& reason, std::string& debug)\n {\n     reason.clear();\n     debug.clear();\n@@ -84,10 +84,13 @@ bool BlockTemplateManager::SubmitBlock(const std::shared_ptr<const CBlock>& bloc\n     // results.\n     auto sc = std::make_shared<SubmitBlockStateCatcher>(block->GetHash());\n     CHECK_NONFATAL(m_chainman.m_options.signals)->RegisterSharedValidationInterface(sc);\n-    bool accepted = m_chainman.ProcessNewBlock(block, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/new_block);\n+    bool new_block;\n+    bool accepted = m_chainman.ProcessNewBlock(block, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block);\n+    // No queue drain is needed. The BlockChecked notification used above is\n+    // emitted synchronously by ProcessNewBlock, unlike most validation signals.\n     CHECK_NONFATAL(m_chainman.m_options.signals)->UnregisterSharedValidationInterface(sc);\n\n-    if (new_block && !*new_block && accepted) {\n+    if (!new_block && accepted) {\n         reason = \"duplicate\";\n     } else if (!accepted && (!sc->m_found || sc->m_state.IsValid())) {\n         // ProcessNewBlock can fail without a validation result, for example\n@@ -96,17 +99,16 @@ bool BlockTemplateManager::SubmitBlock(const std::shared_ptr<const CBlock>& bloc\n         // inconclusive.\n         reason = \"inconclusive\";\n     } else if (!sc->m_found) {\n-        // A block can be accepted and stored without being connected, for\n-        // example if it does not have more work than the current tip. In that\n-        // case no BlockChecked callback is emitted, so the validation result is\n-        // inconclusive. Mining::submitBlock treats this as an error for mining\n-        // clients, but it does not mean the block is invalid.\n+        // The block was accepted but not connected, for example if it does not\n+        // have more work than the current tip.\n         reason = \"inconclusive\";\n     } else if (!sc->m_state.IsValid()) {\n         reason = sc->m_state.GetRejectReason();\n         debug = sc->m_state.GetDebugMessage();\n     }\n-    return accepted;\n+    const bool result{accepted && new_block && reason.empty()};\n+    CHECK_NONFATAL(result == reason.empty());\n+    return result;\n }\n\n std::optional<BlockRef> BlockTemplateManager::GetTip()\ndiff --git a/src/node/block_template_manager.h b/src/node/block_template_manager.h\nindex 27a339ac7d..142665e8ad 100644\n--- a/src/node/block_template_manager.h\n+++ b/src/node/block_template_manager.h\n@@ -49,8 +49,9 @@ public:\n     /** Create a fresh block template, applying init-time defaults to any unset options. */\n     std::unique_ptr<CBlockTemplate> CreateNewTemplate(const BlockCreateOptions& options);\n\n-    /** Submit a block via ProcessNewBlock and capture validation state. */\n-    bool SubmitBlock(const std::shared_ptr<const CBlock>& block, bool* new_block, std::string& reason, std::string& debug);\n+    /** Submit a block via ProcessNewBlock and capture validation state.\n+     *  @return whether the block was accepted as a new valid block. */\n+    bool SubmitBlock(const std::shared_ptr<const CBlock>& block, std::string& reason, std::string& debug);\n\n     /** Locks cs_main.\n      *  @return the active chain tip, or nullopt if none exists. */\ndiff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp\nindex f6a15aabc9..c56fd23a25 100644\n--- a/src/node/interfaces.cpp\n+++ b/src/node/interfaces.cpp\n@@ -920,9 +920,7 @@ public:\n     {\n         if (!coinbase) return false;\n         AddMerkleRootAndCoinbase(m_block_template->block, std::move(coinbase), version, timestamp, nonce);\n-        bool new_block;\n-        const bool accepted = block_template_manager().SubmitBlock(std::make_shared<const CBlock>(m_block_template->block), &new_block, reason, debug);\n-        return accepted && new_block && reason.empty();\n+        return block_template_manager().SubmitBlock(std::make_shared<const CBlock>(m_block_template->block), reason, debug);\n     }\n\n     std::unique_ptr<BlockTemplate> waitNext(BlockWaitOptions options) override\n@@ -1013,13 +1011,7 @@ public:\n\n     bool submitBlock(const CBlock& block_in, std::string& reason, std::string& debug) override\n     {\n-        auto block = std::make_shared<const CBlock>(block_in);\n-        bool new_block;\n-        const bool accepted = block_template_manager().SubmitBlock(block, &new_block, reason, debug);\n-        // ProcessNewBlock() can accept and store a block before it is checked\n-        // for validity. Treat duplicates as errors for mining clients, and only\n-        // return success when validation completed without setting a reason.\n-        return accepted && new_block && reason.empty();\n+        return block_template_manager().SubmitBlock(std::make_shared<const CBlock>(block_in), reason, debug);\n     }\n\n     std::vector<CTransactionRef> getTransactionsByTxID(const std::vector<Txid>& txids) override\ndiff --git a/src/test/fuzz/block_template_manager.cpp b/src/test/fuzz/block_template_manager.cpp\nindex 84a11001e8..4dc3e0c853 100644\n--- a/src/test/fuzz/block_template_manager.cpp\n+++ b/src/test/fuzz/block_template_manager.cpp\n@@ -319,11 +319,10 @@ FUZZ_TARGET(block_template_manager, .init = initialize_block_template_manager)\n         if (fuzzed_data_provider.ConsumeBool()) {\n             // An unsolved header can pass the regtest target by chance.\n             if (!CheckProofOfWork(block.GetHash(), block.nBits, node.chainman->GetConsensus())) {\n-                bool new_block{false};\n                 std::string reason;\n                 std::string debug;\n-                assert(!block_template_manager.SubmitBlock(std::make_shared<const CBlock>(block), &new_block, reason, debug));\n-                assert(!new_block && reason == \"high-hash\");\n+                assert(!block_template_manager.SubmitBlock(std::make_shared<const CBlock>(block), reason, debug));\n+                assert(reason == \"high-hash\");\n                 assert(debug == \"proof of work failed\");\n             }\n         }\n```"
  },
  {
   "t": "2026-07-24T13:09:19Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "a87d81548be53ed0af747adf5b8fd22e0d6b8289"
  },
  {
   "t": "2026-07-24T13:09:38Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/block_template_manager.cpp",
   "commit": "c5864dbae4e525162fd075c2d986e1553459eaab",
   "in_reply_to": 3640040021,
   "text": "fixed."
  },
  {
   "t": "2026-07-24T13:12:53Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nTaken, thanks."
  },
  {
   "t": "2026-07-24T13:19:44Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "utACK a87d81548be53ed0af747adf5b8fd22e0d6b8289"
  },
  {
   "t": "2026-07-28T14:12:40Z",
   "kind": "review",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "a87d81548be53ed0af747adf5b8fd22e0d6b8289",
   "text": "It looks like `src/wallet/test/fuzz/fees.cpp` needs the same reset/recreate handling as the other fuzz targets.\n\n```diff\ndiff --git a/src/wallet/test/fuzz/fees.cpp b/src/wallet/test/fuzz/fees.cpp\nindex 36c1f56b0a..2899051179 100644\n--- a/src/wallet/test/fuzz/fees.cpp\n+++ b/src/wallet/test/fuzz/fees.cpp\n@@ -2,6 +2,7 @@\n // Distributed under the MIT software license, see the accompanying\n // file COPYING or http://www.opensource.org/licenses/mit-license.php.\n\n+#include <node/block_template_manager.h>\n #include <test/fuzz/FuzzedDataProvider.h>\n #include <test/fuzz/fuzz.h>\n #include <test/fuzz/util.h>\n@@ -73,7 +74,9 @@ FUZZ_TARGET(wallet_fees, .init = initialize_setup)\n         .min_relay_feerate = CFeeRate{ConsumeMoney(fuzzed_data_provider, 1'000'000)},\n         .dust_relay_feerate = CFeeRate{ConsumeMoney(fuzzed_data_provider, 1'000'000)}\n     };\n+    node.block_template_manager.reset();\n     node.mempool = std::make_unique<CTxMemPool>(mempool_opts, error);\n+    g_setup->CreateBlockTemplateManager();\n     std::unique_ptr<CBlockPolicyEstimator> fee_estimator = std::make_unique<FuzzedBlockPolicyEstimator>(fuzzed_data_provider);\n     g_setup->SetFeeEstimator(std::move(fee_estimator));\n     auto target_feerate{CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/1'000'000)}};\n```"
  },
  {
   "t": "2026-07-29T08:56:16Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "ba196b7f99d762d1efe43248c3069efa938761b5"
  },
  {
   "t": "2026-07-29T08:57:43Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "[a87d81548b...ba196b7f99](https://github.com/bitcoin/bitcoin/compare/a87d81548be53ed0af747adf5b8fd22e0d6b8289..ba196b7f99d762d1efe43248c3069efa938761b5)\n\nhttps://github.com/bitcoin/bitcoin/pull/35675#pullrequestreview-4798195437 Fixed  @w0xlt , thanks"
  },
  {
   "t": "2026-07-29T10:02:06Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "dad787680358adeba552802c152343a7d9ddfbd0"
  },
  {
   "t": "2026-07-29T11:23:33Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "C.I failure seems unrelated."
  },
  {
   "t": "2026-07-29T21:35:57Z",
   "kind": "review_comment",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "path": "src/node/interfaces.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": 3545821698,
   "text": "I think this hasn't been addressed..."
  },
  {
   "t": "2026-07-29T22:28:48Z",
   "kind": "review",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "dad787680358adeba552802c152343a7d9ddfbd0",
   "text": "ACK dad787680358adeba552802c152343a7d9ddfbd0\n\nThat is the code in `ChainTestingSetup::CreateBlockTemplateManager` I was referring to earlier:\n\n```c++\nm_node.block_template_manager = std::make_unique<node::BlockTemplateManager>(*m_node.mempool, *m_node.chainman, *Assert(m_node.notifications), std::move(*mining_args));\n```\n\nAlthough unused here, replacing `node.mempool` would leave `BlockTemplateManager` holding a dangling reference in the previous code."
  },
  {
   "t": "2026-07-30T03:57:10Z",
   "kind": "review_comment",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "path": "src/rpc/mining.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "`getmininginfo` now calls `EnsureBlockTemplateManager()` which throws if called before chainstate is loaded (RPC server doesn't accept connections before chainstate is loaded \u2014 unreachable in practice), whereas [previously](https://github.com/bitcoin/bitcoin/blob/master/src/rpc/mining.cpp#L505) it silently returned zero/ default-initialized mining args. I think this is stricter and more correct."
  },
  {
   "t": "2026-07-30T04:07:08Z",
   "kind": "review_comment",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "nit (not blocking): If PoW passes by chance, it skips entirely. So the `reason == \"duplicate\"`, `reason == \"inconclusive\"`, and `result == true` paths are never exercised. Perhaps this path can be added:\n\n```\n  // Test SubmitBlock success/ duplicate path with a solved block\n  if (use_valid_transactions && fuzzed_data_provider.ConsumeBool()) {\n      CBlock solved = block_template->block;\n      while (!CheckProofOfWork(solved.GetHash(), solved.nBits, node.chainman->GetConsensus()))\n          ++solved.nNonce;\n      std::string reason, debug;\n      block_template_manager.SubmitBlock(std::make_shared<const CBlock>(solved), reason, debug);\n      assert(reason != \"high-hash\"); // PoW is valid so never a pow failure\n  }\n```"
  },
  {
   "t": "2026-07-30T04:13:13Z",
   "kind": "review",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "dad787680358adeba552802c152343a7d9ddfbd0",
   "text": "ACK dad787680358\n\nNotable changes since my [previous](https://github.com/bitcoin/bitcoin/pull/35675#pullrequestreview-4710309763) ACK at 82dc581a:\n- @Sjors' copy-avoidance suggestions landed in two dedicated commits and a [#34672](https://github.com/bitcoin/bitcoin/pull/34672) rebase regression was caught and fixed;\n- @w0xlt caught a missing reset/ recreate in `wallet/test/fuzz/fees.cpp`;\n- @enirox001's fuzz coverage suggestions were taken and the harness extended.\n\nLeft a couple of comments/ nits below."
  },
  {
   "t": "2026-07-30T09:49:06Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/node/interfaces.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": 3545821698,
   "text": "Yeah, it's a nit.  I would prefer not to add this comment, so I ignored it. :)"
  },
  {
   "t": "2026-07-30T15:36:12Z",
   "kind": "review_comment",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "path": "src/node/interfaces.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": 3545821698,
   "text": "No problem at all, thought it got missed as it was marked as fixed. Thanks!"
  },
  {
   "t": "2026-07-31T11:18:05Z",
   "kind": "review",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "dad787680358adeba552802c152343a7d9ddfbd0",
   "text": "ACK https://github.com/bitcoin/bitcoin/pull/35675/changes/dad787680358adeba552802c152343a7d9ddfbd0"
  },
  {
   "t": "2026-08-05T12:28:14Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "af294d84f56b2a9a5f57ac974d03a47cf89c752f"
  },
  {
   "t": "2026-08-05T12:46:49Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "Rebased to fix the merge conflict in iwyu.\n\n@marcofleon, friendly review ping on the fuzz harness added here."
  },
  {
   "t": "2026-08-14T22:07:32Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "6fea30c82cf7e66a3d35cb1ca70735d4ef282806"
  },
  {
   "t": "2026-08-14T22:07:33Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "6fea30c82cf7e66a3d35cb1ca70735d4ef282806"
  },
  {
   "t": "2026-08-18T21:48:19Z",
   "kind": "review",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "6fea30c82cf7e66a3d35cb1ca70735d4ef282806",
   "text": "reACK 6fea30c82cf7e66a3d35cb1ca70735d4ef282806"
  },
  {
   "t": "2026-08-20T11:40:10Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Code review 6fea30c82cf7e66a3d35cb1ca70735d4ef282806\n\nAll good, except:\n\n```diff\ndiff --git a/ci/test/03_test_script.sh b/ci/test/03_test_script.sh\nindex c30a26d113..c7c199d530 100755\n--- a/ci/test/03_test_script.sh\n+++ b/ci/test/03_test_script.sh\n@@ -235,5 +235,5 @@ fi\n if [[ \"${RUN_IWYU}\" == true ]]; then\n   # TODO: Consider enforcing IWYU across the entire codebase.\n-  FILES_WITH_ENFORCED_IWYU='/src/((bench|common|consensus|crypto|index|kernel|primitives|script|univalue/(lib|test)|util|zmq)/.*|node/(block_template_manager\\blockstorage|interfaces|miner|mining_args|utxo_snapshot)|rpc/mining|clientversion|core_io|signet|init)\\.cpp'\n+  FILES_WITH_ENFORCED_IWYU='/src/((bench|common|consensus|crypto|index|kernel|primitives|script|univalue/(lib|test)|util|zmq)/.*|node/(block_template_manager|blockstorage|interfaces|miner|mining_args|utxo_snapshot)|rpc/mining|clientversion|core_io|signet|init)\\.cpp'\n   jq --arg patterns \"$FILES_WITH_ENFORCED_IWYU\" 'map(select(.file | test($patterns)))' \"${BASE_BUILD_DIR}/compile_commands.json\" > \"${BASE_BUILD_DIR}/compile_commands_iwyu_errors.json\"\n   jq --arg patterns \"$FILES_WITH_ENFORCED_IWYU\" 'map(select(.file | test($patterns) | not))' \"${BASE_BUILD_DIR}/compile_commands.json\" > \"${BASE_BUILD_DIR}/compile_commands_iwyu_warnings.json\"\n```\n\nWhich in turn will suggest:\n\n```cpp\n#include <node/mining_types.h>\n#include <util/time.h>\n```"
  },
  {
   "t": "2026-09-08T11:05:44Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "5f01715a32c98ca6fd6dc8301287045be0c8104c"
  },
  {
   "t": "2026-09-08T12:49:18Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "9a663e7032257b92d7e52c510d7bf57c43049bde"
  },
  {
   "t": "2026-09-09T07:44:59Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "ACK 9a663e7032257b92d7e52c510d7bf57c43049bde"
  },
  {
   "t": "2026-09-11T01:47:06Z",
   "kind": "review",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "9a663e7032257b92d7e52c510d7bf57c43049bde",
   "text": "re-ACK 9a663e7032257b92d7e52c510d7bf57c43049bde"
  },
  {
   "t": "2026-09-11T09:50:49Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "(needs a rebase first though)"
  },
  {
   "t": "2026-09-13T11:11:35Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "40d21b36782ba99d4cfac9ac136cd1e4c6eb285f"
  },
  {
   "t": "2026-09-13T12:45:24Z",
   "kind": "force_push",
   "who": "ismaelsadeeq",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08"
  },
  {
   "t": "2026-09-14T09:56:26Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "re-ACK e8b283c35a656f808250e4b32d3494f49a000b08"
  },
  {
   "t": "2026-09-14T15:03:04Z",
   "kind": "review",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "text": "re-ACK e8b283c35a656f808250e4b32d3494f49a000b08\n\nClean rebase since my last review."
  },
  {
   "t": "2026-09-14T23:09:52Z",
   "kind": "review",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "text": "reACK e8b283c35a656f808250e4b32d3494f49a000b08"
  },
  {
   "t": "2026-09-16T16:06:12Z",
   "kind": "review",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "text": "reACK https://github.com/bitcoin/bitcoin/pull/35675/changes/e8b283c35a656f808250e4b32d3494f49a000b08"
  },
  {
   "t": "2026-09-16T16:21:02Z",
   "kind": "review_comment",
   "who": "marcofleon",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "Checking for whether the buffer has any bytes left is fine for this `LIMITED_WHILE`, but not so much in the mempool population functions above. Having all the loops gate on this means that populating the mempool and creating the template are competing for the same fuzz input bytes, so a lot of the time a template won't even be created. Or if one is, then the mempool is likely small/empty.\n\nI think using the classic `LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), N` for the mempool population would be better."
  },
  {
   "t": "2026-09-16T16:37:25Z",
   "kind": "review_comment",
   "who": "marcofleon",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "I don't think this really tests anything here, same for the merge in `CreateNewTemplate()` below. This harness uses the startup args from `TestingSetup` so the four options for the `y` value in `MergeMiningOptions` are unset. Merge would only be tested if `y` had a value afaict.\n\nA unit test might better suited for testing this part of the manager?"
  },
  {
   "t": "2026-09-16T16:42:19Z",
   "kind": "review_comment",
   "who": "marcofleon",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "The main while loop inside of `CooldownIfHeadersAhead` never runs, so this isn't testing much. I don't know if this assertion is worth having in the test."
  },
  {
   "t": "2026-09-16T16:47:57Z",
   "kind": "review_comment",
   "who": "marcofleon",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "With the simplified proof of work for fuzzing, this check would pass half the time. I think if this check passes, the failure would happen at `bad-txnmrklroot`. So we could have a check for that if proof of work passes and then the else block would be what is currently in the test below. I think the `miner_tests` unit test might already test this?\n\nEither way, it would be nice to test `SubmitBlock()` in a more effective way."
  },
  {
   "t": "2026-09-16T16:50:15Z",
   "kind": "review_comment",
   "who": "marcofleon",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/block_template_manager.cpp",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "in_reply_to": null,
   "text": "How in-depth are these assertions for `WaitTipChanged` and `WaitAndCreateNewBlock` if the timeout is 0 and the tip never changes? Maybe we could come up with a harness that would cover these better."
  },
  {
   "t": "2026-09-16T17:26:23Z",
   "kind": "review",
   "who": "marcofleon",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "e8b283c35a656f808250e4b32d3494f49a000b08",
   "text": "light code review ACK 3d7d190b6995b3c0adb6528c2d9eac81a8f839d9\n\nThe refactor is pretty straightforward, mostly moving around existing code to `BlockTemplateManager`.\n\nFor the fuzz test, I don't think this is really a `BlockTemplateManager` test. I'd say it's more testing `BlockAssembler` behavior from a fuzzed mempool (with invalid txs too). I think `tx_pool` and `tx_pool_standard` do this already, although they use `AcceptToMemoryPool` as a filter. For a better manager test, we could use fuzzed startup args and check `CreateNewTemplate` against `BlockAssembler` with those options filled in manually, using the same mempool and tip. Could also be worth trying to figure out how to better test the wait functions, `CooldownIfHeadersAhead`, and `SubmitBlock`, beyond the immediate return paths. I'd be happy to work on or review a separate PR focused on a re-worked `block_template_manager` target.\n\nI don't think this is a blocker for the refactor. I would just drop the last commit and PR it separately."
  }
 ],
 "labels_log": [
  {
   "t": "2026-07-07T11:06:00Z",
   "action": "labeled",
   "label": "Mining",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-20T12:28:35Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-20T13:32:22Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-23T00:52:13Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-23T13:52:56Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-23T14:02:10Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-24T14:11:55Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-29T09:40:33Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-29T12:55:48Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-04T11:48:57Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-05T14:02:15Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-07T09:25:22Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-14T22:29:01Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-21T08:23:54Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-08T11:31:55Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-08T12:08:09Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-08T13:54:17Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-09T16:24:22Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-13T11:28:39Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-13T12:16:00Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-13T13:49:22Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [],
 "text_chars": 49763,
 "text_tokens_estimate": 12440,
 "changed_paths": [
  "ci/test/03_test_script.sh",
  "src/CMakeLists.txt",
  "src/init.cpp",
  "src/node/block_template_manager.cpp",
  "src/node/block_template_manager.h",
  "src/node/context.cpp",
  "src/node/context.h",
  "src/node/interfaces.cpp",
  "src/node/miner.cpp",
  "src/node/miner.h",
  "src/rpc/blockchain.cpp",
  "src/rpc/mining.cpp",
  "src/rpc/server_util.cpp",
  "src/rpc/server_util.h",
  "src/test/fuzz/CMakeLists.txt",
  "src/test/fuzz/block_template_manager.cpp",
  "src/test/fuzz/connect_block.cpp",
  "src/test/fuzz/rpc.cpp",
  "src/test/fuzz/utxo_snapshot.cpp",
  "src/test/miner_tests.cpp",
  "src/test/peerman_tests.cpp",
  "src/test/util/mining.cpp",
  "src/test/util/setup_common.cpp",
  "src/test/util/setup_common.h",
  "src/test/util/validation.cpp",
  "src/test/validation_block_tests.cpp",
  "src/test/validation_chainstatemanager_tests.cpp",
  "src/wallet/test/fuzz/fees.cpp"
 ],
 "files": [
  {
   "path": "ci/test/03_test_script.sh",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/CMakeLists.txt",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/init.cpp",
   "add": 8,
   "del": 8
  },
  {
   "path": "src/node/block_template_manager.cpp",
   "add": 288,
   "del": 0
  },
  {
   "path": "src/node/block_template_manager.h",
   "add": 106,
   "del": 0
  },
  {
   "path": "src/node/context.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/node/context.h",
   "add": 3,
   "del": 11
  },
  {
   "path": "src/node/interfaces.cpp",
   "add": 14,
   "del": 26
  },
  {
   "path": "src/node/miner.cpp",
   "add": 0,
   "del": 243
  },
  {
   "path": "src/node/miner.h",
   "add": 0,
   "del": 58
  },
  {
   "path": "src/rpc/blockchain.cpp",
   "add": 15,
   "del": 16
  },
  {
   "path": "src/rpc/mining.cpp",
   "add": 36,
   "del": 38
  },
  {
   "path": "src/rpc/server_util.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/rpc/server_util.h",
   "add": 2,
   "del": 4
  },
  {
   "path": "src/test/fuzz/CMakeLists.txt",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/test/fuzz/block_template_manager.cpp",
   "add": 329,
   "del": 0
  },
  {
   "path": "src/test/fuzz/connect_block.cpp",
   "add": 3,
   "del": 0
  },
  {
   "path": "src/test/fuzz/rpc.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/test/fuzz/utxo_snapshot.cpp",
   "add": 3,
   "del": 0
  },
  {
   "path": "src/test/miner_tests.cpp",
   "add": 20,
   "del": 6
  },
  {
   "path": "src/test/peerman_tests.cpp",
   "add": 5,
   "del": 4
  },
  {
   "path": "src/test/util/mining.cpp",
   "add": 9,
   "del": 8
  },
  {
   "path": "src/test/util/setup_common.cpp",
   "add": 15,
   "del": 8
  },
  {
   "path": "src/test/util/setup_common.h",
   "add": 4,
   "del": 0
  },
  {
   "path": "src/test/util/validation.cpp",
   "add": 3,
   "del": 0
  },
  {
   "path": "src/test/validation_block_tests.cpp",
   "add": 10,
   "del": 9
  },
  {
   "path": "src/test/validation_chainstatemanager_tests.cpp",
   "add": 3,
   "del": 0
  },
  {
   "path": "src/wallet/test/fuzz/fees.cpp",
   "add": 3,
   "del": 0
  }
 ],
 "test_lines": 453,
 "git": {
  "head": "e8b283c35a656f808250e4b32d3494f49a000b08",
  "head_matches_backup": true,
  "base": "17817818da3cb67c95320b7a5d4d69bd649c1fbf",
  "commits": [
   {
    "sha": "482a5d2712",
    "subject": "node: introduce BlockTemplateManager",
    "files": 13,
    "add": 110,
    "del": 0
   },
   {
    "sha": "bcdadf2b71",
    "subject": "node: move mining_args to BlockTemplateManager",
    "files": 8,
    "add": 28,
    "del": 23
   },
   {
    "sha": "07964c525d",
    "subject": "miner: move SubmitBlock into BlockTemplateManager",
    "files": 5,
    "add": 79,
    "del": 70
   },
   {
    "sha": "037d3519f2",
    "subject": "node: move tip and wait helpers into BlockTemplateManager",
    "files": 7,
    "add": 250,
    "del": 253
   },
   {
    "sha": "400ac328c9",
    "subject": "interfaces: create block template via BlockTemplateManager",
    "files": 3,
    "add": 8,
    "del": 13
   },
   {
    "sha": "011cbfbcd2",
    "subject": "rpc: route getblocktemplate internals through node",
    "files": 5,
    "add": 28,
    "del": 6
   },
   {
    "sha": "0998c0bbdf",
    "subject": "rpc: build getblocktemplate via BlockTemplateManager",
    "files": 1,
    "add": 6,
    "del": 7
   },
   {
    "sha": "951fac807d",
    "subject": "rpc: do not copy template data in getblocktemplate",
    "files": 1,
    "add": 5,
    "del": 5
   },
   {
    "sha": "38556f817e",
    "subject": "rpc: only copy the header in getblocktemplate",
    "files": 1,
    "add": 11,
    "del": 11
   },
   {
    "sha": "8372721f20",
    "subject": "rpc: build generation templates via BlockTemplateManager",
    "files": 2,
    "add": 11,
    "del": 14
   },
   {
    "sha": "ec4f24ef18",
    "subject": "rpc: wait for tips via BlockTemplateManager",
    "files": 2,
    "add": 18,
    "del": 19
   },
   {
    "sha": "6c71166bcf",
    "subject": "test: create templates via BlockTemplateManager",
    "files": 6,
    "add": 32,
    "del": 32
   },
   {
    "sha": "ffe3a631f3",
    "subject": "node: remove NodeContext::mining and EnsureMining",
    "files": 5,
    "add": 0,
    "del": 21
   },
   {
    "sha": "3d7d190b69",
    "subject": "ci: enforce iwyu for block template manager",
    "files": 1,
    "add": 1,
    "del": 1
   },
   {
    "sha": "e8b283c35a",
    "subject": "test: fuzz BlockTemplateManager",
    "files": 2,
    "add": 330,
    "del": 0
   }
  ],
  "patch_truncated": true
 },
 "input_hash": "3561518962f14e99",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}