Skip to content

kaiax/vrank: report candidate failures against the proposer's own pri… - #992

Merged
ian0371 merged 5 commits into
kaiachain:devfrom
hyunsooda:fix/vrank-cfs-solicitor-self-report
Aug 5, 2026
Merged

kaiax/vrank: report candidate failures against the proposer's own pri…#992
ian0371 merged 5 commits into
kaiachain:devfrom
hyunsooda:fix/vrank-cfs-solicitor-self-report

Conversation

@hyunsooda

@hyunsooda hyunsooda commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Proposed changes

The candidate-failure report (cfReport) for block N was written by the proposer of block N+1, so failures were attributed to that writer rather than to block N's own proposer that requested the responses. A proposer that withholds its VRankPreprepare then has the induced failures charged to honest writers, spread across proposers, surviving the top-F byzantine filter.

Now each proposer reports on its own most recent prior proposal (same epoch). A candidate failure is scored against the reporting block's own proposer, so a withheld or fabricated failure lands in that proposer's own byzantine-filterable column and is filtered — the report carries only the failed candidate list.

Since only that proposer uses the collected responses, a candidate now sends its VRankCandidate to the proposer that sent the VRankPreprepare instead of broadcasting to the whole committee, and only the proposer records and collects the replies.

  • types: non-epoch header.VRank is the failed candidate list
  • consensus: report own prior proposal; validate the list against CandTesting
  • handler/collector: candidates reply to the proposer; only the proposer collects, retaining its own-proposal views until reported
  • scoring: attribute failures to the reporting block's proposer

Types of changes

  • 🐛 Bug fix
  • ✨ Non-hardfork changes (node upgrade not required)
  • 💥 Hardfork / consensus-breaking changes
  • 🧰 CI / build tool
  • 🧪 Test improvements
  • ♻️ Chore / Refactor / Non-functional changes

Checklist

  • 📖 I have read the CONTRIBUTING GUIDELINES doc
  • 📝 I have signed in the PR comment I have read the CLA Document and I hereby sign the CLA in first time contribute after having read CLA
  • 🟢 Lint and unit tests pass locally with my changes ($ make test)

Related issues

Further comments

@hyunsooda hyunsooda self-assigned this Jul 22, 2026
Comment thread kaiax/vrank/types.go Outdated
@hyunsooda

Copy link
Copy Markdown
Contributor Author

@ian0371 Previously a candidate broadcast VRankCandidate to all validators; now it sends only to the proposer that sent the VRankPreprepare, since only that proposer collects and reports on the view.

Comment thread kaiax/vrank/impl/consensus_test.go
Comment thread kaiax/vrank/impl/init.go Outdated
@hyunsooda
hyunsooda force-pushed the fix/vrank-cfs-solicitor-self-report branch from 9e36cb1 to 60490dc Compare July 29, 2026 08:53
ian0371
ian0371 previously approved these changes Jul 30, 2026
hyunsooda and others added 5 commits August 4, 2026 09:42
…or block

A proposer now writes the candidate-failure report (cfReport) for its own
most recent prior proposal in the same epoch, instead of for the immediately
previous block. header.VRank carries the target block number, and VerifyHeader
requires proposer(target) == proposer(block). This makes the report's writer
the same validator that solicited the candidates for the target block, so a
withheld or fabricated failure is attributed to that validator's own column
and absorbed by the top-F byzantine filter.

Changes:
- types: non-epoch header.VRank payload is now CfReport{TargetBlock, Failed}
- consensus: PrepareHeader reports the proposer's own prior proposal;
  VerifyHeader binds target to the same proposer and requires it be prior and
  same-epoch
- handler: track own proposals and retain their collector views until reported
- collector: RemoveOldViews keeps protected (own-proposal) views past the window
- scoring: aggregation reads the target block and skips cross-epoch reports

The non-epoch header.VRank encoding changes (consensus-affecting); this must
land before the permissionless fork activates. It is safe now because
PermissionlessCompatibleBlock is nil on Mainnet and Kairos, so no committed
block carries the previous format.

Confidence: high
Scope-risk: moderate

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The non-epoch VRank cfReport carried a target block (the proposer's own most
recent prior proposal) alongside the failed candidate list. Scoring attributes
every failure to the reporting block's own proposer, so the target block changed
neither scoring nor the property that keeps a withheld or fabricated failure in
the reporting proposer's own byzantine-filterable column.

Remove it: the report becomes the failed address list, VerifyHeader drops the
prior/same-epoch/proposer-match checks and their errors, and the cross-epoch
guard in the CP-matrix aggregation (dead once the target is gone) is removed too.

Confidence: high
Scope-risk: narrow

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The block's proposer is the only node that reports on a view — it evaluates the
candidate replies to its own proposal and records the result in its next
proposal. So a candidate now replies to that proposer alone instead of
broadcasting VRankCandidate to the whole committee, and only the proposer
records the preprepared time and collects the replies.

HandleVRankCandidate now accepts a reply only for a view this node proposed
(collector.HasPreprepared), which drops forged or misdirected messages before
signature recovery and bounds collector memory to the node's own pending
proposals. This removes the committee-wide collection path: the isCommitteeMember
gate, the prepreparedView field, and the prepreparedView-based staleness/window
guard (ErrPrepreparedViewNotSet, ErrTooFar) are no longer needed.

Confidence: high
Scope-risk: moderate
A non-epoch cfReport is about an earlier block of the same epoch, but VerifyHeader
resolves candidate membership at the reporting block's N-1. That holds because
CandTesting changes only at epoch transitions, and nothing pinned it: the other
subtests wire GetCandTesting to answer at any height, so the lookup height was
free to drift.

Confidence: high
Scope-risk: narrow

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The collector pruned views on a fixed 10-block window, which fit the old
committee-wide collection but not a proposer reporting on its own prior proposal
tens of blocks back. That gap was bridged by ownProposals: a second map of the
sequences this node proposed, passed in as a protected set the window had to
skip. The window was the wrong rule, and the protected set existed only to
survive it.

Prune by report progress instead. PruneReported(upto) drops every view below a
sequence, called with the block just reported (its own rounds stay, so a round
change or a failed commit re-reports it) and with the epoch start on each
proposal, which is the only drain for a node that never has a block to report.
selectReportTarget reads the pending sequences straight from the collector via
PendingEvaluations, so ownProposals, its mutex, and its three helpers are gone.

Confidence: high
Scope-risk: narrow

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@hyunsooda
hyunsooda force-pushed the fix/vrank-cfs-solicitor-self-report branch from 60490dc to cbee344 Compare August 4, 2026 00:44

@2dvorak 2dvorak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't we update vrank/README.md accordingly?

@ian0371

ian0371 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Let's update vrank/README.md afterwards thanks.

@ian0371
ian0371 merged commit 5b0890b into kaiachain:dev Aug 5, 2026
9 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants