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)
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
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()
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()