示例#1
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 = BlockVotes.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()
示例#2
0
    def _vote(self, unconfirmed_block: Block, round_: int):
        exc = None
        try:
            block_version = self.blockchain.block_versioner.get_version(unconfirmed_block.header.height)
            block_verifier = BlockVerifier.new(block_version, self.blockchain.tx_versioner)
            block_verifier.invoke_func = self.blockchain.score_invoke
            reps_getter = self.blockchain.find_preps_addresses_by_roothash

            util.logger.debug(f"unconfirmed_block.header({unconfirmed_block.header})")

            block_verifier.verify(unconfirmed_block,
                                  self.blockchain.last_block,
                                  self.blockchain,
                                  generator=self.blockchain.get_expected_generator(unconfirmed_block),
                                  reps_getter=reps_getter)
        except NotInReps as e:
            util.logger.debug(f"Not In Reps({e}) state({self.__channel_service.state_machine.state})")
        except BlockHeightMismatch as e:
            exc = e
            util.logger.warning(f"Don't vote to the block of unexpected height. {e!r}")
        except Exception as e:
            exc = e
            util.logger.exception(f"{e!r}")
        else:
            self.candidate_blocks.add_block(
                unconfirmed_block, self.blockchain.find_preps_addresses_by_header(unconfirmed_block.header))
        finally:
            if isinstance(exc, BlockHeightMismatch):
                return

            is_validated = exc is None
            vote = self.vote_unconfirmed_block(unconfirmed_block, round_, is_validated)
            if self.__channel_service.state_machine.state == "BlockGenerate" and self.consensus_algorithm:
                self.consensus_algorithm.vote(vote)
示例#3
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)
    def test_verify(self, plyvel_db, block_versioner, tx_versioner):
        """
        1. prepare plyvel db, block_versioner, tx_versioner
        2. pick block, transaction, vote, etc from db
        3. verify block, vote transaction, vote, etc...
        """

        # given db instance, block_versioner, tx_versioner

        block_key = plyvel_db.get(b'last_block_key')

        while True:
            # when get block from db
            block_dumped = plyvel_db.get(block_key)
            Logger.info(f"block_dump : {block_dumped}")
            block_serialized = json.loads(block_dumped)
            block_height = block_versioner.get_height(block_serialized)
            block_version = block_versioner.get_version(block_height)
            block_serializer = BlockSerializer.new(block_version, tx_versioner)
            block = block_serializer.deserialize(block_serialized)
            Logger.info(f"block_height : {block_height}, block_version : {block_version}")

            if block_height == 0:
                break

            # then block verify
            block_verifier = BlockVerifier.new(block_version, tx_versioner)
            block_verifier.verify_signature(block)

            # then vote verify
            if parse_version(block_version) >= parse_version("0.3"):
                Logger.info(f"leader_votes : {block.body.leader_votes}")
                for leader_vote in block.body.leader_votes:
                    if not leader_vote:
                        continue
                    leader_vote.verify()

                Logger.info(f"prev_votes : {block.body.prev_votes}")
                for block_vote in block.body.prev_votes:
                    if not block_vote:
                        continue
                    block_vote.verify()

            # then transaction verify
            for tx in block.body.transactions.values():
                tv = TransactionVerifier.new(tx.version, tx.type(), tx_versioner)
                tv.verify_signature(tx)

            Logger.info(f"prev_hash : {block.header.prev_hash}, {bytes(block.header.prev_hash)}")
            block_key = block.header.prev_hash.hex().encode("utf-8")
示例#5
0
    def verify_confirm_info(self, unconfirmed_block: Block):
        unconfirmed_header = unconfirmed_block.header
        my_height = self.blockchain.block_height
        if my_height < (unconfirmed_header.height - 2):
            raise ConfirmInfoInvalidNeedBlockSync(
                f"trigger block sync: my_height({my_height}), "
                f"unconfirmed_block.header.height({unconfirmed_header.height})"
            )

        is_rep = ObjectManager().channel_service.is_support_node_function(
            conf.NodeFunction.Vote)
        if is_rep and my_height == unconfirmed_header.height - 2 and not self.blockchain.last_unconfirmed_block:
            raise ConfirmInfoInvalidNeedBlockSync(
                f"trigger block sync: my_height({my_height}), "
                f"unconfirmed_block.header.height({unconfirmed_header.height})"
            )

        # a block is already added that same height unconfirmed_block height
        if my_height >= unconfirmed_header.height:
            raise ConfirmInfoInvalidAddedBlock(
                f"block is already added my_height({my_height}), "
                f"unconfirmed_block.header.height({unconfirmed_header.height})"
            )

        block_verifier = BlockVerifier.new(unconfirmed_header.version,
                                           self.blockchain.tx_versioner)
        prev_block = self.blockchain.get_prev_block(unconfirmed_block)
        reps_getter = self.blockchain.find_preps_addresses_by_roothash

        util.logger.spam(
            f"prev_block: {prev_block.header.hash if prev_block else None}")
        if not prev_block:
            raise NotReadyToConfirmInfo(
                "There is no prev block or not ready to confirm block (Maybe node is starting)"
            )

        try:
            if prev_block and prev_block.header.reps_hash and unconfirmed_header.height > 1:
                prev_reps = reps_getter(prev_block.header.reps_hash)
                block_verifier.verify_prev_votes(unconfirmed_block, prev_reps)
        except Exception as e:
            util.logger.warning(e)
            traceback.print_exc()
            raise ConfirmInfoInvalid(
                "Unconfirmed block has no valid confirm info for previous block"
            )
示例#6
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)
示例#7
0
    def __confirm_prev_block_by_sync(self, block_):
        prev_block = self.blockchain.last_unconfirmed_block
        confirm_info = block_.body.confirm_prev_block

        util.logger.debug(
            f"confirm_prev_block_by_sync :: height({prev_block.header.height})"
        )

        block_version = self.blockchain.block_versioner.get_version(
            prev_block.header.height)
        block_verifier = BlockVerifier.new(block_version,
                                           self.blockchain.tx_versioner)
        block_verifier.invoke_func = self.blockchain.get_invoke_func(
            prev_block.header.height)

        reps_getter = self.blockchain.find_preps_addresses_by_roothash
        block_verifier.verify_loosely(prev_block,
                                      self.blockchain.last_block,
                                      self.blockchain,
                                      reps_getter=reps_getter)
        return self.blockchain.add_block(prev_block, confirm_info)
示例#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
    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)