Skip to content

chore: prepare v23.1.8 release#7477

Open
thepastaclaw wants to merge 26 commits into
dashpay:v23.1.xfrom
thepastaclaw:release/v23.1.8
Open

chore: prepare v23.1.8 release#7477
thepastaclaw wants to merge 26 commits into
dashpay:v23.1.xfrom
thepastaclaw:release/v23.1.8

Conversation

@thepastaclaw

@thepastaclaw thepastaclaw commented Jul 22, 2026

Copy link
Copy Markdown

Release Preparation for v23.1.8

Prepares Dash Core v23.1.8 on the v23.1.x branch.

Summary

This patch release collects security hardening, P2P and compact-block correctness fixes, CoinJoin reliability corrections, RPC/GUI fixes, and build and CI compatibility updates for the v23.1.x series.

The changes do not alter consensus.

Included Dash backports

CoinJoin and wallet

  • Dash fix: dangling point to cj client #7259: CoinJoin client lifetime, wallet registration, enablement, locked-wallet autostart, and wallet-manager lock-order corrections.
  • Release-local follow-ups ensure duplicate wallet registration does not reinitialize or autostart a client and keep the Qt action synchronized with the wallet's authoritative mixing state.

Security, LLMQ, governance, and serialization

These cover bounded vector deserialization, SPORK/bloom/CoinJoin/quorum message bounds, bounded recovered-signature and signature-share queues, ChainLock cache bounds, governance request authorization and throttling, and associated regression coverage.

Dash #7408 is folded into the release as shipped: cheap QCONTRIB structure checks accept encrypted contribution blob counts in [params.minSize, params.size]. Exact member-count validation remains in the DKG session worker against the selected member set. An earlier review-only attempt to keep an exact-params.size intake check (c38ef02 on the merge-commits lineage) is not present on this history; that regression broke regtest DKG whenever the selected set is smaller than the configured quorum size.

P2P and compact blocks

Upstream Bitcoin Core provenance:

These backports avoid holding cs_main during block reads and harden compact-block download and reconstruction against mutated blocks, malformed headers, failed reconstruction, and parallel-download state corruption.

GUI, RPC, build, and CI

Commit history

The branch uses one GPG-signed cherry-pick of each relevant source PR merge commit rather than preserving each PR's individual constituent commits. All 26 commits in the release range are GPG-signed.

Two narrow provenance exceptions avoid broadening the release scope:

Release preparation and local adaptations

  • Adapt v23.1.x APIs and tests required by the selected backports.
  • Preserve disabled-governance routing and keep fix: bound DKG contribution blob intake #7408 QCONTRIB blob-count range checks ([minSize, size]).
  • Generate and verify v23.1.8 version metadata, manpages, dash.conf, Flatpak metadata, and release notes.
  • Correct CoinJoin duplicate initialization and Qt toggle reconciliation.
  • Isolate process-global CoinJoin test options so randomized test ordering cannot leak autostart state between cases.
  • Sanitize unused macOS SDK manpage paths that actions/upload-artifact@v6 cannot represent.
  • Correct release-note descriptions for CoinJoin, DKG, and protx listdiff.

Validation

  • Complete configured-tree build: passed
  • make check: passed
  • test/lint/all-lint.py: passed
  • Focused serialization/P2P/validation unit tests: passed
  • Focused governance/LLMQ/ChainLock unit tests: passed
  • Focused CoinJoin unit tests, including duplicate-registration and NewKeyPool regressions: passed
  • Focused functional tests passed:
    • feature_governance_objects.py
    • p2p_governance_invs.py
    • p2p_govsync_bloom.py
    • feature_llmq_dkg_intake.py
    • feature_sporks.py
    • p2p_compactblocks.py
    • p2p_filter.py
    • p2p_mutated_blocks.py
    • p2p_quorum_data.py
    • rpc_coinjoin.py
    • rpc_getmerkleblocks.py
    • feature_asset_locks.py (explicit CI reconciliation for fix: bound DKG contribution blob intake #7408 QCONTRIB range)
  • All built release binaries report clean v23.1.8 versions.
  • Generated dash.conf matches the checked-in file.
  • Flatpak XML and the complete branch diff validate cleanly.

Review

The original 68-commit range was inventoried and mapped to its Dash and Bitcoin Core provenance. A focused security review found no new high- or medium-confidence non-resource-exhaustion vulnerabilities. Release-engineering findings around branch API adaptation, DKG range handling, CoinJoin initialization/UI state, test correctness, and release-note accuracy are folded into their owning source or release-preparation commits.

CI reconciliation (remote head 2c5c01e vs this history)

Remote CI on merge-commits head 2c5c01ef20c failed five test jobs (linux64, sqlite, ubsan, tsan, multiprocess) all on feature_asset_locks.py. Logs show:

  • ActiveDKGSessionHandler::InitNewQuorum ... quorum initialization failed
  • subsequent bad-cbtx-assetlocked-amount
  • wait timeout in test_asset_locks

That head still carried the c38ef02 exact-size QCONTRIB intake check (blobs.size() == params.size). Valid contributions encrypt one blob per selected member, so counts in [minSize, size) are legitimate and were rejected before DKG could complete under undersized regtest MN sets. The 65c7183 / #7408 range check restores acceptance of those contributions.

This final-history branch has the correct range folded into #7408; it never applies c38ef02's exact-size regression. Local re-run of feature_asset_locks.py on the final v23.1.8 product tree passed (173 s). Both feature_llmq_simplepose.py variants also passed on clean runs.

Local validation limitations

  • The headless macOS environment prevented direct Cocoa GUI runtime driving due to a PasteBoard trace trap; the configured Qt test target passed under make check.
  • appstreamcli and help2man were unavailable, so Flatpak metadata received XML validation and manpages were checked against the built binary versions and expected help changes.
  • The complete functional suite was not run; the listed touched-path functional tests (including feature_asset_locks.py), full make check, full lint, and complete build were used as the comprehensive local gate.
  • One rpc_getmerkleblocks.py attempt failed locally with RPC port bind contention against leftover functional-test dashd processes; it passed on clean retry after those processes were stopped.

@thepastaclaw

thepastaclaw commented Jul 22, 2026

Copy link
Copy Markdown
Author

✅ Final review complete — no blockers (commit bcdb34c)

@thepastaclaw

Copy link
Copy Markdown
Author

The x86_64-apple-darwin / Check cache failure in run 29927594657 is a base-workflow artifact issue, not a v23.1.8 source failure.

The job loads build-depends.yml from develop at ba143fe163a7dd37cd8222cb1fb61f12ac7f4410. Branch checkout and SDK preparation succeed, but actions/upload-artifact@v6 rejects a restored fallback SDK file named APR::Base64.3pm because colons are not allowed in artifact paths. The job stops before compiling this branch. Tracker #2205 owns the workflow correction; I am leaving the release branch unchanged and not rerunning the same failing workflow.

PastaPastaPasta and others added 26 commits July 22, 2026 12:52
…ed masternodes

536f388 fix(qt): keep PoSe score visible when hiding banned masternodes (PastaClaw)

Pull request description:

  # PR description

  ## Summary

  - Keep the Masternodes tab PoSe Score column visible when "Hide banned" is
    checked.
  - Continue filtering banned masternodes via the existing proxy filter.

  Closes dashpay#7286.

  ## Validation

  - `git diff --check`
  - Pre-PR review gate: ship
  - Not run: full build / GUI smoke test; no build directory was available in
    this worktree.

Top commit has no ACKs.

Tree-SHA512: ca69dd31322d78ced7d48621ea4dbef8ec65305411abcd245a9b8c7b3059870dfc174caec2ec6d9a05f7940263379e4d5991ad5d1eeb69f5b4894d5aa4751bde
…otx listdiff results

9c74948 chore: removed comment - this code to be removed anyway after deprecated branch removed (Konstantin Akimov)
f9a4aeb fix: make non-zero output for platformP2PPort if platform_p2p exist (Konstantin Akimov)
99b0c3d fix: don't include masternode to protx listdiff` if internal migration happened between versions (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented

  There're 2 different versions of protx's internal storages; and they are switched after v24 activation.

  It seems as when version switched, the incorrect platformP2PPort / platformHTTPPort may be added to diff even if they are not used anymore and it happens only if snapshot is used; for example after node restart.

  It causes strange result of `protxlistdiff A B` such as:

  ```
  "MASTERNODE": {
      {
          "lastPaidHeight": 9204,
          "platformP2PPort": 0, <---- INVALID it should be correct port here
          "platformHTTPPort": 0, <----- INVALID it should be correct port here
          "addresses": {
            "platform_p2p": [
              <VALID>
            ],
            "platform_https": [
              <VALID>
            ]
  ```

  It happened on devnet on block on 9216

  ## What was done?
  Added reset of platformP2PPort and platformHTTPPort.
  Added a workaround to show valid results for nodes that already have corrupted database.

  ## How Has This Been Tested?
  Sync node with devnet palona ; restart node -> issue is reproduced.
  Build a node from this PR, restart node -> issue is not reproduced.

  ## Breaking Changes
  N/A

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone

ACKs for top commit:
  PastaPastaPasta:
    utACK 9c74948

Tree-SHA512: d4f364223f38f70c562fe6e387aa315823a966d1d95a00445c5cfae0b85d4610317cb2a5045daedadc696c56476c976a4836c889e76339d7397253dffe5eaf73
…tibility error for freetype

4a8a5a3 Merge bitcoin#32693: depends: fix cmake compatibility error for freetype (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented
  It fixes freetype dependency build on Kubuntu 26.04 which provide cmake-4 by default.

  ## What was done?
  Backport bitcoin#32693

  ## How Has This Been Tested?
  Build succeed

  ## Breaking Changes
  N/A

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  PastaPastaPasta:
    utACK 4a8a5a3

Tree-SHA512: b9b796ef3a6e39a1acd58bee1f990d7a5c1b9bcc1b75bc5cb8f9e00ad9cc57cd85c5fd62f585ba7cb57493e55236d8d95069a739acad271cfd01dc6da23065ab
4e5cc4b fix: stabilize par help text in manpages (PastaClaw)

Pull request description:

  # fix: stabilize par help text in manpages

  ## Issue being fixed or feature implemented

  Regenerating manpages currently records the local machine's CPU count in the
  `-par` and `-parbls` help text. That makes otherwise unrelated release
  manpage regeneration change those entries depending on which machine generated
  the pages.

  ## What was done?

  Updated the `-par` and `-parbls` help text to avoid printing the dynamic lower
  bound derived from `GetNumCores()`.

  Runtime behavior is unchanged: negative values still mean "leave that many
  cores free", `0` still auto-detects, and the existing max/default values remain
  documented. The checked-in `dashd` and `dash-qt` manpages were updated to match
  the new stable generated text.

  ## How Has This Been Tested?

  Tested on macOS arm64:

  ```bash
  git diff --check
  python3 -m py_compile contrib/devtools/gen-manpages.py
  rg -n -e '\\fB\\-[0-9]+\\fR to' doc/man/dashd.1 doc/man/dash-qt.1
  ```

  The grep command returned no matches, confirming the affected generated
  manpages no longer contain a CPU-count-specific lower bound.

  Also ran a pre-PR code review gate against the exact worktree diff:

  ```text
  Recommendation: ship
  ```

  Note: this worktree did not have configured Dash Core build artifacts and
  `help2man` is not installed locally, so I did not rerun full manpage generation
  from rebuilt binaries here. The manpage edits mirror the changed `src/init.cpp`
  help text.

  ## Breaking Changes

  None.

  ## Checklist

  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [x] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone _(for repository
    code-owners and collaborators only)_

ACKs for top commit:
  PastaPastaPasta:
    utACK 4e5cc4b
  UdjinM6:
    utACK 4e5cc4b

Tree-SHA512: a547caf7f7dce3c2d4dc8295cba75d23da3870b90fd274ee9a3ebbcb2320d8415ef682afe5ce8a9cd56f6b1c979a9c5b7f680032aa2774fd2d61282fbb4007e5
eb1857a ci: update GitHub Actions pins for Node 24 (PastaClaw)

Pull request description:

  # CI action Node 24 pins

  ## Issue being fixed or feature implemented

  GitHub Actions now warns that actions targeting Node.js 20 are deprecated and
  are being forced to run on Node.js 24. Dash Core `develop` already has most of
  the action updates, but a few workflows still pin older JavaScript action
  versions.

  ## What was done?

  Updated the remaining workflow action pins to Node 24-compatible versions:

  - `eps1lon/actions-label-merge-conflict@v3.1.0`
  - `actions/github-script@v8`
  - `amannn/action-semantic-pull-request@v6`

  Also replaced the deprecated `actions-ecosystem/action-add-labels@v1` usage in
  the merge-check workflow with `actions/github-script@v8`, and granted the
  minimal `issues: write` permission needed for PR labels/comments.

  ## How Has This Been Tested?

  - `git diff --check upstream/develop..HEAD`
  - Parsed all workflow YAML files with Ruby `YAML.load_file`
  - Searched `.github/workflows` for the deprecated action pins that triggered the
    warning
  - Ran the pre-PR code review gate; result: ship

  ## Breaking Changes

  None.

  ## Checklist

  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone

ACKs for top commit:
  UdjinM6:
    utACK eb1857a
  PastaPastaPasta:
    utACK eb1857a

Tree-SHA512: 706fa8b74bff64c565c7bd5fdad70d5ca1398becbd320c2ea1f61e0c7b74f72f60cfd109a58ad972d56b633c476cf2866df0f2f69e4f25226c0af61bd7ec96b9
actions/upload-artifact@v6 rejects filenames containing ':' (and other
NTFS-unsafe characters). The Xcode SDK ships ~9k Perl man pages under
usr/share/man with '::' in the name (e.g. APR::Base64.3pm), which breaks
the prepared-SDK artifact handoff used by mac depends/src jobs when the
depends-sdks cache misses.

Strip man pages (and any residual upload-unsafe names) after extract in
setup-sdk. Headers/libs/tbd files needed for cross-compiling are kept.
This script is checked out from the PR head even when pull_request_target
runs develop workflows, so the fix unblocks release PRs without waiting
on a default-branch workflow change.
7cb0c29 fix: fall back to serial map when fork is unavailable, drop multiprocess dep (UdjinM6)
de1cc2c fix: run of circular-dependencies with python3.15 (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented
  Multiprocess uses dill to pickle the nested handle_module2 closure and dill's Python 3.15 support is broken (co_lnotab was removed from code objects).

  ## What was done?
  Moved some functions and variables to global namespace.

  ## How Has This Been Tested?
  Run `test/lint/lint-circular-dependencies.py` with python3.15
  Review hint: use `git show -w --color-moved=dimmed-zebra`

  ## Breaking Changes
    _Please describe any breaking changes your code introduces_

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  UdjinM6:
    utACK 7cb0c29

Tree-SHA512: cc1aef094f8ed0ffd17e4ef7fdb3bb20af048b8de5c2120b0283fcf7ebb3957f8a8736d97dcd543ef5ac1d3210ed39934fde898d5c47a0ad482d4bf0e263bb91
b5a7ba0 test: drop redundant quorum manager include (PastaClaw)
59fafb0 fix: bound ChainLock seen cache (PastaClaw)

Pull request description:

  ## Issue being fixed or feature implemented

  Bounds ChainLock seen-CLSIG duplicate tracking so unvalidated or non-current CLSIG inventory cannot grow retained handler state without limit.

  This improves defensive handling for peer-supplied ChainLock messages while preserving the existing best ChainLock validation and update flow.

  ## What was done?

  - Replaced the unbounded `seenChainLocks` map with a bounded `unordered_limitedmap`.
  - Moved same-height/older ChainLock rejection before inserting into duplicate-tracking state.
  - Updated cleanup for the bounded cache.
  - Added focused unit coverage for stale CLSIG retention and cache bounding.

  ## How Has This Been Tested?

  Tested locally on macOS in a fresh worktree rebased on current `upstream/develop`:

  - `make -C src test/test_dash -j$(sysctl -n hw.ncpu)`
  - `src/test/test_dash --run_test=llmq_chainlock_tests,chainlock_handler_tests`
  - `git diff --check upstream/develop..HEAD`
  - `COMMIT_RANGE=upstream/develop..HEAD test/lint/lint-whitespace.py`
  - Scoped Codex review of `upstream/develop..HEAD`: no significant issues found; recommendation ship.

  ## Breaking Changes

  None.

  ## Checklist:

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  kwvg:
    utACK b5a7ba0

Tree-SHA512: 5626a69dfb1a1fbcbee56b62cc9fa442922a877f2d4ce33c51ade33f0718fe6cfae272e2136fb7e128e790edc8aacbbf694d0daae3a78406a98cb0543127d3ec
439fb7d test: cover undersized DKG contribution intake (pasta)
35d4480 fix: bound DKG contribution blob intake (pasta)

Pull request description:

  ## Issue being fixed or feature implemented

  The DKG intake hardening on `develop` pre-validates pushed `qcontrib` messages before retaining them. The valid configured envelope is `params.minSize <= actual members <= params.size`; the later DKG session worker still checks the exact `members.size()` once it has session context.

  Without the lower bound, structurally well-formed but undersized qcontrib payloads can pass the cheap intake check and reach retention before the worker rejects them.

  ## What was done?

  Tightened the cheap qcontrib structure check in `NetDKG` so encrypted contribution blob counts must be within the configured quorum bounds:

  - at least `params.minSize`
  - at most `params.size`

  The exact member-count validation remains in the DKG worker.

  Also added functional coverage for a qcontrib payload that deserializes cleanly but has fewer encrypted contribution blobs than `params.minSize`.

  ## How Has This Been Tested?

  Run feature_asset_locks.py multiple times that has been faulty in the first place (by knst).

  ## Breaking Changes

  None.

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone

  This pull request was created by Codex.

ACKs for top commit:
  knst:
    tACK 439fb7d
  UdjinM6:
    utACK 439fb7d

Tree-SHA512: 32de8ac4775cc066de53761de17f7df4c6126214d1cff245e97fc8eeb6222703f26e8bba32d64f8c2b1eae25e79f058766d640658cd801830fe196361e8a4726
af6a0fd feat(serialize): add bounded-vector deserialization primitives (PastaClaw)

Pull request description:

  ## Issue being fixed or feature implemented

  Network messages often have protocol-specific vector limits below the generic serialization limit. Callers need to enforce those limits before vector allocation and element decoding, without changing the wire format.

  ## What was done?

  - Factor the existing batched vector element decoder into a shared internal helper.
  - Add `UnserializeVectorWithMaxSize` for runtime bounds.
  - Add `LIMITED_VECTOR` / `LimitedVectorFormatter` for compile-time bounds in `READWRITE` declarations.
  - Keep serialization byte-for-byte compatible with ordinary vectors; only deserialization is bounded.
  - Compare CompactSize counts before narrowing or allocating, including counts at and above `MAX_SIZE`.

  ## Stacked adopters

  Each consumer remains a separate command-specific PR:

  - dashpay#7416 — quorum-data response vectors
  - dashpay#7418 — LLMQ signing message vectors
  - dashpay#7419 — CoinJoin message vectors
  - dashpay#7438 — SPORK signature vector

  Reviewing dashpay#7439 first leaves each child PR with only its protocol-specific policy, punishment, and regression tests.

  ## How Has This Been Tested?

  - `src/test/test_dash --run_test=serialize_tests`
  - Exact and over-limit boundaries, zero limits, custom element formatters, `MAX_SIZE` and `MAX_SIZE + 1` declarations, 64-bit CompactSize counts, wire compatibility, and rejection before element decode are covered.

  ## Breaking Changes

  None. Existing vector serialization and deserialization behavior is unchanged.

  ## Checklist:

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone

Top commit has no ACKs.

Tree-SHA512: 61e4183a5a2a173254f3105d30969704c2c53dac3ce421f0b3e8e989bc87ec1a94d2757944694db219802403a668d7460943d507ab7948db30b97145d57f26d6
3cb73fd test: drop redundant spork_tests unit suite (PastaClaw)
44a8fe1 test: regression coverage for CSporkMessage signature-size bound (PastaClaw)
d2e600f fix: bound CSporkMessage signature vector allocation (PastaClaw)

Pull request description:

  Depends on dashpay#7439. Please review only the two SPORK-specific commits here.

  ## What changed

  - bound `CSporkMessage::vchSig` to `CPubKey::COMPACT_SIGNATURE_SIZE` before vector allocation using #7439s `LIMITED_VECTOR` formatter
  - punish peers that send malformed, truncated, or oversized SPORK messages
  - add unit coverage for the exact `CompactSize(MAX_SIZE)` trigger and a P2P disconnect regression test

  ## Why

  A malformed SPORK message could declare a large signature length in a small payload. Generic byte-vector deserialization would allocate its first 5,000,000-byte chunk before discovering the payload was truncated. The resulting exception was caught by the generic message-processing handler without penalizing the peer, allowing repeated attempts on the same connection.

  Valid SPORK signatures are 65-byte compact signatures on every network. The testnet distinction changes the signing preimage, not the signature encoding. Shorter well-formed signatures continue through the existing invalid-signature path and are punished there; the formatter prevents oversized declarations from allocating first.

  The reusable bounded-vector serialization primitives are introduced separately in dashpay#7439.

  ## Validation

  - `src/test/test_dash --run_test=spork_tests,serialize_tests`
  - `test/functional/feature_sporks.py`

  The exact unit and functional regressions were also run against the vulnerable implementation: the unit test observed the old EOF-after-allocation path, and the malformed P2P peer was not disconnected.

ACKs for top commit:
  PastaPastaPasta:
    utACK 3cb73fd
  kwvg:
    utACK 3cb73fd

Tree-SHA512: d547f589c3920baaacb121c618b812314901639c449bd562ea150de3a8743e3fbff10c1c92f2e192de776452d74ad7dc9be02622ddd29d9e5f7e43eacd966fd2
a286e0e fix: bound quorum data response vectors (PastaClaw)

Pull request description:

  Depends on dashpay#7439. Please review only the final command-specific commit here.

  ## Issue being fixed or feature implemented

  Quorum-data responses carry verification-vector and encrypted-contribution vectors whose expected sizes are known from the requested quorum. This hardens QDATA processing by checking the serialized vector count before allocating, deserializing, or processing those vectors.

  This path is MNAuth/request-gated and is intentionally handled separately from unauthenticated public vector intake.

  ## What was done?

  - Read QDATA verification vectors with the requested quorum threshold as the maximum.
  - Read encrypted contributions with the number of valid quorum members as the maximum.
  - Require exact semantic counts and penalize mismatches before decrypt/aggregate work.
  - Extend functional coverage for undersized, oversized, and allocation-amplifying CompactSize declarations.

  The reusable bounded-vector serialization primitives are introduced separately in dashpay#7439.

  ## How Has This Been Tested?

  - `test/functional/p2p_quorum_data.py`

  ## Breaking Changes

  None.

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)

ACKs for top commit:
  kwvg:
    Code near identical to past utACK (see [diff](https://github.com/dashpay/dash/compare/ecf49885bcbb40fd09bd81de95edf54d3996e2fe..a286e0e7eb4fd63eb65f05f5deefd5a2c4c43f55))

---

utACK a286e0e

Tree-SHA512: 1d217cff7096f0dbad736e98be91e6b4eac154ae7ca0512cb3200f448b3152485d89f3f13a5d36617d7e4122aac2ed521f79ccd6ca787c0e8fafb200cb3cf118
45ee9af fix: separate CoinJoin entry wire and semantic limits (PastaClaw)
d81ff3c fix: bound CoinJoin entry intake (PastaClaw)
c72a8f5 fix: bound CoinJoin final signature intake (PastaClaw)

Pull request description:

  Depends on dashpay#7439. Please review only the three CoinJoin-specific commits here.

  ## Issue being fixed or feature implemented

  CoinJoin server messages could deserialize or process peer-controlled input/output vectors before enforcing CoinJoin session bounds. This hardens the final-signature and entry intake paths by separating the wire-safety bound from the per-entry semantic bound.

  ## What was done?

  - Accept `DSSIGNFINALTX` only during signing and only from an active session participant.
  - Bound `DSSIGNFINALTX` input deserialization at the largest complete CoinJoin size: currently 20 participants × 9 inputs = 180.
  - Apply the same 180-element wire-safety cap to both `CCoinJoinEntry` vectors, rejecting larger declarations before vector materialization.
  - Keep the existing semantic limit of 9 inputs and 9 outputs per entry in `AddEntry()`.
  - Allow declarations from 10 through 180 to deserialize safely and reach semantic rejection, consuming collateral only when it matches a collateral accepted for the active session.
  - Remove the special `MAX + 1` input rule, oversized-output flag, and asymmetric input/output deserialization behavior.
  - Add focused boundary, participant, session-state, collateral, and round-trip tests.

  The reusable bounded-vector deserialization primitive is introduced separately in dashpay#7439.

  ## How Has This Been Tested?

  - `make -C src -j15 test/test_dash`
  - `./src/test/test_dash --run_test=coinjoin_inouts_tests` (8/8 passed)
  - `test/lint/lint-whitespace.py`
  - Focused `LogPrint` format-string lint
  - Focused `clang-format-diff.py`
  - `git diff --check`
  - Independent exact-range Codex review (`9474fc5e2a1..48a1eb95838`): ship, 0 findings

  ## Breaking Changes

  None.

  ## Checklist

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  UdjinM6:
    utACK 45ee9af

Tree-SHA512: 8924d894d99ec8420034968cb55dad6ed4970911e01b5cc073b2f71ee79d0246d0aa1936b1d4a99b8f0749f5665097329c4fe65757c5986db25762a5c4ef10b4
…cation

5b6ac38 test: fix getmerkleblocks copyright year (PastaClaw)
862e7b0 test: cover getmerkleblocks oversized bloom-filter prechecks (PastaClaw)
28d2c6c fix(net): bound CBloomFilter and filteradd vectors before allocation (PastaClaw)

Pull request description:

  Prevent peer-declared FILTERADD and CBloomFilter vector lengths from allocating before their command-specific limits are checked.

  The shared CBloomFilter bound covers FILTERLOAD and governance sync. Each P2P entrypoint catches malformed bounded reads locally and applies the existing 100-point punishment, preventing replay through the outer message-processing catch. The getmerkleblocks RPC keeps its prior complete-oversized, truncated, noncanonical, and above-MAX_SIZE behavior.

  Adds exact CompactSize(MAX_SIZE) regressions for FILTERADD, FILTERLOAD, and governance sync while retaining valid boundary coverage.

  Validation:
  - make -C src dashd dash-cli test/test_dash
  - test_dash --run_test=bloom_tests (14 cases)
  - test/functional/p2p_filter.py
  - test/functional/p2p_govsync_bloom.py
  - test/lint/lint-python.py
  - test/lint/lint-whitespace.py
  - git diff --check
  - manual getmerkleblocks RPC matrix for complete/truncated boundary and malformed-prefix behavior
  - independent exact-range review: ship, no significant findings

ACKs for top commit:
  knst:
    utACK 5b6ac38

Tree-SHA512: 8c433ff069021ccbc2fbaed92764704028a00fddfd9391644e00a3a70c768328a4f479142d8feebfd54a10d8c126ddcf77770f05cb3635a39def89ff320dba2e
… remote OOM

5c10539 perf: avoid O(n) map scan on every recovered sig in pending-queue cap check (pasta)
95e160d fix: bound pending recovered sig queue to prevent remote OOM (pasta)

Pull request description:

  ## Issue

  Incoming `QSIGREC` (recovered signature) P2P messages are deserialized and appended to the per-node queue `CSigningManager::pendingRecoveredSigs` after only cheap gating checks (quorum exists/active, dedup by full hash). Actual verification is expensive (BLS) and is deferred to a single worker thread that drains only ~32 unique sign-hash sessions per cycle, whereas ingestion is crypto-free and happens on the network threads in parallel across connections.

  Two problems compound:

  - **No size bound.** `pendingRecoveredSigs` had no per-node or global cap, and the dedup check keys on the full message hash (which includes the signature), so it does not constrain a peer sending many distinct messages. Ingestion structurally outruns the drain.
  - **No cleanup on ban/disconnect.** `RemoveBannedNodeStates` only cleans the separate sig-shares subsystem (`CSigSharesManager::nodeStates`); nothing purged `pendingRecoveredSigs` for banned or disconnected peers.

  Net effect: a peer (or several) can grow the queue faster than it drains, increasing RSS until the node is OOM-killed — a remote, unauthenticated memory-exhaustion DoS. Highest impact against masternodes, which accept many inbound connections and run the LLMQ signing behind ChainLocks / InstantSend.

  ## Fix

  - **Bound the queue** in `VerifyAndProcessRecoveredSig` with a per-node cap (`MAX_PENDING_RECSIGS_PER_NODE = 1000`) and a global cap (`MAX_PENDING_RECSIGS_TOTAL = 10000`). Over-cap messages are dropped **silently, without a misbehaviour score** — verification is single-threaded, so an honest peer can legitimately outrun the drain during a burst, and this is queue-depth backpressure rather than a protocol violation (contrast the per-message structural caps for sig-shares, which do ban).
  - **Purge banned peers' queues** via a new `CSigningManager::RemoveNodesIf(predicate)` (mirroring `CSigSharesManager::RemoveNodesIf`), invoked from `WorkThreadSigning`'s periodic cleanup using `PeerIsBanned`.
  - **Prune drained (empty) node entries** during collection, so the global-cap scan stays cheap and disconnected nodes' queues are reclaimed once drained without waiting for a ban.

  With these caps the attacker-controllable pending queue is bounded to roughly **8 MB** worst case (10,000 entries × ~750 bytes resident per entry; the in-memory `CRecoveredSig` is 696 bytes vs. 193 on the wire, dominated by `CBLSLazySignature`). Previously it was unbounded.

  ## Testing

  - `make check` targets build clean; full `dashd` builds and links.
  - `feature_llmq_signing.py` passes — normal recovered-sig relay across masternodes is unaffected by the caps.
  - `lint-whitespace`, `lint-includes`, `lint-circular-dependencies` clean.

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

ACKs for top commit:
  UdjinM6:
    utACK 5c10539

Tree-SHA512: c51f115c26d03a5174b22e311f0a385f776d7b67578735b3bf342b504b3996448e0d34ebe03a9cc28e2cdf52a3edf1088213300ce64b69ec80c90de6d806a19d
bdca48c refactor: use CountedBucketMap for counting total amount of pending sigs (Konstantin Akimov)
355da45 fix: cap sig-share verification batch by share count (PastaClaw)
08d9533 fix: cap amount of unverified batches by 4 (Konstantin Akimov)
e2862fe fix: the actual cap enforcement for pending signatures (Konstantin Akimov)
4666ef3 test: add regression tests for CountedBucketMap (Konstantin Akimov)
ec15f90 refactor: use dedicated util structure to count internal objects in signing-shares (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented

  Pending incoming LLMQ sig shares are admitted cheaply and verified later by worker threads. Direct and batched share traffic could grow retained pending verification work without per-node/global backpressure.

  This replaces closed PR dashpay#7413 and keeps the same scope: resource/backpressure hardening for LLMQ signing, with no consensus or user-facing behavior change.

  ## What was done?

  - Add per-node and global caps for pending incoming sig shares.
  - Track pending counts explicitly so cap checks do not require repeated scans.
  - Gate `QSIGSHARE` and `QBSIGSHARES` pending admission; over-cap shares are dropped without misbehavior scoring.
  - Clear pending incoming shares when a peer is banned and skip banned node states during collection.
  - Refuse new pending work for already-banned node states.
  - Add unit coverage for cap/drop/count cleanup behavior.

  ## How Has This Been Tested?

  - `./autogen.sh`
  - `./configure --without-gui --disable-bench --disable-fuzz-binary`
  - `make -C src test/test_dash -j$(sysctl -n hw.ncpu)`
  - `src/test/test_dash --run_test=llmq_utils_tests`
  - `git diff --check upstream/develop..HEAD`
  - `COMMIT_RANGE=upstream/develop..HEAD test/lint/lint-whitespace.py`

  ## Breaking Changes

  None.

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  kwvg:
    utACK bdca48c

Tree-SHA512: e2403635a608aab5488dbf10b481a778e735a6e5b065eec5a4cc444818f61e89b3a688f82c14aaceae07fd714432d39c013769a54aa66e31bb8abddb3531aaf3
8ff75e0 llmq: cover QBSIGSHARES aggregate sig-share bound with a testable decoder (PastaClaw)
e51e304 llmq: bound LLMQ signing vector intake (PastaClaw)
c0af156 llmq: bound batched sig-share intake (PastaClaw)

Pull request description:

  Depends on dashpay#7439. Please review only the two command-specific commits here.

  ## Issue being fixed or feature implemented

  LLMQ signing P2P messages previously deserialized peer-controlled vectors before enforcing the existing per-message count limits. This hardens the signing message intake path so oversized counts are rejected before vector materialization.

  This intentionally does not include the QFCOMMITMENT dynamic-bitset fix.

  ## What was done?

  - Use the shared runtime-bounded vector reader for signing message batches in `NetSigning::ProcessMessage`.
  - Bound `CBatchedSigShares::sigShares` declaratively with `LIMITED_VECTOR`.
  - Retain explicit running-total accounting for QBSIGSHARES, where many individually valid batches could exceed the aggregate cap.
  - Ban peers on malformed or oversized signing vectors.
  - Add unit coverage for the exact boundary and oversized batched sig-share vectors.

  The reusable bounded-vector serialization primitives are introduced separately in dashpay#7439.

  ## How Has This Been Tested?

  - `src/test/test_dash --run_test=llmq_utils_tests`
  - `src/test/test_dash --run_test=serialize_tests`
  - `test/lint/lint-whitespace.py`
  - `test/lint/lint-circular-dependencies.py`

  ## Breaking Changes

  None.

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)

ACKs for top commit:
  UdjinM6:
    utACK 8ff75e0

Tree-SHA512: 873fb78ef55749449c0548828dc8709bf0709637732a000bd009d8bba9ed0270e1582eb0020f852c3a8e8bb6d448d1f7b8c0402e271ac42155081a9d13d25bc8
Narrow exception from Dash dashpay#7387 for the v23.1.8 release history.

The full dashpay#7387 merge removes p2p_governance_invs.py after migrating
coverage to unit tests. v23.1.x still ships that functional test, so
only the prerequisite fixture/coverage subset is included here:

- expose RequestedHashCacheSizeForTesting() and
  governance::RELIABLE_PROPAGATION_TIME for unit observation of
  ConfirmInventoryRequest / CheckAndRemove
- add governance_inv_tests coverage for request-cache expiration and
  NetGovernance::Schedule periodic cleanup

Later dashpay#7414/dashpay#7442 build on these fixtures. p2p_governance_invs.py is
retained and updated with dashpay#7442.

Co-Authored-By: Claude <noreply@anthropic.com>
…c requests

5ad1ef1 fix: throttle governance vote sync requests (PastaClaw)

Pull request description:

  ## Issue being fixed or feature implemented
  - Per-object `MNGOVERNANCESYNC` vote requests were not recorded in `NetFulfilledRequestManager`, unlike full governance sync requests.
  - A peer could repeatedly ask for votes for the same governance object and make the node rescan/build the same vote inventory again. This is a low-cost CPU/lock-work concern, not a crash/OOM primitive.

  ## What was done?
  - Add a fulfilled-request key for per-object vote sync requests: `MNGOVERNANCESYNC-votes-<object hash>`.
  - Return early and score repeat requests from the same peer/address.
  - Add unit coverage asserting the per-object request is registered as fulfilled.

  ## How Has This Been Tested?
  - `git diff --check upstream/develop..HEAD`
  - `COMMIT_RANGE=upstream/develop..HEAD test/lint/lint-whitespace.py`
  - Not run locally: `src/test/test_dash --run_test=governance_inv_tests/per_object_vote_sync_is_fulfilled_request_limited` because this fresh worktree has no configured build/test binary.

  ## Breaking Changes
  None.

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  UdjinM6:
    utACK 5ad1ef1

Tree-SHA512: 4d72826ddf3a5e1834281fb53a0c7af724ac97eba7a1a0cc73936641015215511c3cc9c409812cc8168be0523d53f2395d6e871f42ab0fcb36ed67404ab63a9e
…the net-layer per-peer request tracker

0c0ec8c test: cover net-layer per-peer governance inv authorization (UdjinM6)
3f5536c fix(net): authorize governance inv responses via the per-peer request tracker (UdjinM6)

Pull request description:

  ## Issue being fixed or feature implemented

  Governance tracked pending object/vote requests in a global, governance-owned cache
  (`CGovernanceManager::m_requested_hash_time`). `ConfirmInventoryRequest` recorded a hash when we
  decided to fetch an inv, and `AcceptMessage` authorized the eventual response (accepting once, then
  erasing), with entries expiring after `RELIABLE_PROPAGATION_TIME`.

  That cache was unbounded. ~Bounding it with a global cap (as in dashpay#7425) is only a partial fix: the
  cap has to be large, and because it is global, a single peer can fill it (a `MNGOVERNANCESYNC`/INV
  burst) and stall governance-inv intake for everyone until entries expire.~ 18d4725d99cfe527c859621932ed48b436517df4 fixed that.

  The net layer already tracks pending object requests **per peer** in
  `CNodeState::ObjectDownloadState` (`m_object_announced` / `m_object_in_flight`), which is bounded
  per peer by construction (`MAX_PEER_OBJECT_ANNOUNCEMENTS` / `MAX_PEER_OBJECT_IN_FLIGHT`), and
  governance already touches it on receipt (`PeerEraseObjectRequest`). The two trackers are populated
  in lockstep — the same `AlreadyHave` call that populated `m_requested_hash_time` also drove
  `RequestObject`. So the governance-side cache is redundant with state the net layer already keeps,
  and the net copy cannot be flooded by one peer.

  This reuses the net-layer tracker as the authorization source and removes the governance-side cache,
  eliminating the flood surface structurally instead of bounding it.

  ## What was done?

  - Add `PeerManager::PeerRequestedObject(nodeid, inv)`: erases **only** the per-peer
    `m_object_announced` / `m_object_in_flight` for that peer and returns whether the inv was tracked
    (announced by, or requested from, that peer). It deliberately does **not** touch the global
    `g_already_asked_for` / `g_erased_object_requests` markers, so an unsolicited push of a known hash
    from an untracked peer cannot poison request scheduling and suppress a later legitimate fetch.
    `PeerEraseObjectRequest` / `EraseObjectRequest` keep their existing behavior and callers (tx relay
    and the other Dash subsystems) unchanged.
  - Gate `MNGOVERNANCEOBJECT` / `MNGOVERNANCEOBJECTVOTE` acceptance on `PeerRequestedObject` instead of
    `AcceptMessage`.
  - Remove `m_requested_hash_time`, `AcceptMessage`, the `ConfirmInventoryRequest` cache write, the
    `CheckAndRemove` prune, and the obsolete request-cache unit tests. `ConfirmInventoryRequest` keeps
    only its "do we already have it?" role for `AlreadyHave`.

  Acceptance becomes per-peer: an object/vote is accepted only if the sending peer announced it or we
  requested it from that peer. In the inv → getdata gossip model a sender is always a prior announcer,
  and governance objects/votes are only ever sent as `getdata` responses (never pushed unsolicited), so
  legitimate flows are unaffected; genuinely unsolicited pushes are now rejected. This is the same
  INV-tracking requirement the old `AcceptMessage` path already had (`m_requested_hash_time` was only
  populated on an INV), so no legitimately-served response is newly rejected.

  Tests covering the modified code:
  - `src/test/net_tests.cpp` — `peer_requested_object_authorizes_and_erases_per_peer_state`: exercises
    `PeerRequestedObject` for announced-only and in-flight invs, that it erases the per-peer entry, and
    the anti-poisoning property (a rejected unsolicited push must not set `g_erased_object_requests` and
    suppress a later legitimate fetch — driven through the real `SendMessages` getdata scheduler).
  - `src/test/governance_inv_tests.cpp` — `governance_objects_require_peer_announcement_or_request` and
    `governance_votes_require_peer_announcement_or_request`: an announcing peer is accepted (and the
    gate consumes its per-peer tracker entry), an unsolicited peer is rejected without misbehaving, and a
    second independent announcer's duplicate is accepted idempotently.

  ## How Has This Been Tested?

  macOS, `--enable-debug` build.

  - `make -C src test/test_dash -j"$(sysctl -n hw.ncpu)"` — clean build; each commit builds on its own.
  - `src/test/test_dash --run_test=net_tests` (19 cases) — pass, no regression from restoring the
    request-erase functions.
  - `src/test/test_dash --run_test=governance_inv_tests` (3 cases) — pass.
  - `src/test/test_dash --run_test=denialofservice_tests` (5 cases) — pass (object-request machinery).
  - `git diff --check` and `test/lint/lint-whitespace.py` — clean.

  Follow-up (not in this PR): a functional test driving an end-to-end multi-node governance sync plus
  the unsolicited-push-does-not-suppress-fetch case.

  ## Breaking Changes

  None. No consensus, protocol-message, or serialization change. The only behavioral change is that a
  governance object/vote pushed by a peer that neither announced it nor was asked for it is now
  rejected — stricter anti-unsolicited handling on a best-effort, non-consensus gossip path.

  ## Checklist:

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  PastaPastaPasta:
    utACK 0c0ec8c

Tree-SHA512: d93ebf0366e53cdba61cfeb495159e274b9b940d459ae6e4c5f370358d828835fac4d2da3c7765b3c72634980f549eb1bcb7dadcb3e98ebe63219c90b3d634f1
e058152 test: make governance vote fixtures wire-valid (PastaClaw)

Pull request description:

  ## Issue being fixed or feature implemented

  The governance inventory tests merged in dashpay#7442 construct unsigned synthetic votes and round-trip them through the network parser. Develop now requires governance vote signatures to use a structurally valid 65- or 96-byte encoding, so the fixture is rejected as malformed before the authorization behavior under test is reached. This causes six unit-test failures across CI configurations.

  ## What was done?

  Give the synthetic `VOTE_SIGNAL_FUNDING` vote a compact-signature-sized placeholder. The 65-byte encoding matches the real ECDSA voting-key form for funding votes. These tests deliberately use a missing parent object, so vote processing takes the orphan path before cryptographic signature verification; production validation is unchanged.

  ## How Has This Been Tested?

  - Built `src/test/test_dash` in an isolated macOS arm64 worktree using the depends toolchain
  - `./src/test/test_dash --run_test=governance_inv_tests`
  - `./src/test/test_dash --run_test=governance_vote_wire_tests`
  - `git diff --check`
  - Mandatory independent pre-PR review gate: `ship` with no findings

  ## Breaking Changes

  None. Test-only change.

  ## Checklist:

  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone

ACKs for top commit:
  PastaPastaPasta:
    utACK e058152

Tree-SHA512: 5caca1e34ce563444ded42f669ff599f7658c6d1dbea1d541588211ac542ee45f3dd768e158899cb23150c5d45ec8dbcbf412a1472f73a3cc3e72a5ac37636e0
…_main while reading blocks

bd3446f backport: Merge bitcoin#26326: net: don't lock cs_main while reading blocks (PastaClaw)

Pull request description:

  # backport: Merge bitcoin#26326

  ## Upstream backport

  This PR backports bitcoin#26326:

  - Upstream PR: bitcoin#26326
  - Backported commits:
    - bitcoin/bitcoin@613a45c (`net: reduce LOCK(cs_main) scope in GETBLOCKTXN`)
    - bitcoin/bitcoin@75d27fe (`net: reduce LOCK(cs_main) scope in ProcessGetBlockData`)

  ## Issue being fixed or feature implemented

  Block-serving paths in `net_processing` were doing disk reads while holding `cs_main`. A peer can request blocks or block transactions repeatedly, so disk I/O should not extend the validation/global chain lock hold time.

  ## What was done?

  - Capture the requested block's disk position while holding `cs_main`.
  - Release `cs_main` before reading the block from disk.
  - Preserve the upstream `GETBLOCKTXN` invariant that recent blocks cannot be pruned before the post-lock disk read.
  - Apply the sibling upstream `ProcessGetBlockData` behavior: if the post-lock disk read fails because the block was pruned or cannot be loaded, log the condition, disconnect the peer, and return instead of asserting.
  - Capture `CanDirectFetch()` and the current tip while holding `cs_main` and use those cached values after the disk read.

  Dash note: upstream Bitcoin also has a witness-block raw-data fast path in `ProcessGetBlockData`; Dash does not have the corresponding `MSG_WITNESS_BLOCK` path here, so this backport adapts the shared full, filtered, and compact block-serving path.

  ## Backport prerequisite notes

  - `intentional_exclusion: bitcoin#27125` — upstream bitcoin#26326's changed lines use the `BlockManager::ReadBlockFromDisk(..., FlatFilePos)` API introduced by bitcoin#27125. Dash does not have that broader blockstorage refactor, but it already has the equivalent local `node::ReadBlockFromDisk(CBlock&, FlatFilePos, Consensus::Params)` helper, which returns the read block hash. This backport intentionally adapts the changed lines to Dash's existing helper and keeps the post-lock hash validation instead of importing bitcoin#27125 as a prerequisite.

  ## How Has This Been Tested?

  Tested on macOS arm64.

  - `git diff --check upstream/develop...HEAD`
  - `test/lint/lint-whitespace.py`
  - `./autogen.sh`
  - `./configure --without-gui --disable-bench --disable-fuzz-binary`
  - `make -j$(sysctl -n hw.ncpu) src/dashd`

  ## Breaking Changes

  None.

  ## Checklist

  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone

Top commit has no ACKs.

Tree-SHA512: 13e3fed2585e64a45f099a0e0ea482099f399d2133edcde947865a5f2d87a79fa2b328851dbebb7c39c6c4924d8e0d849c9504fd4d41b4c2ff8f2e0d9de2263c
…ate for other peers

Narrow exception from Dash dashpay#7237: include only the Bitcoin Core bitcoin#27608
prerequisite required by later compact-block relay hardening (dashpay#7398).

The full dashpay#7237 v0.26 batch is intentionally not included on v23.1.x.

Co-Authored-By: Claude <noreply@anthropic.com>
…26898, bitcoin#27626, bitcoin#27743, bitcoin#26969, bitcoin#29412, bitcoin#32646, bitcoin#33296)

9d947f3 Merge bitcoin#33296: net: check for empty header before calling FillBlock (pasta)
fe1e05d Merge bitcoin#32646: p2p: Add witness mutation check inside FillBlock (pasta)
c9b3d19 Merge bitcoin#29412: p2p: Don't process mutated blocks (pasta)
5306dfa Merge bitcoin#26969: net, refactor: net_processing, add ProcessCompactBlockTxns (pasta)
6668c2a Merge bitcoin#27743: Unconditionally return when compact block status == READ_STATUS_FAILED (pasta)
cc87ed2 Merge bitcoin#27626: Parallel compact block downloads, take 3 (Claude Code)
622299e Merge bitcoin#26898: fuzz: Add PartiallyDownloadedBlock target (MarcoFalke)

Pull request description:

  ## What

  Backports a stack of Bitcoin Core compact-block relay hardening PRs, including the fix for a remotely-triggerable crash in compact-block reconstruction.

  | PR | Title |
  |----|-------|
  | bitcoin#26898 | fuzz: Add `PartiallyDownloadedBlock` target (+ `FillBlock` returns `READ_STATUS_INVALID` instead of asserting on a null header) |
  | bitcoin#27626 | Parallel compact block downloads, take 3 |
  | bitcoin#27743 | Unconditionally return when compact block status == `READ_STATUS_FAILED` |
  | bitcoin#26969 | net, refactor: add `ProcessCompactBlockTxns` |
  | bitcoin#29412 | p2p: Don't process mutated blocks |
  | bitcoin#32646 | p2p: Add witness mutation check inside `FillBlock` |
  | bitcoin#33296 | net: check for empty header before calling `FillBlock` |

  > bitcoin#27608 (the `from_peer` parameter for `RemoveBlockRequest`), which the stack builds on, is already in `develop` via dashpay#7237, so it is not re-included here.

  ## Why

  The primary motivation is closing a remotely-triggerable abort. A malicious peer acting as the in-flight compact-block source could send a `cmpctblock` followed by two `blocktxn` messages: the first `blocktxn` fails reconstruction (merkle mismatch) and leaves the now header-less `PartiallyDownloadedBlock` in flight (the `READ_STATUS_FAILED` branch does not call `RemoveBlockRequest`), and the second `blocktxn` re-entered `PartiallyDownloadedBlock::FillBlock`, hitting `assert(!header.IsNull())` → `SIGABRT`.

  On this branch that crash is eliminated by bitcoin#26898 (`FillBlock` now returns `READ_STATUS_INVALID` on a null header, which the caller handles by discouraging + removing the block request), with bitcoin#33296's explicit guard as belt-and-suspenders. The stack also brings parallel compact-block downloads (fetch the same block from up to `MAX_CMPCTBLOCKS_INFLIGHT_PER_BLOCK` peers) and a defence-in-depth "don't process mutated blocks" check.

  ## Dash-specific adaptations (non-segwit)

  Dash is not a segwit chain and has none of the witness-commitment machinery (`GetWitnessCommitmentIndex`, `BlockWitnessMerkleRoot`, `HasWitness`, `m_checked_witness_commitment`, `GetWitnessHash`, `DEPLOYMENT_SEGWIT`). The witness-dependent PRs were slimmed accordingly:

  - **bitcoin#29412 `IsBlockMutated`** drops the witness-malleation branch entirely; it reduces to the merkle-root check (`CheckMerkleRoot`, with an `m_checked_merkle_root` cache on `CBlock`) plus the CVE-2012-2459 / 64-byte-transaction check (using `::GetSerializeSize(*tx, PROTOCOL_VERSION)` instead of `TX_NO_WITNESS`). The early `BLOCK`-message check is unconditional (no `prev_block`/`DEPLOYMENT_SEGWIT` gating, which upstream only needs for the witness half).
  - **bitcoin#32646** keeps `FillBlock`'s 2-arg signature (no `segwit_active`); the mutation mock hook becomes `IsBlockMutatedFn`.
  - **bitcoin#33296** drops the `LookupBlockIndex`/`DEPLOYMENT_SEGWIT` lookup.
  - `Misbehaving(Peer&, score, msg)` and `ProcessBlock(..., force_processing)` (no `min_pow_checked`) throughout.
  - Functional tests adapted for the non-witness model: `p2p_compactblocks.py` (the stalling-peer corruption uses a coinbase-content mutation rather than witness data) and a slimmed `p2p_mutated_blocks.py`.

  ## Testing

  - Full build, all 7 commits; every changed TU compiles clean.
  - Unit: full `test_dash` suite passes; `blockencodings_tests` and `validation_tests` specifically exercised.
  - Functional: `p2p_compactblocks.py` (incl. new `test_multiple_blocktxn_response`), `p2p_mutated_blocks.py` (new), `feature_block.py`, `p2p_invalid_block.py` (v1+v2), `p2p_sendheaders.py`, `p2p_unrequested_blocks.py`, `mining_basic.py` — all pass.

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

Top commit has no ACKs.

Tree-SHA512: 66e910053e3576559e687986256a51c07fa7a1da9f3abb01091107e7b98d84d11a28f8a48d9ecf409e0779ab61d3cc77d3436df85d3c960c801b98894731799f
dc74e23 fix: move is_mixing atomic from CCoinJoinClientManager to CWallet (Konstantin Akimov)
8d5876f fix: potential deadlock issue. cs_wallet_manager_map should be locked after cs_wallet (Konstantin Akimov)
268bf4d test: make newkeypool mixing test deterministically reproduce lock cycle (PastaClaw)
5511e29 test: cover newkeypool CoinJoin callback behavior (PastaClaw)
ef7b1f5 fix: don't auto start mixing for locked wallets (Konstantin Akimov)
c7cc8ce refactor: cleanups for CoinJoin client interface (UdjinM6)
f8c71cb fix: keep flag `coinjoin` enabled consistently with user's intention over UI or command line (Konstantin Akimov)
4424487 fix: call InitCoinJoin only wallet is added (Konstantin Akimov)
07d5e3e refactor: remove duplicated methods from CJWalletManagerImpl to prefer interface's (Konstantin Akimov)
d34a4c1 fix: avoid dangling pointers to CJ clients (Konstantin Akimov)
0cb7919 refactor: remove CoinJoinClientImpl wrapper and return bare pointer to interfaces::CoinJoin::Client (Konstantin Akimov)
69b57ab fix: regression test coinjoin_options_tests by enforcing call of `interfaces::MakeCoinJoinLoader` (Konstantin Akimov)
d6f8438 fix: linkage error for non-wallet builds for further changes (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented
  Hold lock during flush operations to prevent TOCTOU race condition.

  getClient() releases cs_wallet_manager_map before returning the raw pointer. If another thread calls removeWallet() concurrently, the unique_ptr is destroyed while flushWallet() still holds a reference, leading to use-after-free. This is inconsistent with removeWallet() which safely erases under the lock.

  Discovered here dashpay#7248 (comment)

  ## What was done?
  Use `doWithClient(name, [](){...})` instead of `getClient(name)->...`

  Mutex `cs_wallet_manager_map` should not be ever locked under `cs_wallet`.

  ## How Has This Been Tested?
  Run unit & functional tests

  ## Breaking Changes
  N/A

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone

ACKs for top commit:
  kwvg:
    utACK dc74e23
  PastaPastaPasta:
    utACK dc74e23

Tree-SHA512: 4030f52aa391c889607d06cdbedc8bbd735ccf628aefd93b37868fb41b0d609cade2229709b8798527cc4d20bc426022767dba776c8a4be5cffe8ec060eb1355
Bump version metadata, refresh manpages, regenerate the example dash.conf
with stable -par/-parbls help text (via dashpay#7394), update Flatpak metainfo,
and archive current release notes.

Co-Authored-By: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bcdb34cbf6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

{
// Enablement will be re-evaluated when a wallet is added or removed
CCoinJoinClientOptions::SetEnabled(false);
CCoinJoinClientOptions::SetEnabled(gArgs.GetBoolArg("-enablecoinjoin", true));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include util/system before using gArgs

In wallet-enabled builds this translation unit now references gArgs, but none of its includes declare that global (it is declared by util/system.h). Compiling coinjoin/interfaces.cpp therefore fails as soon as the new constructor is built; add the missing include or pass the parsed option in from a header that already exposes ArgsManager.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does compile in wallet-enabled builds. I rebuilt dashd and test_dash with this exact source tree; src/coinjoin/libbitcoin_wallet_a-interfaces.o was produced successfully, and the generated .deps/libbitcoin_wallet_a-interfaces.Po explicitly includes util/system.h through the current include graph, so gArgs is declared before use. A direct include could reduce transitive-include reliance, but it is not a P1 build failure and does not justify restarting the release CI/history here.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final validation — Codex + Sonnet

The exact release head was revalidated against source, including the CoinJoin gArgs include chain, governance fulfilled-request behavior, and adjacent governance test commits. No in-scope correctness or review-blocking issue remains; the proposed comments are either speculative maintenance advice or conflict with preserving separately reviewed release-aggregate provenance.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed), gpt-5.6-sol — dash-core-commit-history (completed)
  • Verifier: gpt-5.6-sol — final-verifier (fallback)
  • Sonnet reviewers: claude-sonnet-5 — general (completed), claude-sonnet-5 — dash-core-commit-history (completed)

Note: GitHub does not allow PastaClaw to submit an approval or request changes on their own PR, so the canonical verifier result is transported as a COMMENT review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants