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.

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.

Appendix: 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)