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_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_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_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_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_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_gasPrice(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=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 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_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_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_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_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_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_getTransactionReceipt_on_x_shard_transfer(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( 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(2 | 0), clusters[0].get_shard_state(2 | 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(2 | 0).add_block(b1))) _, 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[0].add_tx(tx)) _, b3 = call_async(master.get_next_block_to_mine(address=acc2)) self.assertTrue(call_async(clusters[0].get_shard(2 | 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_key.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_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) is_root, block = 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))
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, small_coinbase=True) 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] if shard_id is not None: # shard 0 miner_address = Address.create_from( master.env.quark_chain_config.SHARD_LIST[0]. 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, 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 on shard 0 _, 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_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_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 send_request(*args): async def __send_request(*args): async with aiohttp.ClientSession(loop=asyncio.get_event_loop()) as session: client = aiohttpClient(session, "http://localhost:38391") response = await client.request(*args) return response return call_async(__send_request(*args))
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_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 slaves = clusters[0].slave_list branch0 = Branch(2) tx1 = create_transfer_transaction( shard_state=slaves[0].shards[branch0].state, key=id1.get_key(), from_address=acc1, to_address=acc1, value=12345, ) self.assertTrue(call_async(master.add_transaction(tx1))) self.assertEqual(len(slaves[0].shards[branch0].state.tx_queue), 1) branch1 = Branch(2 | 1) tx2 = create_transfer_transaction( shard_state=slaves[1].shards[branch1].state, 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(slaves[1].shards[branch1].state.tx_queue), 1) # check the tx is received by the other cluster tx_queue = clusters[1].slave_list[0].shards[branch0].state.tx_queue assert_true_with_timeout(lambda: len(tx_queue) == 1) self.assertEqual(tx_queue.pop_transaction(), tx1.code.get_evm_transaction()) tx_queue = clusters[1].slave_list[1].shards[branch1].state.tx_queue assert_true_with_timeout(lambda: len(tx_queue) == 1) self.assertEqual(tx_queue.pop_transaction(), tx2.code.get_evm_transaction())
def test_getTransactionReceipt_on_contract_creation_failure(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=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 # Add a root block to update block gas limit for xshard tx throttling # so that the following tx can be processed is_root, root_block = call_async( master.get_next_block_to_mine(acc1)) self.assertTrue(is_root) call_async(master.add_root_block(root_block)) branch = Branch.create(2, 0) to_full_shard_id = (acc1.full_shard_id + 1 ) # x-shard contract creation should fail tx = create_contract_creation_transaction( shard_state=slaves[0].shards[branch].state, key=id1.get_key(), from_address=acc1, to_full_shard_id=to_full_shard_id, ) 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() + branch.serialize().hex()) self.assertEqual(resp["transactionHash"], "0x" + tx.get_hash().hex()) self.assertEqual(resp["status"], "0x0") self.assertEqual(resp["cumulativeGasUsed"], "0x13d6c") self.assertIsNone(resp["contractAddress"])
def test_new_block_pool(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) with ClusterContext(1, acc1) as clusters: shard_state = clusters[0].get_shard_state(0b10) b1 = _tip_gen(shard_state) add_result = call_async(clusters[0].master.add_raw_minor_block( b1.header.branch, b1.serialize())) self.assertTrue(add_result) # Update config to force checking diff clusters[ 0].master.env.quark_chain_config.SKIP_MINOR_DIFFICULTY_CHECK = False b2 = b1.create_block_to_append(difficulty=12345) shard = clusters[0].slave_list[0].shards[b2.header.branch] with self.assertRaises(ValueError): call_async(shard.handle_new_block(b2)) # Also the block should not exist in new block pool self.assertTrue( b2.header.get_hash() not in shard.state.new_block_pool)
def test_getTransactionReceipt_on_contract_creation(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 to_full_shard_key = acc1.full_shard_key + 2 tx = create_contract_creation_transaction( shard_state=clusters[0].get_shard_state(2 | 0), key=id1.get_key(), from_address=acc1, to_full_shard_key=to_full_shard_key, ) self.assertTrue(slaves[0].add_tx(tx)) block1 = 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(block1))) for endpoint in ("getTransactionReceipt", "eth_getTransactionReceipt"): resp = send_request(endpoint, ["0x" + tx.get_hash().hex() + "00000002"]) self.assertEqual(resp["transactionHash"], "0x" + tx.get_hash().hex()) self.assertEqual(resp["status"], "0x1") self.assertEqual(resp["cumulativeGasUsed"], "0x213eb") contract_address = mk_contract_address(acc1.recipient, 0, to_full_shard_key) self.assertEqual( resp["contractAddress"], "0x" + contract_address.hex() + to_full_shard_key.to_bytes(4, "big").hex(), )
def test_getTransactionReceipt_on_contract_creation(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) to_full_shard_id = acc1.full_shard_id + 2 tx = create_contract_creation_transaction( shard_state=slaves[0].shards[branch].state, key=id1.get_key(), from_address=acc1, to_full_shard_id=to_full_shard_id, ) 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( "getTransactionReceipt", "0x" + tx.get_hash().hex() + branch.serialize().hex(), ) self.assertEqual(resp["transactionHash"], "0x" + tx.get_hash().hex()) self.assertEqual(resp["status"], "0x1") self.assertEqual(resp["cumulativeGasUsed"], "0x213eb") contract_address = mk_contract_address(acc1.recipient, to_full_shard_id, 0) self.assertEqual( resp["contractAddress"], "0x" + contract_address.hex() + to_full_shard_id.to_bytes(4, "big").hex(), )
def test_create_shard_at_different_height(self): acc1 = Address.create_random_account() id1 = 0 << 16 | 1 | 0 id2 = 1 << 16 | 1 | 0 genesis_root_heights = {id1: 1, id2: 2} with ClusterContext( 1, acc1, chain_size=2, shard_size=1, genesis_root_heights=genesis_root_heights, ) as clusters: master = clusters[0].master self.assertIsNone(clusters[0].get_shard(id1)) self.assertIsNone(clusters[0].get_shard(id2)) 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.assertIsNotNone(clusters[0].get_shard(id1)) self.assertIsNone(clusters[0].get_shard(id2)) 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.assertIsNotNone(clusters[0].get_shard(id1)) # shard 1 created at root height 2 self.assertIsNotNone(clusters[0].get_shard(id2)) is_root, block = 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))
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=acc1)) self.assertEqual(header_hash_hex[2:], block.header.get_hash_for_mining().hex()) # solve it and submit solver = DoubleSHA256(block) mined = solver.mine(0, 1000) self.assertTrue(mined) nonce_found = "0x" + solver.nonce_found.hex() mixhash = "0x" + sha3_256(b"").hex() resp = send_request("submitWork", shard_id, header_hash_hex, nonce_found, mixhash) # FIXME: also verify root chain block addition after fixing https://github.com/QuarkChain/pyquarkchain/issues/130 if shard_id is not None: self.assertTrue(resp)
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].get_shard_state(0b10) b1 = _tip_gen(shard_state) 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].get_shard_state( 0b11).contain_remote_minor_block_hash(b1.header.get_hash())) self.assertTrue( clusters[0].master.root_state.db.contain_minor_block_by_hash( b1.header.get_hash())) # Make sure another cluster received the new block assert_true_with_timeout(lambda: clusters[0].get_shard_state( 0b10).contain_block_by_hash(b1.header.get_hash())) assert_true_with_timeout( lambda: clusters[1].master.root_state.db. contain_minor_block_by_hash(b1.header.get_hash()))
def create_test_clusters( num_cluster, genesis_account, chain_size, shard_size, num_slaves, genesis_root_heights, genesis_minor_quarkash, remote_mining=False, small_coinbase=False, loadtest_accounts=None, connect=True, # connect the bootstrap node by default should_set_gas_price_limit=False, mblock_coinbase_amount=None, ): # so we can have lower minimum diff easy_diff_calc = EthDifficultyCalculator( cutoff=45, diff_factor=2048, minimum_diff=10 ) bootstrap_port = get_next_port() # first cluster will listen on this port cluster_list = [] loop = asyncio.get_event_loop() for i in range(num_cluster): env = get_test_env( genesis_account, genesis_minor_quarkash=genesis_minor_quarkash, chain_size=chain_size, shard_size=shard_size, genesis_root_heights=genesis_root_heights, remote_mining=remote_mining, ) env.cluster_config.P2P_PORT = bootstrap_port if i == 0 else get_next_port() env.cluster_config.JSON_RPC_PORT = get_next_port() env.cluster_config.PRIVATE_JSON_RPC_PORT = get_next_port() env.cluster_config.SIMPLE_NETWORK = SimpleNetworkConfig() env.cluster_config.SIMPLE_NETWORK.BOOTSTRAP_PORT = bootstrap_port env.quark_chain_config.loadtest_accounts = loadtest_accounts or [] if should_set_gas_price_limit: env.quark_chain_config.MIN_TX_POOL_GAS_PRICE = 10 env.quark_chain_config.MIN_MINING_GAS_PRICE = 10 if small_coinbase: # prevent breaking previous tests after tweaking default rewards env.quark_chain_config.ROOT.COINBASE_AMOUNT = 5 for c in env.quark_chain_config.shards.values(): c.COINBASE_AMOUNT = 5 if mblock_coinbase_amount is not None: for c in env.quark_chain_config.shards.values(): c.COINBASE_AMOUNT = mblock_coinbase_amount env.cluster_config.SLAVE_LIST = [] check(is_p2(num_slaves)) for j in range(num_slaves): slave_config = SlaveConfig() slave_config.ID = "S{}".format(j) slave_config.PORT = get_next_port() slave_config.FULL_SHARD_ID_LIST = [] env.cluster_config.SLAVE_LIST.append(slave_config) full_shard_ids = [ (i << 16) + shard_size + j for i in range(chain_size) for j in range(shard_size) ] for i, full_shard_id in enumerate(full_shard_ids): slave = env.cluster_config.SLAVE_LIST[i % num_slaves] slave.FULL_SHARD_ID_LIST.append(full_shard_id) slave_server_list = [] for j in range(num_slaves): slave_env = env.copy() slave_env.db = InMemoryDb() slave_env.slave_config = env.cluster_config.get_slave_config( "S{}".format(j) ) slave_server = SlaveServer(slave_env, name="cluster{}_slave{}".format(i, j)) slave_server.start() slave_server_list.append(slave_server) root_state = RootState(env, diff_calc=easy_diff_calc) master_server = MasterServer(env, root_state, name="cluster{}_master".format(i)) master_server.start() # Wait until the cluster is ready loop.run_until_complete(master_server.cluster_active_future) # Substitute diff calculate with an easier one for slave in slave_server_list: for shard in slave.shards.values(): shard.state.diff_calc = easy_diff_calc # Start simple network and connect to seed host network = SimpleNetwork(env, master_server, loop) network.start_server() if connect and i != 0: peer = call_async(network.connect("127.0.0.1", bootstrap_port)) else: peer = None cluster_list.append(Cluster(master_server, slave_server_list, network, peer)) return cluster_list