linera-io/linera-protocol · error · ChainError
Equivocation proof must reference two different blocks
Error message
Equivocation proof must reference two different blocks
What it means
Thrown by EquivocationProof::check for a LockViolation proof when the confirmed block and the validated block have the same hash (ChainError::EquivocationProofSameBlock). A lock violation requires the validator to have confirmed one block and validated a different block; if both votes reference the identical block there is no contradiction, so the proof is malformed and check rejects it before signature verification.
Source
Thrown at linera-chain/src/justification/mod.rs:428
/// and signed by the named validator.
pub fn check(&self, committee: &Committee) -> Result<(), ChainError> {
match self {
EquivocationProof::LockViolation {
validator,
confirmed_header,
confirmed_round,
confirmed_attested,
confirmed_commitment,
confirmed_signature,
validated_header,
validated_round,
validated_unlocking_round,
validated_commitment,
validated_signature,
} => {
let confirmed_block_hash = CryptoHash::new(confirmed_header);
let validated_block_hash = CryptoHash::new(validated_header);
ensure!(
confirmed_block_hash != validated_block_hash,
ChainError::EquivocationProofSameBlock
);
// The two votes must concern the same height on the same chain; otherwise there
// is no lock relationship between them — a validator may freely confirm a block at
// one height and validate a different one at another height or on another chain.
ensure!(
confirmed_header.chain_id == validated_header.chain_id
&& confirmed_header.height == validated_header.height,
ChainError::EquivocationProofDifferentChainOrHeight
);
// The unlocking-round claim — "no confirmation of a different block in any round
// at or above the unlocking round" — is made while validating in
// `validated_round`, so it covers only the rounds the voter had already acted in:
// the window `[unlocking_round, validated_round)` (an unlocking round of `None`
// means `0`). The confirmation contradicts it only if it falls in that window,
// i.e. `unlocking_round ≤ confirmed_round < validated_round`. A confirmation at or
// after `validated_round` is a legitimate later switch, not a violation.View on GitHub (pinned to 6c226ddcb3)
Solutions
- When building a LockViolation proof, verify CryptoHash::new(&confirmed_header) != CryptoHash::new(&validated_header) first.
- Search the validator's vote history for votes over genuinely different block hashes at the same height on the same chain.
- Reject the proof as malformed if it fails this check — it is not actionable evidence.
- Add a unit assertion in proof-generation code that the two headers differ.
Example fix
// before
let proof = EquivocationProof::LockViolation { /* headers cloned from same block */ .. };
proof.check(&committee)?; // Err: same block
// after
if CryptoHash::new(&confirmed_header) == CryptoHash::new(&validated_header) {
anyhow::bail!("not a lock violation: same block");
}
let proof = EquivocationProof::LockViolation { .. }; Defensive patterns
Strategy: validation
Validate before calling
if CryptoHash::new(&confirmed_header) == CryptoHash::new(&validated_header) {
return Err(anyhow::anyhow!("lock violation needs two different blocks"));
} Try / catch
match proof.check(committee) {
Err(ChainError::EquivocationProofSameBlock) => {
// malformed evidence: drop it, nothing is provable from identical blocks
Ok(()) // ignore this proof
}
other => other,
} Prevention
- Pair only votes over distinct block hashes when assembling proofs.
- Deduplicate evidence by block hash.
- Unit-test proof builders to assert header inequality.
When it happens
Trigger: Constructing EquivocationProof::LockViolation with confirmed_header and validated_header that hash equally and submitting it to an API that verifies proofs (EquivocationProof::check). Typically from proof-building code that picks two votes of the same block, or from a report where the evidence pairs were mismatched.
Common situations: Off-chain evidence collectors pairing a validator's confirmed vote with its validated vote for the same block; copy-paste errors when assembling proof structs; adversarial reports trying to slash a validator with fabricated/misattributed evidence.
Related errors
- EquivocationProofDifferentChainOrHeight
- EquivocationProofNoLockViolation
- EquivocationProofSameBlock
- EquivocationProofNoFirstRoundViolation
- EquivocationProofValidJustification
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/e41c7ddf7c6acaf9.
Report an issue: GitHub.