Skip to content

perf: deserialize DKG messages once - #7401

Open
thepastaclaw wants to merge 7 commits into
dashpay:developfrom
thepastaclaw:perf/dkg-single-deserialize
Open

perf: deserialize DKG messages once#7401
thepastaclaw wants to merge 7 commits into
dashpay:developfrom
thepastaclaw:perf/dkg-single-deserialize

Conversation

@thepastaclaw

@thepastaclaw thepastaclaw commented Jul 2, 2026

Copy link
Copy Markdown

Issue being fixed or feature implemented

DKG network message intake currently deserializes each accepted payload twice: once on a copied payload in the p2p message handler for structural validation and again from the pending queue on the DKG worker thread. Because these messages contain BLS objects, the redundant intake pass repeats point decompression on a hot network path during DKG rounds.

Tracker: https://github.com/thepastaclaw/tracker/issues/1726

What was done?

  • Kept incoming contributions, complaints, justifications, and premature commitments as exact raw wire bytes in the existing per-message-type pending queues.
  • Replaced the temporary typed intake deserialize with a framing-only wire walk that validates canonical CompactSize counts and dynamic bitsets, quorum-parameter bounds, truncation, and trailing bytes without materializing BLS objects.
  • Kept the DKG worker as the sole typed, canonical, and active-scheme-aware deserialization point, immediately followed by the same parameter-derived structural checks and normal preverification.
  • Preserved the remote-sender quota of twice the quorum size, now keyed by the sender's MNAuth-verified proTxHash so reconnecting with a fresh NodeId does not reset the budget. A queue-wide remote-message cap of four times the quorum size remains in place for each message type, with one reserved slot for the local phase message. The parameter-derived per-message size caps, FIFO worker batches of up to eight, and duplicate tracking remain unchanged.
  • Check duplicate wire hashes before charging the sender's per-masternode queue quota.
  • Preserve inventory hashes over the exact original wire bytes, own-message queueing, and malformed-message scoring behavior.
  • At the start of each new DKG round, discard the globally bounded leftover raw queues without typed or BLS deserialization. Messages that missed their matching phase cannot delay new-round initialization with unbounded stale BLS work.
  • Added focused functional coverage proving that trailing bytes are rejected at intake, that a framing-valid invalid BLS encoding is accepted into the raw queue but rejected and scored by the on-time worker, that reconnecting with a fresh NodeId does not reset an authenticated sender’s quota, and that late framing-valid messages from distinct senders remain globally bounded and are cleared without BLS decoding before the next round initializes.
  • Added a fuzz target that continuously checks the safety-critical parser-equivalence direction across every configured LLMQ and both BLS schemes: the framing-only intake walk must never reject a payload that typed worker deserialization would accept.

Benchmark evidence

A temporary local benchmark compared representative old double-deserialize and new single-deserialize costs for QCONTRIB and QPCOMMITMENT payloads. The benchmark was removed from the codebase because it modeled the deleted legacy path and would be maintenance noise as a permanent bench_dash target. The benchmark notes are preserved at https://gist.github.com/thepastaclaw/b612f49db3d2870d45c6a6ace5c4b0ad.

Representative results:

DKG_QCONTRIB_DoubleDeserialize:      8,665,346.67 ns/op
DKG_QCONTRIB_SingleDeserialize:      4,296,081.31 ns/op
DKG_QPCOMMITMENT_DoubleDeserialize:    872,053.32 ns/op
DKG_QPCOMMITMENT_SingleDeserialize:    454,025.78 ns/op

This showed roughly a 2x reduction in the deserialization/decompression portion of the DKG message path for representative payloads.

How Has This Been Tested?

Validated on macOS arm64 with:

make -C src -j15 llmq/libbitcoin_node_a-net_dkg.o llmq/libbitcoin_node_a-dkgsessionhandler.o dashd
./src/test/test_dash --run_test=llmq_dkg_tests --catch_system_errors=no
test/functional/test_runner.py feature_llmq_dkg_intake.py --jobs=1
FUZZ=dkg_message_framing ./src/test/fuzz/fuzz <corpus-directory>
python3 -m py_compile test/functional/feature_llmq_dkg_intake.py
python3 test/lint/lint-includes.py
python3 test/lint/lint-circular-dependencies.py
test/lint/lint-whitespace.py --prev-commits=1
git diff --check upstream/develop...HEAD

Breaking Changes

None.

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 (for repository code-owners and collaborators only)

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 2, 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.

@thepastaclaw

thepastaclaw commented Jul 2, 2026

Copy link
Copy Markdown
Author

✅ Final review complete — no blockers (commit 0c255ab)

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f399013-c99e-4d21-9c2a-21571f580052

📥 Commits

Reviewing files that changed from the base of the PR and between 981e819 and 0c255ab.

📒 Files selected for processing (4)
  • src/llmq/dkgsessionhandler.h
  • src/llmq/net_dkg.cpp
  • src/llmq/net_dkg.h
  • test/functional/feature_llmq_dkg_intake.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/llmq/net_dkg.h
  • src/llmq/dkgsessionhandler.h
  • test/functional/feature_llmq_dkg_intake.py
  • src/llmq/net_dkg.cpp

Walkthrough

This PR changes DKG pending-message deduplication and sender/queue capacity handling, validates serialized payload structure before retention, defers typed deserialization until worker processing, and updates round processing. Fuzz, unit, and functional tests cover framing, malformed BLS keys, duplicate accounting, queue bounds, worker rejection, and late-message clearing.

Estimated code review effort: 4 (Complex) | ~50 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Peer
  participant NetDKG
  participant CDKGPendingMessages
  participant ProcessPendingMessageBatch
  participant PreVerifyMessage

  Peer->>NetDKG: ProcessMessage serialized payload
  NetDKG->>NetDKG: Validate size and wire structure
  NetDKG->>CDKGPendingMessages: Enqueue serialized payload
  ProcessPendingMessageBatch->>CDKGPendingMessages: Pop serialized payload
  ProcessPendingMessageBatch->>ProcessPendingMessageBatch: Deserialize and validate
  ProcessPendingMessageBatch->>PreVerifyMessage: Forward accepted message
Loading

Possibly related PRs

  • dashpay/dash#7314: Modifies the same DKG pending-message and network intake plumbing.
  • dashpay/dash#7397: Changes related DKG intake validation and processing paths.
  • dashpay/dash#7408: Modifies QCONTRIB structural validation in the same intake implementation.

Suggested reviewers: udjinm6

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: reducing DKG message deserialization to once.
Description check ✅ Passed The description matches the changeset and explains the DKG intake and worker refactor.
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.
✨ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/bench/dkg_deserialize.cpp (1)

115-120: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Mirror the worker allocation in the double-deserialize benchmark.

The single-deserialize path includes std::make_shared, but the simulated old worker deserialize uses stack objects. If the removed worker path materialized shared typed messages, use make_shared here too so the comparison measures only the intended deserialize-count delta.

Proposed adjustment
-            llmq::CDKGContribution qc;
-            s >> qc;
-            ankerl::nanobench::doNotOptimizeAway(qc.contributions->blobs.size());
+            auto qc = std::make_shared<llmq::CDKGContribution>();
+            s >> *qc;
+            ankerl::nanobench::doNotOptimizeAway(qc->contributions->blobs.size());
-            llmq::CDKGPrematureCommitment qc;
-            s >> qc;
-            ankerl::nanobench::doNotOptimizeAway(qc.validMembers.size());
+            auto qc = std::make_shared<llmq::CDKGPrematureCommitment>();
+            s >> *qc;
+            ankerl::nanobench::doNotOptimizeAway(qc->validMembers.size());

Also applies to: 151-155

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/bench/dkg_deserialize.cpp` around lines 115 - 120, The old worker
deserialize benchmark path in the double-deserialize setup is not mirroring the
shared-allocation behavior used by the single-deserialize path. Update the
worker-side deserialize blocks in the benchmark function(s) that use CDataStream
and llmq::CDKGContribution so they materialize the typed message with
std::make_shared, matching the removed worker path’s allocation pattern. Keep
the deserialize-count comparison focused on the intended cost difference by
aligning the allocation behavior between the single and double-deserialize
cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/llmq/dkgsessionhandler.h`:
- Around line 84-94: In CDKGPendingMessages, the duplicate-hash check currently
happens after incrementing messagesPerNode[from], so repeated duplicates can
consume the peer quota without adding work. Move the seenMessages.emplace(hash)
check ahead of the quota accounting in the same flow, then only increment
messagesPerNode[from] and continue enqueueing when the hash is new; keep the
existing logging and early returns in CDKGPendingMessages::AddMessage (or the
corresponding message-handling method).

---

Nitpick comments:
In `@src/bench/dkg_deserialize.cpp`:
- Around line 115-120: The old worker deserialize benchmark path in the
double-deserialize setup is not mirroring the shared-allocation behavior used by
the single-deserialize path. Update the worker-side deserialize blocks in the
benchmark function(s) that use CDataStream and llmq::CDKGContribution so they
materialize the typed message with std::make_shared, matching the removed worker
path’s allocation pattern. Keep the deserialize-count comparison focused on the
intended cost difference by aligning the allocation behavior between the single
and double-deserialize cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7c05047c-80ac-49ea-b4c5-0035e6ec8d3f

📥 Commits

Reviewing files that changed from the base of the PR and between 59d36a9 and c7e3cad4935c2e957f8806b8583716d82ca842da.

📒 Files selected for processing (5)
  • src/Makefile.bench.include
  • src/bench/dkg_deserialize.cpp
  • src/llmq/dkgsessionhandler.cpp
  • src/llmq/dkgsessionhandler.h
  • src/llmq/net_dkg.cpp
💤 Files with no reviewable changes (1)
  • src/llmq/dkgsessionhandler.cpp

Comment thread src/llmq/dkgsessionhandler.h Outdated

@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 perf refactor eliminating a redundant DKG deserialize. All intake invariants (oversize ban, malformed ban, hash-over-wire-bytes, per-node/duplicate suppression, own-message enqueue form) are preserved and independently verified by multiple reviewers. Only remaining suggestion is a Dash-specific lint tracking update for the new benchmark file.

Source: reviewers: opus (general), claude-sonnet-5 (general), gpt-5.5[high] (general), opus (dash-core-commit-history), claude-sonnet-5 (dash-core-commit-history), gpt-5.5[high] (dash-core-commit-history); verifier: opus; specialists: dash-core-commit-history

🟡 1 suggestion

Findings not posted inline (1)

These findings could not be anchored to the current diff, but they are still part of this review.

  • [SUGGESTION] test/util/data/non-backported.txt:4: Add the new Dash-specific benchmark to non-backported tracking — This PR adds src/bench/dkg_deserialize.cpp, a Dash-specific benchmark that is not matched by the existing src/bench/bls*.cpp glob. test/lint/lint-cppcheck-dash.py (line 69-73) builds its Dash-specific file list exclusively from patterns in this file via git ls-files, so the new benchmark will be silently skipped by that extra lint coverage until it is added. Add a matching entry to keep Dash-specific bench files under cppcheck.
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `test/util/data/non-backported.txt`:
- [SUGGESTION] test/util/data/non-backported.txt:4: Add the new Dash-specific benchmark to non-backported tracking
  This PR adds `src/bench/dkg_deserialize.cpp`, a Dash-specific benchmark that is not matched by the existing `src/bench/bls*.cpp` glob. `test/lint/lint-cppcheck-dash.py` (line 69-73) builds its Dash-specific file list exclusively from patterns in this file via `git ls-files`, so the new benchmark will be silently skipped by that extra lint coverage until it is added. Add a matching entry to keep Dash-specific bench files under cppcheck.

@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

Two suggestions remain against head 029fcc228b. New finding in the latest delta: the dedupe-before-quota reorder correctly fixes duplicate resend quota charging, but a quota-exceeded drop now records the hash in seenMessages, so the same message can be poisoned for later legitimate delivery paths in the round. Carried-forward prior finding: src/bench/dkg_deserialize.cpp is still missing from test/util/data/non-backported.txt, so the extra cppcheck-dash lint pass will silently skip the new Dash-specific benchmark. No blocking issues; the benchmark allocation-shape fix is otherwise correct and well-scoped.

Source: reviewers: opus (general), claude-sonnet-5 (general), gpt-5.5[high] (general, failed), opus (dash-core-commit-history), claude-sonnet-5 (dash-core-commit-history), gpt-5.5[high] (dash-core-commit-history, failed); verifier: opus; specialists: dash-core-commit-history

🟡 2 suggestion(s)

Findings not posted inline (1)

These findings could not be anchored to the current diff, but they are still part of this review.

  • [SUGGESTION] test/util/data/non-backported.txt:4: Track new Dash-specific benchmark in non-backported.txt — This PR adds src/bench/dkg_deserialize.cpp, a Dash-specific benchmark that is not matched by the existing src/bench/bls*.cpp glob on line 4. test/lint/lint-cppcheck-dash.py builds its Dash-specific file list exclusively from the patterns in this file via git ls-files, so the new benchmark will be silently excluded from the extra cppcheck-dash coverage until it is listed here. Carried forward from the prior automated review at c7e3cad4 — still unresolved at head 029fcc228b (the tracked file was not touched by the latest push).
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

New finding in the latest delta:
In `src/llmq/dkgsessionhandler.h`:
- [SUGGESTION] src/llmq/dkgsessionhandler.h:82-100: Quota-exceeded drop still poisons the message hash in seenMessages
  The reordered `PushPendingMessage` inserts `hash` into `seenMessages` at line 86 via `emplace(hash).second` before the quota check at line 92. If a sender is already at `maxMessagesPerNode` distinct-message quota, the function returns after having already committed the hash to `seenMessages`. Because `seenMessages` is keyed only by hash (not by sender), the same message subsequently delivered from any other peer — or via `AlreadyHave`/`HasSeen` paths — is now treated as "already seen" for the rest of the round and can never reach `pendingMessages`.
  
  This is a narrower variant of the bug fixed in e8d9b6d803: that commit correctly stopped duplicate resends from silently consuming a peer's quota, but the current ordering trades it for a case where a quota-exceeded drop poisons the hash for other, legitimate delivery paths. DKG already tolerates a single peer's messages being lost, so the operational impact is small, but the current shape unnecessarily couples the two intake failure modes. Splitting the seen-check (read-only) from the seen-insert (commit) so the hash is only recorded once the message is actually accepted preserves both invariants.

Carried-forward prior finding:
In `test/util/data/non-backported.txt`:
- [SUGGESTION] test/util/data/non-backported.txt:4: Track new Dash-specific benchmark in non-backported.txt
  This PR adds `src/bench/dkg_deserialize.cpp`, a Dash-specific benchmark that is not matched by the existing `src/bench/bls*.cpp` glob on line 4. `test/lint/lint-cppcheck-dash.py` builds its Dash-specific file list exclusively from the patterns in this file via `git ls-files`, so the new benchmark will be silently excluded from the extra cppcheck-dash coverage until it is listed here. Carried forward from the prior automated review at c7e3cad4 — still unresolved at head 029fcc228b (the tracked file was not touched by the latest push).

@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

Both prior findings are cleanly resolved by the latest delta: PushPendingMessage now only marks a hash as seen after the per-node quota check passes (no more quota-drop poisoning), and src/bench/dkg_deserialize.cpp is now tracked in non-backported.txt. No new issues in the delta; the full PR stack (single-deserialize refactor, benchmark, dedupe/quota fix) preserves the documented intake invariants. Commit-history agents suggested squashing fixup commits, but Dash merges PRs without squashing and these are low-value stylistic notes that don't warrant blocking.

Source: reviewers: opus (general), claude-sonnet-5 (general), gpt-5.5[high] (general, failed), opus (dash-core-commit-history), claude-sonnet-5 (dash-core-commit-history), gpt-5.5[high] (dash-core-commit-history, failed); verifier: opus; specialists: dash-core-commit-history

@thepastaclaw

Copy link
Copy Markdown
Author

CI note: the current Build source failures are the same depends-cache infrastructure issue tracked in #7403, not a failure in this PR's DKG changes. The source jobs are exiting before compilation at actions/cache/restore with fail-on-cache-miss for freshly built depends cache keys, for example run 28630838726 / linux64-build job 84908995980. No branch change is appropriate for this PR; the workflow/cache fix belongs in #7403.

@thepastaclaw
thepastaclaw force-pushed the perf/dkg-single-deserialize branch from 506a5e4 to 55e1d5e Compare July 4, 2026 14:26
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

This pull request has conflicts, please rebase.

@thepastaclaw
thepastaclaw force-pushed the perf/dkg-single-deserialize branch from 55e1d5e to 98f20b1 Compare July 8, 2026 04:28
@thepastaclaw

Copy link
Copy Markdown
Author

Rebased this PR onto current upstream/develop and resolved the DKG conflict.

Preserved the single-deserialize typed queue path, kept the upstream contribution minSize lower-bound check, and retained the duplicate-before-quota / seen-after-quota behavior from the prior review fix.

Validation run locally on macOS arm64:

  • ./autogen.sh
  • ./configure --without-gui
  • make -C src llmq/libbitcoin_node_a-net_dkg.o bench/bench_dash -j4
  • git diff --check upstream/develop..HEAD
  • COMMIT_RANGE=upstream/develop..HEAD test/lint/lint-whitespace.py
  • COMMIT_RANGE=upstream/develop..HEAD test/lint/lint-cppcheck-dash.py
  • src/bench/bench_dash -filter='DKG_Q.*Deserialize'

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

@thepastaclaw Thanks for the update — rebasing onto upstream/develop and preserving the single-deserialize typed queue path, the contribution minSize check, and the duplicate-before-quota/seen-after-quota behavior all sound correct. The local validation steps (build, whitespace/cppcheck lint, and the DKG benchmark filter) look thorough. I'll kick off a fresh review of the rebased changes now.

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

@thepastaclaw
thepastaclaw force-pushed the perf/dkg-single-deserialize branch from 98f20b1 to 51cf424 Compare July 8, 2026 16:32
@thepastaclaw

thepastaclaw commented Jul 8, 2026

Copy link
Copy Markdown
Author

Reworked this PR to remove the temporary old-vs-new benchmark from the codebase.

Current head 51cf424a2167d106c7677d4d7b8b444989396bfe is now a single production commit touching only:

  • src/llmq/dkgsessionhandler.cpp
  • src/llmq/dkgsessionhandler.h
  • src/llmq/net_dkg.cpp

The benchmark evidence is preserved in the PR description and in this gist:

https://gist.github.com/thepastaclaw/b612f49db3d2870d45c6a6ace5c4b0ad

Validation run locally on macOS arm64:

./autogen.sh
./configure --without-gui --disable-tests --disable-bench \
  --disable-wallet --without-miniupnpc --without-natpmp
make -C src llmq/libbitcoin_node_a-net_dkg.o -j4
make -C src llmq/libbitcoin_node_a-dkgsessionhandler.o -j4
python3 test/lint/lint-includes.py
python3 test/lint/lint-circular-dependencies.py
git diff --check upstream/develop
COMMIT_RANGE=upstream/develop..HEAD test/lint/lint-whitespace.py
npx markdownlint-cli2 /tmp/dash-7401-body-updated.md

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

@thepastaclaw Thanks for the update. Reducing the PR to a single production commit scoped to src/llmq/dkgsessionhandler.cpp, src/llmq/dkgsessionhandler.h, and src/llmq/net_dkg.cpp, while preserving the benchmark evidence in the PR description and gist, makes sense for keeping the diff focused. I'll trigger a fresh review of the current head now.

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

@PastaPastaPasta
PastaPastaPasta marked this pull request as ready for review July 8, 2026 16:50
PastaPastaPasta
PastaPastaPasta previously approved these changes Jul 8, 2026

@PastaPastaPasta PastaPastaPasta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

utACK 51cf424a2167d106c7677d4d7b8b444989396bfe

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 51cf424a21

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

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

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

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/llmq/net_dkg.cpp Outdated
Comment thread src/llmq/dkgsessionhandler.h Outdated

@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

I verified the current head 51cf424a2167d106c7677d4d7b8b444989396bfe against the active reviewer outputs and the older-SHA findings. The current PR diff is confined to the DKG typed pending-queue refactor; the quota poisoning issue is fixed, and the benchmark/non-backported findings are obsolete because src/bench/dkg_deserialize.cpp and test/util/data/non-backported.txt are no longer in the PR diff. No in-scope findings remain.

Source: reviewers: opus (claude general, failed), gpt-5.5 (codex general), opus (claude dash-core-commit-history, failed), gpt-5.5 (codex dash-core-commit-history); verifier: gpt-5.5 (codex; fallback: claude verifier unavailable); specialists: dash-core-commit-history

Prior finding reconciliation
  • Quota-exceeded drop still poisons the message hash in seenMessages — Fixed in the current code. src/llmq/dkgsessionhandler.h now performs a read-only duplicate check with seenMessages.count(hash) before the quota gate, returns on quota overflow without touching seenMessages, and only calls seenMessages.emplace(hash) after the quota check passes immediately before enqueue.
  • Track new Dash-specific benchmark in non-backported.txt — Obsolete at the assigned head. src/bench/dkg_deserialize.cpp is no longer present or tracked, and test/util/data/non-backported.txt is not part of the current PR diff.
  • Add the new Dash-specific benchmark to non-backported tracking — Obsolete at the assigned head for the same reason: the benchmark-only files from the older review are no longer in the PR diff.

Note: Claude/Opus lanes were unavailable due quota during this run; Codex reviewer and verifier lanes completed.

@thepastaclaw

Copy link
Copy Markdown
Author

Updated perf/dkg-single-deserialize with ce4180d18e8 to address the current review threads:

  • Pending DKG queues now retain bounded canonical CDataStream bytes rather than typed shared_ptr<Message> objects, preserving the previous compact retained-memory behavior.
  • DKG payload deserialization and structural checks now happen in the DKG phase worker immediately before PreVerifyMessage, so BLS-bearing objects are materialized under the active scheme at processing time while still deserializing only once.

Local validation:

  • ./autogen.sh
  • ./configure --without-gui --disable-tests --disable-bench --disable-wallet --without-miniupnpc --without-natpmp
  • make -C src llmq/libbitcoin_node_a-net_dkg.o -j4
  • make -C src llmq/libbitcoin_node_a-dkgsessionhandler.o -j4
  • git diff --check upstream/develop..HEAD
  • python3 test/lint/lint-includes.py
  • python3 test/lint/lint-circular-dependencies.py

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ce4180d18e

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread src/llmq/net_dkg.cpp Outdated
@thepastaclaw
thepastaclaw force-pushed the perf/dkg-single-deserialize branch from ce4180d to 48de1d0 Compare July 8, 2026 17:37
@thepastaclaw

Copy link
Copy Markdown
Author

Updated perf/dkg-single-deserialize with 48de1d0c4f2 to address the latest Codex review thread.

What changed:

  • DKG intake now validates a temporary copy of the canonical wire bytes before retaining anything in the pending queue.
  • Malformed or structurally invalid DKG payloads are banned immediately with malformed DKG message before PeerEraseObjectRequest / PushPendingMessage.
  • The pending queues still retain canonical CDataStream bytes, and the DKG worker still deserializes those bytes once at processing time.

Local validation:

  • ./configure --without-gui --disable-tests --disable-bench --without-miniupnpc --without-natpmp
  • make -C src llmq/libbitcoin_node_a-net_dkg.o -j4
  • make -C src llmq/libbitcoin_node_a-dkgsessionhandler.o -j4
  • make -C src dashd -j4
  • test/functional/feature_llmq_dkg_intake.py
  • git diff --check upstream/develop..HEAD
  • COMMIT_RANGE=upstream/develop..HEAD test/lint/lint-whitespace.py
  • python3 test/lint/lint-includes.py
  • python3 test/lint/lint-circular-dependencies.py
  • python3 -m py_compile test/functional/feature_llmq_dkg_intake.py

@thepastaclaw
thepastaclaw force-pushed the perf/dkg-single-deserialize branch from d5b4334 to 5d42157 Compare July 15, 2026 07:47
@thepastaclaw

Copy link
Copy Markdown
Author

Addressed the round-start stale-BLS workload in amended commit 5d42157166e: each raw message-type queue is now capped globally across remote NodeIds, reserves one local-message slot, avoids rejected-ID metadata growth, and clears bounded stale raw bytes without typed/BLS deserialization. Added queue-level churn/cap/reset coverage and an end-to-end reconnect-overflow test. Targeted build, llmq_dkg_tests, feature_llmq_dkg_intake.py, Python/include/circular/whitespace lints, clang-format diff, and diff checks pass. Fresh exact-head CI and automated review follow-through are running.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5d42157166

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread src/llmq/dkgsessionhandler.cpp Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/llmq/dkgsessionhandler.cpp`:
- Around line 46-50: Update the pending-queue capacity logic in the DKG session
handler to count remote entries independently of the local entry, so remote
messages can always use the full maxMessagesPerNode allowance regardless of
enqueue order while still enforcing the total maxPendingMessages limit. Preserve
the existing local enqueue behavior and Dash-specific DKG handling, and add a
focused unit test covering local-first enqueue followed by the maximum remote
messages.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b3d3c15b-d49b-4699-a70b-0dc569245709

📥 Commits

Reviewing files that changed from the base of the PR and between d5b433419fd45a3211db0598ec3d30f857fa3350 and 5d42157166e77017adf4a6eca484b9e17d6f89a5.

📒 Files selected for processing (5)
  • src/llmq/dkgsessionhandler.cpp
  • src/llmq/dkgsessionhandler.h
  • src/llmq/net_dkg.cpp
  • src/test/llmq_dkg_tests.cpp
  • test/functional/feature_llmq_dkg_intake.py

Comment thread src/llmq/dkgsessionhandler.cpp Outdated
@thepastaclaw
thepastaclaw force-pushed the perf/dkg-single-deserialize branch from 5d42157 to 45821d0 Compare July 15, 2026 08:28
@thepastaclaw

Copy link
Copy Markdown
Author

Addressed both queue-cap review findings in the amended single commit 45821d0:

  • remote capacity now permits two peers to consume their full per-peer quotas while remaining globally bounded at 4x quorum size;
  • remote occupancy is independent of the reserved local slot, so local-first enqueueing does not reduce remote capacity;
  • added focused queue tests for local-first ordering, two-peer fairness, overflow rejection, pop recovery, and clear/reset behavior;
  • updated the functional reconnect-bound test to the new cap.

Validation: focused unit tests passed, feature_llmq_dkg_intake.py passed, diff/py_compile checks passed, and the clean code-review gate returned ship with zero findings.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45821d078a

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread src/llmq/net_dkg.cpp
@thepastaclaw

thepastaclaw commented Jul 15, 2026

Copy link
Copy Markdown
Author

Exact-head CI is complete for 45821d078a9. The amended single commit still contains all review fixes, and the branch needs no further change.

Two functional-test failures are unrelated to this DKG-only diff:

  • TSAN: p2p_node_network_limited.py --v2transport hit the known connect_nodes(0, 2) peer-disconnect race tracked in test: p2p_node_network_limited.py --v2transport intermittently disconnects during connect_nodes #7288, with the same NODE_NETWORK_LIMITED stack on all three attempts.
  • Multiprocess: feature_governance.py --legacy-wallet timed out waiting for one trigger vote. The same exact-head legacy and descriptor tests passed in both the normal Linux and UBSAN jobs, and the descriptor variant passed in the multiprocess job itself. This PR changes only DKG intake/queue code and focused DKG tests, not governance or multiprocess code.

A later rerun of the failed multiprocess job did not reach tests: actions/checkout@v6 rejected fork code in the run's frozen pull_request_target workflow. That workflow-level checkout issue was fixed by merged #7474, but rerunning this older workflow invocation still uses its original definition, so no PR-branch change can affect it.

All build, lint, format, unit-test, and remaining functional-test jobs passed or were intentionally skipped. No rerun or branch mutation was performed.

@thepastaclaw

thepastaclaw commented Jul 18, 2026

Copy link
Copy Markdown
Author

@PastaPastaPasta The current head 45821d078a9 is ready for another look. It includes the queue-cap fixes made after the prior review, is mergeable, and exact-head review found no blockers. The two red functional-test jobs were traced to unrelated p2p_node_network_limited.py --v2transport and feature_governance.py --legacy-wallet failures. Could you re-review when you have a chance?

Keep incoming DKG messages as exact raw wire bytes after cheap framing validation. The DKG worker remains the sole normal typed and BLS deserialization point, avoiding repeated point decoding on the message-handler thread.

Preserve authenticated-only intake, parameter-derived size and framing checks, exact-wire inventory hashes, duplicate-before-quota ordering, worker preverification, malformed-message scoring in the matching phase, and own-message processing.

Bound each raw message-type queue across all remote NodeIds to four times the quorum size, allowing two peers to use their full per-node quotas, with one reserved slot for the local phase message. New-round cleanup discards bounded stale raw bytes without deserializing them, so reconnect churn cannot create an unbounded synchronous BLS workload before quorum initialization.

Add unit coverage for per-node and queue-wide caps, duplicate ordering, local-message capacity, and round clearing. Update the focused functional test to cover worker-deferred BLS rejection plus bounded late-message retention across reconnect-generated NodeIds and prompt next-round initialization.

Co-Authored-By: Claude <noreply@anthropic.com>
@thepastaclaw
thepastaclaw force-pushed the perf/dkg-single-deserialize branch from 45821d0 to 5580082 Compare July 24, 2026 20:21

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Final validation — Codex + Sonnet

Independently verified against the exact checked-out head (5580082): the DKG message-intake redesign is correct and matches both agents' claims. Wire-structure validation (CheckContributionWireStructure/CheckComplaintWireStructure/CheckJustificationWireStructure/CheckPrematureCommitmentWireStructure) skips fixed-size BLS blobs via SkipBytes without ever constructing/decompressing a BLS object, the DKG worker's DeserializeAndCheckDKGMessage remains the sole typed+BLS deserialization point, per-node/queue-wide caps (size2 / size4+1) are enforced in PushPendingMessage, round-start CDKGPendingMessages::Clear() is an O(1) container clear with no deserialization, and NetDKG::ProcessMessage rejects oversized/malformed payloads before the CDataStream is ever constructed or pushed into the pending queue. All five carried-forward prior findings are confirmed FIXED with concrete code evidence at this exact head, and no new blocking or suggestion-level issues were identified in the range.

Review provenance

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

Note: the verifier’s canonical action is APPROVE, but GitHub does not allow an author to approve their own PR. This exact-SHA final review is posted as COMMENT without changing the canonical result.

@thepastaclaw

Copy link
Copy Markdown
Author

@PastaPastaPasta Current head 55800822f10d5a0597bf4c5664eed2002c407eee is mergeable, all CI checks are green, and the exact-head final review found no blockers. The prior approval was dismissed by the subsequent branch update; could you re-review when you have a chance?

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown

Potential PR merge conflicts

This is advisory only. It does not block CI, but it marks PRs that will likely need a rebase depending on merge order.

If this PR merges first

These open PRs will likely need a rebase:

If these PRs merge first

This PR will likely need a rebase:

@UdjinM6

UdjinM6 commented Jul 27, 2026

Copy link
Copy Markdown

pls consider commits from https://github.com/UdjinM6/dash/commits/pr7401-review/

UdjinM6 and others added 5 commits July 27, 2026 13:11
ReadAndCheckDynBitset materialized a byte vector per dynamic bitset just to
inspect the final padding byte. Skip to the last byte instead -- CDataStream
already bounds-checks ignore() and throws on overrun -- and return the bit count
as an optional rather than a bool plus an out-parameter.

Turn the typed CheckDKGMessageStructure explicit specializations into plain
overloads and lift them, together with CheckDKGMessageWireStructure, out of the
anonymous namespace so a fuzz target can reach them. Overloads fail at compile
time rather than link time when a new message type is added. The header comment
records that the walk mirrors the (Un)serialize implementations in
llmq/dkgmessages.h by hand and must be kept in step with them.

The walk still validates a copy of the payload rather than a view of it. The
copy is a memcpy bounded by the size cap applied just before the call, which is
noise next to the BLS point decompression this code path exists to avoid;
eliminating it would need SpanReader::ignore(), which Dash does not have yet
(bitcoin#28721 / bitcoin#34483) and which belongs in its own backport rather
than here.

No behaviour change: the walk accepts and rejects exactly the same payloads.
DKG intake validates framing without materializing BLS objects, then the worker
deserializes the retained bytes exactly once. Those are two hand-maintained
parsers over one wire format, so they can drift apart.

The safety-critical direction is that framing must never reject a payload the
worker would accept: honest DKG messages would then be dropped at intake and
quorum formation would degrade. Assert exactly that, over all four message
types, every entry in available_llmqs, and both BLS schemes. The converse is
deliberately not asserted -- framing accepts payloads whose BLS points fail to
decode so the worker can score the sender.

Also build a well-formed message from fuzzer-chosen field values, serialize it,
and require framing to accept our own serializer's output. That catches drift
from an empty corpus, without needing the fuzzer to synthesize a valid BLS
encoding by chance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The worker logged "failed to deserialize message" for two different outcomes:
a payload whose BLS points or canonical encoding failed to decode, and one that
decoded fine but violated a param-derived bound. Both score the sender 100, but
they say different things about the peer, and the framing walk at intake has
already ruled out plain malformed framing by the time either fires.

Return an explicit result from DeserializeAndCheckDKGMessage and log the two
cases separately. The out-parameter stays because the result now carries
information the pointer cannot.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
_start_fresh_dkg_cycle generated up to a full cycle of blocks with plain
generate(), leaving mocktime behind while the DKG phase clock advanced. Every
other block move in this test uses move_blocks, which bumps mocktime first; use
it here too so phase timing cannot drift.

Hoist the hardcoded cycle length into a named module constant while touching
both of its users.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CDKGPendingMessages charged its per-sender retention quota against the NodeId.
A peer that disconnects and reconnects gets a fresh NodeId, so it started each
connection with a full budget and could hold far more than size*2 messages per
round by cycling connections.

Key the quota by the sender's MNAuth-verified proTxHash instead. Every pusher on
this path is already MNAuth-gated -- CMNAuth::ProcessMessage requires the
proTxHash to resolve in the deterministic MN list and to carry a valid
operator-key signature, and allows one MNAUTH per connection -- so the identity
survives reconnects and is not attacker-inflatable. Own messages pass a null
hash and stay exempt from the quota while remaining subject to the queue-wide
caps.

The map is deliberately not pruned when the worker drains the queue: the quota
is cumulative for the round, so that draining does not refund retention slots.
Its size is therefore bounded by the registered masternode set rather than by
the queue caps, which the declaration now records.

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

Copy link
Copy Markdown
Author

@UdjinM6 Thanks — I reviewed and took all five commits unchanged via cherry-pick, preserving your authorship:

  • expose the DKG framing checks and remove the bitset copy
  • add the framing-vs-typed-deserialization fuzz target
  • distinguish structure-check failures from decode failures in logs
  • keep mocktime aligned when advancing to a fresh DKG cycle
  • key the pending-message quota by the MNAuth-verified proTxHash

Validation passed: focused builds, llmq_dkg_tests, the DKG intake functional test (including the new quota assertion), the fuzz target with a targeted corpus that reached successful typed deserialization, git diff --check, and 761/761 otherwise-runnable unit tests. The intermittent feature_llmq_simplepose.py timeout also reproduced on the unmodified prior PR head and produced no quota-drop logs, confirming the known pre-existing wait flake rather than a regression here.

I also updated the PR description to reflect the proTxHash-keyed quota. Current head is 981e8196d06; its single CI run is now queued.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 981e8196d0

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread src/llmq/net_dkg.cpp
Comment on lines +483 to +484
const auto decode_result = DeserializeAndCheckDKGMessage(*p.second, params, msg);
if (decode_result != DKGMessageDecodeResult::Ok) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve sender identity for deferred BLS penalties

When an MNAuth peer sends framing-valid but BLS-invalid messages before the matching phase and disconnects before this deferred decode, the queue retains only its NodeId; PeerManagerImpl::PeerMisbehaving later calls GetPeerRef(nodeId) and silently does nothing once that peer has been removed. The sender can therefore force the allowed BLS-decompression workload every round without ever being discouraged. Fresh evidence relative to the earlier late-payload thread is the disconnect window: before this change, intake deserialized the BLS objects and scored the still-connected peer synchronously. Retain an identity against which deferred failures can be enforced, or validate before losing the connection context.

AGENTS.md reference: AGENTS.md:L160-L166

Useful? React with 👍 / 👎.

Capture the MNAuth-verified proTxHash once at intake and reuse it for the per-sender quota, use the codebase's salted uint256 hash map for sender accounting, and retain the existing malformed-message assertion alongside the new worker failure classes. Also describe the exposed framing helpers as test-facing rather than fuzz-only.

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

Copy link
Copy Markdown
Author

Thanks — I reviewed all five commits against the current head and adopted all of them.

The resulting stack keeps your authorship and includes a small follow-up tightening:

  • the framing helpers are exposed for tests, with the fuzz-target reference introduced in the same commit as the target;
  • the fuzz assertion checks the intended one-way invariant: framing must accept everything typed worker deserialization accepts, while still allowing framing to defer invalid BLS points to the worker;
  • structure-bound failures and decode failures now log distinctly without changing the 100-point misbehavior handling;
  • _start_fresh_dkg_cycle now advances mocktime through move_blocks;
  • per-sender retention accounting is keyed by the MNAuth-verified proTxHash, so reconnecting under new NodeIds cannot refill the quota; the verified hash is captured once at intake and sender accounting uses Uint256HashMap;
  • existing malformed DKG message round-clearing coverage remains alongside the two new worker failure strings.

Local validation at 0c255ab7a17:

  • make -j2 src/test/test_dash src/test/fuzz/fuzz src/dashd
  • ./src/test/test_dash --run_test=llmq_dkg_tests --catch_system_errors=no — 4/4 pass
  • test/functional/test_runner.py feature_llmq_dkg_intake.py --jobs=1 — pass
  • FUZZ=dkg_message_framing ./src/test/fuzz/fuzz <300-file generated corpus> — pass
  • circular-dependency, include, Python, whitespace/diff checks — pass

I also mutation-checked the reconnect regression: changing the quota back to NodeId makes the new test retain four messages instead of two.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0c255ab7a1

ℹ️ About Codex in GitHub

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

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

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

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

maxMessagesPerSender(_maxMessagesPerSender),
// Belt-and-braces bound on live queue occupancy. The per-sender quota is
// the primary limit; this only caps the total across distinct senders.
maxPendingRemoteMessages(_maxMessagesPerSender * 2),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Raise the queue cap beyond two senders

Fresh evidence after the one-sender fix is that the final constructor still makes the global remote cap exactly two full per-sender quotas: _maxMessagesPerSender is 2 * params.size, and this multiplier limits the queue to 4 * params.size. Two MNAuth-verified masternodes—including registered nodes outside the target quorum—can therefore fill a future-phase queue with unique, framing-valid messages before its worker starts; subsequent valid member messages hit pending queue full and are dropped, allowing two identities to disrupt that DKG round. Size the cap for the intended Byzantine population or cheaply account messages by their claimed quorum member before reserving slots.

AGENTS.md reference: AGENTS.md:L160-L160

Useful? React with 👍 / 👎.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Final validation — Codex + Sonnet

Carried-forward: all five prior findings (round-start unbounded BLS deserialization, double deserialization, opposite-scheme rejection, stale commit-message complaint, malformed-before-queue) remain FIXED at current head 0c255ab; none are STILL_VALID, so none re-enter canonical findings. New latest-delta findings: both agent checkpoints converge on one suggestion — the new Dash-specific fuzz target src/test/fuzz/dkg_message_framing.cpp is not covered by test/util/data/non-backported.txt (whose only relevant pattern, src/test/llmq*.cpp, does not match the src/test/fuzz/ subdirectory), so it is silently skipped by the Dash-specific cppcheck lint pass. The commit-history follow-up-squash suggestion was raised by both agents but is dropped as not actionable given documented intentional cherry-pick-preserving integration practice on this repo.

Review provenance

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

🟡 1 suggestion(s)

1 additional finding(s) omitted (not in diff).

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `test/util/data/non-backported.txt`:
- [SUGGESTION] test/util/data/non-backported.txt:66: Register the new Dash-specific fuzz target as non-backported
  This PR adds src/test/fuzz/dkg_message_framing.cpp, a Dash-specific fuzz target that did not originate upstream (it fuzzes llmq/net_dkg.h and llmq/dkgmessages.h). The existing manifest entry `src/test/llmq*.cpp` only matches files directly under src/test/ — it does not match the src/test/fuzz/ subdirectory, and grepping the file confirms no pattern currently covers it. Because test/lint/lint-cppcheck-dash.py builds its Dash-specific cppcheck input list from this manifest, the new target is silently excluded from that additional lint pass. Add `src/test/fuzz/dkg_message_framing.cpp` (or a `src/test/fuzz/dkg_*.cpp` pattern) to the manifest.

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.

3 participants