{"record":{"id":"fb9da7c31b7a39cd","repo":"linera-io/linera-protocol","slug":"equivocationproofnofirstroundviolation","errorCode":"EquivocationProofNoFirstRoundViolation","errorMessage":"Equivocation proof's earlier vote is not below the attested first round","messagePattern":"Equivocation proof's earlier vote is not below the attested first round","errorType":"validation","errorClass":"ChainError","httpStatus":null,"severity":"error","filePath":"linera-chain/src/justification/mod.rs","lineNumber":543,"sourceCode":"                attested_round,\n                attested_commitment,\n                attested_signature,\n                earlier_header,\n                earlier_round,\n                earlier_attested,\n                earlier_commitment,\n                earlier_signature,\n            } => {\n                // Both votes must concern the same height on the same chain: the attestation only\n                // asserts that no lower round exists at *this* height, so a vote at a lower round\n                // number elsewhere does not contradict it. The blocks themselves may be equal —\n                // the contradiction is between the rounds, not the blocks.\n                ensure!(\n                    attested_header.chain_id == earlier_header.chain_id\n                        && attested_header.height == earlier_header.height,\n                    ChainError::EquivocationProofDifferentChainOrHeight\n                );\n                ensure!(\n                    *earlier_round < *attested_round,\n                    ChainError::EquivocationProofNoFirstRoundViolation\n                );\n                let attested = VoteValue(\n                    CryptoHash::new(attested_header),\n                    *attested_round,\n                    CertificateKind::Confirmed,\n                    None,\n                    true,\n                    *attested_commitment,\n                );\n                attested_signature.check(&attested, *validator)?;\n                let earlier = VoteValue(\n                    CryptoHash::new(earlier_header),\n                    *earlier_round,\n                    CertificateKind::Confirmed,\n                    None,\n                    *earlier_attested,","sourceCodeStart":525,"sourceCodeEnd":561,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-chain/src/justification/mod.rs#L525-L561","documentation":"Thrown by EquivocationProof::check for a FirstRoundViolation proof when the earlier vote's round is not strictly below the attested round (ChainError::EquivocationProofNoFirstRoundViolation). The proof's logic is that the validator attested 'this is the first round at this height' while having already voted in a lower round at that height; if earlier_round >= attested_round there is no contradiction. Note the earlier vote may itself carry an attestation — only the rounds are compared here.","triggerScenarios":"Submitting a FirstRoundViolation where earlier_round >= attested_round — e.g. pairing the attested vote with a vote in the same or a higher round, or mixing up which of the two votes carries the attestation during extraction.","commonSituations":"Evidence collectors pairing an attestation with a later vote instead of an earlier one; mislabeled fields when building the proof struct; tests using equal Round values for both votes.","solutions":["Assert earlier_round < attested_round before constructing the proof.","When scanning, select as 'earlier' the vote with the strictly smaller round at the same (chain, height).","If the rounds are equal or reversed, drop the pair — no violation is provable from it.","Double-check which vote carries first_round == true; the attested one must be the higher round."],"exampleFix":"// before\nlet proof = FirstRoundViolation { attested_round: r1, earlier_round: r2, .. }; // r2 > r1\nproof.check(&committee)?; // Err\n\n// after\nif earlier_round >= attested_round {\n    (attested, earlier) = (earlier, attested); // only if the attestation is on the higher round\n}\nensure!(earlier_round < attested_round, \"no contradiction between rounds\");\nlet proof = FirstRoundViolation { attested_round, earlier_round, .. };","handlingStrategy":"validation","validationCode":"ensure!(\n    earlier_round < attested_round,\n    \"earlier vote must be strictly below the attested round\"\n);","typeGuard":null,"tryCatchPattern":"match proof.check(committee) {\n    Err(ChainError::EquivocationProofNoFirstRoundViolation) => {\n        // no contradiction between rounds: discard the proof\n        Ok(())\n    }\n    other => other,\n}","preventionTips":["Select the strictly-lower-round vote as 'earlier' when pairing.","Confirm the attestation (first_round == true) sits on the higher round.","Round-compare both votes before assembling the proof."],"tags":["consensus","equivocation","first-round","rounds","proof-validation"],"backgroundTag":"invalid-equivocation-proof","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}