research/slasher_withholding_exploit.py

44 lines
1.4 KiB
Python

def fac(n): return 1 if n==0 else n * fac(n-1)
def choose(n,k): return fac(n) / fac(k) / fac(n-k)
def prob(n,k,p): return choose(n,k) * p ** k * (1-p) ** (n-k)
def prob_lt(n,k,p): return sum([prob(n,i,p) for i in range(p)])
SIGS = 30
ACTUALSIGS = 10
POWRETURN = 0.03
POSRETURN = 0.01
# Expected number of signatures on a block
def ev(pos):
return SIGS * pos
# Chance you have at least k sigs
def at_least_k(pos, k):
return sum([prob(SIGS, i, pos) for i in range(k, SIGS + 1)])
# Expected number of signatures on a block filtering all <k
def ev_atleast_k(pos, k):
total, subprob = 0, 0
for i in range(k, SIGS + 1):
p = prob(SIGS, i, pos)
subprob += p
total += i * p
return total / subprob
def normal_mining_return(pow, pos):
return pow * POWRETURN + ev(pos) * POSRETURN / ACTUALSIGS
def attack_mining_return(pow, pos, k):
powtotal, postotal, subprob = 0, 0, 0
# Case 1: mined PoW block, PoS at least k instances
case_1_prob = pow * at_least_k(pos, k)
subprob += case_1_prob
postotal += case_1_prob * ev_atleast_k(pos, k) * POSRETURN / ACTUALSIGS
powtotal += case_1_prob * POWRETURN
# Case 2: mined PoW block, PoS less than k: discard
# Case 3: others mined PoW block
subprob += (1 - pow)
postotal += (1 - pow) * ev(pos) * POSRETURN / ACTUALSIGS
return powtotal / subprob + postotal / subprob