コード例 #1
0
    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
コード例 #2
0
    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()
コード例 #3
0
    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)
コード例 #4
0
    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()
コード例 #5
0
    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()
コード例 #6
0
    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()
コード例 #7
0
    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)