refactor: add bounded vector deserialization - #7439
Conversation
177b53f to
bf865ac
Compare
|
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: |
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>
bf865ac to
af6a0fd
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
WalkthroughThe serialization framework now shares batched vector allocation and element decoding through a helper. It adds runtime maximum-size decoding and a compile-time 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
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
✅ Review complete (commit af6a0fd) |
There was a problem hiding this comment.
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.
| formatter.Unser(s, v.back()); | ||
| } | ||
| } | ||
| detail::UnserializeVectorContents<Formatter>(s, v, ReadCompactSize(s)); |
There was a problem hiding this comment.
💬 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']
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
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
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
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
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
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
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
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
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
…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>
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)
…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)
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)
…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)
…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)
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)
…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)
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
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
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?
UnserializeVectorWithMaxSizefor runtime bounds.LIMITED_VECTOR/LimitedVectorFormatterfor compile-time bounds inREADWRITEdeclarations.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_testsMAX_SIZEandMAX_SIZE + 1declarations, 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: