Added block speed simulations
This commit is contained in:
parent
533442d819
commit
a2433df971
|
@ -1,7 +1,7 @@
|
|||
The general idea of this implementation of Casper is as follows:
|
||||
|
||||
1. There exists a deterministic algorithm which determines a single proposer for each block. Here, the algorithm is simple: every validator is assigned an ID in the range `0 <= i < NUM_VALIDATORS`, and validator `i` proposes all blocks `NUM_VALIDATORS * k + i` for all `k ϵ Z`.
|
||||
2. Validators perform a binary repeated betting procedure on every height, where they bet a value `0 < p < 1` for the probability that they think that a block at that height will be finalized. The bets are incentivized via logarithmic scoring rule, and the result of the bets themselves determines finalization (ie. if 2/3 of all validators bet `p > 0.9999`, the block is considered finalized, and if 2/3 of all validators bet `p < 0.0001`, then the state of no block existing at that height is considered finalized); hence, the betting process is self-referential.
|
||||
2. Validators perform a binary repeated betting procedure on every height, where they bet a value `0 < p < 1` for the probability that they think that a block at that height will be finalized. The bets are incentivized via logarithmic scoring rule, and the result of the bets themselves determines finalization (ie. if 2/3 of all validators bet `p > 0.9999`, the block is considered finalized, and if 2/3 of all validators bet `p < 0.0001`, then it is agreed/finalized that _no_ block exists at that height, and the post-state of that height is equal to the post-state of the previous height); hence, the betting process is self-referential.
|
||||
3. From an incentive standpoint, each validator's optimal strategy is to bet the way they expect everyone else to be betting; hence, it is like a schellingcoin game in certain respects. Convergence in either direction is incentivized. As `p` approaches 0 or 1, the reward for betting correctly increases, but the penalty for betting incorrectly increases hyperbolically, so one only has the incentive to bet `p > 0.9999` or `p < 0.0001` if they are _really_ sure that their bet is correct.
|
||||
4. If a validator's vote exceeds `p = 0.9`, they also need to supply the hash of the block header. Proposing two blocks at a given height is punishable by total deposit slashing.
|
||||
5. From a BFT theory standpoint, this algorithm can be combined with a default strategy where bets are recorded in log odds (ie. `q = ln(p/(1-p))`), if 2/3 of voters vote `q = k` or higher for `k >= 1`, you vote `q = k+1`, and if 2/3 of voters vote `q = k` or lower for `k <= -1`, you vote `q = k-1`; this is similar to a highly protracted ten-round version of Tendermint (log odds of p = 0.9999 ~= 9.21).
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import math
|
||||
BLKTIME = 17
|
||||
X = 0.28
|
||||
|
||||
faclog = [1]
|
||||
for i in range(5000):
|
||||
faclog.append(faclog[-1] * len(faclog))
|
||||
|
||||
def fac(x):
|
||||
return faclog[x]
|
||||
|
||||
def poisson(expected, actual):
|
||||
if expected == 0:
|
||||
return 1 if actual == 0 else 0
|
||||
return 2.718281828 ** (-expected + actual * math.log(expected) - math.log(fac(actual)))
|
||||
|
||||
def p_we_win(k, x):
|
||||
return 1 - (x / (1.0 - x)) ** k
|
||||
|
||||
def p_we_win_after(s):
|
||||
p = 0
|
||||
for i in range(4000):
|
||||
p += poisson(s * 1.0 / BLKTIME, i) * p_we_win(i, X)
|
||||
return p
|
||||
|
||||
for i in range(0, 7200, 12):
|
||||
print i, p_we_win_after(i)
|
|
@ -0,0 +1,52 @@
|
|||
import random
|
||||
BLKTIME = 600
|
||||
LATENCY = 10
|
||||
TEST_MAX = 1200
|
||||
TEST_INTERVAL = 6
|
||||
|
||||
class Block():
|
||||
def __init__(self, parent, txstate):
|
||||
self.parent = parent
|
||||
self.score = 1 if parent is None else parent.score + 1
|
||||
if parent is None or parent.txstate is None:
|
||||
self.txstate = txstate
|
||||
else:
|
||||
self.txstate = parent.txstate
|
||||
|
||||
|
||||
results = {}
|
||||
|
||||
|
||||
for double_spend_delay in range(0, TEST_MAX, TEST_INTERVAL):
|
||||
results[double_spend_delay] = 0
|
||||
for _ in range(1000):
|
||||
a_head = None
|
||||
b_head = None
|
||||
recvqueue = {}
|
||||
for time_elapsed in range(5000):
|
||||
txstate = 1 if time_elapsed < double_spend_delay else 2
|
||||
# Miner A mines and sends (has 50% network share)
|
||||
if random.random() * BLKTIME < 0.5:
|
||||
a_head = Block(a_head, txstate)
|
||||
if time_elapsed + LATENCY not in recvqueue:
|
||||
recvqueue[time_elapsed + LATENCY] = []
|
||||
recvqueue[time_elapsed + LATENCY].append(a_head)
|
||||
# Miner B mines and sends (has 50% network share)
|
||||
if random.random() * BLKTIME < 0.5:
|
||||
b_head = Block(b_head, txstate)
|
||||
if time_elapsed + LATENCY not in recvqueue:
|
||||
recvqueue[time_elapsed + LATENCY] = []
|
||||
recvqueue[time_elapsed + LATENCY].append(b_head)
|
||||
# Receive blocks
|
||||
if time_elapsed in recvqueue:
|
||||
for b in recvqueue[time_elapsed]:
|
||||
if not a_head or b.score > a_head.score or (b.score == a_head.score and random.random() < 0.5):
|
||||
a_head = b
|
||||
if not b_head or b.score > b_head.score or (b.score == b_head.score and random.random() < 0.5):
|
||||
b_head = b
|
||||
# Check which transaction "made it"
|
||||
if a_head and a_head.txstate == 1:
|
||||
results[double_spend_delay] += 0.001
|
||||
print (double_spend_delay, results[double_spend_delay])
|
||||
|
||||
print(results)
|
Loading…
Reference in New Issue