Quellcode durchsuchen

[research/lotterysim] simulate transaction fee, and tipless mechanism, with controlled pid

ertosns vor 3 Jahren
Ursprung
Commit
35405831e3

+ 10 - 0
script/research/lotterysim/core/constants.py

@@ -38,6 +38,16 @@ TARGET_APR = Num(0.12)
 PRIMARY_REWARD_TARGET = 0.35 # staked ratio
 PRIMARY_REWARD_TARGET = 0.35 # staked ratio
 # secondary controller assumes certain frequency of leaders per slot
 # secondary controller assumes certain frequency of leaders per slot
 SECONDARY_LEAD_TARGET = 1 #number of lead per slot
 SECONDARY_LEAD_TARGET = 1 #number of lead per slot
+# maximum transaction size
+MAX_BLOCK_SIZE = 1000
+# maximum transaction computational cost
+MAX_BLOCK_CC = 100
+# fee controller computational capacity target
+FEE_TARGET = MAX_BLOCK_CC
+# max fee base value
+FEE_MAX = 1
+# min fee base value
+FEE_MIN = 0.0001
 # negligible value added to denominator to avoid invalid division by zero
 # negligible value added to denominator to avoid invalid division by zero
 EPSILON = 1
 EPSILON = 1
 # window of accuracy calculation
 # window of accuracy calculation

+ 35 - 1
script/research/lotterysim/core/darkie.py

@@ -36,7 +36,7 @@ class Darkie():
     """
     """
     def apr_scaled_to_runningtime(self):
     def apr_scaled_to_runningtime(self):
         initial_stake = self.vesting_wrapped_initial_stake()
         initial_stake = self.vesting_wrapped_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())
+        #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_scaled = Num(self.stake - initial_stake) / Num(initial_stake) if initial_stake>0 else 0
         apr_scaled = Num(self.stake - initial_stake) / Num(initial_stake) if initial_stake>0 else 0
         if self.slot <= HEADSTART_AIRDROP:
         if self.slot <= HEADSTART_AIRDROP:
             # during this phase, it's only called at end of epoch
             # during this phase, it's only called at end of epoch
@@ -160,3 +160,37 @@ class Darkie():
             buf+='\r\n'
             buf+='\r\n'
             buf += 'apr: {}'.format(self.apr_scaled_to_runningtime())
             buf += 'apr: {}'.format(self.apr_scaled_to_runningtime())
             f.write(buf)
             f.write(buf)
+
+    """
+    anonymous contract assumed to be random stream from uniform distribution,
+    naive emulation of smart contract based transactions with certain computational cost.
+
+    @returns: transaction emulated as series of random floats between 0,1
+    """
+    def tx(self):
+        return Tx(random.randint(0, MAX_BLOCK_SIZE))
+
+    """
+    deduct tip paid to miner plus burned base fee or computational cost.
+    """
+    def pay_fee(self, fee):
+        self.stake -= fee
+
+class Tx(object):
+    def __init__(self, size):
+        self.tx = [random.random() for _ in range(size)]
+        self.len = size
+
+    """
+    anonymous contract assumed to be of random streams from uniform distribution,
+    it's circuit execution cost it thus random.
+    naive emulation of transaction smart contract computational cost (aka tip) as a avg of txs sum,
+    which is random function
+
+    @returns: transaction computational cost
+    """
+    def cc(self):
+        return sum(self.tx) if len(self.tx)>0 else 0
+
+    def __len__(self):
+        return len(self.tx)

+ 64 - 4
script/research/lotterysim/core/lottery.py

@@ -8,7 +8,7 @@ from tqdm import tqdm
 import random
 import random
 
 
 class DarkfiTable:
 class DarkfiTable:
-    def __init__(self, airdrop, running_time, controller_type=CONTROLLER_TYPE_DISCRETE, kp=0, ki=0, kd=0, dt=1, kc=0, ti=0, td=0, ts=0, debug=False, r_kp=0, r_ki=0, r_kd=0):
+    def __init__(self, airdrop, running_time, controller_type=CONTROLLER_TYPE_DISCRETE, kp=0, ki=0, kd=0, dt=1, kc=0, ti=0, td=0, ts=0, debug=False, r_kp=0, r_ki=0, r_kd=0, fee_kp=0, fee_ki=0, fee_kd=0):
         self.Sigma=airdrop
         self.Sigma=airdrop
         self.darkies = []
         self.darkies = []
         self.running_time=running_time
         self.running_time=running_time
@@ -18,9 +18,11 @@ class DarkfiTable:
         print('secondary min/max : {}/{}'.format(self.secondary_pid.clip_min, self.secondary_pid.clip_max))
         print('secondary min/max : {}/{}'.format(self.secondary_pid.clip_min, self.secondary_pid.clip_max))
         self.primary_pid = PrimaryDiscretePID(kp=r_kp, ki=r_ki, kd=r_kd) if controller_type==CONTROLLER_TYPE_DISCRETE else PrimaryTakahashiPID(kc=kc, ti=ti, td=td, ts=ts)
         self.primary_pid = PrimaryDiscretePID(kp=r_kp, ki=r_ki, kd=r_kd) if controller_type==CONTROLLER_TYPE_DISCRETE else PrimaryTakahashiPID(kc=kc, ti=ti, td=td, ts=ts)
         print('primary min/max : {}/{}'.format(self.primary_pid.clip_min, self.primary_pid.clip_max))
         print('primary min/max : {}/{}'.format(self.primary_pid.clip_min, self.primary_pid.clip_max))
+        self.basefee_pid = FeePID(kp=fee_kp, ki=fee_ki, kd=fee_kd) if controller_type==CONTROLLER_TYPE_DISCRETE else SecondaryTakahashiPID(kc=fee_kc, ti=fee_ti, td=fee_td, ts=fee_ts)
         self.debug=debug
         self.debug=debug
         self.rewards = []
         self.rewards = []
         self.winners = []
         self.winners = []
+        self.computational_cost = [0]
 
 
     def add_darkie(self, darkie):
     def add_darkie(self, darkie):
         self.darkies+=[darkie]
         self.darkies+=[darkie]
@@ -67,6 +69,7 @@ class DarkfiTable:
                 winners += self.darkies[i].won_hist[-1]
                 winners += self.darkies[i].won_hist[-1]
             self.winners +=[winners]
             self.winners +=[winners]
             feedback = winners
             feedback = winners
+            darkie_lead_idx = -1
             ################
             ################
             # resolve fork #
             # resolve fork #
             ################
             ################
@@ -78,7 +81,33 @@ class DarkfiTable:
                             print('stakeholder {} slashed'.format(i))
                             print('stakeholder {} slashed'.format(i))
                         else:
                         else:
                             self.darkies[i].update_stake(self.rewards[-1])
                             self.darkies[i].update_stake(self.rewards[-1])
+                            darkie_lead_idx=i
                         break
                         break
+                ###############
+                # tip auction #
+                ###############
+                if darkie_lead_idx>=0:
+                    txs = []
+                    for darkie in self.darkies:
+                        txs += [darkie.tx()]
+                    ret, actual_cc = DarkfiTable.auction(txs)
+                    tips = ret[0]
+                    idxs = ret[1]
+                    basefee = self.basefee_pid.pid_clipped(self.computational_cost[-1], debug)
+                    assert basefee<=1
+                    for idx in idxs:
+                        fee = txs[idx].cc()+basefee
+                        self.darkies[idx].pay_fee(fee)
+                        #print("charging darkie[{}]: {} DRK per tx of length: {}, burning: {}".format(idx, fee, len(txs[idx]), basefee))
+                    self.darkies[darkie_lead_idx].pay_fee(-1*tips)
+                    #print('reward miner: {} DRK'.format(tips))
+                    self.computational_cost += [actual_cc]
+                    # subtract base fee from total stake
+                    self.Sigma -= basefee*len(txs)
+                ###################
+                # end tip auction #
+                ###################
+
                 # resolve finalization
                 # resolve finalization
                 self.Sigma += self.rewards[-1]
                 self.Sigma += self.rewards[-1]
                 # resync nodes
                 # resync nodes
@@ -100,9 +129,10 @@ class DarkfiTable:
                         if self.darkies[darkie_idx].won_hist[resync_slot_id]:
                         if self.darkies[darkie_idx].won_hist[resync_slot_id]:
                             darkie_winning_idx = darkie_idx
                             darkie_winning_idx = darkie_idx
                             break
                             break
-                    if self.darkie_winning_idx>=0:
-                        self.darkies[darkie_winning_idx].resync_stake(resync_reward)
-                        self.Sigma += resync_reward
+                    self.darkies[darkie_winning_idx].resync_stake(resync_reward)
+                    self.Sigma += resync_reward
+                    if darkie_winning_idx>=0:
+                        pass
                     else:
                     else:
                         # single lead got slashed
                         # single lead got slashed
                         pass
                         pass
@@ -153,3 +183,33 @@ class DarkfiTable:
         with open('log/rewards.log', 'w+') as f:
         with open('log/rewards.log', 'w+') as f:
             buff = ','.join([str(i) for i in self.rewards])
             buff = ','.join([str(i) for i in self.rewards])
             f.write(buff)
             f.write(buff)
+
+    """
+    tip auction
+
+    @return total tip for miner, and list of indices of darkies included.
+    """
+    def auction(txs):
+        #print("len(txs): {}".format(len(txs)))
+        W = MAX_BLOCK_CC
+        n = len(txs)
+        K = [[[0,[]] for x in range(W + 1)] for x in range(n + 1)]
+        for i in range(n + 1):
+            for w in range(W + 1):
+                if i == 0 or w == 0:
+                    K[i][w] = [0,[]]
+                elif len(txs[i-1]) <= w:
+                    if txs[i-1].cc() + K[i-1][w-len(txs[i-1])][0] > K[i-1][w][0]:
+                        K[i][w] = [txs[i-1].cc() + K[i-1][w-len(txs[i-1])][0], K[i-1][w-len(txs[i-1])][1] + [i-1]]
+                    else:
+                        K[i][w] = K[i-1][w]
+                else:
+                    K[i][w] = K[i-1][w]
+        tip = K[n][W][0]
+        actual_cc = W
+        for w in reversed(range(W+1)):
+            if K[n][w][0] == tip:
+                actual_cc = w
+            else:
+                break
+        return K[n][W], actual_cc

BIN
script/research/lotterysim/img/apr_distribution.png


BIN
script/research/lotterysim/img/feedback_history_processed.png


BIN
script/research/lotterysim/img/output_history_processed.png


+ 5 - 0
script/research/lotterysim/pid/cascade.py

@@ -31,3 +31,8 @@ class SecondaryDiscretePID(LeadPID):
 class SecondaryTakahashiPID(LeadPID):
 class SecondaryTakahashiPID(LeadPID):
     def __init__(self, kc, ti, td, ts):
     def __init__(self, kc, ti, td, ts):
         LeadPID.__init__(self, CONTROLLER_TYPE_TAKAHASHI, Kc=kc, Ti=ti, Td=td, Ts=ts)
         LeadPID.__init__(self, CONTROLLER_TYPE_TAKAHASHI, Kc=kc, Ti=ti, Td=td, Ts=ts)
+
+
+class FeePID(BasePID):
+    def __init__(self, kp=0, ki=0, kd=0, dt=1, Kc=0, Ti=0, Td=0, Ts=0, debug=False):
+        BasePID.__init__(self, MAX_BLOCK_SIZE, FEE_MIN, FEE_MAX,  CONTROLLER_TYPE_DISCRETE, kp=kp, ki=ki, kd=kd, dt=dt, Kc=Kc, Ti=Ti, Td=Td, Ts=Ts, debug=debug)