Skip to content

fix: stop caching invalid governance votes - #7569

Open
PastaPastaPasta wants to merge 1 commit into
dashpay:developfrom
PastaPastaPasta:t3code/fix-invalid-vote-cache-keys
Open

fix: stop caching invalid governance votes#7569
PastaPastaPasta wants to merge 1 commit into
dashpay:developfrom
PastaPastaPasta:t3code/fix-invalid-vote-cache-keys

Conversation

@PastaPastaPasta

@PastaPastaPasta PastaPastaPasta commented Aug 10, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Governance cached permanently rejected votes by their semantic vote hash, which excludes the signature. A forged signature cached first could therefore suppress a later legitimate signature over the same vote fields.

The cache did not provide meaningful protection against adaptive verification work: a peer can always produce a fresh cache miss by changing the signature, timestamp, or parent hash. Invalid votes already carry a misbehavior penalty of 20, discouraging a peer after five failures.

What was done?

  • Removed invalid-vote cache lookup and insertion from vote processing.
  • Removed the in-memory invalid-vote cache and its key-rotation cleanup.
  • Preserved the existing governance.dat version and field layout by consuming legacy invalid-cache entries during deserialization and writing the historical field back empty.
  • Added ECDSA and BLS regressions proving a forged vote cannot suppress a legitimate vote with the same semantic hash.
  • Added persistence coverage proving legacy invalid-cache entries are discarded without losing following governance objects.

Valid-vote inventory and deduplication remain keyed by the existing semantic vote hash.

How Has This Been Tested?

  • make -j4
  • ./src/test/test_dash --run_test=governance_vote_processing_tests
  • ./src/test/test_dash --run_test=cachemap_tests
  • make check -j4
  • test/lint/all-lint.py

Tested on macOS arm64 using the repository's depends toolchain.

Breaking Changes

None. This does not change vote serialization, inventory hashes, protocol versions, database versions, consensus rules, or valid-vote deduplication.

Checklist:

  • I have performed a self-review of my own code
  • 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

This pull request was created by Codex.

@github-actions

github-actions Bot commented Aug 10, 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:

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@PastaPastaPasta, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b11da17f-cd34-484e-b69c-9768ad408d65

📥 Commits

Reviewing files that changed from the base of the PR and between f57cb59 and 6a06268.

📒 Files selected for processing (1)
  • src/governance/governance.h

Walkthrough

Governance vote processing no longer caches permanently invalid votes. Valid votes remain indexed by GetHash(). The store no longer retains the invalid-vote cache, but serialization preserves the historical field and discards legacy entries during deserialization. Tests cover forged signatures, valid votes with reused hashes, and version-16 store compatibility.

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

Possibly related PRs

  • dashpay/dash#7527: Both changes modify invalid-vote handling in CGovernanceManager::ProcessVote.
  • dashpay/dash#7528: Both changes update governance vote-processing tests for signature and invalid-vote behavior.

Suggested reviewers: knst

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.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
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the removal of invalid governance vote caching.
Description check ✅ Passed The description directly explains the invalid-vote cache issue, implementation, compatibility handling, and tests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@thepastaclaw

thepastaclaw commented Aug 10, 2026

Copy link
Copy Markdown

✅ Final review complete — no blockers (commit 6a06268)

@PastaPastaPasta
PastaPastaPasta force-pushed the t3code/fix-invalid-vote-cache-keys branch from 2cc4932 to f57cb59 Compare August 10, 2026 01:53
@PastaPastaPasta PastaPastaPasta changed the title fix: include signatures in invalid governance vote cache keys fix: stop caching invalid governance votes Aug 10, 2026
@PastaPastaPasta
PastaPastaPasta force-pushed the t3code/fix-invalid-vote-cache-keys branch from f57cb59 to 6a06268 Compare August 10, 2026 01:56

@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: f57cb5974c

ℹ️ 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 on lines 847 to 850
bool fOk = govobj.ProcessVote(m_mn_metaman, fRateChecksEnabled, tip_mn_list, vote, exception);
if (fOk) {
fOk = cmapVoteToObject.Insert(nHashVote, it->second);
} else if (exception.GetType() == GOVERNANCE_EXCEPTION_PERMANENT_ERROR && exception.GetNodePenalty() == 20) {
cmapInvalidVotes.Insert(nHashVote, vote);
}

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 Retain exact invalid-vote deduplication

When peers repeatedly advertise and serve the same invalid BLS or ECDSA vote for a known governance object, each wire message is deserialized into a fresh CGovernanceVote, so its per-instance signature memo starts empty. Because this branch now stores only successful votes, ProcessVote repeats the expensive signature verification while holding cs_store, and ConfirmInventoryRequest continues requesting the hash because cmapVoteToObject never contains it. The removed global cache bounded exact repeats to one verification; retain an invalid cache keyed by the semantic vote hash plus signature instead of eliminating it.

AGENTS.md reference: AGENTS.md:L162-L171

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

as discussed in the PR description; this was the first thought, but was decided against. An attacker can just twiddle with bits inside the vote to make more and more invalid votes. they are limited to 5 before their connection is dropped.

Avoiding the verify operation is fine, but isn't much (5 BLS verifies or whatever). And introduces bugs such as what this is fixing.

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

Final validation — Codex/Sol only (Phase 2 disabled)

At exact head 6a06268, the change removes semantic-hash caching of invalid governance votes while preserving valid-vote deduplication and the version-16 governance.dat field layout; the ECDSA, BLS, and persistence tests directly cover the intended regressions. The earlier exact-replay caching suggestion is non-actionable because each invalid response incurs a 20-point peer penalty and an attacker can trivially bypass a signature-aware cache by varying the signature or other hashed fields.
Source: reviewers gpt-5.6-sol (general) and gpt-5.6-sol (dash-core-commit-history); final verifier gpt-5.6-sol. Orchestration-only: openclaw-agent/cliproxy/gpt-5.6-sol (not reviewer evidence).

Validated zero-blocker Codex/Sol precheck evidence was promoted to final because Phase 2 (Sonnet/Opus) is temporarily disabled. This is Codex/Sol-only final validation, not Codex + Sonnet/Opus coverage.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed), gpt-5.6-sol — dash-core-commit-history (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet/Opus: not run (Phase 2 disabled — temporary Codex/Sol-only final)
  • Secondary pass: disabled (temporary_phase2_sonnet_disable)

@PastaPastaPasta

Copy link
Copy Markdown
Member Author

LGTM

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants