Skip to content

fix: bound pending sig share queue - #7415

Merged
PastaPastaPasta merged 6 commits into
dashpay:developfrom
thepastaclaw:fix/bound-pending-sig-share-queue
Jul 11, 2026
Merged

fix: bound pending sig share queue#7415
PastaPastaPasta merged 6 commits into
dashpay:developfrom
thepastaclaw:fix/bound-pending-sig-share-queue

Conversation

@thepastaclaw

Copy link
Copy Markdown

Issue being fixed or feature implemented

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

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

What was done?

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

How Has This Been Tested?

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

Breaking Changes

None.

Checklist:

  • 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)

@thepastaclaw

thepastaclaw commented Jul 7, 2026

Copy link
Copy Markdown
Author

✅ Review complete (commit bdca48c)

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

⚠️ Potential Merge Conflicts Detected

This PR has potential conflicts with the following open PRs:

Please coordinate with the authors of these PRs to avoid merge conflicts.

@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: 18d245370f

ℹ️ 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/signing_shares.cpp Outdated
return !ns.pendingIncomingSigShares.Empty();
},
rnd);

pendingIncomingSigSharesCount -= erasedCount;

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 Keep in-flight shares counted against the cap

When BLS verification is slower than incoming QSIGSHARE/QBSIGSHARES traffic, this decrement frees global capacity as soon as the dispatcher collects a batch, before those shares have been verified; NetSigning::WorkThreadDispatcher then moves the batch into worker_pool.push, whose underlying ctpl queue is unbounded, so peers can refill another 10k shares while the previous batches are still queued/in flight. That leaves the intended global backpressure bypassable under sustained traffic; keep these shares counted until the worker finishes or bound the worker queue as well.

Useful? React with 👍 / 👎.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e7e1b429-a6d9-4001-a2de-5fffdfbffbce

📥 Commits

Reviewing files that changed from the base of the PR and between 355da45 and bdca48c.

📒 Files selected for processing (3)
  • src/llmq/signing_shares.cpp
  • src/llmq/signing_shares.h
  • src/test/llmq_utils_tests.cpp

Walkthrough

This PR replaces fold-based sig-share sizing with counted bucket storage, adds per-node and global caps for pending incoming shares, and bounds verification batches and collection by share count. Pending shares are rejected for banned or over-cap nodes and cleared when nodes are banned. Unit tests cover map mutations, bucket erasure, and pending-session removal.

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

Sequence Diagram(s)

sequenceDiagram
  participant SigShareHandler
  participant CSigSharesManager
  participant WorkerPool
  SigShareHandler->>CSigSharesManager: Admit pending signature shares
  CSigSharesManager->>CSigSharesManager: Enforce per-node and global caps
  CSigSharesManager->>WorkerPool: Queue bounded verification batches
  WorkerPool->>CSigSharesManager: Process pending signature shares
Loading

Suggested reviewers: UdjinM6, PastaPastaPasta, kwvg

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 matches the main change: bounding the pending sig share queue.
Description check ✅ Passed The description is directly related to the PR and summarizes the backpressure and cleanup changes.
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.

@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

Source: reviewers claude/general=opus(failed), codex/general=gpt-5.5(completed), claude/dash-core-commit-history=opus(failed), codex/dash-core-commit-history=gpt-5.5(completed); verifier=gpt-5.5

The PR adds per-node and global admission caps for pending LLMQ sig shares, and the local map/count bookkeeping checks out for the paths reviewed. However, the global cap is released when shares are dispatched to the worker pool, before BLS verification completes, so the unbounded worker queue can still retain arbitrarily many pending verification batches. That leaves the PR's stated backpressure goal incomplete.

🔴 1 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)

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

- [BLOCKING] src/llmq/signing_shares.cpp:565: Keep dispatched verification work under the global cap
  `CollectPendingSigSharesToVerify` subtracts shares from `pendingIncomingSigSharesCount` as soon as they are copied into `retSigShares`, but `NetSigning::WorkThreadDispatcher` then moves those vectors into `worker_pool.push()` for later verification. That pool uses an unbounded `std::queue`, and only 1-4 worker threads drain it, so a peer can repeatedly fill the 10k pending map, let the dispatcher move batches into queued lambdas, and immediately refill the map while earlier batches are still retained and unverified. The new cap therefore limits only the pre-dispatch maps, not the actual retained verification backlog this PR is meant to bound. Keep queued-for-verification shares counted until the worker finishes, or add an explicit bounded in-flight/queued verification counter before accepting more incoming shares.

Note: posted as a COMMENT review because GitHub does not allow approving/requesting changes on my own PR via the normal review event.

@thepastaclaw
thepastaclaw force-pushed the fix/bound-pending-sig-share-queue branch from 18d2453 to 64c31bb Compare July 7, 2026 18:42
@thepastaclaw

Copy link
Copy Markdown
Author

Handled the review blocker in 64c31bb2e87.

What changed:

  • Kept shares dispatched to the BLS worker pool reserved against MAX_PENDING_SIG_SHARES_TOTAL until the worker finishes.
  • Released only erased-but-not-queued shares immediately, so duplicate/already-known shares do not leak the global counter.
  • Added a small RAII reservation helper around worker processing so queued-share accounting is released on early return paths.

Validation:

  • make -C src -j4 llmq/libbitcoin_node_a-net_signing.o llmq/libbitcoin_node_a-signing_shares.o test/test_dash-llmq_utils_tests.o test/test_dash
  • src/test/test_dash --run_test=llmq_utils_tests
  • COMMIT_RANGE=upstream/develop..HEAD test/lint/lint-whitespace.py
  • git diff --check upstream/develop..HEAD

@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

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

@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: 64c31bb2e8

ℹ️ 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/signing_shares.cpp Outdated
__func__, MAX_PENDING_SIG_SHARES_TOTAL, nodeId);
return false;
}
if (nodeState.GetPendingIncomingSigSharesCount() >= MAX_PENDING_SIG_SHARES_PER_NODE) {

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 Keep in-flight shares in the per-node cap

When BLS verification is slower than incoming traffic from one peer, this check only sees shares still in that peer's pendingIncomingSigShares map; CollectPendingSigSharesToVerify now erases dispatched shares from the node state while keeping only the global reservation until the worker releases them. Fresh evidence in this revision is that the global counter is reserved until ReleasePendingIncomingSigShares, but the per-node counter is decremented at dispatch, so the same peer can refill another 1000 shares repeatedly and occupy the full 10000-share global cap, starving other peers despite the new per-node limit.

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.

Code Review

Source: reviewers claude/general=opus(failed), codex/general=gpt-5.5(completed), claude/dash-core-commit-history=opus(failed), codex/dash-core-commit-history=gpt-5.5(completed); verifier=gpt-5.5.

Carried-forward prior findings: prior-18d24537-1 is FIXED by 64c31bb2e87; queued worker-pool sig shares now remain reserved against the global cap until ReleasePendingIncomingSigShares runs after processing. New findings in latest delta: the same reservation model was not applied to the per-node cap, so one peer can cycle shares into worker tasks and refill its node-state queue until it occupies the full 10000-share global budget. The latest commit also repairs the original cap commit and should be squashed for one logical change.

🔴 1 blocking | 🟡 1 suggestion(s)

Note: GitHub does not allow me to request changes on my own PR, so this is posted as a COMMENT while preserving the verified blocking findings.

Posting note: review_poster could not post an inline/top-level review cleanly; posting exact-SHA body-only review from the same verifier output.

Prior Finding Reconciliation

  • Keep dispatched verification work under the global cap — FIXED in 64c31bb2e87: CollectPendingSigSharesToVerify now keeps queued shares reserved against the global counter, and NetSigning::ProcessPendingSigShares releases that reservation after worker processing via PendingSigSharesReservation.

Carried-Forward Prior Findings

  • None.

New Findings In Latest Delta

  • blocking: Keep queued shares under the per-node cap toosrc/llmq/signing_shares.cpp:1308
  • suggestion: Squash the queued-share cap repair into the original cap commit<commit:64c31bb2e87>:1

Verified Findings

blocking: Keep queued shares under the per-node cap too

src/llmq/signing_shares.cpp:1308-1311

TryAddPendingIncomingSigShare enforces the per-node limit only with nodeState.GetPendingIncomingSigSharesCount(), but CollectPendingSigSharesToVerify erases shares from that node state when they are moved into worker-pool tasks. The new global reservation keeps those queued shares charged to pendingIncomingSigSharesCount until processing releases them, but the sender's node-state count drops immediately. A single peer can therefore fill its 1000-share per-node queue, let the dispatcher move those shares into queued verification work, and refill repeatedly until it monopolizes the full 10000-share global budget. Keep queued-but-unverified shares charged to the originating node, or add an in-flight per-node counter that is released together with the global reservation.

suggestion: Squash the queued-share cap repair into the original cap commit

<commit:64c31bb2e87>:1-1

64c31bb2e87 only repairs the cap accounting introduced by 1b040ba5e04: it keeps sig shares dispatched to the worker pool counted against the global pending-share cap until verification releases them. Since 1b040ba5e04 is already titled fix: bound pending sig share queue, leaving the repair as a separate permanent commit makes the first commit incomplete for the same logical change. Squash 64c31bb2e87 into 1b040ba5e04 before merge.

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

- [BLOCKING] src/llmq/signing_shares.cpp:1308-1311: Keep queued shares under the per-node cap too
  `TryAddPendingIncomingSigShare` enforces the per-node limit only with `nodeState.GetPendingIncomingSigSharesCount()`, but `CollectPendingSigSharesToVerify` erases shares from that node state when they are moved into worker-pool tasks. The new global reservation keeps those queued shares charged to `pendingIncomingSigSharesCount` until processing releases them, but the sender's node-state count drops immediately. A single peer can therefore fill its 1000-share per-node queue, let the dispatcher move those shares into queued verification work, and refill repeatedly until it monopolizes the full 10000-share global budget. Keep queued-but-unverified shares charged to the originating node, or add an in-flight per-node counter that is released together with the global reservation.
- [SUGGESTION] <commit:64c31bb2e87>:1-1: Squash the queued-share cap repair into the original cap commit
  `64c31bb2e87` only repairs the cap accounting introduced by `1b040ba5e04`: it keeps sig shares dispatched to the worker pool counted against the global pending-share cap until verification releases them. Since `1b040ba5e04` is already titled `fix: bound pending sig share queue`, leaving the repair as a separate permanent commit makes the first commit incomplete for the same logical change. Squash `64c31bb2e87` into `1b040ba5e04` before merge.

@thepastaclaw
thepastaclaw force-pushed the fix/bound-pending-sig-share-queue branch from 64c31bb to 7827750 Compare July 8, 2026 04:26
@thepastaclaw

Copy link
Copy Markdown
Author

Pushed 782775085e2 to address the exact-head self-review feedback.

  • Kept queued-but-unverified sig shares charged against the originating node's per-node reservation until worker processing releases them.
  • Preserved the global queued-share reservation from the prior fix.
  • Squashed the queued-share repairs into the original fix: bound pending sig share queue commit so the PR is back to one logical commit.

Local validation:

  • ./autogen.sh
  • ./configure --without-gui --disable-bench --disable-fuzz-binary
  • make -C src -j4 llmq/libbitcoin_node_a-net_signing.o llmq/libbitcoin_node_a-signing_shares.o test/test_dash-llmq_utils_tests.o test/test_dash
  • src/test/test_dash --run_test=llmq_utils_tests
  • test/lint/lint-tests.py
  • COMMIT_RANGE=HEAD^..HEAD test/lint/lint-whitespace.py
  • git diff --check HEAD^..HEAD

@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

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

Source: reviewers claude/general=opus(failed: quota), codex/general=gpt-5.5(completed), claude/dash-core-commit-history=opus(failed: quota), codex/dash-core-commit-history=gpt-5.5(completed); verifier=gpt-5.5.

Carried-forward prior findings: both prior-64c31bb2-1 and prior-64c31bb2-2 are fixed at 782775085e2388755a6a4dfb090c303fc635fc5a. New findings in the latest delta: none; the per-node in-flight reservation now keeps queued shares charged against the per-node cap, and the stack has been rewritten to one logical commit.

Note: GitHub does not allow me to approve my own PR, so this clean automated review is posted as a COMMENT.

Prior Finding Reconciliation

  • Keep queued shares under the per-node cap too — FIXED. The current code adds per-node in-flight reservation accounting and checks pending plus in-flight shares before accepting more from a node.
  • Squash the queued-share cap repair into the original cap commit — FIXED. The current stack is a single fix: bound pending sig share queue commit.

Carried-Forward Prior Findings

  • None.

New Findings In Latest Delta

  • None.

UdjinM6
UdjinM6 previously approved these changes Jul 8, 2026

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

utACK 782775085e2388755a6a4dfb090c303fc635fc5a

@knst

knst commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

concept ack, but implementation is a bit over-engineered:

    [[nodiscard]] size_t GetPendingIncomingSigSharesCount() const;
    [[nodiscard]] size_t GetPendingIncomingSigSharesReservedCount() const;
    Session* GetSessionBySignHash(const uint256& signHash);
    Session* GetSessionByRecvId(uint32_t sessionId);
    bool GetSessionInfoByRecvId(uint32_t sessionId, SessionInfo& retInfo);
    bool AddPendingIncomingSigShare(const CSigShare& sigShare);
    bool ErasePendingIncomingSigShare(const SigShareKey& key);
    void ReservePendingIncomingSigShares(size_t count);
    void ReleasePendingIncomingSigShares(size_t count);
    size_t ClearPendingIncomingSigShares();
    size_t ErasePendingIncomingSigSharesForSession(const uint256& signHash);

Instead I think better to create a dedicated datastructure which is a tuple of std::map<uint256, std::map<A, B>> and counts in O(1) total amount of elements. It has been implemented as CountedBucketMap in this branch: knst/fix/bound-pending-sig-share-queue-v2

That approach is better than current approach of PR due to several reasons:

  • logic of processing pending sigs and util code is separated
  • easier to provide a better coverage for this datastructure
  • in theory this data stacture [CountedBucketMap]

@thepastaclaw please check if any disadvantages with my https://github.com/knst/dash/tree/fix/bound-pending-sig-share-queue-v2-tmp and pull changes from there

@knst knst left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Comment thread src/test/llmq_utils_tests.cpp Outdated
{
CSigSharesNodeState node_state;

for (size_t i{0}; i < MAX_PENDING_SIG_SHARES_PER_NODE - 1; ++i) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit:

for (size_t i{1}; i < MAX_PENDING_SIG_SHARES_PER_NODE; ++i) {
  BOOST_CHECK(node_state.AddPendingIncomingSigShare(MakeSigShare(i)));

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.

Superseded by the CountedBucketMap integration at 906f8118251: the old per-node cap-loop test is removed, and the replacement tests exercise the counted map and session removal directly.

@thepastaclaw
thepastaclaw force-pushed the fix/bound-pending-sig-share-queue branch from 7827750 to 906f811 Compare July 9, 2026 21:02
@thepastaclaw

Copy link
Copy Markdown
Author

Checked the CountedBucketMap branch in detail and adopted it. I did not find a material disadvantage versus the existing PR design:

  • count maintenance is centralized in CountedBucketMap, removing the eight manual CSigSharesNodeState bookkeeping methods and counter-drift risk;
  • total admission becomes an O(peer-count) sum, which is trivial at our peer counts;
  • the four-batch worker cap bounds in-flight overshoot to at most 128 shares against the 10,000-share stored cap.

Force-pushed the reviewed four-commit stack as head 906f8118251, preserving Konstantin's authorship. Focused validation passed: llmq_utils_tests (9/9), whitespace, includes, Python, and circular-dependency lint.

@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: 906f811825

ℹ️ 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_signing.cpp Outdated
Comment on lines +340 to +341
ProcessPendingSigShares(std::move(sigSharesByNodes), std::move(quorums));
--unverified_batches;

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 Release unverified batch slots on exceptions

If ProcessPendingSigShares exits via an exception, this task never reaches the decrement, and the returned future from worker_pool.push is ignored so the failure is silent. One concrete path is a stale quorum: CollectPendingSigSharesToVerify can return false after filling sigSharesByNodes but without all retQuorums, then ProcessPendingSigShares hits quorums.at(...); after four such tasks unverified_batches < MAX_UNVERIFIED_BATCHES stays false and the dispatcher stops draining pending shares until restart. Use a scope guard/RAII cleanup so the slot is released on every exit path.

Useful? React with 👍 / 👎.

@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/net_signing.cpp`:
- Around line 338-342: The unverified_batches counter is not decremented if
ProcessPendingSigShares throws, which can permanently block new batches. In the
worker_pool lambda in net_signing.cpp, wrap the decrement with an RAII guard so
it always runs on every exit path, even when ProcessPendingSigShares or
quorums.at(...) throws. Keep the fix localized around the lambda that calls
ProcessPendingSigShares and updates unverified_batches.
🪄 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: 5a211296-01e8-4294-852e-24be50d824bd

📥 Commits

Reviewing files that changed from the base of the PR and between 782775085e2388755a6a4dfb090c303fc635fc5a and 906f811825176562cca4592d455e8a5c9549e8af.

📒 Files selected for processing (5)
  • src/llmq/net_signing.cpp
  • src/llmq/net_signing.h
  • src/llmq/signing_shares.cpp
  • src/llmq/signing_shares.h
  • src/test/llmq_utils_tests.cpp

Comment thread src/llmq/net_signing.cpp
@thepastaclaw

Copy link
Copy Markdown
Author

The linux64_tsan-test failure is pre-existing flakiness in feature_llmq_singlenode.py, not a regression from this PR. The test exhausted all three retries at the same line-178 recovered-signature cleanup wait on unrelated PR #7350 before this branch existed: https://github.com/dashpay/dash/actions/runs/27177094667/job/80231968170. An earlier unrelated UI PR also failed once and passed on retry: https://github.com/dashpay/dash/actions/runs/25496950766/job/74825897605.

This PR is proper as-is: it only bounds pending sig-share admission/verification work, while the failure happens after recovery and quorum rotation when CRecoveredSigsDb should expire an already recovered signature. Filed #7435 with the diagnosis and reproduction evidence: #7435. No branch change or CI retrigger was made.

CollectPendingSigSharesToVerify used to bound each dispatched batch by
unique (nodeId, signHash) sessions. A single session can carry up to the
full MAX_PENDING_SIG_SHARES_TOTAL (10000) shares, so with the four
in-flight batches allowed on the worker pool, tens of thousands of BLS
shares could sit outside the pending accounting.

Bound each batch by the actual number of shares added (32) instead. The
rotation-based fairness across peers is preserved: one share at a time
in randomized round-robin order. Combined with MAX_UNVERIFIED_BATCHES=4,
at most 128 shares can be in-flight in / on the worker pool at once.

Also collapse the O(peers) global-cap sum in TryAddPendingIncomingSigShare
into a running m_pending_sig_shares_total maintained on every mutation
site. The primitives now return the count they erased so the running
total stays in lockstep with the aggregate node-state size.

Add regression coverage for the count-returning primitives and for
CSigSharesNodeState::RemoveSession's returned drop count.

@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: 355da45d4a

ℹ️ 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".

auto& nodeState = nodeStates[pfrom.GetId()];
for (const auto& s : sigSharesToProcess) {
nodeState.pendingIncomingSigShares.Add(s.GetKey(), s);
TryAddPendingIncomingSigShare(pfrom.GetId(), nodeState, s);

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 request state when admission fails

When TryAddPendingIncomingSigShare returns false because the per-node or global pending-share cap is full, this loop silently drops a requested QBSIGSHARES response even though line 401 already removed the peer's requestedSigShares entry and the request path clears session.announced.inv once the request is sent. In that over-cap scenario the share is not verified and is not requested from the same peer again, so recovery can stall if no other peer advertises that quorum member; keep the request/announcement state until the share is admitted, or requeue it on admission failure.

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.

Code Review

Source: reviewers claude/general=opus(completed), codex/general=gpt-5.5(failed), claude/dash-core-commit-history=opus(completed), codex/dash-core-commit-history=gpt-5.5(failed); verifier=opus.

Carried-forward prior findings: none. New findings in latest delta: none. Prior finding prior-18d24537-1 (Global pending-sig-share cap check is O(peers) per admission) is FIXED at 355da45 — a running counter m_pending_sig_shares_total (src/llmq/signing_shares.h:477) is maintained at every mutation site (Add:504, Erase:547, RemoveSession:1412, Clear:1463, EraseAllForSignHash-related:1402/1434) so the global cap check at line 496 is now O(1). Cumulative review of upstream/develop...HEAD finds no additional issues.

Posting note: review_poster could not post an inline/top-level review cleanly; posting exact-SHA body-only review from the same verifier output.

Prior Finding Reconciliation

  • Global pending-sig-share cap check is O(peers) per admission — FIXED at 355da45. Introduced size_t m_pending_sig_shares_total GUARDED_BY(cs) (src/llmq/signing_shares.h:477) as a running total. All pendingIncomingSigShares mutation sites update it: ++ on successful Add (signing_shares.cpp:504), -= Erase return value on batch collection (547), RemoveSession (1412), Clear (1463), and EraseAllForSignHash-adjacent removals (1402, 1434). The global admission check at line 496 now reads the counter instead of iterating nodeStates, restoring O(1) behavior on the hot admission path.

Carried-Forward Prior Findings

  • None.

New Findings In Latest Delta

  • None.

Verified Findings

No active findings.

@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

@knst

knst commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

@thepastaclaw

Copy link
Copy Markdown
Author

@knst applied on top of as requested and pushed the branch at , preserving your authorship. Focused validation passed locally: GEN obj/build.h
make: `test/test_dash-llmq_utils_tests.o' is up to date., Running 10 test cases..., , and .

@thepastaclaw

Copy link
Copy Markdown
Author

@knst applied 25f2d9177a23c9045ec3e245b904ed7c7b1e54c1 on top of 355da45d4a45e7ceac552fd005a5519de3c13c62 as requested and pushed the branch at bdca48cde09153c54f67ef5b9b3faaf22be7f81d, preserving your authorship. Focused validation passed locally: make -C src -j4 llmq/libbitcoin_node_a-signing_shares.o test/test_dash-llmq_utils_tests.o test/test_dash, src/test/test_dash --run_test=llmq_utils_tests, COMMIT_RANGE=HEAD^..HEAD test/lint/lint-whitespace.py, and git diff --check HEAD^..HEAD.

@thepastaclaw

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

@knst knst left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

lgtm bdca48c (assuming CI succeed)

@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

Source: reviewers claude/general=opus(completed), codex/general=gpt-5.6-sol(failed), claude/dash-core-commit-history=opus(completed), codex/dash-core-commit-history=gpt-5.6-sol(failed); verifier=opus.

Carried-forward prior findings: none. The prior review at 355da45 had an empty active-finding set, so classification is not applicable and nothing is carried forward; I re-validated that the previously-clean areas remain clean at head bdca48c. New findings in latest delta: two non-blocking suggestions, both verified against the current checkout. (1) The head commit bdca48 fully reverts the running-total accounting mechanism introduced one commit earlier in 355da45 (member m_pending_sig_shares_total, count-returning Erase/EraseBucket/EraseIf/Clear/EraseAllForSignHash/RemoveSession primitives, and their asserting tests) — pure intra-PR churn that should be squashed, especially since 355da45's commit message documents an approach the shipped code does not use. (2) The cap/drop/banned enforcement in TryAddPendingIncomingSigShare has no direct unit coverage. The PR is otherwise a well-bounded DoS-hardening change; no blocking issues.

🟡 2 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 `src/llmq/signing_shares.h`:
- [SUGGESTION] src/llmq/signing_shares.h:459: Squash bdca48 into 355da45 — running-total mechanism is introduced and reverted within the same PR
  Commit 355da45 ("fix: cap sig-share verification batch by share count") introduces a global-cap accounting mechanism: the member `m_pending_sig_shares_total GUARDED_BY(cs)`, count-returning versions of the `CountedBucketMap`/`SigShareMap` primitives (`Erase`, `EraseBucket`, `EraseIf`, `Clear`, `EraseAllForSignHash`) and `CSigSharesNodeState::RemoveSession`, plus regression tests asserting those returned counts. Its commit message body explicitly documents this: "collapse the O(peers) global-cap sum ... into a running m_pending_sig_shares_total maintained on every mutation site. The primitives now return the count they erased ...".

  The immediately-following head commit bdca48 ("refactor: use CountedBucketMap for counting total amount of pending sigs") undoes all of it: it deletes `m_pending_sig_shares_total`, reverts every primitive and `RemoveSession` back to `void`, restores the O(peers) sum over `nodeStates[*].pendingIncomingSigShares.Size()` in `TryAddPendingIncomingSigShare` (signing_shares.cpp:496-501), and drops the count assertions from the tests.

  Net effect on permanent develop history: the running-total member, the count-returning API, and their asserting tests exist only in 355da45 and are erased by the very next commit — they never ship. A `git bisect` landing on 355da45 would show a design the shipped code never had, `git blame` records a return-type flip-flop across these primitives, and 355da45's message body describes an approach directly contradicted by the final code. Both commits compile independently and this is DoS/resource hardening rather than consensus logic, so it is not blocking, but it should be cleaned up before merge given the misleading permanent history. Squash bdca48 into 355da45 (e.g. mark bdca48 as `fixup!`/`squash!` of 355da45 and rebase --autosquash) and reword the merged message to drop the now-obsolete running-total rationale while keeping 355da45's genuinely shipped change (share-count batch bounding + MAX_UNVERIFIED_BATCHES) alongside the final O(peers)-sum cap check.

In `src/llmq/signing_shares.cpp`:
- [SUGGESTION] src/llmq/signing_shares.cpp:483-508: Cap/drop/banned enforcement in TryAddPendingIncomingSigShare has no direct unit coverage
  The PR's stated purpose is bounding pending incoming sig shares, but the enforcement logic in `TryAddPendingIncomingSigShare` — the per-node cap at `MAX_PENDING_SIG_SHARES_PER_NODE` (line 491), the global cap at `MAX_PENDING_SIG_SHARES_TOTAL` (line 502), and the banned-state refusal (line 488) — is not exercised by any added test. A grep of llmq_utils_tests.cpp finds no reference to `TryAddPendingIncomingSigShare`, `MAX_PENDING_SIG_SHARES`, or the banned path; the new tests only cover the supporting `CountedBucketMap` size tracking and `RemoveSession` behavior. So the machinery the caps rely on (`Size()` accuracy) is tested, but the threshold/drop-without-misbehavior/banned-refusal behavior that is the point of the change is not. `TryAddPendingIncomingSigShare` is private and needs a `CSigSharesManager` plus quorum setup to drive, which makes this materially harder than the node-state-level tests already added, so this is a suggestion rather than a blocker.

Comment on lines +483 to +508
bool CSigSharesManager::TryAddPendingIncomingSigShare(NodeId nodeId, CSigSharesNodeState& nodeState,
const CSigShare& sigShare)
{
AssertLockHeld(cs);

if (nodeState.banned) {
return false;
}
if (nodeState.pendingIncomingSigShares.Size() >= MAX_PENDING_SIG_SHARES_PER_NODE) {
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- per-node pending sig shares cap reached (%d), dropping sigShare. node=%d\n",
__func__, MAX_PENDING_SIG_SHARES_PER_NODE, nodeId);
return false;
}
size_t total{0};
for (const auto& [_, ns] : nodeStates) {
// the size of nodeStates is limited by DEFAULT_MAX_PEER_CONNECTIONS(125) so it should not be performance issue
// The name of variable is intentionally mentioned in comment to make this code snippet relevant for possible changes in future
total += ns.pendingIncomingSigShares.Size();
}
if (total >= MAX_PENDING_SIG_SHARES_TOTAL) {
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- global pending sig shares cap reached (%d), dropping sigShare. node=%d\n",
__func__, MAX_PENDING_SIG_SHARES_TOTAL, nodeId);
return false;
}
return nodeState.pendingIncomingSigShares.Add(sigShare.GetKey(), sigShare);
}

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.

🟡 Suggestion: Cap/drop/banned enforcement in TryAddPendingIncomingSigShare has no direct unit coverage

The PR's stated purpose is bounding pending incoming sig shares, but the enforcement logic in TryAddPendingIncomingSigShare — the per-node cap at MAX_PENDING_SIG_SHARES_PER_NODE (line 491), the global cap at MAX_PENDING_SIG_SHARES_TOTAL (line 502), and the banned-state refusal (line 488) — is not exercised by any added test. A grep of llmq_utils_tests.cpp finds no reference to TryAddPendingIncomingSigShare, MAX_PENDING_SIG_SHARES, or the banned path; the new tests only cover the supporting CountedBucketMap size tracking and RemoveSession behavior. So the machinery the caps rely on (Size() accuracy) is tested, but the threshold/drop-without-misbehavior/banned-refusal behavior that is the point of the change is not. TryAddPendingIncomingSigShare is private and needs a CSigSharesManager plus quorum setup to drive, which makes this materially harder than the node-state-level tests already added, so this is a suggestion rather than a blocker.

source: ['claude']

@kwvg kwvg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

utACK bdca48c

@PastaPastaPasta
PastaPastaPasta merged commit ecf2382 into dashpay:develop Jul 11, 2026
48 checks passed
@UdjinM6 UdjinM6 added this to the 24 milestone Jul 11, 2026
thepastaclaw pushed a commit to thepastaclaw/dash that referenced this pull request Jul 22, 2026
bdca48c refactor: use CountedBucketMap for counting total amount of pending sigs (Konstantin Akimov)
355da45 fix: cap sig-share verification batch by share count (PastaClaw)
08d9533 fix: cap amount of unverified batches by 4 (Konstantin Akimov)
e2862fe fix: the actual cap enforcement for pending signatures (Konstantin Akimov)
4666ef3 test: add regression tests for CountedBucketMap (Konstantin Akimov)
ec15f90 refactor: use dedicated util structure to count internal objects in signing-shares (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented

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

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

  ## What was done?

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

  ## How Has This Been Tested?

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

  ## Breaking Changes

  None.

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

ACKs for top commit:
  kwvg:
    utACK bdca48c

Tree-SHA512: e2403635a608aab5488dbf10b481a778e735a6e5b065eec5a4cc444818f61e89b3a688f82c14aaceae07fd714432d39c013769a54aa66e31bb8abddb3531aaf3
thepastaclaw pushed a commit to thepastaclaw/dash that referenced this pull request Jul 22, 2026
bdca48c refactor: use CountedBucketMap for counting total amount of pending sigs (Konstantin Akimov)
355da45 fix: cap sig-share verification batch by share count (PastaClaw)
08d9533 fix: cap amount of unverified batches by 4 (Konstantin Akimov)
e2862fe fix: the actual cap enforcement for pending signatures (Konstantin Akimov)
4666ef3 test: add regression tests for CountedBucketMap (Konstantin Akimov)
ec15f90 refactor: use dedicated util structure to count internal objects in signing-shares (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented

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

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

  ## What was done?

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

  ## How Has This Been Tested?

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

  ## Breaking Changes

  None.

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

ACKs for top commit:
  kwvg:
    utACK bdca48c

Tree-SHA512: e2403635a608aab5488dbf10b481a778e735a6e5b065eec5a4cc444818f61e89b3a688f82c14aaceae07fd714432d39c013769a54aa66e31bb8abddb3531aaf3
PastaPastaPasta added a commit to PastaPastaPasta/dash that referenced this pull request Jul 29, 2026
Backport of dashpay#7415 (upstream merge ecf2382, cherry-picked with -m1).

v23.1.x adaptations: (1) the per-node/global pending caps, TryAddPendingIncomingSigShare, CountedBucketMap and the share-count batch bound apply unchanged; only develop-only context around them (the dashpay#7115 NetSigning dispatcher, GetMaxSessionsForPeer session caps) was not brought along. (2) Develop's MAX_UNVERIFIED_BATCHES cap on dispatched-but-unverified worker-pool batches has no counterpart here: on this branch shares leave the capped pending maps only inside the worker that immediately verifies them (ProcessPendingSigSharesLoop), so shares never accumulate in the pool's task queue and there is nothing to bound. (3) llmq_utils_tests.cpp gains the PR's three new test cases plus the MakeSigShare helper they need (taken verbatim from develop, where an out-of-scope earlier PR introduced it); develop-only session-limit tests are not included.

(cherry picked from commit ecf2382)
PastaPastaPasta added a commit to PastaPastaPasta/dash that referenced this pull request Jul 29, 2026
Backport of dashpay#7415 (upstream merge ecf2382, cherry-picked with -m1).

v23.1.x adaptations: (1) the per-node/global pending caps, TryAddPendingIncomingSigShare, CountedBucketMap and the share-count batch bound apply unchanged; only develop-only context around them (the dashpay#7115 NetSigning dispatcher, GetMaxSessionsForPeer session caps) was not brought along. (2) Develop's MAX_UNVERIFIED_BATCHES cap on dispatched-but-unverified worker-pool batches has no counterpart here: on this branch shares leave the capped pending maps only inside the worker that immediately verifies them (ProcessPendingSigSharesLoop), so shares never accumulate in the pool's task queue and there is nothing to bound. (3) llmq_utils_tests.cpp gains the PR's three new test cases plus the MakeSigShare helper they need (taken verbatim from develop, where an out-of-scope earlier PR introduced it); develop-only session-limit tests are not included.

(cherry picked from commit ecf2382)
PastaPastaPasta added a commit to PastaPastaPasta/dash that referenced this pull request Jul 29, 2026
Backport of dashpay#7415 (upstream merge ecf2382, cherry-picked with -m1).

v23.1.x adaptations: (1) the per-node/global pending caps, TryAddPendingIncomingSigShare, CountedBucketMap and the share-count batch bound apply unchanged; only develop-only context around them (the dashpay#7115 NetSigning dispatcher, GetMaxSessionsForPeer session caps) was not brought along. (2) Develop's MAX_UNVERIFIED_BATCHES cap on dispatched-but-unverified worker-pool batches has no counterpart here: on this branch shares leave the capped pending maps only inside the worker that immediately verifies them (ProcessPendingSigSharesLoop), so shares never accumulate in the pool's task queue and there is nothing to bound. (3) llmq_utils_tests.cpp gains the PR's three new test cases plus the MakeSigShare helper they need (taken verbatim from develop, where an out-of-scope earlier PR introduced it); develop-only session-limit tests are not included.

(cherry picked from commit ecf2382)
PastaPastaPasta added a commit to PastaPastaPasta/dash that referenced this pull request Jul 29, 2026
Backport of dashpay#7415 (upstream merge ecf2382, cherry-picked with -m1).

v23.1.x adaptations: (1) the per-node/global pending caps, TryAddPendingIncomingSigShare, CountedBucketMap and the share-count batch bound apply unchanged; only develop-only context around them (the dashpay#7115 NetSigning dispatcher, GetMaxSessionsForPeer session caps) was not brought along. (2) Develop's MAX_UNVERIFIED_BATCHES cap on dispatched-but-unverified worker-pool batches has no counterpart here: on this branch shares leave the capped pending maps only inside the worker that immediately verifies them (ProcessPendingSigSharesLoop), so shares never accumulate in the pool's task queue and there is nothing to bound. (3) llmq_utils_tests.cpp gains the PR's three new test cases plus the MakeSigShare helper they need (taken verbatim from develop, where an out-of-scope earlier PR introduced it); develop-only session-limit tests are not included.

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

Pull request description:

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

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

  ## Contents

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

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

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

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

  ## Note for reviewers: this branch was rebuilt

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

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

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

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

  ## Dropped from this branch

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

  ## Added after the initial review pass

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

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

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

  ## Adaptations worth flagging

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

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

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

  ## Known CI failure

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

  ## Testing

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

Top commit has no ACKs.

Tree-SHA512: 0fa469c9a33820aa85fbb8b90c5877409d09490f746f1300b05ceda470a600765f900bec42e5aad5d90a2d46b44e28c20e44dc4a8fe719553a072298069eaec4
@UdjinM6 UdjinM6 modified the milestones: 24, 23.1.8 Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants