Minimmit: Fast Finality with Even Faster Blocks
Over the last few months, there has been renewed interest in developing propose-and-vote consensus protocols that reach finality after just one round of voting (~100-200ms). "Two-Phase" protocols, not without tradeoff, only remain safe if a Byzantine adversary controls less than ~20% of stake (rather than the ~33% tolerance typically considered). Concerned primarily with addressing this drop in fault tolerance, recent constructions propose augmenting Byzantine fault tolerance with crash fault tolerance to retain some path to finality under a "blended" ~33% assumption. Minimmit, a new propose-and-vote construction below, optimizes for a different goal: minimizing block time.
Background
In March, Matter Labs released ChonkyBFT: Consensus Protocol of ZKsync. In May, Offchain Labs released Kudzu: Fast and Simple High-Throughput BFT and Anza Labs released Alpenglow at Solana Accelerate. And just a few days ago, Supra Research and Espresso Systems released Hydrangea: A Fast and Simple Consensus Protocol for High-Throughput Blockchains.
All capable of propose-and-vote confirmation in ~100-200ms, this cohort of new constructions primarily differs in the technique employed to finalize blocks if faults exceed ~20% of stake (when comprised of at most f Byzantine faults and p crash faults). Finalizing quickly via some "fast path" when replicas are honest and online, these constructions fallback to a "slow path" that resembles the "Three-Phase" protocols commonly deployed today.
Minimmit: Fast Finality with Even Faster Blocks
Today, we are excited to share a different take on propose-and-vote consensus: Minimmit: Fast Finality with Even Faster Blocks. Like the constructions above, Minimmit delivers minimal confirmation latency under the ~20% Byzantine fault assumption. Unlike those constructions, however, it optimizes for view latency instead of f+ confirmation robustness. In an alto-like configuration (with 50 uniformly distributed validators), we expect a Minimmit-powered blockchain to reach 130ms block time and 250ms finality. In a regionally-biased configuration, we expect Minimmit to deliver 50ms block time and 100ms finality.
While not yet peer-reviewed or fully implemented, we are releasing Minimmit under both an MIT and Apache-2 license for others to build with and build upon. Below, we provide a high-level summary of the specification and share some intuition about its correctness:
1. Introduction
Minimmit is a responsive, leader-based consensus protocol designed for simplicity and speed, tolerant of a Byzantine adversary that controls fewer than 20% of replicas. Minimmit advances to the next view when a 40% quorum is reached and finalizes blocks when an 80% quorum is reached (after only a single round of voting). Minimmit can be instantiated with a number of practical optimizations to improve performance when deployed in production.
2. Model & Parameters
- Byzantine replicas:
≤ f - Total replicas:
n ≥ 5f + 1 - Partial synchrony: every message arrives within
Δafter an unknown global stabilization time (GST).
3. Quorums
L = n - 3f(2f + 1)Q = n - f(4f + 1)
There exists ≥ 1 honest replica in any Q-set and L-set intersection.
4. Message Types
| Message | Purpose |
|---|---|
genesis |
The genesis block. |
propose(c, v, (c', v')) |
Leader's proposal c for view v with parent c' in view v'. |
notarize(c, v) |
Vote to finalize block c in view v. |
nullify(v) |
Vote to advance to view v + 1. |
notarization(c, v) |
Certificate of ≥ L notarize(c, v) messages for (c, v). |
nullification(v) |
Certificate of ≥ L nullify(v) messages for view v. |
proof(v) |
Either a notarization(*, v) or a nullification(v) certificate. |
5. Initial Replica State
view = 0
notarized = ⊥ # the proposal this replica has notarized
nullified = false # whether this replica has nullified this view
timer = None # time until nullify if not yet nullified or notarized
messages = [] # list of messages this replica has seen
proofs = [] # list of proofs this replica has collected
6. External Functions
// Select the leader for view `v`
fn leader(v) -> L;
// Build a block on top of `c'`. This should pass `verify(c, c')`.
fn build(c') -> c;
// Verify whether `c` is valid given the parent `c'`. Anything produced by
// `build(c')` should pass `verify(c, c')`.
fn verify(c, c') -> bool;
7. Helpers
// Find a valid parent to build on
fn select_parent(v) -> (c', v') {
let i = v - 1;
while i >= 0 {
if notarization(c', i) ∈ proofs[i] {
// If there are multiple, pick any.
return (c', i);
}
if nullification(i) ∈ proofs[i] {
i -= 1;
continue;
}
return ⊥;
}
return genesis;
}
// Ensure there are proofs for all views between `v` and `v'`
fn valid_parent(v, (c', v')) -> bool {
let i = v - 1;
while i > v' {
if nullification(i) ∈ proofs[i] {
i -= 1;
continue;
}
return false;
}
return notarization(c', v') ∈ proofs[v']
}
// Enter view `next`
fn enter_view(next) {
if view >= next {
return;
}
view = next;
notarized = ⊥;
nullified = false;
timer = 2Δ;
}
// Record a message from a `replica`
fn record_message(replica, message) -> bool {
if replica ∉ messages[message.view] {
messages[message.view][replica] = [];
}
if message ∉ messages[message.view][replica] {
messages[message.view][replica].add(message);
return true;
}
return false;
}
// Prune data less than `view`
fn prune(view) {
messages.remove(m => m.view < view);
proofs.remove(p => p.view < view);
}
8. Protocol for View v
8.1. Propose
If the leader, propose.
- Upon entering view
v, if identity is equal toleader(v):(c', v') = select_parent(v)(if⊥, return).c = build(c').notarized = c.- Broadcast
propose(c, v, (c', v')).
Treat propose(c, v, (c', v')) as a leader l's notarize(c, v).
8.2. Notarize
Upon receipt of a first valid block proposal from leader, broadcast notarize(c, v).
- On receiving first
propose(c, v, (c', v'))fromleader(v):- If
notarized != ⊥ornullified, return. - If
!valid_parent(v, (c', v')), return. - If
!verify(c, c'), return. notarized = c.- Broadcast
notarize(c, v).
- If
8.3. Nullify by Timeout
If timer expires, broadcast nullify(v) if not yet broadcasted notarize(c, v).
- On
timerexpiry:- If
notarized != ⊥ornullified, return. nullified = true.- Broadcast
nullify(v).
- If
8.4. Notarization & Finalization
After L messages, create and broadcast a notarization(c, v) certificate. After Q messages, finalize.
- On receiving
notarize(c, v)from replicar:- If
!record_message(r, notarize(c, v)), return.
- If
- On observing
≥ Lnotarize(c, v)messages:- Assemble
notarization(c, v). - Add
notarization(c, v)toproofs. - Broadcast
notarization(c, v). enter_view(v + 1).
- Assemble
- On observing
≥ Qnotarize(c, v)messages:- Finalize
cand all of its ancestors. prune(v).
- Finalize
8.5. Nullification
After L messages, create and broadcast a nullification(v) certificate.
- On receiving
nullify(v)from replicar:- If
!record_message(r, nullify(v)), return.
- If
- On observing
≥ Lnullify(v)messages (or a singlenullification(v)message):- Assemble
nullification(v). - Add
nullification(v)toproofs. - Broadcast
nullification(v). enter_view(v + 1).
- Assemble
8.6 Nullify by Contradiction
If you have already broadcast notarize(c, v) for a c that cannot be finalized directly, broadcast nullify(v) to ensure some proof(v) will exist in view v.
- On observing messages from
≥ Lreplicas of eithernullify(v)ornotarize(*, v)(wherenotarized != ⊥andnotarized != *):nullified = true.- Broadcast
nullify(v).
9. Intuition
9.1 General
- A leader selected in
v + 1may propose any blockcthat extends some knownnotarization(c', v')as long as there existnullification(j)proofs for all views in(v', v]. Notably, this means that leaders are never required to re-propose a block from an earlier view and can only skip some block proposed in an earlier viewvif there exists somenullification(v).
9.2 Safety
- Honest replicas may not broadcast a
notarize(c, v)after first broadcasting anullify(v). - Honest replicas may broadcast a
nullify(v)after first broadcasting anotarize(c, v).- To broadcast both a
notarize(c, v)and anullify(v)message, a replica must first see that it is impossible for the proposal that it notarized to reach a quorum ofQnotarize(c, v)messages. Otherwise, the replica is forbidden from broadcastingnullify(v), no matter how much time has passed. - A replica knows it is impossible for its notarized proposal
cto reach the finalization quorumQonce it has observedLother replicas that conflict. A conflicting replica has broadcast either anullify(v)message or anotarize(*, v)message for a different proposal*. - If a replica has seen
Lconflicting votes, at leastL - f(i.e.f + 1) are from honest replicas. Therefore, the maximum number ofnotarize(c, v)it can receive isn - (f + 1), or4f(strictly less thanQ).
- To broadcast both a
- Suppose a correct leader broadcasts a block
cand, after honest replicas broadcastnotarize(c, v), message delivery is disrupted, preventing any replica from receivingLsuch messages. In this state, replicas have locked oncfor viewvand cannot broadcast somenullify(v). Progress is stalled until network conditions improve, allowing anotarization(c, v)to be assembled, which in turn allows replicas to enter viewv + 1. - In any given view
v, there may be multiplenotarization(*, v)messages and onenullification(v). If there are multiplenotarization(*, v)s, no block*referenced by anotarization(*, v)can be finalized inv. If there exists somenullification(v), no block can be finalized inv.
9.3 Liveness
- There exists at least one
proof(v)for every viewv. - After GST, all views with honest leaders will emit a
notarizationmessage before the timer of any honest replica expires. To see this is true, consider the following:- The first honest replica broadcasts some
proof(v - 1)message to all replicas and enters viewvat timet_0. - The leader of view
vwill receive saidproof(v - 1)message byt_0 + Δand broadcast somepropose(c, v, (c', v'))message to all replicas. - All honest replicas will receive said
propose(c, v, (c', v'))message byt_0 + 2Δand broadcast somenotarize(c, v)message.
- The first honest replica broadcasts some
- Replicas enter
v + 1as soon as they see someproof(v)(as fast asLmessages). If the network is partitioned in two, replicas in each half of the partition may continue to enter successive views (on differentproof(v)s) but will never finalize conflicting blocks. To bound the depth of forks in a partition, replicas can wait to enter some viewv + kuntil they have seenQmessages in viewv. - A Byzantine leader could equivocate, sending a distinct proposal to each replica and causing them to broadcast a
notarize(*, v)for different blocks. After a replica observes≥ Lnotarize(*, v)messages for some* != c, it will then choose to broadcast anullify(v)message. Eventually,Lnullify(v)messages will be received and honest replicas will enterv + 1(withinΔof the first honest replica). - Since at most
fnodes are Byzantine or faulty, once an honest leader is assigned, it is possible for at leastQcorrect replicas to finalize a block (including all of its ancestors).
10. Extensions
Minimmit can be instantiated in several different ways to tune performance when deployed to production. Some examples are below:
- Use block digests (i.e.
c = hash(block)) inpropose(c, v, (c', v')),notarize(c, v), andnotarization(c, v)messages. - Employ BLS multi-signatures or BLS threshold signatures, like Threshold Simplex, to cap
notarization(c, v)andnullification(v)messages at a constant size regardless of the number of replicas. - Attach some recent set of
proof(v)messages to eachpropose(c, v, (c', v'))message (to ensure honest replicas that are not yet aware of recent proofs can still broadcast anotarize(c, v)message for valid blocks). - If
≥ f + 1notarize(c, v)messages are observed for someproposal(c, v, (c', v'))considered invalid, request the missingnotarization(c, v')ornullification(v')not found in ourproofs(that prohibited us from broadcasting anotarize(c, v)) from the peers that consider it valid. - If stuck in the same view
vfor timet_s, re-broadcast someproof(v - 1)(to ensure all correct replicas enterv) and re-broadcastnotarized(if not⊥) andnullified(if notfalse). - Assemble and broadcast a
finalization(c, v)message after finalizing somec(i.e.≥ Qnotarize(c, v)messages). This can both help lagging replicas catch up to the finalized tip and make it easier for downstream services to integrate. - Disseminate blocks using
(k,d)-erasure codes, like DispersedSimplex, Kudzu, and Alpenglow, to avoid a leader broadcast bottleneck. Eachnotarizemessage would be augmented with the relevant fragment.kwould be set to the number of replicas, anddcan be set asf+1so that the replicas only have a bandwidth requirement of about ~5 times the size of the full block. If anotarizationexists, then at leastf+1honest nodes have been distributed a fragment. This preventsByzantinenodes from constructing anotarizationwithout honest nodes being able to reconstruct the block among themselves.dcan be set at higher values like2f+1to halve the required bandwidth, but replicas would have to ignore any gossipednotarizationmessages, instead making sure to gather the2f+1notarizemessages themselves. - To punish equivocating leaders, treat
proposemessages for different blocks in the same view as a slashable offense. To incentivize performant leaders, issue a reward for any blockcincluded in the canonical chain.
Have an idea to simplify, improve, or extend Minimmit? Open a PR or reach out at minimmit@commonware.xyz.