mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 14:48:08 -05:00
[research/ourobors] add support for delayed endorsing]
This commit is contained in:
@@ -23,6 +23,7 @@ class TrustedBeacon(SynchedNTPClock, threading.Thread):
|
||||
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)
|
||||
|
||||
def __repr__(self):
|
||||
return f"trustedbeacon"
|
||||
@@ -43,11 +44,16 @@ class TrustedBeacon(SynchedNTPClock, threading.Thread):
|
||||
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.log.info(f"callback: new slot of idx: {self.current_slot}")
|
||||
y, pi = self.vrf.sign(self.current_slot)
|
||||
self.log.info(f"callback: signature calculated for {str(self.node)}")
|
||||
self.node.new_slot(self.current_slot, y, pi)
|
||||
else:
|
||||
self.bb+=1
|
||||
sigmas = []
|
||||
proofs = []
|
||||
#TODO since it's expensive, but to generate single (y,pi) pair as seed
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import time
|
||||
from ouroboros.utils import encode_genesis_data, decode_gensis_data, state_hash
|
||||
from ouroboros.consts import *
|
||||
from ouroboros.logger import Logger
|
||||
@@ -17,7 +18,7 @@ class Block(object):
|
||||
it's one-based
|
||||
@param gensis: boolean, True for gensis block
|
||||
'''
|
||||
def __init__(self, previous_block, data, slot_uid, genesis_time, genesis=False):
|
||||
def __init__(self, previous_block, data, slot_uid, genesis_time=time.time(), genesis=False):
|
||||
# state is hte hash of the previous block in the blockchain
|
||||
self.state=''
|
||||
if slot_uid>1:
|
||||
@@ -80,7 +81,7 @@ class GensisBlock(Block):
|
||||
and stake respectively of the corresponding stakeholder U_i,
|
||||
seed of the leader election function.
|
||||
'''
|
||||
def __init__(self, previous_block, data, slot_uid):
|
||||
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
|
||||
self.stakeholders = data[STAKEHOLDERS]
|
||||
self.distribution = data[STAKEHOLDERS_DISTRIBUTIONS]
|
||||
@@ -90,7 +91,7 @@ class GensisBlock(Block):
|
||||
shd_buff +=str(shd)
|
||||
#data = encode_genesis_data(shd_buff)
|
||||
data_dict = {'seed':self.seed, 'distribution':shd_buff}
|
||||
Block.__init__(self, previous_block, str(data_dict), slot_uid, True)
|
||||
Block.__init__(self, previous_block, str(data_dict), slot_uid, genesis_time, True)
|
||||
'''
|
||||
@return: the number of participating stakeholders in the blockchain
|
||||
'''
|
||||
@@ -109,5 +110,5 @@ lead by offline leader
|
||||
is an empty Block
|
||||
'''
|
||||
class EmptyBlock(Block):
|
||||
def __init__(self, genesis_time):
|
||||
def __init__(self, genesis_time=time.time()):
|
||||
Block.__init__(self, '', -1, genesis_time, False)
|
||||
|
||||
@@ -8,7 +8,7 @@ import math
|
||||
|
||||
class SynchedNTPClock(object):
|
||||
|
||||
def __init__(self, slot_length=1, ntp_server='europe.pool.ntp.org'):
|
||||
def __init__(self, slot_length=60, ntp_server='europe.pool.ntp.org'):
|
||||
#TODO how long should be the slot length
|
||||
self.slot_length=slot_length
|
||||
self.ntp_server = ntp_server
|
||||
@@ -16,7 +16,7 @@ class SynchedNTPClock(object):
|
||||
#TODO validate the server
|
||||
# when was darkfi birthday? as seconds since the epoch
|
||||
self.darkfi_epoch=0
|
||||
|
||||
self.offline_cnt=0
|
||||
def __repr__(self):
|
||||
return 'darkfi time: '+ ctime(self.darkfi_time) + ', current synched time: ' + ctime(self.synched_time)
|
||||
|
||||
@@ -44,5 +44,10 @@ class SynchedNTPClock(object):
|
||||
return self.synched_time - self.darkfi_epoch
|
||||
|
||||
@property
|
||||
def slot(self):
|
||||
def offline_time(self):
|
||||
self.offline_cnt+=1
|
||||
return self.offline_cnt
|
||||
|
||||
@property
|
||||
def slot(self):
|
||||
return math.floor(self.darkfi_time/self.slot_length)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from debugpy import configure
|
||||
import numpy as np
|
||||
import math
|
||||
import random
|
||||
@@ -23,7 +22,7 @@ class Z(object):
|
||||
self.current_blk_endorser_sig=None
|
||||
|
||||
def __repr__(self):
|
||||
buff= f"envirnment of {self.length} stakholders"
|
||||
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
|
||||
@@ -53,7 +52,7 @@ class Z(object):
|
||||
|
||||
@property
|
||||
def current_endorser_id(self):
|
||||
return self.current_endorser_id[self.epoch_slot]
|
||||
return self.current_epoch_endorsers[self.epoch_slot]
|
||||
|
||||
@property
|
||||
def current_endorser(self):
|
||||
@@ -76,6 +75,26 @@ class Z(object):
|
||||
def current_endorser_sig_pk(self):
|
||||
return self.stakeholders[self.current_endorser_id].sig_pk
|
||||
|
||||
def endorser(self, epoch_slot):
|
||||
assert epoch_slot >= 0 and epoch_slot < self.epoch_length
|
||||
return self.stakeholders[epoch_slot]
|
||||
|
||||
def endorser_sig_pk(self, epoch_slot):
|
||||
return self.endorser(epoch_slot).sig_pk
|
||||
|
||||
def endorser_vrf_pk(self, epoch_slot):
|
||||
return self.endorser(epoch_slot).vrf_pk
|
||||
|
||||
def leader(self, epoch_slot):
|
||||
assert epoch_slot >= 0 and epoch_slot < self.epoch_length
|
||||
return self.stakeholders[epoch_slot]
|
||||
|
||||
def leader_sig_pk(self, epoch_slot):
|
||||
return self.leader(epoch_slot).sig_pk
|
||||
|
||||
def leader_vrf_pk(self, epoch_slot):
|
||||
return self.leader(epoch_slot).vrf_pk
|
||||
|
||||
#TODO complete
|
||||
def obfuscate_idx(self, i):
|
||||
return i
|
||||
@@ -117,11 +136,11 @@ class Z(object):
|
||||
seed = leader_selection_hash(sigma)
|
||||
random.seed(seed)
|
||||
leader_idx=seed%self.length
|
||||
endorser_idx=random.randint(0,self.length)
|
||||
endorser_idx=random.randint(0,self.length-1)
|
||||
# only select an honest leaders
|
||||
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)
|
||||
enderser_idx=random.randint(0,self.length)
|
||||
leader_idx=random.randint(0,self.length-1)
|
||||
endorser_idx=random.randint(0,self.length-1)
|
||||
|
||||
#TODO select the following leader for this epoch, note,
|
||||
# under a single condition that no one is able to predict who is next
|
||||
@@ -133,30 +152,41 @@ class Z(object):
|
||||
self.current_slot=slot
|
||||
self.log.info(f"stakeholders: {self.stakeholders}")
|
||||
current_leader = self.stakeholders[self.current_leader_id]
|
||||
assert current_leader!=None, "current leader cant be None"
|
||||
if current_leader.is_leader:
|
||||
#pass leadership to the current slot leader from the epoch leader
|
||||
self.stakeholders[self.current_epoch_leaders[self.current_leader_id]].set_leader()
|
||||
self.stakeholders[self.current_epoch_endorsers[self.current_endorser_id]].set_endorser()
|
||||
|
||||
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_id]].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.current_slot=slot
|
||||
leaders, endorsers = self.select_epoch_leaders(sigmas, proofs)
|
||||
return leaders, endorsers
|
||||
|
||||
def broadcast_block(self, 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
|
||||
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 encorse_block(self, sig, slot_uid):
|
||||
@property
|
||||
def block_id(self):
|
||||
return self.current_slot%self.epoch_length
|
||||
|
||||
def endorse_block(self, sig, slot_uid):
|
||||
#TODO commit this step to handshake phases
|
||||
self.current_blk_endorser_sig=sig
|
||||
confirmed = self.stakeholders[self.current_leader_id].confirm_endorsing(sig, slot_uid)
|
||||
self.current_blk_endorser_sig=None
|
||||
self.log.info(f"endorsing block for current_leader_id: {self.current_leader_id}")
|
||||
confirmed = self.stakeholders[self.current_leader_id].confirm_endorsing(sig, self.block_id, self.epoch_slot)
|
||||
if confirmed:
|
||||
self.current_blk_endorser_sig=sig
|
||||
|
||||
else:
|
||||
self.log.warn("unconfirmed endorsed siganture")
|
||||
|
||||
def start(self):
|
||||
for sh in self.stakeholders:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from copy import deepcopy
|
||||
import time
|
||||
from ouroboros.block import Block, GensisBlock, EmptyBlock
|
||||
from ouroboros.blockchain import Blockchain
|
||||
from ouroboros.epoch import Epoch
|
||||
@@ -11,8 +13,7 @@ from ouroboros.consts import *
|
||||
\class Stakeholder
|
||||
'''
|
||||
class Stakeholder(object):
|
||||
|
||||
def __init__(self, epoch_length=2, passwd='password'):
|
||||
def __init__(self, epoch_length=10, passwd='password'):
|
||||
#TODO (fix) remove redundant variables reley on environment
|
||||
self.passwd=passwd
|
||||
self.stake=0
|
||||
@@ -32,11 +33,10 @@ class Stakeholder(object):
|
||||
self.tx=''
|
||||
self.current_epoch = None
|
||||
self.am_current_leader=False
|
||||
self.am_current_endorder=False
|
||||
self.am_current_endorser=False
|
||||
self.am_corrupt=False
|
||||
#
|
||||
self.blockchain=None
|
||||
self.current_blk_endorser_sig = None
|
||||
|
||||
@property
|
||||
def is_leader(self):
|
||||
@@ -51,7 +51,13 @@ class Stakeholder(object):
|
||||
return self.__vrf_base
|
||||
|
||||
def __repr__(self):
|
||||
buff = f"\tstakeholder with stake:{self.stake}\t"
|
||||
buff=''
|
||||
if self.am_current_leader:
|
||||
buff = f"\tleader {(hash(self.passwd))} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
|
||||
elif self.am_current_endorser:
|
||||
buff = f"\tendorser {(hash(self.passwd))} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
|
||||
else:
|
||||
buff = f"\thonest committee memeber {(hash(self.passwd))} with stake:{self.stake}\nsig_sk: {self.sig_pk}"
|
||||
return buff
|
||||
|
||||
def __call__(self, env):
|
||||
@@ -63,9 +69,9 @@ class Stakeholder(object):
|
||||
|
||||
|
||||
def start(self):
|
||||
self.log.info("start [started]")
|
||||
self.log.info("thread [started]")
|
||||
self.beacon.start()
|
||||
self.log.info("start [ended]")
|
||||
self.log.info("thread [ended]")
|
||||
|
||||
@property
|
||||
def epoch_index(self):
|
||||
@@ -78,6 +84,7 @@ class Stakeholder(object):
|
||||
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)
|
||||
assert self.current_block is not None
|
||||
self.current_epoch=Epoch(self.current_block, self.epoch_length, self.epoch_index, self.env.genesis_time)
|
||||
|
||||
'''
|
||||
@@ -97,10 +104,17 @@ class Stakeholder(object):
|
||||
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.__gen_genesis_epoch()
|
||||
self.__gen_genesis_epoch()
|
||||
if self.am_current_leader:
|
||||
self.broadcast_block()
|
||||
self.am_current_leader=False
|
||||
self.end_leadership()
|
||||
elif self.am_current_endorser:
|
||||
assert self.current_slot_uid==self.env.current_slot, f' current slot: {self.current_slot_uid}, env current slot {self.env.current_slot}'
|
||||
#assert self.sig_pk==self.env.current_endorser_sig_pk, f'current sig_pk: {self.sig_pk}\nZ sig_pk:{self.env.current_endorser_sig_pk}'
|
||||
if not self.sig_pk==self.env.current_endorser_sig_pk:
|
||||
return
|
||||
self.endorse_block()
|
||||
self.end_endorsing()
|
||||
|
||||
'''
|
||||
it's a callback function, and called by the diffuser
|
||||
@@ -118,9 +132,9 @@ class Stakeholder(object):
|
||||
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.log.warn(f"<new_slot> leader verification fails, current_epoch is None!")
|
||||
self.__gen_genesis_epoch()
|
||||
self.current_epoch.add_block(self.current_block)
|
||||
return
|
||||
@@ -131,87 +145,123 @@ class Stakeholder(object):
|
||||
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)
|
||||
assert self.current_block is not None
|
||||
if self.am_current_leader:
|
||||
self.log.highlight(f"leader {str(self)} is broadcasting block")
|
||||
self.broadcast_block()
|
||||
self.end_leadership()
|
||||
elif self.am_current_endorder:
|
||||
elif self.am_current_endorser:
|
||||
self.log.highlight(f"endorser {str(self)} is endorsing block")
|
||||
assert self.sig_pk==self.env.current_endorser_sig_pk
|
||||
self.endorse_block()
|
||||
self.end_endorsing()
|
||||
else:
|
||||
self.log.highlight(f"committee memeber is listening...")
|
||||
|
||||
|
||||
def end_leadership(self):
|
||||
self.log(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}")
|
||||
self.am_current_leader=False
|
||||
|
||||
def end_endorsing(self):
|
||||
self.log(f"stakeholder:{str(self)} ending endorsing for slot{self.current_slot_uid}")
|
||||
self.am_current_endorder=False
|
||||
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_endorser=True
|
||||
self.am_current_endorser=True
|
||||
|
||||
def set_corrupt(self):
|
||||
self.am_corrupt=False
|
||||
|
||||
def broadcast_block(self):
|
||||
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
|
||||
#TODO should wait for l slot until block is endorsed
|
||||
endorsing_cnt=10
|
||||
while not self.current_block.endorsed:
|
||||
time.sleep(1)
|
||||
self.log.info("...waiting for endorsment..")
|
||||
endorsing_cnt-=1
|
||||
if not self.current_block.endorsed:
|
||||
self.log.warn("failure endorsing the block...")
|
||||
if not self.current_block.endorsed:
|
||||
self.current_block = EmptyBlock(self.env.genesis_time)
|
||||
else:
|
||||
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.current_block=None
|
||||
|
||||
def endorse_block(self):
|
||||
if not self.am_current_endorder:
|
||||
if not self.am_current_endorser:
|
||||
return
|
||||
self.log.info(f"endorsing block for current_leader_id: {self.env.current_leader_id}")
|
||||
if not self.am_current_endorser:
|
||||
self.log.warn("not endorser")
|
||||
return
|
||||
assert self.current_block is not None
|
||||
sig = sign_message(self.passwd, self.sig_sk, self.current_block)
|
||||
self.log.highlight(f'block to be endorsed {str(self.current_block)}')
|
||||
self.log.highlight(f'block to be endorsed has slot_uid: {self.current_slot_uid}')
|
||||
self.log.highlight(f'block to be endorsed has sig_pk: {str(self.sig_pk)}')
|
||||
assert self.env.current_endorser_sig_pk==self.sig_pk
|
||||
self.env.endorse_block(sig, self.current_slot_uid)
|
||||
|
||||
def __get_blk(self, blk_uid):
|
||||
cur_blk = None
|
||||
assert(blk_uid>0)
|
||||
assert(blk_uid>=0)
|
||||
stashed=True
|
||||
if blk_uid>= len(self.blockchain):
|
||||
cur_blk = self.current_block
|
||||
else:
|
||||
cur_blk = self.current_block
|
||||
if blk_uid < len(self.blockchain):
|
||||
#TODO this assumes synced blockchain
|
||||
cur_blk = self.blockchain[blk_uid]
|
||||
self.log.warn(f"current block from blockchain: {(cur_blk)}")
|
||||
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'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}')
|
||||
assert cur_blk is not None and self.current_block is not None
|
||||
return cur_blk, stashed
|
||||
|
||||
def receive_block(self, signed_block, endorser_sig, blk_uid):
|
||||
self.log.highlight("receiving block")
|
||||
cur_blk, stashed = self.__get_blk(blk_uid)
|
||||
|
||||
assert cur_blk is not None
|
||||
#TODO to consider deley should retrive leader_pk of corresponding blk_uid
|
||||
if verify_signature(self.env.current_leader_sig_pk, cur_blk, signed_block) \
|
||||
and verify_signature(self.env.current_endorser_sig_pk, cur_blk, endorser_sig):
|
||||
self.log.highlight(f'receiving block {str(cur_blk)}')
|
||||
self.log.highlight(f'receiving block has slot_uid: {self.current_slot_uid}')
|
||||
self.log.highlight(f'receiving block has sig_pk: {self.env.current_endorser_sig_pk}')
|
||||
blk_verified = verify_signature(self.env.current_leader_sig_pk, cur_blk, signed_block)
|
||||
self.log.info("endorser sig_pk {self.env.current_endorser_sig_pk}, cur_blk: {cur_blk}, endorser_sig: {endorser_sig}")
|
||||
blk_edrs_verified = verify_signature(self.env.current_endorser_sig_pk, cur_blk, endorser_sig)
|
||||
if blk_verified and blk_edrs_verified:
|
||||
if stashed:
|
||||
self.current_epoch.add_block(cur_blk)
|
||||
else:
|
||||
if not blk_verified:
|
||||
self.log.warn("block verification failed")
|
||||
elif not blk_edrs_verified:
|
||||
self.log.warn("block endorsing verification failed")
|
||||
self.env.corrupt_blk()
|
||||
|
||||
def confirm_endorsing(self, signed_endorsed_block, blk_uid):
|
||||
confirmed=False
|
||||
if self.am_current_leader:
|
||||
cur_blk, _ = self.__get_blk(blk_uid)
|
||||
if verify_signature(self.env.current_endorser_sig_pk, cur_blk, signed_endorsed_block):
|
||||
if blk_uid==self.current_slot_uid:
|
||||
self.current_block.set_endorsed()
|
||||
else:
|
||||
self.blockchain[blk_uid].set_endorsed()
|
||||
confirmed=True
|
||||
#self.env.confirm_endorsing(signed_endorsed_block, blk_uid)
|
||||
def confirm_endorsing(self, endorser_sig, blk_uid, epoch_slot):
|
||||
while not self.current_slot_uid == self.env.current_slot:
|
||||
self.log.info(" ...pending start of slot...")
|
||||
time.sleep(1)
|
||||
self.log.highlight("receiving block")
|
||||
confirmed = False
|
||||
cur_blk, _ = self.__get_blk(blk_uid)
|
||||
assert cur_blk is not None
|
||||
self.log.highlight(f'confirming endorsed block {str(cur_blk)}')
|
||||
self.log.highlight(f'confirming endorsed has slot_uid: {self.current_slot_uid}')
|
||||
self.log.highlight(f'confirming endorsed has sig_pk: {self.env.current_endorser_sig_pk}')
|
||||
if verify_signature(self.env.current_endorser_sig_pk, cur_blk, endorser_sig):
|
||||
if self.current_slot_uid==self.env.current_slot:
|
||||
self.current_block.set_endorsed()
|
||||
else:
|
||||
confirmed=False
|
||||
#self.env.confirm_endorsing(None, blk_uid)
|
||||
self.log.info(f"<confirm_endorsing enderser signature: {self.current_blk_endorser_sig}")
|
||||
self.blockchain[blk_uid].set_endorsed()
|
||||
confirmed=True
|
||||
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)}")
|
||||
confirmed=False
|
||||
return confirmed
|
||||
|
||||
|
||||
@@ -6,11 +6,12 @@ EPOCH_LENGTH = 2
|
||||
stakeholders = []
|
||||
|
||||
for i in range(2):
|
||||
stakeholders.append(Stakeholder(EPOCH_LENGTH))
|
||||
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()
|
||||
sh.beacon.join()
|
||||
Reference in New Issue
Block a user