Note
Go to the end to download the full example code or to run this example in your browser via JupyterLite.
Byzantine reliable broadcast: echo, then ready (Bracha 1987)#
A sender broadcasts a value, but it may be Byzantine and tell different
peers different things. Bracha’s protocol makes delivery consistent anyway:
peers echo what they received, become ready for a value once a large
quorum echoed it (or once t + 1 peers are ready for it), and deliver
once 2t + 1 peers are ready. With n > 3t, all correct peers deliver
the same value, or none delivers at all.
Naively trusting the sender splits the correct peers. Bracha’s quorums never do, for any split the faulty sender and its accomplices try, as long as at most a third are faulty. One faulty peer too many breaks agreement. Block gossip uses the same idea: a peer forwards what enough others vouch for.
import itertools
import matplotlib.pyplot as plt
import blockchainkit as bk
An equivocating sender#
Sender 0 is faulty and tells peers 1-3 “a” and peers 4-6 “b”.
proposals = ["a", "a", "a", "a", "b", "b", "b"]
naive = {p: proposals[p] for p in range(1, 7)}
print("trust the sender:", naive)
assert len(set(naive.values())) == 2
result = bk.network.reliable_broadcast(7, {0}, proposals)
print("Bracha:", result.delivered)
assert result.agreement and result.totality
trust the sender: {1: 'a', 2: 'a', 3: 'a', 4: 'b', 5: 'b', 6: 'b'}
Bracha: {1: None, 2: None, 3: None, 4: None, 5: None, 6: None}
Every split, every set of faulty peers#
Check all faulty sets of size up to t and all ways to split the peers.
outcomes = {"all deliver": 0, "none deliver": 0, "disagree": 0}
for faulty in itertools.chain.from_iterable(itertools.combinations(range(7), f) for f in (1, 2)):
if 0 not in faulty:
continue
for split in itertools.product("ab", repeat=7):
r = bk.network.reliable_broadcast(7, faulty, list(split))
delivered = set(r.delivered.values())
if not r.agreement:
outcomes["disagree"] += 1
elif delivered == {None}:
outcomes["none deliver"] += 1
else:
outcomes["all deliver"] += 1
print(outcomes)
assert outcomes["disagree"] == 0
{'all deliver': 856, 'none deliver': 40, 'disagree': 0}
One faulty peer too many#
With n = 4 the protocol tolerates t = 1. Two colluding faulty peers can make each correct peer see a different echo quorum.
broken = bk.network.reliable_broadcast(4, {0, 3}, ["a", "a", "b", "b"])
print(broken.delivered)
assert not broken.agreement
fig, ax = plt.subplots(figsize=(6, 3.5))
ax.bar(outcomes.keys(), outcomes.values(), color=["#16a34a", "#94a3b8", "#dc2626"])
ax.set(ylabel="runs", title="n = 7, faulty sender plus up to one accomplice")
fig.tight_layout()
plt.show()

{1: 'a', 2: 'b'}
Exercise#
The echo threshold is ceil((n + t + 1) / 2). Show that two sets of
that size among n peers share at least t + 1 peers, so at least one
correct peer, which echoes only one value. What goes wrong with a simple
majority threshold?
A worked solution is in Exercises: networking.
Total running time of the script: (0 minutes 0.071 seconds)