Byzantine fault tolerance is the part of consensus where the clean model (“nodes exchange messages and agree”) collides with the messy reality (“nodes can be buggy, slow, or malicious, and the network lies to you”).
In blockchain systems, BFT protocols are what give you instant finality and clear safety guarantees when up to a fraction of validators misbehave. PBFT is the classic theory; Tendermint (CometBFT) is how that theory shows up in production validator networks. (pmg.csail.mit.edu)
I’ll walk through the theory you actually need, then how it maps to Tendermint‑style blockchains and the practical things that bite you when you run real validator sets.
1. Prerequisites
You should be comfortable with:
- Basics of distributed systems: processes, messages, failure modes.
- The idea of “consensus” as: all non‑faulty nodes agree on a value and don’t change their minds. (Wikipedia)
- High‑level blockchain structure: blocks, validators, commit rules.
I’ll keep the math light and use ASCII diagrams rather than pseudo‑code.
2. BFT model in one page
2.1 Failure models: crash vs Byzantine
Crash‑fault tolerant protocols (like Raft) assume “good or dead” nodes: a node either behaves correctly or stops. Byzantine‑fault tolerant protocols assume nodes can do anything: send conflicting messages, lie about what they saw, or collude with others. (users.cs.utah.edu)
In BFT state machine replication we usually assume:
- n replicas (validators)
- up to f can be Byzantine
- the rest "honest-but-buggy": they follow the protocol as implemented
The classic result: to tolerate f Byzantine nodes, you need at least n ≥ 3f + 1. (users.cs.utah.edu)
2.2 Safety vs liveness and FLP
Two properties matter:
Safety:
"Nothing bad happens"
– No two honest nodes commit different values.
Liveness:
"Something good eventually happens"
– Honest nodes eventually commit *some* value.
The FLP result says: in a fully asynchronous network (no timing assumptions), you cannot guarantee both safety and liveness at the same time, even with just crash faults. (dinhtta.github.io)
Modern BFT protocols (including PBFT and Tendermint) sidestep this by assuming partial synchrony: the network can be arbitrarily slow for a while, but eventually messages are delivered within some (unknown) bound. Safety doesn’t depend on timing; liveness does. (preethikasireddy.com)
2.3 Why 3f + 1 and quorums
The core BFT trick is quorum intersection.
You want two properties for commit votes:
- A commit quorum must be big enough to "see past" the faulty nodes.
- Any two commit quorums must overlap in at least one honest node.
With n = 3f + 1 and quorums of size 2f + 1:
- At most f nodes are faulty.
- Each quorum has at least (2f + 1 - f) = f + 1 honest nodes.
- Any two quorums intersect in at least (2f + 1 + 2f + 1 - 3f - 1) = f + 1 nodes.
ASCII sketch:
Total nodes: ● ● ● ● ● ● ● (7 = 3*2 + 1)
Quorum A: [● ● ● ● ●]
Quorum B: [● ● ● ● ●]
Overlap: [● ● ●]
at least f+1 honest nodes in the intersection
That overlap is what prevents two different conflicting values from both reaching a “commit” quorum among honest nodes. (users.cs.utah.edu)
3. PBFT in a nutshell
Practical Byzantine Fault Tolerance (PBFT) is the classic protocol that turned BFT from theory into something implementable. (pmg.csail.mit.edu)
3.1 Setup and roles
PBFT is designed for replicated services:
- n = 3f + 1 replicas (one primary, others backups).
- Clients send requests; replicas agree on an order of requests.
- Every replica runs the same deterministic state machine on the agreed sequence of commands.
High‑level data flow:
Client -> Primary -> All replicas
All replicas <-> All replicas (prepare/commit floods)
Client <- 2f+1 matching replies
3.2 The three phases
For one client request (or one “slot” in the log), PBFT does:
1. PRE-PREPARE
- Primary proposes a request with a sequence number.
2. PREPARE
- Replicas echo the proposal to each other (vote "I saw this").
3. COMMIT
- Replicas echo again, then commit once they see 2f+1 matching votes.
ASCII message flow:
Client Primary Replica i Replica j
| | | |
| request | | |
|-----------> | | |
| | PRE-PREPARE | |
| |---------------->| |
| |-------------------------------> | (broadcast)
| | | PREPARE |
| | |---------------->|
| |<----------------|<----------------|
| | | |
| | | COMMIT |
| | |<--------------->|
| | | commit |
| reply from 2f+1 replicas | |
The exact message formats and checks are more involved, but the intuition is:
- Pre‑prepare: primary proposes.
- Prepare: everyone agrees what was proposed and in what slot.
- Commit: everyone agrees that “enough of us agreed”, so we execute.
PBFT ensures that once a correct replica commits a request in a given sequence number, no other correct replica will commit a different request in that sequence number, even if the primary is Byzantine. (pmg.csail.mit.edu)
3.3 View changes
If the primary is slow or malicious, replicas trigger a view change:
- They elect the next replica as primary (round‑robin or similar).
- They transfer the log of prepared requests so the new primary can continue.
PBFT’s view‑change protocol is what gives you liveness under partial synchrony: once the network calms down, some correct replica will become primary and make progress. (pmg.csail.mit.edu)
3.4 Why PBFT isn’t your blockchain consensus
PBFT was designed for small, permissioned clusters (n in the single digits to tens). It has:
- O(n²) message complexity per operation.
- Explicit client‑reply paths.
- No notion of “blocks” chained together.
That’s why modern PoS blockchains adapt the ideas but change the shape. Tendermint is the canonical example.
4. Tendermint / CometBFT: PBFT for blockchains
Tendermint (and its modern fork, CometBFT) is essentially PBFT adapted to a blockchain: instead of agreeing on an ordered list of requests, it agrees on one block per height using a round‑based protocol. (Tendermint)
4.1 Heights, rounds, steps
Consensus runs at each block height H as a loop of rounds:
Height H:
Round 0: Propose -> Prevote -> Precommit
Round 1: Propose -> Prevote -> Precommit
...
Commit -> NewHeight(H+1)
Cosmos/CometBFT docs describe this as steps Propose, Prevote, Precommit, plus Commit / NewHeight. (docs.cometbft.com)
ASCII state flow for one height:
NewHeight(H)
|
v
PROPOSE --> PREVOTE --> PRECOMMIT --> COMMIT
^ |
| v
timeouts New round (H, r+1)
If all goes well, you commit in the first round. If not, you increment the round and try again with a new proposer.
4.2 Validators and voting power
Validators have voting power (stake). Tendermint’s BFT guarantee is:
- At most 1/3 of the voting power can be Byzantine.
- If ≥ 2/3 of the voting power follows the protocol, you get safety and liveness (under partial synchrony). (Autonity Documentation)
The 3f+1 logic is the same, but now f is “faulty voting power”, not simply “number of nodes”.
4.3 Propose, prevote, precommit
In each round r at height H:
-
Propose
- The proposer (deterministically chosen from the validator set) broadcasts a block proposal.
- Block is identified by a
BlockID(hash + metadata); data is gossiped separately. (Tendermint Core Documentation)
-
Prevote
-
Each validator checks the proposal.
-
They prevote for:
- the proposed block’s ID, or
nilif they didn’t see a valid proposal or are locked on another block.
-
-
Precommit
- If a validator sees ≥ 2/3 of voting power prevote for the same blockID in this round, it locks that block and precommits it.
- Otherwise it may precommit
nil.
-
Commit
- If a validator sees ≥ 2/3 precommits for the same blockID in some round, it commits that block as the next block and moves to
NewHeight. (Tendermint Core Documentation)
- If a validator sees ≥ 2/3 precommits for the same blockID in some round, it commits that block as the next block and moves to
Locking and the two‑stage vote (prevote → precommit) are the Tendermint counterparts of PBFT’s prepare/commit, adjusted for a round‑based blockchain protocol. The Tendermint paper formalizes safety and liveness under the usual “<1/3 Byzantine” and partial synchrony assumptions. (Tendermint)
4.4 Why locking matters
Locking prevents two different blocks from both being committed at the same height.
Intuition:
- Once ≥ 2/3 prevote a block B in round r, any honest validator that sees this locks B.
- In later rounds, locked validators will not prevote a conflicting block unless they see ≥ 2/3 prevotes for something else in a higher round.
- With <1/3 Byzantine power, you cannot assemble 2/3 precommits for two different blocks at the same height; the quorums must overlap in ≥1/3+ honest voting power. (Tendermint)
That’s what gives you instant finality: once 2/3 precommits for a block are observed, there is no alternative chain at that height that honest nodes could later switch to.
5. Practical implementation considerations
Theory is nice; running a real validator network is where rough edges show up.
5.1 Network and identity
In PBFT/Tendermint you know the participants:
- Fixed or slowly changing validator set.
- Each validator has a public key and voting power.
- Network is permissioned at the consensus layer.
CometBFT/Tendermint docs treat validators as long‑lived processes with known keys; P2P gossip handles block/data propagation while consensus messages are signed and authenticated. (Tendermint Core Documentation)
This has design implications:
- You can use authenticated P2P overlays, whitelisting, and TLS.
- Message spoofing is off the table; equivocation is provable via signatures.
5.2 Message authentication and evidence
Every prevote and precommit is signed. That lets you:
- prove double‑signing (same height/round, two different blockIDs);
- slash misbehaving validators in PoS systems;
- build light‑client protocols that trust vote sets instead of full blocks. (cosmos-network.gitbooks.io)
From an implementation point of view:
- You need efficient signature verification (batching, caching).
- You need a robust "evidence" subsystem that:
- gossips proof of faults,
- stores it durably,
- triggers slashing / jailing logic exactly once.
5.3 Timeouts and partial synchrony
Tendermint uses explicit timeouts for each step (proposal, prevote, precommit). When timeouts fire, nodes move to the next round. (Tendermint Core Documentation)
Practically:
- Timeouts must be long enough to cover normal network delay and validation cost.
- Timeouts may be dynamically increased if rounds keep timing out (hinting at bad network conditions).
- Very aggressive timeouts cause needless round churn; very lax timeouts kill throughput.
You also need to persist enough consensus state (height, round, votes, locked block) to disk so that crashes/restarts don’t break safety or stall liveness.
5.4 Validator set changes
Real networks change validator sets over time (staking/unstaking, governance, slashing). The tricky bit is aligning these changes with consensus:
Height H:
- commit block with new validator set for H+1
- consensus for H+1 uses that new set and their voting powers
Cosmos SDK / CometBFT do exactly this: the application logic computes the next validator set, and Tendermint uses it in the next height. (Cosmos Documentation)
You must:
- ensure all nodes deterministically compute the same new set;
- handle edge cases where a validator is added/removed around a height boundary;
- deal with key rotations (new consensus keys) without breaking identity.
5.5 Performance and scaling
Classic PBFT is O(n²) in messages per decision. Tendermint optimizes some paths (gossip, aggregation), but you’re still fundamentally doing a lot of all‑to‑all communication. (Tendermint)
In practice:
- Keep validator sets modest in size (tens to low hundreds); push broader participation into delegation (stake → validators) rather than more validators.
- Use efficient networking (authenticated gossip, limited connection counts).
- Consider signature aggregation (BLS) in newer designs to shrink vote sets.
6. Testing BFT consensus
You can’t unit‑test your way into confidence on a BFT implementation; you need faulty network tests.
I like three layers:
6.1 Deterministic simulation
Run the consensus state machine inside a simulator that controls message delivery:
- Deliver messages in random orders.
- Drop, delay, or duplicate messages.
- Simulate crashes and restarts.
- Ensure:
- safety: no two simulated nodes commit different blocks at same height.
- liveness: eventually a block commits once network becomes "nice".
This is essentially how research evaluations of BFT protocols are done: inject faults, check invariants. (ScienceDirect)
6.2 Integration tests with real processes
Spin up n validator nodes in containers or VMs and:
- introduce network partitions (firewall rules);
- kill and restart processes;
- vary CPU load to stress timeouts;
- inject a “Byzantine node” that equivocates or withholds votes.
Check:
- that the chain seen by honest nodes remains linear and identical;
- that evidence of double‑signing is generated and propagated;
- that nodes recover from partitions once connectivity is restored.
6.3 Long‑running chaos
In production‑like environments, run continuous chaos:
- Randomly restart nodes.
- Randomly throttle or drop a percentage of messages.
- Monitor time‑to‑finality, missed‑vote rates, and evidence volume.
You’re looking for “silent safety violations” (should never happen) and “pathological liveness issues” (e.g. frequent multi‑round commits under normal conditions).
7. From theory to validator operations
The interesting thing about BFT in blockchains is that operations now matter as much as protocol math.
A few patterns that show up over and over:
-
Key management and slashing. Consensus keys must be tightly controlled; a compromised validator key can double‑sign, causing slashing. You need HSMs or MPC for keys, clear rotation procedures, and monitoring of signing behaviour. (cosmos-network.gitbooks.io)
-
Monitoring quorum health. You care about “voting power online”, not just “nodes up”. Dashboards should show precommit participation per block, so you can spot validators that are flapping or misconfigured.
-
Upgrade discipline. Because safety depends on deterministic behaviour, all validators must run compatible software and configuration. State machine changes (application layer) must be coordinated with consensus expectations.
-
Inter‑chain implications. When you use Tendermint‑like chains as IBC/bridge endpoints, BFT safety becomes bridge safety. A BFT safety break at height H can ripple into cross‑chain state if clients trust an invalid block. (Cosmos Documentation)
From the trenches. The nastiest bugs I’ve seen in Tendermint‑style networks weren’t mere crashes; they were subtle configuration mismatches or non‑determinism in the application layer that caused nodes to diverge on block contents. Consensus did its job (commit a block if 2/3+ validators agree), but some nodes couldn’t execute that block. The protocol was fine; the state machine wasn’t.
8. Conclusion
Byzantine fault tolerance is the part of blockchain consensus that actually earns the word “consensus”.
The theory gives you a clean story:
- With n ≥ 3f+1 and < 1/3 Byzantine power,
- under partial synchrony,
- you can get safety (no forks) and liveness (eventual commits).
PBFT shows how to do this for generic services; Tendermint/CometBFT show how to do it for blockchains, one block at a time, with prevotes, precommits, locking, and signed evidence. (pmg.csail.mit.edu)
In practice, your real work as an engineer is:
- implementing the protocol carefully (state machine, timeouts, persistence),
- running robust validator operations (keys, upgrades, monitoring),
- and understanding exactly how much you can trust the “2/3+ precommits” signal in the presence of bugs and adversaries.
Once you have that, you can start reasoning confidently about things like “finality”, “fork choice”, and “cross‑chain safety” in a Tendermint‑style ecosystem—using BFT not as a buzzword, but as a concrete set of guarantees you know how to maintain.
Source notes
- Castro Liskov – Practical Byzantine Fault Tolerance and follow‑up thesis: foundational PBFT protocol and analysis. (pmg.csail.mit.edu)
- Kwon – Tendermint: Consensus without Mining – BFT consensus adapted to blockchains. (Tendermint)
- Tendermint / CometBFT docs – consensus overview, height/round/step state machine, validator behaviour. (Tendermint Core Documentation)
- FLP and partial synchrony – why we assume partial synchrony and separate safety from liveness. (dinhtta.github.io)
- Recent BFT surveys and optimizations – overviews of modern variants and scaling approaches. (ScienceDirect)
Completion scope and production contract
This completion review turns the earlier conceptual treatment into a release-oriented engineering contract. It treats Byzantine Fault Tolerance: Theory and Practice as a cryptographic or distributed-security component, follows a key, message, transcript, proof, signature, hash input, or authenticated protocol event through validation and durable state, and separates normative requirements from implementation policy. The normative baseline is the named standard, security definition, test vectors, and maintained library contract; deployment defaults, caching, retry limits, and operator thresholds are explicitly local policy. 13
The intended audience is experienced developers and architects. Readers should understand the surrounding chain or application model, typed data structures, persistence, and basic security engineering. The scope includes correctness, implementation boundaries, deterministic tests, failure recovery, security, performance, and observability. It does not claim that the educational companion is a drop-in replacement for a maintained protocol or cryptographic library. Production adoption requires an independent threat model, compatibility testing against the authoritative implementation, and operational ownership. 14
The mental model used throughout is deliberately strict: untrusted input crosses entropy, parsing, key custody, verification, protocol, and storage boundaries; a validator derives facts under the named standard, security definition, test vectors, and maintained library contract; accepted transitions update domain-separated cryptographic state and explicitly authenticated protocol state; and observers consume committed facts, never optimistic intermediate mutations. A guarantee is stated only when it follows from those rules and assumptions. Heuristics such as fee selection, caching, peer scoring, timeouts, user messaging, or alert thresholds remain policy and may be tuned without redefining validity. 15
Reader contract and scope
For Byzantine Fault Tolerance: Theory and Practice, this review makes the exact user decision and the prerequisites needed to make it safely explicit. Start from one key, message, transcript, proof, signature, hash input, or authenticated protocol event and write down its origin, canonical representation, validation context, authority, and durable outcome. The cryptographic or distributed-security component must not infer a stronger fact from transport success, cache presence, or an upstream acknowledgement. Its authoritative state is domain-separated cryptographic state and explicitly authenticated protocol state, and every projection must remain rebuildable or reconcilable from committed facts. This framing distinguishes a protocol guarantee from an operational convenience and gives reviewers a concrete place to challenge an assumption. 13
The principal failure to design against is an attractive example being mistaken for a complete production design. Address it before optimizing by defining a narrow ownership boundary, stable identities, bounded resource use, and a machine-readable outcome for every rejected transition. Record a scope statement, excluded concerns, and a reviewable acceptance criterion. A reviewer should be able to trace each accepted result to input bytes, rule or policy version, prior state, and commit identity without relying on prose logs. When the authority cannot be reached or context is incomplete, return an explicit unavailable or pending state; do not convert uncertainty into acceptance.
Precise vocabulary and authority
Treat precise vocabulary and authority as part of the executable design of Byzantine Fault Tolerance: Theory and Practice, not as documentation added after coding. The relevant operating envelope includes valid vectors, malformed encodings, chosen inputs, key rotation, retries, and hostile traffic. For each mode, identify which state is authoritative, which work may be retried, what is bounded, and which observation proves progress. This is especially important across entropy, parsing, key custody, verification, protocol, and storage boundaries, where delivery and processing are different events and where local time or arrival order may not reflect authoritative order. 14
A useful review asks how the design behaves under malleability, nonce reuse, biased randomness, side channels, parser differentials, key compromise, and denial of service. The unsafe outcome is teams using the same word for incompatible states or guarantees. Prevent it with explicit preconditions and postconditions, and retain a glossary tied to the normative authority for every overloaded term as release evidence. Use stable codes rather than exception text, keep policy configuration versioned, and attach the accepted policy or rule version to durable results. An security engineer or protocol operator must be able to stop intake, drain or quarantine work, compare local state with authority, and resume without inventing a second side effect.
Trust assumptions
The implementation of Byzantine Fault Tolerance: Theory and Practice should expose which actors, clocks, stores, libraries, and upstream systems may fail or act maliciously through types and module boundaries. Parse external representations once, preserve the original identity when audit or replay needs it, and pass validated domain values inward. Mutations of domain-separated cryptographic state and explicitly authenticated protocol state belong behind one authoritative transition function or transaction boundary. Network clients, storage adapters, user interfaces, and telemetry exporters must not duplicate consensus or business rules. That separation keeps deterministic logic testable and prevents a library upgrade from silently redefining validity. 15
Assume that an implicit trusted component invalidating the claimed guarantee will eventually occur in a staging fault test or production incident. The control is not a catch-all retry: classify the outcome, decide whether the identical operation is safe to repeat, bound attempts and elapsed time, and surface terminal work for reconciliation. The minimum evidence is a trust-boundary diagram and an assumption register with owners. Keep secret scalars, seeds, nonces, key-encryption keys, authentication tags, and timing behavior out of ordinary logs, and prefer hashes, version identifiers, counts, and sanitized reason codes. Any emergency bypass must be narrow, time-limited, approved, and observable.
Architecture and ownership
Verification for Byzantine Fault Tolerance: Theory and Practice must demonstrate component responsibilities and the direction in which facts and commands move at several layers. Small tests cover deterministic transforms and boundary values; contract tests pin serialized forms; integration tests exercise real adapters; replay tests compare state roots or projections; and fault tests interrupt work at every commit boundary. The dataset must include normal history, malformed input, duplicates, reordering, maximum-size values, and changes of rule or schema version. A passing example is evidence about that environment and dataset, not a universal performance claim. 13
Make two components both believing they own the same transition a named negative test. The release packet should retain a context diagram, ownership table, and dependency rule, exact dependency and tool versions, the deterministic command, and its result. For performance work, report warm-up, repetitions, concurrency, percentiles, resource limits, and the point where backpressure begins. For security work, include abuse cases and independent review. A change is ready only when failures leave domain-separated cryptographic state and explicitly authenticated protocol state safe, recovery is rehearsed, and telemetry explains both user-visible outcome and operator action.
Canonical representation
For Byzantine Fault Tolerance: Theory and Practice, this review makes the byte-level or schema-level representation used for hashing, comparison, storage, and transport explicit. Start from one key, message, transcript, proof, signature, hash input, or authenticated protocol event and write down its origin, canonical representation, validation context, authority, and durable outcome. The cryptographic or distributed-security component must not infer a stronger fact from transport success, cache presence, or an upstream acknowledgement. Its authoritative state is domain-separated cryptographic state and explicitly authenticated protocol state, and every projection must remain rebuildable or reconcilable from committed facts. This framing distinguishes a protocol guarantee from an operational convenience and gives reviewers a concrete place to challenge an assumption. 14
The principal failure to design against is semantically equal values producing different identifiers or verification outcomes. Address it before optimizing by defining a narrow ownership boundary, stable identities, bounded resource use, and a machine-readable outcome for every rejected transition. Record golden encodings, round-trip tests, and rejection of non-canonical forms. A reviewer should be able to trace each accepted result to input bytes, rule or policy version, prior state, and commit identity without relying on prose logs. When the authority cannot be reached or context is incomplete, return an explicit unavailable or pending state; do not convert uncertainty into acceptance.
State-machine model
Treat state-machine model as part of the executable design of Byzantine Fault Tolerance: Theory and Practice, not as documentation added after coding. The relevant operating envelope includes valid vectors, malformed encodings, chosen inputs, key rotation, retries, and hostile traffic. For each mode, identify which state is authoritative, which work may be retried, what is bounded, and which observation proves progress. This is especially important across entropy, parsing, key custody, verification, protocol, and storage boundaries, where delivery and processing are different events and where local time or arrival order may not reflect authoritative order. 15
A useful review asks how the design behaves under malleability, nonce reuse, biased randomness, side channels, parser differentials, key compromise, and denial of service. The unsafe outcome is an impossible intermediate state becoming durable after interruption. Prevent it with explicit preconditions and postconditions, and retain a transition table exercised by positive, negative, and replay tests as release evidence. Use stable codes rather than exception text, keep policy configuration versioned, and attach the accepted policy or rule version to durable results. An security engineer or protocol operator must be able to stop intake, drain or quarantine work, compare local state with authority, and resume without inventing a second side effect.
Invariants
The implementation of Byzantine Fault Tolerance: Theory and Practice should expose properties that must hold before and after every accepted operation through types and module boundaries. Parse external representations once, preserve the original identity when audit or replay needs it, and pass validated domain values inward. Mutations of domain-separated cryptographic state and explicitly authenticated protocol state belong behind one authoritative transition function or transaction boundary. Network clients, storage adapters, user interfaces, and telemetry exporters must not duplicate consensus or business rules. That separation keeps deterministic logic testable and prevents a library upgrade from silently redefining validity. 13
Assume that local success concealing corruption in a related aggregate or index will eventually occur in a staging fault test or production incident. The control is not a catch-all retry: classify the outcome, decide whether the identical operation is safe to repeat, bound attempts and elapsed time, and surface terminal work for reconciliation. The minimum evidence is executable assertions at the narrowest authoritative boundary. Keep secret scalars, seeds, nonces, key-encryption keys, authentication tags, and timing behavior out of ordinary logs, and prefer hashes, version identifiers, counts, and sanitized reason codes. Any emergency bypass must be narrow, time-limited, approved, and observable.
Validation pipeline
Verification for Byzantine Fault Tolerance: Theory and Practice must demonstrate cheap structural checks, contextual checks, authoritative verification, and commit order at several layers. Small tests cover deterministic transforms and boundary values; contract tests pin serialized forms; integration tests exercise real adapters; replay tests compare state roots or projections; and fault tests interrupt work at every commit boundary. The dataset must include normal history, malformed input, duplicates, reordering, maximum-size values, and changes of rule or schema version. A passing example is evidence about that environment and dataset, not a universal performance claim. 14
Make expensive or stateful work running before malformed input is rejected a named negative test. The release packet should retain ordered validation stages with stable machine-readable rejection codes, exact dependency and tool versions, the deterministic command, and its result. For performance work, report warm-up, repetitions, concurrency, percentiles, resource limits, and the point where backpressure begins. For security work, include abuse cases and independent review. A change is ready only when failures leave domain-separated cryptographic state and explicitly authenticated protocol state safe, recovery is rehearsed, and telemetry explains both user-visible outcome and operator action.
Error semantics
For Byzantine Fault Tolerance: Theory and Practice, this review makes the distinction between invalid input, conflict, unavailable dependency, retryable interruption, and internal defect explicit. Start from one key, message, transcript, proof, signature, hash input, or authenticated protocol event and write down its origin, canonical representation, validation context, authority, and durable outcome. The cryptographic or distributed-security component must not infer a stronger fact from transport success, cache presence, or an upstream acknowledgement. Its authoritative state is domain-separated cryptographic state and explicitly authenticated protocol state, and every projection must remain rebuildable or reconcilable from committed facts. This framing distinguishes a protocol guarantee from an operational convenience and gives reviewers a concrete place to challenge an assumption. 15
The principal failure to design against is blind retries amplifying a permanent failure or changing user intent. Address it before optimizing by defining a narrow ownership boundary, stable identities, bounded resource use, and a machine-readable outcome for every rejected transition. Record typed errors mapped consistently across logs, metrics, APIs, and queues. A reviewer should be able to trace each accepted result to input bytes, rule or policy version, prior state, and commit identity without relying on prose logs. When the authority cannot be reached or context is incomplete, return an explicit unavailable or pending state; do not convert uncertainty into acceptance.
Concurrency control
Treat concurrency control as part of the executable design of Byzantine Fault Tolerance: Theory and Practice, not as documentation added after coding. The relevant operating envelope includes valid vectors, malformed encodings, chosen inputs, key rotation, retries, and hostile traffic. For each mode, identify which state is authoritative, which work may be retried, what is bounded, and which observation proves progress. This is especially important across entropy, parsing, key custody, verification, protocol, and storage boundaries, where delivery and processing are different events and where local time or arrival order may not reflect authoritative order. 13
A useful review asks how the design behaves under malleability, nonce reuse, biased randomness, side channels, parser differentials, key compromise, and denial of service. The unsafe outcome is a check-then-act race accepting two individually plausible operations. Prevent it with explicit preconditions and postconditions, and retain a linearization argument plus stress tests at the chosen contention boundary as release evidence. Use stable codes rather than exception text, keep policy configuration versioned, and attach the accepted policy or rule version to durable results. An security engineer or protocol operator must be able to stop intake, drain or quarantine work, compare local state with authority, and resume without inventing a second side effect.
Idempotency and replay
The implementation of Byzantine Fault Tolerance: Theory and Practice should expose how duplicate delivery, process restart, and historical backfill preserve the same result through types and module boundaries. Parse external representations once, preserve the original identity when audit or replay needs it, and pass validated domain values inward. Mutations of domain-separated cryptographic state and explicitly authenticated protocol state belong behind one authoritative transition function or transaction boundary. Network clients, storage adapters, user interfaces, and telemetry exporters must not duplicate consensus or business rules. That separation keeps deterministic logic testable and prevents a library upgrade from silently redefining validity. 14
Assume that at-least-once delivery creating a second side effect will eventually occur in a staging fault test or production incident. The control is not a catch-all retry: classify the outcome, decide whether the identical operation is safe to repeat, bound attempts and elapsed time, and surface terminal work for reconciliation. The minimum evidence is stable operation identities, deduplication state, and deterministic replay fixtures. Keep secret scalars, seeds, nonces, key-encryption keys, authentication tags, and timing behavior out of ordinary logs, and prefer hashes, version identifiers, counts, and sanitized reason codes. Any emergency bypass must be narrow, time-limited, approved, and observable.
Persistence and atomicity
Verification for Byzantine Fault Tolerance: Theory and Practice must demonstrate which facts commit together and how derived views catch up at several layers. Small tests cover deterministic transforms and boundary values; contract tests pin serialized forms; integration tests exercise real adapters; replay tests compare state roots or projections; and fault tests interrupt work at every commit boundary. The dataset must include normal history, malformed input, duplicates, reordering, maximum-size values, and changes of rule or schema version. A passing example is evidence about that environment and dataset, not a universal performance claim. 15
Make a crash exposing a cursor that claims work whose state was not committed a named negative test. The release packet should retain transaction boundaries, durable checkpoints, and reconciliation queries, exact dependency and tool versions, the deterministic command, and its result. For performance work, report warm-up, repetitions, concurrency, percentiles, resource limits, and the point where backpressure begins. For security work, include abuse cases and independent review. A change is ready only when failures leave domain-separated cryptographic state and explicitly authenticated protocol state safe, recovery is rehearsed, and telemetry explains both user-visible outcome and operator action.
API and schema contracts
For Byzantine Fault Tolerance: Theory and Practice, this review makes input limits, optionality, pagination, versioning, and compatibility behavior explicit. Start from one key, message, transcript, proof, signature, hash input, or authenticated protocol event and write down its origin, canonical representation, validation context, authority, and durable outcome. The cryptographic or distributed-security component must not infer a stronger fact from transport success, cache presence, or an upstream acknowledgement. Its authoritative state is domain-separated cryptographic state and explicitly authenticated protocol state, and every projection must remain rebuildable or reconcilable from committed facts. This framing distinguishes a protocol guarantee from an operational convenience and gives reviewers a concrete place to challenge an assumption. 13
The principal failure to design against is a technically valid deployment silently changing a consumer-visible meaning. Address it before optimizing by defining a narrow ownership boundary, stable identities, bounded resource use, and a machine-readable outcome for every rejected transition. Record consumer fixtures, schema-diff checks, and explicit deprecation windows. A reviewer should be able to trace each accepted result to input bytes, rule or policy version, prior state, and commit identity without relying on prose logs. When the authority cannot be reached or context is incomplete, return an explicit unavailable or pending state; do not convert uncertainty into acceptance.
Security controls
Treat security controls as part of the executable design of Byzantine Fault Tolerance: Theory and Practice, not as documentation added after coding. The relevant operating envelope includes valid vectors, malformed encodings, chosen inputs, key rotation, retries, and hostile traffic. For each mode, identify which state is authoritative, which work may be retried, what is bounded, and which observation proves progress. This is especially important across entropy, parsing, key custody, verification, protocol, and storage boundaries, where delivery and processing are different events and where local time or arrival order may not reflect authoritative order. 14
A useful review asks how the design behaves under malleability, nonce reuse, biased randomness, side channels, parser differentials, key compromise, and denial of service. The unsafe outcome is a correct core algorithm being exposed through an overpowered interface. Prevent it with explicit preconditions and postconditions, and retain abuse cases, permission tests, secret scans, and independently reviewed defaults as release evidence. Use stable codes rather than exception text, keep policy configuration versioned, and attach the accepted policy or rule version to durable results. An security engineer or protocol operator must be able to stop intake, drain or quarantine work, compare local state with authority, and resume without inventing a second side effect.