def __block_request_by_citizen(self, block_height): rs_client = ObjectManager().channel_service.rs_client get_block_result = rs_client.call( RestMethod.GetBlockByHeight, RestMethod.GetBlockByHeight.value.params(height=str(block_height))) last_block = rs_client.call(RestMethod.GetLastBlock) if not last_block: raise exception.InvalidBlockSyncTarget( "The Radiostation may not be ready. It will retry after a while." ) max_height = self.blockchain.block_versioner.get_height(last_block) block_version = self.blockchain.block_versioner.get_version( block_height) block_serializer = BlockSerializer.new(block_version, self.blockchain.tx_versioner) block = block_serializer.deserialize(get_block_result['block']) votes_dumped: str = get_block_result.get('confirm_info', '') try: votes_serialized = json.loads(votes_dumped) version = self.blockchain.block_versioner.get_version(block_height) votes = Votes.get_block_votes_class(version).deserialize_votes( votes_serialized) except json.JSONDecodeError: votes = votes_dumped return block, max_height, -1, votes, message_code.Response.success
def __block_request_by_voter(self, block_height, peer_stub): response = peer_stub.BlockSync( loopchain_pb2.BlockSyncRequest(block_height=block_height, channel=self.__channel_name), conf.GRPC_TIMEOUT) if response.response_code == message_code.Response.fail_no_confirm_info: raise NoConfirmInfo( f"The peer has not confirm_info of the block by height({block_height})." ) else: try: block = self.blockchain.block_loads(response.block) except Exception as e: traceback.print_exc() raise exception.BlockError( f"Received block is invalid: original exception={e}") votes_dumped: bytes = response.confirm_info try: votes_serialized = json.loads(votes_dumped) version = self.blockchain.block_versioner.get_version( block_height) votes = Votes.get_block_votes_class(version).deserialize_votes( votes_serialized) except json.JSONDecodeError: votes = votes_dumped return block, response.max_block_height, response.unconfirmed_block_height, votes, response.response_code
def _makeup_new_block(self, block_version, complain_votes, block_hash): self._blockchain.last_unconfirmed_block = None dumped_votes = self._blockchain.find_confirm_info_by_hash(block_hash) if block_version == '0.1a': votes = dumped_votes else: votes_class = Votes.get_block_votes_class(block_version) votes = votes_class.deserialize_votes( json.loads(dumped_votes.decode('utf-8'))) return self._block_manager.epoch.makeup_block(complain_votes, votes)
def add_unconfirmed_block(self, unconfirmed_block: Block, round_: int): """ :param unconfirmed_block: :param round_: :return: """ self.__validate_epoch_of_unconfirmed_block(unconfirmed_block, round_) self.__validate_duplication_of_unconfirmed_block(unconfirmed_block) last_unconfirmed_block: Block = self.blockchain.last_unconfirmed_block # TODO After the v0.4 update, remove this version parsing. if parse_version( unconfirmed_block.header.version) >= parse_version("0.4"): ratio = conf.VOTING_RATIO else: ratio = conf.LEADER_COMPLAIN_RATIO if unconfirmed_block.header.reps_hash: reps = self.blockchain.find_preps_addresses_by_roothash( unconfirmed_block.header.reps_hash) version = self.blockchain.block_versioner.get_version( unconfirmed_block.header.height) leader_votes = Votes.get_leader_votes_class(version)( reps, ratio, unconfirmed_block.header.height, None, unconfirmed_block.body.leader_votes) need_to_confirm = leader_votes.get_result() is None elif unconfirmed_block.body.confirm_prev_block: need_to_confirm = True else: need_to_confirm = False try: if need_to_confirm: self.blockchain.confirm_prev_block(unconfirmed_block) if unconfirmed_block.header.is_unrecorded: self.blockchain.last_unconfirmed_block = None raise UnrecordedBlock("It's an unnecessary block to vote.") elif last_unconfirmed_block is None: if self.blockchain.last_block.header.hash != unconfirmed_block.header.prev_hash: raise BlockchainError( f"last block is not previous block. block={unconfirmed_block}" ) self.blockchain.last_unconfirmed_block = unconfirmed_block except BlockchainError as e: util.logger.warning( f"BlockchainError while confirm_block({e}), retry block_height_sync" ) self.__channel_service.state_machine.block_sync() raise InvalidUnconfirmedBlock(e)
def __add_block_by_sync(self, block_, confirm_info=None): util.logger.debug( f"__add_block_by_sync :: height({block_.header.height}) hash({block_.header.hash})" ) block_version = self.blockchain.block_versioner.get_version( block_.header.height) block_verifier = BlockVerifier.new(block_version, self.blockchain.tx_versioner, raise_exceptions=False) block_verifier.invoke_func = self.blockchain.get_invoke_func( block_.header.height) reps_getter = self.blockchain.find_preps_addresses_by_roothash block_verifier.verify_loosely(block_, self.blockchain.last_block, self.blockchain, reps_getter=reps_getter) need_to_write_tx_info, need_to_score_invoke = True, True for exc in block_verifier.exceptions: if isinstance(exc, TransactionDuplicatedHashError): need_to_write_tx_info = False if isinstance(exc, ScoreInvokeError) and not need_to_write_tx_info: need_to_score_invoke = False exc = next((exc for exc in block_verifier.exceptions if not isinstance(exc, TransactionDuplicatedHashError)), None) if exc: if isinstance(exc, ScoreInvokeError) and not need_to_score_invoke: pass else: raise exc if parse_version(block_.header.version) >= parse_version("0.3"): reps = reps_getter(block_.header.reps_hash) round_ = next(vote for vote in confirm_info if vote).round votes = Votes.get_block_votes_class(block_.header.version)( reps, conf.VOTING_RATIO, block_.header.height, round_, block_.header.hash, confirm_info) votes.verify() return self.blockchain.add_block(block_, confirm_info, need_to_write_tx_info, need_to_score_invoke)
async def node_ws_PublishNewBlock(self, **kwargs): block_dict, votes_dumped = kwargs.get('block'), kwargs.get( 'confirm_info', '') try: votes_serialized = json.loads(votes_dumped) vote = Votes.get_block_votes_class( block_dict["version"]).deserialize_votes(votes_serialized) except json.JSONDecodeError: vote = votes_dumped blockchain = ObjectManager().channel_service.block_manager.blockchain new_block_height = blockchain.block_versioner.get_height(block_dict) if new_block_height > blockchain.block_height: block_version = blockchain.block_versioner.get_version( new_block_height) block_serializer = BlockSerializer.new(block_version, blockchain.tx_versioner) confirmed_block = block_serializer.deserialize(block_dict) block_verifier = BlockVerifier.new(block_version, blockchain.tx_versioner) block_verifier.invoke_func = blockchain.score_invoke reps_getter = blockchain.find_preps_addresses_by_roothash try: block_verifier.verify( confirmed_block, blockchain.last_block, blockchain, generator=blockchain.get_expected_generator( confirmed_block), reps_getter=reps_getter) except Exception as e: self._exception = AnnounceNewBlockError( f"error: {type(e)}, message: {str(e)}") else: logging.debug( f"add_confirmed_block height({confirmed_block.header.height}), " f"hash({confirmed_block.header.hash.hex()}), votes_dumped({votes_dumped})" ) ObjectManager( ).channel_service.block_manager.add_confirmed_block( confirmed_block=confirmed_block, confirm_info=vote) finally: ObjectManager().channel_service.reset_block_monitoring_timer()
async def _block_request_by_citizen( self, block_height: int, request_client: RestClient = None) -> RequestResult: max_height = self._max_height if request_client is None: request_client = self._rest_client get_block_result = await request_client.call_async( RestMethod.GetBlockByHeight, RestMethod.GetBlockByHeight.value.params(height=str(block_height))) response_code = get_block_result["response_code"] if response_code != message_code.Response.success: exc = exception.MessageCodeError( f"getBlockByHeight failed height={block_height}") exc.message_code = response_code raise exc if max_height == block_height: last_block_height = self._get_last_block_height() if last_block_height > max_height: max_height = last_block_height block_version = self._blockchain.block_versioner.get_version( block_height) block_serializer = BlockSerializer.new(block_version, self._blockchain.tx_versioner) block = block_serializer.deserialize(get_block_result['block']) votes_dumped: str = get_block_result.get('confirm_info', '') try: votes_serialized = json.loads(votes_dumped) version = self._blockchain.block_versioner.get_version( block_height) votes = Votes.get_block_votes_class(version).deserialize_votes( votes_serialized) except json.JSONDecodeError: votes = votes_dumped return block, max_height, -1, votes, message_code.Response.success
def _add_block_by_sync(self, block_, confirm_info: Optional[List] = None): """ TODO : If possible, change _add_block_by_sync to coroutine :param block_: :param confirm_info: :return: """ utils.logger.debug( f"height({block_.header.height}) hash({block_.header.hash})") block_version = self._blockchain.block_versioner.get_version( block_.header.height) block_verifier = BlockVerifier.new(block_version, self._blockchain.tx_versioner, raise_exceptions=False) block_verifier.invoke_func = self._blockchain.get_invoke_func( block_.header.height) reps_getter = self._blockchain.find_preps_addresses_by_roothash block_verifier.verify_loosely(block_, self._blockchain.last_block, self._blockchain, reps_getter=reps_getter) need_to_write_tx_info, need_to_score_invoke = True, True for exc in block_verifier.exceptions: if isinstance(exc, exception.TransactionDuplicatedHashError): need_to_write_tx_info = False if isinstance( exc, exception.ScoreInvokeError) and not need_to_write_tx_info: need_to_score_invoke = False exc = next( (exc for exc in block_verifier.exceptions if not isinstance(exc, exception.TransactionDuplicatedHashError)), None) if exc: if isinstance( exc, exception.ScoreInvokeError) and not need_to_score_invoke: pass else: raise exc try: if parse_version(block_.header.version) >= parse_version("0.3"): reps = reps_getter(block_.header.reps_hash) round_ = next(vote for vote in confirm_info if vote).round votes = Votes.get_block_votes_class(block_.header.version)( reps, conf.VOTING_RATIO, block_.header.height, round_, block_.header.hash, confirm_info) votes.verify() except StopIteration: # I think is is unconfirmed_block because no confirm_info. # should be fix prevent unconfirmed block later. utils.logger.warning(f"block: {block_}") raise Exception(f"confirm_info is empty: {confirm_info}") self._blockchain.add_block(block_, confirm_info, need_to_write_tx_info, need_to_score_invoke)
async def __get_votes(self, block_hash: Hash32): try: prev_votes = self._block_manager.candidate_blocks.get_votes( block_hash, self._block_manager.epoch.round) except KeyError as e: util.logger.debug(f"There is no block in candidates list: {e!r}") prev_votes = None if prev_votes: last_unconfirmed_block = self._blockchain.last_unconfirmed_block try: if last_unconfirmed_block is None: warning_msg = f"There is prev_votes({prev_votes}). But I have no last_unconfirmed_block." if self._blockchain.find_block_by_hash32(block_hash): warning_msg += "\nBut already added block so no longer have to wait for the vote." # TODO An analysis of the cause of this situation is necessary. util.logger.notice(warning_msg) self._block_manager.candidate_blocks.remove_block( block_hash) else: util.logger.warning(warning_msg) return None self.__check_timeout(last_unconfirmed_block) if not prev_votes.is_completed(): self.__broadcast_block(last_unconfirmed_block) if await self._wait_for_voting(last_unconfirmed_block ) is None: return None prev_votes_list = prev_votes.votes except TimeoutError: util.logger.warning(f"Timeout block of hash : {block_hash}") if self._block_manager.epoch.complained_result: self._blockchain.last_unconfirmed_block = None self.stop_broadcast_send_unconfirmed_block_timer() ObjectManager().channel_service.state_machine.switch_role() return None except NotEnoughVotes: if last_unconfirmed_block: util.logger.warning( f"The last unconfirmed block has not enough votes. {block_hash}" ) return None else: util.exit_and_msg( f"The block that has not enough votes added to the blockchain." ) else: prev_votes_dumped = self._blockchain.find_confirm_info_by_hash( block_hash) try: prev_votes_serialized = json.loads(prev_votes_dumped) except json.JSONDecodeError as e: # handle exception for old votes util.logger.debug(f"{e!r}") prev_votes_list = [] except TypeError as e: # handle exception for not existing (NoneType) votes util.logger.debug(f"{e!r}") prev_votes_list = [] else: version = self._blockchain.block_versioner.get_version( self._block_manager.epoch.height) prev_votes_list = Votes.get_block_votes_class( version).deserialize_votes(prev_votes_serialized) return prev_votes_list