소스 검색

[research/ouroboros] made TrustedBeacon centralized in the environment, with single instance

mohab 4 년 전
부모
커밋
50d3673bb0

+ 20 - 40
script/research/dpos/ouroboros/beacon.py

@@ -8,61 +8,41 @@ from ouroboros.logger import Logger
 
 the trusted beacon is decentralized, such that at the onset of the Epoch,
 the leader of the first slot generated the signed seed, and release the signature, 
-proof, and base to the genesis block.
+proof, and base to the ge nesis block.
 
 #TODO implement trustedbeacon as a node
 '''
 class TrustedBeacon(SynchedNTPClock, threading.Thread):
-    def __init__(self, node, vrf, epoch_length, genesis_time):
-        self.epoch_length=epoch_length # how many slots in a a block
-        SynchedNTPClock.__init__(self)
+
+    def __init__(self, epoch_length, genesis_time):
+        SynchedNTPClock.__init__(self, epoch_length)
         threading.Thread.__init__(self)
         self.daemon=True
-        self.node = node #stakeholder
-        self.vrf = vrf
         self.current_slot = self.slot
         self.log = Logger(self, genesis_time)
-        self.log.info(f"constructed for node {str(node)}")
         self.bb=0 # epoch counts since genesis (big bang)
-        self.sigmas = []
-        self.proofs = []
+        self.proofs_epoch=-1
 
     def __repr__(self):
         return f"trustedbeacon"
 
     def run(self):
         self.log.highlight("thread [start]")
-        self.__background()
-        self.log.info("thread [end]")
-
-    def __background(self):
-        current_epoch = self.slot
-        self.log.info('background waiting for the onset of next synched epoch...')
+        prev_slot = self.slot
+        self.__callback()
         while True:
-            if self.slot != current_epoch:
-                current_epoch = self.slot
+            if not self.slot == prev_slot:
+                prev_slot = self.slot
                 self.__callback()
-
-    def __callback(self):
-        self.current_slot = self.slot
-        if self.current_slot%self.epoch_length!=0:
-            if self.bb==0:
-                # 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 
-                return
-            self.node.new_slot(self.current_slot, self.sigmas[self.current_slot%self.epoch_length], self.proofs[self.current_slot%self.epoch_length])
-        else:
-            #TODO since it's expensive, but to generate single (y,pi) pair as seed 
-            # and use random hash function to generate the rest randomly. 
-            if self.node.am_current_leader:
-                self.bb+=1
-                self.sigmas = []
-                self.proofs = []
-                for i in range(self.epoch_length):
-                    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 next_epoch_seeds(self, vrf):
+        rands = {}
+        for i in range(self.epoch_length):
+            slot_idx = self.current_slot+i
+            y, pi = vrf.sign(slot_idx)
+            rands[slot_idx] = (y,pi)
+        return rands
+    '''
     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)
+    '''

+ 1 - 4
script/research/dpos/ouroboros/block.py

@@ -33,7 +33,6 @@ class Block(object):
 
     def __repr__(self):
         if self.is_genesis:
-            #return "GensisBlock at {slot:"+str(self.sl)+",data:"+self.tx+",state:"+str(self.state)+"}\n"+decode_gensis_data(self.tx)
             return "GensisBlock at {slot:"+str(self.sl)+",data:"+str(self.tx)+",state:"+str(self.state)+"}\n"+str(self.tx)
         return "Block at {slot:"+str(self.sl)+",data:"+str(self.tx)+",state:"+str(self.state)+"}"
     
@@ -41,11 +40,9 @@ class Block(object):
         if type(self.tx)==str:
             return hash((self.state, self.tx, self.sl))
         elif type(self.tx)==dict:
-            #TODO include distribution
             return hash((self.state, self.tx[SEED], self.tx[TX]))
         else: 
-            #TODO (fix) shouldn't reach here
-            return 0
+            return hash(str(self))
 
     def __eq__(self, block):
         return self.state==block.state and \

+ 17 - 7
script/research/dpos/ouroboros/clock.py

@@ -3,22 +3,24 @@ synchronized NTP clock
 '''
 
 import ntplib
-from time import ctime
+import time
 import math
 
 class SynchedNTPClock(object):
 
-    def __init__(self, slot_length=60, ntp_server='europe.pool.ntp.org'):
+    def __init__(self, epoch_length, slot_length=120, ntp_server='europe.pool.ntp.org'):
         #TODO how long should be the slot length
+        self.epoch_length=epoch_length # how many slots in a a block
         self.slot_length=slot_length
         self.ntp_server = ntp_server
         self.ntp_client = ntplib.NTPClient()
         #TODO validate the server
         # when was darkfi birthday? as seconds since the epoch 
-        self.darkfi_epoch=0
+        self.darkfi_epoch=time.mktime(time.strptime("2022-01-01", "%Y-%m-%d"))
         self.offline_cnt=0
+
     def __repr__(self):
-        return 'darkfi time: '+ ctime(self.darkfi_time) + ', current synched time: ' + ctime(self.synched_time)
+        return 'darkfi time: '+ time.ctime(self.darkfi_time) + ', current synched time: ' + ctime(self.synched_time)
 
     def __get_time_stat(self):
         response=None
@@ -36,8 +38,8 @@ class SynchedNTPClock(object):
     @property
     def synched_time(self):
         state = self.__get_time_stat()
-        synched_time = state.tx_time
-        return synched_time
+        stime = state.tx_time
+        return stime
 
     @property
     def darkfi_time(self):
@@ -50,4 +52,12 @@ class SynchedNTPClock(object):
 
     @property
     def slot(self):
-        return math.floor(self.darkfi_time/self.slot_length)
+        return math.floor(self.offline_time/self.slot_length)
+
+    @property
+    def epoch(self):
+        return math.floor(self.slot/self.epoch_length)
+    
+    @property
+    def epoch_slot(self):
+        return self.slot%self.epoch_length

+ 158 - 47
script/research/dpos/ouroboros/environment.py

@@ -2,26 +2,30 @@ import numpy as np
 import math
 import random
 import time
+from threading import Thread
 from ouroboros.logger import Logger
 from ouroboros.consts import *
-from ouroboros.data import Item, GenesisItem
+from ouroboros.data import GenesisItem, Data
 from ouroboros import utils
+from ouroboros.beacon import TrustedBeacon
+from ouroboros.block import GensisBlock
+from ouroboros.epoch import Epoch
+from ouroboros.stakeholder import Stakeholder
 
 '''
 \class Z is the environment,
 environment is ought to interfece with the network
 '''
 class Z(object):
-    def __init__(self, stakeholdes, epoch_length, genesis_time=time.time()):
+    def __init__(self, stakeholdes,  epoch_length, genesis_time=time.time()):
         self.genesis_time=genesis_time
+        self.beacon = TrustedBeacon(epoch_length, genesis_time)
         self.log = Logger(self, genesis_time)
         self.epoch_length=epoch_length
         self.stakeholders = np.array(stakeholdes)
         self.adversary_mask=np.array([True]*len(stakeholdes))
-        self.current_epoch_leaders=[-1]*self.epoch_length
-        self.current_epoch_endorsers=[-1]*self.epoch_length
+        self.slot_committee = {}
         self.current_slot=0
-        self.log.info("Z initialized")
         self.current_blk_endorser_sig=None
         self.epoch_inited=False
         self.cached_dist = []
@@ -31,14 +35,93 @@ class Z(object):
         #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 
-    
+        self.epoch_initialized = {}
+        #TODO (fix) replace those by query from blockchain genesis block
+        self.rands = {}
+        self.prev_leader_id=-1
+        #
+        self.current_block=None
+        self.init()
+
+
+    def init(self):
+        for sh in self.stakeholders:
+            sh(self)
+        assert len(self.stakeholders) > 2
+        #pick initial leader to be the first stakeholder
+        initial_leader = self.stakeholders[0]
+        #pick initial endorser to be the first endorser
+        initial_endorser = self.stakeholders[1]
+        self.current_epoch = self.beacon.epoch
+        self.rands = self.beacon.next_epoch_seeds(initial_leader.vrf)
+        self.current_slot = self.beacon.slot
+        self.select_epoch_leaders()
+        self.prev_leader_id=0
+        self.signal()
+        #TODO need to assign the block from the last slot in the epoch
+        while True:
+            if not self.beacon.slot == self.current_slot:
+                self.current_slot = self.beacon.slot
+                self.signal()
+        
+    def signal(self):
+        ########################
+        #TODO fix cretical
+        ########################
+        # run the state on a member, no static function for new_epoch, new_slot.
+
+        if self.beacon.epoch_slot!=0:
+            ############
+            # NEW SLOT #
+            ############
+            y, pi = self.rands[self.current_slot]
+            threads = []
+            for sk in self.stakeholders:
+                #TODO (fix) failed to synchronized current_slot 1234 for epoch length of 2 is two states
+                # need to pass the slot with it's corresponding sigma, and proof
+                thread = Thread(target=Stakeholder.new_slot, args=(sk, self.current_slot, y, pi))                
+                #sk.new_slot(self.current_slot, y, pi)
+                threads.append(thread)
+                thread.start()
+            for th in threads:
+                th.join()
+        else:
+            #############
+            # NEW EPOCH #
+            #############
+            vrf = self.stakeholders[self.current_leader_id].vrf
+            if self.beacon.epoch != self.current_epoch:
+                self.current_epoch = self.beacon.epoch
+                self.rands = self.beacon.next_epoch_seeds(vrf)
+                self.select_epoch_leaders()
+            for idx, sk in enumerate(self.stakeholders):
+                if sk.id==id:
+                    self.prev_leader_id=idx
+            self.cached_dist = self.get_epoch_distribution()
+            for sk in self.stakeholders:
+                sk.current_slot_uid=self.beacon.slot
+            ###
+            genesis_item = self.get_genesis_data()
+            data = Data()
+            data.append(genesis_item)
+            self.current_block=GensisBlock(self.current_block, data, self.beacon.slot, self.genesis_time)
+            assert self.current_block is not None
+            current_epoch=Epoch(self.current_block, self.epoch_length, self.epoch, self.genesis_time)
+            threads = []
+            for sk in self.stakeholders:
+                #sk.new_epoch(current_epoch)
+                thread = Thread(target=Stakeholder.new_epoch, args=(sk, current_epoch))
+                threads.append(thread)
+                thread.start()
+            for th in threads:
+                th.join()
     @property
     def endorser_len(self):
         #TODO (impl)
         pass
 
     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_idx}"
+        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}"
         for sh in self.stakeholders:
             buff+=str(sh)+"\n"
         return buff
@@ -85,7 +168,7 @@ class Z(object):
 
     @property
     def current_leader_id(self):
-        return self.current_epoch_leaders[self.epoch_slot]
+        return self.slot_committee[self.current_slot][0]
 
     @property
     def current_stakeholder(self):
@@ -93,16 +176,13 @@ class Z(object):
         return self.stakeholders[self.current_leader_id]
 
     @property
-    def current_endorser_idx(self):
-        return self.current_epoch_endorsers[self.epoch_slot]
-
     def current_endorser_id(self):
-        return self.current_endorser.id
+        return self.slot_committee[self.current_slot][1]
 
     @property
     def current_endorser(self):
         self.log.info(f"getting endorser of id: {self.current_leader_id}")
-        return self.stakeholders[self.current_endorser_idx]
+        return self.stakeholders[self.current_endorser_id]
 
     @property
     def current_leader_vrf_pk(self):
@@ -112,13 +192,26 @@ class Z(object):
     def current_leader_vrf_g(self):
         return self.stakeholders[self.current_leader_id].vrf_base
 
+    '''
+    @property
+    def current_epoch_leader(self):
+        return self.stakeholders[self.current_epoch_leaders[0]]
+
+    @property 
+    def current_epoch_leader_vrf_pk(self):
+        return self.current_epoch_leader.vrf_pk
+
+    @property
+    def current_epoch_leader_vrf_g(self):
+        return self.current_epoch_leader.vrf_base
+    '''
     @property
     def current_leader_sig_pk(self):
         return self.stakeholders[self.current_leader_id].sig_pk
     
     @property
     def current_endorser_sig_pk(self):
-        return self.stakeholders[self.current_endorser_idx].sig_pk
+        return self.stakeholders[self.current_endorser_id].sig_pk
 
     def endorser(self, epoch_slot):
         assert epoch_slot >= 0 and epoch_slot < self.epoch_length
@@ -130,6 +223,7 @@ class Z(object):
     def endorser_vrf_pk(self, epoch_slot):
         return self.endorser(epoch_slot).vrf_pk
 
+    #note! assumes epoch_slot lays in the current epoch
     def leader(self, epoch_slot):
         assert epoch_slot >= 0 and epoch_slot < self.epoch_length
         return self.stakeholders[epoch_slot]
@@ -139,7 +233,16 @@ class Z(object):
 
     def leader_vrf_pk(self, epoch_slot):
         return self.leader(epoch_slot).vrf_pk
-        
+    
+    def leader_vrf_g(self, epoch_slot):
+        return self.leader(epoch_slot).vrf_base
+
+    def prev_leader_vrf_pk(self):
+        return self.stakeholders[self.prev_leader_id].vrf_pk
+    
+    def prev_leader_vrf_g(self):
+        return self.stakeholders[self.prev_leader_id].vrf_base
+
     #TODO complete
     def obfuscate_idx(self, i):
         return i
@@ -168,15 +271,33 @@ class Z(object):
     def honest(self):
         return len(self.stakeholders[self.adversary_mask])
 
+    @property
+    def epoch_stake_distribution(self):
+        #stakes = {}
+        ordered_stakes = [] #with the same stakeholders order
+        for sk in self.stakeholders:
+            #stakes[sk.id] = sk.stake
+            ordered_stakes.append(sk.stake)
+        return  ordered_stakes
+
     @property
     def random(self):
-        return utils.weighted_random(self.previous_epoch_stake_distribution)
+        return utils.weighted_random(self.epoch_stake_distribution)
+
+    '''
+    since clocks are synched
+    '''
+    @property
+    def epoch(self):
+        return self.beacon.epoch
 
-    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}")
+    def select_epoch_leaders(self):
+        #assert len(self.sigmas)==self.epoch_length and len(self.proofs)==self.epoch_length, \
+            #self.log.error(f"size mismatch between sigmas: {len(self.sigmas)}, proofs: {len(self.proofs)}, and epoch_length: {self.epoch_length}")
         for i in range(self.epoch_length):
-            self.log.info(f"current sigma of index {i} of total {len(sigmas)}, epoch_length: {self.epoch_length}")
-            sigma = sigmas[i]
+            #self.log.info(f"current sigma of index {i} , epoch_length: {self.epoch_length}")
+            slot_idx = self.current_slot + i
+            sigma, _ = self.rands[slot_idx]
             assert sigma!=None, 'proof cant be None'
             def leader_selection_hash(sigma):
                 Y = np.array(sigma)
@@ -193,38 +314,32 @@ class Z(object):
             #TODO select the following leader for this epoch, note, 
             # 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_endorsers[i]=endorser_idx
-        return self.current_epoch_leaders, self.current_epoch_endorsers
-
-    def new_slot(self, slot):
-        self.current_slot=slot
-        self.log.info(f"stakeholders: {self.stakeholders}")
-        current_leader = self.stakeholders[self.current_leader_id]
-        assert current_leader is not None, "current leader cant be None"
-        self.log.highlight('selecting epochs leaders, and ensorsers ---->')
-        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.log.highlight('selected epochs leaders, and ensorsers <----')
-        
-    def new_epoch(self, slot, sigmas, proofs):
-        self.cached_dist = self.get_epoch_distribution()
-        self.epoch_inited=True
-        self.current_slot=slot
-        leaders, endorsers = self.select_epoch_leaders(sigmas, proofs)
-        return leaders, endorsers
-
-    def broadcast_block(self, signed_block, slot_uid):
+            #TODO move leader/endorser to a dictionary
+
+            self.slot_committee[slot_idx] = (leader_idx, endorser_idx)
+        self.epoch_initialized[str(self.epoch)] = True
+
+    def broadcast_block(self, cur_block, signed_block, slot_uid):
         while self.current_blk_endorser_sig is None:
             self.log.info('pending endorsing...')
             time.sleep(1)
             #wait for it untill it gets endorsed
             pass
+        self.current_block = cur_block
         for stakeholder in self.stakeholders:
             if not stakeholder.is_leader:
                 stakeholder.receive_block(signed_block, self.current_blk_endorser_sig, slot_uid)
         self.print_blockchain()
 
+
+    def is_current_leader(self, id):
+        ldr_idx, _ = self.slot_committee[self.beacon.current_slot]
+        return id == self.stakeholders[ldr_idx].id
+
+    def is_current_endorser(self, id):
+        _, edr_idx = self.slot_committee[self.beacon.current_slot]
+        return id == self.stakeholders[edr_idx].id
+
     @property
     def block_id(self):
         return self.current_slot%self.epoch_length
@@ -239,10 +354,6 @@ class Z(object):
         else:
             self.log.warn("unconfirmed endorsed siganture")
 
-    def start(self):
-        for sh in self.stakeholders:
-            sh(self)
-            sh.start()
 
     def print_blockchain(self):
         for sh in self.stakeholders:
@@ -257,7 +368,7 @@ class Z(object):
         self.corrupt(self.current_leader_id)
 
     def corrupt_endorse(self):
-        self.corrupt(self.current_endorser_idx)
+        self.corrupt(self.current_endorser_id)
 
     def corrupt_blk(self):
         self.log.warn(f"<corrupt_blk> at slot: {self.current_slot}")

+ 24 - 92
script/research/dpos/ouroboros/stakeholder.py

@@ -3,7 +3,6 @@ import time
 from ouroboros.block import Block, GensisBlock, EmptyBlock
 from ouroboros.blockchain import Blockchain
 from ouroboros.epoch import Epoch
-from ouroboros.beacon import TrustedBeacon
 from ouroboros.vrf import verify, VRF
 from ouroboros.utils import *
 from ouroboros.logger import Logger
@@ -31,15 +30,12 @@ class Stakeholder(object):
         #
         self.current_block = None
         self.current_epoch = None
-        self.am_current_leader=False
-        self.am_current_endorser=False
         self.am_corrupt=False
         #
         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):
@@ -50,10 +46,6 @@ class Stakeholder(object):
         self.data.append(tx)
         self.env.broadcast_tx(tx)
 
-    @property
-    def is_leader(self):
-        return self.am_current_leader
-
     @property
     def vrf_pk(self):
         return self.__vrf_pk
@@ -64,9 +56,9 @@ class Stakeholder(object):
     
     def __repr__(self):
         buff=''
-        if self.am_current_leader:
+        if self.env.is_current_leader(self.id):
             buff = f"\tleader {self.id} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
-        elif self.am_current_endorser:
+        elif self.env.is_current_endorser(self.id):
             buff = f"\tendorser {self.id} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
         else:
             buff = f"\thonest committee memeber {self.id} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
@@ -76,27 +68,13 @@ class Stakeholder(object):
         self.env=env
         self.log = Logger(self, self.env.genesis_time)
         self.blockchain = Blockchain(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
-
-    def start(self):
-        self.log.info("thread [started]")
-        self.beacon.start()  
-        self.log.info("thread [ended]")
+        #self.beacon = TrustedBeacon(self,  self.vrf, self.epoch_length, self.env.genesis_time)
+        #self.current_slot_uid = self.beacon.slot
 
     @property
     def epoch_index(self):
         return round(self.current_slot_uid/self.epoch_length)
-
-    def __gen_genesis_epoch(self):
-        '''
-        '''
-        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
-        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()
@@ -104,70 +82,36 @@ class Stakeholder(object):
     def add_epoch(self):
         self.blockchain.append(self.current_epoch)
         self.update_stake()
-    '''
-    it's a callback function, and called by the diffuser
-    '''
-    def new_epoch(self, slot, sigmas, proofs):
-        '''
-        #TODO implement praos
-        for this implementation we assume synchrony,
-        and at this point, and no delay is considered (for simplicity)
-        '''
-        self.log.highlight("<new_epoch> start")
-        if self.am_current_leader:
-            self.env.new_epoch(slot, sigmas, proofs)
-        self.current_slot_uid = slot
-        # add old epoch to the ledger
-        if self.current_slot_uid > 1 and self.current_epoch!=None and len(self.current_epoch)>0:
+    
+    def new_epoch(self, current_epoch):
+        if self.current_epoch!=None:
             self.add_epoch()
-        while not self.env.epoch_inited:
-            self.log.info("pending epoch initialization")
-            time.sleep(1)
-        self.__gen_genesis_epoch()
-        self.new_slot(self.current_slot_uid, sigmas[0], proofs[0])
+        self.current_epoch = current_epoch
 
-    def terminate_slot(self):
-        pass
-
-    '''
-    it's a callback function, and called by the diffuser
-    '''
     def new_slot(self, slot, sigma, proof):
-        '''
-        #TODO implement praos
-        for this implementation we assume synchrony,
-        and at this point, and no delay is considered (for simplicity)
-        '''
         self.log.highlight("<new_slot> start")
-        self.terminate_slot()
-        self.env.new_slot(slot)
-
-        vrf_pk = self.env.current_leader_vrf_pk
-        vrf_g = self.env.current_leader_vrf_g
-        if not verify(slot, sigma, proof, vrf_pk,vrf_g):
+        vrf_pk = self.env.prev_leader_vrf_pk()
+        vrf_g = self.env.prev_leader_vrf_g()
+        self.log.highlight(f"verifying slot leader with pk: {str(vrf_pk)}, : {str(vrf_g)}")
+        self.log.highlight(f"verifying slot {slot}\nsigma {sigma}\nproof {proof}\npk {vrf_pk} \nbase {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
             #in this case this slot is empty
             self.log.warn(f"<new_slot> leader verification fails")
             self.current_block=EmptyBlock(self.env.genesis_time) 
-            if self.current_epoch==None:
-                self.__gen_genesis_epoch()
             self.current_epoch.add_block(self.current_block)
             return
-
         self.current_slot_uid = slot
         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.env.is_current_leader(self.id):
             self.log.highlight(f"{str(self)} is broadcasting block")
             self.broadcast_block()
-            self.end_leadership()
-        elif self.am_current_endorser:
+        elif self.env.is_current_endorser(self.id):
             self.log.highlight(f"{str(self)} is endorsing block")
             self.endorse_block()
-            self.end_endorsing()
 
     def update_stake(self):
         if len(self.blockchain)==0:
@@ -184,20 +128,6 @@ class Stakeholder(object):
         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):
-        self.log.info(f"stakeholder:{str(self)} ending leadership for slot{self.current_slot_uid}")
-        self.am_current_leader=False
-
-    def end_endorsing(self):
-        self.log.info(f"stakeholder:{str(self)} ending endorsing for slot{self.current_slot_uid}")
-        self.am_current_endorser=False
-
-    def set_leader(self):
-        self.am_current_leader=True
-
-    def set_endorser(self):
-        self.am_current_endorser=True
-
     def set_corrupt(self):
         self.am_corrupt=False
 
@@ -205,11 +135,11 @@ class Stakeholder(object):
     only leader can broadcast block
     '''
     def broadcast_block(self):
-        if not self.am_current_leader:
+        if not self.env.is_current_leader(self.id):
             return
         self.current_block.set_leader(self.id)
         self.log.highlight("broadcasting block")
-        assert self.am_current_leader and self.current_block is not None
+        assert self.env.is_current_leader(self.id) and self.current_block is not None
         signed_block=None
         #TODO should wait for l slot until block is endorsed
         endorsing_cnt=10
@@ -221,17 +151,17 @@ class Stakeholder(object):
             self.log.warn("failure endorsing the block...")
             self.current_block = EmptyBlock(self.env.genesis_time)
         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(self.current_block, signed_block, self.current_slot_uid)
     
     '''
     only endorser can broadcast block
     '''
     def endorse_block(self):
-        if not self.am_current_endorser:
+        if not self.env.is_current_endorser(self.id):
             return
         self.current_block.set_endorser(self.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.env.is_current_endorser(self.id):
             self.log.warn("not endorser")
             return
         assert self.current_block is not None
@@ -252,6 +182,8 @@ class Stakeholder(object):
             stashed=False
         self.log.info(f"current block : {str(cur_blk)}\tblock uid: {blk_uid}\tstashed: {stashed}")
         if cur_blk is None:
+            self.log.warn(f"blk uid {blk_uid}, blockchain length: {len(self.blockchain)}")
+            self.log.warn(f"requested block is None\nblk_uid: {blk_uid}, blockchain: {self.blockchain}")
             self.log.warn(f'block is none, current block is {str(self.current_block)} and current slot {self.current_slot_uid}, current block uid {blk_uid}, env slot {self.env.current_slot}, env blk {self.env.block_id}')
         while cur_blk is None:
             self.log.info("waiting for start of slot/epoch...")
@@ -279,7 +211,7 @@ class Stakeholder(object):
             self.env.corrupt_blk()
 
     def confirm_endorsing(self, endorser_sig, blk_uid, epoch_slot):
-        self.log.highlight("receiving block")
+        self.log.highlight(f"confirming block with epoch slot id {blk_uid}")
         confirmed = False
         cur_blk, _ = self.__get_blk(blk_uid)
         self.log.highlight(f'confirming endorsed block  {str(cur_blk)}')

+ 2 - 1
script/research/dpos/ouroboros/vrf.py

@@ -40,7 +40,7 @@ class VRF(object):
         g = ecc.gen()
         pk = ecc.scalar_mult(sk, g)
         #
-        self.pk = pk
+        self.pk = pk 
         self.sk = sk
         self.g=g
 
@@ -52,6 +52,7 @@ class VRF(object):
     def sign(self, x):
         pi = ecc.scalar_mult(inverse_of(x+self.sk, self.order), self.g)
         y = eta.pairing(*self.g[1:], *pi[1:])
+        self.log.highlight(f"signing slot {x}\nsigma {y}\nproof {pi}\npk {self.pk} \nbase {self.g}")
         return (y, pi)
 
     def update(self, pk, sk, g):

+ 2 - 8
script/research/dpos/simulation.py

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