{
 "number": 34457,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/34457",
 "title": "wallet: add private broadcast support for wallet transactions",
 "author": "w0xlt",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-01-30T08:06:34Z",
 "updated_at": "2026-07-26T01:19:18Z",
 "age_days": 230,
 "draft": true,
 "labels": [
  "Wallet",
  "Needs rebase",
  "Private Broadcast"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "eb2493bbecf88d26d476dd5b0f377aa7314ef187",
 "head_ref": "wprv_29012",
 "head_repo": "w0xlt/bitcoin",
 "head_history": [
  {
   "t": "2026-01-31T03:20:44Z",
   "sha": "d9debb3348ad7d7950ff466167e4d471c392fa78"
  },
  {
   "t": "2026-02-07T09:49:45Z",
   "sha": "e22c7744253da44d16ea876ea949e1113cd54706"
  },
  {
   "t": "2026-02-07T09:59:56Z",
   "sha": "da2282182dc57ced5742badecff9380c1de09109"
  },
  {
   "t": "2026-02-07T18:27:20Z",
   "sha": "331996f8589ba4cd00800d8d3d5502b8f47389a2"
  },
  {
   "t": "2026-02-07T18:42:42Z",
   "sha": "dcc3070a24ec39f3b5746c2b6f18dc8396d9aaa2"
  },
  {
   "t": "2026-02-07T18:47:41Z",
   "sha": "caad89cb6c960384f1f45095910f8e6efc9131bf"
  },
  {
   "t": "2026-02-07T18:54:18Z",
   "sha": "3645cccf8ade879381bd3c9f2f5196e66510006e"
  },
  {
   "t": "2026-02-23T06:42:55Z",
   "sha": "eb2493bbecf88d26d476dd5b0f377aa7314ef187"
  }
 ],
 "additions": 510,
 "deletions": 45,
 "changed_files": 11,
 "commit_count": 5,
 "size_bucket": "L",
 "mergeable_state": "dirty",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "andrewtoth",
      "url": "https://github.com/bitcoin/bitcoin/pull/34457#issuecomment-3825847879"
     },
     {
      "login": "Bicaru20",
      "url": "https://github.com/bitcoin/bitcoin/pull/34457#issuecomment-3886940981"
     }
    ]
   },
   "conflicts": [
    {
     "number": 34684,
     "title": "refactor: Enable -Wswitch in exhaustive switch'es, Enable -Wcovered-switch-default",
     "author": "maflcko"
    },
    {
     "number": 34533,
     "title": "wallet: resubmit transactions with private broadcast if enabled",
     "author": "vasild"
    },
    {
     "number": 34410,
     "title": "test: let connections happen in any order in p2p_private_broadcast.py",
     "author": "vasild"
    },
    {
     "number": 34038,
     "title": "logging: replace -loglevel with -trace, various API improvements",
     "author": "ajtowns"
    },
    {
     "number": 33954,
     "title": "test: add functional test for outbound connection management",
     "author": "mzumsande"
    },
    {
     "number": 30343,
     "title": "wallet, logging: Replace WalletLogPrintf() with LogInfo()",
     "author": "ryanofsky"
    },
    {
     "number": 29278,
     "title": "Wallet:  Add `maxfeerate` wallet startup option",
     "author": "ismaelsadeeq"
    }
   ]
  }
 },
 "acks_parsed": {
  "andrewtoth": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-01-30T21:29:38Z",
   "stale": false
  },
  "Bicaru20": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-02-11T20:22:45Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 2,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "Bicaru20",
   "andrewtoth",
   "mzumsande",
   "rkrux"
  ]
 },
 "signals": {
  "needs_rebase": true,
  "ci_failed": false,
  "mergeable_state": "dirty",
  "last_author_activity": "2026-02-25T05:31:14Z",
  "last_reviewer_activity": "2026-03-13T13:23:17Z",
  "last_reviewer": "rkrux",
  "author_silent_days": 204,
  "waiting_on_author_days": 188,
  "days_since_update": 53
 },
 "refs": {
  "mentioned": [
   29415,
   34322,
   34387,
   34410,
   34533
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 29415,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-01-12",
    "title": "Broadcast own transactions only via short-lived Tor or I2P connections"
   },
   {
    "number": 34322,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "node: Persist private broadcast transactions over node restarts"
   },
   {
    "number": 34387,
    "type": "issue",
    "state": "closed",
    "merged": false,
    "merged_at": null,
    "title": "qa: failure in p2p_private_broadcast.py"
   },
   {
    "number": 34410,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-03-02",
    "title": "test: let connections happen in any order in p2p_private_broadcast.py"
   },
   {
    "number": 34533,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "wallet: resubmit transactions with private broadcast if enabled"
   }
  ],
  "conflicts": [
   34684,
   34533,
   34410,
   34038,
   33954,
   30343,
   29278
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/wallet/wallet.cpp",
  "test/functional/wallet_private_broadcast.py"
 ],
 "body": "## Summary\n\nExtends the private broadcast feature (#29415) to wallet transactions. When `-privatebroadcast=1` is enabled, wallet RPCs (`sendtoaddress`, `send`, `sendall`, `sendmany`) now broadcast transactions through short-lived Tor/I2P connections instead of announcing to all connected peers.\n\nEDIT: The per-transaction private_broadcast flag in mapValue of the original proposal has been removed (rationale: https://github.com/bitcoin/bitcoin/pull/34533#pullrequestreview-3829186494).\n\n  Previous approach\n\n## Key Changes\n\n**Centralized availability check** (commit 1)\n- Moves the Tor/I2P reachability check from `sendrawtransaction` RPC to `BroadcastTransaction()`\n- All broadcast callers now get consistent error handling\n\n**Wallet integration** (commit 2)\n- `CommitTransaction()`: Uses private broadcast when enabled, fails loudly if Tor/I2P unavailable\n- `ResubmitWalletTransactions()`: Rebroadcasts using the original broadcast method\n- Persists a `private_broadcast` flag in the wallet transaction's `mapValue`\n\n**Functional tests** (commits 3-4)\n- Tests wallet send via SOCKS5 proxy\n- Tests flag persistence across restarts\n- Tests error handling when Tor/I2P unavailable\n\n## Behavior Matrix\n\n| Scenario | TX Flag | Node Setting | Expected Behavior | Tested |\n|----------|---------------|--------------|-------------------|--------|\n| New tx | Private | - | Via SOCKS5, flag set | Test 1 \u2713 |\n| Resubmit | Private | Public | Skip | Test 2 \u2713 |\n| Resubmit | Public | Private | Skip | Test 3 \u2713 |\n| Resubmit | Private | Private | Rebroadcast | Test 3 \u2713 |\n| New tx | Private | - (no Tor) | RPC error | Test 4 \u2713 |\n| Resubmit | Private | Private (no Tor) | Log error | Test 4 \u2713 |\n\n**Why skip on mode mismatch?** Rebroadcasting via a different method than originally used may allow correlation of transaction to origin, or may indicate origin has `-privatebroadcast` enabled.",
 "commits": [
  {
   "sha": "5f5b60544d65b1603e82f9b92d4e7c54f60b0fee",
   "date": "2026-02-21T06:42:14Z",
   "message": "node: move private broadcast availability check to BroadcastTransaction()\n\nMove the Tor/I2P network reachability check from the sendrawtransaction RPC\nto BroadcastTransaction(). This centralizes the check so that all callers\n(including wallet RPCs) get consistent behavior when using private broadcast.\n\nChanges:\n- Add TransactionError::PRIVATE_BROADCAST_UNAVAILABLE enum value\n- Add corresponding error message in common/messages.cpp\n- Add RPC error code mapping in rpc/util.cpp\n- Move the check from rpc/mempool.cpp to node/transaction.cpp\n- Set err_string when the error occurs for proper error propagation"
  },
  {
   "sha": "3b3f319a4202d821037618552d461a4bcbd26acc",
   "date": "2026-02-22T06:53:04Z",
   "message": "qt: handle CommitTransaction exceptions in send dialog\n\nCommitTransaction() can throw std::runtime_error (e.g., on wallet DB\nwrite failure), but sendButtonClicked() did not catch it, which would\ncrash the GUI. Add a try-catch around sendCoins() to display the error\nin a message box instead."
  },
  {
   "sha": "ad4d63918626af5740340890f81b99deccb9db0c",
   "date": "2026-02-22T06:53:53Z",
   "message": "wallet: integrate private broadcast for wallet transactions\n\nEnable private broadcast for wallet transaction submission when\n-privatebroadcast is set.\n\nChanges to CommitTransaction():\n- Fail early before AddToWallet() when -privatebroadcast is enabled but\n  Tor/I2P is unreachable, so no wallet state is modified on permanent failure\n- Use NO_MEMPOOL_PRIVATE_BROADCAST method when -privatebroadcast enabled\n\nChanges to MaybeResendWalletTxs():\n- Use private broadcast for resubmission when -privatebroadcast enabled,\n  so periodic rebroadcasts also go through Tor/I2P"
  },
  {
   "sha": "dc2c27f69143d10ff4ce995fa112977822654b5a",
   "date": "2026-02-22T06:53:53Z",
   "message": "test: add Socks5ProxyHelper and add_addresses_to_addrman helpers\n\nAdd helper utilities to test_framework/socks5.py to reduce code duplication\nin private broadcast tests:\n\n- Socks5ProxyHelper: Simplifies SOCKS5 proxy setup with ephemeral ports,\n  thread-safe destination tracking, and simple redirect factory\n- add_addresses_to_addrman(): Helper to populate a node's addrman with\n  test addresses\n- FAKE_ONION_ADDRESSES: Shared list of fake .onion addresses for tests\n\nUpdate p2p_private_broadcast.py to use ephemeral ports and the new\nadd_addresses_to_addrman helper."
  },
  {
   "sha": "eb2493bbecf88d26d476dd5b0f377aa7314ef187",
   "date": "2026-02-22T06:53:53Z",
   "message": "test: add wallet_private_broadcast.py functional test\n\nAdd a functional test for wallet-specific private broadcast behavior:\n\n- Test 1: sendtoaddress with -privatebroadcast sends via SOCKS5 proxy\n- Test 2: Error when Tor/I2P not reachable\n- Test 3: Rebroadcast with -privatebroadcast uses private broadcast method\n- Test 4: Rebroadcast without -privatebroadcast uses normal method\n\nUses Socks5ProxyHelper for simplified proxy setup and connection tracking."
  }
 ],
 "timeline": [
  {
   "t": "2026-01-30T21:29:38Z",
   "kind": "comment",
   "who": "andrewtoth",
   "assoc": "CONTRIBUTOR",
   "text": "Concept ACK\n\nI don't think we need to skip rebroadcast if we initially created the transaction with private broadcast disabled. This is only important the other way - if we initially broadcast via private broadcast, but then restart with private broadcast disabled.\n\n[quoted text omitted]\nIdeally every node has this enabled in the future. But also, one cannot conclude this if they receive a tx both from a persistently connected node and then later via a private broadcast. This would actually make the original broadcast more private, since this would indicate that perhaps the originator is not actually the originator.\n\nSending decoys will also help here."
  },
  {
   "t": "2026-01-31T03:20:44Z",
   "kind": "force_push",
   "who": "w0xlt",
   "commit": "d9debb3348ad7d7950ff466167e4d471c392fa78"
  },
  {
   "t": "2026-01-31T03:26:39Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "@andrewtoth  Thanks for the insight about plausible deniability.\n\nUpdated to only skip private\u2192public (the other direction is now allowed):\n\n| Scenario | TX Flag | Node Setting | Behavior |\n|----------|---------|--------------|----------|\n| Resubmit | Private | Public | Skip |\n| Resubmit | Public | Private | Rebroadcast privately |\n| Resubmit | Private | Private | Rebroadcast privately |\n\n- **Private\u2192Public: Skip** - Protects origin from being correlated\n- **Public\u2192Private: Allow** - Provides plausible deniability"
  },
  {
   "t": "2026-02-02T16:01:55Z",
   "kind": "review",
   "who": "mzumsande",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "d9debb3348ad7d7950ff466167e4d471c392fa78",
   "text": "Some conceptual thoughts:\n- The rebroadcasting behavior will probably lead to frequent 3 minute timeouts. If the tx didn't get mined within ~24 hours due to insufficient fees, but remains in our and our peer's mempools during this time, the peers wont's send us a GETDATA to our repeated submission. However, I think that this would not be a huge problem because after 3 attempts, we'd abort during the retry because we check there if the tx is already in the mempool.\n- while this improves the rebroadcast behavior, there is still the issue that transactions relevant for the wallet but submitted via RPC (`sendrawtransaction`) will be received back from the network, added to `mapWallet`, and then rebroadcast over clearnet if the user restarted without `-privatebroadcast`. In master, these txns would be rebroadcast normally over clearnet even without a restart.\n- should something be done about feebumping behavior? I think a user sending via private broadcast, restarting without the flag and then bumping a tx would lead to the tx being sent via clearnet."
  },
  {
   "t": "2026-02-07T09:49:45Z",
   "kind": "force_push",
   "who": "w0xlt",
   "commit": "e22c7744253da44d16ea876ea949e1113cd54706"
  },
  {
   "t": "2026-02-07T09:59:56Z",
   "kind": "force_push",
   "who": "w0xlt",
   "commit": "da2282182dc57ced5742badecff9380c1de09109"
  },
  {
   "t": "2026-02-07T10:13:18Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nAgreed.\n\n[quoted text omitted]\nAs far as I can see there is no simple solution to this scenario. Even if we have a persistent database of privately broadcast transactions (like proposed in #34322) and the wallet could query it when a relevant transaction is received, the user can submit the transaction from a different node than the wallet's node. One approach would be for the wallet to mark all `IsFromMe()` transactions not originated through `CommitTransaction()` as private. Not sure if that is an acceptable solution but it would cover this case unless I am missing something.\n\n[quoted text omitted]\nAdded a solution and a functional test for this case. Thanks for raising it."
  },
  {
   "t": "2026-02-07T10:15:01Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "CI error is unrelated"
  },
  {
   "t": "2026-02-07T18:27:20Z",
   "kind": "force_push",
   "who": "w0xlt",
   "commit": "331996f8589ba4cd00800d8d3d5502b8f47389a2"
  },
  {
   "t": "2026-02-07T18:42:42Z",
   "kind": "force_push",
   "who": "w0xlt",
   "commit": "dcc3070a24ec39f3b5746c2b6f18dc8396d9aaa2"
  },
  {
   "t": "2026-02-07T18:47:41Z",
   "kind": "force_push",
   "who": "w0xlt",
   "commit": "caad89cb6c960384f1f45095910f8e6efc9131bf"
  },
  {
   "t": "2026-02-07T18:54:18Z",
   "kind": "force_push",
   "who": "w0xlt",
   "commit": "3645cccf8ade879381bd3c9f2f5196e66510006e"
  },
  {
   "t": "2026-02-11T20:08:59Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "NONE",
   "path": "test/functional/wallet_private_broadcast.py",
   "commit": "3645cccf8ade879381bd3c9f2f5196e66510006e",
   "in_reply_to": null,
   "text": "```suggestion\n        time.wait(0.1)\n        self.restart_node(0, extra_args=[\n            \"-privatebroadcast=1\",\n            \"-v2transport=0\",\n            \"-listenonion\",\n            \"-torcontrol=127.0.0.1:1\",\n        ])\n```\nI had this error:\n```\n    TestFramework.socks5 (ERROR): socks5 request handling failed.\n    Traceback (most recent call last):\n    File \"/home/bitcoin/test/functional/test_framework/socks5.py\", line 185, in handle\n    forward_sockets(self.conn, conn_to)\n    ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^\n    File \"/home/bitcoin/test/functional/test_framework/socks5.py\", line 68, in forward_sockets\n    data = s.recv(4096)\n  ConnectionResetError: [Errno 104] Connection reset by peer\n```\nAfter investigating, by putting the time.sleep(0.1) I was able to solve it. I believe the error is because the proxy does not have enough time to create the connexion.\n\nPS: I am not sure if putting a `time.sleep` is the best approach."
  },
  {
   "t": "2026-02-11T20:12:52Z",
   "kind": "review_comment",
   "who": "Bicaru20",
   "assoc": "NONE",
   "path": "test/functional/wallet_private_broadcast.py",
   "commit": "eb2493bbecf88d26d476dd5b0f377aa7314ef187",
   "in_reply_to": null,
   "text": "I had the same error after the log message `Stopping nodes`. I've solved it doing the same.\n```suggestion\n        self.log.info(\"All wallet private broadcast tests passed!\")\n        time.sleep(0.1)\n```"
  },
  {
   "t": "2026-02-11T20:22:45Z",
   "kind": "comment",
   "who": "Bicaru20",
   "assoc": "NONE",
   "text": "Concept ACK\n\n[quoted text omitted]\nI was worried about using this approach for one case:\n* If a user imports a wallet into another node (let's call it _new node_) from which the transaction was originated (_original node_), the new wallet would not know if the transaction was originated through `CommitTransaction()` or not.\n\nBuut I modified the `wallet_private_broadcast.py` to test if that happened, but when you import the wallet to another node (the _new node_ ), the information about whether if an unconfirmed transaction it is using a private broadcast or not is kept if the transaction was sent  (from the _original node_) before importing the wallet to the _new node_ .\n\nThe only case where it might cause trouble is if a user already has the same wallet in both the _original node_ and _new node_ and then sends a private transaction from one of them. Then the other one will not know whether the transaction is private or not. It is a weird case, and I am not even sure it would behave like this (I have not tested it).\n\nI also left some comments in `wallet_private_breadcast.py` about an error I encountered."
  },
  {
   "t": "2026-02-11T20:35:24Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "@Bicaru20 Thanks for the review.\n\nRegarding the wallet import, I believe there shouldn\u2019t be any issues since the private flag persists.\nAs for the test error, it appears to be the same as in #34387. A solution was provided in #34410.\nI\u2019ll update the PR with the same solution shortly."
  },
  {
   "t": "2026-02-11T20:48:21Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\n@Bicaru20 Yes, in this case, the wallet that didn\u2019t originate the transaction has no way of knowing how it was broadcast after receiving it through the mempool. That\u2019s why I suggested that such a wallet, for example, should treat any transaction received via the mempool with owned inputs as private.\n\nRegarding the import, I assumed you were backing up the wallet file and restoring it. If you restore the wallet using `importdescriptors`, there\u2019s no way to determine how the unconfirmed transactions were originally broadcast."
  },
  {
   "t": "2026-02-11T22:03:47Z",
   "kind": "comment",
   "who": "Bicaru20",
   "assoc": "NONE",
   "text": "[quoted text omitted]\n\nOkay. I don't know why I understood another thing, my bad. Thanks for the clarification. I agree with your approach, then. It\u2019s better to be much too private than too little.\n\n[quoted text omitted]\nYes, I just did a quick test to see the behavior in this case."
  },
  {
   "t": "2026-02-23T06:42:55Z",
   "kind": "force_push",
   "who": "w0xlt",
   "commit": "eb2493bbecf88d26d476dd5b0f377aa7314ef187"
  },
  {
   "t": "2026-02-23T06:43:52Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "Force-pushed with a simplified approach (rationale: https://github.com/bitcoin/bitcoin/pull/34533#pullrequestreview-3829186494). The per-transaction `private_broadcast` flag in `mapValue` has been removed \u2014 the broadcast method is now purely a function of the current `-privatebroadcast` setting.\n\nThis simplification aligns with https://github.com/bitcoin/bitcoin/pull/34533#issuecomment-3896028216 approach: \"_Hmm, as a user, this is what I would expect - if restarted with -privatebroadcast=0, then the wallet rebroadcasts should be done using the send-to-all method (\"via the mempool\"). I would expect that because I have switched off private broadcast._\"\n\nChanges against master (5 commits):\n\n1. Centralize Tor/I2P reachability check \u2014 moved from `sendrawtransaction` RPC into `BroadcastTransaction()` so all callers (including wallet) get consistent behavior. Added `TransactionError::PRIVATE_BROADCAST_UNAVAILABLE`.\n2. Qt fix \u2014 `sendButtonClicked()` didn't catch std::runtime_error from `CommitTransaction()`, which would crash the GUI. Pre-existing bug, but now more reachable.\n3. Wallet integration \u2014 `CommitTransaction()` uses `NO_MEMPOOL_PRIVATE_BROADCAST` when `-privatebroadcast` is set, with an early-fail before `AddToWallet()` when Tor/I2P is unreachable. `MaybeResendWalletTxs()` uses private broadcast for periodic resubmission when the flag is set.\n4. Test infrastructure + functional test \u2014 `Socks5ProxyHelper`, `add_addresses_to_addrman`, `FAKE_ONION_ADDRESSES` helpers, and `wallet_private_broadcast.py` testing: send via `SOCKS5` proxy, error on Tor/I2P unavailable, rebroadcast with/without `-privatebroadcast`."
  },
  {
   "t": "2026-02-25T00:48:55Z",
   "kind": "comment",
   "who": "andrewtoth",
   "assoc": "CONTRIBUTOR",
   "text": "Why was this marked draft?"
  },
  {
   "t": "2026-02-25T02:19:43Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "I moved it to draft due to the discussion about the transaction private flag. It\u2019s now marked as ready."
  },
  {
   "t": "2026-02-25T03:45:47Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/wallet.cpp",
   "commit": "ad4d63918626af5740340890f81b99deccb9db0c",
   "in_reply_to": null,
   "text": "Hmm so this is done in #34533, except the method is hoisted above the loop."
  },
  {
   "t": "2026-02-25T05:31:14Z",
   "kind": "review_comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/wallet.cpp",
   "commit": "ad4d63918626af5740340890f81b99deccb9db0c",
   "in_reply_to": 2850655316,
   "text": "Yes, the two PRs overlap in the `MaybeResendWalletTxs` method.\nAfter #34533 is merged, I\u2019ll rebase on top of it."
  },
  {
   "t": "2026-03-13T13:23:17Z",
   "kind": "comment",
   "who": "rkrux",
   "assoc": "CONTRIBUTOR",
   "text": "Based on an initial glance:\n1. 3b3f319a4202d821037618552d461a4bcbd26acc \"qt: handle CommitTransaction exceptions in send dialog\" can be a separate PR, it's pretty self contained.\n2. dc2c27f69143d10ff4ce995fa112977822654b5a \"test: add Socks5ProxyHelper and add_addresses_to_addrman helpers\" can be a separate PR as well. Would it be possible to use `Socks5ProxyHelper` class in `p2p_private_broadcast` test? The newly added `start_with_custom_factory` method in the class is unused at the moment."
  }
 ],
 "labels_log": [
  {
   "t": "2026-01-30T08:06:37Z",
   "action": "labeled",
   "label": "Wallet",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-30T08:48:49Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-31T05:41:11Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-07T10:00:37Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-07T20:11:38Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-23T07:55:57Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-03-02T16:48:42Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-03-02T17:19:10Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-04-21T13:55:43Z",
   "action": "labeled",
   "label": "Private Broadcast",
   "who": "fanquake"
  }
 ],
 "state_log": [
  {
   "t": "2026-02-16T22:34:30Z",
   "kind": "convert_to_draft",
   "who": "w0xlt"
  },
  {
   "t": "2026-02-25T02:18:16Z",
   "kind": "ready_for_review",
   "who": "w0xlt"
  },
  {
   "t": "2026-04-28T21:59:31Z",
   "kind": "convert_to_draft",
   "who": "w0xlt"
  }
 ],
 "text_chars": 13250,
 "text_tokens_estimate": 3312,
 "changed_paths": [],
 "files": [],
 "test_lines": null,
 "git": null,
 "input_hash": "b4744790f888df5a",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}