linera-io/linera-protocol · error · ChainError
EquivocationProofValidJustification
EquivocationProofValidJustification
Error message
Equivocation proof's opened justification is a valid quorum
What it means
Thrown by EquivocationProof::check for an InvalidJustification proof when the cited opening (CommittedQuorum) actually passes check_cited_quorum — i.e. it is a justification an honest voter could legitimately have cited (ChainError::EquivocationProofValidJustification). The proof's thesis is that the validator signed a commitment to an invalid opening; if the opening is valid, no fault is attributable and the proof is malformed. The signature itself is verified first, binding the validator to exactly this opening, before validity is assessed.
Source
Thrown at linera-chain/src/justification/mod.rs:589
kind,
unlocking_round,
first_round,
signature,
opening,
} => {
// The signed payload contains the opening's hash, so verifying the signature
// binds the validator to exactly this opening.
let value = VoteValue(
CryptoHash::new(header),
*round,
*kind,
*unlocking_round,
*first_round,
Some(opening.commitment()),
);
signature.check(&value, *validator)?;
// The opening must be one that no honest voter could have cited.
ensure!(
check_cited_quorum(header, *round, *kind, *unlocking_round, opening, committee)
.is_err(),
ChainError::EquivocationProofValidJustification
);
Ok(())
}
}
}
}
/// Checks that `opening` is a quorum an honest voter could cite from a vote of the given kind,
/// round and unlocking round for the block with the given header: it validates the same block, in
/// the round the vote's payload grounds on, and its signatures form a genuine quorum of
/// `committee` over the reconstructed `ValidatedBlock` payload. These are exactly the checks a
/// voter performs before signing the opening's commitment, so their failure on a signed opening
/// convicts the signer.
fn check_cited_quorum(
header: &BlockHeader,View on GitHub (pinned to 6c226ddcb3)
Solutions
- Verify the proof against the committee of the epoch in which the vote was cast (the same committee the opening's signatures are checked against).
- Before reporting, re-run check_cited_quorum on the opening and require it to fail for the specific reason expected (hash mismatch, round grounding, or quorum failure).
- If the opening is valid, withdraw the fault report — the validator did nothing wrong.
- Add a regression test that builds a genuinely invalid opening (e.g. wrong value hash) to confirm the detector path.
Example fix
// before
let proof = InvalidJustification { opening: valid_quorum.clone(), .. };
proof.check(&committee)?; // Err: opening is valid, no fault
// after
if check_cited_quorum(header, round, kind, unlocking_round, &opening, &committee).is_ok() {
return Ok(()); // not a fault: opening is honest evidence
}
let proof = InvalidJustification { opening, .. }; Defensive patterns
Strategy: validation
Validate before calling
// only report a fault when the opening is genuinely invalid
if check_cited_quorum(header, round, kind, unlocking_round, &opening, &epoch_committee).is_ok() {
return Ok(()); // opening is honest: no fault to report
} Try / catch
match proof.check(committee) {
Err(ChainError::EquivocationProofValidJustification) => {
// withdraw the report; possibly wrong committee — retry with the vote's epoch committee
let epoch_committee = committee_of_epoch(vote_epoch)?;
proof.check(&epoch_committee)
}
other => other,
} Prevention
- Verify proofs against the committee of the epoch the vote was cast in.
- Require check_cited_quorum to fail for the expected reason before reporting a fault.
- Test detectors with both valid and invalid openings.
When it happens
Trigger: Submitting an EquivocationProof::InvalidJustification whose opening is a genuine quorum: right block hash, correctly grounded round relationship, valid quorum signatures for the committee passed to check. Common when the committee used for verification is the wrong epoch's (making an invalid opening look valid), or when the proof was built from an opening that was in fact correct.
Common situations: Epoch transitions: verifying a proof against a committee where the opening's signers happen to hold weight, or where threshold interpretation differs; misimplemented fault detectors flagging honest voters; openings whose round/kind relationship satisfies the rules by accident of construction.
Related errors
- JustificationCommitmentMismatch
- Equivocation proof must reference two different blocks
- EquivocationProofDifferentChainOrHeight
- EquivocationProofNoLockViolation
- EquivocationProofSameBlock
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/04d3182565ce5f8e.
Report an issue: GitHub.