environment.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import numpy as np
  2. import math
  3. import random
  4. import time
  5. from ouroboros.logger import Logger
  6. from ouroboros.consts import *
  7. '''
  8. \class Z is the environment
  9. '''
  10. class Z(object):
  11. def __init__(self, stakeholdes, epoch_length, genesis_time=time.time()):
  12. self.genesis_time=genesis_time
  13. self.log = Logger(self, genesis_time)
  14. self.epoch_length=epoch_length
  15. self.stakeholders = np.array(stakeholdes)
  16. self.adversary_mask=np.array([True]*len(stakeholdes))
  17. self.current_epoch_leaders=[-1]*self.epoch_length
  18. self.current_epoch_endorsers=[-1]*self.epoch_length
  19. self.current_slot=0
  20. self.log.info("Z initialized")
  21. self.current_blk_endorser_sig=None
  22. self.epoch_inited=False
  23. def __repr__(self):
  24. 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}"
  25. for sh in self.stakeholders:
  26. buff+=str(sh)+"\n"
  27. return buff
  28. '''
  29. return genesis data of the current epoch
  30. '''
  31. def get_genesis_data(self):
  32. #TODO implement dynaming staking
  33. genesis_data = {STAKEHOLDERS: self.stakeholders,
  34. STAKEHOLDERS_DISTRIBUTIONS:[],
  35. SEED: ''}
  36. return genesis_data
  37. @property
  38. def epoch_slot(self):
  39. return self.current_slot%self.epoch_length
  40. @property
  41. def current_leader_id(self):
  42. return self.current_epoch_leaders[self.epoch_slot]
  43. @property
  44. def current_stakeholder(self):
  45. self.log.info(f"getting leader of id: {self.current_leader_id}")
  46. return self.stakeholders[self.current_leader_id]
  47. @property
  48. def current_endorser_id(self):
  49. return self.current_epoch_endorsers[self.epoch_slot]
  50. @property
  51. def current_endorser(self):
  52. self.log.info(f"getting endorser of id: {self.current_leader_id}")
  53. return self.stakeholders[self.current_endorser_id]
  54. @property
  55. def current_leader_vrf_pk(self):
  56. return self.stakeholders[self.current_leader_id].vrf_pk
  57. @property
  58. def current_leader_vrf_g(self):
  59. return self.stakeholders[self.current_leader_id].vrf_base
  60. @property
  61. def current_leader_sig_pk(self):
  62. return self.stakeholders[self.current_leader_id].sig_pk
  63. @property
  64. def current_endorser_sig_pk(self):
  65. return self.stakeholders[self.current_endorser_id].sig_pk
  66. def endorser(self, epoch_slot):
  67. assert epoch_slot >= 0 and epoch_slot < self.epoch_length
  68. return self.stakeholders[epoch_slot]
  69. def endorser_sig_pk(self, epoch_slot):
  70. return self.endorser(epoch_slot).sig_pk
  71. def endorser_vrf_pk(self, epoch_slot):
  72. return self.endorser(epoch_slot).vrf_pk
  73. def leader(self, epoch_slot):
  74. assert epoch_slot >= 0 and epoch_slot < self.epoch_length
  75. return self.stakeholders[epoch_slot]
  76. def leader_sig_pk(self, epoch_slot):
  77. return self.leader(epoch_slot).sig_pk
  78. def leader_vrf_pk(self, epoch_slot):
  79. return self.leader(epoch_slot).vrf_pk
  80. #TODO complete
  81. def obfuscate_idx(self, i):
  82. return i
  83. #TODO complete
  84. def deobfuscate_idx(self, i):
  85. return i
  86. def corrupt(self, i):
  87. if i<0 or i>len(self.adversary_mask):
  88. return False
  89. self.adversary_mask[self.deobfuscate_idx(i)]=False
  90. return True
  91. '''
  92. return the length of all parties
  93. '''
  94. def __len__(self):
  95. return len(self.stakeholders)
  96. @property
  97. def length(self):
  98. return len(self.stakeholders)
  99. @property
  100. def honest(self):
  101. return len(self.stakeholders[self.adversary_mask])
  102. def select_epoch_leaders(self, sigmas, proofs):
  103. 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}")
  104. for i in range(self.epoch_length):
  105. self.log.info(f"current sigma of index {i} of total {len(sigmas)}, epoch_length: {self.epoch_length}")
  106. sigma = sigmas[i]
  107. assert sigma!=None, 'proof cant be None'
  108. def leader_selection_hash(sigma):
  109. Y = np.array(sigma)
  110. y_hypotenuse2 = math.ceil(np.sum(Y[1]**2+Y[2]**2))
  111. return y_hypotenuse2
  112. seed = leader_selection_hash(sigma)
  113. random.seed(seed)
  114. leader_idx=seed%self.length
  115. endorser_idx=random.randint(0,self.length-1)
  116. # only select an honest leaders
  117. while leader_idx==endorser_idx or not self.adversary_mask[leader_idx] or not self.adversary_mask[endorser_idx]:
  118. leader_idx=random.randint(0,self.length-1)
  119. endorser_idx=random.randint(0,self.length-1)
  120. #TODO select the following leader for this epoch, note,
  121. # under a single condition that no one is able to predict who is next
  122. self.current_epoch_leaders[i]=leader_idx
  123. self.current_epoch_endorsers[i]=endorser_idx
  124. return self.current_epoch_leaders, self.current_epoch_endorsers
  125. def new_slot(self, slot):
  126. self.current_slot=slot
  127. self.log.info(f"stakeholders: {self.stakeholders}")
  128. current_leader = self.stakeholders[self.current_leader_id]
  129. assert current_leader is not None, "current leader cant be None"
  130. self.log.highlight('selecting epochs leaders, and ensorsers ---->')
  131. self.stakeholders[self.current_epoch_endorsers[self.current_endorser_id]].set_endorser()
  132. self.stakeholders[self.current_epoch_leaders[self.current_leader_id]].set_leader()
  133. self.log.highlight('selected epochs leaders, and ensorsers <----')
  134. def new_epoch(self, slot, sigmas, proofs):
  135. self.epoch_inited=True
  136. self.current_slot=slot
  137. leaders, endorsers = self.select_epoch_leaders(sigmas, proofs)
  138. return leaders, endorsers
  139. def broadcast_block(self, signed_block, slot_uid):
  140. while self.current_blk_endorser_sig is None:
  141. self.log.info('pending endorsing...')
  142. time.sleep(1)
  143. #wait for it untill it gets endorsed
  144. pass
  145. for stakeholder in self.stakeholders:
  146. if not stakeholder.is_leader:
  147. stakeholder.receive_block(signed_block, self.current_blk_endorser_sig, slot_uid)
  148. self.print_blockchain()
  149. @property
  150. def block_id(self):
  151. return self.current_slot%self.epoch_length
  152. def endorse_block(self, sig, slot_uid):
  153. #TODO commit this step to handshake phases
  154. self.current_blk_endorser_sig=None
  155. self.log.info(f"endorsing block for current_leader_id: {self.current_leader_id}")
  156. confirmed = self.stakeholders[self.current_leader_id].confirm_endorsing(sig, self.block_id, self.epoch_slot)
  157. if confirmed:
  158. self.current_blk_endorser_sig=sig
  159. else:
  160. self.log.warn("unconfirmed endorsed siganture")
  161. def start(self):
  162. for sh in self.stakeholders:
  163. sh(self)
  164. sh.start()
  165. def print_blockchain(self):
  166. for sh in self.stakeholders:
  167. bc = sh.blockchain
  168. self.log.highlight(f"<blockchain> {len(bc)} blocks: "+str(bc))
  169. def confirm_endorsing(self, sig, blk_uid):
  170. if blk_uid==self.current_slot:
  171. self.current_blk_endorser_sig = sig
  172. def corrupt_leader(self):
  173. self.corrupt(self.current_leader_id)
  174. def corrupt_endorse(self):
  175. self.corrupt(self.current_endorser_id)
  176. def corrupt_blk(self):
  177. self.log.warn(f"<corrupt_blk> at slot: {self.current_slot}")
  178. self.corrupt_leader()
  179. self.corrupt_endorse()