{
 "number": 36068,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/36068",
 "title": "fuzz: reuse one fuzzed wallet across inputs",
 "author": "brunoerg",
 "author_association": "MEMBER",
 "created_at": "2026-08-24T17:19:49Z",
 "updated_at": "2026-09-07T14:24:33Z",
 "age_days": 23,
 "draft": false,
 "labels": [
  "Fuzzing"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "029f6ec92ca397f88b602e022cf5ad191c340b38",
 "head_ref": "2026-08-fuzz-fuzzedwallet-impv",
 "head_repo": "brunoerg/bitcoin",
 "head_history": [
  {
   "t": "2026-09-07T14:22:20Z",
   "sha": "029f6ec92ca397f88b602e022cf5ad191c340b38"
  }
 ],
 "additions": 146,
 "deletions": 17,
 "changed_files": 4,
 "commit_count": 2,
 "size_bucket": "M",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "approach_ack": [
     {
      "login": "jeanpablojp",
      "url": "https://github.com/bitcoin/bitcoin/pull/36068#pullrequestreview-5059090941"
     }
    ]
   },
   "conflicts": [
    {
     "number": 35511,
     "title": "RFC: consensus: Make `CAmount` a class",
     "author": "hodlinator"
    }
   ]
  }
 },
 "acks_parsed": {
  "jeanpablojp": {
   "kind": "approach_ack",
   "hash": null,
   "t": "2026-08-29T20:14:54Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 0,
  "approach_ack": 1,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "jeanpablojp"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-09-07T14:24:33Z",
  "last_reviewer_activity": "2026-08-29T20:14:54Z",
  "last_reviewer": "jeanpablojp",
  "author_silent_days": 10,
  "waiting_on_author_days": 0,
  "days_since_update": 10
 },
 "refs": {
  "mentioned": [],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [],
  "conflicts": [
   35511
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/test/fuzz/util/wallet.h",
  "src/wallet/wallet.cpp"
 ],
 "body": "FuzzedWallet was rebuilt for every input. That imports eight\ndescriptors, and each address it then hands out costs a BIP32\nderivation plus three separate WalletBatch transactions (see\nDescriptorScriptPubKeyMan::GetNewDestination) - all paid before the\ntarget reaches the code under test.\n\nThis PR changes it to build the wallet once in the target's .init hook and\nreset it perinput instead. I benchmarked on my Ubuntu machine without\nsanitizers and got a ~6x speedup.",
 "commits": [
  {
   "sha": "3fc5f3d483410a2a6974ed6287bdc3584bc05952",
   "date": "2026-09-07T14:22:08Z",
   "message": "wallet: add test-only helper to clear in-memory transaction state\n\nAdd CWallet::ClearInMemoryTxStateForTest(), which drops mapWallet,\nwtxOrdered, mapTxSpends, m_txos and m_locked_coins, leaving the\ndatabase, the wallet flags and the ScriptPubKeyMans untouched.\n\nThis exists so that fuzz harnesses can reuse a single wallet across\ninputs instead of rebuilding it for every one. m_txos and mapTxSpends\nare private, so a harness cannot reset them on its own.\n\nRemoveTxs() is not usable here: it erases each transaction's\nm_it_wtxOrdered from wtxOrdered, and that iterator is only valid for\ntransactions added through AddToWallet(). Harnesses that populate\nmapWallet directly leave it uninitialized."
  },
  {
   "sha": "029f6ec92ca397f88b602e022cf5ad191c340b38",
   "date": "2026-09-07T14:22:08Z",
   "message": "fuzz: reuse one fuzzed wallet across inputs\n\nFuzzedWallet was rebuilt for every input. That imports eight\ndescriptors, and each address it then hands out costs a BIP32\nderivation plus three separate WalletBatch transactions (see\nDescriptorScriptPubKeyMan::GetNewDestination) - all paid before the\ntarget reaches the code under test.\n\nBuild the wallet once in the target's .init hook and reset it per\ninput instead."
  }
 ],
 "timeline": [
  {
   "t": "2026-08-24T17:21:33Z",
   "kind": "comment",
   "who": "brunoerg",
   "assoc": "MEMBER",
   "text": "cc @maflcko"
  },
  {
   "t": "2026-08-29T20:14:54Z",
   "kind": "review",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "907eacd6fd53769168b6ab6d896767bb4a8e588a",
   "text": "Approach ACK\n\nRan the qa-assets corpus on master and on this head, the speedup reproduces, around 4x here.\n\nOne thing I didn't expect. With the fixed pool the biggest `OutputGroup` in this target goes from 1 to 19, so `GroupOutputs` finally has something to group. I didn't see the pool mentioned in the PR description or the commits, might be worth a line?\n\nLeft two inline comments."
  },
  {
   "t": "2026-08-29T20:14:54Z",
   "kind": "review_comment",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/util/wallet.h",
   "commit": "029f6ec92ca397f88b602e022cf5ad191c340b38",
   "in_reply_to": null,
   "text": "The descriptor's `next_index` isn't reset here, so the same input builds a different transaction depending on how many ran before it. I don't think it hurts in practice since only the change output and its signature move, but the comment on the drift assert assumes an iteration advances one index and the APS retry can keep two. Is this intended?"
  },
  {
   "t": "2026-08-29T20:14:54Z",
   "kind": "review_comment",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/wallet.cpp",
   "commit": "029f6ec92ca397f88b602e022cf5ad191c340b38",
   "in_reply_to": null,
   "text": "Nit, `mapWallet.clear()` runs before `wtxOrdered` and `m_txos`, and both point into it. Nothing dereferences in between so it's latent, but `RemoveTxs` does it the other way round and leaves `mapWallet` for last. Any reason for the order?"
  },
  {
   "t": "2026-09-07T14:22:20Z",
   "kind": "force_push",
   "who": "brunoerg",
   "commit": "029f6ec92ca397f88b602e022cf5ad191c340b38"
  },
  {
   "t": "2026-09-07T14:23:56Z",
   "kind": "review_comment",
   "who": "brunoerg",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/wallet.h",
   "commit": "029f6ec92ca397f88b602e022cf5ad191c340b38",
   "in_reply_to": 3887572966,
   "text": "Good point, I think the comment should be improved to reflect it - \"one index per iteration\" might not be correct. Thanks."
  },
  {
   "t": "2026-09-07T14:24:08Z",
   "kind": "comment",
   "who": "brunoerg",
   "assoc": "MEMBER",
   "text": "Force-pushed addressing https://github.com/bitcoin/bitcoin/pull/36068#discussion_r3887572966"
  },
  {
   "t": "2026-09-07T14:24:33Z",
   "kind": "review_comment",
   "who": "brunoerg",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "029f6ec92ca397f88b602e022cf5ad191c340b38",
   "in_reply_to": 3887572967,
   "text": "No reason, will leave as is but can think about it in case I have to touch it again. Thanks."
  }
 ],
 "labels_log": [
  {
   "t": "2026-08-24T17:19:53Z",
   "action": "labeled",
   "label": "Fuzzing",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-08-24T17:19:52Z",
   "kind": "renamed",
   "who": "DrahtBot",
   "from": "fuzz: reuse one fuzzed wallet across inputs ",
   "to": "fuzz: reuse one fuzzed wallet across inputs"
  }
 ],
 "text_chars": 2915,
 "text_tokens_estimate": 728,
 "changed_paths": [
  "src/test/fuzz/util/wallet.h",
  "src/wallet/test/fuzz/spend.cpp",
  "src/wallet/wallet.cpp",
  "src/wallet/wallet.h"
 ],
 "files": [
  {
   "path": "src/test/fuzz/util/wallet.h",
   "add": 118,
   "del": 12
  },
  {
   "path": "src/wallet/test/fuzz/spend.cpp",
   "add": 9,
   "del": 5
  },
  {
   "path": "src/wallet/wallet.cpp",
   "add": 11,
   "del": 0
  },
  {
   "path": "src/wallet/wallet.h",
   "add": 8,
   "del": 0
  }
 ],
 "test_lines": 144,
 "git": {
  "head": "029f6ec92ca397f88b602e022cf5ad191c340b38",
  "head_matches_backup": true,
  "base": "0c6f2af97b5cf297d26638688d47785690cd3bbc",
  "commits": [
   {
    "sha": "3fc5f3d483",
    "subject": "wallet: add test-only helper to clear in-memory transaction state",
    "files": 2,
    "add": 19,
    "del": 0
   },
   {
    "sha": "029f6ec92c",
    "subject": "fuzz: reuse one fuzzed wallet across inputs",
    "files": 2,
    "add": 127,
    "del": 17
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "8034d053b58a0301",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}