Proof of stake: a stake-weighted proposer lottery (Peercoin 2012)#

Weighting proposer selection by stake changes the resource used to allocate influence. This sampler isolates that one mechanism; it has no voting, slashing, randomness beacon, or finality and is not a full PoS protocol.

Compare observed selection frequencies with stake proportions. More trials tend to bring them closer; splitting one stake among names does not increase its combined expected share.

from collections import Counter

import matplotlib.pyplot as plt

import blockchainkit as bk
stakes = {"alice": 10, "bob": 30, "carol": 60}
rounds = 10_000
proposers = bk.consensus.StakeSampler(stakes, seed=42).sample(rounds)
assert proposers == bk.consensus.StakeSampler(stakes, seed=42).sample(rounds)
counts = Counter(proposers)
expected = [stake / sum(stakes.values()) for stake in stakes.values()]
observed = [counts[name] / rounds for name in stakes]
print("Observed selection fractions:", dict(zip(stakes, observed, strict=True)))
Observed selection fractions: {'alice': 0.0938, 'bob': 0.2949, 'carol': 0.6113}

Stake concentration is visible even when the lottery is fair#

fig, ax = plt.subplots(figsize=(7, 4))
xs = list(range(len(stakes)))
ax.bar([x - 0.18 for x in xs], expected, 0.36, label="Stake fraction")
ax.bar([x + 0.18 for x in xs], observed, 0.36, label="Observed proposer fraction")
ax.set(
    xticks=xs,
    xticklabels=list(stakes),
    ylabel="Fraction",
    title="Seeded proposer sampling over 10,000 rounds",
)
ax.legend()
fig.tight_layout()

plt.show()
Seeded proposer sampling over 10,000 rounds

Exercise#

Split Carol’s stake across ten names. Does her total expected influence change? Why is choosing a proposer insufficient to decide between two conflicting blocks that proposer might sign? Why is a public seed unsuitable as the sole randomness mechanism for a live consensus protocol?

A worked solution is in Exercises: consensus.

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

Gallery generated by Sphinx-Gallery