44 lines
1.4 KiB
Python
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
|