linera_core/proof/liveness.rs
1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! The liveness theorems, and a precise account of what they exclude.
5//!
6//! Everything here is conditional on the assumptions in [`super::assumptions`], all of which can
7//! fail without endangering [`CommitAgreement`]. Read [`LivenessScope`] alongside
8//! [`UnboundedProgress`]: several natural readings of "the protocol makes progress" are *not*
9//! implied, and the differences are design choices rather than oversights.
10//!
11//! [`CommitAgreement`]: linera_chain::manager::proof::safety::CommitAgreement
12
13use linera_chain::manager::proof::{
14 commit::{CommittedBlock, TipAdvancesOnlyOnValidCertificate},
15 safety::{CommitAgreement, UniqueChain},
16 timeouts::RoundsWithoutTimeout,
17};
18
19use super::{
20 assumptions::ActiveCorrectDriver,
21 progress::{
22 EventuallyCorrectLeader, FinalizationQuorumForms, LockRecovery, ProposalAccepted,
23 TimeoutCertificateForms, ValidationQuorumForms,
24 },
25};
26
27/// **Theorem (Round progress).** Consider a chain with at least one regular owner, at a height
28/// whose consensus instance has not yet committed. Under [`ActiveCorrectDriver`] and the other
29/// assumptions of [`super::assumptions`], there is a round `r` beginning after GST such that the
30/// height commits during `r`.
31///
32/// *Proof.* By [`RoundAdvancement`] the correct validators' common round grows without bound
33/// after GST, and by [`RoundTimeoutGrowth`] the timeout of round number `n` is
34/// `base_timeout + timeout_increment · n`, unbounded in `n`. Let `T` be the wall-clock time a
35/// correct leader needs to complete one round after GST: by
36/// [`ProposalAccepted`], [`ValidationQuorumForms`] and [`FinalizationQuorumForms`] this is
37/// `O(Δ)` plus bounded local processing, hence finite. Choose `n` with
38/// `base_timeout + timeout_increment · n > T`.
39///
40/// By [`EventuallyCorrectLeader`] there is a [`SingleLeader`] round `r` with number at least `n`,
41/// beginning after GST, whose leader is the correct driver's owner. By [`RoundAdvancement`] every
42/// correct validator is in `r` when it begins, and by [`LockRecovery`] the driver enters `r`
43/// holding a lock at least as high as every correct validator's confirmation. Then:
44///
45/// 1. by [`ProposalAccepted`], every correct validator accepts the driver's proposal in `r`;
46/// 2. by [`ValidationQuorumForms`], a [`ValidatedBlockCertificate`] for the proposed block forms
47/// in round `r`;
48/// 3. by [`FinalizationQuorumForms`], a [`ConfirmedBlockCertificate`] for it forms in round `r`.
49///
50/// All three complete within `T < ` the round's timeout, so no correct validator signs a timeout
51/// vote for `r` in the meantime ([`TimeoutVoteConditions`]) and the round is not cut short. The
52/// block is therefore a [`CommittedBlock`]. ∎
53///
54/// Step 3's precondition that no correct validator has left `r` is what the timeout comparison
55/// buys: without [`RoundTimeoutGrowth`] the round could expire mid-flight, every attempt could
56/// fail the same way, and rounds would advance forever without committing.
57///
58/// [`SingleLeader`]: linera_base::data_types::Round::SingleLeader
59/// [`ValidatedBlockCertificate`]: linera_chain::types::ValidatedBlockCertificate
60/// [`ConfirmedBlockCertificate`]: linera_chain::types::ConfirmedBlockCertificate
61/// [`RoundTimeoutGrowth`]: super::assumptions::RoundTimeoutGrowth
62/// [`TimeoutVoteConditions`]: linera_chain::manager::proof::timeouts::TimeoutVoteConditions
63/// [`RoundAdvancement`]: super::progress::RoundAdvancement
64pub trait RoundProgress:
65 EventuallyCorrectLeader
66 + LockRecovery
67 + ProposalAccepted
68 + ValidationQuorumForms
69 + FinalizationQuorumForms
70 + TimeoutCertificateForms
71 + CommittedBlock
72{
73}
74
75/// **Theorem (Height progress).** Under the same assumptions, once the driver has a block to
76/// propose at height `h`, a block at height `h` is committed within finite time after GST, and
77/// every correct validator's [`ChainTipState::next_block_height`] reaches `h + 1` — provided it
78/// is reachable and receives the certificate.
79///
80/// *Proof.* Commitment at `h` is [`RoundProgress`]. For the observable half: the driver's
81/// `ChainClient::process_pending_block` ends by calling `Client::update_validators` with the new
82/// [`ConfirmedBlockCertificate`], which delivers it to every validator; a correct recipient runs
83/// `ChainWorkerState::process_confirmed_block`, which verifies it and — since the block is
84/// contiguous with the recipient's tip, `h` being the pending height — executes it and advances
85/// [`ChainTipState::next_block_height`] to `h + 1` ([`TipAdvancesOnlyOnValidCertificate`]). By
86/// [`CommitAgreement`] the block it records is the same at all of them. ∎
87///
88/// The proviso is not removable and is not a defect: a validator that is partitioned away, or
89/// that has been down since before GST, simply has not received the certificate yet. It catches
90/// up through the ordinary certificate-download path, since the certificate is by then durable at
91/// a quorum.
92///
93/// [`ChainTipState::next_block_height`]: linera_chain::ChainTipState::next_block_height
94/// [`ConfirmedBlockCertificate`]: linera_chain::types::ConfirmedBlockCertificate
95pub trait HeightProgress:
96 RoundProgress + TipAdvancesOnlyOnValidCertificate + CommitAgreement
97{
98}
99
100/// **Theorem (Unbounded progress).** If a correct driver ([`ActiveCorrectDriver`]) keeps
101/// supplying blocks to propose — never exhausting its stream of operations — then under the
102/// assumptions of [`super::assumptions`] every correct, reachable validator's
103/// [`ChainTipState::next_block_height`] grows without bound, and by [`UniqueChain`] the
104/// validators' committed prefixes remain identical throughout.
105///
106/// *Proof.* Induction on the height. Given that height `h` has committed and every correct
107/// reachable validator has advanced to `h + 1` ([`HeightProgress`]), each such validator's
108/// `ChainStateView::reset_chain_manager` has created the consensus instance for `h + 1`
109/// ([`ConsensusInstance`]) with its round reset to
110/// [`ChainOwnership::first_round`]. The hypotheses of [`RoundProgress`] then hold again at
111/// `h + 1`: GST has passed, the driver has a block, and [`EventuallyCorrectLeader`] applies to
112/// the fresh instance since the leader seed is the new height. So `h + 1` commits, and the
113/// induction continues. Uniqueness of what is committed at each height is [`UniqueChain`]. ∎
114///
115/// Note that the round numbering restarts at each height, so the round-timeout argument of
116/// [`RoundProgress`] restarts too: each height may again spend a bounded number of rounds before
117/// its timeout exceeds `T`. This costs latency, not liveness.
118///
119/// [`ChainTipState::next_block_height`]: linera_chain::ChainTipState::next_block_height
120/// [`ConsensusInstance`]: linera_chain::manager::proof::model::ConsensusInstance
121/// [`ChainOwnership::first_round`]: linera_base::ownership::ChainOwnership::first_round
122pub trait UnboundedProgress: HeightProgress + UniqueChain {}
123
124/// **Remark (What liveness does not claim).** Five exclusions, each of which a reader may
125/// reasonably expect [`UnboundedProgress`] to cover.
126///
127/// * **No progress without a client.** [`ActiveCorrectDriver`] is indispensable: a validator
128/// never proposes and never advances a round unprompted. A microchain whose owners have all
129/// gone away makes no progress and is not thereby faulty. This is the deepest structural
130/// difference from a validator-driven BFT protocol, and it is what makes a Linera validator's
131/// per-chain cost proportional to use.
132///
133/// * **The fast round can wedge a height permanently.** By [`RoundsWithoutTimeout`], with the
134/// default [`TimeoutConfig`] the fast round has no timeout at all, and while the current round
135/// is fast only a super owner may open a later one. A super owner that issues two conflicting
136/// fast proposals splits the correct validators — each locks onto the first it sees, by
137/// [`FastConfirmationNeedsEmptyLock`] — so neither reaches a quorum, and the height stops until
138/// that same super owner proposes again in a higher round. [`CommitAgreement`] is untouched;
139/// this is exactly the trade the fast path makes, and the reason
140/// `ChainClient::process_pending_block_inner` refuses to replace a pending fast proposal whose
141/// signing key it no longer holds.
142///
143/// * **Multi-leader rounds are not covered by [`RoundProgress`].** The theorem is stated for
144/// [`SingleLeader`] and [`Validator`] rounds, where a unique leader is guaranteed by
145/// [`LeaderEligibility`]. In a multi-leader round several owners may propose simultaneously;
146/// correct validators then vote for whichever proposal they see first, quorums may not form,
147/// and by [`RoundsWithoutTimeout`] a non-final multi-leader round has no timeout — it is left
148/// only by a proposal in a higher round. This is by design (multi-leader rounds are the
149/// uncontended fast path) and costs nothing, because the round sequence passes through them
150/// into single-leader rounds where the theorem applies.
151///
152/// * **No bound on the number of rounds, and no bound before GST.** [`RoundProgress`] asserts the
153/// existence of a successful round, not a bound on how many precede it. Faulty leaders and
154/// pre-GST delays each cost rounds.
155///
156/// * **Nothing is claimed about the *content* of the committed blocks.** A driver competing with
157/// other owners may find its own block superseded: [`LockRecovery`] obliges it to re-propose
158/// whatever is locked rather than its own pending block. `process_pending_block` then returns
159/// the committed certificate for the other block and keeps the driver's proposal pending for a
160/// later height. Liveness of the *chain* does not imply liveness of any particular
161/// transaction.
162///
163/// [`SingleLeader`]: linera_base::data_types::Round::SingleLeader
164/// [`Validator`]: linera_base::data_types::Round::Validator
165/// [`TimeoutConfig`]: linera_base::ownership::TimeoutConfig
166/// [`FastConfirmationNeedsEmptyLock`]: linera_chain::manager::proof::voting::FastConfirmationNeedsEmptyLock
167/// [`LeaderEligibility`]: linera_chain::manager::proof::timeouts::LeaderEligibility
168pub trait LivenessScope:
169 UnboundedProgress + RoundsWithoutTimeout + ActiveCorrectDriver + LockRecovery
170{
171}