Ejemplo n.º 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 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)
Ejemplo n.º 2
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:
                # TODO vote 타임 아웃을 설정하고 타임 아웃 이내에 완성되지 않는 경우
                # 우선 해당 블럭은 버리는 것으로 임시 처리, 타임 아웃 블럭에 대한 정책 필요
                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_transaction_list.__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_transaction_list.__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().peer_service.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_service = ObjectManager().peer_service
            peer_manager = peer_service.channel_manager.get_peer_manager(
                self._channel_name)

            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

                # 생성된 블럭을 투표 요청하기 위해서 broadcast 한다.
                self._blockmanager.broadcast_send_unconfirmed_block(
                    candidate_block)

                # # turn off previous block's timer when a general peer received new block for generating block
                # peer_service.timer_service.stop_timer(candidate_block.prev_block_hash)
                #
                # # start timer after broadcasting unconfirmed block
                # # TODO: set appropriate callback function and parameters
                # timer = Timer(
                #     candidate_block.block_hash,
                #     conf.TIMEOUT_FOR_PEER_BLOCK_GENERATION,
                #     peer_service.timer_test_callback_function,
                #     ["test after broadcasting by consensus_lft"]
                # )
                # peer_service.timer_service.add_timer(candidate_block.block_hash, timer)

                # broadcast 를 요청했으면 다음 투표 block 이 있는지 계속 검사하기 위해 return 한다.
                return
            elif self._block is not None and \
                    (self._block.prev_block_confirm is True) and \
                    (self._block.confirmed_transaction_list.__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:
                    # TODO LEADER_BLOCK_CREATION_LIMIT 에서 무조건 리더가 변경된다. 잔여 tx 처리가 필요하다.
                    self._stop_gen_block()
                    peer_service.rotate_next_leader(self._channel_name)

        self._makeup_block()

        time.sleep(conf.SLEEP_SECONDS_IN_SERVICE_LOOP)