Discrete-event simulation: a network on an event queue (GPSS 1961, Simula 1965)#

A discrete-event simulator keeps a queue of future events ordered by time. It pops the earliest, jumps the clock straight to it, and lets it schedule new events. Nothing happens between events, so no time is wasted waiting, and with a seeded random source every run is exactly reproducible. SimulatedNetwork simulates gossip this way: a message sent on a link becomes a delivery event a random number of ticks later.

Follow arrival times as the message crosses the network. An isolated peer misses it, and reconnecting the peer requires an explicit retransmission of old news. Rerunning with the same seed reproduces every arrival time.

import matplotlib.pyplot as plt

import blockchainkit as bk
network = bk.network.SimulatedNetwork(["alice", "bob", "carol", "dave"], seed=7)
network.connect("alice", "bob", latency=(2, 4))
network.connect("bob", "carol", latency=(2, 4))
# Dave starts partitioned from the other three peers.
network.broadcast("alice", b"a new block announcement")
network.run(until=10)
assert {event.recipient for event in network.deliveries} == {"alice", "bob", "carol"}

Same seed, same history#

The simulator draws latencies from its own seeded generator, so a second run with the same operations reproduces every event exactly.

def replay(seed):
    net = bk.network.SimulatedNetwork(["alice", "bob", "carol", "dave"], seed=seed)
    net.connect("alice", "bob", latency=(2, 4))
    net.connect("bob", "carol", latency=(2, 4))
    net.broadcast("alice", b"a new block announcement")
    net.run(until=10)
    net.connect("carol", "dave", latency=(2, 2))
    net.broadcast("carol", b"a new block announcement")
    net.run()
    return net.deliveries


assert replay(7) == network.deliveries
fig, ax = plt.subplots(figsize=(8, 4))
names = [event.recipient for event in network.deliveries]
times = [event.time for event in network.deliveries]
ax.barh(names, times, color=["#2563eb"] * 3 + ["#ea580c"])
ax.axvline(10, color="black", linestyle="--", label="Partition healed")
ax.scatter(times, names, color="black", zorder=3)
ax.set(
    xlabel="First receipt time (simulation ticks)",
    title="Propagation and explicit resynchronization",
)
ax.legend()
fig.tight_layout()

plt.show()
Propagation and explicit resynchronization

Exercise#

Connect a triangle and check that duplicate paths do not duplicate receipts. Disconnect a link while a message is in flight. Then replace fixed latency with a range and compare results under the same and different seeds.

A worked solution is in Exercises: networking.

Total running time of the script: (0 minutes 0.056 seconds)

Gallery generated by Sphinx-Gallery