예제 #1
0
    def test_valid_timestamp(self):
        """Test for timestamp buffer in block verifier"""
        def block_maker(timestamp: int, height: int = 0, prev_hash=None):
            """Make dummy block"""
            tx_versioner = TransactionVersioner()

            dummy_receipts = {}
            block_builder = BlockBuilder.new("0.1a", tx_versioner)

            for i in range(1000):
                tx_builder = TransactionBuilder.new("0x3", None, tx_versioner)
                tx_builder.signer = test_signer
                tx_builder.to_address = ExternalAddress.new()
                tx_builder.step_limit = random.randint(0, 10000)
                tx_builder.value = random.randint(0, 10000)
                tx_builder.nid = 2
                tx = tx_builder.build()

                tx_serializer = TransactionSerializer.new(tx.version, tx.type(), tx_versioner)
                block_builder.transactions[tx.hash] = tx
                dummy_receipts[tx.hash.hex()] = {
                    "dummy_receipt": "dummy",
                    "tx_dumped": tx_serializer.to_full_data(tx)
                }

            block_builder.signer = test_signer
            block_builder.prev_hash = prev_hash
            block_builder.height = height
            block_builder.state_hash = Hash32(bytes(Hash32.size))
            block_builder.receipts = dummy_receipts
            block_builder.reps = [ExternalAddress.fromhex_address(test_signer.address)]
            block_builder.peer_id = ExternalAddress.fromhex(test_signer.address)
            block_builder.next_leader = ExternalAddress.fromhex(test_signer.address)
            block_builder.fixed_timestamp = timestamp

            b = block_builder.build()
            assert b.header.timestamp == timestamp

            return b

        test_signer = Signer.from_prikey(os.urandom(32))

        first_block = block_maker(height=0, timestamp=utils.get_time_stamp())
        second_block = block_maker(height=1, timestamp=utils.get_time_stamp() + 5, prev_hash=first_block.header.hash)
        third_block_from_far_future = block_maker(height=2, prev_hash=second_block.header.hash,
                                                  timestamp=utils.get_time_stamp() + conf.TIMESTAMP_BUFFER_IN_VERIFIER + 5_000_000)

        block_verifier = BlockVerifier.new("0.1a", TransactionVersioner())
        leader = first_block.header.peer_id
        reps = [ExternalAddress.fromhex_address(test_signer.address)]
        print("*---Normal time range")
        block_verifier.verify(block=second_block, prev_block=first_block,
                              blockchain=None, generator=leader, reps=reps)

        print("*---Abnormal time range")
        with self.assertRaises(Exception):
            block_verifier.verify(block=third_block_from_far_future, prev_block=second_block,
                                  blockchain=None, generator=leader, reps=reps)
예제 #2
0
    def generate_block(self, prev_block=None):
        """블럭을 생성한다 \n
        이전블럭을 입력하지 않으면, 제네시스 블럭으로 생성됨
        이전블럭을 입력하면 링킹된 블럭으로 생성됨
        블럭 높이와 이전 블럭 hash, 현재블럭의 hash계산, 머클트리 계산을 실행함

        :param prev_block: 이전 블럭
        :returns: 생성된 블럭 해쉬 값
        """

        if prev_block is None:  # 제네시스 블럭일 경우
            # Genesis Block Data
            self.prev_block_hash = ""
            self.height = 0
            self.time_stamp = 0
        elif self.time_stamp == 0:  # 블럭생성이 시작되지 않았으면
            if self.prev_block_hash == "":
                self.prev_block_hash = prev_block.block_hash
                self.height = prev_block.height + 1
            self.time_stamp = util.get_time_stamp()  # ms단위

        # 트랜잭션이 있을 경우 머클트리 생성
        if len(self.confirmed_transaction_list) > 0:
            self.__calculate_merkle_tree_root_hash()
        self.block_hash = self.__generate_hash()

        return self.block_hash
예제 #3
0
    def verify_prev_block(self, block: 'Block', prev_block: 'Block'):
        if block.header.height != prev_block.header.height + 1:
            exception = BlockHeightMismatch(
                f"Block({block.header.height}, {block.header.hash.hex()}, "
                f"Height({block.header.height}), "
                f"Expected({prev_block.header.height + 1}).")
            self._handle_exception(exception)

        if block.header.prev_hash != prev_block.header.hash:
            exception = RuntimeError(
                f"Block({block.header.height}, {block.header.hash.hex()}, "
                f"PrevHash({block.header.prev_hash.hex()}), "
                f"Expected({prev_block.header.hash.hex()}).")
            self._handle_exception(exception)

        valid_max_timestamp = utils.get_time_stamp(
        ) + conf.TIMESTAMP_BUFFER_IN_VERIFIER
        if prev_block and not (prev_block.header.timestamp <
                               block.header.timestamp < valid_max_timestamp):
            exception = RuntimeError(
                f"Block({block.header.height}, {block.header.hash.hex()},"
                f"timestamp({block.header.timestamp} is invalid. "
                f"prev_block timestamp({prev_block.header.timestamp}), "
                f"current timestamp({utils.get_now_time_stamp()}")
            self._handle_exception(exception)
예제 #4
0
    def leader_complain(self):
        complained_leader_id, new_leader_id = self.get_leader_ids_for_complaint(
        )
        version = self.blockchain.block_versioner.get_version(
            self.epoch.height)
        leader_vote = Vote.get_leader_vote_class(version).new(
            signer=ChannelProperty().peer_auth,
            block_height=self.epoch.height,
            round_=self.epoch.round,
            old_leader=ExternalAddress.fromhex_address(complained_leader_id),
            new_leader=ExternalAddress.fromhex_address(new_leader_id),
            timestamp=util.get_time_stamp())
        util.logger.info(
            f"LeaderVote : old_leader({complained_leader_id}), new_leader({new_leader_id}), round({self.epoch.round})"
        )
        self.add_complain(leader_vote)

        leader_vote_serialized = leader_vote.serialize()
        leader_vote_dumped = json.dumps(leader_vote_serialized)
        request = loopchain_pb2.ComplainLeaderRequest(
            complain_vote=leader_vote_dumped, channel=self.channel_name)

        util.logger.debug(f"leader complain "
                          f"complained_leader_id({complained_leader_id}), "
                          f"new_leader_id({new_leader_id})")

        reps_hash = self.blockchain.get_next_reps_hash_by_header(
            self.blockchain.last_block.header)
        self.__channel_service.broadcast_scheduler.schedule_broadcast(
            "ComplainLeader", request, reps_hash=reps_hash)
예제 #5
0
    def __send_fail_leader_vote(self, leader_vote: LeaderVote):
        version = self.blockchain.block_versioner.get_version(
            leader_vote.block_height)
        fail_vote = Vote.get_leader_vote_class(version).new(
            signer=ChannelProperty().peer_auth,
            block_height=leader_vote.block_height,
            round_=leader_vote.round,
            old_leader=leader_vote.old_leader,
            new_leader=ExternalAddress.empty(),
            timestamp=util.get_time_stamp())

        fail_vote_dumped = json.dumps(fail_vote.serialize())
        request = loopchain_pb2.ComplainLeaderRequest(
            complain_vote=fail_vote_dumped, channel=self.channel_name)

        reps_hash = self.blockchain.last_block.header.revealed_next_reps_hash or ChannelProperty(
        ).crep_root_hash
        rep_id = leader_vote.rep.hex_hx()
        target = self.blockchain.find_preps_targets_by_roothash(
            reps_hash)[rep_id]

        util.logger.debug(f"fail leader complain "
                          f"complained_leader_id({leader_vote.old_leader}), "
                          f"new_leader_id({ExternalAddress.empty()}),"
                          f"round({leader_vote.round}),"
                          f"target({target})")

        self.__channel_service.broadcast_scheduler.schedule_send_failed_leader_complain(
            "ComplainLeader", request, target=target)
예제 #6
0
    def verify_common(self, block: 'Block', prev_block: 'Block', generator: 'ExternalAddress'=None, **kwargs):
        header: BlockHeader = block.header

        if header.timestamp is None:
            exception = RuntimeError(f"Block({header.height}, {header.hash.hex()} does not have timestamp.")
            self._handle_exception(exception)

        if header.height > 0 and header.prev_hash is None:
            exception = RuntimeError(f"Block({header.height}, {header.hash.hex()} does not have prev_hash.")
            self._handle_exception(exception)

        if prev_block and not (prev_block.header.timestamp < header.timestamp < utils.get_time_stamp()):
            exception = RuntimeError(f"Block({header.height}, {header.hash.hex()} timestamp({header.timestamp} is invalid. "
                                     f"prev_block timestamp({prev_block.header.timestamp}), "
                                     f"current timestamp({utils.get_now_time_stamp()}")
            self._handle_exception(exception)

        self.verify_version(block)

        if block.header.height > 0:
            self.verify_signature(block)

        if prev_block:
            self.verify_prev_block(block, prev_block)

        return self._verify_common(block, prev_block, generator, **kwargs)
예제 #7
0
    def generate_block(self, prev_block=None):
        """블럭을 생성한다 \n
        이전블럭을 입력하지 않으면, 제네시스 블럭으로 생성됨
        이전블럭을 입력하면 링킹된 블럭으로 생성됨
        블럭 높이와 이전 블럭 hash, 현재블럭의 hash계산, 머클트리 계산을 실행함

        :param prev_block: 이전 블럭
        :returns: 생성된 블럭 해쉬 값
        """
        try:
            util.logger.spam(
                f"ENGINE-303 generate_block prev_block: {prev_block.height} {prev_block.block_hash}"
            )
        except Exception:
            pass

        if prev_block is None:
            # Genesis Block Data
            self.prev_block_hash = ""
            self.height = 0
            self.time_stamp = 0
        elif self.time_stamp == 0:
            if self.prev_block_hash == "":
                self.prev_block_hash = prev_block.block_hash
                self.height = prev_block.height + 1
            self.time_stamp = util.get_time_stamp()

        # 트랜잭션이 있을 경우 머클트리 생성
        if self.confirmed_tx_len > 0:
            Block.__calculate_merkle_tree_root_hash(self)
        self.block_hash = Block.__generate_hash(self)

        return self.block_hash
예제 #8
0
    def vote_unconfirmed_block(self, block: Block, round_: int, is_validated):
        util.logger.debug(
            f"vote_unconfirmed_block() ({block.header.height}/{block.header.hash}/{is_validated})"
        )
        vote = Vote.get_block_vote_class(block.header.version).new(
            signer=ChannelProperty().peer_auth,
            block_height=block.header.height,
            round_=round_,
            block_hash=block.header.hash if is_validated else Hash32.empty(),
            timestamp=util.get_time_stamp())
        self.candidate_blocks.add_vote(vote)

        vote_serialized = vote.serialize()
        vote_dumped = json.dumps(vote_serialized)
        block_vote = loopchain_pb2.BlockVote(vote=vote_dumped,
                                             channel=ChannelProperty().name)

        target_reps_hash = block.header.reps_hash
        if not target_reps_hash:
            target_reps_hash = self.__channel_service.peer_manager.crep_root_hash

        self.__channel_service.broadcast_scheduler.schedule_broadcast(
            "VoteUnconfirmedBlock", block_vote, reps_hash=target_reps_hash)

        return vote
예제 #9
0
    def __init__(self, block_hash: Hash32, block_height: int):
        """Recommend use factory methods(from_*) instead direct this.

        """
        self.votes: dict[int, BlockVotes] = {}
        self.votes_buffer: List[BlockVote] = []
        self.start_time = util.get_time_stamp()  # timestamp
        self.hash = block_hash
        self.height = block_height
        self.__block = None
        self._reps: List[ExternalAddress] = []
예제 #10
0
    def __init__(self, block_hash: Hash32):
        """Recommend use factory methods(from_*) instead direct this.

        """
        if ObjectManager().channel_service:
            audience = ObjectManager().channel_service.peer_manager
        else:
            audience = None

        self.start_time = util.get_time_stamp()  # timestamp
        self.hash = block_hash
        self.vote = Vote(block_hash.hex(), audience)
        self.__block = None
예제 #11
0
    def test_block_v0_4(self):
        block_version = "0.4"
        test_signer = Signer.from_prikey(os.urandom(32))
        tx_versioner = TransactionVersioner()

        dummy_receipts = {}
        block_builder = BlockBuilder.new(block_version, tx_versioner)
        for i in range(5):
            tx_builder = TransactionBuilder.new("0x3", None, tx_versioner)
            tx_builder.signer = test_signer
            tx_builder.to_address = ExternalAddress.new()
            tx_builder.step_limit = random.randint(0, 10000)
            tx_builder.value = random.randint(0, 10000)
            tx_builder.nid = 2
            tx = tx_builder.build()

            tx_serializer = TransactionSerializer.new(tx.version, tx.type(),
                                                      tx_versioner)
            block_builder.transactions[tx.hash] = tx
            dummy_receipts[tx.hash.hex()] = {
                "dummy_receipt": "dummy",
                "tx_dumped": tx_serializer.to_full_data(tx)
            }

        next_leader = ExternalAddress.fromhex(
            "hx00112233445566778899aabbccddeeff00112233")

        block_builder.signer = test_signer
        block_builder.height = 0
        block_builder.prev_hash = Hash32(bytes(Hash32.size))
        block_builder.state_hash = Hash32(bytes(Hash32.size))
        block_builder.receipts = dummy_receipts
        block_builder.reps = [
            ExternalAddress.fromhex_address(test_signer.address)
        ]
        block_builder.next_leader = next_leader
        block_builder.next_reps = []

        vote = BlockVote.new(test_signer, utils.get_time_stamp(),
                             block_builder.height - 1, 0,
                             block_builder.prev_hash)
        votes = BlockVotes(block_builder.reps, conf.VOTING_RATIO,
                           block_builder.height - 1, 0,
                           block_builder.prev_hash)
        votes.add_vote(vote)
        block_builder.prev_votes = votes.votes

        block = block_builder.build()
        block_verifier = BlockVerifier.new(block_version, tx_versioner)

        block_verifier.invoke_func = lambda b, prev_b: (block, dummy_receipts)
        reps_getter = lambda _: block_builder.reps
        generator = ExternalAddress.fromhex_address(test_signer.address)
        block_verifier.verify(block,
                              None,
                              None,
                              generator=generator,
                              reps_getter=reps_getter)

        block_serializer = BlockSerializer.new(block_version, tx_versioner)
        block_serialized = block_serializer.serialize(block)
        logging.info(json.dumps(block_serialized, indent=4))
        block_deserialized = block_serializer.deserialize(block_serialized)
        logging.info(
            json.dumps(block_serializer.serialize(block_deserialized),
                       indent=4))

        assert block.header == block_deserialized.header
        assert block.body == block_deserialized.body

        tx_hashes = list(block.body.transactions)
        tx_index = random.randrange(0, len(tx_hashes))

        block_prover = BlockProver.new(block.header.version, tx_hashes,
                                       BlockProverType.Transaction)
        tx_proof = block_prover.get_proof(tx_index)
        assert block_prover.prove(tx_hashes[tx_index],
                                  block.header.transactions_hash, tx_proof)

        block_prover = BlockProver.new(block.header.version,
                                       block_builder.receipts,
                                       BlockProverType.Receipt)
        receipt_proof = block_prover.get_proof(tx_index)
        receipts_hash = block_prover.to_hash32(
            block_builder.receipts[tx_index])
        assert block_prover.prove(receipts_hash, block.header.receipts_hash,
                                  receipt_proof)