Skip to content

refactor: add bounded vector deserialization - #7439

Merged
PastaPastaPasta merged 1 commit into
dashpay:developfrom
thepastaclaw:serialize/limited-vector
Jul 10, 2026
Merged

refactor: add bounded vector deserialization#7439
PastaPastaPasta merged 1 commit into
dashpay:developfrom
thepastaclaw:serialize/limited-vector

Conversation

@thepastaclaw

@thepastaclaw thepastaclaw commented Jul 10, 2026

Copy link
Copy Markdown

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:

Reviewing #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:

  • 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

@kwvg kwvg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Consider pr_7416 (especially f80422c1 9cfe87c and 6b47508d split out for review, should be squashed in final iteration)

@thepastaclaw

Copy link
Copy Markdown
Author

Addressed in bf865ac126d. I ported the substance of f80422c1 and 6b47508d onto this branch: restored the normal CompactSize range check, documented the unrelated serialization-exception behavior, and updated the focused boundary tests. The changes are squashed into the existing single PR commit as requested; unrelated commits from pr_7416 were not brought over.

Validation: make -j15 -C src test/test_dash, ./src/test/test_dash --run_test=serialize_tests, the focused unserialize_vector_with_max_size case, and git diff --check all pass.

Introduce a runtime and a compile-time bounded reader for CompactSize-prefixed
vectors so downstream call sites can reject grossly oversized wire counts
before any element decode or allocation. Both primitives preserve the ordinary
std::vector wire format — the limit is a deserialization safety property.

- Factor the batched vector element-decode loop out of VectorFormatter::Unser
  into detail::UnserializeVectorContents<Formatter>, and reuse it from both
  new primitives so the DoS-resistant 5 MiB batching is not duplicated.

- UnserializeVectorWithMaxSize<Formatter>(stream, vec, max_size) reads the
  CompactSize prefix and rejects a count above the caller's max_size before
  any element decode, returning false without consuming an element. A count
  above the generic MAX_SIZE cap throws from ReadCompactSize as malformed;
  since a realistic max_size is far below MAX_SIZE (you reach
  MAX_PROTOCOL_MESSAGE_LENGTH long before MAX_SIZE), the caller's own limit is
  the gate that fires in practice. The helper may otherwise throw on an
  unrelated serialization failure.

- LimitedVectorFormatter<Limit, ElemFormatter=DefaultFormatter> is the
  READWRITE-friendly wrapper; a LIMITED_VECTOR(obj, n) macro mirrors the
  existing LIMITED_STRING convention. Ser delegates to VectorFormatter so
  the write side stays unbounded and interoperable with the standard reader.

Cover the primitives with focused unit tests: within/exact/over/zero-limit
boundaries, wire-format identity vs the ordinary vector encoding, that Ser
remains unbounded, and — via a CountingFormatter that tallies Unser calls —
that a MAX_SIZE wire count is rejected before any element decode occurs, while
counts above MAX_SIZE throw at the CompactSize level.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thepastaclaw
thepastaclaw force-pushed the serialize/limited-vector branch from bf865ac to af6a0fd Compare July 10, 2026 13:41
@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The serialization framework now shares batched vector allocation and element decoding through a helper. It adds runtime maximum-size decoding and a compile-time LimitedVectorFormatter, exposed through the LIMITED_VECTOR macro. Oversized vectors are rejected before element decoding, while serialization retains the standard vector wire format. Tests cover size boundaries, malformed prefixes, failure behavior, and compatibility with unbounded vector readers.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant LimitedVectorFormatter
  participant Stream
  participant UnserializeVectorContents
  participant ElemFormatter
  Caller->>LimitedVectorFormatter: unserialize bounded vector
  LimitedVectorFormatter->>Stream: read CompactSize element count
  Stream-->>LimitedVectorFormatter: element count
  alt count <= Limit
    LimitedVectorFormatter->>UnserializeVectorContents: decode vector elements
    UnserializeVectorContents->>ElemFormatter: unserialize each element
    ElemFormatter-->>UnserializeVectorContents: decoded element
    UnserializeVectorContents-->>LimitedVectorFormatter: populated vector
  else count > Limit
    LimitedVectorFormatter-->>Caller: throw length-limit failure
  end
Loading

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed It clearly summarizes the main change: adding bounded vector deserialization.
Description check ✅ Passed It directly describes the implemented bounded vector deserialization features.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@PastaPastaPasta
PastaPastaPasta marked this pull request as ready for review July 10, 2026 15:23
@thepastaclaw

thepastaclaw commented Jul 10, 2026

Copy link
Copy Markdown
Author

✅ Review complete (commit af6a0fd)

@knst knst left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM af6a0fd

@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.

Code Review

Clean, well-scoped serialization refactor adding bounded-vector deserialization primitives with byte-identical wire format and thorough unit coverage. No blocking issues: the count is validated before any allocation, the Ser side stays unbounded for wire compatibility, and CompactSize range handling is correct. One legitimate but practically-inert nitpick about formatter construction-order reordering in the refactor.

💬 1 nitpick(s)

Source: reviewers opus (general, dash-core-commit-history) and gpt-5.5 (general, dash-core-commit-history); verifier opus.

Comment thread src/serialize.h
formatter.Unser(s, v.back());
}
}
detail::UnserializeVectorContents<Formatter>(s, v, ReadCompactSize(s));

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.

💬 Nitpick: Refactor reorders Formatter construction relative to clear/count-read

The pre-refactor VectorFormatter::Unser default-constructed Formatter formatter; before calling v.clear() and ReadCompactSize(s). The shared helper now constructs the Formatter inside detail::UnserializeVectorContents, after the vector is cleared and the count has been consumed from the stream. For a Formatter whose default constructor has side effects or can throw, a construction failure would now leave the stream advanced past the count and the destination vector cleared — an observable difference in a change advertised as behavior-preserving. This is inert for every formatter in the tree today (all are stateless empty structs with trivial default constructors), so it is only worth noting for the refactor's stated invariant, not a defect. If you want to preserve the exact original ordering, construct the Formatter in VectorFormatter::Unser and pass it into the shared helper by reference.

source: ['codex']

@PastaPastaPasta
PastaPastaPasta merged commit 9474fc5 into dashpay:develop Jul 10, 2026
47 checks passed
@UdjinM6 UdjinM6 added this to the 24 milestone Jul 10, 2026
PastaPastaPasta added a commit that referenced this pull request Jul 10, 2026
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 #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 #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
PastaPastaPasta added a commit that referenced this pull request Jul 10, 2026
a286e0e fix: bound quorum data response vectors (PastaClaw)

Pull request description:

  Depends on #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 #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
PastaPastaPasta added a commit that referenced this pull request Jul 10, 2026
8f0b813 fix(governance): bound vote signature deserialization (PastaClaw)

Pull request description:

  Uses the shared bounded-vector deserialization primitive merged in #7439.

  ## Motivation

  Governance vote signatures were deserialized through the generic byte-vector path. A peer could declare a very large signature length, causing allocation before the stream reported truncation. The outer message-processing catch did not score or disconnect the peer, allowing repeated malformed messages.

  ## Changes

  - bound network governance-vote signature reads to 96 bytes before allocation
  - require one of the two structurally valid encodings: 65-byte compact ECDSA or 96-byte BLS
  - score malformed or truncated governance vote messages with 100 misbehavior points
  - preserve disk, hash, and outbound serialization behavior
  - add focused unit coverage

  ## Testing

  - `./src/test/test_dash --run_test=governance_vote_wire_tests` (4/4 tests)
  - `./src/test/test_dash --run_test=serialize_tests` (10/10 tests)
  - `test/lint/lint-python.py`
  - `git diff --check upstream/develop...HEAD`

ACKs for top commit:
  knst:
    utACK 8f0b813

Tree-SHA512: 5cd804ffb410f47936615d230f5a5ebe4b2b4e1dd85455acb42df37fb65c0397f05c38704900a3ecf5d2729e157d03b298c4a7774b7764b3830cb57273a724cc
PastaPastaPasta added a commit that referenced this pull request Jul 13, 2026
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 #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 #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
PastaPastaPasta added a commit that referenced this pull request Jul 20, 2026
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 #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 #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
thepastaclaw pushed a commit to thepastaclaw/dash that referenced this pull request Jul 22, 2026
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
thepastaclaw pushed a commit to thepastaclaw/dash that referenced this pull request Jul 22, 2026
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
thepastaclaw pushed a commit to thepastaclaw/dash that referenced this pull request Jul 22, 2026
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
thepastaclaw pushed a commit to thepastaclaw/dash that referenced this pull request Jul 22, 2026
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
thepastaclaw pushed a commit to thepastaclaw/dash that referenced this pull request Jul 22, 2026
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
thepastaclaw pushed a commit to thepastaclaw/dash that referenced this pull request Jul 22, 2026
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
thepastaclaw pushed a commit to thepastaclaw/dash that referenced this pull request Jul 22, 2026
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
thepastaclaw pushed a commit to thepastaclaw/dash that referenced this pull request Jul 22, 2026
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
thepastaclaw pushed a commit to thepastaclaw/dash that referenced this pull request Jul 22, 2026
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
thepastaclaw pushed a commit to thepastaclaw/dash that referenced this pull request Jul 22, 2026
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
thepastaclaw added a commit to thepastaclaw/dash that referenced this pull request Jul 28, 2026
…lization

8f0b813 fix(governance): bound vote signature deserialization (PastaClaw)

Pull request description:

  Uses the shared bounded-vector deserialization primitive merged in dashpay#7439.

  ## Motivation

  Governance vote signatures were deserialized through the generic byte-vector path. A peer could declare a very large signature length, causing allocation before the stream reported truncation. The outer message-processing catch did not score or disconnect the peer, allowing repeated malformed messages.

  ## Changes

  - bound network governance-vote signature reads to 96 bytes before allocation
  - require one of the two structurally valid encodings: 65-byte compact ECDSA or 96-byte BLS
  - score malformed or truncated governance vote messages with 100 misbehavior points
  - preserve disk, hash, and outbound serialization behavior
  - add focused unit coverage

  ## Testing

  - `./src/test/test_dash --run_test=governance_vote_wire_tests` (4/4 tests)
  - `./src/test/test_dash --run_test=serialize_tests` (10/10 tests)
  - `test/lint/lint-python.py`

Tree-SHA512: backported to v23.1.x by cherry-picking 8f0b813 (applies cleanly).

Backport note for v23.1.8
-------------------------

This was missing from the original v23.1.8 branch while its test follow-up
 dashpay#7450 ("test: make governance vote fixtures wire-valid", 915566d) was
already included. That ordering was inverted: dashpay#7450 exists solely to adapt the
 dashpay#7442 governance-inv fixtures to the bound that dashpay#7440 introduces.

Verified by removing dashpay#7450's SetSignature() line and rebuilding: without dashpay#7440
present the fixtures pass regardless, and re-adding dashpay#7440 reproduces exactly
the six governance_inv_tests failures dashpay#7450's description cites. So the branch
was shipping the compensating test change for a hardening fix it did not have,
leaving CGovernanceVote::vchSig unbounded on the network path.

The prerequisite dashpay#7439 (LIMITED_VECTOR) is already present via 099b99d, as
are the sibling bounding backports dashpay#7416/dashpay#7418/dashpay#7419/dashpay#7438/dashpay#7444, so this
restores the intended set rather than widening release scope.

Reported-by: UdjinM6
Co-Authored-By: Claude <noreply@anthropic.com>
PastaPastaPasta added a commit to PastaPastaPasta/dash that referenced this pull request Jul 29, 2026
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
(cherry picked from commit 9474fc5)
PastaPastaPasta added a commit to PastaPastaPasta/dash that referenced this pull request Jul 29, 2026
…lization

8f0b813 fix(governance): bound vote signature deserialization (PastaClaw)

Pull request description:

  Uses the shared bounded-vector deserialization primitive merged in dashpay#7439.

  ## Motivation

  Governance vote signatures were deserialized through the generic byte-vector path. A peer could declare a very large signature length, causing allocation before the stream reported truncation. The outer message-processing catch did not score or disconnect the peer, allowing repeated malformed messages.

  ## Changes

  - bound network governance-vote signature reads to 96 bytes before allocation
  - require one of the two structurally valid encodings: 65-byte compact ECDSA or 96-byte BLS
  - score malformed or truncated governance vote messages with 100 misbehavior points
  - preserve disk, hash, and outbound serialization behavior
  - add focused unit coverage

  ## Testing

  - `./src/test/test_dash --run_test=governance_vote_wire_tests` (4/4 tests)
  - `./src/test/test_dash --run_test=serialize_tests` (10/10 tests)
  - `test/lint/lint-python.py`
  - `git diff --check upstream/develop...HEAD`

ACKs for top commit:
  knst:
    utACK 8f0b813

Tree-SHA512: 5cd804ffb410f47936615d230f5a5ebe4b2b4e1dd85455acb42df37fb65c0397f05c38704900a3ecf5d2729e157d03b298c4a7774b7764b3830cb57273a724cc
(cherry picked from commit 24c0ead)
PastaPastaPasta added a commit to PastaPastaPasta/dash that referenced this pull request Jul 29, 2026
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
(cherry picked from commit 9474fc5)
PastaPastaPasta added a commit to PastaPastaPasta/dash that referenced this pull request Jul 29, 2026
…lization

8f0b813 fix(governance): bound vote signature deserialization (PastaClaw)

Pull request description:

  Uses the shared bounded-vector deserialization primitive merged in dashpay#7439.

  ## Motivation

  Governance vote signatures were deserialized through the generic byte-vector path. A peer could declare a very large signature length, causing allocation before the stream reported truncation. The outer message-processing catch did not score or disconnect the peer, allowing repeated malformed messages.

  ## Changes

  - bound network governance-vote signature reads to 96 bytes before allocation
  - require one of the two structurally valid encodings: 65-byte compact ECDSA or 96-byte BLS
  - score malformed or truncated governance vote messages with 100 misbehavior points
  - preserve disk, hash, and outbound serialization behavior
  - add focused unit coverage

  ## Testing

  - `./src/test/test_dash --run_test=governance_vote_wire_tests` (4/4 tests)
  - `./src/test/test_dash --run_test=serialize_tests` (10/10 tests)
  - `test/lint/lint-python.py`
  - `git diff --check upstream/develop...HEAD`

ACKs for top commit:
  knst:
    utACK 8f0b813

Tree-SHA512: 5cd804ffb410f47936615d230f5a5ebe4b2b4e1dd85455acb42df37fb65c0397f05c38704900a3ecf5d2729e157d03b298c4a7774b7764b3830cb57273a724cc
(cherry picked from commit 24c0ead)
PastaPastaPasta added a commit to PastaPastaPasta/dash that referenced this pull request Jul 29, 2026
…lization

8f0b813 fix(governance): bound vote signature deserialization (PastaClaw)

Pull request description:

  Uses the shared bounded-vector deserialization primitive merged in dashpay#7439.

  ## Motivation

  Governance vote signatures were deserialized through the generic byte-vector path. A peer could declare a very large signature length, causing allocation before the stream reported truncation. The outer message-processing catch did not score or disconnect the peer, allowing repeated malformed messages.

  ## Changes

  - bound network governance-vote signature reads to 96 bytes before allocation
  - require one of the two structurally valid encodings: 65-byte compact ECDSA or 96-byte BLS
  - score malformed or truncated governance vote messages with 100 misbehavior points
  - preserve disk, hash, and outbound serialization behavior
  - add focused unit coverage

  ## Testing

  - `./src/test/test_dash --run_test=governance_vote_wire_tests` (4/4 tests)
  - `./src/test/test_dash --run_test=serialize_tests` (10/10 tests)
  - `test/lint/lint-python.py`
  - `git diff --check upstream/develop...HEAD`

ACKs for top commit:
  knst:
    utACK 8f0b813

Tree-SHA512: 5cd804ffb410f47936615d230f5a5ebe4b2b4e1dd85455acb42df37fb65c0397f05c38704900a3ecf5d2729e157d03b298c4a7774b7764b3830cb57273a724cc
(cherry picked from commit 24c0ead)
PastaPastaPasta added a commit to PastaPastaPasta/dash that referenced this pull request Jul 29, 2026
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
(cherry picked from commit 9474fc5)
PastaPastaPasta added a commit to PastaPastaPasta/dash that referenced this pull request Jul 29, 2026
…lization

8f0b813 fix(governance): bound vote signature deserialization (PastaClaw)

Pull request description:

  Uses the shared bounded-vector deserialization primitive merged in dashpay#7439.

  ## Motivation

  Governance vote signatures were deserialized through the generic byte-vector path. A peer could declare a very large signature length, causing allocation before the stream reported truncation. The outer message-processing catch did not score or disconnect the peer, allowing repeated malformed messages.

  ## Changes

  - bound network governance-vote signature reads to 96 bytes before allocation
  - require one of the two structurally valid encodings: 65-byte compact ECDSA or 96-byte BLS
  - score malformed or truncated governance vote messages with 100 misbehavior points
  - preserve disk, hash, and outbound serialization behavior
  - add focused unit coverage

  ## Testing

  - `./src/test/test_dash --run_test=governance_vote_wire_tests` (4/4 tests)
  - `./src/test/test_dash --run_test=serialize_tests` (10/10 tests)
  - `test/lint/lint-python.py`
  - `git diff --check upstream/develop...HEAD`

ACKs for top commit:
  knst:
    utACK 8f0b813

Tree-SHA512: 5cd804ffb410f47936615d230f5a5ebe4b2b4e1dd85455acb42df37fb65c0397f05c38704900a3ecf5d2729e157d03b298c4a7774b7764b3830cb57273a724cc
(cherry picked from commit 24c0ead)
PastaPastaPasta added a commit that referenced this pull request Jul 30, 2026
24920a0 chore: prepare v23.1.8 release (pasta)
2194248 Merge #7348: fix: penalize oversized notfound messages (pasta)
f5c72c3 Merge #7347: fix: punish invalid dstx messages (pasta)
550caf7 Merge #7465: fix(qt): handle pixel-sized fonts when scaling widgets (pasta)
e203710 Merge #7419: fix(net): bound CoinJoin message vector intake (pasta)
5f5b960 Merge #7418: fix(net): bound signing message vector intake (Pasta)
7cc2cca Merge #7450: test: make governance vote fixtures wire-valid (Pasta)
f011c80 Merge #7440: fix(net): bound governance vote signature deserialization (Pasta)
4b4d96a Merge #7442: fix(net): authorize governance inv responses via the net-layer per-peer request tracker (Pasta)
f855b13 Merge #7444: fix(net): bound bloom message vectors before allocation (Pasta)
5b5c6fb Merge #7415: fix: bound pending sig share queue (Pasta)
9bbe808 Merge #7416: fix(net): bound quorum data response vectors (Pasta)
da42f50 Merge #7424: fix: bound ChainLock seen cache (Pasta)
5b310df Merge #7438: fix: bound SPORK signature deserialization (Pasta)
e118d0c Merge #7259: fix: dangling point to cj client (Pasta)
9921621 Merge #7439: refactor: add bounded vector deserialization (Pasta)
89bdf7c Merge #7414: fix(net): throttle per-object governance vote sync requests (Pasta)
44c396d Merge #7402: fix: bound pending recovered sig queue to prevent remote OOM (Pasta)
05cfe27 Merge #7351: fix: limit signing share sessions per peer (pasta)
3ef3a5b Merge #7408: fix: bound DKG contribution blob intake (pasta)
0ea6532 Merge #7387: test: migrate governance inv cache coverage to unit tests (Pasta)
8ffdf7f Merge #7398: backport: compact block relay hardening (bitcoin#26898, bitcoin#27626, bitcoin#27743, bitcoin#26969, bitcoin#29412, bitcoin#32646, bitcoin#33296) (Pasta)
2915142 backport: bitcoin#27608 - p2p: Avoid prematurely clearing download state for other peers (PastaClaw)
90b5473 Merge #7396: fix: run of circular-dependencies with python3.15 (Pasta)
b003cdc Merge #7395: ci: update GitHub Actions pins for Node 24 (pasta)
97c3dd1 Merge #7394: fix: stabilize par help text in manpages (pasta)
8f8616b Merge #7372: backport: bitcoin#32693: depends: fix cmake compatibility error for freetype (pasta)
48f72be Merge #7360: fix: empty platformP2PPort deprecated field in protx listdiff results (pasta)
a8cccff Merge #7298: fix(qt): keep PoSe score visible when hiding banned masternodes (pasta)

Pull request description:

  Release PR for Dash Core v23.1.8, a patch release on top of v23.1.7.

  Fast-forwards from `v23.1.x` (currently at `chore: prepare v23.1.7 release`), 29 commits, no merge commits, no conflicts.

  ## Contents

  Backports of PRs already reviewed and merged on `develop`:

  `#7259` `#7347` `#7348` `#7351` `#7298` `#7360` `#7372` `#7387` `#7394` `#7395` `#7396` `#7398` `#7402` `#7408` `#7414` `#7415` `#7416` `#7418` `#7419` `#7424` `#7438` `#7439` `#7440` `#7442` `#7444` `#7450` `#7465`

  Plus `backport: bitcoin#27608`, a single commit taken from Dash #7237 because #7398's compact-block hardening depends on it. The rest of that v0.26 batch is intentionally not included on v23.1.x. The commit is byte-identical to its reviewed counterpart inside #7237.

  And release preparation: version bump, regenerated man pages, release notes, archived 23.1.7 notes.

  ## Note for reviewers: this branch was rebuilt

  An earlier revision of this PR was discarded and the branch rebuilt from scratch. Review comments on the previous revision point at commits that no longer exist, though the feedback itself was carried over (see below).

  The reason: several commits titled `Merge #NNNN` in the earlier revision contained substantial code that exists nowhere upstream — apparently written from a description of each PR rather than ported from its diff. For example, `feature_llmq_simplepose.py` is byte-identical between v23.1.7 and `develop`, yet the earlier `Merge #7408` rewrote 66 lines of it; `test/functional/p2p_governance_invs.py` does not exist on `develop` at all, yet had grown from 62 to 148 lines.

  That mislabeling matters because a commit titled `Merge #NNNN` invites less scrutiny, not more. It also had consequences: the earlier revision was **missing #7440 entirely**, and contained eleven consecutive commits that did not compile (code written against newer upstream APIs this branch does not have — `Misbehaving(Peer&)`, and `PeerIsBanned` used five commits before it was declared).

  Every commit on this branch has now been diffed against its upstream merge commit. Where a backport differs, it is because v23.1.x predates an upstream refactor and the change had to be applied to the pre-refactor file — for example #7418 and #7438 patch `signing_shares.cpp` / `spork.cpp` where upstream patches `net_signing.cpp` / `net_processing.cpp`.

  ## Dropped from this branch

  - **#7350** (`net: don't lock cs_main while reading blocks`) — dropped on review feedback. It is a 110-line lock-structure refactor of `ProcessGetBlockData` with no measured benefit, and it would add avoidable churn to the eventual master→develop merge-back. Nothing on this branch depends on it: #7398's compact-block work precedes it, and the remaining 14 commits replay with zero conflicts once it is removed. Thanks @knst.

  ## Added after the initial review pass

  - **#7351** (`fix: limit signing share sessions per peer`) — cherry-picked as a single
    commit and placed before #7402, matching upstream's merge order. The include block
    additionally carries `<ranges>`: upstream's diff adds only `<algorithm>` because develop
    already had it, whereas v23.1.x did not and the backported `GetSessionCount()` /
    `GetAnnouncementSessionCount()` use `std::ranges::count_if`.
  - **#7465** (`fix(qt): handle pixel-sized fonts when scaling widgets`) — cherry-picked from
    the five upstream commits. `optiontests.cpp` additionally includes `qt/guiutil_font.h`,
    because `fontsLoaded()` and `updateFonts()` are declared there on v23.1.x while develop
    declares them in `qt/guiutil.h`, which is all the upstream test includes.

  Two further backports were added later and applied without any adaptation --
  their diffs are byte-for-byte identical to upstream:

  - **#7347** (`fix: punish invalid dstx messages`)
  - **#7348** (`fix: penalize oversized notfound messages`)

  ## Adaptations worth flagging

  - **#7360** — upstream gates `platformP2PPort` / `platformHTTPPort` in `protx listdiff` behind `IsServiceDeprecatedRPCEnabled()`. On 23.x those deprecated fields are deliberately not enforced through gating (see `bbcd9d543e6`), so shipping the gate as-is would silently drop two fields that v23.1.7 always returned. Changed to `if (true)` with a comment, per review feedback, keeping the block aligned with `develop`. The substantive fix from #7360 — reading the live port from `netInfo` instead of the always-zero scalar — is retained.

  - **#7415** — the pending-map caps (`MAX_PENDING_SIG_SHARES_PER_NODE`, `MAX_PENDING_SIG_SHARES_TOTAL`) are backported. The additional bound upstream places on batches awaiting verification is not, because it guards a condition that does not exist here: upstream's dispatcher pushes one task per batch inside an inner loop, whereas v23.1.x pushes a single looping worker per 10 ms tick. There is no unbounded task queue to bound.

  - **Man pages** — regenerated without the `lock` debug category, which only exists under `DEBUG_LOCKCONTENTION` and so is absent from release binaries. Thanks @UdjinM6 for catching this.

  ## Known CI failure

  macOS jobs are expected to fail. `actions/upload-artifact@v6` rejects filenames containing `:`, and the Xcode SDK ships Perl man pages with `::` in the name. A release-branch-only workaround existed on the earlier revision but was dropped as it corresponds to no upstream PR. This is accepted for this release.

  ## Testing

  - Every commit through #7465 compiles individually (verified for 27 of the 29; the three additions below were verified at the tip) — verified individually, not just at the tip.
  - Full build clean; no new warnings.
  - Unit tests pass.
  - Functional tests pass: `feature_llmq_signing` (both variants), `feature_llmq_chainlocks`, `feature_llmq_dkgerrors`, `feature_llmq_is_cl_conflicts`, `p2p_instantsend`, `feature_dip3_deterministicmns` (both wallet types), `rpc_coinjoin`.
  - Qt unit tests pass (32 cases, run under the `cocoa` platform plugin so the pixel-sized
    font regression from #7465 actually executes rather than self-skipping).
  - Lint: one pre-existing `lint-cppcheck-dash` failure, identical on v23.1.7, in files this branch does not touch.

Top commit has no ACKs.

Tree-SHA512: 0fa469c9a33820aa85fbb8b90c5877409d09490f746f1300b05ceda470a600765f900bec42e5aad5d90a2d46b44e28c20e44dc4a8fe719553a072298069eaec4
@UdjinM6 UdjinM6 modified the milestones: 24, 23.1.8 Jul 30, 2026
PastaPastaPasta added a commit that referenced this pull request Aug 3, 2026
728f505 doc: record the v23.1.8 critical crash fixes in the release notes (pasta)
ca56af8 fix(llmq): reject parentless quorum base blocks instead of terminating (pasta)
a801d3f fix(llmq): reject unregistered LLMQ types from qsigshare before quorum lookup (pasta)
6c1f611 fix: skip already-removed conflicts when a ProTx key change clears the mempool (pasta)
24920a0 chore: prepare v23.1.8 release (pasta)
2194248 Merge #7348: fix: penalize oversized notfound messages (pasta)
f5c72c3 Merge #7347: fix: punish invalid dstx messages (pasta)
550caf7 Merge #7465: fix(qt): handle pixel-sized fonts when scaling widgets (pasta)
e203710 Merge #7419: fix(net): bound CoinJoin message vector intake (pasta)
5f5b960 Merge #7418: fix(net): bound signing message vector intake (Pasta)
7cc2cca Merge #7450: test: make governance vote fixtures wire-valid (Pasta)
f011c80 Merge #7440: fix(net): bound governance vote signature deserialization (Pasta)
4b4d96a Merge #7442: fix(net): authorize governance inv responses via the net-layer per-peer request tracker (Pasta)
f855b13 Merge #7444: fix(net): bound bloom message vectors before allocation (Pasta)
5b5c6fb Merge #7415: fix: bound pending sig share queue (Pasta)
9bbe808 Merge #7416: fix(net): bound quorum data response vectors (Pasta)
da42f50 Merge #7424: fix: bound ChainLock seen cache (Pasta)
5b310df Merge #7438: fix: bound SPORK signature deserialization (Pasta)
e118d0c Merge #7259: fix: dangling point to cj client (Pasta)
9921621 Merge #7439: refactor: add bounded vector deserialization (Pasta)
89bdf7c Merge #7414: fix(net): throttle per-object governance vote sync requests (Pasta)
44c396d Merge #7402: fix: bound pending recovered sig queue to prevent remote OOM (Pasta)
05cfe27 Merge #7351: fix: limit signing share sessions per peer (pasta)
3ef3a5b Merge #7408: fix: bound DKG contribution blob intake (pasta)
0ea6532 Merge #7387: test: migrate governance inv cache coverage to unit tests (Pasta)
8ffdf7f Merge #7398: backport: compact block relay hardening (bitcoin#26898, bitcoin#27626, bitcoin#27743, bitcoin#26969, bitcoin#29412, bitcoin#32646, bitcoin#33296) (Pasta)
2915142 backport: bitcoin#27608 - p2p: Avoid prematurely clearing download state for other peers (PastaClaw)
90b5473 Merge #7396: fix: run of circular-dependencies with python3.15 (Pasta)
b003cdc Merge #7395: ci: update GitHub Actions pins for Node 24 (pasta)
97c3dd1 Merge #7394: fix: stabilize par help text in manpages (pasta)
8f8616b Merge #7372: backport: bitcoin#32693: depends: fix cmake compatibility error for freetype (pasta)
48f72be Merge #7360: fix: empty platformP2PPort deprecated field in protx listdiff results (pasta)
a8cccff Merge #7298: fix(qt): keep PoSe score visible when hiding banned masternodes (pasta)

Pull request description:

  ## Issue being fixed or feature implemented
  Merges master (v23.1.8) back into develop so develop carries the v23.1.8 release and, most importantly, the three remotely reachable crash fixes shipped on top of it. Without this, develop-based nodes remain vulnerable to all three crashes.

  ## What was done?
  Merged `upstream/master` into `develop`. The genuinely new payload is:

  - **Three remote-crash fixes** (with their regression tests):
    - skip already-removed conflicts when a ProTx key change clears the mempool (`txmempool.cpp`)
    - reject unregistered LLMQ types from `qsigshare` before quorum lookup (`llmq/blockprocessor.cpp`, `llmq/quorumsman.cpp`)
    - reject parentless quorum base blocks instead of terminating (`llmq/commitment.cpp`, `llmq/utils.cpp`, `validation.{cpp,h}`)
  - **Release bookkeeping**: v23.1.8 release notes, archived 23.1.7 notes, flatpak release entry, regenerated v23.1.8 man pages, and the `configure.ac` version bump (develop keeps `IS_RELEASE=false` and its own configure flags).

  Everything else on master since the last merge (the v23.1.x security-hardening train, #7259#7465 and #7398) was dual-merged and already exists on develop as its own merge commits, so all conflicts from those files resolve to develop's side. Deliberate adaptations, itemized in the merge commit message:

  - The `qsigshare` LLMQ-type gate is ported into develop's `net_signing.cpp` and the parentless-base DKG check into develop's `net_dkg.cpp` (develop moved message processing out of `signing_shares.cpp`/`dkgsessionmgr.cpp`).
  - The defence-in-depth `find()` lookup is additionally applied to `CQuorumManager::GetCachedMutableQuorum()`, a develop-only method reached with a wire-supplied LLMQ type from the `QDATA` handler.
  - The new mempool regression test is adapted to develop's `CreateProRegTx`/`CreateProUpServTx`/`CreateProUpRevTx` helper signatures and `MemPoolOptionsForTest`.
  - `src/active/quorums.cpp` (deleted by develop's ActiveContext refactor) stays deleted; develop's #7416 equivalent already covers the new layout.

  ## How Has This Been Tested?
  Built with `--enable-debug` on macOS (arm64). Ran the new/extended suites — `evo_deterministicmns_tests`, `llmq_invalid_type_tests`, `evo_utils_tests` — plus adjacent ones (`llmq_signing_tests`, `llmq_dkg_tests`, `llmq_blockprocessor_tests`, `llmq_commitment_tests`, `llmq_utils_tests`, `mempool_tests`, `validation_tests`): all pass. The three regression tests abort the process on unpatched code.

  ## Breaking Changes
  None. `ChainstateManager::IsQuorumTypeEnabled()` loosens its `pindexPrev` parameter from `gsl::not_null` to a plain pointer (null now returns false instead of aborting); all existing callers are unaffected.

  ## 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
  - [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:
  UdjinM6:
    utACK e15bb64

Tree-SHA512: 0897f0e1267c4083ef505127ceb18309e310caf36a066674ea298d66836afb86c762532e7cc68d6c0761142b2e5b291b497496b7e82d0f7ce60549761c0ebdd8
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.

5 participants