def consensus(self): # broadcasting 한 블럭이 검증이 끝났는지 확인한다. confirmed_block = None try: confirmed_block = self._candidate_blocks.get_confirmed_block() except candidate_blocks.NoExistBlock as e: logging.error(e) except candidate_blocks.NotCompleteValidation as e: # try re count voters logging.warning( f"This block need more validation vote from Peers block " f"hash({str(e.block.block_hash)}) channel({self._channel_name})" ) self._blockmanager.broadcast_audience_set() if util.diff_in_seconds( e.block.time_stamp) > conf.BLOCK_VOTE_TIMEOUT: logging.warning("Time Outed Block not confirmed duration: " + str(util.diff_in_seconds(e.block.time_stamp))) self._candidate_blocks.remove_broken_block(e.block.block_hash) self.__throw_out_block(e.block) else: time.sleep(conf.INTERVAL_WAIT_PEER_VOTE) except candidate_blocks.InvalidatedBlock as e: # 실패한 투표에 대한 처리 logging.error("InvalidatedBlock!! hash: " + str(e.block.block_hash)) logging.debug("InvalidatedBlock!! prev_hash: " + str(e.block.prev_block_hash)) # 현재 블록은 데이터가 있나? logging.debug("This block status: " + str(self._block.confirmed_tx_len)) self.__throw_out_block(e.block) # 검증이 끝난 블럭이 있으면 result = None if confirmed_block is not None: logging.info( f"Block Validation is Complete " f"hash({confirmed_block.block_hash}) channel({self._channel_name})" ) # 현재 블럭에 이전 투표에 대한 기록을 갱신한다. self._block.prev_block_confirm = True # 검증이 끝나면 BlockChain 에 해당 block 의 block_hash 로 등록 완료 confirmed_block.block_status = BlockStatus.confirmed result = self._blockmanager.add_block(confirmed_block) # 새로운 블럭의 broadcast 를 위해 current_vote_block_hash 를 리셋한다. self._current_vote_block_hash = "" block_is_verified = True if self._current_vote_block_hash == "": if self._block and (conf.ALLOW_MAKE_EMPTY_BLOCK or self._block.confirmed_tx_len > 0): if conf.ALLOW_MAKE_EMPTY_BLOCK and \ self._txQueue.get_item_in_status(TransactionStatusInQueue.normal, TransactionStatusInQueue.normal): self._makeup_block() if conf.CHANNEL_OPTION[ self._channel_name]['store_valid_transaction_only']: # candidate_block을 broadcasting하기 전에 invoke result verifying 실행 self._block.generate_block( self._candidate_blocks.get_last_block( self._blockchain)) block_is_verified, need_rebuild, invoke_results = \ self._block.verify_through_score_invoke(is_leader=True) old_block_hash = self._block.block_hash if need_rebuild: verified_commit_state = copy.deepcopy( self._block.commit_state) self._block.generate_block( self._candidate_blocks.get_last_block( self._blockchain)) assert verified_commit_state == self._block.commit_state ObjectManager( ).channel_service.score_change_block_hash( block_height=self._block.height, old_block_hash=old_block_hash, new_block_hash=self._block.block_hash) self._blockmanager.set_invoke_results( self._block.block_hash, invoke_results) self._blockmanager.get_blockchain().set_last_commit_state( self._block.height, self._block.commit_state) else: # 검증 받을 블록의 hash 를 생성하고 후보로 등록한다. # logging.warning("add unconfirmed block to candidate blocks") self._block.generate_block( self._candidate_blocks.get_last_block( self._blockchain)) if block_is_verified: self._block.sign(ObjectManager().channel_service.peer_auth) self._candidate_blocks.add_unconfirmed_block(self._block) # 새로운 Block 을 생성하여 다음 tx 을 수집한다. self._gen_block() else: failed_block = self._block self._reset_block() self.__throw_out_block(failed_block) # 다음 검증 후보 블럭이 있는지 확인한다. candidate_block = self._candidate_blocks.get_candidate_block() peer_manager = ObjectManager().channel_service.peer_manager if candidate_block is not None: # 있으면 해당 블럭을 broadcast 하여 Peer 에게 검증을 요청한다. self._current_vote_block_hash = candidate_block.block_hash logging.info( f"candidate block height: {candidate_block.height}") logging.info("candidate block hash: " + self._current_vote_block_hash) util.logger.spam( f"consensus_siever:consensus try peer_manager.get_next_leader_peer().peer_id" ) candidate_block.next_leader_peer = peer_manager.get_next_leader_peer( ).peer_id # 생성된 블럭을 투표 요청하기 위해서 broadcast 한다. self._blockmanager.broadcast_send_unconfirmed_block( candidate_block) time.sleep(conf.SLEEP_SECONDS_IN_SERVICE_LOOP) # broadcast 를 요청했으면 다음 투표 block 이 있는지 계속 검사하기 위해 return 한다. return result elif self._block is not None and \ not conf.ALLOW_MAKE_EMPTY_BLOCK and \ (self._block.prev_block_confirm is True) and \ (self._block.confirmed_tx_len == 0): # 검증할 후보 블럭이 없으면서 이전 블럭이 unconfirmed block 이면 투표가 담긴 빈 블럭을 전송한다. self._block.prev_block_hash = confirmed_block.block_hash self._block.block_type = BlockType.vote self.made_block_count -= 1 logging.debug(f"made_block_count({self.made_block_count})") self._block.next_leader_peer = peer_manager.get_next_leader_peer( ).peer_id self._blockmanager.broadcast_send_unconfirmed_block( self._block) # 전송한 빈블럭을 대체한다. if self.made_block_count < conf.LEADER_BLOCK_CREATION_LIMIT: # or not self._txQueue.empty(): self._gen_block() else: self._stop_gen_block() util.logger.spam( f"consensus_siever:consensus channel({self._channel_name}) " f"\ntry ObjectManager().peer_service.rotate_next_leader({self._channel_name})" ) ObjectManager().peer_service.rotate_next_leader( self._channel_name) self._makeup_block() time.sleep(conf.SLEEP_SECONDS_IN_SERVICE_LOOP) return result
async def consensus(self): util.logger.debug( f"-------------------consensus " f"candidate_blocks({len(self._block_manager.candidate_blocks.blocks)})" ) async with self.__lock: if self._block_manager.epoch.leader_id != ChannelProperty( ).peer_id: util.logger.warning( f"This peer is not leader. epoch leader={self._block_manager.epoch.leader_id}" ) return self._vote_queue = asyncio.Queue(loop=self._loop) complained_result = self._block_manager.epoch.complained_result block_builder = self._block_manager.epoch.makeup_block( complained_result) vote_result = None last_unconfirmed_block = self._blockchain.last_unconfirmed_block next_leader = ExternalAddress.fromhex(ChannelProperty().peer_id) need_next_call = False try: if complained_result: util.logger.spam("consensus block_builder.complained") """ confirm_info = self._blockchain.find_confirm_info_by_hash(self._blockchain.last_block.header.hash) if not confirm_info and self._blockchain.last_block.header.height > 0: util.logger.spam("Can't make a block as a leader, this peer will be complained too.") return """ self._made_block_count += 1 elif self.made_block_count >= (conf.MAX_MADE_BLOCK_COUNT - 1): if last_unconfirmed_block: await self.__add_block(last_unconfirmed_block) peer_manager = ObjectManager( ).channel_service.peer_manager next_leader = ExternalAddress.fromhex( peer_manager.get_next_leader_peer( current_leader_peer_id=ChannelProperty( ).peer_id).peer_id) else: util.logger.info( f"This leader already made {self.made_block_count} blocks. " f"MAX_MADE_BLOCK_COUNT is {conf.MAX_MADE_BLOCK_COUNT} " f"There is no more right. Consensus loop will return." ) return elif len(block_builder.transactions ) > 0 or conf.ALLOW_MAKE_EMPTY_BLOCK: if last_unconfirmed_block: next_leader = await self.__add_block_and_new_epoch( block_builder, last_unconfirmed_block) elif len(block_builder.transactions) == 0 and ( last_unconfirmed_block and len(last_unconfirmed_block.body.transactions) > 0): next_leader = await self.__add_block_and_new_epoch( block_builder, last_unconfirmed_block) else: need_next_call = True except NotEnoughVotes: need_next_call = True finally: if need_next_call: return self.__block_generation_timer.call() candidate_block = self.__build_candidate_block( block_builder, next_leader, vote_result) candidate_block, invoke_results = ObjectManager( ).channel_service.score_invoke(candidate_block) self._block_manager.set_invoke_results( candidate_block.header.hash.hex(), invoke_results) util.logger.spam(f"candidate block : {candidate_block.header}") self._block_manager.vote_unconfirmed_block( candidate_block.header.hash, True) self._block_manager.candidate_blocks.add_block(candidate_block) self._blockchain.last_unconfirmed_block = candidate_block broadcast_func = partial( self._block_manager.broadcast_send_unconfirmed_block, candidate_block) self.__start_broadcast_send_unconfirmed_block_timer(broadcast_func) if await self._wait_for_voting(candidate_block) is None: return if next_leader.hex_hx() != ChannelProperty().peer_id: util.logger.spam(f"-------------------turn_to_peer " f"next_leader({next_leader.hex_hx()}) " f"peer_id({ChannelProperty().peer_id})") ObjectManager().channel_service.reset_leader( next_leader.hex_hx()) ObjectManager().channel_service.turn_on_leader_complain_timer() else: self._block_manager.epoch = Epoch.new_epoch( next_leader.hex_hx()) if not conf.ALLOW_MAKE_EMPTY_BLOCK: self.__block_generation_timer.call_instantly() else: self.__block_generation_timer.call()
async def consensus(self): start_time = time.time() empty_block: Block = None try: self._loop = asyncio.get_event_loop() self._vote_queue = asyncio.Queue(loop=self._loop) block_builder = self._makeup_block() if len(block_builder.transactions ) == 0 and not conf.ALLOW_MAKE_EMPTY_BLOCK: return peer_manager = ObjectManager().channel_service.peer_manager last_block = self._blockchain.last_block block_builder.height = last_block.header.height + 1 block_builder.prev_hash = last_block.header.hash block_builder.next_leader = Address.fromhex( peer_manager.get_next_leader_peer().peer_id) block_builder.peer_private_key = ObjectManager( ).channel_service.peer_auth.peer_private_key block_builder.confirm_prev_block = (self._made_block_count > 0) candidate_block = block_builder.build() candidate_block, invoke_results = ObjectManager( ).channel_service.score_invoke(candidate_block) block_verifier = BlockVerifier.new("0.1a") block_verifier.verify(candidate_block, self._blockchain.last_block, self._blockchain) logging.info( f"candidate block height: {candidate_block.header.height}") logging.info( f"candidate block hash: {candidate_block.header.hash.hex()}") logging.info( f"candidate block next leader: {candidate_block.header.next_leader.hex()}" ) logging.info( f"candidate block confirm_prev_block: {candidate_block.body.confirm_prev_block}" ) vote = Vote(candidate_block.header.hash.hex(), ObjectManager().channel_service.peer_manager) vote.add_vote(ChannelProperty().group_id, ChannelProperty().peer_id, True) self._blockmanager.broadcast_send_unconfirmed_block( candidate_block) success = await self._wait_for_voting(candidate_block, vote) if not success: return self._blockmanager.set_invoke_results( candidate_block.header.hash.hex(), invoke_results) self._blockmanager.add_block(candidate_block) self._made_block_count += 1 pending_tx = self._txQueue.get_item_in_status( TransactionStatusInQueue.normal, TransactionStatusInQueue.normal) if not pending_tx and not conf.ALLOW_MAKE_EMPTY_BLOCK: block_builder = BlockBuilder.new("0.1a") block_builder.prev_hash = candidate_block.header.hash block_builder.height = candidate_block.header.height + 1 block_builder.next_leader = candidate_block.header.next_leader block_builder.peer_private_key = ObjectManager( ).channel_service.peer_auth.peer_private_key block_builder.confirm_prev_block = True empty_block = block_builder.build() self._blockmanager.broadcast_send_unconfirmed_block( empty_block) ObjectManager().channel_service.state_machine.turn_to_peer() finally: if not empty_block: elapsed_time = time.time() - start_time delay_time = conf.INTERVAL_BLOCKGENERATION - elapsed_time self._start_consensus_timer(delay_time)
async def consensus(self): util.logger.debug( f"-------------------consensus " f"candidate_blocks({len(self._blockmanager.candidate_blocks.blocks)})" ) with self.__lock: block_builder = self._makeup_block() vote_result = None if len(block_builder.transactions) > 0: # util.logger.debug(f"-------------------consensus logic-1") next_leader = ExternalAddress.fromhex( ChannelProperty().peer_id) if self._blockchain.last_unconfirmed_block: if (len(self._blockchain.last_unconfirmed_block.body. transactions) > 0) or ( len(self._blockchain.last_unconfirmed_block. body.transactions) == 0 and self._blockchain.last_unconfirmed_block.header. peer_id.hex_hx() != ChannelProperty().peer_id): # util.logger.debug(f"-------------------consensus logic-2") vote = self._blockmanager.candidate_blocks.get_vote( self._blockchain.last_unconfirmed_block.header.hash ) vote_result = vote.get_result( self._blockchain.last_unconfirmed_block.header. hash.hex(), conf.VOTING_RATIO) if not vote_result: return self.__block_generation_timer.call() self._blockmanager.add_block( self._blockchain.last_unconfirmed_block, vote) self._made_block_count += 1 next_leader = self._blockchain.last_unconfirmed_block.header.next_leader else: if self._blockchain.last_unconfirmed_block and len( self._blockchain.last_unconfirmed_block.body. transactions) > 0: # util.logger.debug(f"-------------------consensus logic-3") vote = self._blockmanager.candidate_blocks.get_vote( self._blockchain.last_unconfirmed_block.header.hash) vote_result = vote.get_result( self._blockchain.last_unconfirmed_block.header.hash. hex(), conf.VOTING_RATIO) if not vote_result: return self.__block_generation_timer.call() self._blockmanager.add_block( self._blockchain.last_unconfirmed_block, vote) self._made_block_count += 1 peer_manager = ObjectManager().channel_service.peer_manager next_leader = ExternalAddress.fromhex( peer_manager.get_next_leader_peer().peer_id) else: # util.logger.spam(f"tx count in block({len(block_builder.transactions)})") return self.__block_generation_timer.call() last_block = self._blockchain.last_block block_builder.height = last_block.header.height + 1 block_builder.prev_hash = last_block.header.hash block_builder.next_leader = next_leader block_builder.peer_private_key = ObjectManager( ).channel_service.peer_auth.peer_private_key block_builder.confirm_prev_block = vote_result or ( self._made_block_count > 0) candidate_block = block_builder.build() candidate_block, invoke_results = ObjectManager( ).channel_service.score_invoke(candidate_block) self._blockmanager.set_invoke_results( candidate_block.header.hash.hex(), invoke_results) block_verifier = BlockVerifier.new(candidate_block.header.version, self._blockchain.tx_versioner) block_verifier.verify(candidate_block, self._blockchain.last_block, self._blockchain) logging.debug(f"candidate block : {candidate_block.header}") self._blockmanager.vote_unconfirmed_block( candidate_block.header.hash, True) self._blockmanager.candidate_blocks.add_block(candidate_block) self._blockchain.last_unconfirmed_block = candidate_block broadcast_func = partial( self._blockmanager.broadcast_send_unconfirmed_block, candidate_block) self.__start_broadcast_send_unconfirmed_block_timer(broadcast_func) if len(block_builder.transactions) == 0 and not conf.ALLOW_MAKE_EMPTY_BLOCK and \ next_leader.hex() != ChannelProperty().peer_id: # util.logger.debug(f"-------------------turn_to_peer") ObjectManager().channel_service.state_machine.turn_to_peer() else: self.__block_generation_timer.call()
async def consensus(self): util.logger.debug( f"-------------------consensus " f"candidate_blocks({len(self._block_manager.candidate_blocks.blocks)})" ) with self.__lock: complained_result = self._block_manager.epoch.complained_result block_builder = self._block_manager.epoch.makeup_block( complained_result) vote_result = None last_unconfirmed_block = self._blockchain.last_unconfirmed_block next_leader = ExternalAddress.fromhex(ChannelProperty().peer_id) if complained_result: util.logger.spam("consensus block_builder.complained") confirm_info = self._blockchain.find_confirm_info_by_hash( self._blockchain.last_block.header.hash) if not confirm_info and self._blockchain.last_block.header.height > 0: util.logger.spam( "Can't make a block as a leader, this peer will be complained too." ) return vote_result = True self._block_manager.epoch.set_epoch_leader( ChannelProperty().peer_id) self._made_block_count += 1 elif len(block_builder.transactions) > 0: util.logger.spam( f"consensus len(block_builder.transactions) > 0") if last_unconfirmed_block: if (len(last_unconfirmed_block.body.transactions) > 0 or last_unconfirmed_block.header.complained) or ( len(last_unconfirmed_block.body.transactions) == 0 and last_unconfirmed_block.header.peer_id.hex_hx() != ChannelProperty().peer_id): vote = self._block_manager.candidate_blocks.get_vote( last_unconfirmed_block.header.hash) vote_result = vote.get_result( last_unconfirmed_block.header.hash.hex(), conf.VOTING_RATIO) if not vote_result: return self.__block_generation_timer.call() self.__add_block(last_unconfirmed_block, vote) next_leader = last_unconfirmed_block.header.next_leader else: if (last_unconfirmed_block) and ( len(last_unconfirmed_block.body.transactions) > 0 or last_unconfirmed_block.header.complained): vote = self._block_manager.candidate_blocks.get_vote( last_unconfirmed_block.header.hash) vote_result = vote.get_result( last_unconfirmed_block.header.hash.hex(), conf.VOTING_RATIO) if not vote_result: return self.__block_generation_timer.call() self.__add_block(last_unconfirmed_block, vote) peer_manager = ObjectManager().channel_service.peer_manager next_leader = ExternalAddress.fromhex( peer_manager.get_next_leader_peer( current_leader_peer_id=ChannelProperty().peer_id). peer_id) else: return self.__block_generation_timer.call() last_block = self._blockchain.last_block block_builder.height = last_block.header.height + 1 block_builder.prev_hash = last_block.header.hash block_builder.next_leader = next_leader block_builder.peer_private_key = ObjectManager( ).channel_service.peer_auth.private_key block_builder.confirm_prev_block = vote_result or ( self._made_block_count > 0) candidate_block = block_builder.build() candidate_block, invoke_results = ObjectManager( ).channel_service.score_invoke(candidate_block) self._block_manager.set_invoke_results( candidate_block.header.hash.hex(), invoke_results) util.logger.spam(f"candidate block : {candidate_block.header}") block_verifier = BlockVerifier.new(candidate_block.header.version, self._blockchain.tx_versioner) block_verifier.verify(candidate_block, self._blockchain.last_block, self._blockchain) self._block_manager.vote_unconfirmed_block( candidate_block.header.hash, True) self._block_manager.candidate_blocks.add_block(candidate_block) self._blockchain.last_unconfirmed_block = candidate_block broadcast_func = partial( self._block_manager.broadcast_send_unconfirmed_block, candidate_block) # TODO Temporary ignore below line for developing leader complain self.__start_broadcast_send_unconfirmed_block_timer(broadcast_func) if len(block_builder.transactions) == 0 and not conf.ALLOW_MAKE_EMPTY_BLOCK and \ next_leader.hex_hx() != ChannelProperty().peer_id: util.logger.spam(f"-------------------turn_to_peer " f"next_leader({next_leader.hex_hx()}) " f"peer_id({ChannelProperty().peer_id})") await ObjectManager().channel_service.reset_leader( next_leader.hex_hx()) else: self.__block_generation_timer.call()
async def consensus(self): util.logger.debug( f"-------------------consensus " f"candidate_blocks({len(self._block_manager.candidate_blocks.blocks)})" ) async with self.__lock: if self._block_manager.epoch.leader_id != ChannelProperty( ).peer_id: util.logger.warning( f"This peer is not leader. epoch leader={self._block_manager.epoch.leader_id}" ) return self._vote_queue = asyncio.Queue(loop=self._loop) complained_result = self._block_manager.epoch.complained_result block_builder = self._block_manager.epoch.makeup_block( complained_result) vote_result = None last_unconfirmed_block = self._blockchain.last_unconfirmed_block next_leader = ExternalAddress.fromhex(ChannelProperty().peer_id) if complained_result: util.logger.spam("consensus block_builder.complained") """ confirm_info = self._blockchain.find_confirm_info_by_hash(self._blockchain.last_block.header.hash) if not confirm_info and self._blockchain.last_block.header.height > 0: util.logger.spam("Can't make a block as a leader, this peer will be complained too.") return """ self._made_block_count += 1 elif len(block_builder.transactions) > 0: util.logger.spam( f"consensus len(block_builder.transactions) > 0") if last_unconfirmed_block: if (len(last_unconfirmed_block.body.transactions) > 0 or last_unconfirmed_block.header.complained) or ( len(last_unconfirmed_block.body.transactions) == 0 and last_unconfirmed_block.header.peer_id.hex_hx() != ChannelProperty().peer_id): vote = self._block_manager.candidate_blocks.get_vote( last_unconfirmed_block.header.hash) vote_result = await self._wait_for_voting( last_unconfirmed_block) if not vote_result: return self.__block_generation_timer.call() self.__add_block(last_unconfirmed_block, vote) self._block_manager.epoch = Epoch.new_epoch( ChannelProperty().peer_id) next_leader = last_unconfirmed_block.header.next_leader else: if (last_unconfirmed_block) and ( len(last_unconfirmed_block.body.transactions) > 0 or last_unconfirmed_block.header.complained): vote = self._block_manager.candidate_blocks.get_vote( last_unconfirmed_block.header.hash) vote_result = await self._wait_for_voting( last_unconfirmed_block) if not vote_result: return self.__block_generation_timer.call() self.__add_block(last_unconfirmed_block, vote) peer_manager = ObjectManager().channel_service.peer_manager next_leader = ExternalAddress.fromhex( peer_manager.get_next_leader_peer( current_leader_peer_id=ChannelProperty().peer_id). peer_id) else: return self.__block_generation_timer.call() candidate_block = self.__build_candidate_block( block_builder, next_leader, vote_result) candidate_block, invoke_results = ObjectManager( ).channel_service.score_invoke(candidate_block) self._block_manager.set_invoke_results( candidate_block.header.hash.hex(), invoke_results) util.logger.spam(f"candidate block : {candidate_block.header}") self._block_manager.vote_unconfirmed_block( candidate_block.header.hash, True) self._block_manager.candidate_blocks.add_block(candidate_block) self._blockchain.last_unconfirmed_block = candidate_block broadcast_func = partial( self._block_manager.broadcast_send_unconfirmed_block, candidate_block) self.__start_broadcast_send_unconfirmed_block_timer(broadcast_func) if await self._wait_for_voting(candidate_block) is None: return if len(candidate_block.body.transactions) == 0 and not conf.ALLOW_MAKE_EMPTY_BLOCK and \ next_leader.hex_hx() != ChannelProperty().peer_id: util.logger.spam(f"-------------------turn_to_peer " f"next_leader({next_leader.hex_hx()}) " f"peer_id({ChannelProperty().peer_id})") ObjectManager().channel_service.reset_leader( next_leader.hex_hx()) else: self._block_manager.epoch = Epoch.new_epoch( next_leader.hex_hx()) if not conf.ALLOW_MAKE_EMPTY_BLOCK: self.__block_generation_timer.call_instantly() else: self.__block_generation_timer.call()
def consensus(self): # broadcasting 한 블럭이 검증이 끝났는지 확인한다. confirmed_block = None try: confirmed_block = self._candidate_blocks.get_confirmed_block() except candidate_blocks.NoExistBlock as e: logging.error(e) except candidate_blocks.NotCompleteValidation as e: # try re count voters logging.warning( f"This block need more validation vote from Peers block hash({str(e.block.block_hash)})" ) self._blockmanager.broadcast_audience_set() if util.diff_in_seconds( e.block.time_stamp) > conf.BLOCK_VOTE_TIMEOUT: logging.warning("Time Outed Block: " + str(util.diff_in_seconds(e.block.time_stamp))) self._candidate_blocks.remove_broken_block(e.block.block_hash) else: peer_service = ObjectManager().peer_service if peer_service is not None: peer_service.reset_voter_count() self._candidate_blocks.reset_voter_count( str(e.block.block_hash)) time.sleep(conf.INTERVAL_WAIT_PEER_VOTE) except candidate_blocks.InvalidatedBlock as e: # 실패한 투표에 대한 처리 logging.error("InvalidatedBlock!! hash: " + str(e.block.block_hash)) logging.debug("InvalidatedBlock!! prev_hash: " + str(e.block.prev_block_hash)) # 현재 블록은 데이터가 있나? logging.debug("This block status: " + str(self._block.confirmed_tx_len)) self.__throw_out_block(e.block) # 검증이 끝난 블럭이 있으면 if confirmed_block is not None: logging.info("Block Validation is Complete hash: " + confirmed_block.block_hash) # 현재 블럭에 이전 투표에 대한 기록을 갱신한다. self._block.prev_block_confirm = True # 검증이 끝나면 BlockChain 에 해당 block 의 block_hash 로 등록 완료 confirmed_block.block_status = BlockStatus.confirmed self._blockmanager.add_block(confirmed_block) # 새로운 블럭의 broadcast 를 위해 current_vote_block_hash 를 리셋한다. self._current_vote_block_hash = "" # logging.debug("current_vote_block_hash: " + current_vote_block_hash) # BlockChain 으로 부터 hash 를 받은 하나의 block 만 검증을 위해 broadcast 되어야 한다. # 하나의 block 이 검증 성공 또는 실패 시 current_vote_block_hash 는 "" 로 재설정 한다. if self._current_vote_block_hash == "": # block 에 수집된 tx 가 있으면 if self._block is not None and self._block.confirmed_tx_len > 0: # 검증 받을 블록의 hash 를 생성하고 후보로 등록한다. logging.debug("add unconfirmed block to candidate blocks") self._block.generate_block( self._candidate_blocks.get_last_block(self._blockchain)) self._block.sign(ObjectManager().channel_service.peer_auth) self._candidate_blocks.add_unconfirmed_block(self._block) # logging.warning("blockchain.last_block_hash: " + self._blockchain.last_block.block_hash) # logging.warning("block.block_hash: " + self._block.block_hash) # logging.warning("block.prev_block_hash: " + self._block.prev_block_hash) # 새로운 Block 을 생성하여 다음 tx 을 수집한다. self._gen_block() # 다음 검증 후보 블럭이 있는지 확인한다. candidate_block = self._candidate_blocks.get_candidate_block() peer_manager = ObjectManager().channel_service.peer_manager if candidate_block is not None: # 있으면 해당 블럭을 broadcast 하여 Peer 에게 검증을 요청한다. self._current_vote_block_hash = candidate_block.block_hash logging.info("candidate block hash: " + self._current_vote_block_hash) candidate_block.next_leader_peer = peer_manager.get_next_leader_peer( ).peer_id self._blockmanager.broadcast_send_unconfirmed_block( candidate_block) return elif self._block is not None and \ (self._block.prev_block_confirm is True) and \ (self._block.confirmed_tx_len == 0): logging.debug( "broadcast voting block (has no tx but has a vote result)") # 검증할 후보 블럭이 없으면서 이전 블럭이 unconfirmed block 이면 투표가 담긴 빈 블럭을 전송한다. self._block.prev_block_hash = confirmed_block.block_hash self._block.block_type = BlockType.vote self.made_block_count -= 1 logging.debug(f"made_block_count({self.made_block_count})") self._block.next_leader_peer = peer_manager.get_next_leader_peer( ).peer_id self._blockmanager.broadcast_send_unconfirmed_block( self._block) # 전송한 빈블럭을 대체한다. if self.made_block_count < conf.LEADER_BLOCK_CREATION_LIMIT: # or not self._txQueue.empty(): self._gen_block() else: self._stop_gen_block() peer_service.rotate_next_leader(self._channel_name) self._makeup_block() time.sleep(conf.SLEEP_SECONDS_IN_SERVICE_LOOP)