def get_minor_block_by_hash(self,
                             h,
                             consistency_check=True
                             ) -> Optional[MinorBlock]:
     if consistency_check and h not in self.m_header_pool:
         return None
     return MinorBlock.deserialize(self.db.get(b"mblock_" + h))
Example #2
0
    def create_minor_block(self, root_block: RootBlock, full_shard_id: int,
                           evm_state: EvmState) -> MinorBlock:
        """ Create genesis block for shard.
        Genesis block's hash_prev_root_block is set to the genesis root block.
        Genesis state will be committed to the given evm_state.
        Based on ALLOC, genesis_token will be added to initial accounts.
        """
        branch = Branch(full_shard_id)
        shard_config = self._qkc_config.shards[full_shard_id]
        genesis = shard_config.GENESIS

        for address_hex, alloc_amount in genesis.ALLOC.items():
            address = Address.create_from(bytes.fromhex(address_hex))
            check(
                self._qkc_config.get_full_shard_id_by_full_shard_key(
                    address.full_shard_key) == full_shard_id)
            evm_state.full_shard_key = address.full_shard_key
            if isinstance(alloc_amount, dict):
                for k, v in alloc_amount.items():
                    evm_state.delta_token_balance(address.recipient,
                                                  token_id_encode(k), v)
            else:
                evm_state.delta_token_balance(address.recipient,
                                              self._qkc_config.genesis_token,
                                              alloc_amount)

        evm_state.commit()

        meta = MinorBlockMeta(
            hash_merkle_root=bytes.fromhex(genesis.HASH_MERKLE_ROOT),
            hash_evm_state_root=evm_state.trie.root_hash,
        )

        local_fee_rate = 1 - self._qkc_config.reward_tax_rate  # type: Fraction
        coinbase_tokens = {
            self._qkc_config.genesis_token:
            shard_config.COINBASE_AMOUNT * local_fee_rate.numerator //
            local_fee_rate.denominator
        }

        coinbase_address = Address.create_empty_account(full_shard_id)

        header = MinorBlockHeader(
            version=genesis.VERSION,
            height=genesis.HEIGHT,
            branch=branch,
            hash_prev_minor_block=bytes.fromhex(genesis.HASH_PREV_MINOR_BLOCK),
            hash_prev_root_block=root_block.header.get_hash(),
            evm_gas_limit=genesis.GAS_LIMIT,
            hash_meta=sha3_256(meta.serialize()),
            coinbase_amount_map=TokenBalanceMap(coinbase_tokens),
            coinbase_address=coinbase_address,
            create_time=genesis.TIMESTAMP,
            difficulty=genesis.DIFFICULTY,
            extra_data=bytes.fromhex(genesis.EXTRA_DATA),
        )
        return (
            MinorBlock(header=header, meta=meta, tx_list=[]),
            TokenBalanceMap(coinbase_tokens),
        )
    def recover_state(self, r_header, m_header):
        """ When recovering from local database, we can only guarantee the consistency of the best chain.
        Forking blocks can be in inconsistent state and thus should be pruned from the database
        so that they can be retried in the future.
        """
        r_hash = r_header.get_hash()
        while (len(self.r_header_pool) <
               self.env.quark_chain_config.ROOT.max_root_blocks_in_memory):
            block = RootBlock.deserialize(self.db.get(b"rblock_" + r_hash))
            self.r_minor_header_pool[
                r_hash] = self.__get_last_minor_block_in_root_block(block)
            self.r_header_pool[r_hash] = block.header
            if (block.header.height <=
                    self.env.quark_chain_config.get_genesis_root_height(
                        self.branch.get_shard_id())):
                break
            r_hash = block.header.hash_prev_block

        m_hash = m_header.get_hash()
        shard_config = self.env.quark_chain_config.SHARD_LIST[
            self.branch.get_shard_id()]
        while len(
                self.m_header_pool) < shard_config.max_minor_blocks_in_memory:
            block = MinorBlock.deserialize(self.db.get(b"mblock_" + m_hash))
            self.m_header_pool[m_hash] = block.header
            self.m_meta_pool[m_hash] = block.meta
            if block.header.height <= 0:
                break
            m_hash = block.header.hash_prev_minor_block

        Logger.info("[{}] recovered {} minor blocks and {} root blocks".format(
            self.branch.get_shard_id(),
            len(self.m_header_pool),
            len(self.r_header_pool),
        ))
Example #4
0
 def get_minor_block_by_hash(self,
                             h: bytes,
                             consistency_check=True
                             ) -> Optional[MinorBlock]:
     if consistency_check and h not in self.m_header_pool:
         return None
     data = self.db.get(b"mblock_" + h, None)
     return MinorBlock.deserialize(data) if data else None
 def get_minor_block_by_hash(self, h: bytes) -> Optional[MinorBlock]:
     key = b"mblock_" + h
     if key in self.mblock_cache:
         return self.mblock_cache[key]
     raw_block = self.db.get(key, None)
     block = raw_block and MinorBlock.deserialize(raw_block)
     if block is not None:
         self.mblock_cache[key] = block
     return block
Example #6
0
    def test_getNextBlockToMine_with_shard_mask(self):
        id1 = Identity.create_random_identity()
        acc1 = Address.create_from_identity(id1, full_shard_id=0)

        with ClusterContext(1, acc1) as clusters, jrpc_server_context(
                clusters[0].master):
            response = send_request("getNextBlockToMine",
                                    "0x" + acc1.serialize().hex(), "0x2")
            self.assertFalse(response["isRootBlock"])
            block1 = MinorBlock.deserialize(
                bytes.fromhex(response["blockData"][2:]))
            self.assertEqual(block1.header.branch.value, 0b10)

            response = send_request("getNextBlockToMine",
                                    "0x" + acc1.serialize().hex(), "0x3")
            self.assertFalse(response["isRootBlock"])
            block1 = MinorBlock.deserialize(
                bytes.fromhex(response["blockData"][2:]))
            self.assertEqual(block1.header.branch.value, 0b11)
Example #7
0
    async def handle_get_transaction_request(self, req):
        minor_block, i = self.slave_server.get_transaction_by_hash(
            req.tx_hash, req.branch
        )
        if not minor_block:
            empty_block = MinorBlock(MinorBlockHeader(), MinorBlockMeta())
            return GetTransactionResponse(
                error_code=1, minor_block=empty_block, index=0
            )

        return GetTransactionResponse(error_code=0, minor_block=minor_block, index=i)
    def test_get_minor_block_by_hash(self):
        db = ShardDbOperator(InMemoryDb(), DEFAULT_ENV, Branch(2))
        block = MinorBlock(MinorBlockHeader(), MinorBlockMeta())
        block_hash = block.header.get_hash()
        db.put_minor_block(block, [])
        self.assertEqual(db.get_minor_block_by_hash(block_hash), block)
        self.assertIsNone(db.get_minor_block_by_hash(b""))

        self.assertEqual(db.get_minor_block_header_by_hash(block_hash),
                         block.header)
        self.assertIsNone(db.get_minor_block_header_by_hash(b""))
Example #9
0
 async def handle_get_transaction_receipt_request(self, req):
     resp = self.slave_server.get_transaction_receipt(req.tx_hash, req.branch)
     if not resp:
         empty_block = MinorBlock(MinorBlockHeader(), MinorBlockMeta())
         empty_receipt = TransactionReceipt.create_empty_receipt()
         return GetTransactionReceiptResponse(
             error_code=1, minor_block=empty_block, index=0, receipt=empty_receipt
         )
     minor_block, i, receipt = resp
     return GetTransactionReceiptResponse(
         error_code=0, minor_block=minor_block, index=i, receipt=receipt
     )
Example #10
0
    async def handle_get_minor_block_request(self, req):
        if req.minor_block_hash != bytes(32):
            block = self.slave_server.get_minor_block_by_hash(
                req.minor_block_hash, req.branch)
        else:
            block = self.slave_server.get_minor_block_by_height(
                req.height, req.branch)

        if not block:
            empty_block = MinorBlock(MinorBlockHeader(), MinorBlockMeta())
            return GetMinorBlockResponse(error_code=1, minor_block=empty_block)

        return GetMinorBlockResponse(error_code=0, minor_block=block)
Example #11
0
    def create_minor_block(self, root_block: RootBlock, shard_id: int,
                           evm_state: EvmState) -> MinorBlock:
        """ Create genesis block for shard.
        Genesis block's hash_prev_root_block is set to the genesis root block.
        Genesis state will be committed to the given evm_state.
        """
        branch = Branch.create(self._qkc_config.SHARD_SIZE, shard_id)
        shard_config = self._qkc_config.SHARD_LIST[shard_id]
        genesis = shard_config.GENESIS

        for address_hex, amount_in_wei in genesis.ALLOC.items():
            address = Address.create_from(bytes.fromhex(address_hex))
            check(
                address.get_shard_id(self._qkc_config.SHARD_SIZE) == shard_id)
            evm_state.full_shard_id = address.full_shard_id
            evm_state.delta_balance(address.recipient, amount_in_wei)

        evm_state.commit()

        meta = MinorBlockMeta(
            hash_merkle_root=bytes.fromhex(genesis.HASH_MERKLE_ROOT),
            hash_evm_state_root=evm_state.trie.root_hash,
        )

        local_fee_rate = 1 - self._qkc_config.reward_tax_rate  # type: Fraction
        coinbase_amount = (shard_config.COINBASE_AMOUNT *
                           local_fee_rate.numerator //
                           local_fee_rate.denominator)
        coinbase_address = Address.create_empty_account(shard_id)

        header = MinorBlockHeader(
            version=genesis.VERSION,
            height=genesis.HEIGHT,
            branch=branch,
            hash_prev_minor_block=bytes.fromhex(genesis.HASH_PREV_MINOR_BLOCK),
            hash_prev_root_block=root_block.header.get_hash(),
            evm_gas_limit=genesis.GAS_LIMIT,
            hash_meta=sha3_256(meta.serialize()),
            coinbase_amount=coinbase_amount,
            coinbase_address=coinbase_address,
            create_time=genesis.TIMESTAMP,
            difficulty=genesis.DIFFICULTY,
            extra_data=bytes.fromhex(genesis.EXTRA_DATA),
        )
        return MinorBlock(header=header, meta=meta, tx_list=[])
Example #12
0
    async def handle_add_minor_block_request(self, req):
        """ For local miner to submit mined blocks through master """
        try:
            block = MinorBlock.deserialize(req.minor_block_data)
        except Exception:
            return AddMinorBlockResponse(error_code=errno.EBADMSG)
        shard = self.shards.get(block.header.branch, None)
        if not shard:
            return AddMinorBlockResponse(error_code=errno.EBADMSG)

        if block.header.hash_prev_minor_block != shard.state.header_tip.get_hash(
        ):
            # Tip changed, don't bother creating a fork
            Logger.info("[{}] dropped stale block {} mined locally".format(
                block.header.branch.get_shard_id(), block.header.height))
            return AddMinorBlockResponse(error_code=0)

        success = await shard.add_block(block)
        return AddMinorBlockResponse(error_code=0 if success else errno.EFAULT)
Example #13
0
    def test_getNextBlockToMine_and_addBlock(self):
        id1 = Identity.create_random_identity()
        acc1 = Address.create_from_identity(id1, full_shard_id=0)
        acc3 = Address.create_random_account(full_shard_id=1)

        with ClusterContext(1, acc1) as clusters, jrpc_server_context(
                clusters[0].master):
            slaves = clusters[0].slave_list

            # Expect to mine root that confirms the genesis minor blocks
            response = send_request("getNextBlockToMine",
                                    "0x" + acc1.serialize().hex(), "0x0")
            self.assertTrue(response["isRootBlock"])
            block = RootBlock.deserialize(
                bytes.fromhex(response["blockData"][2:]))

            self.assertEqual(block.header.height, 1)
            self.assertEqual(len(block.minor_block_header_list), 2)
            self.assertEqual(block.minor_block_header_list[0].height, 0)
            self.assertEqual(block.minor_block_header_list[1].height, 0)

            send_request("addBlock", "0x0", response["blockData"])

            tx = create_transfer_transaction(
                shard_state=clusters[0].get_shard_state(0),
                key=id1.get_key(),
                from_address=acc1,
                to_address=acc3,
                value=14,
                gas=opcodes.GTXXSHARDCOST + opcodes.GTXCOST,
            )
            self.assertTrue(slaves[0].add_tx(tx))

            # Expect to mine shard 0 since it has one tx
            response = send_request("getNextBlockToMine",
                                    "0x" + acc1.serialize().hex(), "0x0")
            self.assertFalse(response["isRootBlock"])
            block1 = MinorBlock.deserialize(
                bytes.fromhex(response["blockData"][2:]))
            self.assertEqual(block1.header.branch.value, 0b10)

            self.assertTrue(
                send_request("addBlock", "0x2", response["blockData"]))
            self.assertEqual(
                clusters[0].get_shard_state(1).get_balance(acc3.recipient, 0),
                0)

            # Expect to mine shard 1 due to proof-of-progress
            response = send_request("getNextBlockToMine",
                                    "0x" + acc1.serialize().hex(), "0x0")
            self.assertFalse(response["isRootBlock"])
            block2 = MinorBlock.deserialize(
                bytes.fromhex(response["blockData"][2:]))
            self.assertEqual(block2.header.branch.value, 0b11)

            self.assertTrue(
                send_request("addBlock", "0x3", response["blockData"]))

            # Expect to mine root
            response = send_request("getNextBlockToMine",
                                    "0x" + acc1.serialize().hex(), "0x0")
            self.assertTrue(response["isRootBlock"])
            block = RootBlock.deserialize(
                bytes.fromhex(response["blockData"][2:]))

            self.assertEqual(block.header.height, 2)
            self.assertEqual(len(block.minor_block_header_list), 2)
            self.assertEqual(block.minor_block_header_list[0], block1.header)
            self.assertEqual(block.minor_block_header_list[1], block2.header)

            send_request("addBlock", "0x0", response["blockData"])
            self.assertEqual(
                clusters[0].get_shard_state(1).get_balance(acc3.recipient, 0),
                0)

            # Expect to mine shard 1 for the gas on xshard tx to acc3
            response = send_request("getNextBlockToMine",
                                    "0x" + acc1.serialize().hex(), "0x0")
            self.assertFalse(response["isRootBlock"])
            block3 = MinorBlock.deserialize(
                bytes.fromhex(response["blockData"][2:]))
            self.assertEqual(block3.header.branch.value, 0b11)

            self.assertTrue(
                send_request("addBlock", "0x3", response["blockData"]))
            # Expect withdrawTo is included in acc3's balance
            resp = send_request("getBalance", "0x" + acc3.serialize().hex())
            self.assertEqual(resp["branch"], "0x3")
            self.assertEqual(resp["balance"], "0xe")
Example #14
0
 def get_genesis_block(self, root_block_hash):
     data = self.db.get(b"genesis_" + root_block_hash, None)
     if not data:
         return None
     else:
         return MinorBlock.deserialize(data)
Example #15
0
 def get_minor_block_by_hash(self, h: bytes) -> Optional[MinorBlock]:
     data = self.db.get(b"mblock_" + h, None)
     return MinorBlock.deserialize(data) if data else None
Example #16
0
    def create_minor_block(
            self, root_block: RootBlock, full_shard_id: int,
            evm_state: EvmState) -> Tuple[MinorBlock, TokenBalanceMap]:
        """ Create genesis block for shard.
        Genesis block's hash_prev_root_block is set to the genesis root block.
        Genesis state will be committed to the given evm_state.
        Based on ALLOC, genesis_token will be added to initial accounts.
        """
        branch = Branch(full_shard_id)
        shard_config = self._qkc_config.shards[full_shard_id]
        genesis = shard_config.GENESIS

        for address_hex, alloc_data in genesis.ALLOC.items():
            address = Address.create_from(bytes.fromhex(address_hex))
            check(
                self._qkc_config.get_full_shard_id_by_full_shard_key(
                    address.full_shard_key) == full_shard_id)
            evm_state.full_shard_key = address.full_shard_key
            recipient = address.recipient
            if "code" in alloc_data:
                code = decode_hex(remove_0x_head(alloc_data["code"]))
                evm_state.set_code(recipient, code)
                evm_state.set_nonce(recipient, 1)
            if "storage" in alloc_data:
                for k, v in alloc_data["storage"].items():
                    evm_state.set_storage_data(
                        recipient,
                        big_endian_to_int(decode_hex(k[2:])),
                        big_endian_to_int(decode_hex(v[2:])),
                    )
            # backward compatible:
            # v1: {addr: {QKC: 1234}}
            # v2: {addr: {balances: {QKC: 1234}, code: 0x, storage: {0x12: 0x34}}}
            balances = alloc_data
            if "balances" in alloc_data:
                balances = alloc_data["balances"]
            for k, v in balances.items():
                if k in ("code", "storage"):
                    continue
                evm_state.delta_token_balance(recipient, token_id_encode(k), v)

        evm_state.commit()

        meta = MinorBlockMeta(
            hash_merkle_root=bytes.fromhex(genesis.HASH_MERKLE_ROOT),
            hash_evm_state_root=evm_state.trie.root_hash,
            xshard_tx_cursor_info=XshardTxCursorInfo(root_block.header.height,
                                                     0, 0),
        )

        local_fee_rate = 1 - self._qkc_config.reward_tax_rate  # type: Fraction
        coinbase_tokens = {
            self._qkc_config.genesis_token:
            shard_config.COINBASE_AMOUNT * local_fee_rate.numerator //
            local_fee_rate.denominator
        }

        coinbase_address = Address.create_empty_account(full_shard_id)

        header = MinorBlockHeader(
            version=genesis.VERSION,
            height=genesis.HEIGHT,
            branch=branch,
            hash_prev_minor_block=bytes.fromhex(genesis.HASH_PREV_MINOR_BLOCK),
            hash_prev_root_block=root_block.header.get_hash(),
            evm_gas_limit=genesis.GAS_LIMIT,
            hash_meta=sha3_256(meta.serialize()),
            coinbase_amount_map=TokenBalanceMap(coinbase_tokens),
            coinbase_address=coinbase_address,
            create_time=genesis.TIMESTAMP,
            difficulty=genesis.DIFFICULTY,
            extra_data=bytes.fromhex(genesis.EXTRA_DATA),
        )
        return (
            MinorBlock(header=header, meta=meta, tx_list=[]),
            TokenBalanceMap(coinbase_tokens),
        )