Exemple #1
0
    def test_tree(self):
        class Data(Serializable):
            FIELDS = [("value", uint32)]

            def __init__(self, value):
                self.value = value

        N = 64

        for n in range(N + 1):
            item_list = [Data(i) for i in range(n)]
            print(n)

            hash0 = calculate_merkle_root(item_list)
            hash1 = calculate_merkle_root1(item_list)
            self.assertEqual(hash0, hash1)
Exemple #2
0
    def validate_block(self, block, block_hash=None):
        if not self.db.contain_root_block_by_hash(
                block.header.hash_prev_block):
            raise ValueError("previous hash block mismatch")

        block_hash = self.validate_block_header(block.header, block_hash)

        # Check the merkle tree
        merkle_hash = calculate_merkle_root(block.minor_block_header_list)
        if merkle_hash != block.header.hash_merkle_root:
            raise ValueError("incorrect merkle root")

        # Check whether all minor blocks are ordered, validated (and linked to previous block)
        headers_map = dict()  # shard_id -> List[MinorBlockHeader]
        shard_id = (block.minor_block_header_list[0].branch.get_shard_id()
                    if block.minor_block_header_list else None)
        for m_header in block.minor_block_header_list:
            if not self.db.contain_minor_block_by_hash(m_header.get_hash()):
                raise ValueError("minor block is not validated. {}-{}".format(
                    m_header.branch.get_shard_id(), m_header.height))
            if m_header.create_time > block.header.create_time:
                raise ValueError(
                    "minor block create time is larger than root block {} > {}"
                    .format(m_header.create_time, block.header.create_time))
            if not self.__is_same_chain(
                    self.db.get_root_block_header_by_hash(
                        block.header.hash_prev_block),
                    self.db.get_root_block_header_by_hash(
                        m_header.hash_prev_root_block),
            ):
                raise ValueError(
                    "minor block's prev root block must be in the same chain")

            if m_header.branch.get_shard_id() < shard_id:
                raise ValueError("shard id must be ordered")
            elif m_header.branch.get_shard_id() > shard_id:
                shard_id = m_header.branch.get_shard_id()

            headers_map.setdefault(m_header.branch.get_shard_id(),
                                   []).append(m_header)
            # TODO: Add coinbase

        # check proof of progress
        shard_ids_to_check_proof_of_progress = self.env.quark_chain_config.get_initialized_shard_ids_before_root_height(
            block.header.height)
        for shard_id in shard_ids_to_check_proof_of_progress:
            if (len(headers_map.get(shard_id, [])) <
                    self.env.quark_chain_config.PROOF_OF_PROGRESS_BLOCKS):
                raise ValueError("fail to prove progress")

        # check minor block headers are linked
        prev_last_minor_block_header_list = self.db.get_root_block_last_minor_block_header_list(
            block.header.hash_prev_block)
        prev_header_map = dict()  # shard_id -> MinorBlockHeader or None
        for header in prev_last_minor_block_header_list:
            prev_header_map[header.branch.get_shard_id()] = header

        last_minor_block_header_list = []
        for shard_id, headers in headers_map.items():
            check(len(headers) > 0)

            last_minor_block_header_list.append(headers[-1])

            if shard_id not in shard_ids_to_check_proof_of_progress:
                raise ValueError(
                    "found minor block header in root block {} for uninitialized shard {}"
                    .format(block_hash.hex(), shard_id))
            prev_header_in_last_root_block = prev_header_map.get(
                shard_id, None)
            if not prev_header_in_last_root_block:
                pass
                # no header in previous root block then it must start with genesis block
                if headers[0].height != 0:
                    raise ValueError(
                        "genesis block height is not 0 for shard {} block hash {}"
                        .format(shard_id, headers[0].get_hash().hex()))
            else:
                headers = [prev_header_in_last_root_block] + headers
            for i in range(len(headers) - 1):
                if headers[i +
                           1].hash_prev_minor_block != headers[i].get_hash():
                    raise ValueError(
                        "minor block {} does not link to previous block {}".
                        format(headers[i + 1].get_hash(),
                               headers[i].get_hash()))

        return block_hash, last_minor_block_header_list
Exemple #3
0
    def validate_block(self, block):
        """Raise on validation errors """
        if block.header.version != 0:
            raise ValueError("incorrect root block version")

        if not self.db.contain_root_block_by_hash(
                block.header.hash_prev_block):
            raise ValueError("previous hash block mismatch")

        block_hash = self.validate_block_header(block.header)

        if (len(block.tracking_data) >
                self.env.quark_chain_config.BLOCK_EXTRA_DATA_SIZE_LIMIT):
            raise ValueError("tracking_data in block is too large")

        # Check the merkle tree
        merkle_hash = calculate_merkle_root(block.minor_block_header_list)
        if merkle_hash != block.header.hash_merkle_root:
            raise ValueError("incorrect merkle root")

        # Check the trie
        if block.header.hash_evm_state_root != BLANK_ROOT:
            raise ValueError("incorrect evm state root")

        # Check coinbase
        if not self.env.quark_chain_config.SKIP_ROOT_COINBASE_CHECK:
            expected_coinbase_amount = self._calculate_root_block_coinbase(
                [
                    header.get_hash()
                    for header in block.minor_block_header_list
                ],
                block.header.height,
            )
            actual_coinbase_amount = block.header.coinbase_amount_map.balance_map
            if expected_coinbase_amount != actual_coinbase_amount:
                raise ValueError(
                    "Bad coinbase amount for root block {}. expect {} but got {}."
                    .format(
                        block.header.get_hash().hex(),
                        expected_coinbase_amount,
                        actual_coinbase_amount,
                    ))

        # Check whether all minor blocks are ordered, validated (and linked to previous block)
        headers_map = dict()  # shard_id -> List[MinorBlockHeader]
        full_shard_id = (
            block.minor_block_header_list[0].branch.get_full_shard_id()
            if block.minor_block_header_list else None)
        for m_header in block.minor_block_header_list:
            if not self.db.contain_minor_block_by_hash(m_header.get_hash()):
                raise ValueError("minor block is not validated. {}-{}".format(
                    m_header.branch.get_full_shard_id(), m_header.height))
            if m_header.create_time > block.header.create_time:
                raise ValueError(
                    "minor block create time is larger than root block {} > {}"
                    .format(m_header.create_time, block.header.create_time))
            if not self.is_same_chain(
                    self.db.get_root_block_header_by_hash(
                        block.header.hash_prev_block),
                    self.db.get_root_block_header_by_hash(
                        m_header.hash_prev_root_block,
                        consistency_check=False),
            ):
                raise ValueError(
                    "minor block's prev root block must be in the same chain")

            if m_header.branch.get_full_shard_id() < full_shard_id:
                raise ValueError("shard id must be ordered")
            elif m_header.branch.get_full_shard_id() > full_shard_id:
                full_shard_id = m_header.branch.get_full_shard_id()

            headers_map.setdefault(m_header.branch.get_full_shard_id(),
                                   []).append(m_header)

        # check minor block headers are linked
        prev_last_minor_block_header_list = self.db.get_root_block_last_minor_block_header_list(
            block.header.hash_prev_block)
        prev_header_map = dict()  # shard_id -> MinorBlockHeader or None
        for header in prev_last_minor_block_header_list:
            prev_header_map[header.branch.get_full_shard_id()] = header

        full_shard_ids_to_check_proof_of_progress = self.env.quark_chain_config.get_initialized_full_shard_ids_before_root_height(
            block.header.height)
        for full_shard_id, headers in headers_map.items():
            check(len(headers) > 0)

            shard_config = self.env.quark_chain_config.shards[full_shard_id]
            if len(headers
                   ) > shard_config.max_blocks_per_shard_in_one_root_block:
                raise ValueError(
                    "too many minor blocks in the root block for shard {}".
                    format(full_shard_id))

            if full_shard_id not in full_shard_ids_to_check_proof_of_progress:
                raise ValueError(
                    "found minor block header in root block {} for uninitialized shard {}"
                    .format(block_hash.hex(), full_shard_id))
            prev_header_in_last_root_block = prev_header_map.get(
                full_shard_id, None)
            if not prev_header_in_last_root_block:
                # no header in previous root block then it must start with genesis block
                if headers[0].height != 0:
                    raise ValueError(
                        "genesis block height is not 0 for shard {} block hash {}"
                        .format(full_shard_id, headers[0].get_hash().hex()))
            else:
                headers = [prev_header_in_last_root_block] + headers
            for i in range(len(headers) - 1):
                if headers[i +
                           1].hash_prev_minor_block != headers[i].get_hash():
                    raise ValueError(
                        "minor block {} does not link to previous block {}".
                        format(headers[i + 1].get_hash(),
                               headers[i].get_hash()))

            prev_header_map[full_shard_id] = headers[-1]

        return block_hash, prev_header_map.values()
Exemple #4
0
    def validate_block(self, block, block_hash=None):
        """Raise on valiadtion errors """
        if not self.db.contain_root_block_by_hash(
                block.header.hash_prev_block):
            raise ValueError("previous hash block mismatch")

        block_hash = self.validate_block_header(block.header, block_hash)

        if (len(block.tracking_data) >
                self.env.quark_chain_config.BLOCK_EXTRA_DATA_SIZE_LIMIT):
            raise ValueError("tracking_data in block is too large")

        # Check the merkle tree
        merkle_hash = calculate_merkle_root(block.minor_block_header_list)
        if merkle_hash != block.header.hash_merkle_root:
            raise ValueError("incorrect merkle root")

        # Check whether all minor blocks are ordered, validated (and linked to previous block)
        headers_map = dict()  # shard_id -> List[MinorBlockHeader]
        full_shard_id = (
            block.minor_block_header_list[0].branch.get_full_shard_id()
            if block.minor_block_header_list else None)
        for m_header in block.minor_block_header_list:
            if not self.db.contain_minor_block_by_hash(m_header.get_hash()):
                raise ValueError("minor block is not validated. {}-{}".format(
                    m_header.branch.get_full_shard_id(), m_header.height))
            if m_header.create_time > block.header.create_time:
                raise ValueError(
                    "minor block create time is larger than root block {} > {}"
                    .format(m_header.create_time, block.header.create_time))
            if not self.__is_same_chain(
                    self.db.get_root_block_header_by_hash(
                        block.header.hash_prev_block),
                    self.db.get_root_block_header_by_hash(
                        m_header.hash_prev_root_block),
            ):
                raise ValueError(
                    "minor block's prev root block must be in the same chain")

            if m_header.branch.get_full_shard_id() < full_shard_id:
                raise ValueError("shard id must be ordered")
            elif m_header.branch.get_full_shard_id() > full_shard_id:
                full_shard_id = m_header.branch.get_full_shard_id()

            headers_map.setdefault(m_header.branch.get_full_shard_id(),
                                   []).append(m_header)
            # TODO: Add coinbase

        # check minor block headers are linked
        prev_last_minor_block_header_list = self.db.get_root_block_last_minor_block_header_list(
            block.header.hash_prev_block)
        prev_header_map = dict()  # shard_id -> MinorBlockHeader or None
        for header in prev_last_minor_block_header_list:
            prev_header_map[header.branch.get_full_shard_id()] = header

        full_shard_ids_to_check_proof_of_progress = self.env.quark_chain_config.get_initialized_full_shard_ids_before_root_height(
            block.header.height)
        for full_shard_id, headers in headers_map.items():
            check(len(headers) > 0)

            if full_shard_id not in full_shard_ids_to_check_proof_of_progress:
                raise ValueError(
                    "found minor block header in root block {} for uninitialized shard {}"
                    .format(block_hash.hex(), full_shard_id))
            prev_header_in_last_root_block = prev_header_map.get(
                full_shard_id, None)
            if not prev_header_in_last_root_block:
                # no header in previous root block then it must start with genesis block
                if headers[0].height != 0:
                    raise ValueError(
                        "genesis block height is not 0 for shard {} block hash {}"
                        .format(full_shard_id, headers[0].get_hash().hex()))
                # TODO: validate genesis block (CRITICAL)
            else:
                headers = [prev_header_in_last_root_block] + headers
            for i in range(len(headers) - 1):
                if headers[i +
                           1].hash_prev_minor_block != headers[i].get_hash():
                    raise ValueError(
                        "minor block {} does not link to previous block {}".
                        format(headers[i + 1].get_hash(),
                               headers[i].get_hash()))

            prev_header_map[full_shard_id] = headers[-1]

        return block_hash, prev_header_map.values()