def test_add_non_neighbor_tx_fail(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_random_account( full_shard_id=3) # not acc1's neighbor acc3 = Address.create_random_account( full_shard_id=8) # acc1's neighbor env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000, shard_size=64) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=0, gas=1000000, ) self.assertFalse(state.add_tx(tx)) self.assertEqual(len(state.tx_queue), 0) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc3, value=0, gas=1000000, ) self.assertTrue(state.add_tx(tx)) self.assertEqual(len(state.tx_queue), 1)
def test_native_token_gas(self): """in-shard transfer QETH using native token as gas TODODLL: support native token as gas """ QETH = token_id_encode("QETH") id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_random_account(full_shard_key=0) acc3 = Address.create_random_account(full_shard_key=0) env = get_test_env(genesis_account=acc1, genesis_minor_token_balances={"QETH": 10000000}) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, gas=21000, gas_token_id=QETH, transfer_token_id=QETH, ) self.assertFalse( state.add_tx(tx) ) # warning log: Failed to add transaction: Gas token must be QKC return self.assertTrue(state.add_tx(tx)) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1) state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) self.assertEqual(state.get_token_balance(id1.recipient, QETH), 10000000 - opcodes.GTXCOST) self.assertEqual( state.get_token_balance(acc1.recipient, QETH), 10000000 - opcodes.GTXCOST - 12345, ) self.assertEqual(state.get_token_balance(acc2.recipient, QETH), 12345) # tx fee self.assertEqual( state.get_token_balance(acc3.recipient, QETH), self.getAfterTaxReward(opcodes.GTXCOST), ) # miner coinbase self.assertEqual( state.get_token_balance(acc3.recipient, self.genesis_token), self.getAfterTaxReward(self.shard_coinbase), ) tx_list, _ = state.db.get_transactions_by_address(acc1) self.assertEqual(tx_list[0].value, 12345) self.assertEqual(tx_list[0].gas_token_id, QETH) self.assertEqual(tx_list[0].transfer_token_id, QETH) tx_list, _ = state.db.get_transactions_by_address(acc2) self.assertEqual(tx_list[0].value, 12345) self.assertEqual(tx_list[0].gas_token_id, QETH) self.assertEqual(tx_list[0].transfer_token_id, QETH)
def test_shard_genesis_fork_fork(self): """ Test shard forks at genesis blocks due to root chain fork at GENESIS.ROOT_HEIGHT""" acc1 = Address.create_random_account(0) acc2 = Address.create_random_account(1) with ClusterContext(2, acc1, genesis_root_heights=[0, 1]) as clusters: # shutdown cluster connection clusters[1].peer.close() master0 = clusters[0].master is_root, root0 = call_async(master0.get_next_block_to_mine(acc1)) self.assertTrue(is_root) call_async(master0.add_root_block(root0)) genesis0 = clusters[0].get_shard_state(1).db.get_minor_block_by_height(0) self.assertEqual( genesis0.header.hash_prev_root_block, root0.header.get_hash() ) master1 = clusters[1].master is_root, root1 = call_async(master1.get_next_block_to_mine(acc2)) self.assertTrue(is_root) self.assertNotEqual(root0.header.get_hash(), root1.header.get_hash()) call_async(master1.add_root_block(root1)) genesis1 = clusters[1].get_shard_state(1).db.get_minor_block_by_height(0) self.assertEqual( genesis1.header.hash_prev_root_block, root1.header.get_hash() ) # let's make cluster1's root chain longer than cluster0's # expect to mine shard 0 due to proof-of-progress is_root, block1 = call_async(master1.get_next_block_to_mine(acc2)) self.assertFalse(is_root) self.assertEqual(block1.header.branch.get_shard_id(), 0) result = call_async( master1.add_raw_minor_block(block1.header.branch, block1.serialize()) ) self.assertTrue(result) is_root, root2 = call_async(master1.get_next_block_to_mine(acc2)) self.assertTrue(is_root) call_async(master1.add_root_block(root2)) self.assertEqual(master1.root_state.tip.height, 2) # reestablish cluster connection call_async( clusters[1].network.connect( "127.0.0.1", clusters[0].master.env.cluster_config.SIMPLE_NETWORK.BOOTSTRAP_PORT, ) ) # Expect cluster0's genesis change to genesis1 assert_true_with_timeout( lambda: clusters[0] .get_shard_state(1) .db.get_minor_block_by_height(0) .header.get_hash() == genesis1.header.get_hash() ) self.assertTrue(clusters[0].get_shard_state(1).root_tip == root2.header)
def test_shard_genesis_fork_fork(self): """ Test shard forks at genesis blocks due to root chain fork at GENESIS.ROOT_HEIGHT""" acc1 = Address.create_random_account(0) acc2 = Address.create_random_account(1) genesis_root_heights = {2: 0, 3: 1} with ClusterContext( 2, acc1, chain_size=1, shard_size=2, genesis_root_heights=genesis_root_heights, ) as clusters: # shutdown cluster connection clusters[1].peer.close() master0 = clusters[0].master root0 = call_async( master0.get_next_block_to_mine(acc1, branch_value=None)) call_async(master0.add_root_block(root0)) genesis0 = (clusters[0].get_shard_state( 2 | 1).db.get_minor_block_by_height(0)) self.assertEqual(genesis0.header.hash_prev_root_block, root0.header.get_hash()) master1 = clusters[1].master root1 = call_async( master1.get_next_block_to_mine(acc2, branch_value=None)) self.assertNotEqual(root0.header.get_hash(), root1.header.get_hash()) call_async(master1.add_root_block(root1)) genesis1 = (clusters[1].get_shard_state( 2 | 1).db.get_minor_block_by_height(0)) self.assertEqual(genesis1.header.hash_prev_root_block, root1.header.get_hash()) self.assertNotEqual(genesis0.header.get_hash(), genesis1.header.get_hash()) # let's make cluster1's root chain longer than cluster0's root2 = call_async( master1.get_next_block_to_mine(acc2, branch_value=None)) call_async(master1.add_root_block(root2)) self.assertEqual(master1.root_state.tip.height, 2) # reestablish cluster connection call_async(clusters[1].network.connect( "127.0.0.1", clusters[0].master.env.cluster_config.SIMPLE_NETWORK. BOOTSTRAP_PORT, )) # Expect cluster0's genesis change to genesis1 assert_true_with_timeout(lambda: clusters[0].get_shard_state( 2 | 1).db.get_minor_block_by_height(0).header.get_hash() == genesis1.header.get_hash()) self.assertTrue( clusters[0].get_shard_state(2 | 1).root_tip == root2.header)
def test_duplicated_tx(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_random_account(full_shard_id=0) acc3 = Address.create_random_account(full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, ) self.assertTrue(state.add_tx(tx)) self.assertFalse(state.add_tx(tx)) # already in tx_queue self.assertEqual(len(state.tx_queue), 1) self.assertEqual(len(state.tx_dict), 1) block, i = state.get_transaction_by_hash(tx.get_hash()) self.assertEqual(len(block.tx_list), 1) self.assertEqual(block.tx_list[0], tx) self.assertEqual(block.header.create_time, 0) self.assertEqual(i, 0) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1) # Should succeed state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) self.assertEqual(state.get_balance(id1.recipient), 10000000 - opcodes.GTXCOST - 12345) self.assertEqual(state.get_balance(acc2.recipient), 12345) self.assertEqual(state.get_balance(acc3.recipient), opcodes.GTXCOST // 2) # Check receipts self.assertEqual(len(state.evm_state.receipts), 1) self.assertEqual(state.evm_state.receipts[0].state_root, b"\x01") self.assertEqual(state.evm_state.receipts[0].gas_used, 21000) block, i = state.get_transaction_by_hash(tx.get_hash()) self.assertEqual(block, b1) self.assertEqual(i, 0) # tx already confirmed self.assertTrue(state.db.contain_transaction_hash(tx.get_hash())) self.assertFalse(state.add_tx(tx))
def test_native_token_transfer(self): """in-shard transfer QETH using genesis_token as gas """ QETH = token_id_encode("QETH") id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_random_account(full_shard_key=0) acc3 = Address.create_random_account(full_shard_key=0) env = get_test_env( genesis_account=acc1, genesis_minor_token_balances={ self.GENESIS_TOKEN: 10000000, "QETH": 99999 }, ) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, gas=21000, gas_token_id=self.genesis_token, transfer_token_id=QETH, ) self.assertTrue(state.add_tx(tx)) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1) state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) self.assertEqual( state.get_token_balance(id1.recipient, self.genesis_token), 10000000 - opcodes.GTXCOST, ) self.assertEqual(state.get_token_balance(acc1.recipient, QETH), 99999 - 12345) self.assertEqual(state.get_token_balance(acc2.recipient, QETH), 12345) self.assertEqual( state.get_token_balance(acc3.recipient, self.genesis_token), self.getAfterTaxReward(opcodes.GTXCOST + self.shard_coinbase), ) tx_list, _ = state.db.get_transactions_by_address(acc1) self.assertEqual(tx_list[0].value, 12345) self.assertEqual(tx_list[0].gas_token_id, self.genesis_token) self.assertEqual(tx_list[0].transfer_token_id, QETH) tx_list, _ = state.db.get_transactions_by_address(acc2) self.assertEqual(tx_list[0].value, 12345) self.assertEqual(tx_list[0].gas_token_id, self.genesis_token) self.assertEqual(tx_list[0].transfer_token_id, QETH)
def test_native_token_gas(self): """in-shard transfer QETH using native token as gas """ qeth = token_id_encode("QETH") id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_random_account(full_shard_key=0) # Miner acc3 = Address.create_random_account(full_shard_key=0) env = get_test_env( genesis_account=acc1, genesis_minor_token_balances={"QETH": 10000000}, charge_gas_reserve=True, ) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, gas=21000, gas_token_id=qeth, transfer_token_id=qeth, ) self.assertTrue(state.add_tx(tx)) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1) state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) self.assertEqual( state.get_token_balance(acc1.recipient, qeth), 10000000 - opcodes.GTXCOST - 12345, ) self.assertEqual(state.get_token_balance(acc2.recipient, qeth), 12345) # after-tax coinbase + tx fee should only be in QKC self.assertEqual( state.get_token_balance(acc3.recipient, self.genesis_token), self.get_after_tax_reward(self.shard_coinbase + opcodes.GTXCOST), ) tx_list, _ = state.db.get_transactions_by_address(acc1) self.assertEqual(tx_list[0].value, 12345) self.assertEqual(tx_list[0].gas_token_id, qeth) self.assertEqual(tx_list[0].transfer_token_id, qeth) tx_list, _ = state.db.get_transactions_by_address(acc2) self.assertEqual(tx_list[0].value, 12345) self.assertEqual(tx_list[0].gas_token_id, qeth) self.assertEqual(tx_list[0].transfer_token_id, qeth)
def test_get_transaction_by_address(self): id1 = Identity.create_random_identity() miner_addr = Address.create_random_account(full_shard_key=0) acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_from_identity(id1, full_shard_key=100) acc3 = Address.create_random_account(full_shard_key=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env) # Add a root block to have all the shards initialized root_block = state.root_tip.create_block_to_append().finalize() state.add_root_block(root_block) tx1 = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, ) self.assertTrue(state.add_tx(tx1)) b1 = state.create_block_to_mine(address=miner_addr) state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) tx2 = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc3, value=54321, ) self.assertTrue(state.add_tx(tx2)) b2 = state.create_block_to_mine(address=miner_addr) state.finalize_and_add_block(b2) self.assertEqual(state.header_tip, b2.header) # acc1 and acc2 should have the same transaction history # while acc3 is different tx_list1, _ = state.db.get_transactions_by_address(acc1) self.assertListEqual([t.value for t in tx_list1], [12345, 54321]) tx_list2, _ = state.db.get_transactions_by_address(acc2) self.assertListEqual(tx_list2, tx_list1) tx_list3, _ = state.db.get_transactions_by_address(acc3) self.assertEqual(tx_list3[0].value, 54321) self.assertNotEqual(tx_list3, tx_list1)
def test_createTransactions(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_random_account(full_shard_key=1) loadtest_accounts = [ { "address": "b067ac9ebeeecb10bbcd1088317959d58d1e38f6b0ee10d5", "key": "ca0143c9aa51c3013f08e83f3b6368a4f3ba5b52c4841c6e0c22c300f7ee6827", }, { "address": "9f2b984937ff8e3f20d2a2592f342f47257870909fffa247", "key": "40efdb8528de149c35fb43a572fc821d8fbdf2469dcc7fe1a9e847ef29e3c941", }, ] with ClusterContext( 1, acc1, small_coinbase=True, loadtest_accounts=loadtest_accounts ) as clusters, jrpc_server_context(clusters[0].master): slaves = clusters[0].slave_list master = clusters[0].master block = call_async( master.get_next_block_to_mine(address=acc2, branch_value=None) ) call_async(master.add_root_block(block)) send_request("createTransactions", {"numTxPerShard": 1, "xShardPercent": 0})
def test_sendTransaction_with_bad_signature(self): """ sendTransaction validates signature """ id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_random_account(full_shard_key=1) with ClusterContext( 1, acc1, small_coinbase=True) as clusters, jrpc_server_context( clusters[0].master): master = clusters[0].master block = call_async( master.get_next_block_to_mine(address=acc2, branch_value=None)) call_async(master.add_root_block(block)) request = dict( to="0x" + acc2.recipient.hex(), gasPrice="0x6", gas=hex(30000), value="0xf", v="0x1", r="0x2", s="0x3", nonce="0x0", fromFullShardKey="0x00000000", toFullShardKey="0x00000001", ) self.assertEqual(send_request("sendTransaction", [request]), EMPTY_TX_ID) self.assertEqual(len(clusters[0].get_shard_state(2 | 0).tx_queue), 0)
def test_get_primary_account_data(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_random_account(full_shard_id=1) with ClusterContext(1, acc1) as clusters: master = clusters[0].master slaves = clusters[0].slave_list branch = Branch.create(2, 0) self.assertEqual( call_async(master.get_primary_account_data(acc1)).transaction_count, 0 ) tx = create_transfer_transaction( shard_state=slaves[0].shards[branch].state, key=id1.get_key(), from_address=acc1, to_address=acc1, value=12345, ) self.assertTrue(slaves[0].add_tx(tx)) is_root, block1 = call_async(master.get_next_block_to_mine(address=acc1)) self.assertTrue( call_async( master.add_raw_minor_block(block1.header.branch, block1.serialize()) ) ) self.assertEqual( call_async(master.get_primary_account_data(acc1)).transaction_count, 1 ) self.assertEqual( call_async(master.get_primary_account_data(acc2)).transaction_count, 0 )
def test_sendTransaction_with_bad_signature(self): """ sendTransaction validates signature """ id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = 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 branch = Branch.create(2, 0) request = dict( to="0x" + acc2.recipient.hex(), gasPrice="0x6", gas=hex(30000), value="0xf", v="0x1", r="0x2", s="0x3", nonce="0x0", fromFullShardId="0x00000000", toFullShardId="0x00000001", ) self.assertIsNone(send_request("sendTransaction", request)) self.assertEqual(len(slaves[0].shards[branch].state.tx_queue), 0)
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)
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)
def test_create_shard_at_different_height(self): acc1 = Address.create_random_account() with ClusterContext(1, acc1, genesis_root_heights=[1, 2]) as clusters: master = clusters[0].master slaves = clusters[0].slave_list self.assertEqual(len(slaves[0].shards), 0) self.assertEqual(len(slaves[1].shards), 0) is_root, root = call_async(master.get_next_block_to_mine(acc1)) self.assertTrue(is_root) self.assertEqual(len(root.minor_block_header_list), 0) call_async(master.add_root_block(root)) # shard 0 created at root height 1 self.assertEqual(len(slaves[0].shards), 1) self.assertEqual(len(slaves[1].shards), 0) is_root, root = call_async(master.get_next_block_to_mine(acc1)) self.assertTrue(is_root) self.assertEqual(len(root.minor_block_header_list), 1) call_async(master.add_root_block(root)) self.assertEqual(len(slaves[0].shards), 1) # shard 1 created at root height 2 self.assertEqual(len(slaves[1].shards), 1) # Expect to mine shard 0 due to proof of progress is_root, block = call_async(master.get_next_block_to_mine(acc1)) self.assertFalse(is_root) self.assertEqual(block.header.branch.get_shard_id(), 0) self.assertEqual(block.header.height, 1)
def test_xshard_tx_sent(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_from_identity(id1, full_shard_id=1) acc3 = Address.create_random_account(full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env, shard_id=0) env1 = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state1 = create_default_shard_state(env=env1, shard_id=1) # Add a root block to update block gas limit so that xshard tx can be included root_block = ( state.root_tip.create_block_to_append().add_minor_block_header( state.header_tip).add_minor_block_header( state1.header_tip).finalize()) state.add_root_block(root_block) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=888888, gas=opcodes.GTXXSHARDCOST + opcodes.GTXCOST, ) state.add_tx(tx) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1) self.assertEqual(state.evm_state.gas_used, 0) # Should succeed state.finalize_and_add_block(b1) self.assertEqual(len(state.evm_state.xshard_list), 1) self.assertEqual( state.evm_state.xshard_list[0], CrossShardTransactionDeposit( tx_hash=tx.get_hash(), from_address=acc1, to_address=acc2, value=888888, gas_price=1, ), ) self.assertEqual( state.get_balance(id1.recipient), 10000000 - 888888 - opcodes.GTXCOST - opcodes.GTXXSHARDCOST, ) # Make sure the xshard gas is not used by local block self.assertEqual(state.evm_state.gas_used, opcodes.GTXCOST + opcodes.GTXXSHARDCOST) # GTXXSHARDCOST is consumed by remote shard self.assertEqual(state.get_balance(acc3.recipient), opcodes.GTXCOST // 2)
def test_exceeding_xshard_limit(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_random_account(full_shard_id=1) acc3 = Address.create_random_account(full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) # a huge number to make xshard tx limit become 0 so that no xshard tx can be # included in the block env.quark_chain_config.MAX_NEIGHBORS = 10**18 state = create_default_shard_state(env=env) # xshard tx tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, gas=50000, ) self.assertTrue(state.add_tx(tx)) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 0) # inshard tx tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc3, value=12345, gas=50000, ) self.assertTrue(state.add_tx(tx)) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1)
def test_serialization_and_deserialization(self): cstor_params = { "tx_hash": bytes(32), "from_address": Address.create_random_account(1), "to_address": Address.create_random_account(1), "value": 123, "gas_price": 456, "gas_token_id": 789, "transfer_token_id": 101, } deposit_deprecated = CrossShardTransactionDepositDeprecated( **cstor_params) deposit_v0 = CrossShardTransactionDepositV0(**cstor_params) deposit_v1 = CrossShardTransactionDeposit(**cstor_params) testcases = [ (CrossShardTransactionDeprecatedList([deposit_deprecated]), None), (CrossShardTransactionListV0([deposit_v0]), None), (CrossShardTransactionList([deposit_v1]), 55), ] for ls, refund_rate_update in testcases: if refund_rate_update: ls.tx_list[0].refund_rate = refund_rate_update else: refund_rate_update = 100 # default refund rate, for comparison deserialized = CrossShardTransactionList.from_data(ls.serialize()) self.assertIsInstance(deserialized, CrossShardTransactionList) self.assertEqual(len(deserialized.tx_list), 1) deposit = deserialized.tx_list[0] self.assertIsInstance(deposit, CrossShardTransactionDeposit) self.assertEqual(deposit.refund_rate, refund_rate_update) # serialize and deserialize again deposit_deserialized_again = CrossShardTransactionDeposit.deserialize( deposit.serialize()) self.assertIsInstance(deposit_deserialized_again, CrossShardTransactionDeposit) self.assertEqual(deposit_deserialized_again.refund_rate, refund_rate_update)
def test_transaction(self): id1 = Identity.create_random_identity() acc2 = Address.create_random_account() tx = create_random_test_transaction(id1, acc2) barray = tx.serialize(bytearray()) bb = ByteBuffer(barray) tx1 = Transaction.deserialize(bb) self.assertEqual(bb.remaining(), 0) self.assertEqual(tx, tx1) self.assertTrue(tx1.verify_signature([id1.get_recipient()]))
def test_sendTransaction(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_random_account(full_shard_key=1) with ClusterContext( 1, acc1, small_coinbase=True) as clusters, jrpc_server_context( clusters[0].master): slaves = clusters[0].slave_list master = clusters[0].master block = call_async( master.get_next_block_to_mine(address=acc2, branch_value=None)) call_async(master.add_root_block(block)) evm_tx = EvmTransaction( nonce=0, gasprice=6, startgas=30000, to=acc2.recipient, value=15, data=b"", from_full_shard_key=acc1.full_shard_key, to_full_shard_key=acc2.full_shard_key, network_id=slaves[0].env.quark_chain_config.NETWORK_ID, gas_token_id=master.env.quark_chain_config.genesis_token, transfer_token_id=master.env.quark_chain_config.genesis_token, ) evm_tx.sign(id1.get_key()) request = dict( to="0x" + acc2.recipient.hex(), gasPrice="0x6", gas=hex(30000), value="0xf", # 15 v=quantity_encoder(evm_tx.v), r=quantity_encoder(evm_tx.r), s=quantity_encoder(evm_tx.s), nonce="0x0", fromFullShardKey="0x00000000", toFullShardKey="0x00000001", network_id=hex(slaves[0].env.quark_chain_config.NETWORK_ID), ) tx = TypedTransaction(SerializedEvmTransaction.from_evm_tx(evm_tx)) response = send_request("sendTransaction", [request]) self.assertEqual(response, "0x" + tx.get_hash().hex() + "00000000") state = clusters[0].get_shard_state(2 | 0) self.assertEqual(len(state.tx_queue), 1) self.assertEqual( state.tx_queue.pop_transaction(state.get_transaction_count), evm_tx)
def test_getTransactionCount(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_random_account(full_shard_key=1) with ClusterContext( 1, acc1, small_coinbase=True ) as clusters, jrpc_server_context(clusters[0].master): master = clusters[0].master slaves = clusters[0].slave_list self.assertEqual( call_async(master.get_primary_account_data(acc1)).transaction_count, 0 ) for i in range(3): tx = create_transfer_transaction( shard_state=clusters[0].get_shard_state(2 | 0), key=id1.get_key(), from_address=acc1, to_address=acc1, value=12345, ) self.assertTrue(slaves[0].add_tx(tx)) block = call_async( master.get_next_block_to_mine(address=acc1, branch_value=0b10) ) self.assertEqual(i + 1, block.header.height) self.assertTrue( call_async(clusters[0].get_shard(2 | 0).add_block(block)) ) response = send_request( "getTransactionCount", "0x" + acc2.serialize().hex() ) self.assertEqual(response, "0x0") response = send_request( "getTransactionCount", "0x" + acc1.serialize().hex() ) self.assertEqual(response, "0x3") response = send_request( "getTransactionCount", "0x" + acc1.serialize().hex(), "latest" ) self.assertEqual(response, "0x3") for i in range(3): response = send_request( "getTransactionCount", "0x" + acc1.serialize().hex(), hex(i + 1) ) self.assertEqual(response, hex(i + 1))
def test_native_token_transfer_0_value_success(self): """to prevent storage spamming, do not delta_token_balance does not take action if value is 0 """ MALICIOUS0 = token_id_encode("MALICIOUS0") id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc3 = Address.create_random_account(full_shard_key=0) env = get_test_env( genesis_account=acc1, genesis_minor_token_balances={ self.GENESIS_TOKEN: 10000000, "MALICIOUS0": 0, }, ) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc1, value=0, gas=opcodes.GTXCOST, gas_token_id=self.genesis_token, transfer_token_id=MALICIOUS0, ) self.assertTrue(state.add_tx(tx)) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1) state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) self.assertEqual( state.get_token_balance(id1.recipient, self.genesis_token), 10000000 - opcodes.GTXCOST, ) self.assertEqual(state.get_token_balance(acc1.recipient, MALICIOUS0), 0) # MALICIOUS0 shall not be in the dict self.assertNotEqual( state.get_balances(acc1.recipient), { self.genesis_token: 10000000 - opcodes.GTXCOST, MALICIOUS0: 0 }, ) self.assertEqual( state.get_balances(acc1.recipient), {self.genesis_token: 10000000 - opcodes.GTXCOST}, )
def test_add_block_receipt_root_not_match(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1) acc3 = Address.create_random_account(full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env) b1 = state.create_block_to_mine(address=acc3) # Should succeed state.finalize_and_add_block(b1) b1.finalize(evm_state=state.run_block(b1)) b1.meta.hash_evm_receipt_root = b"00" * 32 self.assertRaises(ValueError, state.add_block(b1))
def test_add_tx_incorrect_from_shard_id(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=1) acc2 = Address.create_random_account(full_shard_id=1) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env) # state is shard 0 but tx from shard 1 tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, ) self.assertFalse(state.add_tx(tx)) self.assertIsNone(state.execute_tx(tx, acc1))
def test_revert_fork_put_tx_back_to_queue(self): """Tx in the reverted chain should be put back to the queue""" id1 = Identity.create_random_identity() id2 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_from_identity(id2, full_shard_id=0) acc3 = Address.create_random_account(full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=2000000 + opcodes.GTXCOST) state = create_default_shard_state(env=env) state.add_tx( create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=1000000, )) b0 = state.create_block_to_mine(address=acc3) b1 = state.create_block_to_mine(address=acc3) state.finalize_and_add_block(b0) self.assertEqual(len(state.tx_queue), 0) b1.tx_list = [] # make b1 empty state.finalize_and_add_block(b1) self.assertEqual(len(state.tx_queue), 0) b2 = b1.create_block_to_append() state.finalize_and_add_block(b2) # now b1-b2 becomes the best chain and we expect b0 to be reverted and put the tx back to queue self.assertEqual(len(state.tx_queue), 1) b3 = b0.create_block_to_append() state.finalize_and_add_block(b3) self.assertEqual(len(state.tx_queue), 1) b4 = b3.create_block_to_append() state.finalize_and_add_block(b4) # b0-b3-b4 becomes the best chain self.assertEqual(len(state.tx_queue), 0)
def test_execute_tx(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_random_account(full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, ) # adding this line to make sure `execute_tx` would reset `gas_used` state.evm_state.gas_used = state.evm_state.gas_limit res = state.execute_tx(tx, acc1) self.assertEqual(res, b"")
def test_stale_block_count(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc3 = Address.create_random_account(full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env) b1 = state.create_block_to_mine(address=acc3) b2 = state.create_block_to_mine(address=acc3) b2.header.create_time += 1 state.finalize_and_add_block(b1) self.assertEqual(state.db.get_block_count_by_height(1), 1) state.finalize_and_add_block(b2) self.assertEqual(state.db.get_block_count_by_height(1), 2)
def test_add_invalid_tx_fail(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_random_account(full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env) tx = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=999999999999999999999, # insane ) self.assertFalse(state.add_tx(tx)) self.assertEqual(len(state.tx_queue), 0)
def test_sendTransaction(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_random_account(full_shard_id=1) with ClusterContext( 1, acc1, small_coinbase=True) as clusters, jrpc_server_context( clusters[0].master): slaves = clusters[0].slave_list branch = Branch.create(2, 0) evm_tx = EvmTransaction( nonce=0, gasprice=6, startgas=30000, to=acc2.recipient, value=15, data=b"", from_full_shard_id=acc1.full_shard_id, to_full_shard_id=acc2.full_shard_id, network_id=slaves[0].env.quark_chain_config.NETWORK_ID, ) evm_tx.sign(id1.get_key()) request = dict( to="0x" + acc2.recipient.hex(), gasPrice="0x6", gas=hex(30000), value="0xf", # 15 v=quantity_encoder(evm_tx.v), r=quantity_encoder(evm_tx.r), s=quantity_encoder(evm_tx.s), nonce="0x0", fromFullShardId="0x00000000", toFullShardId="0x00000001", network_id=hex(slaves[0].env.quark_chain_config.NETWORK_ID), ) tx = Transaction(code=Code.create_evm_code(evm_tx)) response = send_request("sendTransaction", [request]) self.assertEqual(response, "0x" + tx.get_hash().hex() + "00000000") self.assertEqual(len(slaves[0].shards[branch].state.tx_queue), 1) self.assertEqual( slaves[0].shards[branch].state.tx_queue.pop_transaction(), evm_tx)
def test_estimate_gas(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_random_account(full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=10000000) state = create_default_shard_state(env=env) tx_gen = lambda data: create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=12345, data=data, ) tx = tx_gen(b"") estimate = state.estimate_gas(tx, acc1) self.assertEqual(estimate, 21000) tx = tx_gen(b"12123478123412348125936583475758") estimate = state.estimate_gas(tx, acc1) self.assertEqual(estimate, 23176)