pub trait CastValidationRoundFloor:
LockRoundMonotone
+ ProposalGate
+ ConfirmationOnlyInCurrentRound
+ VoteRoundBelowCurrentRound
+ CurrentRoundMonotone
+ RoundFloor { }Expand description
Invariant (Cast validation rounds leave a floor). If a correct validator has ever cast a
validation vote in round s during an instance, then from that moment on
max(validated_vote.round, lock round) ≥ swhere an absent field contributes ⊥.
This is the invariant that survives create_final_vote clearing validated_vote, and it
is what makes OneValidationVotePerRound hold over a whole instance rather than only
between confirmations.
Proof. Induction over the transitions of the instance.
Base. Immediately after the vote, validated_vote is Some(_, s) — the non-fast branch
of create_vote ends with self.validated_vote.get_mut().insert(vote).
Step. Only two transitions can lower either side of the maximum. The lock round is
non-decreasing by LockRoundMonotone, so only validated_vote can fall, and by the
writer table it is set to None in exactly two places:
create_vote, fast branch (self.validated_vote.set(None)). Unreachable here: that branch requiresChainManager::check_proposed_blockto have accepted a proposal inRound::Fast(ProposalGate), whosevalidated_voteguardensure!(new_round > vote.round)is unsatisfiable for the minimum round unlessvalidated_voteis alreadyNone; and if it isNone, the induction hypothesis is carried by the lock, which this branch does not lower.create_final_voteat some roundr. It clears the field only afterupdate_locking(LockingBlock::Regular(validated), blobs)and only on the branch where a vote is cast, which byConfirmationOnlyInCurrentRoundrequirescurrent_round == r. ByVoteRoundBelowCurrentRoundandCurrentRoundMonotone,current_round ≥ s, sor ≥ s. ByRoundFloorandLockRoundMonotone, afterupdate_lockingthe lock round is≥ r ≥ s. So the maximum is preserved by the lock. ∎