def get_or_query_transaction_receipt( self, tx_hash: str, ) -> Optional['EthereumTxReceipt']: """ Gets the receipt from the DB if it exist. If not queries the chain for it, saves it in the DB and then returns it. Also if the actual transaction does not exist in the DB it queries it and saves it there. May raise: - DeserializationError - RemoteError """ tx_hash_b = hexstring_to_bytes(tx_hash) dbethtx = DBEthTx(self.database) # If the transaction is not in the DB then query it and add it result, _ = dbethtx.get_ethereum_transactions(ETHTransactionsFilterQuery.make(tx_hash=tx_hash_b)) # noqa: E501 if len(result) == 0: transaction = self.ethereum.get_transaction_by_hash(tx_hash) if transaction is None: return None # hash does not correspond to a transaction dbethtx.add_ethereum_transactions([transaction]) tx_receipt = dbethtx.get_receipt(tx_hash_b) if tx_receipt is not None: return tx_receipt # not in the DB, so we need to query the chain for it tx_receipt_data = self.ethereum.get_transaction_receipt(tx_hash=tx_hash) dbethtx.add_receipt_data(tx_receipt_data) tx_receipt = dbethtx.get_receipt(tx_hash_b) return tx_receipt
def read_hash(data: Dict[str, Any], key: str) -> bytes: try: result = hexstring_to_bytes(data[key]) except ValueError: raise DeserializationError( f'Failed to read {key} as a hash during etherscan transaction query', ) return result
def get_validator_deposits( self, indices_or_pubkeys: Union[List[int], List[Eth2PubKey]], ) -> List[Eth2Deposit]: """Get the deposits of all the validators given from the list of indices or pubkeys Queries in chunks of 100 due to api limitations May raise: - RemoteError due to problems querying beaconcha.in API """ chunks = _calculate_query_chunks(indices_or_pubkeys) data = [] for chunk in chunks: result = self._query( module='validator', endpoint='deposits', encoded_args=','.join(str(x) for x in chunk), ) if isinstance(result, list): data.extend(result) else: data.append(result) deposits = [] for entry in data: try: amount = from_gwei(FVal(entry['amount'])) timestamp = entry['block_ts'] usd_price = query_usd_price_zero_if_error( asset=A_ETH, time=timestamp, location=f'Eth2 staking deposit at time {timestamp}', msg_aggregator=self.msg_aggregator, ) deposits.append( Eth2Deposit( from_address=deserialize_ethereum_address( entry['from_address']), pubkey=entry['publickey'], withdrawal_credentials=entry['withdrawal_credentials'], value=Balance( amount=amount, usd_value=amount * usd_price, ), tx_hash=hexstring_to_bytes(entry['tx_hash']), tx_index=entry['tx_index'], timestamp=entry['block_ts'], )) except (DeserializationError, KeyError) as e: msg = str(e) if isinstance(e, KeyError): msg = f'Missing key entry for {msg}.' raise RemoteError( f'Beaconchai.in deposits response processing error. {msg}', ) from e return deposits
def generate_address_via_create2( address: HexAddress, salt: HexStr, init_code: HexStr, ) -> ChecksumEthAddress: """Python implementation of CREATE2 opcode. Given an address (deployer), a salt and an init code (contract creation bytecode), returns the expected contract address once it is deployed. Pseudocode: keccak256( 0xff ++ address ++ salt ++ keccak256(init_code))[12:] EIP-1014: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1014.md """ contract_address = Web3.keccak( hexstring_to_bytes('0xff') + hexstring_to_bytes(address) + hexstring_to_bytes(salt) + Web3.keccak(hexstring_to_bytes(init_code)), )[12:].hex() return to_checksum_address(contract_address)
def read_hash(data: Dict[str, Any], key: str, api: str = DEFAULT_API) -> bytes: if isinstance(data[key], HexBytes): return bytes(data[key]) try: result = hexstring_to_bytes(data[key]) except DeserializationError as e: raise DeserializationError( f'Failed to read {key} as a hash during {api} transaction query', ) from e return result
def test_maybe_schedule_ethereum_txreceipts(task_manager, ethereum_manager, database, one_receipt_in_db): # noqa: E501 task_manager.potential_tasks = [ task_manager._maybe_schedule_ethereum_txreceipts ] # pylint: disable=protected-member # noqa: E501 _, receipts = setup_ethereum_transactions_test( database=database, transaction_already_queried=True, one_receipt_in_db=one_receipt_in_db, ) dbethtx = DBEthTx(database) timeout = 10 tx_hash_1 = '0x692f9a6083e905bdeca4f0293f3473d7a287260547f8cbccc38c5cb01591fcda' tx_hash_2 = '0x6beab9409a8f3bd11f82081e99e856466a7daf5f04cca173192f79e78ed53a77' receipt_task_patch = patch.object( task_manager, '_run_ethereum_txreceipts_query', wraps=task_manager._run_ethereum_txreceipts_query) # pylint: disable=protected-member # noqa: E501 queried_receipts = set() try: with gevent.Timeout(timeout): with receipt_task_patch as receipt_task_mock: task_manager.schedule() while True: if len(queried_receipts) == 2: break for txhash in (tx_hash_1, tx_hash_2): if dbethtx.get_receipt( hexstring_to_bytes(txhash)) is not None: queried_receipts.add(txhash) gevent.sleep(.3) task_manager.schedule() gevent.sleep(.5) assert receipt_task_mock.call_count == 1, '2nd schedule should do nothing' except gevent.Timeout as e: raise AssertionError( f'receipts query was not completed within {timeout} seconds' ) from e # noqa: E501 txmodule = EthTransactions(ethereum=ethereum_manager, database=database) receipt1 = txmodule.get_or_query_transaction_receipt(tx_hash_1) assert receipt1 == receipts[0] receipt2 = txmodule.get_or_query_transaction_receipt(tx_hash_2) assert receipt2 == receipts[1]
def prepare(self) -> Tuple[List[str], List[Any]]: if self.tx_hash is None: return [], [] if isinstance(self.tx_hash, str): try: value = hexstring_to_bytes(self.tx_hash) except DeserializationError as e: log.error(f'Failed to filter a DB transaction query by tx_hash: {str(e)}') return [], [] else: value = self.tx_hash return ['tx_hash=?'], [value]
def check_result_of_history_creation( start_ts: Timestamp, end_ts: Timestamp, trade_history: List[Union[Trade, MarginPosition]], loan_history: List[Loan], asset_movements: List[AssetMovement], eth_transactions: List[EthereumTransaction], ) -> Dict[str, Any]: """This function offers some simple assertions on the result of the created history. The entire processing part of the history is mocked away by this checking function""" if history_start_ts is None: assert start_ts == 0, 'if no start_ts is given it should be zero' else: assert start_ts == history_start_ts, 'should be same as given to process_history' if history_end_ts is not None: assert end_ts == history_end_ts, 'should be same as given to process_history' # TODO: Add more assertions/check for each action # OR instead do it in tests for conversion of actions(trades, loans, deposits e.t.c.) # from exchange to our format for each exchange assert len(trade_history) == 11 assert isinstance(trade_history[0], Trade) assert trade_history[0].location == Location.KRAKEN assert trade_history[0].pair == 'ETH_EUR' assert trade_history[0].trade_type == TradeType.BUY assert isinstance(trade_history[1], Trade) assert trade_history[1].location == Location.KRAKEN assert trade_history[1].pair == 'BTC_EUR' assert trade_history[1].trade_type == TradeType.BUY assert isinstance(trade_history[2], Trade) assert trade_history[2].location == Location.BITTREX assert trade_history[2].pair == 'LTC_BTC' assert trade_history[2].trade_type == TradeType.BUY assert isinstance(trade_history[3], Trade) assert trade_history[3].location == Location.BITTREX assert trade_history[3].pair == 'LTC_ETH' assert trade_history[3].trade_type == TradeType.SELL assert isinstance(trade_history[4], MarginPosition) assert trade_history[4].profit_loss == FVal('0.05') assert isinstance(trade_history[5], Trade) assert trade_history[5].location == Location.BINANCE assert trade_history[5].pair == 'ETH_BTC' assert trade_history[5].trade_type == TradeType.BUY assert isinstance(trade_history[6], Trade) assert trade_history[6].location == Location.BINANCE assert trade_history[6].pair == 'RDN_ETH' assert trade_history[6].trade_type == TradeType.SELL assert isinstance(trade_history[7], Trade) assert trade_history[7].location == Location.POLONIEX assert trade_history[7].pair == 'ETH_BTC' assert trade_history[7].trade_type == TradeType.SELL assert isinstance(trade_history[8], Trade) assert trade_history[8].location == Location.POLONIEX assert trade_history[8].pair == 'ETH_BTC' assert trade_history[8].trade_type == TradeType.BUY assert isinstance(trade_history[9], Trade) assert trade_history[9].location == Location.POLONIEX assert trade_history[9].pair == 'XMR_ETH' assert trade_history[9].trade_type == TradeType.BUY assert isinstance(trade_history[10], MarginPosition) assert trade_history[10].profit_loss == FVal('5E-9') assert len(loan_history) == 2 assert loan_history[0].currency == A_ETH assert loan_history[0].earned == AssetAmount(FVal('0.00000001')) assert loan_history[1].currency == A_BTC assert loan_history[1].earned == AssetAmount(FVal('0.00000005')) assert len(asset_movements) == 11 assert asset_movements[0].location == Location.POLONIEX assert asset_movements[0].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[0].asset == A_BTC assert asset_movements[1].location == Location.POLONIEX assert asset_movements[1].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[1].asset == A_ETH assert asset_movements[2].location == Location.POLONIEX assert asset_movements[2].category == AssetMovementCategory.DEPOSIT assert asset_movements[2].asset == A_BTC assert asset_movements[3].location == Location.POLONIEX assert asset_movements[3].category == AssetMovementCategory.DEPOSIT assert asset_movements[3].asset == A_ETH assert asset_movements[4].location == Location.BITMEX assert asset_movements[4].category == AssetMovementCategory.DEPOSIT assert asset_movements[4].asset == A_BTC assert asset_movements[5].location == Location.BITMEX assert asset_movements[5].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[5].asset == A_BTC assert asset_movements[6].location == Location.BITMEX assert asset_movements[6].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[6].asset == A_BTC assert asset_movements[7].location == Location.KRAKEN assert asset_movements[7].category == AssetMovementCategory.DEPOSIT assert asset_movements[7].asset == A_BTC assert asset_movements[8].location == Location.KRAKEN assert asset_movements[8].category == AssetMovementCategory.DEPOSIT assert asset_movements[8].asset == A_ETH assert asset_movements[9].location == Location.KRAKEN assert asset_movements[9].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[9].asset == A_BTC assert asset_movements[10].location == Location.KRAKEN assert asset_movements[10].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[10].asset == A_ETH # The history creation for these is not yet tested assert len(eth_transactions) == 3 assert eth_transactions[0].block_number == 54092 assert eth_transactions[0].tx_hash == hexstring_to_bytes(TX_HASH_STR1) assert eth_transactions[0].from_address == ETH_ADDRESS1 assert eth_transactions[0].to_address == '' assert eth_transactions[0].value == FVal('11901464239480000000000000') assert eth_transactions[0].input_data == MOCK_INPUT_DATA assert eth_transactions[1].block_number == 54093 assert eth_transactions[1].tx_hash == hexstring_to_bytes(TX_HASH_STR2) assert eth_transactions[1].from_address == ETH_ADDRESS2 assert eth_transactions[1].to_address == ETH_ADDRESS1 assert eth_transactions[1].value == FVal('40000300') assert eth_transactions[1].input_data == MOCK_INPUT_DATA assert eth_transactions[2].block_number == 54094 assert eth_transactions[2].tx_hash == hexstring_to_bytes(TX_HASH_STR3) assert eth_transactions[2].from_address == ETH_ADDRESS3 assert eth_transactions[2].to_address == ETH_ADDRESS1 assert eth_transactions[2].value == FVal('500520300') assert eth_transactions[2].input_data == MOCK_INPUT_DATA return {}
def check_result_of_history_creation( start_ts: Timestamp, end_ts: Timestamp, trade_history: List[Union[Trade, MarginPosition, AMMTrade]], loan_history: List[Loan], asset_movements: List[AssetMovement], eth_transactions: List[EthereumTransaction], defi_events: List[DefiEvent], ledger_actions: List[LedgerAction], ) -> Dict[str, Any]: """This function offers some simple assertions on the result of the created history. The entire processing part of the history is mocked away by this checking function""" if history_start_ts is None: assert start_ts == 0, 'if no start_ts is given it should be zero' else: assert start_ts == history_start_ts, 'should be same as given to process_history' if history_end_ts is not None: assert end_ts == history_end_ts, 'should be same as given to process_history' # TODO: terrible way to check. Figure out something better # This whole function needs better thinking also on the order it expects # the events to be. It's super brittle right now limited_range_test = False expected_trades_num = 11 expected_asset_movements_num = 11 if end_ts == 1539713238: limited_range_test = True expected_trades_num = 10 expected_asset_movements_num = 10 # TODO: Add more assertions/check for each action # OR instead do it in tests for conversion of actions(trades, loans, deposits e.t.c.) # from exchange to our format for each exchange assert len(trade_history) == expected_trades_num assert isinstance(trade_history[0], Trade) assert trade_history[0].location == Location.KRAKEN assert trade_history[0].base_asset == A_ETH assert trade_history[0].quote_asset == A_EUR assert trade_history[0].trade_type == TradeType.BUY assert isinstance(trade_history[1], Trade) assert trade_history[1].location == Location.KRAKEN assert trade_history[1].base_asset == A_BTC assert trade_history[1].quote_asset == A_EUR assert trade_history[1].trade_type == TradeType.BUY assert isinstance(trade_history[2], Trade) assert trade_history[2].location == Location.BITTREX assert trade_history[2].base_asset == A_LTC assert trade_history[2].quote_asset == A_BTC assert trade_history[2].trade_type == TradeType.BUY assert isinstance(trade_history[3], Trade) assert trade_history[3].location == Location.BITTREX assert trade_history[3].base_asset == A_LTC assert trade_history[3].quote_asset == A_ETH assert trade_history[3].trade_type == TradeType.SELL assert isinstance(trade_history[4], MarginPosition) assert trade_history[4].profit_loss == FVal('0.05') assert isinstance(trade_history[5], Trade) assert trade_history[5].location == Location.BINANCE assert trade_history[5].base_asset == A_ETH assert trade_history[5].quote_asset == A_BTC assert trade_history[5].trade_type == TradeType.BUY assert isinstance(trade_history[6], Trade) assert trade_history[6].location == Location.BINANCE assert trade_history[6].base_asset == A_RDN assert trade_history[6].quote_asset == A_ETH assert trade_history[6].trade_type == TradeType.SELL assert isinstance(trade_history[7], Trade) assert trade_history[7].location == Location.POLONIEX assert trade_history[7].base_asset == A_ETH assert trade_history[7].quote_asset == A_BTC assert trade_history[7].trade_type == TradeType.SELL assert isinstance(trade_history[8], Trade) assert trade_history[8].location == Location.POLONIEX assert trade_history[8].base_asset == A_ETH assert trade_history[8].quote_asset == A_BTC assert trade_history[8].trade_type == TradeType.BUY assert isinstance(trade_history[9], Trade) assert trade_history[9].location == Location.POLONIEX assert trade_history[9].base_asset == A_XMR assert trade_history[9].quote_asset == A_ETH assert trade_history[9].trade_type == TradeType.BUY if not limited_range_test: assert isinstance(trade_history[10], MarginPosition) assert trade_history[10].profit_loss == FVal('5E-9') assert len(loan_history) == 2 assert loan_history[0].currency == A_ETH assert loan_history[0].earned == AssetAmount(FVal('0.00000001')) assert loan_history[1].currency == A_BTC assert loan_history[1].earned == AssetAmount(FVal('0.00000005')) assert len(asset_movements) == expected_asset_movements_num if not limited_range_test: assert asset_movements[0].location == Location.POLONIEX assert asset_movements[ 0].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[0].asset == A_BTC assert asset_movements[1].location == Location.POLONIEX assert asset_movements[ 1].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[1].asset == A_ETH assert asset_movements[2].location == Location.POLONIEX assert asset_movements[2].category == AssetMovementCategory.DEPOSIT assert asset_movements[2].asset == A_BTC assert asset_movements[3].location == Location.POLONIEX assert asset_movements[3].category == AssetMovementCategory.DEPOSIT assert asset_movements[3].asset == A_ETH assert asset_movements[4].location == Location.BITMEX assert asset_movements[4].category == AssetMovementCategory.DEPOSIT assert asset_movements[4].asset == A_BTC assert asset_movements[5].location == Location.BITMEX assert asset_movements[ 5].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[5].asset == A_BTC assert asset_movements[6].location == Location.BITMEX assert asset_movements[ 6].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[6].asset == A_BTC assert asset_movements[7].location == Location.KRAKEN assert asset_movements[7].category == AssetMovementCategory.DEPOSIT assert asset_movements[7].asset == A_BTC assert asset_movements[8].location == Location.KRAKEN assert asset_movements[8].category == AssetMovementCategory.DEPOSIT assert asset_movements[8].asset == A_ETH assert asset_movements[9].location == Location.KRAKEN assert asset_movements[ 9].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[9].asset == A_BTC assert asset_movements[10].location == Location.KRAKEN assert asset_movements[ 10].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[10].asset == A_ETH # The history creation for these is not yet tested assert len(eth_transactions) == 3 assert eth_transactions[0].block_number == 54092 assert eth_transactions[0].tx_hash == hexstring_to_bytes(TX_HASH_STR1) assert eth_transactions[0].from_address == ETH_ADDRESS1 assert eth_transactions[0].to_address is None assert eth_transactions[0].value == FVal('11901464239480000000000000') assert eth_transactions[0].input_data == MOCK_INPUT_DATA assert eth_transactions[1].block_number == 54093 assert eth_transactions[1].tx_hash == hexstring_to_bytes(TX_HASH_STR2) assert eth_transactions[1].from_address == ETH_ADDRESS2 assert eth_transactions[1].to_address == ETH_ADDRESS1 assert eth_transactions[1].value == FVal('40000300') assert eth_transactions[1].input_data == MOCK_INPUT_DATA assert eth_transactions[2].block_number == 54094 assert eth_transactions[2].tx_hash == hexstring_to_bytes(TX_HASH_STR3) assert eth_transactions[2].from_address == ETH_ADDRESS3 assert eth_transactions[2].to_address == ETH_ADDRESS1 assert eth_transactions[2].value == FVal('500520300') assert eth_transactions[2].input_data == MOCK_INPUT_DATA assert len(defi_events) == 0 assert len(ledger_actions) == 0 return {}
def test_eth2_deposits_serialization(): addr1 = make_ethereum_address() addr2 = make_ethereum_address() deposits = [ Eth2Deposit( from_address=addr1, pubkey= '0xb016e31f633a21fbe42a015152399361184f1e2c0803d89823c224994af74a561c4ad8cfc94b18781d589d03e952cd5b', # noqa: E501 withdrawal_credentials= '0x004c7691c2085648f394ffaef851f3b1d51b95f7263114bc923fc5338f5fc499', # noqa: E501 value=Balance(FVal(32), FVal(64)), tx_hash=hexstring_to_bytes( '0xd9eca1c2a0c5ff2f25071713432b21cc4d0ff2e8963edc63a48478e395e08db1' ), # noqa: E501 tx_index=22, timestamp=Timestamp(int(1604506685)), ), Eth2Deposit( from_address=addr2, pubkey= '0xa8ff5fc88412d080a297683c25a791ef77eb52d75b265fabab1f2c2591bb927c35818ac6289bc6680ab252787d0ebab3', # noqa: E501 withdrawal_credentials= '0x00cfe1c10347d642a8b8daf86d23bcb368076972691445de2cf517ff43765817', # noqa: E501 value=Balance(FVal(32), FVal(64)), tx_hash=hexstring_to_bytes( '0x6905f4d1843fb8c003c1fbbc2c8e6c5f9792f4f44ddb1122553412ee0b128da7' ), # noqa: E501 tx_index=221, timestamp=Timestamp(int(1605043544)), ), ] serialized = process_result_list(deposits) assert serialized == [ { 'from_address': addr1, 'pubkey': '0xb016e31f633a21fbe42a015152399361184f1e2c0803d89823c224994af74a561c4ad8cfc94b18781d589d03e952cd5b', # noqa: E501 'withdrawal_credentials': '0x004c7691c2085648f394ffaef851f3b1d51b95f7263114bc923fc5338f5fc499', # noqa: E501 'value': { 'amount': '32', 'usd_value': '64' }, 'tx_hash': '0xd9eca1c2a0c5ff2f25071713432b21cc4d0ff2e8963edc63a48478e395e08db1', 'tx_index': 22, 'timestamp': 1604506685, }, { 'from_address': addr2, 'pubkey': '0xa8ff5fc88412d080a297683c25a791ef77eb52d75b265fabab1f2c2591bb927c35818ac6289bc6680ab252787d0ebab3', # noqa: E501 'withdrawal_credentials': '0x00cfe1c10347d642a8b8daf86d23bcb368076972691445de2cf517ff43765817', # noqa: E501 'value': { 'amount': '32', 'usd_value': '64' }, 'tx_hash': '0x6905f4d1843fb8c003c1fbbc2c8e6c5f9792f4f44ddb1122553412ee0b128da7', 'tx_index': 221, 'timestamp': 1605043544, }, ]
'0x00F8a0D8EE1c21151BCcB416bCa1C152f9952D19') ADDR3 = string_to_ethereum_address( '0x3266F3546a1e5Dc6A15588f3324741A0E20a3B6c') # List of ADDR1, ADDR2 and ADDR3 deposit events from 1604506685 to 1605044577 # sorted by (timestamp, log_index). EXPECTED_DEPOSITS = [ Eth2Deposit( from_address=ADDR1, pubkey= '0xb016e31f633a21fbe42a015152399361184f1e2c0803d89823c224994af74a561c4ad8cfc94b18781d589d03e952cd5b', # noqa: E501 withdrawal_credentials= '0x004c7691c2085648f394ffaef851f3b1d51b95f7263114bc923fc5338f5fc499', # noqa: E501 value=Balance(FVal(32), FVal(64)), tx_hash=hexstring_to_bytes( '0xd9eca1c2a0c5ff2f25071713432b21cc4d0ff2e8963edc63a48478e395e08db1' ), # noqa: E501 tx_index=15, timestamp=Timestamp(int(1604506685)), ), Eth2Deposit( from_address=ADDR3, pubkey= '0x90b2f65cb43d9cdb2279af9f76010d667b9d8d72e908f2515497a7102820ce6bb15302fe2b8dc082fce9718569344ad8', # noqa: E501 withdrawal_credentials= '0x00a257d19e1650dec1ab59fc9e1cb9a9fc2fe7265b0f27e7d79ff61aeff0a1f0', # noqa: E501 value=Balance(FVal(32), FVal(64)), tx_hash=hexstring_to_bytes( '0x3403bd94a1bf185ee18a525499e408a1b9b7d801cff6418e31efda346762e754' ), # noqa: E501 tx_index=105,
def setup_ethereum_transactions_test( database: DBHandler, transaction_already_queried: bool, one_receipt_in_db: bool = False, ) -> Tuple[List[EthereumTransaction], List[EthereumTxReceipt]]: dbethtx = DBEthTx(database) tx_hash1 = '0x692f9a6083e905bdeca4f0293f3473d7a287260547f8cbccc38c5cb01591fcda' tx_hash1_b = hexstring_to_bytes(tx_hash1) transaction1 = EthereumTransaction( tx_hash=tx_hash1_b, timestamp=Timestamp(1630532276), block_number=13142218, from_address=string_to_ethereum_address( '0x443E1f9b1c866E54e914822B7d3d7165EdB6e9Ea'), to_address=string_to_ethereum_address( '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'), value=int(10 * 10**18), gas=194928, gas_price=int(0.000000204 * 10**18), gas_used=136675, input_data=hexstring_to_bytes( '0x7ff36ab5000000000000000000000000000000000000000000000367469995d0723279510000000000000000000000000000000000000000000000000000000000000080000000000000000000000000443e1f9b1c866e54e914822b7d3d7165edb6e9ea00000000000000000000000000000000000000000000000000000000612ff9b50000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000002a3bff78b79a009976eea096a51a948a3dc00e34' ), # noqa: E501 nonce=13, ) tx_hash2 = '0x6beab9409a8f3bd11f82081e99e856466a7daf5f04cca173192f79e78ed53a77' tx_hash2_b = hexstring_to_bytes(tx_hash2) transaction2 = EthereumTransaction( tx_hash=tx_hash2_b, timestamp=Timestamp(1631013757), block_number=13178342, from_address=string_to_ethereum_address( '0x442068F934BE670aDAb81242C87144a851d56d16'), to_address=string_to_ethereum_address( '0xEaDD9B69F96140283F9fF75DA5FD33bcF54E6296'), value=0, gas=77373, gas_price=int(0.000000100314697497 * 10**18), gas_used=46782, input_data=hexstring_to_bytes( '0xa9059cbb00000000000000000000000020c8032d4f7d4a380385f87aeadf05bed84504cb000000000000000000000000000000000000000000000000000000003b9deec6' ), # noqa: E501 nonce=3, ) transactions = [transaction1, transaction2] if transaction_already_queried is True: dbethtx.add_ethereum_transactions(ethereum_transactions=transactions) result, _ = dbethtx.get_ethereum_transactions( ETHTransactionsFilterQuery.make()) assert result == transactions expected_receipt1 = EthereumTxReceipt( tx_hash=tx_hash1_b, contract_address=None, status=True, type=0, logs=[ EthereumTxReceiptLog( log_index=295, data=hexstring_to_bytes( '0x0000000000000000000000000000000000000000000000008ac7230489e80000' ), # noqa: E501 address=string_to_ethereum_address( '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'), removed=False, topics=[ hexstring_to_bytes( '0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c' ), # noqa: E501 hexstring_to_bytes( '0x0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d' ), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=296, data=hexstring_to_bytes( '0x0000000000000000000000000000000000000000000000008ac7230489e80000' ), # noqa: E501 address=string_to_ethereum_address( '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'), removed=False, topics=[ hexstring_to_bytes( '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' ), # noqa: E501 hexstring_to_bytes( '0x0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d' ), # noqa: E501 hexstring_to_bytes( '0x000000000000000000000000caa004418eb42cdf00cb057b7c9e28f0ffd840a5' ), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=297, data=hexstring_to_bytes( '0x00000000000000000000000000000000000000000000036ba1d53baeeda5ed20' ), # noqa: E501 address=string_to_ethereum_address( '0x2a3bFF78B79A009976EeA096a51A948a3dC00e34'), removed=False, topics=[ hexstring_to_bytes( '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' ), # noqa: E501 hexstring_to_bytes( '0x000000000000000000000000caa004418eb42cdf00cb057b7c9e28f0ffd840a5' ), # noqa: E501 hexstring_to_bytes( '0x000000000000000000000000443e1f9b1c866e54e914822b7d3d7165edb6e9ea' ), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=298, data=hexstring_to_bytes( '0x000000000000000000000000000000000000000000007b6ea033189ba7d047e30000000000000000000000000000000000000000000000140bc8194dd0f5e4be' ), # noqa: E501 address=string_to_ethereum_address( '0xcaA004418eB42cdf00cB057b7C9E28f0FfD840a5'), removed=False, topics=[ hexstring_to_bytes( '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1' ) ], # noqa: E501 ), EthereumTxReceiptLog( log_index=299, data=hexstring_to_bytes( '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000036ba1d53baeeda5ed200000000000000000000000000000000000000000000000000000000000000000' ), # noqa: E501 address=string_to_ethereum_address( '0xcaA004418eB42cdf00cB057b7C9E28f0FfD840a5'), removed=False, topics=[ hexstring_to_bytes( '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822' ), # noqa: E501 hexstring_to_bytes( '0x0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d' ), # noqa: E501 hexstring_to_bytes( '0x000000000000000000000000443e1f9b1c866e54e914822b7d3d7165edb6e9ea' ), # noqa: E501 ], ), ], ) expected_receipt2 = EthereumTxReceipt( tx_hash=tx_hash2_b, contract_address=None, status=True, type=2, logs=[ EthereumTxReceiptLog( log_index=438, data=hexstring_to_bytes( '0x000000000000000000000000000000000000000000000000000000003b9deec6' ), # noqa: E501 address=string_to_ethereum_address( '0xEaDD9B69F96140283F9fF75DA5FD33bcF54E6296'), removed=False, topics=[ hexstring_to_bytes( '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' ), # noqa: E501 hexstring_to_bytes( '0x000000000000000000000000442068f934be670adab81242c87144a851d56d16' ), # noqa: E501 hexstring_to_bytes( '0x00000000000000000000000020c8032d4f7d4a380385f87aeadf05bed84504cb' ), # noqa: E501 ], ), ], ) if one_receipt_in_db: dbethtx.add_receipt_data(txreceipt_to_data(expected_receipt1)) return transactions, [expected_receipt1, expected_receipt2]
def test_get_transaction_receipt( ethereum_manager, call_order, ethereum_manager_connect_at_start, database, ): wait_until_all_nodes_connected( ethereum_manager_connect_at_start=ethereum_manager_connect_at_start, ethereum=ethereum_manager, ) tx_hash = '0x12d474b6cbba04fd1a14e55ef45b1eb175985612244631b4b70450c888962a89' result = ethereum_manager.get_transaction_receipt(tx_hash, call_order=call_order) block_hash = '0x6f3a7838a8788c3371b88df170c3643d19bad896c915a7368681292882b6ad61' assert result['blockHash'] == block_hash assert len(result['logs']) == 2 assert result['gasUsed'] == 144046 assert result['blockNumber'] == 10840714 assert result['logs'][0]['blockNumber'] == 10840714 assert result['logs'][1]['blockNumber'] == 10840714 assert result['status'] == 1 assert result['transactionIndex'] == 110 assert result['logs'][0]['transactionIndex'] == 110 assert result['logs'][1]['transactionIndex'] == 110 assert result['logs'][0]['logIndex'] == 235 assert result['logs'][1]['logIndex'] == 236 tx_hash_bytes = hexstring_to_bytes(tx_hash) db = DBEthTx(database) db.add_ethereum_transactions([ EthereumTransaction( # need to add the tx first tx_hash=tx_hash_bytes, timestamp=1, # all other fields don't matter for this test block_number=1, from_address='0x0', to_address='0x0', value=1, gas=1, gas_price=1, gas_used=1, input_data=b'', nonce=1, ) ]) # also test receipt can be stored and retrieved from the DB. # This tests that all node types (say openethereum) are processed properly db.add_receipt_data(result) receipt = db.get_receipt(tx_hash_bytes) assert receipt == EthereumTxReceipt( tx_hash=tx_hash_bytes, contract_address=None, status=True, type=0, logs=[ EthereumTxReceiptLog( log_index=235, data= b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02T\x0b\xe4\x00', # noqa: E501 address='0x5bEaBAEBB3146685Dd74176f68a0721F91297D37', removed=False, topics=[ b'\xdd\xf2R\xad\x1b\xe2\xc8\x9bi\xc2\xb0h\xfc7\x8d\xaa\x95+\xa7\xf1c\xc4\xa1\x16(\xf5ZM\xf5#\xb3\xef', # noqa: E501 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00s(*c\xf0\xe3\xd7\xe9`EuB\x0fwsa\xec\xa3\xc8j', # noqa: E501 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb6 \xf1\x93ME\x84\xdd\xa6\x99\x9e\xdc\xad\xd3)\x81)dj\xa5', # noqa: E501 ]), EthereumTxReceiptLog( log_index=236, data= b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb6 \xf1\x93ME\x84\xdd\xa6\x99\x9e\xdc\xad\xd3)\x81)dj\xa5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb6 \xf1\x93ME\x84\xdd\xa6\x99\x9e\xdc\xad\xd3)\x81)dj\xa5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00[\xea\xba\xeb\xb3\x14f\x85\xddt\x17oh\xa0r\x1f\x91)}7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02T\x0b\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r\xe0\xb6\xb3\xa7d\x00\x00', # noqa: E501 address='0x73282A63F0e3D7e9604575420F777361ecA3C86A', removed=False, topics=[ b'\xd6\xd4\xf5h\x1c$l\x9fB\xc2\x03\xe2\x87\x97Z\xf1`\x1f\x8d\xf8\x03Z\x92Q\xf7\x9a\xab\\\x8f\t\xe2\xf8' ], # noqa: E501 ), ])
def add_receipt_data(self, data: Dict[str, Any]) -> None: """Add tx receipt data as they are returned by the chain to the DB This assumes the transaction is already in the DB. May raise: - Key Error if any of the expected fields are missing - DeserializationError if there is a problem deserializing a value - sqlcipher.DatabaseError if the transaction hash is not in the DB or if the receipt already exists in the DB. TODO: Differentiate? """ tx_hash_b = hexstring_to_bytes(data['transactionHash']) # some nodes miss the type field for older non EIP1559 transactions. So assume legacy (0) tx_type = hexstr_to_int(data.get('type', '0x0')) cursor = self.db.conn.cursor() status = data.get('status', 1) # status may be missing for older txs. Assume 1. if status is None: status = 1 contract_address = deserialize_ethereum_address(data['contractAddress']) if data['contractAddress'] else None # noqa: E501 cursor.execute( 'INSERT INTO ethtx_receipts (tx_hash, contract_address, status, type) ' 'VALUES(?, ?, ?, ?) ', (tx_hash_b, contract_address, status, tx_type), ) log_tuples = [] topic_tuples = [] for log_entry in data['logs']: log_index = log_entry['logIndex'] log_tuples.append(( tx_hash_b, log_index, hexstring_to_bytes(log_entry['data']), deserialize_ethereum_address(log_entry['address']), int(log_entry['removed']), )) for idx, topic in enumerate(log_entry['topics']): topic_tuples.append(( tx_hash_b, log_index, hexstring_to_bytes(topic), idx, )) if len(log_tuples) != 0: cursor.executemany( 'INSERT INTO ethtx_receipt_logs (tx_hash, log_index, data, address, removed) ' 'VALUES(? ,? ,? ,? ,?)', log_tuples, ) if len(topic_tuples) != 0: cursor.executemany( 'INSERT INTO ethtx_receipt_log_topics (tx_hash, log_index, topic, topic_index) ' # noqa: E501 'VALUES(? ,? ,?, ?)', topic_tuples, ) self.db.conn.commit() self.db.update_last_write()