Explorar o código

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

mohab %!s(int64=4) %!d(string=hai) anos
pai
achega
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 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, 
 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
 #TODO implement trustedbeacon as a node
 '''
 '''
 class TrustedBeacon(SynchedNTPClock, threading.Thread):
 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)
         threading.Thread.__init__(self)
         self.daemon=True
         self.daemon=True
-        self.node = node #stakeholder
-        self.vrf = vrf
         self.current_slot = self.slot
         self.current_slot = self.slot
         self.log = Logger(self, genesis_time)
         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.bb=0 # epoch counts since genesis (big bang)
-        self.sigmas = []
-        self.proofs = []
+        self.proofs_epoch=-1
 
 
     def __repr__(self):
     def __repr__(self):
         return f"trustedbeacon"
         return f"trustedbeacon"
 
 
     def run(self):
     def run(self):
         self.log.highlight("thread [start]")
         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:
         while True:
-            if self.slot != current_epoch:
-                current_epoch = self.slot
+            if not self.slot == prev_slot:
+                prev_slot = self.slot
                 self.__callback()
                 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):
     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):
     def __repr__(self):
         if self.is_genesis:
         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 "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)+"}"
         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:
         if type(self.tx)==str:
             return hash((self.state, self.tx, self.sl))
             return hash((self.state, self.tx, self.sl))
         elif type(self.tx)==dict:
         elif type(self.tx)==dict:
-            #TODO include distribution
             return hash((self.state, self.tx[SEED], self.tx[TX]))
             return hash((self.state, self.tx[SEED], self.tx[TX]))
         else: 
         else: 
-            #TODO (fix) shouldn't reach here
-            return 0
+            return hash(str(self))
 
 
     def __eq__(self, block):
     def __eq__(self, block):
         return self.state==block.state and \
         return self.state==block.state and \

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

@@ -3,22 +3,24 @@ synchronized NTP clock
 '''
 '''
 
 
 import ntplib
 import ntplib
-from time import ctime
+import time
 import math
 import math
 
 
 class SynchedNTPClock(object):
 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
         #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.slot_length=slot_length
         self.ntp_server = ntp_server
         self.ntp_server = ntp_server
         self.ntp_client = ntplib.NTPClient()
         self.ntp_client = ntplib.NTPClient()
         #TODO validate the server
         #TODO validate the server
         # when was darkfi birthday? as seconds since the epoch 
         # 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
         self.offline_cnt=0
+
     def __repr__(self):
     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):
     def __get_time_stat(self):
         response=None
         response=None
@@ -36,8 +38,8 @@ class SynchedNTPClock(object):
     @property
     @property
     def synched_time(self):
     def synched_time(self):
         state = self.__get_time_stat()
         state = self.__get_time_stat()
-        synched_time = state.tx_time
-        return synched_time
+        stime = state.tx_time
+        return stime
 
 
     @property
     @property
     def darkfi_time(self):
     def darkfi_time(self):
@@ -50,4 +52,12 @@ class SynchedNTPClock(object):
 
 
     @property
     @property
     def slot(self):
     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 math
 import random
 import random
 import time
 import time
+from threading import Thread
 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.data import GenesisItem, Data
 from ouroboros import utils
 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,
 \class Z is the environment,
 environment is ought to interfece with the network
 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()):
         self.genesis_time=genesis_time
         self.genesis_time=genesis_time
+        self.beacon = TrustedBeacon(epoch_length, genesis_time)
         self.log = Logger(self, genesis_time)
         self.log = Logger(self, genesis_time)
         self.epoch_length=epoch_length
         self.epoch_length=epoch_length
         self.stakeholders = np.array(stakeholdes)
         self.stakeholders = np.array(stakeholdes)
         self.adversary_mask=np.array([True]*len(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.current_slot=0
-        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.cached_dist = []
@@ -31,14 +35,93 @@ class Z(object):
         #a transaction is declared stable if and only if it is in a block that,
         #a transaction is declared stable if and only if it is in a block that,
         # is more than k blocks deep in the ledger.
         # is more than k blocks deep in the ledger.
         self.k = self.epoch_length/2 - self.l -1 
         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
     @property
     def endorser_len(self):
     def endorser_len(self):
         #TODO (impl)
         #TODO (impl)
         pass
         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_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:
         for sh in self.stakeholders:
             buff+=str(sh)+"\n"
             buff+=str(sh)+"\n"
         return buff
         return buff
@@ -85,7 +168,7 @@ class Z(object):
 
 
     @property
     @property
     def current_leader_id(self):
     def current_leader_id(self):
-        return self.current_epoch_leaders[self.epoch_slot]
+        return self.slot_committee[self.current_slot][0]
 
 
     @property
     @property
     def current_stakeholder(self):
     def current_stakeholder(self):
@@ -93,16 +176,13 @@ class Z(object):
         return self.stakeholders[self.current_leader_id]
         return self.stakeholders[self.current_leader_id]
 
 
     @property
     @property
-    def current_endorser_idx(self):
-        return self.current_epoch_endorsers[self.epoch_slot]
-
     def current_endorser_id(self):
     def current_endorser_id(self):
-        return self.current_endorser.id
+        return self.slot_committee[self.current_slot][1]
 
 
     @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_idx]
+        return self.stakeholders[self.current_endorser_id]
 
 
     @property
     @property
     def current_leader_vrf_pk(self):
     def current_leader_vrf_pk(self):
@@ -112,13 +192,26 @@ class Z(object):
     def current_leader_vrf_g(self):
     def current_leader_vrf_g(self):
         return self.stakeholders[self.current_leader_id].vrf_base
         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
     @property
     def current_leader_sig_pk(self):
     def current_leader_sig_pk(self):
         return self.stakeholders[self.current_leader_id].sig_pk
         return self.stakeholders[self.current_leader_id].sig_pk
     
     
     @property
     @property
     def current_endorser_sig_pk(self):
     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):
     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
@@ -130,6 +223,7 @@ class Z(object):
     def endorser_vrf_pk(self, epoch_slot):
     def endorser_vrf_pk(self, epoch_slot):
         return self.endorser(epoch_slot).vrf_pk
         return self.endorser(epoch_slot).vrf_pk
 
 
+    #note! assumes epoch_slot lays in the current epoch
     def leader(self, epoch_slot):
     def leader(self, epoch_slot):
         assert epoch_slot >= 0 and epoch_slot < self.epoch_length
         assert epoch_slot >= 0 and epoch_slot < self.epoch_length
         return self.stakeholders[epoch_slot]
         return self.stakeholders[epoch_slot]
@@ -139,7 +233,16 @@ class Z(object):
 
 
     def leader_vrf_pk(self, epoch_slot):
     def leader_vrf_pk(self, epoch_slot):
         return self.leader(epoch_slot).vrf_pk
         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
     #TODO complete
     def obfuscate_idx(self, i):
     def obfuscate_idx(self, i):
         return i
         return i
@@ -168,15 +271,33 @@ 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 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
     @property
     def random(self):
     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):
         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'
             assert sigma!=None, 'proof cant be None'
             def leader_selection_hash(sigma):
             def leader_selection_hash(sigma):
                 Y = np.array(sigma)
                 Y = np.array(sigma)
@@ -193,38 +314,32 @@ class Z(object):
             #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
             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:
         while self.current_blk_endorser_sig is None:
             self.log.info('pending endorsing...')
             self.log.info('pending endorsing...')
             time.sleep(1)
             time.sleep(1)
             #wait for it untill it gets endorsed
             #wait for it untill it gets endorsed
             pass
             pass
+        self.current_block = cur_block
         for stakeholder in self.stakeholders:
         for stakeholder in self.stakeholders:
             if not stakeholder.is_leader:
             if not stakeholder.is_leader:
                 stakeholder.receive_block(signed_block, self.current_blk_endorser_sig, slot_uid)
                 stakeholder.receive_block(signed_block, self.current_blk_endorser_sig, slot_uid)
         self.print_blockchain()
         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
     @property
     def block_id(self):
     def block_id(self):
         return self.current_slot%self.epoch_length
         return self.current_slot%self.epoch_length
@@ -239,10 +354,6 @@ class Z(object):
         else:
         else:
             self.log.warn("unconfirmed endorsed siganture")
             self.log.warn("unconfirmed endorsed siganture")
 
 
-    def start(self):
-        for sh in self.stakeholders:
-            sh(self)
-            sh.start()
 
 
     def print_blockchain(self):
     def print_blockchain(self):
         for sh in self.stakeholders:
         for sh in self.stakeholders:
@@ -257,7 +368,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_idx)
+        self.corrupt(self.current_endorser_id)
 
 
     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}")

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

@@ -3,7 +3,6 @@ import time
 from ouroboros.block import Block, GensisBlock, EmptyBlock
 from ouroboros.block import Block, GensisBlock, EmptyBlock
 from ouroboros.blockchain import Blockchain
 from ouroboros.blockchain import Blockchain
 from ouroboros.epoch import Epoch
 from ouroboros.epoch import Epoch
-from ouroboros.beacon import TrustedBeacon
 from ouroboros.vrf import verify, VRF
 from ouroboros.vrf import verify, VRF
 from ouroboros.utils import *
 from ouroboros.utils import *
 from ouroboros.logger import Logger
 from ouroboros.logger import Logger
@@ -31,15 +30,12 @@ class Stakeholder(object):
         #
         #
         self.current_block = None
         self.current_block = None
         self.current_epoch = None
         self.current_epoch = None
-        self.am_current_leader=False
-        self.am_current_endorser=False
         self.am_corrupt=False
         self.am_corrupt=False
         #
         #
         self.blockchain=None
         self.blockchain=None
         #
         #
         self.data = Data()
         self.data = Data()
         #verifiable fingerprint for a stakeholder taking advantage of public sig, vrf
         #verifiable fingerprint for a stakeholder taking advantage of public sig, vrf
-
         self.id = sign_message(self.passwd, self.sig_sk, str(self.vrf_pk))
         self.id = sign_message(self.passwd, self.sig_sk, str(self.vrf_pk))
 
 
     def receive_tx(self, tx):
     def receive_tx(self, tx):
@@ -50,10 +46,6 @@ class Stakeholder(object):
         self.data.append(tx)
         self.data.append(tx)
         self.env.broadcast_tx(tx)
         self.env.broadcast_tx(tx)
 
 
-    @property
-    def is_leader(self):
-        return self.am_current_leader
-
     @property
     @property
     def vrf_pk(self):
     def vrf_pk(self):
         return self.__vrf_pk
         return self.__vrf_pk
@@ -64,9 +56,9 @@ class Stakeholder(object):
     
     
     def __repr__(self):
     def __repr__(self):
         buff=''
         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}"
             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}"
             buff = f"\tendorser {self.id} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
         else:
         else:
             buff = f"\thonest committee memeber {self.id} 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}"
@@ -76,27 +68,13 @@ class Stakeholder(object):
         self.env=env
         self.env=env
         self.log = Logger(self, self.env.genesis_time)
         self.log = Logger(self, self.env.genesis_time)
         self.blockchain = Blockchain(self.epoch_length, 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
     @property
     def epoch_index(self):
     def epoch_index(self):
         return round(self.current_slot_uid/self.epoch_length)
         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):
     def end_slot(self):
         # start new transactions 
         # start new transactions 
         self.data = Data()
         self.data = Data()
@@ -104,70 +82,36 @@ class Stakeholder(object):
     def add_epoch(self):
     def add_epoch(self):
         self.blockchain.append(self.current_epoch)
         self.blockchain.append(self.current_epoch)
         self.update_stake()
         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()
             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):
     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.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
             #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")
             self.current_block=EmptyBlock(self.env.genesis_time) 
             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)
             self.current_epoch.add_block(self.current_block)
             return
             return
-
         self.current_slot_uid = slot
         self.current_slot_uid = slot
         if self.current_slot_uid%self.epoch_length!=0:
         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)
             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_block=Block(prev_blk, self.data, self.current_slot_uid, self.env.genesis_time)
             self.current_epoch.add_block(self.current_block)
             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.log.highlight(f"{str(self)} is broadcasting block")
             self.broadcast_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.log.highlight(f"{str(self)} is endorsing block")
             self.endorse_block()
             self.endorse_block()
-            self.end_endorsing()
 
 
     def update_stake(self):
     def update_stake(self):
         if len(self.blockchain)==0:
         if len(self.blockchain)==0:
@@ -184,20 +128,6 @@ class Stakeholder(object):
         self.stake += (self.env.beta * (endorser_cnt/self.env.endorser_len) + \
         self.stake += (self.env.beta * (endorser_cnt/self.env.endorser_len) + \
             (1-self.env.beta) * (leader_cnt/self.env.epoch_length)) * pall
             (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):
     def set_corrupt(self):
         self.am_corrupt=False
         self.am_corrupt=False
 
 
@@ -205,11 +135,11 @@ class Stakeholder(object):
     only leader can broadcast block
     only leader can broadcast block
     '''
     '''
     def broadcast_block(self):
     def broadcast_block(self):
-        if not self.am_current_leader:
+        if not self.env.is_current_leader(self.id):
             return
             return
         self.current_block.set_leader(self.id)
         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.env.is_current_leader(self.id) and self.current_block is not None
         signed_block=None
         signed_block=None
         #TODO should wait for l slot until block is endorsed
         #TODO should wait for l slot until block is endorsed
         endorsing_cnt=10
         endorsing_cnt=10
@@ -221,17 +151,17 @@ class Stakeholder(object):
             self.log.warn("failure endorsing the block...")
             self.log.warn("failure endorsing the block...")
             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(self.current_block, signed_block, self.current_slot_uid)
     
     
     '''
     '''
     only endorser can broadcast block
     only endorser can broadcast block
     '''
     '''
     def endorse_block(self):
     def endorse_block(self):
-        if not self.am_current_endorser:
+        if not self.env.is_current_endorser(self.id):
             return
             return
         self.current_block.set_endorser(self.id)
         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.env.is_current_endorser(self.id):
             self.log.warn("not endorser")
             self.log.warn("not endorser")
             return
             return
         assert self.current_block is not None
         assert self.current_block is not None
@@ -252,6 +182,8 @@ class Stakeholder(object):
             stashed=False
             stashed=False
         self.log.info(f"current block : {str(cur_blk)}\tblock uid: {blk_uid}\tstashed: {stashed}")
         self.log.info(f"current block : {str(cur_blk)}\tblock uid: {blk_uid}\tstashed: {stashed}")
         if cur_blk is None:
         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}')
             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:
         while cur_blk is None:
             self.log.info("waiting for start of slot/epoch...")
             self.log.info("waiting for start of slot/epoch...")
@@ -279,7 +211,7 @@ class Stakeholder(object):
             self.env.corrupt_blk()
             self.env.corrupt_blk()
 
 
     def confirm_endorsing(self, endorser_sig, blk_uid, epoch_slot):
     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
         confirmed = False
         cur_blk, _ = self.__get_blk(blk_uid)
         cur_blk, _ = self.__get_blk(blk_uid)
         self.log.highlight(f'confirming endorsed block  {str(cur_blk)}')
         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()
         g = ecc.gen()
         pk = ecc.scalar_mult(sk, g)
         pk = ecc.scalar_mult(sk, g)
         #
         #
-        self.pk = pk
+        self.pk = pk 
         self.sk = sk
         self.sk = sk
         self.g=g
         self.g=g
 
 
@@ -52,6 +52,7 @@ class VRF(object):
     def sign(self, x):
     def sign(self, x):
         pi = ecc.scalar_mult(inverse_of(x+self.sk, self.order), self.g)
         pi = ecc.scalar_mult(inverse_of(x+self.sk, self.order), self.g)
         y = eta.pairing(*self.g[1:], *pi[1:])
         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)
         return (y, pi)
 
 
     def update(self, pk, sk, g):
     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
 from ouroboros import Z
 
 
 
 
-EPOCH_LENGTH = 2
+EPOCH_LENGTH = 3
 stakeholders = []
 stakeholders = []
 
 
-for i in range(2):
+for i in range(3):
     stakeholders.append(Stakeholder(EPOCH_LENGTH, 'passwd'+str(i)))
     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 = Z(stakeholders, EPOCH_LENGTH, genesis_time=time.time())
-environment.start()
-
-for sh in environment.stakeholders:
-    sh.beacon.join()