{
 "number": 36357,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/36357",
 "title": "mining: move BlockAssembler statics into BlockTemplateManager",
 "author": "w0xlt",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-09-27T09:09:59Z",
 "updated_at": "2026-10-31T23:32:49Z",
 "description_hash": "ee5b1f48bd379fef",
 "age_days": 35,
 "draft": true,
 "labels": [
  "Mining"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "e47e7acca377da1edb45616a6c0e3d9233841900",
 "head_ref": "block-template-manager-last-block-stats",
 "head_repo": "w0xlt/bitcoin",
 "head_history": [],
 "additions": 201,
 "deletions": 44,
 "changed_files": 5,
 "commit_count": 3,
 "size_bucket": "M",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "ismaelsadeeq",
      "url": "https://github.com/bitcoin/bitcoin/pull/36357#issuecomment-5866835254"
     }
    ]
   },
   "conflicts": []
  }
 },
 "acks_parsed": {
  "ismaelsadeeq": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-09-28T09:05:55Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 1,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "ismaelsadeeq"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-09-27T08:49:55Z",
  "last_reviewer_activity": "2026-09-28T09:05:55Z",
  "last_reviewer": "ismaelsadeeq",
  "author_silent_days": 35,
  "waiting_on_author_days": 34,
  "days_since_update": 1
 },
 "refs": {
  "mentioned": [
   33758,
   34075
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 33758,
    "type": "issue",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "BlockTemplate Manager Tracking issue"
   },
   {
    "number": 34075,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-08-21",
    "title": "fees: Introduce Mempool Based Fee Estimation to reduce overestimation"
   }
  ],
  "conflicts": []
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [],
 "body": "Based on https://github.com/bitcoin/bitcoin/pull/36356. Together with it, this completes the \"Remove remaining global states\" TODO in #33758.\n\n`getmininginfo` reports `currentblockweight` and `currentblocktx` from two process-wide statics, `BlockAssembler::m_last_block_weight` and `m_last_block_num_txs`, which every `BlockAssembler::CreateNewBlock()` call overwrites.\n\nLike the `getblocktemplate` statics, they are shared by every node context in the process and nothing checks their locking. They also record templates that were not built for mining: the mempool fee estimator builds its own templates with `BlockAssembler`, so `getmininginfo` can report the fee estimator's template instead of the last mining template.\n\nThis PR:\n\n- makes the two fields per-`BlockAssembler` instance, still set at the same point in `CreateNewBlock()`;\n- has `BlockTemplateManager::CreateNewTemplate()` record them in a member guarded by `cs_main`, which `getmininginfo` reads. `CreateNewTemplate()` now holds `cs_main` across the build, as `CreateNewBlock()` already did internally, so concurrent builds cannot record their stats out of order;\n- adds a unit test showing that manager builds are recorded, that the latest one wins, and that a direct `BlockAssembler` build is ignored.\n\nIn short: the last global mining state moves out of `BlockAssembler`, and `getmininginfo` reports the last template built for mining rather than whatever the node assembled last.",
 "commits": [
  {
   "sha": "ccb8e840397cda6ace76d53b87b463abe6ec5384",
   "date": "2026-09-27T07:48:52Z",
   "message": "node: cache a getblocktemplate template in BlockTemplateManager\n\nAdd a CachedBlockTemplate member to BlockTemplateManager, guarded by\ncs_main, along with GetCachedTransactionsUpdated() and\nRefreshCachedTemplate().\n\nRefreshCachedTemplate() follows the rules of the function-local statics\nin the getblocktemplate RPC, which the next commit replaces: the template\nis rebuilt when the tip changes, or when the mempool changed and the\ncached template is more than 5 seconds old. A failed rebuild leaves the\ncached template unset so that the next call tries again. The deprecated\nGetTime() is replaced with the equivalent Now<NodeSeconds>().\n\nUnlike the RPC, RefreshCachedTemplate() does not take the tip from the\ncaller or cache its CBlockIndex. It reads the active chain tip itself\nand compares it with the template's hashPrevBlock, so the cache cannot\npair a template with a tip it does not build on."
  },
  {
   "sha": "5cbfbc6b92a303589391f586e8e9eaa972a26e7c",
   "date": "2026-09-27T07:50:19Z",
   "message": "rpc: use the BlockTemplateManager cache in getblocktemplate\n\nReplace the function-local statics in getblocktemplate with the template\ncached by BlockTemplateManager. The state now lives as long as the node\ncontext instead of the process, and its synchronization is checked\nthrough GUARDED_BY(::cs_main).\n\nThe static CBlockIndex pointer is not carried over: pindexPrev is looked\nup from the template's hashPrevBlock, so it can no longer outlive the\nChainstateManager that owns it. The longpollid is built from\nhashPrevBlock as well, so it always names the block the template builds\non, and the tip no longer needs to be re-read after longpolling."
  },
  {
   "sha": "e47e7acca377da1edb45616a6c0e3d9233841900",
   "date": "2026-09-27T08:49:55Z",
   "message": "node: move last block stats into BlockTemplateManager\n\nBlockAssembler::m_last_block_num_txs and m_last_block_weight were\nprocess-wide statics, written by every BlockAssembler::CreateNewBlock()\ncall and read by getmininginfo as currentblocktx and currentblockweight.\n\nMake them per-instance, and have\nBlockTemplateManager::CreateNewTemplate() copy them into a member\nguarded by cs_main, which getmininginfo reads.\nCreateNewTemplate() now holds cs_main across the build, as\nCreateNewBlock() already did internally, so concurrent builds cannot\nrecord their stats out of order.\n\nBehavior change: only templates built through BlockTemplateManager\n(getblocktemplate, the generate RPCs and the mining interface) update\nthese fields. Templates that the mempool fee estimator builds with\nBlockAssembler directly no longer do, and neither does a build that\nthrows after assembling the block (e.g. a TestBlockValidity() failure).\n\nUpdate the getmininginfo help text to match."
  }
 ],
 "timeline": [
  {
   "t": "2026-09-28T09:05:55Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "Concept ACK\n\nNow that #34075 is merged, we can also move its cache in there, i.e cache the block template based on time and tip changes. This brings us multiple advantages\n\ni) We consolidate the block template caching for the RPC and fees to the block template manager for better code organisation and reuse.\nii) The mempool fee rate estimator's reliance on the chainstate manager is reduced; the only dependence after that will be on the chain tip, which can also be removed by using chain tip change validation signals. After that, the mempool fee rate estimator will be independent of the mempool and chainstate manager, in replacement with block template manager.",
   "id": "c:5866835254",
   "url": "https://github.com/bitcoin/bitcoin/pull/36357#issuecomment-5866835254",
   "hash": "bf596d03a453c77a",
   "edited": null
  }
 ],
 "labels_log": [
  {
   "t": "2026-09-27T09:10:03Z",
   "action": "labeled",
   "label": "Mining",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-09-27T15:09:34Z",
   "kind": "convert_to_draft",
   "who": "w0xlt"
  }
 ],
 "text_chars": 4689,
 "text_tokens_estimate": 1172,
 "changed_paths": [
  "src/node/block_template_manager.cpp",
  "src/node/block_template_manager.h",
  "src/node/miner.h",
  "src/rpc/mining.cpp",
  "src/test/miner_tests.cpp"
 ],
 "files": [
  {
   "path": "src/node/block_template_manager.cpp",
   "add": 49,
   "del": 2
  },
  {
   "path": "src/node/block_template_manager.h",
   "add": 45,
   "del": 2
  },
  {
   "path": "src/node/miner.h",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/rpc/mining.cpp",
   "add": 12,
   "del": 36
  },
  {
   "path": "src/test/miner_tests.cpp",
   "add": 91,
   "del": 0
  }
 ],
 "test_lines": 91,
 "git": {
  "head": "e47e7acca377da1edb45616a6c0e3d9233841900",
  "head_matches_backup": true,
  "base": "e8e7e91a1144c378dff4da2e2a562eb0f3f2e1d6",
  "patch_id": "410102f5a066356e",
  "commits": [
   {
    "sha": "ccb8e84039",
    "subject": "node: cache a getblocktemplate template in BlockTemplateManager",
    "files": 3,
    "add": 124,
    "del": 1
   },
   {
    "sha": "5cbfbc6b92",
    "subject": "rpc: use the BlockTemplateManager cache in getblocktemplate",
    "files": 1,
    "add": 6,
    "del": 31
   },
   {
    "sha": "e47e7acca3",
    "subject": "node: move last block stats into BlockTemplateManager",
    "files": 5,
    "add": 73,
    "del": 14
   }
  ],
  "patch_truncated": false
 },
 "patch_id": "410102f5a066356e",
 "input_hash": "516578578ec68bab",
 "extracted_at": "2026-11-02T01:09:49+00:00"
}