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_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_getMinorBlock(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) 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 ) 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)) _, block1 = call_async(master.get_next_block_to_mine(address=acc1)) self.assertTrue(call_async(clusters[0].get_shard(2 | 0).add_block(block1))) # By id resp = send_request( "getMinorBlockById", "0x" + block1.header.get_hash().hex() + "0" * 8, False, ) self.assertEqual( resp["transactions"][0], "0x" + tx.get_hash().hex() + "00000002" ) resp = send_request( "getMinorBlockById", "0x" + block1.header.get_hash().hex() + "0" * 8, True, ) self.assertEqual( resp["transactions"][0]["hash"], "0x" + tx.get_hash().hex() ) resp = send_request("getMinorBlockById", "0x" + "ff" * 36, True) self.assertIsNone(resp) # By height resp = send_request("getMinorBlockByHeight", "0x0", "0x1", False) self.assertEqual( resp["transactions"][0], "0x" + tx.get_hash().hex() + "00000002" ) resp = send_request("getMinorBlockByHeight", "0x0", "0x1", True) self.assertEqual( resp["transactions"][0]["hash"], "0x" + tx.get_hash().hex() ) resp = send_request("getMinorBlockByHeight", "0x1", "0x2", False) self.assertIsNone(resp) resp = send_request("getMinorBlockByHeight", "0x0", "0x4", False) self.assertIsNone(resp)
def test_getCode(self): key = bytes.fromhex( "c987d4506fb6824639f9a9e3b8834584f5165e94680501d1b0044071cd36c3b3" ) id1 = Identity.create_from_key(key) acc1 = Address.create_from_identity(id1, full_shard_key=0) created_addr = "0x8531eb33bba796115f56ffa1b7df1ea3acdd8cdd00000000" with ClusterContext( 1, acc1, small_coinbase=True ) as clusters, jrpc_server_context(clusters[0].master): master = clusters[0].master slaves = clusters[0].slave_list tx = create_contract_with_storage_transaction( shard_state=clusters[0].get_shard_state(2 | 0), key=id1.get_key(), from_address=acc1, to_full_shard_key=acc1.full_shard_key, ) self.assertTrue(slaves[0].add_tx(tx)) _, block = call_async(master.get_next_block_to_mine(address=acc1)) self.assertTrue(call_async(clusters[0].get_shard(2 | 0).add_block(block))) for using_eth_endpoint in (True, False): if using_eth_endpoint: resp = send_request("eth_getCode", created_addr[:-8], "0x0") else: resp = send_request("getCode", created_addr) self.assertEqual( resp, "0x6080604052600080fd00a165627a7a72305820a6ef942c101f06333ac35072a8ff40332c71d0e11cd0e6d86de8cae7b42696550029", )
def test_getTransactionReceipt_on_transfer(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): master = clusters[0].master slaves = clusters[0].slave_list branch = Branch.create(2, 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)) _, block1 = call_async(master.get_next_block_to_mine(address=acc1)) self.assertTrue( call_async(clusters[0].get_shard(0).add_block(block1))) for endpoint in ("getTransactionReceipt", "eth_getTransactionReceipt"): resp = send_request( endpoint, "0x" + tx.get_hash().hex() + acc1.full_shard_id.to_bytes(4, "big").hex(), ) self.assertEqual(resp["transactionHash"], "0x" + tx.get_hash().hex()) self.assertEqual(resp["status"], "0x1") self.assertEqual(resp["cumulativeGasUsed"], "0x5208") self.assertIsNone(resp["contractAddress"])
def test_broadcast_cross_shard_transactions_to_neighbor_only(self): """ Test the broadcast is only done to the neighbors """ id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) # create 64 shards so that the neighbor rule can kick in # explicitly set num_slaves to 4 so that it does not spin up 64 slaves with ClusterContext(1, acc1, shard_size=64, num_slaves=4) as clusters: master = clusters[0].master # Add a root block first so that later minor blocks referring to this root # can be broadcasted to other shards root_block = call_async( master.get_next_block_to_mine(Address.create_empty_account(), branch_value=None)) call_async(master.add_root_block(root_block)) b1 = clusters[0].get_shard_state(64).create_block_to_mine( address=acc1) self.assertTrue( call_async( master.add_raw_minor_block(b1.header.branch, b1.serialize()))) neighbor_shards = [2**i for i in range(6)] for shard_id in range(64): xshard_tx_list = (clusters[0].get_shard_state( 64 | shard_id).db.get_minor_block_xshard_tx_list( b1.header.get_hash())) # Only neighbor should have it if shard_id in neighbor_shards: self.assertIsNotNone(xshard_tx_list) else: self.assertIsNone(xshard_tx_list)
def test_getTransactionById(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): 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)) _, block1 = call_async(master.get_next_block_to_mine(address=acc1)) self.assertTrue( call_async(clusters[0].get_shard(0).add_block(block1))) resp = send_request( "getTransactionById", "0x" + tx.get_hash().hex() + acc1.full_shard_id.to_bytes(4, "big").hex(), ) self.assertEqual(resp["hash"], "0x" + tx.get_hash().hex())
def test_sendTransaction_missing_from_full_shard_key(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) 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=acc1, branch_value=None)) call_async(master.add_root_block(block)) request = dict( to="0x" + acc1.recipient.hex(), gasPrice="0x6", gas=hex(30000), value="0xf", v="0x1", r="0x2", s="0x3", nonce="0x0", ) with self.assertRaises(Exception): send_request("sendTransaction", [request])
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_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_add_minor_block_request_list(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) with ClusterContext(2, acc1) as clusters: shard_state = clusters[0].slave_list[0].shards[Branch(0b10)].state coinbase_amount = (shard_state.env.quark_chain_config.shards[ shard_state.full_shard_id].COINBASE_AMOUNT // 2) b1 = shard_state.get_tip().create_block_to_append() evm_state = shard_state.run_block(b1) b1.finalize( evm_state=evm_state, coinbase_amount=evm_state.block_fee + coinbase_amount, ) add_result = call_async(clusters[0].master.add_raw_minor_block( b1.header.branch, b1.serialize())) self.assertTrue(add_result) # Make sure the xshard list is not broadcasted to the other shard self.assertFalse(clusters[0].slave_list[1].shards[Branch( 0b11)].state.contain_remote_minor_block_hash( b1.header.get_hash())) self.assertTrue( clusters[0].master.root_state.is_minor_block_validated( b1.header.get_hash())) # Make sure another cluster received the new block assert_true_with_timeout( lambda: clusters[1].slave_list[0].shards[Branch( 0b10)].state.contain_block_by_hash(b1.header.get_hash())) assert_true_with_timeout( lambda: clusters[1].master.root_state.is_minor_block_validated( b1.header.get_hash()))
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_gasPrice(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): master = clusters[0].master slaves = clusters[0].slave_list branch = Branch.create(2, 0) # run for multiple times for _ in range(3): tx = create_transfer_transaction( shard_state=slaves[0].shards[branch].state, key=id1.get_key(), from_address=acc1, to_address=acc1, value=0, gas_price=12, ) self.assertTrue(slaves[0].add_tx(tx)) _, block = call_async( master.get_next_block_to_mine(address=acc1)) self.assertTrue( call_async(clusters[0].get_shard(0).add_block(block))) for using_eth_endpoint in (True, False): if using_eth_endpoint: resp = send_request("eth_gasPrice", "0x0") else: resp = send_request("gasPrice", "0x0") self.assertEqual(resp, "0xc")
def test_getStorageAt(self): key = bytes.fromhex( "c987d4506fb6824639f9a9e3b8834584f5165e94680501d1b0044071cd36c3b3" ) id1 = Identity.create_from_key(key) acc1 = Address.create_from_identity(id1, full_shard_key=0) created_addr = "0x8531eb33bba796115f56ffa1b7df1ea3acdd8cdd00000000" with ClusterContext( 1, acc1, small_coinbase=True ) as clusters, jrpc_server_context(clusters[0].master): master = clusters[0].master slaves = clusters[0].slave_list tx = create_contract_with_storage_transaction( shard_state=clusters[0].get_shard_state(2 | 0), key=id1.get_key(), from_address=acc1, to_full_shard_key=acc1.full_shard_key, ) self.assertTrue(slaves[0].add_tx(tx)) block = call_async( master.get_next_block_to_mine(address=acc1, branch_value=0b10) ) self.assertTrue(call_async(clusters[0].get_shard(2 | 0).add_block(block))) for using_eth_endpoint in (True, False): if using_eth_endpoint: req = lambda k: send_request( "eth_getStorageAt", created_addr[:-8], k, "0x0" ) else: req = lambda k: send_request("getStorageAt", created_addr, k) # first storage response = req("0x0") # equals 1234 self.assertEqual( response, "0x00000000000000000000000000000000000000000000000000000000000004d2", ) # mapping storage k = sha3_256( bytes.fromhex(acc1.recipient.hex().zfill(64) + "1".zfill(64)) ) response = req("0x" + k.hex()) self.assertEqual( response, "0x000000000000000000000000000000000000000000000000000000000000162e", ) # doesn't exist response = req("0x3") self.assertEqual( response, "0x0000000000000000000000000000000000000000000000000000000000000000", )
def test_shard_synchronizer_with_fork(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) with ClusterContext(2, acc1) as clusters: # shutdown cluster connection clusters[1].peer.close() block_list = [] # cluster 0 has 13 blocks added shard_state0 = clusters[0].get_shard_state(0b10) for i in range(13): block = _tip_gen(shard_state0) add_result = call_async(clusters[0].master.add_raw_minor_block( block.header.branch, block.serialize())) self.assertTrue(add_result) block_list.append(block) self.assertEqual( clusters[0].get_shard_state(0b10).header_tip.height, 13) # cluster 1 has 12 blocks added shard_state0 = clusters[1].get_shard_state(0b10) for i in range(12): block = _tip_gen(shard_state0) add_result = call_async(clusters[1].master.add_raw_minor_block( block.header.branch, block.serialize())) self.assertTrue(add_result) self.assertEqual( clusters[1].get_shard_state(0b10).header_tip.height, 12) # reestablish cluster connection call_async(clusters[1].network.connect( "127.0.0.1", clusters[0].master.env.cluster_config.SIMPLE_NETWORK. BOOTSTRAP_PORT, )) # a new block from cluster 0 will trigger sync in cluster 1 shard_state0 = clusters[0].get_shard_state(0b10) block = _tip_gen(shard_state0) add_result = call_async(clusters[0].master.add_raw_minor_block( block.header.branch, block.serialize())) self.assertTrue(add_result) block_list.append(block) # expect cluster 1 has all the blocks from cluter 0 and # has the same tip as cluster 0 for block in block_list: assert_true_with_timeout( lambda: clusters[1].slave_list[0].shards[Branch(0b10)]. state.contain_block_by_hash(block.header.get_hash())) assert_true_with_timeout( lambda: clusters[1].master.root_state.db. contain_minor_block_by_hash(block.header.get_hash())) self.assertEqual( clusters[1].get_shard_state(0b10).header_tip, clusters[0].get_shard_state(0b10).header_tip, )
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_getTransactionReceipt_on_x_shard_transfer(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) with ClusterContext( 1, acc1, small_coinbase=True) as clusters, jrpc_server_context( clusters[0].master): master = clusters[0].master slaves = clusters[0].slave_list is_root, block = call_async( master.get_next_block_to_mine(address=acc2)) self.assertTrue(is_root) call_async(master.add_root_block(block)) s1, s2 = clusters[0].get_shard_state( 0), clusters[0].get_shard_state(1) tx_gen = lambda s, f, t: create_transfer_transaction( shard_state=s, key=id1.get_key(), from_address=f, to_address=t, gas=21000 if f == t else 30000, value=12345, ) self.assertTrue(slaves[0].add_tx(tx_gen(s1, acc1, acc2))) _, b1 = call_async(master.get_next_block_to_mine(address=acc1)) self.assertTrue(call_async(clusters[0].get_shard(0).add_block(b1))) _, b2 = call_async(master.get_next_block_to_mine(address=acc2)) self.assertTrue(call_async(clusters[0].get_shard(1).add_block(b2))) _, root_block = call_async( master.get_next_block_to_mine(address=acc1, prefer_root=True)) call_async(master.add_root_block(root_block)) tx = tx_gen(s2, acc2, acc2) self.assertTrue(slaves[1].add_tx(tx)) _, b3 = call_async(master.get_next_block_to_mine(address=acc2)) self.assertTrue(call_async(clusters[0].get_shard(1).add_block(b3))) # in-shard tx 21000 + receiving x-shard tx 9000 self.assertEqual(s2.evm_state.gas_used, 30000) self.assertEqual(s2.evm_state.xshard_receive_gas_used, 9000) for endpoint in ("getTransactionReceipt", "eth_getTransactionReceipt"): resp = send_request( endpoint, "0x" + tx.get_hash().hex() + acc2.full_shard_id.to_bytes(4, "big").hex(), ) self.assertEqual(resp["transactionHash"], "0x" + tx.get_hash().hex()) self.assertEqual(resp["status"], "0x1") self.assertEqual(resp["cumulativeGasUsed"], hex(30000)) self.assertEqual(resp["gasUsed"], hex(21000)) self.assertIsNone(resp["contractAddress"])
def test_getTransactionReceipt_not_exist(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): resp = send_request("getTransactionReceipt", "0x" + bytes(36).hex()) self.assertIsNone(resp)
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_getWork_and_submitWork(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) with ClusterContext( 1, acc1, remote_mining=True, shard_size=1, small_coinbase=True) as clusters, jrpc_server_context( clusters[0].master): master = clusters[0].master slaves = clusters[0].slave_list tx = create_transfer_transaction( shard_state=clusters[0].get_shard_state(1 | 0), key=id1.get_key(), from_address=acc1, to_address=acc1, value=0, gas_price=12, ) self.assertTrue(slaves[0].add_tx(tx)) for shard_id in ["0x0", None]: # shard, then root resp = send_request("getWork", [shard_id]) self.assertEqual(resp[1:], ["0x1", "0xa"]) # height and diff header_hash_hex = resp[0] if shard_id is not None: # shard 0 miner_address = Address.create_from( master.env.quark_chain_config.shards[1]. COINBASE_ADDRESS) else: # root miner_address = Address.create_from( master.env.quark_chain_config.ROOT.COINBASE_ADDRESS) block = call_async( master.get_next_block_to_mine(address=miner_address, branch_value=shard_id and 0b01)) # solve it and submit work = MiningWork(bytes.fromhex(header_hash_hex[2:]), 1, 10) solver = DoubleSHA256(work) nonce = solver.mine(0, 10000).nonce mixhash = "0x" + sha3_256(b"").hex() resp = send_request( "submitWork", [ shard_id, header_hash_hex, hex(nonce), mixhash, "0x" + bytes(65).hex(), ], ) self.assertTrue(resp) # show progress on shard 0 self.assertEqual( clusters[0].get_shard_state(1 | 0).get_tip().header.height, 1)
def test_estimateGas(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("estimateGas", {"to": "0x" + acc1.serialize().hex()}) self.assertEqual(response, "0x5208") # 21000
def test_getTransactionReceipt_not_exist(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) with ClusterContext( 1, acc1, small_coinbase=True) as clusters, jrpc_server_context( clusters[0].master): for endpoint in ("getTransactionReceipt", "eth_getTransactionReceipt"): resp = send_request(endpoint, ["0x" + bytes(36).hex()]) self.assertIsNone(resp)
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_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_getWork_and_submitWork(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) with ClusterContext( 1, acc1, remote_mining=True, shard_size=1 ) as clusters, jrpc_server_context(clusters[0].master): master = clusters[0].master slaves = clusters[0].slave_list branch = Branch.create(1, 0) tx = create_transfer_transaction( shard_state=slaves[0].shards[branch].state, key=id1.get_key(), from_address=acc1, to_address=acc1, value=0, gas_price=12, ) self.assertTrue(slaves[0].add_tx(tx)) for shard_id in ["0x0", None]: # shard, then root resp = send_request("getWork", shard_id) self.assertEqual(resp[1:], ["0x1", "0xa"]) # height and diff header_hash_hex = resp[0] _, block = call_async( master.get_next_block_to_mine( address=master.env.quark_chain_config.miner_address, prefer_root=shard_id is None, ) ) self.assertEqual( header_hash_hex[2:], block.header.get_hash_for_mining().hex() ) # solve it and submit work = MiningWork(bytes.fromhex(resp[0][2:]), 1, 10) solver = DoubleSHA256(work) nonce = solver.mine(0, 10000).nonce mixhash = "0x" + sha3_256(b"").hex() resp = send_request( "submitWork", shard_id, header_hash_hex, hex(nonce), mixhash ) self.assertTrue(resp) # show progress _, new_block = call_async(master.get_next_block_to_mine(address=acc1)) self.assertIsInstance(new_block, MinorBlock) self.assertEqual(new_block.header.height, 2)
def test_get_work_from_slave(self): genesis = Address.create_empty_account(full_shard_key=0) with ClusterContext(1, genesis, remote_mining=True) as clusters: slaves = clusters[0].slave_list # no posw state = clusters[0].get_shard_state(2 | 0) branch = state.create_block_to_mine().header.branch work = call_async(slaves[0].get_work(branch)) self.assertEqual(work.difficulty, 10) # enable posw, with total stakes equal genesis balance state.shard_config.POSW_CONFIG.ENABLED = True state.shard_config.POSW_CONFIG.TOTAL_STAKE_PER_BLOCK = 1000000 work = call_async(slaves[0].get_work(branch)) self.assertEqual(work.difficulty, 0)
def test_add_transaction(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_from_identity(id1, full_shard_key=1) with ClusterContext(2, acc1) as clusters: master = clusters[0].master root = call_async( master.get_next_block_to_mine(acc1, branch_value=None)) call_async(master.add_root_block(root)) tx1 = create_transfer_transaction( shard_state=clusters[0].get_shard_state(0b10), key=id1.get_key(), from_address=acc1, to_address=acc1, value=12345, ) self.assertTrue(call_async(master.add_transaction(tx1))) self.assertEqual(len(clusters[0].get_shard_state(0b10).tx_queue), 1) branch1 = Branch(2 | 1) tx2 = create_transfer_transaction( shard_state=clusters[0].get_shard_state(0b11), key=id1.get_key(), from_address=acc2, to_address=acc1, value=12345, gas=30000, ) self.assertTrue(call_async(master.add_transaction(tx2))) self.assertEqual(len(clusters[0].get_shard_state(0b11).tx_queue), 1) # check the tx is received by the other cluster tx_queue = clusters[1].get_shard_state(0b10).tx_queue assert_true_with_timeout(lambda: len(tx_queue) == 1) self.assertEqual(tx_queue.pop_transaction(), tx1.tx.to_evm_tx()) tx_queue = clusters[1].get_shard_state(0b11).tx_queue assert_true_with_timeout(lambda: len(tx_queue) == 1) self.assertEqual(tx_queue.pop_transaction(), tx2.tx.to_evm_tx())
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_sendTransaction_missing_from_full_shard_id(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): request = dict( to="0x" + acc1.recipient.hex(), gasPrice="0x6", gas=hex(30000), value="0xf", v="0x1", r="0x2", s="0x3", nonce="0x0", ) with self.assertRaises(Exception): send_request("sendTransaction", request)
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)