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)) root = call_async( master.get_next_block_to_mine(acc1, branch_value=None)) 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)) root = call_async( master.get_next_block_to_mine(acc1, branch_value=None)) 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)) block = call_async( master.get_next_block_to_mine(acc1, branch_value=None)) self.assertEqual(len(root.minor_block_header_list), 1) call_async(master.add_root_block(root))
def update( self, chain_size, shard_size_per_chain, root_block_time, minor_block_time, default_token, ): self.CHAIN_SIZE = chain_size self.ROOT = RootConfig() self.ROOT.CONSENSUS_TYPE = ConsensusType.POW_SIMULATE self.ROOT.CONSENSUS_CONFIG = POWConfig() self.ROOT.CONSENSUS_CONFIG.TARGET_BLOCK_TIME = root_block_time self.GENESIS_TOKEN = default_token self.CHAINS = [] self.shards = dict() for chain_id in range(chain_size): chain_config = ChainConfig() chain_config.CHAIN_ID = chain_id chain_config.SHARD_SIZE = shard_size_per_chain chain_config.CONSENSUS_TYPE = ConsensusType.POW_SIMULATE chain_config.CONSENSUS_CONFIG = POWConfig() chain_config.CONSENSUS_CONFIG.TARGET_BLOCK_TIME = minor_block_time chain_config.DEFAULT_CHAIN_TOKEN = default_token self.CHAINS.append(chain_config) for shard_id in range(shard_size_per_chain): shard_config = ShardConfig(chain_config) shard_config.root_config = self.ROOT shard_config.SHARD_ID = shard_id shard_config.COINBASE_ADDRESS = ( Address.create_from(shard_config.COINBASE_ADDRESS) .address_in_shard(shard_config.get_full_shard_id()) .serialize() .hex() ) self.shards[shard_config.get_full_shard_id()] = shard_config self.init_and_validate()
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)) 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, to_full_shard_key, 0) self.assertEqual( resp["contractAddress"], "0x" + contract_address.hex() + to_full_shard_key.to_bytes(4, "big").hex(), )
def test_fork_resolve(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, 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) b0 = state.get_tip().create_block_to_append() b1 = state.get_tip().create_block_to_append() state.finalize_and_add_block(b0) self.assertEqual(state.header_tip, b0.header) # Fork happens, first come first serve state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b0.header) # Longer fork happens, override existing one b2 = b1.create_block_to_append() state.finalize_and_add_block(b2) self.assertEqual(state.header_tip, b2.header)
def test_add_minor_block_request_list(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) with ClusterContext(2, acc1) as clusters: shard_state = clusters[0].slave_list[0].shards[Branch(0b10)].state b1 = shard_state.get_tip().create_block_to_append() b1.finalize(evm_state=shard_state.run_block(b1)) addResult = call_async( clusters[0].master.add_raw_minor_block(b1.header.branch, b1.serialize()) ) self.assertTrue(addResult) # 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_gasPrice(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 # run for multiple times for _ 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=0, gas_price=12, ) 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: resp = send_request("eth_gasPrice", ["0x0"]) else: resp = send_request( "gasPrice", ["0x0", quantity_encoder(token_id_encode("QKC"))]) self.assertEqual(resp, "0xc")
def __init_miner(self): miner_address = Address.create_from( self.env.quark_chain_config.SHARD_LIST[ self.shard_id].COINBASE_ADDRESS) async def __create_block(retry=True): # hold off mining if the shard is syncing while self.synchronizer.running or not self.state.initialized: if not retry: break await asyncio.sleep(0.1) return self.state.create_block_to_mine(address=miner_address) async def __add_block(block): # Do not add block if there is a sync in progress if self.synchronizer.running: return # Do not add stale block if self.state.header_tip.height >= block.header.height: return await self.handle_new_block(block) def __get_mining_param(): return { "target_block_time": self.slave.artificial_tx_config.target_minor_block_time } shard_config = self.env.quark_chain_config.SHARD_LIST[ self.shard_id] # type: ShardConfig self.miner = Miner( shard_config.CONSENSUS_TYPE, __create_block, __add_block, __get_mining_param, remote=shard_config.CONSENSUS_CONFIG.REMOTE_MINE, )
def test_getTransactionReceipt_on_transfer(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 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, 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() + acc1.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"], "0x5208") self.assertIsNone(resp["contractAddress"])
def from_dict(cls, d): config = super().from_dict(d) config.ROOT = RootConfig.from_dict(config.ROOT) chains = [] shards = dict() for s in config.CHAINS: chain_config = ChainConfig.from_dict(s) chains.append(chain_config) for shard_id in range(chain_config.SHARD_SIZE): shard_config = ShardConfig(chain_config) shard_config.root_config = config.ROOT shard_config.SHARD_ID = shard_id shard_config.COINBASE_ADDRESS = ( Address.create_from(shard_config.COINBASE_ADDRESS) .address_in_shard(shard_config.get_full_shard_id()) .serialize() .hex() ) shards[shard_config.get_full_shard_id()] = shard_config config.CHAINS = chains config.shards = shards config.init_and_validate() return config
def create_block_to_mine(self, m_header_list, address=None, create_time=None): if not address: address = Address.create_empty_account() if create_time is None: create_time = max(self.tip.create_time + 1, int(time.time())) tracking_data = { "inception": time_ms(), "cluster": self.env.cluster_config.MONITORING.CLUSTER_ID, } difficulty = self.diff_calc.calculate_diff_with_parent(self.tip, create_time) block = self.tip.create_block_to_append( create_time=create_time, address=address, difficulty=difficulty ) block.minor_block_header_list = m_header_list coinbase_tokens = self._calculate_root_block_coinbase( [header.get_hash() for header in m_header_list], block.header.height ) tracking_data["creation_ms"] = time_ms() - tracking_data["inception"] block.tracking_data = json.dumps(tracking_data).encode("utf-8") return block.finalize(coinbase_tokens=coinbase_tokens, coinbase_address=address)
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, 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: resp = send_request("eth_getCode", [created_addr[:-8], "0x0"]) else: resp = send_request("getCode", [created_addr]) self.assertEqual( resp, "0x6080604052600080fd00a165627a7a72305820a6ef942c101f06333ac35072a8ff40332c71d0e11cd0e6d86de8cae7b42696550029", )
def test_tx_size(self): id1 = Identity.create_from_key(b"0" * 32) acc1 = Address.create_from_identity(id1, full_shard_key=0) evm_tx = EvmTransaction( nonce=0, gasprice=1, startgas=30000, to=acc1.recipient, value=0, data=b"", from_full_shard_key=0xFFFF, to_full_shard_key=0xFFFF, network_id=1, gas_token_id=12345, transfer_token_id=1234, ) evm_tx.sign(key=id1.get_key()) tx = TypedTransaction(SerializedEvmTransaction.from_evm_tx(evm_tx)) self.assertEqual(len(tx.serialize()), TX_MIN_SIZE) evm_tx = EvmTransaction( nonce=TT256 - 1, gasprice=TT256 - 1, startgas=TT256 - 1, to=acc1.recipient, value=TT256 - 1, data=b"", from_full_shard_key=SHARD_KEY_MAX, to_full_shard_key=SHARD_KEY_MAX, network_id=1, gas_token_id=TOKEN_ID_MAX, transfer_token_id=TOKEN_ID_MAX, ) evm_tx.sign(key=id1.get_key()) tx = TypedTransaction(SerializedEvmTransaction.from_evm_tx(evm_tx)) self.assertEqual(len(tx.serialize()), TX_MAX_SIZE)
async def handle_mined_block(): while True: res = await self.output_q.coro_get() # type: MiningResult if not res: return # empty result means ending # start mining before processing and propagating mined block self._mine_new_block_async() block = self.work_map[res.header_hash] block.header.nonce = res.nonce block.header.mixhash = res.mixhash del self.work_map[res.header_hash] self._track(block) try: # FIXME: Root block should include latest minor block headers while it's being mined # This is a hack to get the latest minor block included since testnet does not check difficulty if self.consensus_type == ConsensusType.POW_SIMULATE: block = await self.create_block_async_func( Address.create_empty_account()) block.header.nonce = random.randint(0, 2**32 - 1) self._track(block) self._log_status(block) await self.add_block_async_func(block) except Exception: Logger.error_exception()
def test_disallowed_unknown_token(self): """do not allow tx with unknown token id """ MALICIOUS0 = token_id_encode("MALICIOUS0") MALICIOUS1 = token_id_encode("MALICIOUS1") id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) env = get_test_env( genesis_account=acc1, genesis_minor_token_balances={self.GENESIS_TOKEN: 10000000}, ) 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.assertFalse(state.add_tx(tx)) tx1 = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc1, value=0, gas=opcodes.GTXCOST, gas_token_id=MALICIOUS1, transfer_token_id=self.genesis_token, ) self.assertFalse(state.add_tx(tx1))
def test_getTransactionReceipt_on_contract_creation_failure(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 # 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)) to_full_shard_key = ( acc1.full_shard_key + 1 ) # x-shard contract creation should fail 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)) 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"], "0x0") self.assertEqual(resp["cumulativeGasUsed"], "0x13d6c") self.assertIsNone(resp["contractAddress"])
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 test_call_success(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): slaves = clusters[0].slave_list branch = Branch.create(2, 0) response = send_request( "call", { "to": "0x" + acc1.serialize().hex(), "gas": hex(21000) }, "latest", ) self.assertEqual(response, "0x") self.assertEqual( len(slaves[0].shards[branch].state.tx_queue), 0, "should not affect tx queue", )
def __init__( self, num_cluster, genesis_account=Address.create_empty_account(), chain_size=2, shard_size=2, num_slaves=None, genesis_root_heights=None, remote_mining=False, small_coinbase=False, loadtest_accounts=None, ): self.num_cluster = num_cluster self.genesis_account = genesis_account self.chain_size = chain_size self.shard_size = shard_size self.num_slaves = num_slaves if num_slaves else chain_size self.genesis_root_heights = genesis_root_heights self.remote_mining = remote_mining self.small_coinbase = small_coinbase self.loadtest_accounts = loadtest_accounts check(is_p2(self.num_slaves)) check(is_p2(self.shard_size))
def test_getTransactionById(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, branch_value=0b10)) self.assertTrue( call_async(clusters[0].get_shard(2 | 0).add_block(block1))) resp = send_request( "getTransactionById", [ "0x" + tx.get_hash().hex() + acc1.full_shard_key.to_bytes(4, "big").hex() ], ) self.assertEqual(resp["hash"], "0x" + tx.get_hash().hex())
async def getStorageAt(self, address, key, block_height=None): res = await self.master.get_storage_at(Address.deserialize(address), key, block_height) return data_encoder(res) if res is not None else None
def get_test_env( genesis_account=Address.create_empty_account(), genesis_minor_quarkash=0, chain_size=2, shard_size=2, genesis_root_heights=None, # dict(full_shard_id, genesis_root_height) remote_mining=False, genesis_minor_token_balances=None, charge_gas_reserve=False, ): check(is_p2(shard_size)) env = DEFAULT_ENV.copy() env.db = InMemoryDb() env.set_network_id(1234567890) env.cluster_config = ClusterConfig() env.quark_chain_config.update( chain_size, shard_size, 10, 1, env.quark_chain_config.GENESIS_TOKEN ) env.quark_chain_config.MIN_TX_POOL_GAS_PRICE = 0 env.quark_chain_config.MIN_MINING_GAS_PRICE = 0 if remote_mining: env.quark_chain_config.ROOT.CONSENSUS_CONFIG.REMOTE_MINE = True env.quark_chain_config.ROOT.CONSENSUS_TYPE = ConsensusType.POW_DOUBLESHA256 env.quark_chain_config.ROOT.GENESIS.DIFFICULTY = 10 env.quark_chain_config.ROOT.DIFFICULTY_ADJUSTMENT_CUTOFF_TIME = 40 env.quark_chain_config.ROOT.DIFFICULTY_ADJUSTMENT_FACTOR = 1024 if genesis_root_heights: check(len(genesis_root_heights) == shard_size * chain_size) for chain_id in range(chain_size): for shard_id in range(shard_size): full_shard_id = chain_id << 16 | shard_size | shard_id shard = env.quark_chain_config.shards[full_shard_id] shard.GENESIS.ROOT_HEIGHT = genesis_root_heights[full_shard_id] # fund genesis account in all shards for full_shard_id, shard in env.quark_chain_config.shards.items(): addr = genesis_account.address_in_shard(full_shard_id).serialize().hex() if genesis_minor_token_balances is not None: shard.GENESIS.ALLOC[addr] = genesis_minor_token_balances else: shard.GENESIS.ALLOC[addr] = { env.quark_chain_config.GENESIS_TOKEN: genesis_minor_quarkash } if charge_gas_reserve: gas_reserve_addr = ( SystemContract.GENERAL_NATIVE_TOKEN.addr().hex() + addr[-8:] ) shard.GENESIS.ALLOC[gas_reserve_addr] = { env.quark_chain_config.GENESIS_TOKEN: int(1e18) } shard.CONSENSUS_CONFIG.REMOTE_MINE = remote_mining shard.DIFFICULTY_ADJUSTMENT_CUTOFF_TIME = 7 shard.DIFFICULTY_ADJUSTMENT_FACTOR = 512 if remote_mining: shard.CONSENSUS_TYPE = ConsensusType.POW_DOUBLESHA256 shard.GENESIS.DIFFICULTY = 10 shard.POSW_CONFIG.WINDOW_SIZE = 2 env.quark_chain_config.SKIP_MINOR_DIFFICULTY_CHECK = True env.quark_chain_config.SKIP_ROOT_DIFFICULTY_CHECK = True env.cluster_config.ENABLE_TRANSACTION_HISTORY = True env.cluster_config.DB_PATH_ROOT = "" check(env.cluster_config.use_mem_db()) return env
def test_getLogs(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) expected_log_parts = { "logIndex": "0x0", "transactionIndex": "0x0", "blockNumber": "0x1", "blockHeight": "0x1", "data": "0x", } 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_creation_with_event_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): shard_id = hex(acc1.full_shard_key) if using_eth_endpoint: req = lambda o: send_request("eth_getLogs", [o, shard_id]) else: # `None` needed to bypass some request modification req = lambda o: send_request("getLogs", [o, shard_id]) # no filter object as wild cards resp = req({}) self.assertEqual(1, len(resp)) self.assertDictContainsSubset(expected_log_parts, resp[0]) # filter by contract address contract_addr = mk_contract_address(acc1.recipient, 0, acc1.full_shard_key) filter_obj = { "address": "0x" + contract_addr.hex() + ("" if using_eth_endpoint else hex( acc1.full_shard_key)[2:].zfill(8)) } resp = req(filter_obj) self.assertEqual(1, len(resp)) # filter by topics filter_obj = { "topics": [ "0xa9378d5bd800fae4d5b8d4c6712b2b64e8ecc86fdc831cb51944000fc7c8ecfa" ] } filter_obj_nested = { "topics": [[ "0xa9378d5bd800fae4d5b8d4c6712b2b64e8ecc86fdc831cb51944000fc7c8ecfa" ]] } for f in (filter_obj, filter_obj_nested): resp = req(f) self.assertEqual(1, len(resp)) self.assertDictContainsSubset(expected_log_parts, resp[0]) self.assertEqual( "0xa9378d5bd800fae4d5b8d4c6712b2b64e8ecc86fdc831cb51944000fc7c8ecfa", resp[0]["topics"][0], )
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 block = call_async( master.get_next_block_to_mine(address=acc2, branch_value=None)) 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, branch_value=0b10)) 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, branch_value=None)) 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, branch_value=0b11)) 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"])
async def getTransactionCount(self, address, block_height=None): account_branch_data = await self.master.get_primary_account_data( Address.deserialize(address), block_height) return account_branch_data.transaction_count
def test_getRootblockConfirmationIdAndCount(self): # TODO test root chain forks 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) block = call_async( master.get_next_block_to_mine(address=acc1, branch_value=None)) call_async(master.add_root_block(block)) 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, branch_value=0b10)) self.assertTrue( call_async(clusters[0].get_shard(2 | 0).add_block(block1))) tx_id = ("0x" + tx.get_hash().hex() + acc1.full_shard_key.to_bytes(4, "big").hex()) resp = send_request("getTransactionById", [tx_id]) self.assertEqual(resp["hash"], "0x" + tx.get_hash().hex()) self.assertEqual( resp["blockId"], "0x" + block1.header.get_hash().hex() + block1.header.branch.get_full_shard_id().to_bytes( 4, byteorder="big").hex(), ) minor_hash = resp["blockId"] # zero root block confirmation resp_hash = send_request("getRootHashConfirmingMinorBlockById", [minor_hash]) self.assertIsNone( resp_hash, "should return None for unconfirmed minor blocks") resp_count = send_request( "getTransactionConfirmedByNumberRootBlocks", [tx_id]) self.assertEqual(resp_count, "0x0") # 1 root block confirmation block = call_async( master.get_next_block_to_mine(address=acc1, branch_value=None)) call_async(master.add_root_block(block)) resp_hash = send_request("getRootHashConfirmingMinorBlockById", [minor_hash]) self.assertIsNotNone(resp_hash, "confirmed by root block") self.assertEqual(resp_hash, "0x" + block.header.get_hash().hex()) resp_count = send_request( "getTransactionConfirmedByNumberRootBlocks", [tx_id]) self.assertEqual(resp_count, "0x1") # 2 root block confirmation block = call_async( master.get_next_block_to_mine(address=acc1, branch_value=None)) call_async(master.add_root_block(block)) resp_hash = send_request("getRootHashConfirmingMinorBlockById", [minor_hash]) self.assertIsNotNone(resp_hash, "confirmed by root block") self.assertNotEqual(resp_hash, "0x" + block.header.get_hash().hex()) resp_count = send_request( "getTransactionConfirmedByNumberRootBlocks", [tx_id]) self.assertEqual(resp_count, "0x2")
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, branch_value=0b10)) self.assertTrue( call_async(clusters[0].get_shard(2 | 0).add_block(block1))) # By id for need_extra_info in [True, False]: resp = send_request( "getMinorBlockById", [ "0x" + block1.header.get_hash().hex() + "0" * 8, False, need_extra_info, ], ) 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 for need_extra_info in [True, False]: resp = send_request("getMinorBlockByHeight", ["0x0", "0x1", False, need_extra_info]) 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)
async def eth_getCode(self, address, shard=None): addr = Address.deserialize(address) if shard is not None: addr = Address(addr.recipient, shard) res = await self.master.get_code(addr, None) return data_encoder(res) if res is not None else None
async def getCode(self, address, block_height=None): res = await self.master.get_code(Address.deserialize(address), block_height) return data_encoder(res) if res is not None else None
def testnet_master_address(self): return Address.create_from(self.TESTNET_MASTER_ADDRESS)
def miner_address(self): return Address.create_from(self.MINER_ADDRESS)