import pandas as pd
import numpy as np
The number of validators considered is 491,875 (As on 23rd Dec’22) and the corresponding base reward per increment is 510.
num_validators = 491875
base_reward_factor = 64 #constant
base_reward_ = (10** 9 * base_reward_factor) / np.sqrt(32*10**9 * num_validators)
print(base_reward_)
base_reward = base_reward_
Ws=14
Wt=26
Wh=14
Wsum = 64
Basis of Permissionless node operators - bottom performers
Performance Metrics
Rsth = (Ws+Wt+Wh)/Wsum # percent reward for correct source + target + head
Rnst = (-Ws-Wt)/Wsum # penality for wrong source + target
Rsnt = (Ws-Wt)/Wsum #Penality for correct source, wrong target
Rstnh = (Ws+Wt)/Wsum # Not included due to lack of data on Permissionless node operators - bottom performers
k= (Rsth*base_reward)/(10**9)
k1=(Rnst*base_reward)/(10** 9)
k2 =(Rsnt*base_reward)/(10** 9)
DVT significantly improves operator uptime:
DVT technology makes a validator more efficient by improving uptime to 94% when compared to a stand-alone validator with an uptime of 89.2%, given the same level of validator performance.
In a DVT-network, for a validator to perform duties (correctly & timely), atleast 3 out 4 nodes will need to correctly perform duties ie, we will need to calculate:
Probability of atleast 3 nodes performing duties correctly = probability of all 4 nodes performing duties correctly + probability of 3 out of 4 nodes performing duties correctly
It is assumed that the period of poor performance that attracts penalties and the inactivity leak occursconsecutively in the first set of epochs, and this repeats for 6 cycles of 30 days each.
The implications are as follows:
Correlation Penalty Calculation: To calculate correlation penalty we need to get the sum of effective balances of slashed validators in the past 36 days. In order to do so we need the probability of slash of a validator in a 36 day window.
Probability of a validator being slashed in a 36 day window
def thirty_day_cycle_no_inactivity(b,k1,k2,k3,b_eff):
b_new=b
b_eff=b_eff
inactivity_score=0
attest_penality=0
k=k1
for i in range(1,6751):
if i <519:
k =k1
inactivity_score =inactivity_score+4
inactivity_score =inactivity_score-16
elif i <727:
k =k2
inactivity_score =inactivity_score+4
inactivity_score =inactivity_score-16
else :
k = k3
inactivity_score =inactivity_score-1
inactivity_score =inactivity_score-16
inactivity_score =max(inactivity_score,0)
attest_penality =k*b_eff
inactivity_penality = (inactivity_score*b_eff)/(4*16777216)
b_new = b_new + attest_penality -inactivity_penality
if b_new -b_eff >=1.25:
b_eff =b_eff+1
b_eff =min(b_eff,32)
if b_eff-b_new>=0.25:
b_eff =b_eff-1
b_eff =max(b_eff,0)
b_eff =min(b_eff,32)
b_prev =b_new
return b_new, b_eff
# Simualtion for Permissionless node operators - Bottom Performers with no inactivity leak
b_new=32
b_eff=32
for i in range(1,7):
b_new,b_eff =thirty_day_cycle_no_inactivity(b_new,k1,k2,k,b_eff)
print(b_new, i*30,'days')
Result:
32.077049457807114 30 days
32.15409891561533 60 days
32.231148373423544 90 days
32.30819783123176 120 days
32.385247289039974 150 days
32.46229674684819 180 days
def thirty_day_cycle_ssv(b,k1,k2,b_eff):
b_new=b
b_eff =b_eff
inactivity_score=0
attest_penality=0
k=k1
for i in range(1,6751):
if i <406:
k =k1
inactivity_score =inactivity_score+4
inactivity_score =inactivity_score-16
else :
k = k2
inactivity_score =inactivity_score-1
inactivity_score =inactivity_score-16
inactivity_score =max(inactivity_score,0)
attest_penality =k*b_eff
inactivity_penality = (inactivity_score*b_eff)/(4*16777216)
b_new = b_new + attest_penality -inactivity_penality
if b_new -b_eff >=1.25:
b_eff =b_eff+1
b_eff =min(b_eff,32)
if b_eff-b_new>=0.25:
b_eff =b_eff-1
b_eff =max(b_eff,0)
b_eff =min(b_eff,32)
b_prev =b_new
return b_new,b_eff
b_new=32
b_eff=32
for i in range(1,7):
b_new,b_eff =thirty_day_cycle_ssv(b_new,k1,k,b_eff)
print(b_new, i*30,'days')
Result:
32.08326024401464 30 days
32.16652048803072 60 days
32.2497807320468 90 days
32.33304097606288 120 days
32.416301220078964 150 days
32.499561464095045 180 days
def thirty_day_cycle_with_inactivity(b,k1,k2,k3,b_eff):
b_new=b
b_eff =b
inactivity_score=0
attest_penality=0
k=k1
for i in range(1,6751):
if i <519 :
k =k1
inactivity_score =inactivity_score+4
elif i <727:
k =k2
inactivity_score =inactivity_score+4
elif i <=1575 :
k = 0
inactivity_score =inactivity_score-1
else:
k=k3
inactivity_score=inactivity_score-17 # 16 from no inactivity and 1 from being active in epoch participation so total 17
inactivity_score =max(inactivity_score,0)
attest_penality =k*b_eff
inactivity_penality = (inactivity_score*b_eff)/(4*16777216)
b_new = b_new + attest_penality -inactivity_penality
if b_new -b_eff >=1.25:
b_eff =b_eff+1
b_eff =min(b_eff,32)
if b_eff-b_new>=0.25:
b_eff =b_eff-1
b_eff =max(b_eff,0)
b_eff =min(b_eff,32)
return b_new, b_eff
# Permissionless node operators - Bottom Performers with inactivity leak for 7 days
b_new=32
b_eff=32
for i in range(1,7):
if i==1:
b_new,b_eff =thirty_day_cycle_with_inactivity(b_new,k1,k2,k,b_eff)
else:
b_new,b_eff=thirty_day_cycle_no_inactivity(b_new,k1,k2,k,b_eff)
print(b_new, i*30,'days')
Result:
30.545535629329766 30 days
30.617769496015566 60 days
30.690003362701365 90 days
30.762237229387164 120 days
30.834471096072964 150 days
30.906704962758763 180 days
def thirty_day_cycle_ssv_inactivity(b,k1,k2,b_eff):
b_new=b
b_eff=b_eff
inactivity_score=0
attest_penality=0
k=k1
for i in range(1,6751):
if i <406: #worst case
k =k1
inactivity_score =inactivity_score+4
elif i <1575:#1575 ->7 days
k = 0 # during inactivity leak you do not receive rewards
inactivity_score =inactivity_score-1
else:
k = k2 #good case
inactivity_score =inactivity_score-17
inactivity_score =max(inactivity_score,0)
attest_penality =k*b_eff
inactivity_penality = (inactivity_score*b_eff)/(4*16777216)
b_new = b_new + attest_penality -inactivity_penality
if b_new -b_eff >=1.25:
b_eff =b_eff+1
b_eff =min(b_eff,32)
if b_eff-b_new>=0.25:
b_eff =b_eff-1
b_eff =max(b_eff,0)
b_eff =min(b_eff,32)
b_prev =b_new
return b_new,b_eff
b_new=32
b_eff=32
for i in range(1,7):
if i==1:
b_new,b_eff =thirty_day_cycle_ssv_inactivity(b_new,k1,k,b_eff)
else:
b_new,b_eff=thirty_day_cycle_ssv(b_new,k1,k,b_eff)
print(b_new, i*30,'days')
Result:
31.34376643054227 30 days
31.424424791915733 60 days
31.505083153289196 90 days
31.585741514662658 120 days
31.66639987603612 150 days
31.747058237409583 180 days
b_new=32
b_eff=32
for i in range(1,7):
b_new,b_eff =thirty_day_cycle_ssv(b_new,k1,k,b_eff)
print(b_new, i*30,'days')
Result:
32.08326024401464 30 days
32.16652048803072 60 days
32.2497807320468 90 days
32.33304097606288 120 days
32.416301220078964 150 days
32.499561464095045 180 days
def slashing_thirty_day_cycle_no_inactivity(b,k1,epoch,b_eff):
b_new=b
b_eff=b_eff
attest_penality=0
k=k1
initial_slash=1
corr_penality_sbyt=0.00004133173837
for i in range(1,epoch):
if i==1:
b_new=b_new-initial_slash
if i==4050:
corr_penality =3*corr_penality_sbyt*b_eff
b_new =b_new-corr_penality
k =k1
attest_penality =k*b_eff
b_new = b_new + attest_penality
if b_new -b_eff >=1.25:
b_eff =b_eff+1
b_eff =min(b_eff,32)
if b_eff-b_new>=0.25:
b_eff =b_eff-1
b_eff =max(b_eff,0)
b_eff =min(b_eff,32)
b_prev =b_new
return b_new,b_eff
#30 day
slashing_thirty_day_cycle_no_inactivity(32,k1,30*225,32)
Result:
(30.929450774080273, 31)
#36 day (180 day)
slashing_thirty_day_cycle_no_inactivity(32,k1,36*225,32)
Result:
(30.916107786256845, 31)
def slashing_with_inactivity(b,epoch,k1,b_eff):
b_new=b
b_eff =b_eff
inactivity_score=0
attest_penality=0
k=k1
initial_slash=1
corr_penality_sbyt=0.00004133173837
for i in range(1,epoch):
# inactivity leak lasts for 7 days-our assumption
if i==1:
b_new=b_new-initial_slash
if i==4050:
corr_penality =3*corr_penality_sbyt*b_eff
print(corr_penality,'-',corr_penality_sbyt,'-',b_eff)
b_new =b_new-corr_penality
if i <1575 :
k=k1
inactivity_score =inactivity_score+4
else:
k=k1
inactivity_score =inactivity_score+4
inactivity_score=inactivity_score-16
inactivity_score =max(inactivity_score,0)
attest_penality =k*b_eff
inactivity_penality = (inactivity_score*b_eff)/(4*16777216)
b_new = b_new + attest_penality -inactivity_penality
if b_new -b_eff >=1.25:
b_eff =b_eff+1
b_eff =min(b_eff,32)
if b_eff-b_new>=0.25:
b_eff =b_eff-1
b_eff =max(b_eff,0)
b_eff =min(b_eff,32)
b_prev =b_new
return b_new,b_eff
slashing_with_inactivity(32,30*225,k1,32)
Result:
(28.05433309931474, 28)
slashing_with_inactivity(32,36*225,k1,32)
Result:
(28.042281368379925, 28)
def slashing_thirty_day_cycle_no_inactivity(b,k1,epoch,b_eff):
b_new=b
b_eff=b_eff
inactivity_score=0
attest_penality=0
k=k1
initial_slash=1
corr_penality_sbyt=0.00004133173837 ->Normal Slash
corr_penality_sbyt=0.0009689140088817117 #->Staked.us
for i in range(1,epoch):
if i==1:
b_new=b_new-initial_slash
if i==4050:
corr_penality =3*corr_penality_sbyt*b_eff
b_new =b_new-corr_penality
k =k1
inactivity_score =max(inactivity_score,0)
attest_penality =k*b_eff
inactivity_penality = (inactivity_score*b_new)/(4*50331648)
b_new = b_new + attest_penality
print(i,'-',inactivity_score,'-', round(inactivity_penality,10),'-', round(attest_penality,10), round(b_new,5))
if b_new -b_eff >=1.25:
b_eff =b_eff+1
b_eff =min(b_eff,32)
if b_eff-b_new>=0.25:
b_eff =b_eff-1
b_eff =max(b_eff,0)
print(i,'-',b_prev,'-',b_new,'-',b_prev-b_new,'-',b_eff,'-',i,'-',inactivity_score)
b_eff =min(b_eff,32)
b_prev =b_new
return b_new,b_eff
slashing_thirty_day_cycle_no_inactivity(32,k1,36*225,32)
(30.829842635099254, 31)
def slashing_with_inactivity(b,epoch,k1,b_eff):
b_new=b
b_eff =b_eff
inactivity_score=0
attest_penality=0
k=k1
initial_slash=1
corr_penality_sbyt=0.00004133173837 ->normal slash
corr_penality_sbyt=0.0009689140088817117 #staked.us
for i in range(1,epoch):
inactivity leak lasts for 7 days-our assumption
if i==1:
b_new=b_new-initial_slash
if i==4050:
corr_penality =3*corr_penality_sbyt*b_eff
print(corr_penality,'-',corr_penality_sbyt,'-',b_eff)
b_new =b_new-corr_penality
if i <1575 :
k=k1
inactivity_score =inactivity_score+4
else:
k=k1
inactivity_score =inactivity_score+4
inactivity_score=inactivity_score-16
inactivity_score =max(inactivity_score,0)
attest_penality =k*b_eff
inactivity_penality = (inactivity_score*b_eff)/(4*16777216)
b_new = b_new + attest_penality -inactivity_penality
if b_new -b_eff >=1.25:
b_eff =b_eff+1
b_eff =min(b_eff,32)
if b_eff-b_new>=0.25:
b_eff =b_eff-1
b_eff =max(b_eff,0)
b_eff =min(b_eff,32)
b_prev =b_new
print(i,'-',inactivity_score,'-',b_new,'-',b_eff)
return b_new,b_eff
slashing_with_inactivity(32,30*225,k1,32)
(27.976416188591756, 28)
slashing_with_inactivity(32,36*225,k1,32)
(27.96436445765694, 28)