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 ''' single block B_i for slot i in the system live time L, assigned to stakeholder U_j, with propability P_j_i, in the chain C, should be signed by U_j keys. ''' class Block(object): ''' @param previous_block: parent block to the current block @param data: is the transaction, or contracts in the leadger, or gensis block data, data is expected to be binary, no format is enforced @param slot_uid: the block corresponding slot monotonically increasing index, it's one-based @param gensis: boolean, True for gensis block ''' 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: self.state=state_hash(previous_block) self.tx = data self.sl = slot_uid self.is_genesis=genesis self.endorsed=False self.log = Logger(genesis_time) self.leader_id=None self.endorser_id=None def __repr__(self): if self.is_genesis: 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)+"}" def __hash__(self): if type(self.tx)==str: return hash((self.state, self.tx, self.sl)) elif type(self.tx)==dict: return hash((self.state, self.tx[SEED], self.tx[TX])) else: return hash(str(self)) def __eq__(self, block): return self.state==block.state and \ self.tx == block.tx and \ self.sl == block.sl def to_json(self): d = {'state':self.state, \ 'data': self.tx, \ 'sl': self.sl} return json.encoder(d) def set_endorsed(self): self.endorsed=True def set_endorser(self, id): self.endorser_id=id self.set_endorsed() def set_leader(self, id): self.leader_id=id @property def data(self): return self.tx @property def slot(self): return self.sl @property def empty(self): return (self.tx=='' or self.slot<0) and self.state=='' def encode(self): return str(self).encode() class GensisBlock(Block): ''' @param data: data is dictionary of list of (pk_i, s_i) public key, and stake respectively of the corresponding stakeholder U_i, seed of the leader election function. ''' 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 dist_block = data[0] self.stakeholders = dist_block[STAKEHOLDERS] self.distribution = dist_block[STAKEHOLDERS_DISTRIBUTIONS] self.seed = dist_block[SEED] #needed for pvss shd_buff = '' for shd in self.distribution: 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, genesis_time, True) ''' @return: the number of participating stakeholders in the blockchain ''' @property def length(self): return len(self.stakeholders) def __getitem__(self, i): if i<0 or i>=self.length: raise "index is out of range!" return self.stakeholders[i] ''' block lead by an adversary, or lead by offline leader is an empty Block ''' class EmptyBlock(Block): def __init__(self, genesis_time=time.time()): Block.__init__(self, '', -1, genesis_time, False)