def receive_vote(self, json_data): ''' Receive the vote message and make the update: (1) Update the inforamtion in given vote storage - prepare certificate.(2) update the node in from_nodes. input: json_data: the json_data received by view change vote broadcast: { "index": self._index, "view_number": self._follow_view.get_view(), "checkpoint":self._ckpt.get_ckpt_info(), "prepare_certificates":self.get_prepare_certificates(), } ''' update_view = None prepare_certificates = json_data["prepare_certificates"] self._log.debug("%d update prepare_certificate for view %d. %s", self._node_index, json_data['view_number'], str(prepare_certificates)) for slot in prepare_certificates: prepare_certificate = Status.Certificate( View(0, self._num_total_nodes)) prepare_certificate.dumps_from_dict(prepare_certificates[slot]) # Keep the prepare certificate who has the largest view number if slot not in self.prepare_certificate_by_slot or ( self.prepare_certificate_by_slot[slot]._view.get_view() < (prepare_certificate._view.get_view())): self.prepare_certificate_by_slot[slot] = prepare_certificate self.from_nodes.add(json_data['index'])
async def commit(self, request): ''' Once receive more than 2f + 1 prepare message, send the commit message. input: request: prepare message from prepare: prepare_msg = { 'index': self._index, 'view': self._n, 'proposal': { this_slot: json_data } 'type': 'prepare' } ''' json_data = await request.json() # self._log.info("%d: on commit", self._index) self._log.info("%d: receive prepare msg from %d", self._index, json_data['index']) # print("\t--->node "+str(self._index)+": receive prepare msg from node "+str(json_data['index'])) # print(json_data) if json_data['view'] < self._follow_view.get_view(): # when receive message with view < follow_view, do nothing return web.Response() for slot in json_data['proposal']: if not self._legal_slot(slot): continue if slot not in self._status_by_slot: self._status_by_slot[slot] = Status(self._f) status = self._status_by_slot[slot] view = View(json_data['view'], self._node_cnt) status._update_sequence(json_data['type'], view, json_data['proposal'][slot], json_data['index']) if status._check_majority(json_data['type']): status.prepare_certificate = Status.Certificate( view, json_data['proposal'][slot]) commit_msg = { 'index': self._index, 'view': json_data['view'], 'proposal': { slot: json_data['proposal'][slot] }, 'type': MessageType.COMMIT } await self._post(self._nodes, MessageType.REPLY, commit_msg) return web.Response()
async def receive_sync(self, request): ''' Update the checkpoint and fill the bubble when receive sync messages. input: request: { 'checkpoint': json_data = { 'next_slot': self._next_slot 'ckpt': json.dumps(ckpt) } 'commit_certificates':commit_certificates (Elements are commit_certificate.to_dict()) } ''' self._log.info("%d: on receive sync stage.", self._index) json_data = await request.json() try: # print(len(self._status_by_slot)) # print(self._ckpt.next_slot, self._last_commit_slot + 1) # # print(len(json_data['checkpoint'])) # print('node :' + str(self._index) +' > '+str(self._blockchain.commit_counter)+' : '+str(self._blockchain.length)) # print() # print() self.committed_to_blockchain = False except Exception as e: traceback.print_exc() print('for i = ' + str(i)) print(e) self._ckpt.update_checkpoint(json_data['checkpoint']) self._last_commit_slot = max(self._last_commit_slot, self._ckpt.next_slot - 1) # TODO: Only check bubble instead of all slots between lowerbound # and upperbound of the commit. for slot in json_data['commit_certificates']: # Skip those slot not qualified for update. if int(slot) >= self._ckpt.get_commit_upperbound() or ( int(slot) < self._ckpt.next_slot): continue certificate = json_data['commit_certificates'][slot] if slot not in self._status_by_slot: self._status_by_slot[slot] = Status(self._f) commit_certificate = Status.Certificate(View( 0, self._node_cnt)) commit_certificate.dumps_from_dict(certificate) self._status_by_slot[ slot].commit_certificate = commit_certificate elif not self._status_by_slot[slot].commit_certificate: commit_certificate = Status.Certificate(View( 0, self._node_cnt)) commit_certificate.dumps_from_dict(certificate) self._status_by_slot[ slot].commit_certificate = commit_certificate # Commit once the next slot of the last_commit_slot get commit certificate while (str(self._last_commit_slot + 1) in self._status_by_slot and self._status_by_slot[str(self._last_commit_slot + 1)].commit_certificate): self._last_commit_slot += 1 # When commit messages fill the next checkpoint, # propose a new checkpoint. if (self._last_commit_slot + 1) % self._checkpoint_interval == 0: await self._ckpt.propose_vote(self.get_commit_decisions()) self._log.info( "%d: During rev_sync, Propose checkpoint with l " "ast slot: %d. In addition, current checkpoint's next_slot is: %d", self._index, self._last_commit_slot, self._ckpt.next_slot) await self.dump_to_file() return web.Response()
async def reply(self, request): ''' Once receive more than 2f + 1 commit message, append the commit certificate and cannot change anymore. In addition, if there is no bubbles ahead, commit the given slots and update the last_commit_slot. input: request: commit message from commit: preprepare_msg = { 'index': self._index, 'n': self._n, 'proposal': { this_slot: json_data } 'type': 'commit' } ''' json_data = await request.json() # self._log.info(" %d: on reply", self._index) # print("\t--->node "+str(self._index)+": on reply ") if json_data['view'] < self._follow_view.get_view(): # when receive message with view < follow_view, do nothing return web.Response() self._log.info(" %d: receive commit msg from %d", self._index, json_data['index']) for slot in json_data['proposal']: if not self._legal_slot(slot): self._log.error("%d: message %s not in valid slot", self._index, json_data) continue if slot not in self._status_by_slot: self._status_by_slot[slot] = Status(self._f) status = self._status_by_slot[slot] view = View(json_data['view'], self._node_cnt) status._update_sequence(json_data['type'], view, json_data['proposal'][slot], json_data['index']) # Commit only when no commit certificate and got more than 2f + 1 if not status.commit_certificate and status._check_majority( json_data['type']): status.commit_certificate = Status.Certificate( view, json_data['proposal'][slot]) self._log.debug("Add commit certifiacte to slot %d", int(slot)) # Reply only once and only when no bubble ahead if self._last_commit_slot == int( slot) - 1 and not status.is_committed: status.is_committed = True self._last_commit_slot += 1 if not self._is_leader: self._next_propose_slot += 1 # When commit messages fill the next checkpoint, propose a new checkpoint. if (self._last_commit_slot + 1) % self._checkpoint_interval == 0: self._log.info( "%d: Propose checkpoint with last slot: %d. " "In addition, current checkpoint's next_slot is: %d", self._index, self._last_commit_slot, self._ckpt.next_slot) await self._ckpt.propose_vote( self.get_commit_decisions()) if (self._last_commit_slot + 1) % self._dump_interval == 0: await self.dump_to_file() reply_msg = { 'index': self._index, 'view': json_data['view'], 'proposal': json_data['proposal'][slot], 'type': MessageType.REPLY } try: await self._session.post( json_data['proposal'][slot]['client_url'], json=reply_msg) except: self._log.error( "Send message failed to %s", json_data['proposal'][slot]['client_url']) pass else: self._log.info( "%d reply to %s successfully!!", self._index, json_data['proposal'][slot]['client_url']) return web.Response()