pub trait ProposalGate: SequentialChainState { }Expand description
Lemma (Proposal gate). A correct validator reaches ChainManager::create_vote for a
proposal p only after ChainManager::check_proposed_block returned
Outcome::Accept for p against the same manager state.
Similarly, it reaches ChainManager::create_final_vote for a certificate c only after
ChainManager::check_validated_block returned Outcome::Accept for c, and after
c.check(committee) succeeded.
Proof. Both methods have exactly one caller in the workspace outside tests, in
linera_core::chain_worker::state:
create_voteis called at the end ofChainWorkerState::try_handle_block_proposal, which earliermatches onchain.manager.check_proposed_block(&proposal)and returns without voting on both non-Acceptarms —Ok(Outcome::Skip)returns the unchanged chain info, andErr(_)returns the error (after, at most, recording the proposal viaChainManager::update_signed_proposal, which casts no vote).create_final_voteis called at the end ofChainWorkerState::process_validated_block, which earlier evaluatescertificate.check(&committee)?and thenshould_skip_validated_block()?, a closure wrappingchain.manager.check_validated_block(&certificate). ASkipoutcome returns early; anErrpropagates via?. OnlyOk(Accept)falls through.
By SequentialChainState no other transition on this instance interleaves, so the state
the guard inspected is the state create_vote / create_final_vote then mutates. ∎
Where this is fragile. The guards are at the call sites, not inside the signing methods:
create_final_vote in particular re-checks only ChainManager::current_round, and would
happily sign a second confirmation in the same round if invoked directly. A new caller must
replicate the guards. This is the single largest gap between “the module is correct” and “the
module cannot be misused”.