linera-io/linera-protocol · error · ChainError

JustificationCommitmentMismatch

JustificationCommitmentMismatch

Error message

Certificate justification commitment does not match its justification chain

What it means

Thrown by check_cited_quorum when the opening quorum's value_hash does not equal the hash of the block header the vote was for (ChainError::JustificationCommitmentMismatch). A vote's justification commitment signs an opening that must validate the same block the vote is about; an opening over a different block cannot ground the vote. This is one of the specific reasons an InvalidJustification equivocation proof is considered genuine — a validator signing a commitment to an opening about a different block is at fault.

Source

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

        }
    }
}

/// 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,
    round: Round,
    kind: CertificateKind,
    unlocking_round: Option<Round>,
    opening: &CommittedQuorum,
    committee: &Committee,
) -> Result<(), ChainError> {
    ensure!(
        opening.value_hash == CryptoHash::new(header),
        ChainError::JustificationCommitmentMismatch
    );
    match kind {
        // A validated vote cites the quorum grounding its unlocking round, in a lower round.
        CertificateKind::Validated => ensure!(
            unlocking_round == Some(opening.round) && opening.round < round,
            ChainError::JustificationUnlockingRoundMismatch
        ),
        // A confirmed vote cites the quorum that validated the block in the same round.
        CertificateKind::Confirmed => ensure!(
            opening.round == round,
            ChainError::JustificationUnlockingRoundMismatch
        ),
        // Timeout votes cite nothing; any commitment is dishonest.
        CertificateKind::Timeout => ensure!(false, ChainError::JustificationCommitmentMismatch),
    }
    // A quorum with an unlocking round cites a quorum itself, and vice versa: its own commitment

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. If you are a voter: only sign a justification commitment computed over the same block you are voting on (recompute chain.commitment(CryptoHash::new(&header))).
  2. If you are verifying: treat this mismatch as attributable misbehavior — report an InvalidJustication proof with the opening as evidence.
  3. If building certificates, derive the opening from the block's own validation path, never from a cached chain of another block.
  4. In tests, ensure headers and openings are generated from the same block.
Defensive patterns

Strategy: validation

Validate before calling

ensure!(
    opening.value_hash == CryptoHash::new(&header),
    "justification opens a different block than the vote is for"
);

Try / catch

match check_cited_quorum(header, round, kind, unlocking_round, &opening, committee) {
    Err(ChainError::JustificationCommitmentMismatch) => {
        // attributable fault: build an InvalidJustification proof with this opening
        let proof = EquivocationProof::InvalidJustification { validator, header: header.clone(), round, kind, unlocking_round, first_round, signature, opening };
        report_fault(proof);
        Err(anyhow::anyhow!("voter cited a justification for a different block"))
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling check_cited_quorum (directly, or via EquivocationProof::check on an InvalidJustification proof) where opening.value_hash != CryptoHash::new(header). In honest certificate flow this appears if a voter cites a justification chain for a different block; in proof flow it is the expected condition that convicts the signer.

Common situations: Voters copying a justification from a different block's context; certificate-building code pairing headers with the wrong CommittedQuorum; slashing evidence where the mismatch is the fault being proven.

Understand the failure class

Related errors


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