{
 "number": 35502,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/35502",
 "title": "refactor: extract per-message helpers from ProcessMessage (move-only)",
 "author": "w0xlt",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-06-10T01:54:27Z",
 "updated_at": "2026-09-17T00:21:54Z",
 "age_days": 99,
 "draft": false,
 "labels": [
  "Refactoring"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "6a76b404464980445171bc35a50ee8fc028b06fe",
 "head_ref": "refactor/extract-processmessage-helpers",
 "head_repo": "w0xlt/bitcoin",
 "head_history": [
  {
   "t": "2026-09-16T23:07:02Z",
   "sha": "6a76b404464980445171bc35a50ee8fc028b06fe"
  }
 ],
 "additions": 1012,
 "deletions": 940,
 "changed_files": 1,
 "commit_count": 9,
 "size_bucket": "XL",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "stickies-v",
      "url": "https://github.com/bitcoin/bitcoin/pull/35502#issuecomment-4669325160"
     },
     {
      "login": "theStack",
      "url": "https://github.com/bitcoin/bitcoin/pull/35502#pullrequestreview-4468168675"
     }
    ],
    "stale_ack": [
     {
      "login": "pseudoramdom",
      "url": "https://github.com/bitcoin/bitcoin/pull/35502#issuecomment-4677153432"
     },
     {
      "login": "thomasbuilds",
      "url": "https://github.com/bitcoin/bitcoin/pull/35502#issuecomment-4677693977"
     },
     {
      "login": "pablomartin4btc",
      "url": "https://github.com/bitcoin/bitcoin/pull/35502#pullrequestreview-4482315193"
     }
    ]
   },
   "conflicts": [
    {
     "number": 35522,
     "title": "refactor: Extract per-message helpers from SendMessages() (move-only)",
     "author": "pablomartin4btc"
    },
    {
     "number": 35315,
     "title": "refactor: Use NodeClock::time_point in more places",
     "author": "maflcko"
    },
    {
     "number": 34824,
     "title": "net: encapsulate TxRelay state and replace recursive mutexes",
     "author": "w0xlt"
    },
    {
     "number": 34628,
     "title": "p2p: Replace per-peer transaction rate-limiting with global rate limits",
     "author": "ajtowns"
    },
    {
     "number": 34565,
     "title": "refactor: extract BlockDownloadManager from PeerManagerImpl",
     "author": "w0xlt"
    },
    {
     "number": 33854,
     "title": "fix assumevalid is ignored during reindex",
     "author": "Eunovo"
    }
   ]
  }
 },
 "acks_parsed": {
  "stickies-v": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-06-10T10:49:42Z",
   "stale": false
  },
  "theStack": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-06-10T13:19:33Z",
   "stale": false
  },
  "pablomartin4btc": {
   "kind": "ack",
   "hash": null,
   "t": "2026-06-12T04:01:10Z",
   "stale": true
  },
  "pseudoramdom": {
   "kind": "ack",
   "hash": "4fe745f27bd8a4a637df522421a4a7104923ca3e",
   "t": "2026-06-11T04:21:57Z",
   "stale": true
  },
  "thomasbuilds": {
   "kind": "ack",
   "hash": "4fe745f",
   "t": "2026-06-11T06:08:24Z",
   "stale": true
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 3,
  "concept_ack": 2,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "davidgumberg",
   "maflcko",
   "pablomartin4btc",
   "pseudoramdom",
   "stickies-v",
   "theStack",
   "thomasbuilds"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-09-16T23:07:47Z",
  "last_reviewer_activity": "2026-06-25T02:15:32Z",
  "last_reviewer": "davidgumberg",
  "author_silent_days": 0,
  "waiting_on_author_days": 0,
  "days_since_update": 0
 },
 "refs": {
  "mentioned": [
   34824
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 34824,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "net: encapsulate TxRelay state and replace recursive mutexes"
   }
  ],
  "conflicts": [
   35522,
   35315,
   34824,
   34628,
   34565,
   33854
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [],
 "body": "`PeerManagerImpl::ProcessMessage()` is a ~1000-line function handling every p2p message type inline, which makes it hard to navigate and review.\n\nThis PR continues splitting it into per-message helper functions, following the pattern of the recently merged fa5ab0220e02377c3c855042ecdf1f5f950d0965 (`ProcessPong()`) and fa55723b8fbd4fd056dddac5b35daf2e86021422 (`ProcessAddrs()`),, as suggested by maflcko https://github.com/bitcoin/bitcoin/pull/34588#issuecomment-4658892004.\n\n  Seven handlers are extracted, one commit each:\n\n  * `ProcessGetAddr()` \u2014 `getaddr`\n  * `ProcessGetDataMessage()` \u2014 `getdata` (named to avoid colliding with the existing `ProcessGetData()`, which services the request queue this handler fills)\n  * `ProcessGetBlocks()` \u2014 `getblocks`\n  * `ProcessGetHeaders()` \u2014 `getheaders`\n  * `ProcessInv()` \u2014 `inv`\n  * `ProcessSendTxRcncl()` \u2014 `sendtxrcncl`\n  * `ProcessTx()` \u2014 `tx`\n\nEach commit is a pure code move: the only new lines are the declaration (with thread-safety annotations), the function signature, and the one-line call site.\nEach call site is `Helper(...); return;`, so `return` statements inside the moved bodies keep identical semantics. No behavior change.\n\nEvery commit can be reviewed with the git options:\n`--color-moved=dimmed-zebra --color-moved-ws=ignore-all-space`\n\nDeclarations and definitions are placed next to related existing helpers (e.g. `ProcessGetDataMessage` next to `ProcessGetData`, `ProcessGetBlocks` next to `ProcessGetBlockData`, `ProcessGetAddr` after `ProcessAddrs`).",
 "commits": [
  {
   "sha": "ac0de44001ba0f77e9beb7180a563e6c8263f5d5",
   "date": "2026-09-16T22:43:37Z",
   "message": "move-only: Extract ProcessGetAddr() helper\n\nThis commit can be reviewed with the git options:\n--color-moved=dimmed-zebra --color-moved-ws=ignore-all-space"
  },
  {
   "sha": "0f21c930ae16fd1add5e3f5b6426494bfc4dea64",
   "date": "2026-09-16T22:43:37Z",
   "message": "move-only: Extract ProcessGetDataMessage() helper\n\nThis commit can be reviewed with the git options:\n--color-moved=dimmed-zebra --color-moved-ws=ignore-all-space"
  },
  {
   "sha": "3b9307dfcb4f6a67741551046a2a059795fcbd8f",
   "date": "2026-09-16T22:43:37Z",
   "message": "move-only: Extract ProcessGetBlocks() helper\n\nThis commit can be reviewed with the git options:\n--color-moved=dimmed-zebra --color-moved-ws=ignore-all-space"
  },
  {
   "sha": "c82869fe65255b9f93ed2cfebf1541a9f5549ee9",
   "date": "2026-09-16T22:43:37Z",
   "message": "move-only: Extract ProcessGetHeaders() helper\n\nThis commit can be reviewed with the git options:\n--color-moved=dimmed-zebra --color-moved-ws=ignore-all-space"
  },
  {
   "sha": "046148da2468b31032bdb14c13a802ef05192c93",
   "date": "2026-09-16T22:44:17Z",
   "message": "move-only: Extract ProcessInv() helper\n\nThis commit can be reviewed with the git options:\n--color-moved=dimmed-zebra --color-moved-ws=ignore-all-space"
  },
  {
   "sha": "5fcd87101b9717524a79cba0ace4f660632fad0f",
   "date": "2026-09-16T22:44:17Z",
   "message": "move-only: Extract ProcessSendTxRcncl() helper\n\nThis commit can be reviewed with the git options:\n--color-moved=dimmed-zebra --color-moved-ws=ignore-all-space"
  },
  {
   "sha": "8767660d1c6850b0a445f0189c2b1294129dc0dc",
   "date": "2026-09-16T22:46:37Z",
   "message": "move-only: Extract ProcessTx() helper\n\nThis commit can be reviewed with the git options:\n--color-moved=dimmed-zebra --color-moved-ws=ignore-all-space"
  },
  {
   "sha": "7da62dbcaa128381048c76703f3c95dfe8b951a9",
   "date": "2026-09-16T22:58:21Z",
   "message": "move-only: Extract ProcessCompactBlock() helper\n\nThis commit can be reviewed with the git options:\n--color-moved=dimmed-zebra --color-moved-ws=ignore-all-space\n\nCo-authored-by: pablomartin4btc <pablomartin4btc@gmail.com>"
  },
  {
   "sha": "6a76b404464980445171bc35a50ee8fc028b06fe",
   "date": "2026-09-16T22:59:43Z",
   "message": "move-only: Extract ProcessVersion() helper\n\nThis commit can be reviewed with the git options:\n--color-moved=dimmed-zebra --color-moved-ws=ignore-all-space\n\nCo-authored-by: pablomartin4btc <pablomartin4btc@gmail.com>"
  }
 ],
 "timeline": [
  {
   "t": "2026-06-10T10:49:42Z",
   "kind": "comment",
   "who": "stickies-v",
   "assoc": "MEMBER",
   "text": "Concept ACK. Pretty straightforward change that makes it easier to navigate net_processing and better encapsulates logic."
  },
  {
   "t": "2026-06-10T13:19:33Z",
   "kind": "review",
   "who": "theStack",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "4fe745f27bd8a4a637df522421a4a7104923ca3e",
   "text": "Concept ACK\n\nFwiw this has been proposed at least once: https://github.com/bitcoin/bitcoin/pull/9608 (it seems to have failed more due to lack of review back then rather than on strong pushback, as far as I understand)"
  },
  {
   "t": "2026-06-10T15:06:46Z",
   "kind": "review",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "4fe745f27bd8a4a637df522421a4a7104923ca3e",
   "text": "Concept ACK.\n\nI'm in favour of these refactoring, while reviewing #34824 also [identified](https://github.com/bitcoin/bitcoin/pull/34824#pullrequestreview-4445529843) another possible candidate `SendMessages()` which is in the same file."
  },
  {
   "t": "2026-06-11T04:21:57Z",
   "kind": "comment",
   "who": "pseudoramdom",
   "assoc": "MEMBER",
   "text": "code review ACK 4fe745f27bd8a4a637df522421a4a7104923ca3e\nExcept for a new comment for ProcessGetDataMessage, verified the change is a move-only refactor.\nAlso verified using `git show--color-moved=dimmed-zebra --color-moved-ws=allow-indentation-change <commit>` against each commit."
  },
  {
   "t": "2026-06-11T06:08:24Z",
   "kind": "comment",
   "who": "thomasbuilds",
   "assoc": "CONTRIBUTOR",
   "text": "code review ACK 4fe745f\n\nVerified each commit is a pure code move (only new lines are declarations, call sites, and one doc comment), call sites preserve return semantics, and the thread-safety annotations match what each body locks. It compiles cleanly with clang -Wthread-safety."
  },
  {
   "t": "2026-06-12T04:01:10Z",
   "kind": "review",
   "who": "pablomartin4btc",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "4fe745f27bd8a4a637df522421a4a7104923ca3e",
   "text": "ACK https://github.com/bitcoin/bitcoin/commit/4fe745f27bd8a4a637df522421a4a7104923ca3e.\n\n`ProcessMessage()` went from ~1500 to ~1000 lines, but `CMPCTBLOCK` (248 lines) and `VERSION` (235 lines) remain the two dominant inline blocks \u2014 extracting those two alone would drop it another ~480 lines, bringing it under 550. The same move-only pattern applied here would make both independently reviewable. Not a blocker, perhaps worth a follow-up PR in the same vein?\n\nOn a side note, on the previous [attempt](https://github.com/bitcoin/bitcoin/pull/9608) mentioned [above](https://github.com/bitcoin/bitcoin/pull/35502#pullrequestreview-4468168675), there were concerns about \"[obscuring the control flow](https://github.com/bitcoin/bitcoin/pull/9608#issuecomment-275336146)\", I don't think this is the case here."
  },
  {
   "t": "2026-06-19T00:44:12Z",
   "kind": "comment",
   "who": "davidgumberg",
   "assoc": "MEMBER",
   "text": "Concept -0\n\nLet's say I'm reading a function called `ProcessBlock()` in `net_processing.cpp`:\n\n- Is this function called by `ProcessMessage()`?\n- Does `ProcessMessage()` do any set up before invoking `ProcessBlock()`?\n- Are there any other callers of `ProcessBlock()` and what are their expectations?\n- Are all the callers of `ProcessBlock()` in the process messages thread?\n\nThese questions have to be answered when reading / modifying code and for all of the logic that lives in the `ProcessMessage()` ~switch statement these ambiguities don't exist. I think `ProcessBlock()` is a good example because it has multiple callers, and anyone reading it or modifying it should find them and think carefully about what they all do. That is a cost paid for by the reusability of `ProcessBlock()`.\n\nI think the main thing I don't share is the feeling that `ProcessMessage()` is too long. It's pretty flat, and there is ~0 state that lives outside of the `if(msg_type) {`, so I don't feel burdened by any of the rest of the function when I'm reading the logic for the message I'm interested in.\n\nNot trying to bikeshed, the PR seems reasonable and everything would be fine if it was merged, just the POV of one person that happens to like `ProcessMessage()`."
  },
  {
   "t": "2026-06-19T07:33:45Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "@davidgumberg Fair point. I agree the cost is real when a helper has multiple callers like `ProcessBlock()`, because setup and caller assumptions become less local.\n\nThese extractions are different though: they are private, single-caller helpers from `ProcessMessage()`, called as `Helper(...); return;`, so the control flow remains simple.\n\nThe benefit I see is lock reasoning. `ProcessMessage()` has to carry the broad lock contract needed by all branches:\n\n```cpp\nEXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_most_recent_block_mutex,\n                         !m_headers_presync_mutex, g_msgproc_mutex,\n                         !m_tx_download_mutex)\n ```\n\nAfter extraction, each handler states only what it actually needs. For example, `ProcessGetAddr()` requires only `g_msgproc_mutex`, while `ProcessTx()` requires `g_msgproc_mutex`, `!m_peer_mutex`, `!m_tx_download_mutex`.\n\nSo this does not add runtime safety by itself, but it makes the per-message lock contract more local and compiler-checked.\n\nBesides that, `ProcessMessage()` arguably accumulates disproportionate responsibility, and this extraction pattern addresses that, though I agree this is a more subjective point."
  },
  {
   "t": "2026-06-19T08:17:29Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nShould be easy to answer with 'yes', otherwise the function wouldn't sit in the peer manager impl?\n\n[quoted text omitted]\nEdit: I'd say generally the goal should be to avoid setup, but here it should be obvious from the function signature, which is passed `min_pow_checked` (and other stuff). There is also the possibility of different message types doing different setup steps, one would have to read the handshake code either way, before and after this pull request.\n\n[quoted text omitted]\nShould be easy to answer by looking at the header: If the function is public in the header, it is called externally. If it is not in the header, then it is not called externally. Generally, I don't think any `Process*` handler was ever exposed in the header?\n\n[quoted text omitted]\nShould be easy to answer with yes, because no process handler was ever exposed publicly. Also, it should be easy to verify with a single call to `git grep`.\n\n----\n\nGenerally, I think the benefits here are limited, because devs generally know that at most a single message is handled in the large body (and thus no variables leak from one message handling block to the next). However, I think there is still a benefit in being able to review code changes with the git option `git diff --function-context` easier.\n\nPreviously, it would basically print the whole file, even if only a single line in a single message type handling was changed. At least for me this makes review harder because I have to scroll past the irrelevant white lines every time and risk missing a green or red line.\n\nAfter this change, `git diff --function-context` nicely prints only the relevant context, so at least for me review would be easier. Also, parts of this file already use this pattern, so for consistency it also makes sense. So I am Concept +1, but this is just me, and maybe other people are using a different review flow?\n\n-----\n\n@w0xlt Please don't `@` in pull descriptions. If this pull was merged, it would lead to ping spam every time the merge is cherry-picked."
  },
  {
   "t": "2026-06-25T02:15:32Z",
   "kind": "comment",
   "who": "davidgumberg",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nI meant directly, this is basically the same point as the set-up one.\n\n[quoted text omitted]\nRight, but after this PR you **have** to look in multiple places, even if there is only one caller that doesn't do any setup and it's `ProcessMessage()`\n\n[quoted text omitted]\nYes, all of these questions can be answered quite easily by checking in one or two files and doing a grep or two, my point is that one doesn't have to just check the header and do an extra grep or two now.\n\ne.g. I don't use `git diff --function-context` but it sounds like reviewing `ProcessMessage()` might be pretty annoying if one does that. Of course I *could* recommend that you use a different set of flags or a different tool, or just open another window with the function in it, but any of these \"solutions\" would add friction for you i.e. they suck in comparison to using the tool / workflow you already like and are used to.\n\nThat being said, it sounds like other contributors prefer the style of this PR to the style of master, and given that this is a stylistic question the thing that people prefer is definitely the thing that should happen."
  },
  {
   "t": "2026-09-16T23:07:02Z",
   "kind": "force_push",
   "who": "w0xlt",
   "commit": "6a76b404464980445171bc35a50ee8fc028b06fe"
  },
  {
   "t": "2026-09-16T23:07:47Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "Rebased.\nAll suggestions addressed."
  }
 ],
 "labels_log": [
  {
   "t": "2026-06-10T01:54:31Z",
   "action": "labeled",
   "label": "Refactoring",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-25T12:12:22Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-17T00:21:52Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  }
 ],
 "state_log": [],
 "text_chars": 10749,
 "text_tokens_estimate": 2687,
 "changed_paths": [
  "src/net_processing.cpp"
 ],
 "files": [
  {
   "path": "src/net_processing.cpp",
   "add": 1012,
   "del": 940
  }
 ],
 "test_lines": 0,
 "git": {
  "head": "6a76b404464980445171bc35a50ee8fc028b06fe",
  "head_matches_backup": true,
  "base": "b8215855437288cac95c7fd5d41104c73c0f3495",
  "commits": [
   {
    "sha": "ac0de44001",
    "subject": "move-only: Extract ProcessGetAddr() helper",
    "files": 1,
    "add": 40,
    "del": 32
   },
   {
    "sha": "0f21c930ae",
    "subject": "move-only: Extract ProcessGetDataMessage() helper",
    "files": 1,
    "add": 56,
    "del": 47
   },
   {
    "sha": "3b9307dfcb",
    "subject": "move-only: Extract ProcessGetBlocks() helper",
    "files": 1,
    "add": 70,
    "del": 62
   },
   {
    "sha": "c82869fe65",
    "subject": "move-only: Extract ProcessGetHeaders() helper",
    "files": 1,
    "add": 84,
    "del": 75
   },
   {
    "sha": "046148da24",
    "subject": "move-only: Extract ProcessInv() helper",
    "files": 1,
    "add": 99,
    "del": 92
   },
   {
    "sha": "5fcd87101b",
    "subject": "move-only: Extract ProcessSendTxRcncl() helper",
    "files": 1,
    "add": 57,
    "del": 49
   },
   {
    "sha": "8767660d1c",
    "subject": "move-only: Extract ProcessTx() helper",
    "files": 1,
    "add": 84,
    "del": 77
   },
   {
    "sha": "7da62dbcaa",
    "subject": "move-only: Extract ProcessCompactBlock() helper",
    "files": 1,
    "add": 268,
    "del": 260
   },
   {
    "sha": "6a76b40446",
    "subject": "move-only: Extract ProcessVersion() helper",
    "files": 1,
    "add": 241,
    "del": 233
   }
  ],
  "patch_truncated": true
 },
 "input_hash": "f9b6e7af8960f38a",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}