예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
    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)
예제 #4
0
    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)
예제 #5
0
    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)
예제 #6
0
    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()
예제 #7
0
    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
예제 #8
0
    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)
예제 #9
0
    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