def test_proc_balance_mnt(self): default_addr = b"\x00" * 19 + b"\x34" token_id = 1234567 token_id_bytes = token_id.to_bytes(32, byteorder="big") state = State() self.__mint(state, default_addr, token_id_bytes, encode_int32(2020)) balance = state.get_balance(default_addr, token_id) self.assertEqual(balance, 2020) data = b"\x00" * 12 + default_addr + token_id_bytes # Gas not enough msg = Message(default_addr, default_addr, gas=399, data=data) ret_tuple = proc_balance_mnt(VmExtBase(state), msg) self.assertEqual(ret_tuple, (0, 0, [])) # Success case testcases = [ (default_addr, token_id, 2020), # Balance already set (default_addr, 54321, 0), # Non-existent token (Address.create_random_account(0).recipient, token_id, 0), # Blank ] for addr, tid, bal in testcases: data = b"\x00" * 12 + addr + tid.to_bytes(32, byteorder="big") msg = Message(addr, addr, gas=500, data=data) result, gas_remained, ret = proc_balance_mnt(VmExtBase(state), msg) ret_int = int.from_bytes(ret, byteorder="big") self.assertEqual(result, 1) self.assertEqual(gas_remained, 500 - 400) self.assertEqual(ret_int, bal)
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 for k, v in alloc_amount.items(): evm_state.delta_token_balance(address.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), )
def test_proc_transfer_mnt(self): sender = b"\x00" * 19 + b"\x34" precompiled = decode_hex(b"000000000000000000000000000000514b430002") token_id = 1234567 token_id_bytes = token_id.to_bytes(32, byteorder="big") state = State() self.__mint( state, sender, token_id_encode("QKC").to_bytes(32, byteorder="big"), encode_int32(30000), ) self.__mint(state, sender, token_id_bytes, encode_int32(100)) balance = state.get_balance(sender, token_id) self.assertEqual(balance, 100) balance = state.get_balance(sender, token_id_encode("QKC")) self.assertEqual(balance, 30000) new_addr = b"\x01" * 20 testcases = [ # Format: (index, description, to, value, gas, expected ret, post check) (1, "no value transfer", sender, 0, 0, (1, 0, []), None), (2, "value transfer needs more gas", sender, 1, 8999, (0, 0, []), None), # Should have stipend gas left (3, "value transfer needs more gas", sender, 1, 9000, (1, 2300, []), None), (4, "tx on new addr needs more gas", new_addr, 1, 33999, (0, 0, []), None), ( 5, "tx on new addr needs more gas", new_addr, 42, 34000, (1, 2300, []), lambda err_msg: self.assertEqual( state.get_balance(new_addr, token_id), 42, err_msg), ), (6, "insufficient balance", sender, 333, 9000, (0, 0, []), None), (7, "target address is special", precompiled, 0, 9000, (0, 0, []), None), ] for i, desc, addr, value, gas, expected_ret, post_check in testcases: # Data = to address + token ID ++ value ++ (optional) msg data data = (b"\x00" * 12 + addr + token_id_bytes + value.to_bytes(32, byteorder="big")) msg = Message(sender, sender, gas=gas, data=data) ret = proc_transfer_mnt(VMExt(state, sender, gas_price=1), msg) self.assertEqual(ret, expected_ret, "%d: %s" % (i, desc)) if callable(post_check): post_check("%d: %s" % (i, desc))
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=[])
def test_proc_mint_mnt(self): sys_contract_addr = decode_hex( b"514b430000000000000000000000000000000002") random_addr = Address.create_random_account().recipient testcases = [(sys_contract_addr, True), (random_addr, False)] minter = b"\x00" * 19 + b"\x34" token_id = b"\x00" * 28 + b"\x11" * 4 amount = b"\x00" * 30 + b"\x22" * 2 data = b"\x00" * 12 + minter + token_id + amount for addr, expect_success in testcases: msg = Message(addr, addr, gas=34001, data=data) state = State() result, gas_remained, ret = proc_mint_mnt(VmExtBase(state), msg) balance = state.get_balance( minter, int.from_bytes(token_id, byteorder="big")) if expect_success: self.assertListEqual([result, gas_remained], [1, 34001 - 34000]) self.assertEqual(len(ret), 32) self.assertEqual(int.from_bytes(ret, byteorder="big"), 1) self.assertEqual(balance, int.from_bytes(amount, byteorder="big")) # Mint again with exactly the same parameters result, gas_remained, ret = proc_mint_mnt( VmExtBase(state), msg) balance = state.get_balance( minter, int.from_bytes(token_id, byteorder="big")) self.assertListEqual([result, gas_remained], [1, 34001 - 9000]) self.assertEqual(len(ret), 32) self.assertEqual(int.from_bytes(ret, byteorder="big"), 1) self.assertEqual(balance, 2 * int.from_bytes(amount, byteorder="big")) else: self.assertListEqual([result, gas_remained], [0, 0]) self.assertEqual(len(ret), 0) self.assertEqual(balance, 0)
def init_state(env, pre): # Setup env db = InMemoryDb() stateEnv = Env(config=konfig) stateEnv.db = db state = State( env=stateEnv, block_prevhash=decode_hex(remove_0x_head(env['previousHash'])), prev_headers=[ mk_fake_header(i) for i in range( parse_int_or_hex(env['currentNumber']) - 1, max(-1, parse_int_or_hex(env['currentNumber']) - 257), -1) ], block_number=parse_int_or_hex(env['currentNumber']), block_coinbase=decode_hex(remove_0x_head(env['currentCoinbase'])), block_difficulty=parse_int_or_hex(env['currentDifficulty']), gas_limit=parse_int_or_hex(env['currentGasLimit']), timestamp=parse_int_or_hex(env['currentTimestamp'])) # Fill up pre for address, h in list(pre.items()): assert len(address) in (40, 42) address = decode_hex(remove_0x_head(address)) assert set(h.keys()) == set(['code', 'nonce', 'balance', 'storage']) state.set_nonce(address, parse_int_or_hex(h['nonce'])) state.set_balance(address, parse_int_or_hex(h['balance'])) state.set_code(address, decode_hex(remove_0x_head(h['code']))) for k, v in h['storage'].items(): state.set_storage_data(address, big_endian_to_int(decode_hex(k[2:])), big_endian_to_int(decode_hex(v[2:]))) state.commit(allow_empties=True) # state.commit() return state
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), )
def init_state(env, pre, is_qkc_state, qkc_env=None): # Setup env db = InMemoryDb() state_env = Env(config=konfig) state_env.db = db state = State( env=state_env, block_prevhash=decode_hex(remove_0x_head(env["previousHash"])), prev_headers=[ mk_fake_header(i) for i in range( parse_int_or_hex(env["currentNumber"]) - 1, max(-1, parse_int_or_hex(env["currentNumber"]) - 257), -1, ) ], block_number=parse_int_or_hex(env["currentNumber"]), block_coinbase=decode_hex(remove_0x_head(env["currentCoinbase"])), block_difficulty=parse_int_or_hex(env["currentDifficulty"]), gas_limit=parse_int_or_hex(env["currentGasLimit"]), timestamp=parse_int_or_hex(env["currentTimestamp"]), qkc_config=qkc_env.quark_chain_config, # If testing QuarkChain states, should not use mock account use_mock_evm_account=not is_qkc_state, ) seen_token_ids = set() # Fill up pre for address, h in list(pre.items()): assert len(address) in (40, 42) address = decode_hex(remove_0x_head(address)) state.set_nonce(address, parse_int_or_hex(h["nonce"])) if is_qkc_state and "balances" in h: # In QuarkChain state tests, can either specify balance map or single balance for token_id, balance in h["balances"].items(): parsed_token_id = parse_int_or_hex(token_id) state.set_token_balance( address, parsed_token_id, parse_int_or_hex(balance) ) seen_token_ids.add(parsed_token_id) else: state.set_balance(address, parse_int_or_hex(h["balance"])) state.set_code(address, decode_hex(remove_0x_head(h["code"]))) for k, v in h["storage"].items(): state.set_storage_data( address, big_endian_to_int(decode_hex(k[2:])), big_endian_to_int(decode_hex(v[2:])), ) # Update allowed token IDs if seen_token_ids: state.qkc_config._allowed_token_ids = seen_token_ids state.commit(allow_empties=True) return state