{
 "number": 35818,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/35818",
 "title": "bloom: avoid undefined sizing calculations",
 "author": "l0rinc",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-07-26T23:27:12Z",
 "updated_at": "2026-08-14T10:34:45Z",
 "age_days": 52,
 "draft": false,
 "labels": [],
 "milestone": null,
 "base": "master",
 "head_sha": "c4302ddf806094fcc2b1e21593e25b2038191a07",
 "head_ref": "l0rinc/bloom-zero-elements",
 "head_repo": "l0rinc/bitcoin",
 "head_history": [
  {
   "t": "2026-07-28T01:17:56Z",
   "sha": "d6eaa9bc2170a3542816a9f5c3a2c9b1861e67e3"
  },
  {
   "t": "2026-07-28T02:36:45Z",
   "sha": "c4302ddf806094fcc2b1e21593e25b2038191a07"
  }
 ],
 "additions": 75,
 "deletions": 4,
 "changed_files": 4,
 "commit_count": 4,
 "size_bucket": "S",
 "mergeable_state": "blocked",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "ack": [
     {
      "login": "rustaceanrob",
      "url": "https://github.com/bitcoin/bitcoin/pull/35818#issuecomment-5292327672"
     }
    ]
   },
   "conflicts": []
  }
 },
 "acks_parsed": {
  "rustaceanrob": {
   "kind": "ack",
   "hash": "c4302ddf806094fcc2b1e21593e25b2038191a07",
   "t": "2026-08-14T10:34:40Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 1,
  "stale_ack": 0,
  "concept_ack": 0,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "rustaceanrob"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "blocked",
  "last_author_activity": "2026-08-05T22:55:41Z",
  "last_reviewer_activity": "2026-08-14T10:34:40Z",
  "last_reviewer": "rustaceanrob",
  "author_silent_days": 42,
  "waiting_on_author_days": 34,
  "days_since_update": 34
 },
 "refs": {
  "mentioned": [],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [],
  "conflicts": []
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/common/bloom.cpp"
 ],
 "body": "**Problem:** The four-argument `CBloomFilter` constructor is used by unit tests and the fuzz target to exercise Bloom filter sizing directly.\nZero elements make the hash-function calculation divide by zero.\nThe size calculation converts its floating-point result to `unsigned int` before applying the protocol limit, which is undefined for zero or invalid false-positive rates and for valid parameters whose calculated size exceeds the integer range.\n\n**Fix:** Require false-positive rates in `[0, 1]`, construct the existing empty match-all state for zero elements, and use the largest permitted filter for a zero rate with positive elements.\nClamp the calculated size before its integer conversion so large valid inputs remain within the protocol limit.",
 "commits": [
  {
   "sha": "3614b38e33cb98528b0c3dc337060f7448bb8e6e",
   "date": "2026-07-27T23:36:41Z",
   "message": "test: characterize bloom filter sizing\n\nExercise ordinary sizing, the protocol size clamp, the match-all rate, tiny positive finite rates, and existing empty-filter behavior without relying on behavior changed by later commits."
  },
  {
   "sha": "77055771169dc44f832083f46468ea75421a009b",
   "date": "2026-07-28T01:03:27Z",
   "message": "bloom: avoid undefined sizing calculations\n\nThe four-argument CBloomFilter constructor is currently used only by Bloom filter tests and the fuzz target, which need it to exercise the sizing calculations directly.\n\nZero elements make the hash-function formula divide by zero, and the size formula can convert NaN, infinity, a negative value, or a finite value outside the unsigned range before applying the protocol clamp.\n\nRequire false-positive rates in [0, 1], construct the existing empty-filter state for zero elements, use the maximum permitted size for a zero rate with positive elements, and clamp the size in floating-point space before converting it."
  },
  {
   "sha": "3acd9ae9016e6c66783bec002435a49c04050db2",
   "date": "2026-07-28T02:35:53Z",
   "message": "test: cover bloom sizing boundaries\n\nCover zero-element and zero-rate behavior, and clamping before unsigned conversion at the maximum element count.\nReject finite out-of-range values, infinities, quiet NaN, and signaling NaN.\nCheck that the covered cases other than signaling NaN do not raise floating-point exceptions.\nMerely classifying a signaling NaN may raise FE_INVALID."
  },
  {
   "sha": "c4302ddf806094fcc2b1e21593e25b2038191a07",
   "date": "2026-07-28T02:35:53Z",
   "message": "fuzz: cover bloom sizing boundaries\n\nExercise the full unsigned element-count range and generate ordinary valid false-positive rates across [0, 1] together with exact floating-point boundaries.\n\nGenerate the rate independently from the element count so zero-element filters exercise the same valid rate range instead of only zero, while empty input still exercises (0, 0)."
  }
 ],
 "timeline": [
  {
   "t": "2026-07-28T01:17:56Z",
   "kind": "force_push",
   "who": "l0rinc",
   "commit": "d6eaa9bc2170a3542816a9f5c3a2c9b1861e67e3"
  },
  {
   "t": "2026-07-28T02:36:45Z",
   "kind": "force_push",
   "who": "l0rinc",
   "commit": "c4302ddf806094fcc2b1e21593e25b2038191a07"
  },
  {
   "t": "2026-08-04T12:13:53Z",
   "kind": "review_comment",
   "who": "rustaceanrob",
   "assoc": "MEMBER",
   "path": "src/common/bloom.cpp",
   "commit": "c4302ddf806094fcc2b1e21593e25b2038191a07",
   "in_reply_to": null,
   "text": "I think this suggestion is too large to do right now, but perhaps can be done as follow up. Given this is only used in test code, perhaps this can be defined in a `test/util/bloom.h` or similar. A free function could then be declared as a friend and defined in the test util, which could also contain the `BloomFilterSize` function. This makes it obvious the `Assert` in that function body is only reachable in test source.\n\n```cpp\nnamespace bloom_test {\nCBloomFilter MakeBloomFilter(...)\n}\n\nclass CBloomFilter {\nprivate:\n    friend CBloomFilter bloom_tests::MakeBloomFilter(...)\n}\n```"
  },
  {
   "t": "2026-08-04T12:32:20Z",
   "kind": "review",
   "who": "rustaceanrob",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "c4302ddf806094fcc2b1e21593e25b2038191a07",
   "text": "Concept ACK"
  },
  {
   "t": "2026-08-05T22:55:41Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "CONTRIBUTOR",
   "path": "src/common/bloom.cpp",
   "commit": "c4302ddf806094fcc2b1e21593e25b2038191a07",
   "in_reply_to": 3712303548,
   "text": "[quoted text omitted]\n\nIndeed, we can move construction and sizing behind a test-only factory in a follow-up so this PR stays focused on making the existing sizing calculations well-defined."
  },
  {
   "t": "2026-08-14T10:34:40Z",
   "kind": "comment",
   "who": "rustaceanrob",
   "assoc": "MEMBER",
   "text": "Code review ACK c4302ddf806094fcc2b1e21593e25b2038191a07"
  }
 ],
 "labels_log": [
  {
   "t": "2026-07-28T02:19:45Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-28T06:02:07Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-07-28T01:18:06Z",
   "kind": "renamed",
   "who": "l0rinc",
   "from": "bloom: support zero-element filters",
   "to": "bloom: avoid undefined sizing calculations"
  },
  {
   "t": "2026-07-28T05:00:33Z",
   "kind": "closed",
   "who": "l0rinc"
  },
  {
   "t": "2026-07-28T05:00:36Z",
   "kind": "reopened",
   "who": "l0rinc"
  }
 ],
 "text_chars": 3284,
 "text_tokens_estimate": 821,
 "changed_paths": [
  "src/common/bloom.cpp",
  "src/common/bloom.h",
  "src/test/bloom_tests.cpp",
  "src/test/fuzz/bloom_filter.cpp"
 ],
 "files": [
  {
   "path": "src/common/bloom.cpp",
   "add": 11,
   "del": 2
  },
  {
   "path": "src/common/bloom.h",
   "add": 2,
   "del": 0
  },
  {
   "path": "src/test/bloom_tests.cpp",
   "add": 51,
   "del": 0
  },
  {
   "path": "src/test/fuzz/bloom_filter.cpp",
   "add": 11,
   "del": 2
  }
 ],
 "test_lines": 64,
 "git": {
  "head": "c4302ddf806094fcc2b1e21593e25b2038191a07",
  "head_matches_backup": true,
  "base": "7dea464d6b51a69bd99a0451be8aaf3a26313eb6",
  "commits": [
   {
    "sha": "3614b38e33",
    "subject": "test: characterize bloom filter sizing",
    "files": 1,
    "add": 32,
    "del": 0
   },
   {
    "sha": "7705577116",
    "subject": "bloom: avoid undefined sizing calculations",
    "files": 2,
    "add": 13,
    "del": 2
   },
   {
    "sha": "3acd9ae901",
    "subject": "test: cover bloom sizing boundaries",
    "files": 1,
    "add": 19,
    "del": 0
   },
   {
    "sha": "c4302ddf80",
    "subject": "fuzz: cover bloom sizing boundaries",
    "files": 1,
    "add": 11,
    "del": 2
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "6af686a05225522c",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}