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 test_tx_decode(evm_transaction_decoder, database): dbethtx = DBEthTx(database) addr1 = '0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12' approve_tx_hash = deserialize_evm_tx_hash('0x5cc0e6e62753551313412492296d5e57bea0a9d1ce507cc96aa4aa076c5bde7a') # noqa: E501 transactions = dbethtx.get_ethereum_transactions( filter_=ETHTransactionsFilterQuery.make( addresses=[addr1], tx_hash=approve_tx_hash, ), has_premium=True, ) decoder = evm_transaction_decoder with patch.object(decoder, 'decode_transaction', wraps=decoder.decode_transaction) as decode_mock: # noqa: E501 for tx in transactions: receipt = dbethtx.get_receipt(tx.tx_hash) assert receipt is not None, 'all receipts should be queried in the test DB' events = decoder.get_or_decode_transaction_events(tx, receipt, ignore_cache=False) if tx.tx_hash == approve_tx_hash: assert len(events) == 2 assert_events_equal(events[0], HistoryBaseEntry( # The no-member is due to https://github.com/PyCQA/pylint/issues/3162 event_identifier=approve_tx_hash.hex(), # pylint: disable=no-member sequence_index=0, timestamp=1569924574000, location=Location.BLOCKCHAIN, location_label=addr1, asset=A_ETH, balance=Balance(amount=FVal('0.000030921')), # The no-member is due to https://github.com/PyCQA/pylint/issues/3162 notes=f'Burned 0.000030921 ETH in gas from {addr1}', event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, counterparty=CPT_GAS, )) assert_events_equal(events[1], HistoryBaseEntry( # The no-member is due to https://github.com/PyCQA/pylint/issues/3162 event_identifier=approve_tx_hash.hex(), # pylint: disable=no-member sequence_index=163, timestamp=1569924574000, location=Location.BLOCKCHAIN, location_label=addr1, asset=A_SAI, balance=Balance(amount=1), notes=f'Approve 1 SAI of {addr1} for spending by 0xdf869FAD6dB91f437B59F1EdEFab319493D4C4cE', # noqa: E501 event_type=HistoryEventType.INFORMATIONAL, event_subtype=HistoryEventSubType.APPROVE, counterparty='0xdf869FAD6dB91f437B59F1EdEFab319493D4C4cE', )) assert decode_mock.call_count == len(transactions) # now go again, and see that no more decoding happens as it's all pulled from the DB for tx in transactions: receipt = dbethtx.get_receipt(tx.tx_hash) assert receipt is not None, 'all receipts should be queried in the test DB' events = decoder.get_or_decode_transaction_events(tx, receipt, ignore_cache=False) assert decode_mock.call_count == len(transactions)
def get_or_query_transaction_receipt( self, tx_hash: EVMTxHash, ) -> 'EthereumTxReceipt': """ Gets the receipt from the DB if it exists. 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 if the transaction hash can't be found in any of the connected nodes """ dbethtx = DBEthTx(self.database) # If the transaction is not in the DB then query it and add it result = dbethtx.get_ethereum_transactions( filter_=ETHTransactionsFilterQuery.make(tx_hash=tx_hash), has_premium=True, # we don't need any limiting here ) if len(result) == 0: transaction = self.ethereum.get_transaction_by_hash(tx_hash) dbethtx.add_ethereum_transactions([transaction], relevant_address=None) self._get_internal_transactions_for_ranges( address=transaction.from_address, start_ts=transaction.timestamp, end_ts=transaction.timestamp, ) self._get_erc20_transfers_for_ranges( address=transaction.from_address, start_ts=transaction.timestamp, end_ts=transaction.timestamp, ) tx_receipt = dbethtx.get_receipt(tx_hash) 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) return tx_receipt # type: ignore # tx_receipt was just added in the DB so should be there # noqa: E501
def test_maybe_schedule_ethereum_txreceipts( task_manager, ethereum_manager, eth_transactions, database, one_receipt_in_db, ): 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 = hexstring_to_bytes( '0x692f9a6083e905bdeca4f0293f3473d7a287260547f8cbccc38c5cb01591fcda' ) # noqa: E501 tx_hash_2 = hexstring_to_bytes( '0x6beab9409a8f3bd11f82081e99e856466a7daf5f04cca173192f79e78ed53a77' ) # noqa: E501 receipt_get_patch = patch.object( ethereum_manager, 'get_transaction_receipt', wraps=ethereum_manager.get_transaction_receipt) # pylint: disable=protected-member # noqa: E501 queried_receipts = set() try: with gevent.Timeout(timeout): with receipt_get_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(txhash) is not None: queried_receipts.add(txhash) gevent.sleep(.3) task_manager.schedule() gevent.sleep(.5) assert receipt_task_mock.call_count == 1 if one_receipt_in_db else 2, '2nd schedule should do nothing' # noqa: E501 except gevent.Timeout as e: raise AssertionError( f'receipts query was not completed within {timeout} seconds' ) from e # noqa: E501 receipt1 = eth_transactions.get_or_query_transaction_receipt(tx_hash_1) assert receipt1 == receipts[0] receipt2 = eth_transactions.get_or_query_transaction_receipt(tx_hash_2) assert receipt2 == receipts[1]
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 = deserialize_evm_tx_hash( '0x12d474b6cbba04fd1a14e55ef45b1eb175985612244631b4b70450c888962a89' ) # noqa: E501 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 from_addy = make_ethereum_address() to_addy = make_ethereum_address() database.add_blockchain_accounts( blockchain=SupportedBlockchain.ETHEREUM, account_data=[ BlockchainAccountData(address=from_addy), BlockchainAccountData(address=to_addy), ], ) db = DBEthTx(database) db.add_ethereum_transactions( [ EthereumTransaction( # need to add the tx first tx_hash=tx_hash, timestamp=1, # all other fields don't matter for this test block_number=1, from_address=from_addy, to_address=to_addy, value=1, gas=1, gas_price=1, gas_used=1, input_data=b'', nonce=1, ) ], relevant_address=from_addy, ) # 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) assert receipt == EthereumTxReceipt( tx_hash=tx_hash, 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 ), ])