System designDeep dive9 min

Byzantine faults

Most fault-tolerant systems assume a broken machine stops. A Byzantine fault is worse: the machine keeps running and says wrong things, by bug, by bad hardware or on purpose. Tolerating that takes more machines, more messages and signed votes, which is why it's standard among parties that don't trust each other and rare inside one company.

1 · The idea

A Byzantine fault is a node that keeps running and says the wrong thing

Four banks in a small country settle transfers between them on a shared ledger. Each bank runs one node, and every node must hold the same list of transfers in the same order. Raft would handle a node that crashes (Consensus & leader election). But the banks don't control each other's servers, and a node that is compromised, misconfigured or simply buggy doesn't stop. It answers, and its answers are wrong.

Fault

What the other banks see

Handled by

Crash

Bank C's node goes silent

Crash-tolerant consensus such as Raft

Omission

Some of C's messages never arrive

Timeouts and retries

Byzantine

C tells A one order of transfers and tells B another

Byzantine fault-tolerant (BFT) protocols

The name comes from a framing of the problem as generals of the Byzantine army agreeing on a plan while some of them are traitors. Telling different peers different things, as bank C does above, is the hard case: each honest node sees a consistent story, and only by comparing notes can they find out the stories differ (Why distributed systems are hard).

2 · Bad hardware

Hardware lies too, so even one company sees wrong answers

Byzantine behavior doesn't need an attacker. Large fleets see CPU cores that silently compute wrong results, called mercurial cores, at a rate of a few per several thousand machines: hundreds of them across hundreds of thousands of machines. Nothing crashes and no error is logged; the only symptom is a wrong answer. Examples from large fleets include database index corruption and an encryption result that only the faulty core could decrypt.

Core 59computes a file size as 0Decompressionskips 'empty' filesDatabasefiles go missingsize = 0nothing writtenCore 59computes a file size as 0Decompressionskips 'empty' filesDatabasefiles go missingsize = 0nothing written

A case Meta traced: one core returned 0 for a power calculation on certain inputs, so valid files were treated as empty and never written. It surfaced as data loss far from its cause.

Storage and networks have long had defenses for this: error-correcting codes, checksums from end to end, and scrubbing stored data for corruption. Computation is harder, because there's no cheap check for an arbitrary result. Google has written self-checking versions of critical functions, such as encryption and compression, and is willing to pay for double or triple computation where a wrong answer is costly. The banks' nodes face the same risk, before anyone is malicious.

3 · 3f + 1

Surviving f liars takes 3f + 1 nodes

A crash-tolerant cluster needs 2f + 1 nodes to survive f failures: any two majorities share a node, and every node tells the truth. With liars, that's not enough.

The argument goes like this. A node must be able to move on after hearing from n − f nodes, since f may never answer. But the f that stayed silent may be honest and slow, so f of the n − f that did answer may be liars. The honest answers must still outnumber the lies: n − 2f > f, so n ≥ 3f + 1.

Survive 1 crash
3 nodes
Survive 1 liar
4 nodes
Survive 2 crashes
5 nodes
Survive 2 liars
7 nodes

2f + 1 against 3f + 1. Four banks can tolerate one misbehaving node; tolerating two would need seven.

The same numbers explain the voting. With 3f + 1 nodes, a quorum is 2f + 1. Two quorums overlap in at least f + 1 nodes, so at least one honest node is in both, and it won't vote for two conflicting orders. For the banks, a quorum is 3 of 4.

With plain messages the bound is proven: with three generals, no algorithm survives one traitor. Unforgeable signatures remove that bound, in a model where a missing message can always be detected. On a real network, where delays have no limit, 3f + 1 is still the minimum for staying both correct and live.

4 · PBFT

PBFT orders each request in three rounds of everyone-to-everyone votes

Practical Byzantine Fault Tolerance (PBFT), from 1999, was the first replication protocol to survive Byzantine faults correctly on an asynchronous network, and fast enough to use. One node is the primary and proposes an order; the others don't trust it, so they check it with each other before acting:

Bank C has failed and stays silent. A, B and D still exchange prepare and commit with every other node (A's are left out here), and each executes transfer T once it holds 2f + 1 = 3 matching commits.

Each phase has a job. Pre-prepare carries the primary's proposal: transfer T gets sequence number 812. Prepare has every node tell every other what it received, so a primary that sends different proposals to different nodes can't get two orders accepted. Commit makes sure the order survives even if the primary is replaced. The client, here the bank that submitted T, waits for f + 1 = 2 matching replies, since at least one of them must be honest.

If the primary stalls or misbehaves, the others vote it out in a view change and the next node takes over. Messages are authenticated, so no node can forge another's vote; PBFT uses message authentication codes, about a thousand times faster to compute than signatures. A file service replicated with PBFT ran only 3% slower than an unreplicated one.

The cost is the everyone-to-everyone rounds: with n nodes, each phase sends about n × (n − 1) messages. That's 12 for the four banks, but 9,900 for 100 nodes, which is why classic BFT groups stay small.

5 · Open networks

When anyone can join, votes must cost something

BFT voting counts nodes, so it needs a fixed, known list of who votes. The banks have that: four signed-up members with known keys. On an open network anyone can start nodes, and one attacker could start a thousand. One vote per IP address could be subverted by anyone able to allocate many addresses.

System

Who votes

Safe while

PBFT-style groups

A fixed list of known nodes

Fewer than a third of nodes misbehave

Hyperledger Fabric, BFT ordering

Ordering nodes of known organizations

Fewer than a third of parties are untrusted

CometBFT

Validators, weighted by voting power

Less than a third of voting power is Byzantine

Bitcoin (proof of work)

Anyone, weighted by computing power

Honest nodes control a majority of CPU power

Proof of work makes a vote cost computing power, one CPU one vote, so faking a thousand voters means paying for a thousand voters' work. CometBFT keeps PBFT's shape, with propose, prevote and precommit steps and more than two-thirds of voting power needed to commit, but counts voting power instead of nodes. Bitcoin instead follows the longest chain, which becomes harder to reverse with every block added after a transaction.

6 · In practice

Use BFT across trust boundaries, and checks inside one

Inside one company, the usual consensus protocols are crash tolerant: Raft and Paxos assume nodes may stop but never lie, and Hyperledger Fabric's Raft ordering service, for one, is crash fault tolerant only. That's reasonable when one team runs every node, because BFT's extra nodes and messages buy protection against a threat the company mostly doesn't face, an operator attacking their own cluster.

The Byzantine faults a single company does face, corrupt bits and wrong computations, are handled with cheaper, targeted checks. The banks use both: BFT between banks, and checks inside each one:

  1. Draw the trust boundary

    BFT between parties that don't trust each other; crash tolerance within one owner.

  2. Size for 3f + 1

    Decide how many misbehaving members to survive; quorums are 2f + 1.

  3. Sign or authenticate every message

    No node may speak for another.

  4. Checksum data end to end

    Verify at the reader, not only at each hop, and scrub stored data.

  5. Validate what other services send

    Reject values that can't be right instead of acting on them.

  6. Double-check critical computation

    Compute twice, or on two machines, where a wrong answer is costly.

Check yourself

4 questions

1. How many nodes does a BFT protocol need to survive two misbehaving nodes on an asynchronous network?
2. In PBFT, why do the backups exchange prepare messages instead of trusting the primary's proposal?
3. Why can't an open network count one vote per node?
4. A company runs all five replicas of its database. Which Byzantine risk is it most likely to face?

Takeaways

Remember this

  • A Byzantine fault is a node that keeps running and sends wrong or inconsistent messages, through bugs, bad hardware or malice.
  • Surviving f such nodes needs 3f + 1 nodes and quorums of 2f + 1, against 2f + 1 nodes for crash faults.
  • PBFT commits each request through pre-prepare, prepare and commit rounds of all-to-all messages, with view changes to replace a bad primary.
  • Open networks can't count nodes, so they weight votes by work or stake.
  • Within one organization, crash-tolerant consensus plus checksums, validation and double computation cover the faults that actually happen.