{
 "number": 36525,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/36525",
 "title": "mining: add getTotalFees(), protect m_block_template with mutex",
 "author": "Sjors",
 "author_association": "MEMBER",
 "created_at": "2026-10-14T08:43:40Z",
 "updated_at": "2026-10-31T01:56:47Z",
 "description_hash": "ecee650776ab6b10",
 "age_days": 18,
 "draft": false,
 "labels": [
  "Mining"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "cbadeff334f182086538d7483dc1146c559859e6",
 "head_ref": "2026/10/block-template-mutex",
 "head_repo": "Sjors/bitcoin",
 "head_history": [
  {
   "t": "2026-10-14T09:22:56Z",
   "sha": "4774cf1de6a4670323686bf93beb93035b3f6dad"
  }
 ],
 "additions": 130,
 "deletions": 33,
 "changed_files": 9,
 "commit_count": 5,
 "size_bucket": "M",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "ack": [
     {
      "login": "ismaelsadeeq",
      "url": "https://github.com/bitcoin/bitcoin/pull/36525#pullrequestreview-5543130614"
     },
     {
      "login": "enirox001",
      "url": "https://github.com/bitcoin/bitcoin/pull/36525#pullrequestreview-5545791068"
     },
     {
      "login": "w0xlt",
      "url": "https://github.com/bitcoin/bitcoin/pull/36525#pullrequestreview-5592756558"
     }
    ]
   },
   "conflicts": []
  }
 },
 "acks_parsed": {
  "ismaelsadeeq": {
   "kind": "ack",
   "hash": "cbadeff334f182086538d7483dc1146c559859e6",
   "t": "2026-10-16T04:37:47Z",
   "stale": false
  },
  "w0xlt": {
   "kind": "ack",
   "hash": "cbadeff334",
   "t": "2026-10-20T22:48:39Z",
   "stale": false
  },
  "enirox001": {
   "kind": "ack",
   "hash": null,
   "t": "2026-10-16T09:34:33Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 3,
  "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": [
   "enirox001",
   "ismaelsadeeq",
   "sedited",
   "w0xlt"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-10-29T09:19:02Z",
  "last_reviewer_activity": "2026-10-28T10:05:26Z",
  "last_reviewer": "sedited",
  "author_silent_days": 3,
  "waiting_on_author_days": 0,
  "days_since_update": 1
 },
 "refs": {
  "mentioned": [
   33745,
   36515
  ],
  "depends_on": [],
  "fixes": [
   36515
  ],
  "linked_issues": [
   {
    "number": 36515,
    "type": "issue",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "ipc: data race on `m_block_template` members"
   }
  ],
  "references": [
   {
    "number": 36515,
    "type": "issue",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "ipc: data race on `m_block_template` members"
   },
   {
    "number": 33745,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2025-11-12",
    "title": "mining: check witness commitment in submitBlock"
   }
  ],
  "conflicts": []
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [],
 "body": "This PR catches four birds with one stone:\n\n- fixes https://github.com/bitcoin/bitcoin/issues/36515\n- add a new `getTotalFees()` getter so IPC clients don't have to sum up individual transaction fees\n- storing fees in `m_total_fees` simplifies the `WaitAndCreateNewBlock` implementation.\n- make it easier to add fee info to (future) external templates, as requested in    https://github.com/bitcoin/bitcoin/pull/35671#issuecomment-6065004542\n\nThe last commit contains the fix for #36515, which is made easier by the changes before it. It can be tested with: https://gist.github.com/Sjors/832841d705e62a6addaf8e6d4cf1b155 (the test itself didn't seem worth including, though I could look into simplifying it)\n\nIn case this PR doesn't make it, I'll open a fresh one with a direct fix.",
 "commits": [
  {
   "sha": "f1385049b23bba4fe9e2999d2a029c3f4d43ba6d",
   "date": "2026-10-14T05:51:35Z",
   "message": "mining: store total fees in CBlockTemplate\n\nAvoid repeated summation and the current_fees cache when comparing\ntemplates.\n\nThis commit does not change behavior."
  },
  {
   "sha": "b2a3c22a937504c561987926d05a25770eda4494",
   "date": "2026-10-14T05:51:48Z",
   "message": "mining: add BlockTemplate::getTotalFees()\n\nMake the new m_total_fees available for IPC clients that don't need individual\ntransaction fees."
  },
  {
   "sha": "01867ed3312715679649aa85530e50c3b2dd3ef1",
   "date": "2026-10-14T05:51:55Z",
   "message": "mining: pass template hash and fees to WaitAndCreateNewBlock\n\nThis lets the caller read the template under lock, then release the lock\nbefore waiting."
  },
  {
   "sha": "4774cf1de6a4670323686bf93beb93035b3f6dad",
   "date": "2026-10-14T08:21:01Z",
   "message": "mining: protect block template access with a mutex\n\nIPC calls on different worker threads can race when submitSolution()\nmodifies a template while another call reads it.\n\nFixes #36515."
  },
  {
   "sha": "cbadeff334f182086538d7483dc1146c559859e6",
   "date": "2026-10-16T00:22:24Z",
   "message": "test: exercise concurrent IPC block template access\n\nRead a template while submitting solutions through a separate IPC worker\nthread to cover races between the getters and submitSolution(). Repeat\nsubmissions to increase the chance of detecting a race under\nThreadSanitizer.\n\nBound the scenario and template cleanup using timeout_factor, and cancel\nand await both concurrent tasks before destroying the template.\n\nCo-authored-by: Abubakar Sadiq Ismail <48946461+ismaelsadeeq@users.noreply.github.com>"
  }
 ],
 "timeline": [
  {
   "t": "2026-10-14T09:22:56Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "4774cf1de6a4670323686bf93beb93035b3f6dad",
   "id": "push:4774cf1de6a4670323686bf93beb93035b3f6dad"
  },
  {
   "t": "2026-10-15T04:44:50Z",
   "kind": "review",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "4774cf1de6a4670323686bf93beb93035b3f6dad",
   "text": "ACK 4774cf1de6a4670323686bf93beb93035b3f6dad\n\n[quoted text omitted]\nI thought the client needed to access the individual fees, so we return a vector. It makes sense to return it so they don't have to sum them, so the vector one is now redundant?\n\nSee minimal test that fails on our tsan functional tests.\n\n```diff\ndiff --git a/test/functional/interface_ipc_mining.py b/test/functional/interface_ipc_mining.py\nindex c55a8f7a54..a969691596 100755\n--- a/test/functional/interface_ipc_mining.py\n+++ b/test/functional/interface_ipc_mining.py\n@@ -41,6 +41,7 @@ from test_framework.ipc_util import (\n     assert_create_new_block_fails,\n     destroying,\n     load_capnp_modules,\n+    make_capnp_init_ctx,\n     make_mining_ctx,\n     mining_create_block_template,\n     mining_get_block,\n@@ -799,6 +800,45 @@ class IPCMiningTest(BitcoinTestFramework):\n\n         asyncio.run(capnp.run(async_routine()))\n\n+    def run_concurrent_template_access_test(self):\n+        \"\"\"Reading and submitting a template at the same time must not race.\"\"\"\n+        self.log.info(\"Running concurrent template access test\")\n+\n+        async def async_routine():\n+            # One context runs on one server thread. Two contexts run on two\n+            # threads, so the read and the submit can run at the same time and race.\n+            reader_ctx, init = await make_capnp_init_ctx(self)\n+            writer_ctx = self.capnp_modules['proxy'].Context()\n+            writer_ctx.thread = init.construct().threadMap.makeThread(\"pythread\").result\n+            mining = init.makeMining(reader_ctx).result\n+\n+            async with AsyncExitStack() as stack:\n+                template = await mining_create_block_template(\n+                    mining, stack, reader_ctx, self.default_block_create_options, cooldown=False)\n+                assert template is not None\n+                block = await mining_get_block(template, reader_ctx)\n+                # No witness means every submit is rejected, so the tip never moves.\n+                coinbase = block.vtx[0].serialize_without_witness()\n+                height = self.nodes[0].getblockcount()\n+\n+                done = asyncio.Event()\n+\n+                async def read_loop():\n+                    while not done.is_set():\n+                        await template.getBlockHeader(reader_ctx)\n+                        await template.getBlock(reader_ctx)\n+\n+                async def submit_loop():\n+                    for _ in range(100):\n+                        await template.submitSolution(\n+                            writer_ctx, block.nVersion, block.nTime, block.nNonce, coinbase)\n+                    done.set()\n+\n+                await asyncio.gather(read_loop(), submit_loop())\n+                assert_equal(self.nodes[0].getblockcount(), height)\n+\n+        asyncio.run(capnp.run(async_routine()))\n+\n     def run_test(self):\n         self.miniwallet = MiniWallet(self.nodes[0])\n         # Amount of time in milliseconds the test is allowed to wait or be idle before it should fail.\n@@ -815,6 +855,7 @@ class IPCMiningTest(BitcoinTestFramework):\n         self.run_block_max_weight_test()\n         self.run_ipc_option_override_test()\n         self.run_transaction_lookup_test()\n+        self.run_concurrent_template_access_test()\n\n         # Needs to run last because it resets the chain.\n         self.run_low_height_test()\n```",
   "id": "r:5528160495",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#pullrequestreview-5528160495",
   "hash": "13c3c80dfb08ce23",
   "edited": null
  },
  {
   "t": "2026-10-15T05:24:06Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "The vector was initially introduced for (internal) compatibility with the `getblocktemplate` RPC, which returns this fee information.\n\n`sv2-tp` only uses fees (the total) for logging, the SRI implementation doesn't use it at all.\n\nI haven't done an inventory of software out there that uses the fees from `getblocktemplate`. One use case I could imagine is a miner dashboard that shows the current template you're working on, and a breakdown of fees.\n\nWe definitely need the internal total. Providing the total and individual via IPC is trivial, so I think we should keep it.",
   "id": "c:6185087776",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6185087776",
   "hash": "bcf1a15fefc411f5",
   "edited": "2026-10-15T05:24:22Z"
  },
  {
   "t": "2026-10-15T05:58:07Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nIt works and I'm open to taking it, but failure isn't deterministic (just very likely with the 100 iterations).",
   "id": "c:6185555791",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6185555791",
   "hash": "e61a789a8fe69053",
   "edited": null
  },
  {
   "t": "2026-10-15T06:06:24Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "Concept ACK",
   "id": "c:6185669122",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6185669122",
   "hash": "e8334cc43879b73c",
   "edited": null
  },
  {
   "t": "2026-10-15T07:26:19Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nIndeed, it is not deterministic but is sure to fail at some point.",
   "id": "c:6186844629",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6186844629",
   "hash": "ecad5741c272c566",
   "edited": null
  },
  {
   "t": "2026-10-15T09:07:11Z",
   "kind": "review",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "4774cf1de6a4670323686bf93beb93035b3f6dad",
   "text": "ACK 4774cf1de6a4670323686bf93beb93035b3f6dad\n\nThis change fixes a data race between ipc calls running on different worker threads, where `submitSolution` modifies a template while another call reads it. It also adds the `getTotalFees` IPC method, allowing external clients to retrieve the total fees without summing individual transaction fees.\n\nRan @ismaelsadeeq\u2019s ipc test with tsan, repeating each submission setting (100, 150, 200 and 300) 25 times. Without the mutex fix, every run detected the race. With the fix, every run passed with no race reports or timeouts\n\nI think the test is worth including. even though reproduction depends on scheduling, it consistently caught the regression on my machine (macOS arm)",
   "id": "r:5530690658",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#pullrequestreview-5530690658",
   "hash": "8db9ac6167aa408a",
   "edited": "2026-10-15T15:17:52Z"
  },
  {
   "t": "2026-10-16T00:35:43Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Added the test in a new commit, with some adjustments:\n- use `timeout_factor` to avoid false positive failures\n- improve cleanup",
   "id": "c:6205291499",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6205291499",
   "hash": "01cb05fd03ef771e",
   "edited": null
  },
  {
   "t": "2026-10-16T04:37:47Z",
   "kind": "review",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "cbadeff334f182086538d7483dc1146c559859e6",
   "text": "reACK cbadeff334f182086538d7483dc1146c559859e6",
   "id": "r:5543130614",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#pullrequestreview-5543130614",
   "hash": "6779e163a8328fe9",
   "edited": null
  },
  {
   "t": "2026-10-16T09:34:33Z",
   "kind": "review",
   "who": "enirox001",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "cbadeff334f182086538d7483dc1146c559859e6",
   "text": "reACK https://github.com/bitcoin/bitcoin/pull/36525/changes/cbadeff334f182086538d7483dc1146c559859e6",
   "id": "r:5545791068",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#pullrequestreview-5545791068",
   "hash": "9470c00a413eeaba",
   "edited": null
  },
  {
   "t": "2026-10-20T22:48:39Z",
   "kind": "review",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "cbadeff334f182086538d7483dc1146c559859e6",
   "text": "ACK cbadeff334\n\nnit:\n```diff\ndiff --git a/src/node/block_template_manager.cpp b/src/node/block_template_manager.cpp\nindex f4875ff4f6..310d9726b5 100644\n--- a/src/node/block_template_manager.cpp\n+++ b/src/node/block_template_manager.cpp\n@@ -146,9 +146,9 @@ std::unique_ptr<CBlockTemplate> BlockTemplateManager::WaitAndCreateNewBlock(\n             m_notifications.m_tip_block_cv.wait_until(lock, std::min(now + tick, deadline), [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {\n                 AssertLockHeld(m_notifications.m_tip_block_mutex);\n                 const auto tip_block{m_notifications.TipBlock()};\n-                // We assume tip_block is set, because this is an instance\n-                // method on BlockTemplate and no template could have been\n-                // generated before a tip exists.\n+                // We assume tip_block is set, because callers pass the parent\n+                // hash of an existing template and no template could have\n+                // been generated before a tip exists.\n                 tip_changed = Assume(tip_block) && tip_block != hash_prev_block;\n                 return tip_changed || m_chainman.m_interrupt || interrupt_wait;\n             });\ndiff --git a/src/node/block_template_manager.h b/src/node/block_template_manager.h\nindex 88bdef4fa4..880af33293 100644\n--- a/src/node/block_template_manager.h\n+++ b/src/node/block_template_manager.h\n@@ -100,6 +100,7 @@ public:\n      * (on testnet3, testnet4, and regtest).\n      *\n      * @param[in] hash_prev_block Parent block hash of the previous template.\n+     *                            The chain tip must already be set.\n      * @param[in] current_fees Total transaction fees in the previous template, in satoshis.\n      * @param[in] wait_options Timeout and minimum fee increase relative to current_fees.\n      * @param[in] create_options Options used to create the next block template.\n```",
   "id": "r:5592756558",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#pullrequestreview-5592756558",
   "hash": "092050a9f04c606f",
   "edited": "2026-10-20T22:48:53Z"
  },
  {
   "t": "2026-10-21T03:54:37Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "@w0xlt I prefer the existing wording:\n\n[quoted text omitted]\nThe function doesn't know where the caller got its hash.",
   "id": "c:6307632624",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6307632624",
   "hash": "a7bc08b801211ad0",
   "edited": null
  },
  {
   "t": "2026-10-21T09:59:08Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "Might it be preferable to make `m_block_template` non-mutating? Do users of the interface even need the mutated template after submitting?",
   "id": "c:6313228827",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6313228827",
   "hash": "340635fe48d4f5a7",
   "edited": null
  },
  {
   "t": "2026-10-21T10:25:51Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "AFAICT no, so we could do that and make the template const, that way no race and we copy when mutating for submission.",
   "id": "c:6313711072",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6313711072",
   "hash": "ccfc187075a49aac",
   "edited": null
  },
  {
   "t": "2026-10-21T16:47:27Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nI think it's a preferable solution. With no writes after construction there's no race, and since `submitSolution()` already copies the block, calling `AddMerkleRootAndCoinbase()` on that copy instead of on the template costs nothing.\n\nThe documentation and tests would need to be updated too, since the in-place mutation is documented and tested since #33745 (released in v31).\n\nThe sv2-tp client apparently relies on that mutation (`SaveBlockAsync()`), but it may be easy to update.",
   "id": "c:6321446193",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6321446193",
   "hash": "98b00c081590caa5",
   "edited": null
  },
  {
   "t": "2026-10-24T05:32:17Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nYes. Both existing clients call `getBlock()` after submit to store a copy of the block. This is potentially very useful when debugging a rejected block.\n\n[quoted text omitted]\nClients could download the non-mutated block and re-apply the solution themselves before storing to disk. That's slightly more complicated, so I prefer the existing approach.",
   "id": "c:6380417140",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6380417140",
   "hash": "72b181115b1ff24b",
   "edited": "2026-10-24T05:36:15Z"
  },
  {
   "t": "2026-10-25T06:16:29Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "Could we just return the block copy after applying the solution?",
   "id": "c:6394296261",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6394296261",
   "hash": "25ab3c5d7db4205c",
   "edited": null
  },
  {
   "t": "2026-10-25T10:03:53Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "@ismaelsadeeq in theory yes, but clients intentionally wait 1 second before requesting it. If the solution _is_ correct, we don't want to \"distract\" the node from relaying it and from producing the next template.",
   "id": "c:6396275706",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6396275706",
   "hash": "35e0c01507197f5f",
   "edited": null
  },
  {
   "t": "2026-10-25T10:22:19Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nI don't understand what you mean here. Doesn't the new valid header get processed synchronously anyway?",
   "id": "c:6396450693",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6396450693",
   "hash": "cd38ff8770ac4bfa",
   "edited": null
  },
  {
   "t": "2026-10-26T04:35:00Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "A call to `getBlock()` locks `cs_main` (briefly). Since `sv2-tp` typically runs on the same machine, when it saves to disk it also blocks the OS. Creating the new template blocks `cs_main`. And IIUC so does our p2p code when it announces the new block and handles compact block missing-transaction requests.\n\nThat said, I haven't done a very thorough analysis on whether the 1 second delay before `getBlock()` actually matters.",
   "id": "c:6408317995",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6408317995",
   "hash": "19691142f940b8a3",
   "edited": "2026-10-26T04:35:39Z"
  },
  {
   "t": "2026-10-28T10:05:26Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nThat should all be unrelated to copying a block. Or is the concern just that the OS might be something more important in the meantime and this might distract it? I don't think that is a valid concern.",
   "id": "c:6466124578",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6466124578",
   "hash": "596976713d009250",
   "edited": null
  },
  {
   "t": "2026-10-29T08:17:42Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "I wrote:\n\n[quoted text omitted]\nThis is not the case.\n\nI benchmarked the approach of making the template block immutable and returning the full block in `submitSolution()`. The good news is that there's no regression in compact block relay, not even when a transaction is missing.\n\nBut it does add a 6ms slowdown to when sv2-tp serves the next template. That's because IPC code still bottlenecks a few things on a single thread (even with multiple connections).\n\nI was able to reduce that by having sv2-tp skip block deserialization (including sha256 hashing), and write the serialized bytes straight to disk (after a 500ms delay). That still left 2.5ms.\n\nThings can be sped up further by:\n1. having another thread copy the result (1ms)\n2. taking advantage of Capnp shared memory support: https://capnproto.org/cxxrpc.html (1ms)\n3. more fundamentally making IPC (response) handling parallel\n\nThese require changes to libmultiprocess.\n\nSo my preference would be to defer making CBlockTemplate const until after these improvements land. This would be a breaking change for clients, but that's straight-forward to work around.",
   "id": "c:6491583234",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6491583234",
   "hash": "41b34d1837d9afbb",
   "edited": "2026-10-29T08:18:41Z"
  },
  {
   "t": "2026-10-29T09:19:02Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "There's another approach, though a bit of a layer violation: we can have `submitSolution()` defer its response so it comes _after_ `waitNext()`. But I also prefer doing that as a followup, rather than increase the scope of this PR.",
   "id": "c:6492816419",
   "url": "https://github.com/bitcoin/bitcoin/pull/36525#issuecomment-6492816419",
   "hash": "710bfc97c6b12822",
   "edited": "2026-10-29T09:46:02Z"
  }
 ],
 "labels_log": [
  {
   "t": "2026-10-14T08:43:44Z",
   "action": "labeled",
   "label": "Mining",
   "who": "DrahtBot"
  },
  {
   "t": "2026-10-14T09:24:32Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-10-14T10:55:41Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [],
 "text_chars": 12742,
 "text_tokens_estimate": 3185,
 "changed_paths": [
  "src/interfaces/mining.h",
  "src/ipc/capnp/mining.capnp",
  "src/node/block_template_manager.cpp",
  "src/node/block_template_manager.h",
  "src/node/interfaces.cpp",
  "src/node/miner.cpp",
  "src/node/miner.h",
  "src/test/miner_tests.cpp",
  "test/functional/interface_ipc_mining.py"
 ],
 "files": [
  {
   "path": "src/interfaces/mining.h",
   "add": 12,
   "del": 2
  },
  {
   "path": "src/ipc/capnp/mining.capnp",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/node/block_template_manager.cpp",
   "add": 4,
   "del": 15
  },
  {
   "path": "src/node/block_template_manager.h",
   "add": 16,
   "del": 3
  },
  {
   "path": "src/node/interfaces.cpp",
   "add": 33,
   "del": 13
  },
  {
   "path": "src/node/miner.cpp",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/node/miner.h",
   "add": 2,
   "del": 0
  },
  {
   "path": "src/test/miner_tests.cpp",
   "add": 4,
   "del": 0
  },
  {
   "path": "test/functional/interface_ipc_mining.py",
   "add": 57,
   "del": 0
  }
 ],
 "test_lines": 61,
 "git": {
  "head": "cbadeff334f182086538d7483dc1146c559859e6",
  "head_matches_backup": true,
  "base": "4b4cdde064caf821dcd48fb8a45114bb1a1effa3",
  "patch_id": "3908b78950a16a12",
  "commits": [
   {
    "sha": "f1385049b2",
    "subject": "mining: store total fees in CBlockTemplate",
    "files": 3,
    "add": 4,
    "del": 13
   },
   {
    "sha": "b2a3c22a93",
    "subject": "mining: add BlockTemplate::getTotalFees()",
    "files": 5,
    "add": 16,
    "del": 0
   },
   {
    "sha": "01867ed331",
    "subject": "mining: pass template hash and fees to WaitAndCreateNewBlock",
    "files": 3,
    "add": 21,
    "del": 7
   },
   {
    "sha": "4774cf1de6",
    "subject": "mining: protect block template access with a mutex",
    "files": 2,
    "add": 37,
    "del": 15
   },
   {
    "sha": "cbadeff334",
    "subject": "test: exercise concurrent IPC block template access",
    "files": 1,
    "add": 54,
    "del": 0
   }
  ],
  "patch_truncated": false
 },
 "patch_id": "3908b78950a16a12",
 "input_hash": "ca93390cdddfe359",
 "extracted_at": "2026-11-02T01:09:49+00:00"
}