Phase 7.2b-4a: candidate-culprit blame classifier#4063
Merged
mswilkison merged 2 commits intoJun 15, 2026
Merged
Conversation
ClassifyCandidateCulprits turns the engine's candidate culprits (the members whose FROST shares failed verification, #4062) into this observer's RejectEntry accusations, adjudicated against the Round2Collector's retained operator-signed bytes - where the engine's crypto verdict becomes envelope-bound, attributable member blame (frozen Q1 boundary; the engine never inspects envelopes). Per attempt, for each candidate against the authoritative package: - accepted retained share that re-verifies INVALID -> RejectEntry (the observer holds the member's operator-signed share: self-incriminating, independently checkable; feeds NextAttempt's existing f+1 establishment gate). - accepted share that re-verifies VALID (yet flagged) -> nothing: not self-incriminating under this observer's package; coordinator-directed faults are a SEPARATE path (7.2b-4b), never inferred here. - divergent share only -> nothing (NEUTRAL: possible targeted coordinator equivocation, must not alone exclude). - no retained share / indeterminate re-verification -> nothing (fail closed). FROST share re-verification is engine-backed (Go has no native FROST share crypto - the collector's SignatureVerifier checks operator sigs, not the share equation): the new Round2ShareVerifier interface is that seam, injected so this lands and reviews now with a fake verifier; the engine-backed impl wires in with the interactive orchestration. Reuses the existing reject category + ExclusionAccuserQuorum/NextAttempt f+1 machinery; ConflictEntry stays reserved for self-conflicting bytes. Deterministic output (deduped, ascending by member, Count 1). Retained bytes are snapshotted under the collector lock; re-verification runs lock-free. Design converged via Codex+Gemini consultation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…4063) The (valid bool, error) verifier seam conflated a member's OWN undecodable / garbage FROST share bytes (operator-signed -> self-incriminating -> blame) with a not-the-member's-fault execution failure. A Go FFI bridge would naturally map a Rust/FROST deserialization error to a Go error, which the classifier read as "indeterminate -> don't blame", letting that cheater dodge a RejectEntry. Replace it with an explicit ShareVerificationResult tri-state (ShareValid / ShareInvalid / ShareIndeterminate): ShareInvalid covers both a mathematically invalid share AND undecodable member bytes; ShareIndeterminate is reserved for failures that are NOT the member's fault. The classifier blames ONLY ShareInvalid - everything else, including any future verdict, fails closed. This forces the engine-backed implementer to categorize the boundary deliberately rather than leak member-fault into an error channel. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3dcaf5b
into
feat/frost-schnorr-migration-scaffold
15 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
ClassifyCandidateCulprits(Phase 7.2b-4a) turns the engine's candidate culprits (members whose FROST shares failed verification — shipped in the Rust engine as #4062) into this observer'sRejectEntryaccusations, adjudicated against theRound2Collector's retained operator-signed bytes. This is where the crypto-only engine's verdict becomes envelope-bound, attributable member blame — the Go side of the frozen Q1 boundary. Design converged via a Codex+Gemini consultation.Classification (per candidate, against the attempt's authoritative package)
RejectEntry{Sender, "invalid_signature_share", 1}ShareInvalidagainst the package it accepted — independently checkable (RFC-21 Layer B's "no bare counters"). Accusations feed this observer'sLocalEvidenceSnapshot.Rejects→NextAttempt's existing f+1 establishment gate; this never excludes anyone by itself.The
Round2ShareVerifierseam (tri-state)FROST share re-verification is engine-backed: Go has no native FROST share crypto (the collector's
SignatureVerifierchecks operator signatures, not the FROST share equation). The newRound2ShareVerifierinterface is that seam — pure crypto, no blame, within the engine's crypto-only boundary. It returns a deliberate tri-stateShareVerificationResult, not(bool, error):ShareValid— valid FROST share → don't blame.ShareInvalid— member-attributable garbage: mathematically invalid or undecodable share bytes the member operator-signed → blame.ShareIndeterminate— not the member's fault (missing verifying material, ambiguous key/root, undecodable authoritative package, engine/FFI failure) → fail closed.The classifier blames only
ShareInvalid. The enum forces the (future, engine-backed) implementer to categorize the member-fault boundary deliberately — a(bool, error)shape would silently route a member's undecodable bytes into the error channel and let the cheater dodge blame. It's injected, so this lands/reviews now with a fake verifier; the engine-backed impl wires in with the (still-unbuilt) interactive orchestration.Notes
rejectcategory +ExclusionAccuserQuorum/NextAttemptf+1 machinery;ConflictEntrystays reserved for self-conflicting bytes.Count 1) so honest observers over identical bytes agree byte-for-byte.Tests
round2_classifier_test.go— full matrix (ShareInvalid→reject, ShareValid→nothing, ShareIndeterminate→nothing, divergent→neutral + verifier-not-consulted, absent→nothing + not-consulted), multi-candidate dedupe/sort/determinism, errors (nil verifier, unknown attempt, no package, no candidates). Fullroastpackage + gofmt + vet green.Review fold
8f8e5bf1a): replaced the(valid, err)verifier seam with theShareVerificationResulttri-state so a member's undecodable/garbage share bytes can't be misrouted as "indeterminate" and dodge blame. Codex: no issues.Follow-ups
Round2ShareVerifierimpl, wired with the interactive orchestration.🤖 Generated with Claude Code