示例#1
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")
            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
示例#2
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)
示例#3
0
    def setUp(self):
        super().setUp()

        dummy_tip = lambda: RootBlockHeader()

        def miner_gen(consensus,
                      create_func,
                      add_func,
                      tip_func=dummy_tip,
                      **kwargs):
            m = Miner(consensus, create_func, add_func, self.get_mining_params,
                      tip_func, **kwargs)
            m.enabled = True
            return m

        self.miner_gen = miner_gen
        self.added_blocks = []
示例#4
0
    def test_submit_work_with_remote_guardian(self):
        now = 42
        doublesha = ConsensusType.POW_DOUBLESHA256
        block = RootBlock(
            RootBlockHeader(create_time=42, extra_data=b"{}", difficulty=1000))
        priv = ecies.generate_privkey()

        async def create(retry=True):
            return block

        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)

        # just with the guardian public key
        miner = self.miner_gen(doublesha, create, add, remote=True)

        async def go():
            for i in range(42, 100):
                work, _ = await miner.get_work(now=now)
                self.assertEqual(work.height, 0)

                # remote guardian: diff 1000 -> 1, any number should work
                # mimic the sign process of the remote guardian server
                block.header.nonce = i
                block.header.mixhash = sha3_256(b"")
                block.header.sign_with_private_key(priv)
                signature = block.header.signature

                # reset the signature to the default value
                block.header.signature = bytes(65)
                # submit the signature through the submit work
                res = await miner.submit_work(work.hash, i, sha3_256(b""),
                                              signature)
                self.assertTrue(res)
                res = await miner.submit_work(work.hash, i, sha3_256(b""),
                                              bytes(65))
                self.assertFalse(res)

        loop = asyncio.get_event_loop()
        loop.run_until_complete(go())
示例#5
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)
示例#6
0
    def test_submit_work(self):
        now = 42
        block = RootBlock(
            RootBlockHeader(create_time=42, extra_data=b"{}", difficulty=2))

        async def create():
            return block

        async def add(block_to_add):
            self.added_blocks.append(block_to_add)

        miner = self.miner_gen(ConsensusType.POW_SHA3SHA3,
                               create,
                               add,
                               remote=True)

        async def go():
            work = await miner.get_work(now=now)
            self.assertEqual(work.height, 0)
            self.assertEqual(work.difficulty, 2)
            # submitted block doesn't exist
            res = await miner.submit_work(b"lolwut", 0, sha3_256(b""))
            self.assertFalse(res)

            solver = DoubleSHA256(block)
            mined = solver.mine(100, 200)
            self.assertTrue(mined)
            sol = int.from_bytes(solver.nonce_found, byteorder="big")
            self.assertGreater(sol, 100)  # ensure non-solution is tried
            non_sol = sol - 1
            # invalid pow proof
            res = await miner.submit_work(work.hash, non_sol, sha3_256(b""))
            self.assertFalse(res)
            # valid submission, also check internal state afterwards
            res = await miner.submit_work(work.hash, sol, sha3_256(b""))
            self.assertTrue(res)
            self.assertEqual(miner.work_map, {})
            self.assertEqual(len(self.added_blocks), 1)
            self.assertIsNone(miner.current_work)

        loop = asyncio.get_event_loop()
        loop.run_until_complete(go())
示例#7
0
    def test_submit_work(self):
        now = 42
        doublesha = ConsensusType.POW_DOUBLESHA256
        block = RootBlock(
            RootBlockHeader(create_time=42, extra_data=b"{}", difficulty=5))

        async def create(retry=True):
            return block

        async def add(block_to_add):
            validate_seal(block_to_add.header, doublesha)
            self.added_blocks.append(block_to_add)

        miner = self.miner_gen(doublesha, create, add, remote=True)

        async def go():
            work, _ = await miner.get_work(now=now)
            self.assertEqual(work.height, 0)
            self.assertEqual(work.difficulty, 5)
            # submitted block doesn't exist
            res = await miner.submit_work(b"lolwut", 0, sha3_256(b""))
            self.assertFalse(res)

            solver = DoubleSHA256(work)
            sol = solver.mine(200, 300).nonce
            self.assertGreater(sol, 200)  # ensure non-solution is tried
            non_sol = sol - 1
            # invalid pow proof
            res = await miner.submit_work(work.hash, non_sol, sha3_256(b""))
            self.assertFalse(res)
            # valid submission, also check internal state afterwards
            res = await miner.submit_work(work.hash, sol, sha3_256(b""))
            self.assertTrue(res)
            self.assertEqual(miner.work_map, {})
            self.assertEqual(len(self.added_blocks), 1)
            self.assertIsNone(miner.current_work)

        loop = asyncio.get_event_loop()
        loop.run_until_complete(go())
示例#8
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()
示例#9
0
 async def create(retry=True):
     nonlocal now
     return RootBlock(RootBlockHeader(create_time=now,
                                      extra_data=b"{}"))
示例#10
0
    def test_get_work(self):
        now, height = 42, 42
        mock_tip = RootBlockHeader(height=height)

        async def create(coinbase_addr, **kwargs):
            nonlocal now, mock_tip
            return RootBlock(
                RootBlockHeader(
                    coinbase_address=coinbase_addr,
                    create_time=now,
                    extra_data=b"{}",
                    hash_prev_block=mock_tip.get_hash(),
                    height=mock_tip.height + 1,
                ))

        def tip_getter():
            nonlocal mock_tip
            return mock_tip

        miner = self.miner_gen(ConsensusType.POW_DOUBLESHA256,
                               create,
                               None,
                               tip_getter,
                               remote=True)

        async def go():
            nonlocal now, mock_tip
            # no current work, will generate a new one
            work, block = await miner.get_work(EMPTY_ADDR, now=now)
            self.assertEqual(len(work), 3)
            self.assertEqual(block.header.coinbase_address,
                             Address.create_empty_account())
            self.assertEqual(len(miner.work_map), 1)
            h = list(miner.work_map.keys())[0]
            self.assertEqual(work.hash, h)
            # cache hit and new block is linked to tip (by default)
            now += 1
            work, _ = await miner.get_work(EMPTY_ADDR, now=now)
            self.assertEqual(work.hash, h)
            self.assertEqual(work.height, 43)
            self.assertEqual(len(miner.work_map), 1)
            # cache hit, but current work is outdated because tip has updated
            mock_tip.height += 1
            work, _ = await miner.get_work(EMPTY_ADDR, now=now)
            h = work.hash
            self.assertEqual(len(miner.work_map), 2)
            self.assertEqual(work.height, 44)
            # new work if interval passed
            now += 11
            work, _ = await miner.get_work(EMPTY_ADDR, now=now)
            self.assertEqual(len(miner.work_map), 3)
            # height didn't change, but hash should
            self.assertNotEqual(work.hash, h)
            self.assertEqual(work.height, 44)
            # get work with specified coinbase address
            addr = Address.create_random_account(0)
            work, block = await miner.get_work(addr, now=now)
            self.assertEqual(block.header.coinbase_address, addr)
            self.assertEqual(len(miner.work_map), 4)
            self.assertEqual(len(miner.current_works), 2)

        loop = asyncio.get_event_loop()
        loop.run_until_complete(go())
示例#11
0
 async def create():
     nonlocal i
     if i >= 5:
         return None
     return RootBlock(RootBlockHeader(create_time=int(time.time()), extra_data="{}".encode("utf-8")))
示例#12
0
 async def dummy_create_block_async() -> Optional[RootBlock]:
     if len(TestMiner.added_blocks) >= 5:
         return None  # stop the game
     return RootBlock(RootBlockHeader(create_time=int(time.time()), extra_data="{}".encode("utf-8")))
示例#13
0
 async def create():
     if len(self.added_blocks) >= 5:
         return None  # stop the game
     return RootBlock(
         RootBlockHeader(create_time=int(time.time()),
                         extra_data="{}".encode("utf-8")))