{
 "number": 36098,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/36098",
 "title": "fuzz: add corpus reachability checks",
 "author": "HowHsu",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-08-27T11:48:25Z",
 "updated_at": "2026-09-17T09:04:00Z",
 "age_days": 21,
 "draft": false,
 "labels": [
  "Fuzzing"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "b3e4cdcf020c6be28e929e6d8b2d29dc372008df",
 "head_ref": "fuzz-reachability-goals",
 "head_repo": "HowHsu/bitcoin",
 "head_history": [
  {
   "t": "2026-08-27T11:53:10Z",
   "sha": "eb6d22affb3cca581d83d3d8dddf58d62db41791"
  },
  {
   "t": "2026-08-27T12:38:23Z",
   "sha": "1475e3c982e6d576bdf4035fb297caa77e32f733"
  },
  {
   "t": "2026-08-27T13:50:51Z",
   "sha": "dece53e016cc613014100a44faf79676c2f84235"
  },
  {
   "t": "2026-08-28T14:30:40Z",
   "sha": "81d4e65ba162e38982ed042bdcfaffb880391280"
  },
  {
   "t": "2026-08-31T06:22:04Z",
   "sha": "faee4fe846aaf6b43c077a83103ea2d6d08007c4"
  },
  {
   "t": "2026-08-31T08:04:15Z",
   "sha": "b3e4cdcf020c6be28e929e6d8b2d29dc372008df"
  }
 ],
 "additions": 274,
 "deletions": 18,
 "changed_files": 14,
 "commit_count": 13,
 "size_bucket": "M",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "brunoerg",
      "url": "https://github.com/bitcoin/bitcoin/pull/36098#issuecomment-5446893369"
     },
     {
      "login": "nervana21",
      "url": "https://github.com/bitcoin/bitcoin/pull/36098#issuecomment-5704593456"
     }
    ]
   },
   "conflicts": [
    {
     "number": 36244,
     "title": "validation, net: Process blocks asynchronously and reduce cs_main contention",
     "author": "w0xlt"
    },
    {
     "number": 29700,
     "title": "kernel, refactor: return error status on all fatal errors",
     "author": "ryanofsky"
    }
   ]
  }
 },
 "acks_parsed": {
  "brunoerg": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-08-27T21:41:32Z",
   "stale": false
  },
  "nervana21": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-09-16T21:13:30Z",
   "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": 1,
  "changes_requested": 0,
  "distinct_reviewers": [
   "Crypt-iQ",
   "brunoerg",
   "dergoegge",
   "maflcko",
   "nervana21"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-09-17T09:04:00Z",
  "last_reviewer_activity": "2026-09-17T05:40:51Z",
  "last_reviewer": "maflcko",
  "author_silent_days": 0,
  "waiting_on_author_days": 0,
  "days_since_update": 0
 },
 "refs": {
  "mentioned": [
   35482
  ],
  "depends_on": [
   35482
  ],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 35482,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-08-07",
    "title": "fuzz: exercise the transaction-handling path in process_message(s)"
   }
  ],
  "conflicts": [
   36244,
   29700
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/test/fuzz/partially_downloaded_block.cpp",
  "src/test/fuzz/process_messages.cpp",
  "src/test/fuzz/util/reachability.cpp",
  "src/test/fuzz/util/reachability.h",
  "src/test/fuzz/validation_load_mempool.cpp"
 ],
 "body": "This is a follow-up to #35482.\n\nAdd `ReachabilityGoal()` to aggregate whether selected conditions are reached across all inputs processed by a fuzz target. Goals are identified by source location and reported at shutdown. Setting `FUZZ_ENFORCE_REACHABILITY=1` makes the process fail if any registered goal was not reached.\n\nAdd goals for selected high-value paths, including IBD and non-IBD message processing, completed P2P handshakes and transport messages, successful estimator/mempool/snapshot operations, transaction and package acceptance, and compact block reconstruction.\n\n### Testing\n\nBuilt the fuzz binary and replayed the current qa-assets corpora for all instrumented targets with `FUZZ_ENFORCE_REACHABILITY=1`.",
 "commits": [
  {
   "sha": "bcf97a3cc487a082231cae9d7a0fa42e21ec2b8e",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: add reachability goal tracking\n\nAdd reachability goal descriptors that fuzz targets register during\ninitialization. Track whether each condition becomes true across all inputs,\nand report registered goals when the process exits.\n\nSet FUZZ_ENFORCE_REACHABILITY=1 to exit unsuccessfully if a registered\ngoal is never satisfied. This allows corpus CI to detect lost path coverage\nwithout a separate build configuration.\n\nCo-authored-by: maflcko <6399679+maflcko@users.noreply.github.com>"
  },
  {
   "sha": "9cc79514d181c0dee3d51a5aba0eac11b45e6d83",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: track process_messages reachability"
  },
  {
   "sha": "193101ba20121149dba00474e753868e651dd423",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: track process_message reachability"
  },
  {
   "sha": "5e8da56fbef21cc1bae4621efdcbf4d594425cd3",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: track p2p handshake reachability"
  },
  {
   "sha": "1a1ef162bf89e649bbf4c94474ff4433379e03de",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: track successful block policy estimator reads"
  },
  {
   "sha": "56a89c4e1d88ad34016d4ac8fe7ccfd51c9972ad",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: track successful mempool loads"
  },
  {
   "sha": "0302981df8366f70c2d69c8754a3925e8356783c",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: track successful UTXO snapshot activation"
  },
  {
   "sha": "e2ba005dade7e0558692bd7c8a956e94136761be",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: track tx_pool_standard acceptance"
  },
  {
   "sha": "84ec1276cbeeb65a9e150d7927973bd518f00c34",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: track tx_pool acceptance"
  },
  {
   "sha": "bb09525fee68d95aed68076f7148f6a474dc042c",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: track valid ephemeral packages"
  },
  {
   "sha": "96ab84c13e9a8053ceb9e26aff507f4a47dfd30d",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: track valid transaction packages"
  },
  {
   "sha": "454f917d020ccdfca7793ad23c131cbcfba9e1f2",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: track compact block reachability"
  },
  {
   "sha": "b3e4cdcf020c6be28e929e6d8b2d29dc372008df",
   "date": "2026-08-31T07:56:09Z",
   "message": "fuzz: track complete v1 transport messages"
  }
 ],
 "timeline": [
  {
   "t": "2026-08-27T11:53:10Z",
   "kind": "force_push",
   "who": "HowHsu",
   "commit": "eb6d22affb3cca581d83d3d8dddf58d62db41791"
  },
  {
   "t": "2026-08-27T11:55:28Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.cpp",
   "commit": "22271f90aaf8bbed30f3599db03d6ecb95ff6547",
   "in_reply_to": null,
   "text": "nit: I wonder if this can use `Mutex` + the annotations"
  },
  {
   "t": "2026-08-27T11:56:49Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.h",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": null,
   "text": "```suggestion\n/// Record whether a reachability goal is satisfied by at least one fuzz input.\n```\n\nnit: Would be nice to use the `///` for new doxygen comments, to avoid the formatting inconsistencies around `/**` comments."
  },
  {
   "t": "2026-08-27T11:59:02Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/process_messages.cpp",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": null,
   "text": "Seems fine, but this seems a bit low-level. I am thinking that goals may be best higher level?\n\nEg. `mempool.size()>=1`, which implies non-ibd message?"
  },
  {
   "t": "2026-08-27T12:00:13Z",
   "kind": "review",
   "who": "maflcko",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "22271f90aaf8bbed30f3599db03d6ecb95ff6547",
   "text": "lgtm. I wonder if reachability goals can be formulated at a higher level. Maybe taking past issues or CVEs into account, to ensure they are covered and remain covered?"
  },
  {
   "t": "2026-08-27T12:20:45Z",
   "kind": "review_comment",
   "who": "dergoegge",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.h",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": null,
   "text": "I think it'd be great to add a message to this, so failures are more verbose.\n\nIt could also make sense to provide multiple reachability functions for different condition types, where the parameters of the comparison are passed directly e.g. `ReachabilityGoalGt(a, b, \"sometimes a is greater than b\");`, this can be used to guide fuzzing in addition to asserting reachability. For example, the assertion feedback in this https://github.com/oss-garage/fuzzamoto/pull/69 PR could make use of the these reachability checks for guidance.\n\nMaybe (although this is likely more controversial) we could also place these checks into production code, to get this feedback for all our tests not just fuzzing."
  },
  {
   "t": "2026-08-27T12:25:01Z",
   "kind": "comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nSure, I'll look into it."
  },
  {
   "t": "2026-08-27T12:38:23Z",
   "kind": "force_push",
   "who": "HowHsu",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733"
  },
  {
   "t": "2026-08-27T13:17:50Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.h",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": 3871627592,
   "text": "[quoted text omitted]\n\nI'd presume that -use_value_profile=1 of libFuzzer takes care of this for e.g. `mempool.size() >= 200`, but I haven't really benchmarked any of this. Is there some gist or background on how to best guide a fuzz engine toward a set goal? I know there is libFuzzers `focus_function`, but it is experimental and last time I tried it didn't work. Also, I haven't tried other fuzzing engines, so I wonder if others have nice findings here."
  },
  {
   "t": "2026-08-27T13:28:36Z",
   "kind": "review_comment",
   "who": "Crypt-iQ",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.h",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": 3871627592,
   "text": "[quoted text omitted]\n\nAFL++ does have IJON annotations which I think can just be added to the harness, though it requires specific macros (maybe there is a way to hide them though?). I tested them but since I did not properly benchmark it compared to not using it, I could not tell whether it actually guided AFL++ to the goal more efficiently."
  },
  {
   "t": "2026-08-27T13:32:33Z",
   "kind": "review_comment",
   "who": "dergoegge",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.h",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": 3871627592,
   "text": "I was more thinking of custom fuzzers that could make use of this (e.g. that fuzzamoto PR I linked), or AFL++ also has an [IJON](https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/IJON.md) implementation now, and Antithesis has their [Sometimes Assertions](https://antithesis.com/docs/best_practices/sometimes_assertions/), which could both be used under the hood in `ReachabilityGoal*`. For Antithesis, this only makes sense if we put these goal annotations in non-test code, as the fuzz tests aren't running there.\n\nSo overall I think splitting into different functions and explicitly passing the parameters gives us: better error messages, as well as more flexibility for different fuzzers. This can of course also be added later. I think what's in this PR already provides value."
  },
  {
   "t": "2026-08-27T13:34:08Z",
   "kind": "review_comment",
   "who": "dergoegge",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.h",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": 3871627592,
   "text": "The same argument could also be made for our `Assert` and `Assume` macros (e.g. having `Assume{Gt,Lt,...}`)"
  },
  {
   "t": "2026-08-27T13:46:37Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/process_messages.cpp",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": 3871472661,
   "text": "[quoted text omitted]\n\n~Not sure if the current corpus data can pass this.~\nTested it, current corpus data fails on it. We have to first construct a valid Tx entry into corpus set first."
  },
  {
   "t": "2026-08-27T13:50:51Z",
   "kind": "force_push",
   "who": "HowHsu",
   "commit": "dece53e016cc613014100a44faf79676c2f84235"
  },
  {
   "t": "2026-08-27T13:59:05Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/process_messages.cpp",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": 3871472661,
   "text": "[quoted text omitted]\n\nHuh, I am pretty sure I generated those manually last week and uploaded the fuzz inputs for that?\n\n```\n$ FUZZ=process_messages ./bld-cmake/bin/fuzz -runs=1 ../qa-assets/fuzz_corpora/process_messages/273fac680feaaa99ede7542dbd7af07db80821e2\nINFO: Running with entropic power schedule (0xFF, 100).\n\ntest/fuzz/process_messages.cpp:139 void process_messages_fuzz_target(FuzzBufferType): Assertion `initial_sequence==end_sequence' failed."
  },
  {
   "t": "2026-08-27T14:13:28Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.h",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": 3871627592,
   "text": "[quoted text omitted]\n\nHmm, I'd presume that reachability goals are test-case dependant. Let's say you want to have a \"full mempool\". For a test that manually fills the mempool with dummy transactions (possibly just as an initial setup for a later test), this goal is mostly meaningless and would be better off not being checked globally. However, if there is a mempool-sepcific test, it could make sense to have a reachability goal toward a \"full mempool\" for this specific test.\n\nMoreover, even if the goals are similar for different test-cases, the specific goal could still differ: E.g. the connman test could have a goal of 1000 peers, whereas the p2p test could have a goal of 10 peers (or so)..."
  },
  {
   "t": "2026-08-27T14:25:13Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/process_messages.cpp",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": 3871472661,
   "text": "[quoted text omitted]\n\nMy bad, forgot to update the data set. But the thing is an input of  `add tx + del tx` in `process_messages` is also valid. so perhaps `end_sequence != initial_sequence` is better?"
  },
  {
   "t": "2026-08-27T14:27:20Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/util/reachability.h",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": 3871627592,
   "text": "[quoted text omitted]\n\nAdded.\n\n[quoted text omitted]\nBeyond my knowledges, I'll take some time to get to know this first.\n\n[quoted text omitted]\nIt is a good follow-up task, thanks."
  },
  {
   "t": "2026-08-27T14:29:34Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/process_messages.cpp",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": 3871472661,
   "text": "Right, the assertion was just to trigger/find the input. The goal should obviously be inverted :)"
  },
  {
   "t": "2026-08-27T21:41:32Z",
   "kind": "comment",
   "who": "brunoerg",
   "assoc": "MEMBER",
   "text": "Concept ACK"
  },
  {
   "t": "2026-08-28T00:08:35Z",
   "kind": "review_comment",
   "who": "brunoerg",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/partially_downloaded_block.cpp",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": null,
   "text": "1e1144a468fed566dddd732886a0a5b78ad9df99: Is this deserialization a good candidate for it? I think the other ones for this target would implicitly catch it."
  },
  {
   "t": "2026-08-28T00:35:02Z",
   "kind": "comment",
   "who": "brunoerg",
   "assoc": "MEMBER",
   "text": "Just playing with the idea, thought on coinselection:\n```diff\ndiff --git a/src/wallet/test/fuzz/coinselection.cpp b/src/wallet/test/fuzz/coinselection.cpp\nindex 8949a6c20d..56703ecf0e 100644\n--- a/src/wallet/test/fuzz/coinselection.cpp\n+++ b/src/wallet/test/fuzz/coinselection.cpp\n@@ -8,6 +8,7 @@\n #include <test/fuzz/FuzzedDataProvider.h>\n #include <test/fuzz/fuzz.h>\n #include <test/fuzz/util.h>\n+#include <test/fuzz/util/reachability.h>\n #include <test/util/setup_common.h>\n #include <wallet/coinselection.h>\n\n@@ -395,6 +396,7 @@ void FuzzCoinSelectionAlgorithm(std::span<const uint8_t> buffer) {\n                 (void)result_bnb->GetInputSet();\n             }\n         }\n+        ReachabilityGoal(result.has_value(), \"coinselection_bnb finds a solution\");\n     }\n\n     if constexpr (Algorithm == CoinSelectionAlgorithm::SRD) {\n@@ -409,6 +411,7 @@ void FuzzCoinSelectionAlgorithm(std::span<const uint8_t> buffer) {\n             (void)result_srd->GetShuffledInputVector();\n             (void)result_srd->GetInputSet();\n         }\n+        ReachabilityGoal(result.has_value(), \"coinselection_srd finds a solution\");\n     }\n\n     if constexpr (Algorithm == CoinSelectionAlgorithm::KNAPSACK) {\n@@ -436,6 +439,7 @@ void FuzzCoinSelectionAlgorithm(std::span<const uint8_t> buffer) {\n             (void)result_knapsack->GetShuffledInputVector();\n             (void)result_knapsack->GetInputSet();\n         }\n+        ReachabilityGoal(result.has_value(), \"coinselection_knapsack finds a solution\");\n     }\n\n     std::vector<COutput> utxos;\n\n```"
  },
  {
   "t": "2026-08-28T03:51:29Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/process_messages.cpp",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": 3871472661,
   "text": "[quoted text omitted]\n\nI meant \"compare to use mempool.size() >= 1, end_sequence != initial_sequence may be a better choice?\""
  },
  {
   "t": "2026-08-28T10:16:56Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/process_messages.cpp",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": 3871472661,
   "text": "I've also uploaded block messages, so marking them as another goal could also make sense?"
  },
  {
   "t": "2026-08-28T13:03:37Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/process_messages.cpp",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": 3871472661,
   "text": "[quoted text omitted]\n\nOne problem is we still need a goal to represent at least one input goes the IBD path, while `mempool.size() == 0` is not accurate for that. If we keep `  ReachabilityGoal(saw_ibd_message, ...);\n`, then seems keep both `saw_ibd_message` and `saw_non_ibd_message` would be more symmetrical"
  },
  {
   "t": "2026-08-28T13:28:13Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/process_messages.cpp",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": 3871472661,
   "text": "`JumpOutOfIbd` and `ResetIbd` already have internal assertions (both ways), so now that those function calls are added back, it is unlikely to regress, unless they are removed?\n\nIf you really want, you can add the redundant goals for it, but I don't think we need to turn every assertion into a goal."
  },
  {
   "t": "2026-08-28T14:30:40Z",
   "kind": "force_push",
   "who": "HowHsu",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280"
  },
  {
   "t": "2026-08-28T14:31:58Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/process_messages.cpp",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": 3871472661,
   "text": "Updated."
  },
  {
   "t": "2026-08-28T14:32:22Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/util/reachability.h",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": 3871457855,
   "text": "Updated."
  },
  {
   "t": "2026-08-28T14:32:36Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/util/reachability.cpp",
   "commit": "22271f90aaf8bbed30f3599db03d6ecb95ff6547",
   "in_reply_to": 3871448979,
   "text": "Updated."
  },
  {
   "t": "2026-08-28T14:59:37Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/partially_downloaded_block.cpp",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": 3876811572,
   "text": "right, but currently the Goal is registered while `ReachabilityGoal()` is called at the first time. Thus removing this causes the early return on line 59 escaped. This reminds me about the design, maybe a explicit Goal registration is better, otherwise we have to look out all the early returns? @maflcko any thoughts on this?"
  },
  {
   "t": "2026-08-28T17:28:21Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/partially_downloaded_block.cpp",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": 3876811572,
   "text": "Well, I'd say this shouldn't be needed, but maybe I am wrong. There could be options:\n\n* One hacky, where one globally looks at the coverage with a tool and then makes the tool complain when it sees uncovered `ReachabilityGoal`s. The benefit would be that everything here stays as-is and the tool sits somewhere else?\n* Make reachability goals global and register them. (Probably with some kind of context to select/check only the active ones?). This will probably increase the state/code/logic a bit, but should be fine.\n\nNo strong opinion. I guess anything is fine here?"
  },
  {
   "t": "2026-08-29T06:04:21Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/partially_downloaded_block.cpp",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": 3876811572,
   "text": "Yes, I'm implementing the second way, will push the code soon."
  },
  {
   "t": "2026-08-31T06:22:04Z",
   "kind": "force_push",
   "who": "HowHsu",
   "commit": "faee4fe846aaf6b43c077a83103ea2d6d08007c4"
  },
  {
   "t": "2026-08-31T06:31:47Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/partially_downloaded_block.cpp",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": 3876811572,
   "text": "Updated to \"two-phase register/observe protocol\"."
  },
  {
   "t": "2026-08-31T06:31:56Z",
   "kind": "comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "text": "Updated to use a two-phase register/observe protocol: each target explicitly registers its reachability goals during target initialization and records observations while processing inputs. This avoids lazy registration at observation sites, where an early return could leave a goal entirely unknown to the tracker and cause enforcement to pass silently instead of reporting it as unreached."
  },
  {
   "t": "2026-08-31T07:37:39Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.h",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": 3871457855,
   "text": "[quoted text omitted]\n\nThis was reverted in the last push?"
  },
  {
   "t": "2026-08-31T08:04:15Z",
   "kind": "force_push",
   "who": "HowHsu",
   "commit": "b3e4cdcf020c6be28e929e6d8b2d29dc372008df"
  },
  {
   "t": "2026-08-31T08:04:42Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/util/reachability.h",
   "commit": "1475e3c982e6d576bdf4035fb297caa77e32f733",
   "in_reply_to": 3871457855,
   "text": "[quoted text omitted]\n\nThanks, fixed."
  },
  {
   "t": "2026-08-31T11:02:32Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/validation_load_mempool.cpp",
   "commit": "b3e4cdcf020c6be28e929e6d8b2d29dc372008df",
   "in_reply_to": null,
   "text": "Instead of a plain \"success\" (which probably happens on an empty file), it may be better to ensure all features are covered:\n\n* txs\n* unbroadcast set\n* fee deltas\n* ... etc?"
  },
  {
   "t": "2026-09-01T21:37:41Z",
   "kind": "review_comment",
   "who": "Crypt-iQ",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.h",
   "commit": "81d4e65ba162e38982ed042bdcfaffb880391280",
   "in_reply_to": 3871627592,
   "text": "[quoted text omitted]\n\nI did a mini experiment with different approaches to try and solve the maze AFL [includes](https://github.com/AFLplusplus/AFLplusplus/blob/45bb74bd3a6591e6853b704c390ab6156c0a3c88/test/ijon-maze.c):\n\n1. for `use_value_profile=1`, I used a simple `if (hashfunc(row, col) == 0) abort();`. My idea is that this would count different positions in the maze as unique because of the hash function, but I'm not sure if I understand the feature. It eventually found the solution after ~67M iterations (this varied a bit, one run hit ~1B iterations).\n\n2. compiling with AFL++ IJON (i.e. `IJON_SET` macro is activated in the maze code) quickly found the solution in ~300K iterations\n\n3. using clang with `__libfuzzer_extra_counters` (incompatible on Darwin, though repurposing `__sanitizer_cov_8bit_counters_init` is compatible and works similarly) hit it in ~450K iterations:\n```\n__attribute__((section(\"__libfuzzer_extra_counters\"))) static uint8_t maze_counters[1 << 20];\n\n... in the maze code...\nmaze_counters[hashfunc(row, col) & 0xfffff]++;\n```\n\nSolving the maze is one type of problem that uses the [state area](https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/IJON.md#memory-layout) of the IJON region which is limited to 65,535 globally unique states, so it would be a bit limiting if we wanted to track many types of states. Similarly, using `__libfuzzer_extra_counters` also seems to take up a bit of memory if we're using a lot of states (especially if we're using a hash function which we shouldn't do). If anybody has ideas on a bitcoin-specific \"maze\", I could also benchmark that."
  },
  {
   "t": "2026-09-03T11:12:58Z",
   "kind": "review_comment",
   "who": "Crypt-iQ",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.cpp",
   "commit": "bcf97a3cc487a082231cae9d7a0fa42e21ec2b8e",
   "in_reply_to": null,
   "text": "Why buffer to `std::cerr` if it may have passed?"
  },
  {
   "t": "2026-09-03T11:17:25Z",
   "kind": "review_comment",
   "who": "Crypt-iQ",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.cpp",
   "commit": "bcf97a3cc487a082231cae9d7a0fa42e21ec2b8e",
   "in_reply_to": null,
   "text": "Why `std::_Exit` which according to https://en.cppreference.com/cpp/utility/program/_Exit does not clean up resources compared to `std::exit`? Also could just remove this since if the destructor runs, we shouldn't need to exit?"
  },
  {
   "t": "2026-09-03T11:20:52Z",
   "kind": "review_comment",
   "who": "Crypt-iQ",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.cpp",
   "commit": "bcf97a3cc487a082231cae9d7a0fa42e21ec2b8e",
   "in_reply_to": null,
   "text": "nit: could instead be `Assert(m_goals.try_emplace ...)` ?"
  },
  {
   "t": "2026-09-03T15:58:08Z",
   "kind": "review_comment",
   "who": "Crypt-iQ",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.cpp",
   "commit": "bcf97a3cc487a082231cae9d7a0fa42e21ec2b8e",
   "in_reply_to": null,
   "text": "I can only get this destructor to run when using libfuzzer by either running a single input or setting `-runs=N`. I guess the purpose of the reachability checks (without the fuzzing guidance mentioned in a thread above) is for a CI job running a static corpus + detecting regressions (i.e. not  fuzzing)? I wasn't able to get this to work with AFL++ when using `-E=N` (equivalent to libfuzzer's -runs option).\n\nI added a signal handler to catch SIGINT to make the destructor run, but seems like adding one is more trouble than it's worth since I ran into a hang and another weird issue."
  },
  {
   "t": "2026-09-16T21:13:30Z",
   "kind": "comment",
   "who": "nervana21",
   "assoc": "CONTRIBUTOR",
   "text": "Concept ACK"
  },
  {
   "t": "2026-09-17T05:39:55Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.cpp",
   "commit": "bcf97a3cc487a082231cae9d7a0fa42e21ec2b8e",
   "in_reply_to": 3926290032,
   "text": "[quoted text omitted]\n\nYes, mostly for a nightly CI job on a static corpora folder with `-runs=1`. Nightly, because we don't want master to randomly fail on (silent or otherwise) merge conflicts with the qa-assets repo.\n\n[quoted text omitted]\nNot sure about AFL, but I guess it won't work anyway, due to the use of global memory, which won't be updated (or not propagated back) due to the forkserver mode?"
  },
  {
   "t": "2026-09-17T05:40:51Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/test/fuzz/util/reachability.cpp",
   "commit": "bcf97a3cc487a082231cae9d7a0fa42e21ec2b8e",
   "in_reply_to": 3923944702,
   "text": "[quoted text omitted]\n\nCan you explain this? My intention was to have an exit code that indicates error. A normal exit would return 0, no?"
  },
  {
   "t": "2026-09-17T09:04:00Z",
   "kind": "review_comment",
   "who": "HowHsu",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/fuzz/validation_load_mempool.cpp",
   "commit": "b3e4cdcf020c6be28e929e6d8b2d29dc372008df",
   "in_reply_to": 3893852415,
   "text": "I'll update this soon"
  }
 ],
 "labels_log": [
  {
   "t": "2026-08-27T11:48:29Z",
   "action": "labeled",
   "label": "Fuzzing",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-27T11:53:40Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-27T15:28:34Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [],
 "text_chars": 13989,
 "text_tokens_estimate": 3497,
 "changed_paths": [
  "src/test/fuzz/cmpctblock.cpp",
  "src/test/fuzz/p2p_handshake.cpp",
  "src/test/fuzz/p2p_transport_serialization.cpp",
  "src/test/fuzz/package_eval.cpp",
  "src/test/fuzz/partially_downloaded_block.cpp",
  "src/test/fuzz/policy_estimator_io.cpp",
  "src/test/fuzz/process_message.cpp",
  "src/test/fuzz/process_messages.cpp",
  "src/test/fuzz/tx_pool.cpp",
  "src/test/fuzz/util/CMakeLists.txt",
  "src/test/fuzz/util/reachability.cpp",
  "src/test/fuzz/util/reachability.h",
  "src/test/fuzz/utxo_snapshot.cpp",
  "src/test/fuzz/validation_load_mempool.cpp"
 ],
 "files": [
  {
   "path": "src/test/fuzz/cmpctblock.cpp",
   "add": 7,
   "del": 0
  },
  {
   "path": "src/test/fuzz/p2p_handshake.cpp",
   "add": 8,
   "del": 0
  },
  {
   "path": "src/test/fuzz/p2p_transport_serialization.cpp",
   "add": 15,
   "del": 4
  },
  {
   "path": "src/test/fuzz/package_eval.cpp",
   "add": 25,
   "del": 2
  },
  {
   "path": "src/test/fuzz/partially_downloaded_block.cpp",
   "add": 7,
   "del": 0
  },
  {
   "path": "src/test/fuzz/policy_estimator_io.cpp",
   "add": 7,
   "del": 1
  },
  {
   "path": "src/test/fuzz/process_message.cpp",
   "add": 10,
   "del": 2
  },
  {
   "path": "src/test/fuzz/process_messages.cpp",
   "add": 10,
   "del": 2
  },
  {
   "path": "src/test/fuzz/tx_pool.cpp",
   "add": 25,
   "del": 2
  },
  {
   "path": "src/test/fuzz/util/CMakeLists.txt",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/test/fuzz/util/reachability.cpp",
   "add": 106,
   "del": 0
  },
  {
   "path": "src/test/fuzz/util/reachability.h",
   "add": 34,
   "del": 0
  },
  {
   "path": "src/test/fuzz/utxo_snapshot.cpp",
   "add": 10,
   "del": 1
  },
  {
   "path": "src/test/fuzz/validation_load_mempool.cpp",
   "add": 9,
   "del": 4
  }
 ],
 "test_lines": 292,
 "git": {
  "head": "b3e4cdcf020c6be28e929e6d8b2d29dc372008df",
  "head_matches_backup": true,
  "base": "994c17d6c0a1453a1d7cc44ee2bbc49afa2d1155",
  "commits": [
   {
    "sha": "bcf97a3cc4",
    "subject": "fuzz: add reachability goal tracking",
    "files": 3,
    "add": 141,
    "del": 0
   },
   {
    "sha": "9cc79514d1",
    "subject": "fuzz: track process_messages reachability",
    "files": 1,
    "add": 10,
    "del": 2
   },
   {
    "sha": "193101ba20",
    "subject": "fuzz: track process_message reachability",
    "files": 1,
    "add": 10,
    "del": 2
   },
   {
    "sha": "5e8da56fbe",
    "subject": "fuzz: track p2p handshake reachability",
    "files": 1,
    "add": 8,
    "del": 0
   },
   {
    "sha": "1a1ef162bf",
    "subject": "fuzz: track successful block policy estimator reads",
    "files": 1,
    "add": 7,
    "del": 1
   },
   {
    "sha": "56a89c4e1d",
    "subject": "fuzz: track successful mempool loads",
    "files": 1,
    "add": 9,
    "del": 4
   },
   {
    "sha": "0302981df8",
    "subject": "fuzz: track successful UTXO snapshot activation",
    "files": 1,
    "add": 10,
    "del": 1
   },
   {
    "sha": "e2ba005dad",
    "subject": "fuzz: track tx_pool_standard acceptance",
    "files": 1,
    "add": 13,
    "del": 1
   },
   {
    "sha": "84ec1276cb",
    "subject": "fuzz: track tx_pool acceptance",
    "files": 1,
    "add": 13,
    "del": 2
   },
   {
    "sha": "bb09525fee",
    "subject": "fuzz: track valid ephemeral packages",
    "files": 1,
    "add": 13,
    "del": 1
   },
   {
    "sha": "96ab84c13e",
    "subject": "fuzz: track valid transaction packages",
    "files": 1,
    "add": 12,
    "del": 1
   },
   {
    "sha": "454f917d02",
    "subject": "fuzz: track compact block reachability",
    "files": 2,
    "add": 14,
    "del": 0
   },
   {
    "sha": "b3e4cdcf02",
    "subject": "fuzz: track complete v1 transport messages",
    "files": 1,
    "add": 15,
    "del": 4
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "a7ca7883e8cb6dee",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}