Exemplo n.º 1
0
 def __validate_block_headers(self,
                              block_header_list: List[MinorBlockHeader]):
     for i in range(len(block_header_list) - 1):
         header, prev = block_header_list[i:i + 2]  # type: MinorBlockHeader
         if header.height != prev.height + 1:
             return False
         if header.hash_prev_minor_block != prev.get_hash():
             return False
         try:
             # Note that PoSW may lower diff, so checks here are necessary but not sufficient
             # More checks happen during block addition
             shard_config = self.shard.env.quark_chain_config.shards[
                 header.branch.get_full_shard_id()]
             consensus_type = shard_config.CONSENSUS_TYPE
             diff = header.difficulty
             if shard_config.POSW_CONFIG.ENABLED:
                 diff //= shard_config.POSW_CONFIG.DIFF_DIVIDER
             validate_seal(
                 header,
                 consensus_type,
                 adjusted_diff=diff,
                 qkchash_with_rotation_stats=consensus_type
                 == ConsensusType.POW_QKCHASH
                 and self.shard.state._qkchashx_enabled(header),
             )
         except Exception as e:
             Logger.warning(
                 "[{}] got block with bad seal in sync: {}".format(
                     header.branch.to_str(), str(e)))
             return False
     return True
Exemplo n.º 2
0
    async def handle_new_block(self, block):
        """
        This is a fast path for block propagation. The block is broadcasted to peers before being added to local state.
        0. if local shard is syncing, doesn't make sense to add, skip
        1. if block parent is not in local state/new block pool, discard (TODO: is this necessary?)
        2. if already in cache or in local state/new block pool, pass
        3. validate: check time, difficulty, POW
        4. add it to new minor block broadcast cache
        5. broadcast to all peers (minus peer that sent it, optional)
        6. add_block() to local state (then remove from cache)
           also, broadcast tip if tip is updated (so that peers can sync if they missed blocks, or are new)
        """
        if self.synchronizer.running:
            # TODO optional: queue the block if it came from broadcast to so that once sync is over,
            # catch up immediately
            return

        if block.header.get_hash() in self.state.new_block_pool:
            return
        if self.state.db.contain_minor_block_by_hash(block.header.get_hash()):
            return

        if not self.state.db.contain_minor_block_by_hash(
                block.header.hash_prev_minor_block):
            if block.header.hash_prev_minor_block not in self.state.new_block_pool:
                return

        # Doing full POSW check requires prev block has been added to the state, which could
        # slow down block propagation.
        # TODO: this is a copy of the code in SyncTask.__validate_block_headers. this it a helper
        try:
            header = block.header
            # Note that PoSW may lower diff, so checks here are necessary but not sufficient
            # More checks happen during block addition
            shard_config = self.env.quark_chain_config.shards[
                header.branch.get_full_shard_id()]
            consensus_type = shard_config.CONSENSUS_TYPE
            diff = header.difficulty
            if shard_config.POSW_CONFIG.ENABLED:
                diff //= shard_config.POSW_CONFIG.DIFF_DIVIDER
            validate_seal(header, consensus_type, adjusted_diff=diff)
        except Exception as e:
            Logger.warning(
                "[{}] got block with bad seal in handle_new_block: {}".format(
                    header.branch.to_str(), str(e)))
            raise e

        if block.header.create_time > time_ms() // 1000 + 30:
            return

        self.state.new_block_pool[block.header.get_hash()] = block

        Logger.info("[{}/{}] got new block with height {}".format(
            block.header.branch.get_chain_id(),
            block.header.branch.get_shard_id(),
            block.header.height,
        ))
        self.broadcast_new_block(block)
        await self.add_block(block)
Exemplo n.º 3
0
    def validate_block_header(self,
                              block_header: RootBlockHeader,
                              block_hash=None):
        """ Validate the block header.
        """
        height = block_header.height
        if height < 1:
            raise ValueError("unexpected height")

        if not self.db.contain_root_block_by_hash(
                block_header.hash_prev_block):
            raise ValueError("previous hash block mismatch")
        prev_block_header = self.db.get_root_block_header_by_hash(
            block_header.hash_prev_block)

        if prev_block_header.height + 1 != height:
            raise ValueError("incorrect block height")

        if (block_header.create_time >
                time_ms() // 1000 + ALLOWED_FUTURE_BLOCKS_TIME_VALIDATION):
            raise ValueError("block too far into future")

        if block_header.create_time <= prev_block_header.create_time:
            raise ValueError(
                "incorrect create time tip time {}, new block time {}".format(
                    block_header.create_time, prev_block_header.create_time))

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

        header_hash = block_header.get_hash()
        if block_hash is None:
            block_hash = header_hash

        # Check difficulty, potentially adjusted by guardian mechanism
        adjusted_diff = None  # type: Optional[int]
        if not self.env.quark_chain_config.SKIP_ROOT_DIFFICULTY_CHECK:
            diff = self.diff_calc.calculate_diff_with_parent(
                prev_block_header, block_header.create_time)
            if diff != block_header.difficulty:
                raise ValueError("incorrect difficulty")
            # lower the difficulty for root block signed by guardian
            if block_header.verify_signature(
                    self.env.quark_chain_config.guardian_public_key):
                adjusted_diff = Guardian.adjust_difficulty(
                    diff, block_header.height)

        if (block_header.difficulty + prev_block_header.total_difficulty !=
                block_header.total_difficulty):
            raise ValueError("incorrect total difficulty")

        # Check PoW if applicable
        consensus_type = self.root_config.CONSENSUS_TYPE
        validate_seal(block_header,
                      consensus_type,
                      adjusted_diff=adjusted_diff)

        return block_hash
Exemplo n.º 4
0
        async def add(block_to_add):
            h = block_to_add.header
            diff = h.difficulty

            if h.verify_signature(priv.public_key):
                diff = Guardian.adjust_difficulty(diff, h.height)

            validate_seal(block_to_add.header, doublesha, adjusted_diff=diff)
Exemplo n.º 5
0
 def __validate_block_headers(self, block_header_list):
     for i in range(len(block_header_list) - 1):
         header, prev = block_header_list[i:i + 2]
         if header.height != prev.height + 1:
             return False
         if header.hash_prev_minor_block != prev.get_hash():
             return False
         shard_id = header.branch.get_shard_id()
         consensus_type = self.shard.env.quark_chain_config.SHARD_LIST[
             shard_id].CONSENSUS_TYPE
         validate_seal(header, consensus_type)
     return True
Exemplo n.º 6
0
    def __validate_block_header(self,
                                block_header: RootBlockHeader,
                                adjusted_diff: int = None):
        """ Validate the block header.
        """
        height = block_header.height
        if height < 1:
            raise ValueError("unexpected height")

        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")
        prev_block_header = self.db.get_root_block_header_by_hash(
            block_header.hash_prev_block)

        if prev_block_header.height + 1 != height:
            raise ValueError("incorrect block height")

        if (block_header.create_time >
                time_ms() // 1000 + ALLOWED_FUTURE_BLOCKS_TIME_VALIDATION):
            raise ValueError("block too far into future")

        if block_header.create_time <= prev_block_header.create_time:
            raise ValueError(
                "incorrect create time tip time {}, new block time {}".format(
                    block_header.create_time, prev_block_header.create_time))

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

        # Check difficulty, potentially adjusted by guardian mechanism
        if not self.env.quark_chain_config.SKIP_ROOT_DIFFICULTY_CHECK:
            diff = self.diff_calc.calculate_diff_with_parent(
                prev_block_header, block_header.create_time)
            if diff != block_header.difficulty:
                raise ValueError("incorrect difficulty")

        if (block_header.difficulty + prev_block_header.total_difficulty !=
                block_header.total_difficulty):
            raise ValueError("incorrect total difficulty")

        # Check PoW if applicable
        if not self.env.quark_chain_config.DISABLE_POW_CHECK:
            consensus_type = self.root_config.CONSENSUS_TYPE
            diff = (adjusted_diff
                    if adjusted_diff is not None else block_header.difficulty)
            validate_seal(block_header, consensus_type, adjusted_diff=diff)

        return block_header.get_hash()
Exemplo n.º 7
0
 def test_sha3sha3(self):
     miner = self.miner_gen(ConsensusType.POW_SHA3SHA3, None, None)
     block = RootBlock(
         RootBlockHeader(create_time=42,
                         extra_data="{}".encode("utf-8"),
                         difficulty=5))
     # only process one block, which is passed in
     miner.input_q.put((None, {}))
     miner.mine_sha3sha3(block, miner.input_q, miner.output_q, {})
     mined_block = miner.output_q.get()
     self.assertEqual(mined_block.header.nonce, 3)
     validate_seal(mined_block.header, ConsensusType.POW_SHA3SHA3)
Exemplo n.º 8
0
    def validate_block_header(self,
                              block_header: RootBlockHeader,
                              block_hash=None):
        """ Validate the block header.
        """
        height = block_header.height
        if height < 1:
            raise ValueError("unexpected height")

        if not self.db.contain_root_block_by_hash(
                block_header.hash_prev_block):
            raise ValueError("previous hash block mismatch")
        prev_block_header = self.db.get_root_block_header_by_hash(
            block_header.hash_prev_block)

        if prev_block_header.height + 1 != height:
            raise ValueError("incorrect block height")

        if block_header.create_time <= prev_block_header.create_time:
            raise ValueError(
                "incorrect create time tip time {}, new block time {}".format(
                    block_header.create_time, prev_block_header.create_time))

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

        header_hash = block_header.get_hash()
        if block_hash is None:
            block_hash = header_hash

        # Check difficulty
        curr_diff = block_header.difficulty
        if not self.env.quark_chain_config.SKIP_ROOT_DIFFICULTY_CHECK:
            if self.env.quark_chain_config.NETWORK_ID == NetworkId.MAINNET:
                diff = self.diff_calc.calculate_diff_with_parent(
                    prev_block_header, block_header.create_time)
                if diff != curr_diff:
                    raise ValueError("incorrect difficulty")
                metric = diff * int.from_bytes(block_hash, byteorder="big")
                if metric >= 2**256:
                    raise ValueError("insufficient difficulty")
            elif (block_header.coinbase_address.recipient !=
                  self.env.quark_chain_config.testnet_master_address.recipient
                  ):
                raise ValueError("incorrect master to create the block")

        # Check PoW if applicable
        consensus_type = self.env.quark_chain_config.ROOT.CONSENSUS_TYPE
        validate_seal(block_header, consensus_type)

        return block_hash
Exemplo n.º 9
0
    def validate_block_header(self, block_header: RootBlockHeader, block_hash=None):
        """ Validate the block header.
        """
        height = block_header.height
        if height < 1:
            raise ValueError("unexpected height")

        if not self.db.contain_root_block_by_hash(block_header.hash_prev_block):
            raise ValueError("previous hash block mismatch")
        prev_block_header = self.db.get_root_block_header_by_hash(
            block_header.hash_prev_block
        )

        if prev_block_header.height + 1 != height:
            raise ValueError("incorrect block height")

        if block_header.create_time <= prev_block_header.create_time:
            raise ValueError(
                "incorrect create time tip time {}, new block time {}".format(
                    block_header.create_time, prev_block_header.create_time
                )
            )

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

        header_hash = block_header.get_hash()
        if block_hash is None:
            block_hash = header_hash

        # Check difficulty
        if not self.env.quark_chain_config.SKIP_ROOT_DIFFICULTY_CHECK:
            diff = self.diff_calc.calculate_diff_with_parent(
                prev_block_header, block_header.create_time
            )
            # lower the difficulty for root block signed by guardian
            if block_header.verify_signature(
                self.env.quark_chain_config.guardian_public_key
            ):
                diff = Guardian.adjust_difficulty(diff, block_header.height)
            if diff != block_header.difficulty:
                raise ValueError("incorrect difficulty")

        # Check PoW if applicable
        consensus_type = self.env.quark_chain_config.ROOT.CONSENSUS_TYPE
        validate_seal(block_header, consensus_type)

        return block_hash
Exemplo n.º 10
0
    def test_validate_seal_with_adjusted_diff(self):
        diff = 1000
        block = RootBlock(
            RootBlockHeader(create_time=42, difficulty=diff),
            tracking_data="{}".encode("utf-8"),
        )
        block.header.nonce = 0
        with self.assertRaises(ValueError):
            validate_seal(block.header, ConsensusType.POW_DOUBLESHA256)

        # significantly lowering the diff should pass
        validate_seal(block.header,
                      ConsensusType.POW_DOUBLESHA256,
                      adjusted_diff=1)
Exemplo n.º 11
0
 def test_sha3sha3(self):
     miner = self.miner_gen(ConsensusType.POW_SHA3SHA3, None, None)
     block = RootBlock(
         RootBlockHeader(create_time=42, difficulty=5),
         tracking_data="{}".encode("utf-8"),
     )
     work = MiningWork(block.header.get_hash_for_mining(), 42, 5)
     # only process one block, which is passed in. `None` means termination right after
     miner.input_q.put((None, {}))
     miner._mine_loop(
         ConsensusType.POW_SHA3SHA3, work, {}, miner.input_q, miner.output_q
     )
     mined_res = miner.output_q.get()
     self.assertEqual(mined_res.nonce, 8)
     block.header.nonce = mined_res.nonce
     validate_seal(block.header, ConsensusType.POW_SHA3SHA3)
Exemplo n.º 12
0
    async def handle_new_block(self, block):
        """
        0. if local shard is syncing, doesn't make sense to add, skip
        1. if block parent is not in local state/new block pool, discard
        2. if already in cache or in local state/new block pool, pass
        3. validate: check time, difficulty, POW
        4. add it to new minor block broadcast cache
        5. broadcast to all peers (minus peer that sent it, optional)
        6. add_block() to local state (then remove from cache)
             also, broadcast tip if tip is updated (so that peers can sync if they missed blocks, or are new)
        """
        if self.synchronizer.running:
            # TODO optinal: queue the block if it came from broadcast to so that once sync is over, catch up immediately
            return

        if block.header.get_hash() in self.state.new_block_pool:
            return
        if self.state.db.contain_minor_block_by_hash(block.header.get_hash()):
            return

        if not self.state.db.contain_minor_block_by_hash(
                block.header.hash_prev_minor_block):
            if block.header.hash_prev_minor_block not in self.state.new_block_pool:
                return

        full_shard_id = block.header.branch.get_full_shard_id()
        consensus_type = self.env.quark_chain_config.shards[
            full_shard_id].CONSENSUS_TYPE
        try:
            validate_seal(block.header, consensus_type)
        except Exception as e:
            Logger.warning("[{}] Got block with bad seal: {}".format(
                full_shard_id, str(e)))
            return

        if block.header.create_time > time_ms() // 1000 + 30:
            return

        self.state.new_block_pool[block.header.get_hash()] = block

        Logger.info("[{}/{}] got new block with height {}".format(
            block.header.branch.get_chain_id(),
            block.header.branch.get_shard_id(),
            block.header.height,
        ))
        self.broadcast_new_block(block)
        await self.add_block(block)
Exemplo n.º 13
0
 def test_qkchash(self):
     miner = self.miner_gen(ConsensusType.POW_QKCHASH, None, None)
     block = RootBlock(
         RootBlockHeader(create_time=42, difficulty=5),
         tracking_data="{}".encode("utf-8"),
     )
     work = MiningWork(block.header.get_hash_for_mining(), 42, 5)
     # only process one block, which is passed in. `None` means termination right after
     miner.input_q.put((None, {}))
     miner.mine_loop(
         work,
         {"consensus_type": ConsensusType.POW_QKCHASH},
         miner.input_q,
         miner.output_q,
     )
     mined_res = miner.output_q.get()
     block.header.nonce = mined_res.nonce
     block.header.mixhash = mined_res.mixhash
     validate_seal(block.header, ConsensusType.POW_QKCHASH)
Exemplo n.º 14
0
 async def add(block_to_add):
     validate_seal(block_to_add.header, doublesha)
     self.added_blocks.append(block_to_add)