linera-io/linera-protocol · error · ChainError

EquivocationProofSameBlock

EquivocationProofSameBlock

Error message

Equivocation proof must reference two different blocks

What it means

Thrown by EquivocationProof::check for a DoubleVote proof when both votes target the same block hash (ChainError::EquivocationProofSameBlock). A double vote requires two votes for different blocks in the same round and kind; two signatures over the identical block are redundant, not contradictory. check computes both header hashes and rejects the proof before verifying signatures.

Source

Thrown at linera-chain/src/justification/mod.rs:490

            }
            EquivocationProof::DoubleVote {
                validator,
                round,
                kind,
                first_header,
                first_unlocking_round,
                first_attested,
                first_commitment,
                first_signature,
                second_header,
                second_unlocking_round,
                second_attested,
                second_commitment,
                second_signature,
            } => {
                let first_block_hash = CryptoHash::new(first_header);
                let second_block_hash = CryptoHash::new(second_header);
                ensure!(
                    first_block_hash != second_block_hash,
                    ChainError::EquivocationProofSameBlock
                );
                // Both votes must concern the same height on the same chain: a validator voting
                // for different blocks at different heights or on different chains in the same
                // round number is not double-voting.
                ensure!(
                    first_header.chain_id == second_header.chain_id
                        && first_header.height == second_header.height,
                    ChainError::EquivocationProofDifferentChainOrHeight
                );
                let first = VoteValue(
                    first_block_hash,
                    *round,
                    *kind,
                    *first_unlocking_round,
                    *first_attested,
                    *first_commitment,

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Deduplicate the validator's votes by (block hash, round, kind) before pairing; a true double vote needs two distinct block hashes.
  2. Assert CryptoHash::new(&first_header) != CryptoHash::new(&second_header) when building the proof.
  3. Treat the failing proof as malformed evidence and drop it.
  4. Check whether the 'second' vote differs only in non-payload metadata — that is a retransmission, not a double vote.

Example fix

// before
let proof = DoubleVote { first_header: h.clone(), second_header: h.clone(), .. };
proof.check(&committee)?; // Err: same block

// after
let distinct: BTreeSet<CryptoHash> = votes.iter().map(|v| v.block_hash).collect();
ensure!(distinct.len() >= 2, "no distinct blocks: no double vote");
let (a, b) = pick_two_distinct(&votes);
let proof = DoubleVote { first_header: a, second_header: b, .. };
Defensive patterns

Strategy: validation

Validate before calling

ensure!(
    CryptoHash::new(&first_header) != CryptoHash::new(&second_header),
    "double vote requires two different blocks"
);

Try / catch

match proof.check(committee) {
    Err(ChainError::EquivocationProofSameBlock) => {
        // retransmission, not a double vote: discard
        Ok(())
    }
    other => other,
}

Prevention

When it happens

Trigger: Constructing EquivocationProof::DoubleVote with first_header and second_header that hash equally — e.g. the same certificate's signature captured twice, or the same vote re-signed under a different unlocking round — and calling check on it.

Common situations: Evidence deduplication failures where the same vote enters the pair twice; re-broadcast of an identical vote mistaken for a second vote; proof builders that pair a vote with a retransmission instead of a genuinely different block vote.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/e313232b1c1cfa22. Report an issue: GitHub.