def get_transactions_by_address(self, address, start=b"", limit=10): if not self.env.cluster_config.ENABLE_TRANSACTION_HISTORY: return [], b"" serialized_address = address.serialize() end = b"addr_" + serialized_address original_start = (int.from_bytes(end, byteorder="big") + 1).to_bytes( len(end), byteorder="big") next = end # reset start to the latest if start is not valid if not start or start > original_start: start = original_start tx_list = [] for k, v in self.db.reversed_range_iter(start, end): limit -= 1 if limit < 0: break height = int.from_bytes(k[5 + 24:5 + 24 + 4], "big") cross_shard = int(k[5 + 24 + 4]) == 0 index = int.from_bytes(k[5 + 24 + 4 + 1:], "big") if cross_shard: # cross shard receive m_block = self.get_minor_block_by_height(height) x_shard_receive_tx_list = self.__get_confirmed_cross_shard_transaction_deposit_list( m_block.header.get_hash()) tx = x_shard_receive_tx_list[ index] # tx is CrossShardTransactionDeposit tx_list.append( TransactionDetail( tx.tx_hash, tx.from_address, tx.to_address, tx.value, height, m_block.header.create_time, True, tx.gas_token_id, tx.transfer_token_id, )) else: m_block = self.get_minor_block_by_height(height) receipt = m_block.get_receipt(self.db, index) tx = m_block.tx_list[index] # tx is Transaction evm_tx = tx.tx.to_evm_tx() tx_list.append( TransactionDetail( tx.get_hash(), Address(evm_tx.sender, evm_tx.from_full_shard_key), Address(evm_tx.to, evm_tx.to_full_shard_key) if evm_tx.to else None, evm_tx.value, height, m_block.header.create_time, receipt.success == b"\x01", evm_tx.gas_token_id, evm_tx.transfer_token_id, )) next = (int.from_bytes(k, byteorder="big") - 1).to_bytes( len(k), byteorder="big") return tx_list, next
def __get_transaction_details( self, start_key: bytes, end_key: bytes, limit: int, decoding_byte_offset: int, skip_coinbase_rewards: bool = False, transfer_token_id: Optional[int] = None, ) -> (List[TransactionDetail], bytes): next_key, tx_list = end_key, [] tx_hashes = set() def skip_xshard(xshard_tx: CrossShardTransactionDeposit): if xshard_tx.is_from_root_chain: return ( skip_coinbase_rewards or xshard_tx.value == 0 ) # value 0 if dummy deposit return ( transfer_token_id is not None and xshard_tx.transfer_token_id != transfer_token_id ) def skip_tx(normal_tx: EvmTransaction): return ( transfer_token_id is not None and normal_tx.transfer_token_id != transfer_token_id ) for k, v in self.db.reversed_range_iter(start_key, end_key): if limit <= 0: break height = int.from_bytes( k[decoding_byte_offset : decoding_byte_offset + 4], "big" ) cross_shard = int(k[decoding_byte_offset + 4]) == 0 index = int.from_bytes(k[decoding_byte_offset + 4 + 1 :], "big") m_block = self.get_minor_block_by_height(height) if cross_shard: # cross shard receive x_shard_receive_tx_list = self.__get_confirmed_cross_shard_transaction_deposit_list( m_block.header.get_hash() ) tx = x_shard_receive_tx_list[ index ] # type: CrossShardTransactionDeposit if tx.tx_hash not in tx_hashes and not skip_xshard(tx): limit -= 1 tx_hashes.add(tx.tx_hash) tx_list.append( TransactionDetail( tx.tx_hash, tx.from_address, tx.to_address, tx.value, height, m_block.header.create_time, True, tx.gas_token_id, tx.transfer_token_id, is_from_root_chain=tx.is_from_root_chain, ) ) else: # no need to provide cross shard deposit list for in-shard tx receipt receipt = m_block.get_receipt( self.db, index, x_shard_receive_tx_list=None ) tx = m_block.tx_list[index] # type: TypedTransaction evm_tx = tx.tx.to_evm_tx() tx_hash = tx.get_hash() if tx_hash not in tx_hashes and not skip_tx(evm_tx): limit -= 1 tx_hashes.add(tx_hash) tx_list.append( TransactionDetail( tx_hash, Address(evm_tx.sender, evm_tx.from_full_shard_key), Address(evm_tx.to, evm_tx.to_full_shard_key) if evm_tx.to else None, evm_tx.value, height, m_block.header.create_time, receipt.success == b"\x01", evm_tx.gas_token_id, evm_tx.transfer_token_id, is_from_root_chain=False, ) ) next_key = (int.from_bytes(k, byteorder="big") - 1).to_bytes( len(k), byteorder="big" ) return tx_list, next_key
def test_two_tx_in_one_block(self): id1 = Identity.create_random_identity() id2 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_from_identity(id2, full_shard_id=0) acc3 = Address.create_random_account(full_shard_id=0) env = get_test_env(genesis_account=acc1, genesis_minor_quarkash=2000000 + opcodes.GTXCOST) state = create_default_shard_state(env=env) state.add_tx( create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=acc2, value=1000000, )) b0 = state.create_block_to_mine(address=acc3) state.finalize_and_add_block(b0) self.assertEqual(state.get_balance(id1.recipient, 0), 1000000) self.assertEqual(state.get_balance(acc2.recipient, 0), 1000000) self.assertEqual(state.get_balance(acc3.recipient, 0), opcodes.GTXCOST // 2) # Check Account has full_shard_id self.assertEqual(state.evm_state.get_full_shard_id(acc2.recipient), acc2.full_shard_id) state.add_tx( create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=Address(acc2.recipient, acc2.full_shard_id + 2), # set a different full shard id value=12345, gas=50000, )) state.add_tx( create_transfer_transaction( shard_state=state, key=id2.get_key(), from_address=acc2, to_address=acc1, value=54321, gas=40000, )) b1 = state.create_block_to_mine(address=acc3, gas_limit=40000) self.assertEqual(len(b1.tx_list), 1) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 2) # Should succeed state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) self.assertEqual(state.get_balance(id1.recipient, 0), 1000000 - opcodes.GTXCOST - 12345 + 54321) self.assertEqual(state.get_balance(acc2.recipient, 0), 1000000 - opcodes.GTXCOST + 12345 - 54321) self.assertEqual(state.get_balance(acc3.recipient, 0), opcodes.GTXCOST * 1.5) # Check receipts self.assertEqual(len(state.evm_state.receipts), 2) self.assertEqual(state.evm_state.receipts[0].state_root, b"\x01") self.assertEqual(state.evm_state.receipts[0].gas_used, 21000) self.assertEqual(state.evm_state.receipts[1].state_root, b"\x01") self.assertEqual(state.evm_state.receipts[1].gas_used, 42000) block, i = state.get_transaction_by_hash(b1.tx_list[0].get_hash()) self.assertEqual(block, b1) self.assertEqual(i, 0) block, i = state.get_transaction_by_hash(b1.tx_list[1].get_hash()) self.assertEqual(block, b1) self.assertEqual(i, 1) # Check acc2 full_shard_id doesn't change self.assertEqual(state.evm_state.get_full_shard_id(acc2.recipient), acc2.full_shard_id)
def test_contract_suicide(self): """ Kill Call Data: 0x41c0e1b5 """ QETH = token_id_encode("QETH") id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) id2 = Identity.create_random_identity() acc2 = Address.create_from_identity(id2, full_shard_key=0) acc3 = Address.create_random_account(full_shard_key=0) env = get_test_env( genesis_account=acc1, genesis_minor_token_balances={ self.GENESIS_TOKEN: 200 * 10**18, "QETH": 99999 }, ) state = create_default_shard_state(env=env) # 1. create contract BYTECODE = "6080604052348015600f57600080fd5b5060948061001e6000396000f3fe6080604052600436106039576000357c01000000000000000000000000000000000000000000000000000000009004806341c0e1b514603b575b005b348015604657600080fd5b50604d604f565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea165627a7a7230582034cc4e996685dcadcc12db798751d2913034a3e963356819f2293c3baea4a18c0029" """ pragma solidity ^0.5.1; contract Sample { function () payable external{} function kill() external {selfdestruct(msg.sender);} } """ CREATION_GAS = 92417 tx = contract_creation_tx( shard_state=state, key=id1.get_key(), from_address=acc1, to_full_shard_key=acc1.full_shard_key, bytecode=BYTECODE, gas_token_id=self.genesis_token, transfer_token_id=self.genesis_token, ) self.assertTrue(state.add_tx(tx)) b1 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b1.tx_list), 1) state.finalize_and_add_block(b1) self.assertEqual(state.header_tip, b1.header) self.assertEqual(len(state.evm_state.receipts), 1) self.assertEqual(state.evm_state.receipts[0].state_root, b"\x01") self.assertEqual(state.evm_state.receipts[0].gas_used, CREATION_GAS) contract_address = mk_contract_address(acc1.recipient, acc1.full_shard_key, 0) self.assertEqual(contract_address, state.evm_state.receipts[0].contract_address) self.assertEqual(acc1.full_shard_key, state.evm_state.receipts[0].contract_full_shard_key) self.assertEqual( state.get_token_balance(id1.recipient, self.genesis_token), 200 * 10**18 - CREATION_GAS, ) self.assertEqual( state.get_token_balance(acc3.recipient, self.genesis_token), self.getAfterTaxReward(CREATION_GAS + self.shard_coinbase), ) tx_list, _ = state.db.get_transactions_by_address(acc1) self.assertEqual(tx_list[0].value, 0) self.assertEqual(tx_list[0].gas_token_id, self.genesis_token) self.assertEqual(tx_list[0].transfer_token_id, self.genesis_token) # 2. send some default token tx_send = create_transfer_transaction( shard_state=state, key=id1.get_key(), from_address=acc1, to_address=Address(contract_address, acc1.full_shard_key), value=10 * 10**18, gas=opcodes.GTXCOST + 40, gas_price=1, nonce=None, data=b"", gas_token_id=self.genesis_token, transfer_token_id=self.genesis_token, ) self.assertTrue(state.add_tx(tx_send)) b2 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b2.tx_list), 1) state.finalize_and_add_block(b2) self.assertEqual(state.header_tip, b2.header) self.assertEqual(len(state.evm_state.receipts), 1) self.assertEqual(state.evm_state.receipts[0].state_root, b"\x01") self.assertEqual(state.evm_state.receipts[0].gas_used, opcodes.GTXCOST + 40) self.assertEqual( state.get_token_balance(id1.recipient, self.genesis_token), 200 * 10**18 - CREATION_GAS - (opcodes.GTXCOST + 40) - 10 * 10**18, ) self.assertEqual( state.get_token_balance(contract_address, self.genesis_token), 10 * 10**18) # 3. suicide SUICIDE_GAS = 13199 tx_kill = create_transfer_transaction( shard_state=state, key=id2.get_key(), from_address=acc2, to_address=Address(contract_address, acc1.full_shard_key), value=0, gas=1000000, gas_price=0, # !!! acc2 has no token yet... nonce=None, data=bytes.fromhex("41c0e1b5"), gas_token_id=self.genesis_token, transfer_token_id=self.genesis_token, ) self.assertTrue(state.add_tx(tx_kill)) b3 = state.create_block_to_mine(address=acc3) self.assertEqual(len(b3.tx_list), 1) state.finalize_and_add_block(b3) self.assertEqual(state.header_tip, b3.header) self.assertEqual(len(state.evm_state.receipts), 1) self.assertEqual(state.evm_state.receipts[0].state_root, b"\x01") self.assertEqual(state.evm_state.receipts[0].gas_used, SUICIDE_GAS) self.assertEqual( state.get_token_balance(id2.recipient, self.genesis_token), 10 * 10**18) self.assertEqual( state.get_token_balance(contract_address, self.genesis_token), 0)
async def eth_getStorageAt(self, address, key, shard=None): addr = Address.deserialize(address) if shard is not None: addr = Address(addr.recipient, shard) res = await self.master.get_storage_at(addr, key, None) return data_encoder(res) if res is not None else None
async def eth_getTransactionCount(self, address, shard=None): address = Address.deserialize(address) if shard is not None: address = Address(address.recipient, shard) account_branch_data = await self.master.get_primary_account_data(address) return account_branch_data.transaction_count