Просмотр исходного кода

[research/lotterysim] document lotterysim/core

ertosns 3 лет назад
Родитель
Сommit
85c53aa7b0

+ 29 - 18
script/research/lotterysim/core/constants.py

@@ -1,45 +1,56 @@
 from decimal import Decimal as Num
 
+# number approximation terms
 N_TERM = 2
+# analogue controller enum
 CONTROLLER_TYPE_ANALOGUE=-1
+# discrete controller enum
 CONTROLLER_TYPE_DISCRETE=0
+# takahashi controller enum
 CONTROLLER_TYPE_TAKAHASHI=1
+# initial distribution of tokens (random value for sake of experimentation)
 ERC20DRK=2.1*10**7
-
+# group base/order
 L = 28948022309329048855892746252171976963363056481941560715954676764349967630337.0
-
+# secondary finalization controller minimal clipped value
 F_MIN = 0.0001
+# secondary finalization controller maximal clipped value
 F_MAX = 0.9999
-
+# primary reward controller minimal clipped value
 REWARD_MIN = 1
+# primary reward controller maximal clipped value
 REWARD_MAX = 1000
-
+# slot length in seconds
 SLOT = 90
+# epoch length in slots
+EPOCH_LENGTH = Num(10)
+# one month in slots
 ONE_MONTH = Num(60*60*24*30/SLOT)
+# one year in slots
 ONE_YEAR = Num(365.25*24*60*60/SLOT)
-ONE_MONTH = int(30*24*60*60/SLOT)
+# vesting issuance period
 VESTING_PERIOD = ONE_MONTH
-
+# stakeholder assumes  APR target
 TARGET_APR = Num(0.12)
-
+# primary controller assumes accuracy target
 PRIMARY_REWARD_TARGET = 0.35 # staked ratio
+# secondary controller assumes certain frequency of leaders per slot
 SECONDARY_LEAD_TARGET = 1 #number of lead per slot
-
+# negligible value added to denominator to avoid invalid division by zero
 EPSILON = 1
-EPOCH_LENGTH = Num(10)
-
+# window of accuracy calculation
+ACC_WINDOW = int(EPOCH_LENGTH)
+# headstart value
+BASE_L = 0.0001*L
+# headstart airdrop period
+HEADSTART_AIRDROP = 288
+# threshold of randomly slashing stakeholder
+SLASHING_RATIO = 0.0001
+# decimal high precision.
 L_HP = Num(L)
 F_MIN_HP = Num(F_MIN)
 F_MAX_HP = Num(F_MAX)
 EPSILON_HP = Num(EPSILON)
 REWARD_MIN_HP = Num(REWARD_MIN)
 REWARD_MAX_HP = Num(REWARD_MAX)
-
-ACC_WINDOW = 100
-
-BASE_L = 0.0001*L
 BASE_L_HP = Num(BASE_L)
-
-# HEADSTART AIRDROP period
-HEADSTART_AIRDROP = 288
-SLASHING_RATIO = 0.0001

+ 64 - 40
script/research/lotterysim/core/darkie.py

@@ -17,32 +17,70 @@ class Darkie():
     def clone(self):
         return Darkie(self.stake)
 
+    """
+    calculate APY (with compound interest every epoch) every epoch scaled to runningtime
+    @param rewards: rewards at each epoch
+    @returns: apy
+    """
     def apy_scaled_to_runningtime(self, rewards):
-        '''
         avg_apy = 0
         for idx, reward in enumerate(rewards):
             #init_stake = Num(self.initial_stake[idx-1]) if len(self.initial_stake)>=idx else Num(self.initial_stake[-1])
             current_epoch_staked_tokens = Num(self.strategy.staked_tokens_ratio[idx-1]) * Num(self.initial_stake[idx-1])
             avg_apy += (Num(reward) / current_epoch_staked_tokens) if current_epoch_staked_tokens!=0 else 0
         return avg_apy * Num(ONE_YEAR/(self.slot/EPOCH_LENGTH)) if self.slot  and self.initial_stake[0]>0 >0 else 0
-        '''
-        return -1
-
-    def vesting_wrapped_initial_stake(self):
-        #print('initial stake: {}, corresponding vesting: {}'.format(self.initial_stake[0], self.vesting[int((self.slot)/VESTING_PERIOD)]))
-        # note index is previous slot since update_vesting is called after background execution.
-        #returns  vesting stake plus initial stake gained from zero coin headstart during aridrop period
-        #return (self.current_vesting() if self.slot>0 else self.initial_stake[-1]) + self.initial_stake[-1]
-        vesting = self.current_vesting()
-        return vesting if vesting > 0 else  self.initial_stake[0]
 
+    """
+    calculate APR every epoch scaled to running time
+    @returns: apr
+    """
     def apr_scaled_to_runningtime(self):
         initial_stake = self.vesting_wrapped_initial_stake()
-        #print('stake: {}, initial_stake: {}'.format(self.stake, initial_stake))
         assert self.stake >= initial_stake, 'stake: {}, initial_stake: {}, slot: {}, current: {}, previous: {} vesting'.format(self.stake, initial_stake, self.slot, self.current_vesting(), self.prev_vesting())
         apr = Num(self.stake - initial_stake) / Num(initial_stake) *  Num(ONE_YEAR/(self.slot)) if initial_stake > 0 and self.slot>0 else 0
         return apr
 
+
+    """
+    add vesting to initial stake
+    @returns: vesting plus initial stake
+    """
+    def vesting_wrapped_initial_stake(self):
+        #returns  vesting stake plus initial stake gained from zero coin headstart during aridrop period
+        vesting = self.current_vesting()
+        #return vesting if vesting > 0 else  self.initial_stake[0]
+        return vesting +  self.initial_stake[0]
+
+    """
+    update stake with vesting return every scheduled vesting period
+    """
+    def update_vesting(self):
+        self.stake += self.vesting_differential()
+
+    """
+    @returns: current epoch vesting
+    """
+    def current_vesting(self):
+        '''
+        current corresponding slot vesting
+        '''
+        vesting_idx = int(self.slot/VESTING_PERIOD)
+        return self.vesting[vesting_idx] if vesting_idx < len(self.vesting) else 0
+
+    """
+    @returns: previous epoch vesting
+    """
+    def prev_vesting(self):
+        '''
+        previous corresponding slot vesting
+        '''
+        prev_vesting_idx = int((self.slot-1)/VESTING_PERIOD)
+        return (self.vesting[prev_vesting_idx] if self.slot>0 else self.current_vesting()) if prev_vesting_idx < len(self.vesting) else  0
+
+    def vesting_differential(self):
+        vesting_value =  self.current_vesting() - self.prev_vesting()
+        return vesting_value
+
     def staked_tokens(self):
         '''
         the ratio of the staked tokens during the epochs
@@ -50,10 +88,11 @@ class Darkie():
         '''
         return Num(self.initial_stake[0])*self.staked_tokens_ratio()
 
-
+    """
+    @returns: average stakeholder's staked ratio from genesis until current slot
+    """
     def staked_tokens_ratio(self):
         staked_ratio = Num(sum(self.strategy.staked_tokens_ratio)/len(self.strategy.staked_tokens_ratio))
-        #print('type: {}, ratio: {}'.format(self.strategy.type, staked_ratio))
         assert staked_ratio <= 1 and staked_ratio >=0, 'staked_ratio: {}'.format(staked_ratio)
         return staked_ratio
 
@@ -64,6 +103,10 @@ class Darkie():
         self.f = (Num(f) if hp else f)
         self.slot = count
 
+    """
+    @param hp: high precision decimal option
+    play lottery if stakeholder won, update state
+    """
     def run(self, hp=True):
         k=N_TERM
         def target(tune_parameter, stake):
@@ -83,36 +126,17 @@ class Darkie():
         won = lottery(T, hp)
         self.won_hist += [won]
 
-    def update_vesting(self):
-        self.stake += self.vesting_differential()
-
-    def current_vesting(self):
-        '''
-        current corresponding slot vesting
-        '''
-        vesting_idx = int(self.slot/VESTING_PERIOD)
-        return self.vesting[vesting_idx] if vesting_idx < len(self.vesting) else 0
-
-    def prev_vesting(self):
-        '''
-        previous corresponding slot vesting
-        '''
-        prev_vesting_idx = int((self.slot-1)/VESTING_PERIOD)
-        return (self.vesting[prev_vesting_idx] if self.slot>0 else self.current_vesting()) if prev_vesting_idx < len(self.vesting) else  0
-
-    def vesting_differential(self):
-        vesting_value =  self.current_vesting() - self.prev_vesting()
-        return vesting_value
-
+    """
+    update stake upon winning lottery with single lead
+    """
     def update_stake(self, reward):
         if self.won_hist[-1]:
-            self.stake+=reward
-            #print('updating stake, stake: {}, last: {}'.format(self.stake, self.initial_stake[-1]))
+            self.stake += reward
 
+    """
+    update stake after fork finalization
+    """
     def resync_stake(self, reward):
-        '''
-        add resync stake
-        '''
         self.stake += reward
 
 

+ 38 - 10
script/research/lotterysim/core/lottery.py

@@ -25,6 +25,15 @@ class DarkfiTable:
     def add_darkie(self, darkie):
         self.darkies+=[darkie]
 
+    """
+    for every slot under given running time, set f based off prior on-chain public \
+    values, set sigmas, f, update vesting, stake for every stakeholder, resolve \
+    forks.
+    @param rand_running_time: randomization running time state
+    @param debug: debug option
+    @param hp: high precision option
+    @returns: acc, avg_apy, avg_reward, stake_ratio, avg_apr
+    """
     def background(self, rand_running_time=True, debug=False, hp=True):
         self.debug=debug
         self.start_time=time.time()
@@ -34,17 +43,16 @@ class DarkfiTable:
         self.running_time = rand_running_time
         rt_range = tqdm(np.arange(0,self.running_time, 1))
         merge_length = 0
+        # loop through slots
         for count in rt_range:
             merge_length = 0
-            winners=0
+            # calculate probability of winning owning 100% of stake
             f = self.secondary_pid.pid_clipped(float(feedback), debug)
-
+            # calculate reward value every epoch
             if count%EPOCH_LENGTH == 0:
                 acc = self.secondary_pid.acc()
                 reward = self.primary_pid.pid_clipped(acc, debug)
                 self.rewards += [reward]
-
-
             #note! thread overhead is 10X slower than sequential node execution!
             total_stake = 0
             for i in range(len(self.darkies)):
@@ -52,16 +60,20 @@ class DarkfiTable:
                 self.darkies[i].update_vesting()
                 self.darkies[i].run(hp)
                 total_stake += self.darkies[i].stake
-
+            # count number of leads per slot
+            winners=0
+            # count secondary controller feedback
             for i in range(len(self.darkies)):
                 winners += self.darkies[i].won_hist[-1]
-
             self.winners +=[winners]
             feedback = winners
+            ################
+            # resolve fork #
+            ################
             if self.winners[-1]==1:
                 for i in range(len(self.darkies)):
                     if self.darkies[i].won_hist[-1]:
-                        if random.random() < SLASHING_RATIO:
+                        if random.random() < len(self.darkies)**-1:
                             self.darkies.remove(self.darkies[i])
                             print('stakeholder {} slashed'.format(i))
                         else:
@@ -90,27 +102,43 @@ class DarkfiTable:
                             break
                     self.darkies[darkie_winning_idx].resync_stake(resync_reward)
                     self.Sigma += resync_reward
+            #################
+            # fork resolved #
+            #################
             rt_range.set_description('epoch: {}, fork: {} issuance {} DRK, acc: {}%, stake = {}%, sr: {}%, reward:{}, apr: {}%'.format(int(count/EPOCH_LENGTH), merge_length, round(self.Sigma,2), round(acc*100, 2), round(total_stake/self.Sigma*100 if self.Sigma>0 else 0,2), round(self.avg_stake_ratio()*100,2) , round(self.rewards[-1],2), round(self.avg_apr()*100,2)))
-            #print('[2]stake: {}, sigma: {}, reward: {}'.format(total_stake, self.Sigma, self.rewards[-1]))
-            #assert(round(total_stake,1) <= round(self.Sigma,1))
+            assert round(total_stake,1) <= round(self.Sigma,1), 'stake: {}, sigma: {}'.format(total_stake, self.Sigma)
             count+=1
         self.end_time=time.time()
         avg_reward = sum(self.rewards)/len(self.rewards)
         stake_ratio = self.avg_stake_ratio()
         avg_apy = self.avg_apy()
         avg_apr = self.avg_apr()
-        #print('apy: {}, staked_ratio: {}'.format(avg_apy, stake_ratio))
         return self.secondary_pid.acc_percentage(), avg_apy, avg_reward, stake_ratio, avg_apr
 
+    """
+    average APY (with compound interest added every epoch) ,
+    scaled to running time for all nodes
+    @returns: average APY for all nodes
+    """
     def avg_apy(self):
         return Num(sum([darkie.apy_scaled_to_runningtime(self.rewards) for darkie in self.darkies])/len(self.darkies))
 
+    """
+    average APR scaled to running time for all nodes
+    @returns: average APR for all nodes
+    """
     def avg_apr(self):
         return Num(sum([darkie.apr_scaled_to_runningtime() for darkie in self.darkies])/len(self.darkies))
 
+    """
+    returns: average stake ratio for all nodes
+    """
     def avg_stake_ratio(self):
         return sum([darkie.staked_tokens_ratio() for darkie in self.darkies]) / len(self.darkies)
 
+    """
+    write lottery reward log
+    """
     def write(self):
         elapsed=self.end_time-self.start_time
         for id, darkie in enumerate(self.darkies):

+ 7 - 4
script/research/lotterysim/core/utils.py

@@ -13,10 +13,13 @@ def fact(n, hp=False):
         return Num(2) if hp else 2
     else:
         return (Num(n) if hp else n)* fact(n-1, hp)
-
-# all inputs to this function are integers
-# sigmas are public
-# stake is private
+"""
+approximate ouroboros phi function
+all inputs to this function are integers
+@param sigmas: n sigmas of n-term approximation of phi target function
+@param stake: stakeholder stake
+@returns: target value T
+"""
 def approx_target_in_zk(sigmas, stake):
     # both sigma_1, sigma_2 are constants, if f is a constant.
     # if f is constant then sigma_12, sigma_2