Byzantine Fault Tolerance concept page. Cluster of 4 replicas (3f+1, f=1) with primary and replica-3 marked Byzantine. Shows PBFT three-phase consensus (pre-prepare, prepare, commit), Byzantine general lying-primary scenario, blockchain PoS BFT analog with slashing, and decision matrix BFT vs CFT.
Raft, Paxos, ZAB и прочие классические consensus-протоколы предполагают crash-fault модель: узел либо работает корректно, либо молчит (fail-stop). Внутри собственного дата-центра этого хватает: своё железо, проверенный софт, доверенные оператора. Но как только узлы принадлежат разным организациям, могут быть взломаны или публично доступны — модель ломается.
Скомпрометированный узел не «падает» — он лжёт: отправляет разные значения разным peer-ам, голосует дважды, фальсифицирует подписи, пропускает только удобные ему сообщения. Crash-fault алгоритмы при таком сценарии теряют safety: разные honest реплики коммитят разные значения, появляется split-brain без видимого failure.
Byzantine Fault Tolerance (BFT) — семейство протоколов, которые сохраняют safety и liveness даже при произвольно злонамеренных узлах, до определённой доли. Это foundation для blockchain (Bitcoin, Ethereum, Cosmos), межбанковского settlement, consortium chains (Hyperledger), военных систем и любых сценариев, где владельцы узлов друг другу не доверяют.
Византийские генералы окружили город. Им надо одновременно атаковать или одновременно отступить — частичная атака гарантирует разгром. Они общаются через гонцов. Часть генералов — предатели, могут отправлять разным союзникам разные приказы. Сколько лояльных нужно, чтобы решение было общим?
Формальный результат Lamport-а: чтобы tolerate f Byzantine узлов, нужно минимум 3f+1 узлов всего.
Почему именно 3f+1:
f узлов могут быть недоступны (partition / crashed) и выглядят как failuresf могут быть Byzantine и активно лгатьf+1 honest correctly-responding узлов, чтобы их голосов было больше, чем голосов лжецовf + f + (f+1) = 3f+1Конкретные цифры:
Byzantine f | Минимум узлов | Quorum (2f+1) |
|---|---|---|
| 1 | 4 | 3 |
| 2 | 7 | 5 |
| 3 | 10 | 7 |
| 4 | 13 | 9 |
Сравните с crash-fault tolerance (Raft/Paxos): 2f+1 узлов, quorum f+1. 5 узлов tolerate 2 crashes, но 0 Byzantine — один лжец валит safety. BFT платит 1/3 ёмкости узлов и O(N²) сообщений за защиту от лжи.
Кластер из 4 узлов — минимальная конфигурация PBFT, выдерживающая ровно f=1 Byzantine. На диаграмме:
Связи между всеми replicas — это O(N²) mesh: каждая реплика broadcast-ит PREPARE и COMMIT всем остальным. Для N=4 это 16 сообщений на одну операцию; для N=100 — 10 000. Поэтому классический PBFT масштабируется до десятков узлов, не сотен.
PBFT в нормальном режиме проходит три фазы: pre-prepare → prepare → commit. Зачем три, а не две?
seq=N, подписывает запрос, рассылает всем. Реплики проверяют подпись и отсутствие дубликата seq.PREPARE всем. Реплика переходит дальше, когда соберёт 2f+1 одинаковых PREPARE сообщений — это доказывает, что quorum видел одинаковое значение и primary не врал разным узлам.COMMIT. Собрав 2f+1 COMMIT-сообщений — выполняет запрос и шлёт client-у. Client ждёт f+1 идентичных ответов: хотя бы один из них — от honest.Trick: prepare phase ловит lying primary (разные значения разным репликам), commit phase защищает от view-change в середине — если primary меняется, новый primary не сможет переписать уже prepared-значения.
Здесь primary скомпрометирован и пытается сделать split-brain: шлёт x=100 двум репликам и x=999 третьей. Что происходит:
x=100, broadcast-ят PREPARE(x=100).pre-prepare(x=999), но видит PREPARE(x=100) от двух других. Конфликт зафиксирован — Replica 3 не может broadcast PREPARE, запускает view-change.2f+1=3 PREPARE(x=100) — quorum достигнут, COMMIT идёт.f+1=2 одинаковых ответа x=100 — принимает.Ключевая идея: prepare phase делает Byzantine ложь видимой, потому что honest реплики кросс-проверяют то, что они получили от primary.
Blockchain BFT — это BFT через криптографию + game theory. Ethereum 2 PoS:
2/3+ stake-weighted голосов за блок — он justified. Следующий epoch justify-ит цепочку → блок finalized, irreversible.Byzantine защита здесь не только криптографическая, но и экономическая: чтобы откатить finalized block, нужно сжечь больше 1/3 общего стейка. На сегодня это десятки миллиардов долларов — атака финансово невыгодна.
Bitcoin использует другой подход — probabilistic BFT через Proof-of-Work: 51%-attack threshold, 6 confirmations ≈ 99.9% finality, но строгого finality нет (теоретически возможен глубокий reorg).
Decision tree: нужен ли BFT?
Rule of thumb: BFT только когда node owners взаимно недоверчивы. Иначе это overkill.
ADR-001 на ноде primary: BFT vs CFT — когда нужен 3f+1. Кратко: BFT платит 1/3 ёмкости + O(N²) сообщений + сложность реализации за защиту от лжущих узлов. Внутри trusted DC это overkill, убивающий производительность. В открытых сетях это необходимость, потому что crash-fault модель там теряет safety при первой же компрометации.
ADR-002 на ноде replica-3: PBFT three-phase — зачем три фазы, а не две. Простой 2PC ломается на lying primary: malicious lead шлёт A половине, B другой, обе половины собирают acks независимо и коммитят split-brain. Третья фаза (prepare) заставляет реплики кросс-проверить, что quorum видел одинаковое значение, прежде чем commit. View-change protocol защищает от случая, когда primary меняется в середине — новый не может переписать prepared-значения.
Дополнительные trade-offs, не вынесенные в ADR:
O(N²) сообщений per request. HotStuff (2018) использует threshold signatures (BLS) и pipeline, уменьшает до O(N) — отсюда возможность 100+ validators.Правило: если в вашем threat model нет «один из наших операторов / провайдеров / партнёров может быть взломан и активно врать» — BFT вам не нужен.