Browse Source

[research/ouroboros] dynamic staking

mohab 4 years ago
parent
commit
112f93389a

+ 11 - 14
script/research/dpos/ouroboros/beacon.py

@@ -24,6 +24,8 @@ class TrustedBeacon(SynchedNTPClock, threading.Thread):
         self.log = Logger(self, genesis_time)
         self.log = Logger(self, genesis_time)
         self.log.info(f"constructed for node {str(node)}")
         self.log.info(f"constructed for node {str(node)}")
         self.bb=0 # epoch counts since genesis (big bang)
         self.bb=0 # epoch counts since genesis (big bang)
+        self.sigmas = []
+        self.proofs = []
 
 
     def __repr__(self):
     def __repr__(self):
         return f"trustedbeacon"
         return f"trustedbeacon"
@@ -48,24 +50,19 @@ class TrustedBeacon(SynchedNTPClock, threading.Thread):
                 # new nodes attached to the network, need to either request old blocks, or wait for next epoch's broadcst
                 # new nodes attached to the network, need to either request old blocks, or wait for next epoch's broadcst
                 # it's temporarily, and or simplicity set to the latter 
                 # it's temporarily, and or simplicity set to the latter 
                 return
                 return
-            self.log.info(f"callback: new slot of idx: {self.current_slot}")
-            #y, pi = self.vrf.sign(self.current_slot)
-            self.log.info(f"callbaxck: signature calculated for {str(self.node)}")
-            self.node.new_slot(self.current_slot)
+            self.node.new_slot(self.current_slot, self.sigmas[self.current_slot%self.epoch_length], self.proofs[self.current_slot%self.epoch_length])
         else:
         else:
-            self.bb+=1
-            sigmas = []
-            proofs = []
             #TODO since it's expensive, but to generate single (y,pi) pair as seed 
             #TODO since it's expensive, but to generate single (y,pi) pair as seed 
             # and use random hash function to generate the rest randomly. 
             # and use random hash function to generate the rest randomly. 
             if self.node.am_current_leader:
             if self.node.am_current_leader:
+                self.bb+=1
+                self.sigmas = []
+                self.proofs = []
                 for i in range(self.epoch_length):
                 for i in range(self.epoch_length):
-                    self.log.info(f"callback: new slot of idx: {self.current_slot}, epoch slot {i}")
-                    y, pi = self.vrf.sign(self.current_slot)
-                    self.log.info(f"callback: signature calculated for {str(self.node)}")
-                    sigmas.append(y)
-                    proofs.append(pi)
-            self.node.new_epoch(self.current_slot, sigmas, proofs)
+                    y, pi = self.vrf.sign(self.current_slot+i)
+                    self.sigmas.append(y)
+                    self.proofs.append(pi)
+                self.node.new_epoch(self.current_slot, self.sigmas, self.proofs)
 
 
     def verify(self, y, pi, pk_raw, g):
     def verify(self, y, pi, pk_raw, g):
-        return VRF.verify(self.current_slot, y, pi, pk_raw, g)
+        return VRF.verify(self.current_slot, y, pi, pk_raw, g)

+ 12 - 3
script/research/dpos/ouroboros/block.py

@@ -28,6 +28,8 @@ class Block(object):
         self.is_genesis=genesis
         self.is_genesis=genesis
         self.endorsed=False
         self.endorsed=False
         self.log = Logger(genesis_time)
         self.log = Logger(genesis_time)
+        self.leader_id=None
+        self.endorser_id=None
 
 
     def __repr__(self):
     def __repr__(self):
         if self.is_genesis:
         if self.is_genesis:
@@ -59,6 +61,12 @@ class Block(object):
     def set_endorsed(self):
     def set_endorsed(self):
         self.endorsed=True
         self.endorsed=True
 
 
+    def set_endorser(self, id):
+        self.endorser_id=id
+        self.set_endorsed()
+
+    def set_leader(self, id):
+        self.leader_id=id
     
     
     @property
     @property
     def data(self):
     def data(self):
@@ -83,9 +91,10 @@ class GensisBlock(Block):
     '''
     '''
     def __init__(self, previous_block, data, slot_uid, genesis_time=time.time()):
     def __init__(self, previous_block, data, slot_uid, genesis_time=time.time()):
         # stakeholders is list of tuple (pk_i, s_i) for the ith stakeholder
         # stakeholders is list of tuple (pk_i, s_i) for the ith stakeholder
-        self.stakeholders = data[STAKEHOLDERS]
-        self.distribution = data[STAKEHOLDERS_DISTRIBUTIONS]
-        self.seed = data.get(SEED, '') #needed for pvss
+        dist_block = data[0]
+        self.stakeholders = dist_block[STAKEHOLDERS]
+        self.distribution = dist_block[STAKEHOLDERS_DISTRIBUTIONS]
+        self.seed = dist_block[SEED] #needed for pvss
         shd_buff = ''
         shd_buff = ''
         for shd in self.distribution:
         for shd in self.distribution:
             shd_buff +=str(shd)
             shd_buff +=str(shd)

+ 24 - 6
script/research/dpos/ouroboros/blockchain.py

@@ -1,3 +1,4 @@
+import math
 from ouroboros.logger import Logger
 from ouroboros.logger import Logger
 
 
 '''
 '''
@@ -9,6 +10,7 @@ class Blockchain(object):
         self.blocks = []
         self.blocks = []
         self.log = Logger(self, genesis_time)
         self.log = Logger(self, genesis_time)
         self.R = R # how many slots in single epoch
         self.R = R # how many slots in single epoch
+        self.epochs = []
     
     
     @property
     @property
     def epoch_length(self):
     def epoch_length(self):
@@ -16,19 +18,29 @@ class Blockchain(object):
 
 
     def __repr__(self):
     def __repr__(self):
         buff=''
         buff=''
-        for i in range(len(self.blocks)):
-            buff+=str(self.blocks[i])
+        for e in range(len(self.epochs)):
+            for b in range(len(e)):
+                buff+=str(b)+'\n'
         return buff
         return buff
-    
+
+    '''
+    @return: epoch reference
+    '''
     def __getitem__(self, i):
     def __getitem__(self, i):
-        return self.blocks[i]
+        e_idx = math.floor(i/self.epoch_length)
+        e_blk_idx = i%self.epoch_length
+        return self.epochs[e_idx][e_blk_idx]
 
 
+    '''
+    @return: number of blocks
+    '''
     def __len__(self):
     def __len__(self):
-        return len(self.blocks)
+        return len(self.epochs*self.epoch_length)
 
 
+    '''
     def __add_block(self, block):
     def __add_block(self, block):
         self.blocks.append(block)
         self.blocks.append(block)
-
+    
     def add_epoch(self, epoch):
     def add_epoch(self, epoch):
         assert epoch!=None, 'epoch cant be None'
         assert epoch!=None, 'epoch cant be None'
         assert len(epoch)>0 , 'epoch cant be zero-sized'
         assert len(epoch)>0 , 'epoch cant be zero-sized'
@@ -37,3 +49,9 @@ class Blockchain(object):
                 self.__add_block(block)
                 self.__add_block(block)
             else:
             else:
                 self.log.warn(f"an empty block at index of index: {block.index},\nrelative slot:{idx}\nabsolute slot: {self.length*idx+block.slot}")
                 self.log.warn(f"an empty block at index of index: {block.index},\nrelative slot:{idx}\nabsolute slot: {self.length*idx+block.slot}")
+    '''
+    def append(self, epoch):
+        assert epoch!=None, 'epoch cant be None'
+        assert len(epoch)>0 , 'epoch cant be zero-sized'
+        assert len(epoch)==self.epoch_length
+        self.append(epoch)

+ 107 - 0
script/research/dpos/ouroboros/data.py

@@ -0,0 +1,107 @@
+import time
+from ouroboros.logger import Logger
+
+'''
+\class Item is the basic item in the block data
+'''
+class Item(object):
+    def __init__(self, data, fee=1):
+        self.data = data
+        self.fee = fee
+        self.log = Logger(self)
+    
+    '''
+    coffee reward for the miner
+    '''
+    @property
+    def coffee(self):
+        return self.fee
+
+class GenesisItem(Item):
+    def __init__(self, dict_data):
+        self.fee=0
+        Item.__init__(self, dict_data)
+    
+    def __getitem__(self, key):
+        return self.data.get(key, '')
+
+#TODO implement
+class StateTransition(Item):
+    def __init__(self, balance):
+        self.balance = balance
+
+#TODO implement
+class TransitionProcessor(object):
+    def __init__(self):
+        pass
+
+'''
+\class Transaction coin exchange between two entities
+'''
+class Transaction(Item):
+    def __init__(self, sndr_addr, rcvr_addr, amnt, fee=1, lock_time=time.time()):
+        self.sndr_addr = sndr_addr
+        self.rcvr_addr = rcvr_addr
+        self.amnt = amnt
+        self.lock_time = lock_time
+        self.stamp = time.time()
+        fee = fee
+        Item.__init__(self, str(self), fee)
+        self.log.info(str(self))
+
+    def __repr__(self):
+        return f'sender: {self.sndr_addr}, receiver: {self.rcvr_addr}, amount: {self.amnt}, self.lock time: {self.lock_time}'
+
+class CoinBase(Item):
+    def __init__(self):
+        pass
+
+'''
+\class Data is the whole data stored in a single block, 
+consist of list of Items
+'''
+class Data(list):
+
+    def __init__(self, txs=[]):
+        self.txs = txs        
+    '''
+    Pall, is the accumulated transactions fee/gas/coffee for a block 
+    '''
+    @property
+    def coffee(self):
+        pall = 0
+        for item in self.txs:
+            pall += item.coffee
+        return pall
+
+    def __repr__(self):
+        buff = ''
+        for item in self.txs:
+            buff += str(item) + '\n'
+        return buff
+
+    def __len__(self):
+        return len(self.txs)
+
+    def __iter__(self):
+        self.n=0
+        return self
+
+    def __next__(self):
+        item  = None
+        if self.n <= self.length:
+            try:
+                item = self.txs[self.n]
+                self.n+=1
+                return item
+            except IndexError:
+                raise StopIteration
+    
+    def append(self, item):
+        self.txs.append(item)
+
+    def __getitem__(self, i):
+        L = len(self)
+        if i >= L or i < 0:
+            return None
+        return self.txs[i]

+ 63 - 14
script/research/dpos/ouroboros/environment.py

@@ -4,9 +4,12 @@ import random
 import time
 import time
 from ouroboros.logger import Logger
 from ouroboros.logger import Logger
 from ouroboros.consts import *
 from ouroboros.consts import *
+from ouroboros.data import Item, GenesisItem
+from ouroboros import utils
 
 
 '''
 '''
-\class Z is the environment
+\class Z is the environment,
+environment is ought to interfece with the network
 '''
 '''
 class Z(object):
 class Z(object):
     def __init__(self, stakeholdes, epoch_length, genesis_time=time.time()):
     def __init__(self, stakeholdes, epoch_length, genesis_time=time.time()):
@@ -21,22 +24,60 @@ class Z(object):
         self.log.info("Z initialized")
         self.log.info("Z initialized")
         self.current_blk_endorser_sig=None
         self.current_blk_endorser_sig=None
         self.epoch_inited=False
         self.epoch_inited=False
+        self.cached_dist = []
+        self.beta = 0.5 # endorser weight
+        #
+        self.l=0
+        #a transaction is declared stable if and only if it is in a block that,
+        # is more than k blocks deep in the ledger.
+        self.k = self.epoch_length/2 - self.l -1 
     
     
+    @property
+    def endorser_len(self):
+        #TODO (impl)
+        pass
+
     def __repr__(self):
     def __repr__(self):
-        buff= f"envirnment of {self.length} stakholders\tcurrent leader's id: {self.current_leader_id}\tepoch_slot: {self.epoch_slot}\tendorser_id: {self.current_endorser_id}"
+        buff= f"envirnment of {self.length} stakholders\tcurrent leader's id: {self.current_leader_id}\tepoch_slot: {self.epoch_slot}\tendorser_id: {self.current_endorser_idx}"
         for sh in self.stakeholders:
         for sh in self.stakeholders:
             buff+=str(sh)+"\n"
             buff+=str(sh)+"\n"
         return buff
         return buff
+   
+    '''
+    issue a coinbase for claimed reward 2(k+l) after the block
+    '''
+    def issue_coinbase(self):
+        #TODO
+        pass
+
+    '''
+    returns true if before time, false otherwise
+    '''
+    @property
+    def iceage(self):
+        return self.block_id==0
+
+    @property
+    def previous_epoch_stake_distribution(self):
+        if self.iceage:
+            return self.get_epoch_distribution()
+        else:
+            return self.cached_dist
+
+    def get_epoch_distribution(self):
+        stakes = [node.stake for node in self.stakeholders]
+        return stakes
 
 
     '''
     '''
         return genesis data of the current epoch
         return genesis data of the current epoch
     '''
     '''
     def get_genesis_data(self):
     def get_genesis_data(self):
         #TODO implement dynaming staking
         #TODO implement dynaming staking
+        distribution = self.get_epoch_distribution()
         genesis_data = {STAKEHOLDERS: self.stakeholders,
         genesis_data = {STAKEHOLDERS: self.stakeholders,
-        STAKEHOLDERS_DISTRIBUTIONS:[],
+        STAKEHOLDERS_DISTRIBUTIONS: distribution,
             SEED: ''}
             SEED: ''}
-        return genesis_data
+        return GenesisItem(genesis_data)
     
     
     @property
     @property
     def epoch_slot(self):
     def epoch_slot(self):
@@ -52,13 +93,16 @@ class Z(object):
         return self.stakeholders[self.current_leader_id]
         return self.stakeholders[self.current_leader_id]
 
 
     @property
     @property
-    def current_endorser_id(self):
+    def current_endorser_idx(self):
         return self.current_epoch_endorsers[self.epoch_slot]
         return self.current_epoch_endorsers[self.epoch_slot]
 
 
+    def current_endorser_id(self):
+        return self.current_endorser.id
+
     @property
     @property
     def current_endorser(self):
     def current_endorser(self):
         self.log.info(f"getting endorser of id: {self.current_leader_id}")
         self.log.info(f"getting endorser of id: {self.current_leader_id}")
-        return self.stakeholders[self.current_endorser_id]
+        return self.stakeholders[self.current_endorser_idx]
 
 
     @property
     @property
     def current_leader_vrf_pk(self):
     def current_leader_vrf_pk(self):
@@ -74,7 +118,7 @@ class Z(object):
     
     
     @property
     @property
     def current_endorser_sig_pk(self):
     def current_endorser_sig_pk(self):
-        return self.stakeholders[self.current_endorser_id].sig_pk
+        return self.stakeholders[self.current_endorser_idx].sig_pk
 
 
     def endorser(self, epoch_slot):
     def endorser(self, epoch_slot):
         assert epoch_slot >= 0 and epoch_slot < self.epoch_length
         assert epoch_slot >= 0 and epoch_slot < self.epoch_length
@@ -124,6 +168,10 @@ class Z(object):
     def honest(self):
     def honest(self):
         return len(self.stakeholders[self.adversary_mask])
         return len(self.stakeholders[self.adversary_mask])
 
 
+    @property
+    def random(self):
+        return utils.weighted_random(self.previous_epoch_stake_distribution)
+
     def select_epoch_leaders(self, sigmas, proofs):
     def select_epoch_leaders(self, sigmas, proofs):
         assert len(sigmas)==self.epoch_length and len(proofs)==self.epoch_length, self.log.error(f"size mismatch between sigmas: {len(sigmas)}, proofs: {len(proofs)}, and epoch_length: {self.epoch_length}")
         assert len(sigmas)==self.epoch_length and len(proofs)==self.epoch_length, self.log.error(f"size mismatch between sigmas: {len(sigmas)}, proofs: {len(proofs)}, and epoch_length: {self.epoch_length}")
         for i in range(self.epoch_length):
         for i in range(self.epoch_length):
@@ -136,15 +184,15 @@ class Z(object):
                 return y_hypotenuse2
                 return y_hypotenuse2
             seed = leader_selection_hash(sigma)
             seed = leader_selection_hash(sigma)
             random.seed(seed)
             random.seed(seed)
-            leader_idx=seed%self.length
-            endorser_idx=random.randint(0,self.length-1)
+            leader_idx=self.random
+            endorser_idx=self.random
             # only select an honest leaders
             # only select an honest leaders
             while leader_idx==endorser_idx or not self.adversary_mask[leader_idx] or not self.adversary_mask[endorser_idx]:
             while leader_idx==endorser_idx or not self.adversary_mask[leader_idx] or not self.adversary_mask[endorser_idx]:
-                leader_idx=random.randint(0,self.length-1)
-                endorser_idx=random.randint(0,self.length-1)
-
+                leader_idx=self.random
+                endorser_idx=self.random
             #TODO select the following leader for this epoch, note, 
             #TODO select the following leader for this epoch, note, 
             # under a single condition that no one is able to predict who is next
             # under a single condition that no one is able to predict who is next
+            assert not leader_idx==endorser_idx
             self.current_epoch_leaders[i]=leader_idx
             self.current_epoch_leaders[i]=leader_idx
             self.current_epoch_endorsers[i]=endorser_idx
             self.current_epoch_endorsers[i]=endorser_idx
         return self.current_epoch_leaders, self.current_epoch_endorsers
         return self.current_epoch_leaders, self.current_epoch_endorsers
@@ -155,11 +203,12 @@ class Z(object):
         current_leader = self.stakeholders[self.current_leader_id]
         current_leader = self.stakeholders[self.current_leader_id]
         assert current_leader is not None, "current leader cant be None"
         assert current_leader is not None, "current leader cant be None"
         self.log.highlight('selecting epochs leaders, and ensorsers ---->')
         self.log.highlight('selecting epochs leaders, and ensorsers ---->')
-        self.stakeholders[self.current_epoch_endorsers[self.current_endorser_id]].set_endorser()
+        self.stakeholders[self.current_epoch_endorsers[self.current_endorser_idx]].set_endorser()
         self.stakeholders[self.current_epoch_leaders[self.current_leader_id]].set_leader()
         self.stakeholders[self.current_epoch_leaders[self.current_leader_id]].set_leader()
         self.log.highlight('selected epochs leaders, and ensorsers <----')
         self.log.highlight('selected epochs leaders, and ensorsers <----')
         
         
     def new_epoch(self, slot, sigmas, proofs):
     def new_epoch(self, slot, sigmas, proofs):
+        self.cached_dist = self.get_epoch_distribution()
         self.epoch_inited=True
         self.epoch_inited=True
         self.current_slot=slot
         self.current_slot=slot
         leaders, endorsers = self.select_epoch_leaders(sigmas, proofs)
         leaders, endorsers = self.select_epoch_leaders(sigmas, proofs)
@@ -208,7 +257,7 @@ class Z(object):
         self.corrupt(self.current_leader_id)
         self.corrupt(self.current_leader_id)
 
 
     def corrupt_endorse(self):
     def corrupt_endorse(self):
-        self.corrupt(self.current_endorser_id)
+        self.corrupt(self.current_endorser_idx)
 
 
     def corrupt_blk(self):
     def corrupt_blk(self):
         self.log.warn(f"<corrupt_blk> at slot: {self.current_slot}")
         self.log.warn(f"<corrupt_blk> at slot: {self.current_slot}")

+ 10 - 4
script/research/dpos/ouroboros/epoch.py

@@ -12,7 +12,6 @@ class Epoch(list):
         self.blocks = []
         self.blocks = []
         self.R = R #maximum epoch legnth, and it's a fixed property of the system
         self.R = R #maximum epoch legnth, and it's a fixed property of the system
         self.e = epoch_idx
         self.e = epoch_idx
-        self.n=0
         self.log = Logger(genesis_time)
         self.log = Logger(genesis_time)
 
 
     @property
     @property
@@ -33,6 +32,13 @@ class Epoch(list):
             return None
             return None
         return self.blocks[0]
         return self.blocks[0]
 
 
+    @property
+    def coffee(self):
+        epoch_fee = 0
+        for blk in self.blocks:
+            epoch_fee += blk.data.coffee
+        return epoch_fee
+
     def __len__(self):
     def __len__(self):
         return self.length
         return self.length
 
 
@@ -53,10 +59,10 @@ class Epoch(list):
     
     
     def __next__(self):
     def __next__(self):
         blk=None
         blk=None
-        for i in range(self.length):
+        if self.n <= self.length:
             try:
             try:
                 blk=self.blocks[self.n]
                 blk=self.blocks[self.n]
+                self.n+=1
+                return blk
             except IndexError:
             except IndexError:
                 raise StopIteration
                 raise StopIteration
-            self.n+=1
-            return blk

+ 13 - 0
script/research/dpos/ouroboros/hashtree.py

@@ -0,0 +1,13 @@
+from ouroboros.data import Data
+
+'''
+\class HashTree
+'''
+class HashTree(object):
+
+    def __init__(self, data):
+        self.data = data
+
+    def root(self):
+        #TODO implement
+        return self.data

+ 72 - 35
script/research/dpos/ouroboros/stakeholder.py

@@ -8,6 +8,7 @@ from ouroboros.vrf import verify, VRF
 from ouroboros.utils import *
 from ouroboros.utils import *
 from ouroboros.logger import Logger
 from ouroboros.logger import Logger
 from ouroboros.consts import *
 from ouroboros.consts import *
+from ouroboros.data import Data, Transaction, Item
 
 
 '''
 '''
 \class Stakeholder
 \class Stakeholder
@@ -16,7 +17,7 @@ class Stakeholder(object):
     def __init__(self, epoch_length, passwd='password'):
     def __init__(self, epoch_length, passwd='password'):
         #TODO (fix) remove redundant variables reley on environment
         #TODO (fix) remove redundant variables reley on environment
         self.passwd=passwd
         self.passwd=passwd
-        self.stake=0
+        self.stake=1
         self.epoch_length=epoch_length
         self.epoch_length=epoch_length
         self.vrf = VRF(self.passwd)
         self.vrf = VRF(self.passwd)
         #verification keys
         #verification keys
@@ -29,14 +30,25 @@ class Stakeholder(object):
         self.sig_pk = sig_pk
         self.sig_pk = sig_pk
         #
         #
         self.current_block = None
         self.current_block = None
-        self.uncommited_tx=''
-        self.tx=''
         self.current_epoch = None
         self.current_epoch = None
         self.am_current_leader=False
         self.am_current_leader=False
         self.am_current_endorser=False
         self.am_current_endorser=False
         self.am_corrupt=False
         self.am_corrupt=False
         #
         #
         self.blockchain=None
         self.blockchain=None
+        #
+        self.data = Data()
+        #verifiable fingerprint for a stakeholder taking advantage of public sig, vrf
+
+        self.id = sign_message(self.passwd, self.sig_sk, str(self.vrf_pk))
+
+    def receive_tx(self, tx):
+        #TODO validate trx
+        self.data.append(tx)
+
+    def broadcast_tx(self, tx):
+        self.data.append(tx)
+        self.env.broadcast_tx(tx)
 
 
     @property
     @property
     def is_leader(self):
     def is_leader(self):
@@ -53,11 +65,11 @@ class Stakeholder(object):
     def __repr__(self):
     def __repr__(self):
         buff=''
         buff=''
         if self.am_current_leader:
         if self.am_current_leader:
-            buff = f"\tleader {(hash(self.passwd))} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
+            buff = f"\tleader {self.id} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
         elif self.am_current_endorser:
         elif self.am_current_endorser:
-            buff = f"\tendorser {(hash(self.passwd))} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
+            buff = f"\tendorser {self.id} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
         else:
         else:
-            buff = f"\thonest committee memeber {(hash(self.passwd))} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
+            buff = f"\thonest committee memeber {self.id} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
         return buff
         return buff
 
 
     def __call__(self, env):
     def __call__(self, env):
@@ -67,10 +79,9 @@ class Stakeholder(object):
         self.beacon = TrustedBeacon(self,  self.vrf, self.epoch_length, self.env.genesis_time)
         self.beacon = TrustedBeacon(self,  self.vrf, self.epoch_length, self.env.genesis_time)
         self.current_slot_uid = self.beacon.slot
         self.current_slot_uid = self.beacon.slot
 
 
-
     def start(self):
     def start(self):
         self.log.info("thread [started]")
         self.log.info("thread [started]")
-        self.beacon.start()
+        self.beacon.start()  
         self.log.info("thread [ended]")
         self.log.info("thread [ended]")
 
 
     @property
     @property
@@ -80,13 +91,19 @@ class Stakeholder(object):
     def __gen_genesis_epoch(self):
     def __gen_genesis_epoch(self):
         '''
         '''
         '''
         '''
-        self.tx = self.env.get_genesis_data()
-        self.tx[TX]=self.uncommited_tx
-        self.uncommited_tx=''
-        self.current_block=GensisBlock(self.current_block, self.tx, self.current_slot_uid, self.env.genesis_time)
+        tx_item = self.env.get_genesis_data()
+        self.data.append(tx_item)
+        self.current_block=GensisBlock(self.current_block, self.data, self.current_slot_uid, self.env.genesis_time)
         assert self.current_block is not None
         assert self.current_block is not None
         self.current_epoch=Epoch(self.current_block, self.epoch_length, self.epoch_index, self.env.genesis_time)
         self.current_epoch=Epoch(self.current_block, self.epoch_length, self.epoch_index, self.env.genesis_time)
-   
+
+    def end_slot(self):
+        # start new transactions 
+        self.data = Data()
+
+    def add_epoch(self):
+        self.blockchain.append(self.current_epoch)
+        self.update_stake()
     '''
     '''
     it's a callback function, and called by the diffuser
     it's a callback function, and called by the diffuser
     '''
     '''
@@ -100,38 +117,34 @@ class Stakeholder(object):
         if self.am_current_leader:
         if self.am_current_leader:
             self.env.new_epoch(slot, sigmas, proofs)
             self.env.new_epoch(slot, sigmas, proofs)
         self.current_slot_uid = slot
         self.current_slot_uid = slot
-        #kickoff gensis block
         # add old epoch to the ledger
         # add old epoch to the ledger
         if self.current_slot_uid > 1 and self.current_epoch!=None and len(self.current_epoch)>0:
         if self.current_slot_uid > 1 and self.current_epoch!=None and len(self.current_epoch)>0:
-            self.blockchain.add_epoch(self.current_epoch)  
-        #if leader, you need to broadcast the block
+            self.add_epoch()
         while not self.env.epoch_inited:
         while not self.env.epoch_inited:
             self.log.info("pending epoch initialization")
             self.log.info("pending epoch initialization")
             time.sleep(1)
             time.sleep(1)
         self.__gen_genesis_epoch()
         self.__gen_genesis_epoch()
-        if self.am_current_leader:
-            self.broadcast_block()
-            self.end_leadership()
-        elif self.am_current_endorser:
-            self.endorse_block()
-            self.end_endorsing()
+        self.new_slot(self.current_slot_uid, sigmas[0], proofs[0])
+
+    def terminate_slot(self):
+        pass
 
 
     '''
     '''
     it's a callback function, and called by the diffuser
     it's a callback function, and called by the diffuser
     '''
     '''
-    #def new_slot(self, slot, sigma, proof):
-    def new_slot(self, slot):
+    def new_slot(self, slot, sigma, proof):
         '''
         '''
         #TODO implement praos
         #TODO implement praos
         for this implementation we assume synchrony,
         for this implementation we assume synchrony,
         and at this point, and no delay is considered (for simplicity)
         and at this point, and no delay is considered (for simplicity)
         '''
         '''
         self.log.highlight("<new_slot> start")
         self.log.highlight("<new_slot> start")
+        self.terminate_slot()
         self.env.new_slot(slot)
         self.env.new_slot(slot)
-        '''
+
         vrf_pk = self.env.current_leader_vrf_pk
         vrf_pk = self.env.current_leader_vrf_pk
         vrf_g = self.env.current_leader_vrf_g
         vrf_g = self.env.current_leader_vrf_g
-        if not verify(slot, sigma, proof, vrf_pk,vrf_g) :
+        if not verify(slot, sigma, proof, vrf_pk,vrf_g):
             #TODO the leader is corrupted, action to be taken against the corrupt stakeholder
             #TODO the leader is corrupted, action to be taken against the corrupt stakeholder
             #in this case this slot is empty
             #in this case this slot is empty
             self.log.warn(f"<new_slot> leader verification fails")
             self.log.warn(f"<new_slot> leader verification fails")
@@ -140,14 +153,13 @@ class Stakeholder(object):
                 self.__gen_genesis_epoch()
                 self.__gen_genesis_epoch()
             self.current_epoch.add_block(self.current_block)
             self.current_epoch.add_block(self.current_block)
             return
             return
-        '''
-        if self.current_epoch==None:
-            self.log.warn(f"<new_slot> current_epoch is None!")
-            self.__gen_genesis_epoch()
+
         self.current_slot_uid = slot
         self.current_slot_uid = slot
-        prev_blk = self.blockchain[-1] if len(self.blockchain)>0 else EmptyBlock(self.env.genesis_time)
-        self.current_block=Block(prev_blk, self.tx, self.current_slot_uid, self.env.genesis_time)
-        self.current_epoch.add_block(self.current_block)
+        if self.current_slot_uid%self.epoch_length!=0:
+            prev_blk = self.blockchain[-1] if len(self.blockchain)>0 else EmptyBlock(self.env.genesis_time)
+            self.current_block=Block(prev_blk, self.data, self.current_slot_uid, self.env.genesis_time)
+            self.current_epoch.add_block(self.current_block)
+
         if self.am_current_leader:
         if self.am_current_leader:
             self.log.highlight(f"{str(self)} is broadcasting block")
             self.log.highlight(f"{str(self)} is broadcasting block")
             self.broadcast_block()
             self.broadcast_block()
@@ -157,6 +169,20 @@ class Stakeholder(object):
             self.endorse_block()
             self.endorse_block()
             self.end_endorsing()
             self.end_endorsing()
 
 
+    def update_stake(self):
+        if len(self.blockchain)==0:
+            return
+        epoch = self.blockchain[-1]
+        pall = epoch.coffee()
+        leader_cnt=0
+        endorser_cnt=0
+        for blk in epoch:
+            if blk.leader_id==self.id:
+                leader_cnt+=1
+            elif blk.endorser_id==self.id:
+                endorser_cnt+=1
+        self.stake += (self.env.beta * (endorser_cnt/self.env.endorser_len) + \
+            (1-self.env.beta) * (leader_cnt/self.env.epoch_length)) * pall
 
 
     def end_leadership(self):
     def end_leadership(self):
         self.log.info(f"stakeholder:{str(self)} ending leadership for slot{self.current_slot_uid}")
         self.log.info(f"stakeholder:{str(self)} ending leadership for slot{self.current_slot_uid}")
@@ -175,7 +201,13 @@ class Stakeholder(object):
     def set_corrupt(self):
     def set_corrupt(self):
         self.am_corrupt=False
         self.am_corrupt=False
 
 
+    '''
+    only leader can broadcast block
+    '''
     def broadcast_block(self):
     def broadcast_block(self):
+        if not self.am_current_leader:
+            return
+        self.current_block.set_leader(self.id)
         self.log.highlight("broadcasting block")
         self.log.highlight("broadcasting block")
         assert self.am_current_leader and self.current_block is not None
         assert self.am_current_leader and self.current_block is not None
         signed_block=None
         signed_block=None
@@ -187,14 +219,17 @@ class Stakeholder(object):
             endorsing_cnt-=1
             endorsing_cnt-=1
         if not self.current_block.endorsed:
         if not self.current_block.endorsed:
             self.log.warn("failure endorsing the block...")
             self.log.warn("failure endorsing the block...")
-        if not self.current_block.endorsed:
             self.current_block = EmptyBlock(self.env.genesis_time)
             self.current_block = EmptyBlock(self.env.genesis_time)
         signed_block = sign_message(self.passwd, self.sig_sk, self.current_block)
         signed_block = sign_message(self.passwd, self.sig_sk, self.current_block)
         self.env.broadcast_block(signed_block, self.current_slot_uid)
         self.env.broadcast_block(signed_block, self.current_slot_uid)
     
     
+    '''
+    only endorser can broadcast block
+    '''
     def endorse_block(self):
     def endorse_block(self):
         if not self.am_current_endorser:
         if not self.am_current_endorser:
             return
             return
+        self.current_block.set_endorser(self.id)
         self.log.info(f"endorsing block for current_leader_id: {self.env.current_leader_id}")
         self.log.info(f"endorsing block for current_leader_id: {self.env.current_leader_id}")
         if not self.am_current_endorser:
         if not self.am_current_endorser:
             self.log.warn("not endorser")
             self.log.warn("not endorser")
@@ -252,11 +287,13 @@ class Stakeholder(object):
         self.log.highlight(f'confirming endorsed has sig_pk: {self.env.current_endorser_sig_pk}')
         self.log.highlight(f'confirming endorsed has sig_pk: {self.env.current_endorser_sig_pk}')
         if verify_signature(self.env.endorser_sig_pk(epoch_slot), cur_blk, endorser_sig):
         if verify_signature(self.env.endorser_sig_pk(epoch_slot), cur_blk, endorser_sig):
             if self.current_slot_uid==self.env.current_slot:
             if self.current_slot_uid==self.env.current_slot:
+                self.current_block.set_endorser(self.current_endorser_id)
                 self.current_block.set_endorsed()
                 self.current_block.set_endorsed()
             else:
             else:
+                self.blockchain[blk_uid].set_endorser(self.current_endorser_id)
                 self.blockchain[blk_uid].set_endorsed()
                 self.blockchain[blk_uid].set_endorsed()
             confirmed=True
             confirmed=True
         else:
         else:
             self.log.warn(f"confirmed enderser signature failure for pk: {str(self.env.current_endorser_sig_pk)} on block {str(cur_blk)}  of signature {str(endorser_sig)}")
             self.log.warn(f"confirmed enderser signature failure for pk: {str(self.env.current_endorser_sig_pk)} on block {str(cur_blk)}  of signature {str(endorser_sig)}")
             confirmed=False
             confirmed=False
-        return confirmed
+        return confirmed

+ 0 - 1
script/research/dpos/ouroboros/utils.py

@@ -3,7 +3,6 @@ from cryptography.hazmat.primitives.asymmetric import rsa, padding
 from cryptography.hazmat.backends import default_backend
 from cryptography.hazmat.backends import default_backend
 from cryptography.exceptions import InvalidSignature
 from cryptography.exceptions import InvalidSignature
 import random
 import random
-import joblib
 import pickle
 import pickle
 
 
 def extended_euclidean_algorithm(a, b):
 def extended_euclidean_algorithm(a, b):

+ 3 - 4
script/research/dpos/simulation.py

@@ -2,11 +2,11 @@ import time
 from ouroboros import Stakeholder
 from ouroboros import Stakeholder
 from ouroboros import Z
 from ouroboros import Z
 
 
-'''
-EPOCH_LENGTH = 3
+
+EPOCH_LENGTH = 2
 stakeholders = []
 stakeholders = []
 
 
-for i in range(3):
+for i in range(2):
     stakeholders.append(Stakeholder(EPOCH_LENGTH, 'passwd'+str(i)))
     stakeholders.append(Stakeholder(EPOCH_LENGTH, 'passwd'+str(i)))
 
 
 stakeholders[0].set_leader()
 stakeholders[0].set_leader()
@@ -16,4 +16,3 @@ environment.start()
 
 
 for sh in environment.stakeholders:
 for sh in environment.stakeholders:
     sh.beacon.join()
     sh.beacon.join()
-'''