def __get_branch_to_add_xshard_tx_list_request(self, block_hash, xshard_tx_list, prev_root_height): xshard_map = dict( ) # type: Dict[Branch, List[CrossShardTransactionDeposit]] # only broadcast to the shards that have been initialized initialized_shard_ids = self.env.quark_chain_config.get_initialized_shard_ids_before_root_height( prev_root_height) for shard_id in initialized_shard_ids: branch = Branch.create(self.__get_shard_size(), shard_id) xshard_map[branch] = [] for xshard_tx in xshard_tx_list: shard_id = xshard_tx.to_address.get_shard_id( self.__get_shard_size()) branch = Branch.create(self.__get_shard_size(), shard_id) check(branch in xshard_map) xshard_map[branch].append(xshard_tx) branch_to_add_xshard_tx_list_request = ( dict()) # type: Dict[Branch, AddXshardTxListRequest] for branch, tx_list in xshard_map.items(): cross_shard_tx_list = CrossShardTransactionList(tx_list) request = AddXshardTxListRequest(branch, block_hash, cross_shard_tx_list) branch_to_add_xshard_tx_list_request[branch] = request return branch_to_add_xshard_tx_list_request
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_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_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())
async def eth_getBlockByNumber(self, block_height, include_transactions): """ NOTE: only support block_id "latest" or hex """ def block_transcoder(block): """ QuarkChain Block => ETH Block """ return { **block, "number": block["height"], "parentHash": block["hashPrevMinorBlock"], "sha3Uncles": "", "logsBloom": "", "transactionsRoot": block["hashMerkleRoot"], # ? "stateRoot": block["hashEvmStateRoot"], # ? } block = await self.master.get_minor_block_by_height( block_height, Branch.create(self.master.get_shard_size(), 0) ) if block is None: return None return block_transcoder(minor_block_encoder(block))
def get_shard(self, shard_id) -> Shard: branch = Branch.create(self.master.env.quark_chain_config.SHARD_SIZE, shard_id) for slave in self.slave_list: if branch in slave.shards: return slave.shards[branch] return None
def __init_miner(self): miner_address = self.env.quark_chain_config.testnet_master_address.address_in_branch( Branch.create(self.__get_shard_size(), self.shard_id)) async def __create_block(): # hold off mining if the shard is syncing while self.synchronizer.running or not self.state.initialized: 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, )
async def _get_logs(self, data, shard, decoder: Callable[[str], bytes]): start_block = data.get("fromBlock", "latest") end_block = data.get("toBlock", "latest") # TODO: not supported yet for "earliest" or "pending" block if (isinstance(start_block, str) and start_block != "latest") or (isinstance(end_block, str) and end_block != "latest"): return None # parse addresses / topics addresses, topics = [], [] if "address" in data: if isinstance(data["address"], str): addresses = [Address.deserialize(decoder(data["address"]))] elif isinstance(data["address"], list): addresses = [ Address.deserialize(decoder(a)) for a in data["address"] ] if shard is not None: addresses = [Address(a.recipient, shard) for a in addresses] if "topics" in data: for topic_item in data["topics"]: if isinstance(topic_item, str): topics.append([data_decoder(topic_item)]) elif isinstance(topic_item, list): topics.append([data_decoder(tp) for tp in topic_item]) branch = Branch.create(self.master.get_shard_size(), shard) logs = await self.master.get_logs(addresses, topics, start_block, end_block, branch) if logs is None: return None return loglist_encoder(logs)
def get_balance(self, address): branch = Branch.create(self.__get_shard_size(), address.get_shard_id(self.__get_shard_size())) shard = self.shards.get(branch, None) if not shard: return None return shard.state.get_balance(address.recipient)
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_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_getMinorBlock(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) 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))) # 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() + "0" * 8) 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() + "0" * 8) 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 add_tx(self, tx: Transaction) -> bool: evm_tx = tx.code.get_evm_transaction() evm_tx.set_shard_size(self.__get_shard_size()) branch = Branch.create(self.__get_shard_size(), evm_tx.from_shard_id()) shard = self.shards.get(branch, None) if not shard: return False return shard.add_tx(tx)
async def getMinorBlockById(self, block_id, include_transactions=False): block_hash, full_shard_id = block_id shard_size = self.master.get_shard_size() branch = Branch.create(shard_size, (shard_size - 1) & full_shard_id) block = await self.master.get_minor_block_by_hash(block_hash, branch) if not block: return None return minor_block_encoder(block, include_transactions)
def estimate_gas(self, tx, from_address) -> Optional[int]: evm_tx = tx.code.get_evm_transaction() evm_tx.set_shard_size(self.__get_shard_size()) branch = Branch.create(self.__get_shard_size(), evm_tx.from_shard_id()) shard = self.shards.get(branch, None) if not shard: return None return shard.state.estimate_gas(tx, from_address)
def get_transaction_list_by_address(self, address, start, limit): branch = Branch.create(self.__get_shard_size(), address.get_shard_id(self.__get_shard_size())) shard = self.shards.get(branch, None) if not shard: return None return shard.state.get_transaction_list_by_address( address, start, limit)
def test_getStorageAt(self): key = bytes.fromhex( "c987d4506fb6824639f9a9e3b8834584f5165e94680501d1b0044071cd36c3b3") id1 = Identity.create_from_key(key) acc1 = Address.create_from_identity(id1, full_shard_id=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 branch = Branch.create(2, 0) tx = create_contract_with_storage_transaction( shard_state=slaves[0].shards[branch].state, key=id1.get_key(), from_address=acc1, to_full_shard_id=acc1.full_shard_id, ) 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: 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 get_code(self, address: Address, block_height: Optional[int]) -> Optional[bytes]: shard_size = self.__get_shard_size() shard_id = address.get_shard_id(shard_size) branch = Branch.create(shard_size, shard_id) shard = self.shards.get(branch, None) if not shard: return None return shard.state.get_code(address.recipient, block_height)
async def gasPrice(self, shard): shard = shard_id_decoder(shard) if shard is None: return None branch = Branch.create(self.master.get_shard_size(), shard) ret = await self.master.gas_price(branch) if ret is None: return None return quantity_encoder(ret)
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 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.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, 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 self.assertEqual( clusters[0].get_shard_state(1 | 0).get_tip().header.height, 1 )
async def getTransactionById(self, tx_id): tx_hash, full_shard_id = tx_id shard_size = self.master.get_shard_size() branch = Branch.create(shard_size, (shard_size - 1) & full_shard_id) minor_block, i = await self.master.get_transaction_by_hash(tx_hash, branch) if not minor_block: return None if len(minor_block.tx_list) <= i: return None return tx_encoder(minor_block, i)
async def getTransactionReceipt(self, tx_id): tx_hash, full_shard_id = tx_id shard_size = self.master.get_shard_size() branch = Branch.create(shard_size, (shard_size - 1) & full_shard_id) resp = await self.master.get_transaction_receipt(tx_hash, branch) if not resp: return None minor_block, i, receipt = resp return receipt_encoder(minor_block, i, receipt)
def test_add_transaction(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(2, acc1) as clusters: master = clusters[0].master slaves = clusters[0].slave_list branch0 = Branch.create(2, 0) 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.create(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())
async def getWork(self, shard): branch = None # `None` means getting work from root chain if shard is not None: branch = Branch.create(self.master.get_shard_size(), shard) ret = await self.master.get_work(branch) if ret is None: return None return [ data_encoder(ret.hash), quantity_encoder(ret.height), quantity_encoder(ret.difficulty), ]
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 branch = Branch.create(2, 0) self.assertEqual( call_async(master.get_primary_account_data(acc1)).transaction_count, 0 ) for i 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=12345, ) self.assertTrue(slaves[0].add_tx(tx)) _, block = call_async(master.get_next_block_to_mine(address=acc1)) 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))
async def getAccountData(self, address, block_height=None, include_shards=False): # do not allow specify height if client wants info on all shards if include_shards and block_height is not None: return None address = Address.deserialize(address) if not include_shards: account_branch_data = await self.master.get_primary_account_data( address, block_height) branch = account_branch_data.branch balance = account_branch_data.balance count = account_branch_data.transaction_count primary = { "branch": quantity_encoder(branch.value), "shard": quantity_encoder(branch.get_shard_id()), "balance": quantity_encoder(balance), "transactionCount": quantity_encoder(count), "isContract": account_branch_data.is_contract, } return {"primary": primary} branch_to_account_branch_data = await self.master.get_account_data( address) shard_size = self.master.get_shard_size() shards = [] for shard in range(shard_size): branch = Branch.create(shard_size, shard) account_branch_data = branch_to_account_branch_data[branch] data = { "branch": quantity_encoder(account_branch_data.branch.value), "shard": quantity_encoder(account_branch_data.branch.get_shard_id()), "balance": quantity_encoder(account_branch_data.balance), "transactionCount": quantity_encoder(account_branch_data.transaction_count), "isContract": account_branch_data.is_contract, } shards.append(data) if shard == address.get_shard_id(shard_size): primary = data return {"primary": primary, "shards": shards}
async def getMinorBlockByHeight( self, shard: int, height=None, include_transactions=False ): shard_size = self.master.get_shard_size() if height is not None: height = quantity_decoder(height) if shard >= shard_size: raise InvalidParams( "shard is larger than shard size {} > {}".format(shard, shard_size) ) branch = Branch.create(shard_size, shard) block = await self.master.get_minor_block_by_height(height, branch) if not block: return None return minor_block_encoder(block, include_transactions)
def create_minor_block(self, root_block: RootBlock, shard_id: int, evm_state: EvmState) -> MinorBlock: """ Create genesis block for shard. Genesis block's hash_prev_root_block is set to the genesis root block. Genesis state will be committed to the given evm_state. """ branch = Branch.create(self._qkc_config.SHARD_SIZE, shard_id) shard_config = self._qkc_config.SHARD_LIST[shard_id] genesis = shard_config.GENESIS for address_hex, amount_in_wei in genesis.ALLOC.items(): address = Address.create_from(bytes.fromhex(address_hex)) check( address.get_shard_id(self._qkc_config.SHARD_SIZE) == shard_id) evm_state.full_shard_id = address.full_shard_id evm_state.delta_balance(address.recipient, amount_in_wei) evm_state.commit() meta = MinorBlockMeta( hash_merkle_root=bytes.fromhex(genesis.HASH_MERKLE_ROOT), hash_evm_state_root=evm_state.trie.root_hash, ) local_fee_rate = 1 - self._qkc_config.reward_tax_rate # type: Fraction coinbase_amount = (shard_config.COINBASE_AMOUNT * local_fee_rate.numerator // local_fee_rate.denominator) coinbase_address = Address.create_empty_account(shard_id) header = MinorBlockHeader( version=genesis.VERSION, height=genesis.HEIGHT, branch=branch, hash_prev_minor_block=bytes.fromhex(genesis.HASH_PREV_MINOR_BLOCK), hash_prev_root_block=root_block.header.get_hash(), evm_gas_limit=genesis.GAS_LIMIT, hash_meta=sha3_256(meta.serialize()), coinbase_amount=coinbase_amount, coinbase_address=coinbase_address, create_time=genesis.TIMESTAMP, difficulty=genesis.DIFFICULTY, extra_data=bytes.fromhex(genesis.EXTRA_DATA), ) return MinorBlock(header=header, meta=meta, tx_list=[])
async def create_shards(self, root_block: RootBlock): """ Create shards based on GENESIS config and root block height if they have not been created yet.""" futures = [] for shard_id, shard_config in enumerate(self.env.quark_chain_config.SHARD_LIST): branch = Branch.create(self.env.quark_chain_config.SHARD_SIZE, shard_id) if branch in self.shards: continue if not self.__cover_shard_id(shard_id) or not shard_config.GENESIS: continue if root_block.header.height >= shard_config.GENESIS.ROOT_HEIGHT: shard = Shard(self.env, shard_id, self) futures.append(shard.init_from_root_block(root_block)) self.shards[branch] = shard if self.mining: shard.miner.start() await asyncio.gather(*futures)
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)