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