From d44d4fd2ac1422527a7d3e64e09a65741280e57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sa=C5=82aban?= Date: Fri, 13 Sep 2019 23:12:25 +0200 Subject: [PATCH] Add payment filtering by tx_id --- monero/backends/jsonrpc.py | 55 ++++-- monero/transaction.py | 23 +++ ...est_incoming_by_tx_id-00-get_accounts.json | 26 +++ ...ming_by_tx_id-30-get_transfer_by_txid.json | 58 +++++++ ...g_by_tx_id-31b52-get_transfer_by_txid.json | 58 +++++++ ...g_by_tx_id-55e75-get_transfer_by_txid.json | 58 +++++++ ...g_by_tx_id-7ab84-get_transfer_by_txid.json | 108 ++++++++++++ ...g_by_tx_id-e0b15-get_transfer_by_txid.json | 8 + ...est_outgoing_by_tx_id-00-get_accounts.json | 26 +++ ...g_by_tx_id-362c3-get_transfer_by_txid.json | 78 +++++++++ ...g_by_tx_id-afaf0-get_transfer_by_txid.json | 70 ++++++++ ...g_by_tx_id-eda89-get_transfer_by_txid.json | 70 ++++++++ tests/test_jsonrpcwallet.py | 160 ++++++++++++++++++ 13 files changed, 781 insertions(+), 17 deletions(-) create mode 100644 tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-00-get_accounts.json create mode 100644 tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-30-get_transfer_by_txid.json create mode 100644 tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-31b52-get_transfer_by_txid.json create mode 100644 tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-55e75-get_transfer_by_txid.json create mode 100644 tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-7ab84-get_transfer_by_txid.json create mode 100644 tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-e0b15-get_transfer_by_txid.json create mode 100644 tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-00-get_accounts.json create mode 100644 tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-362c3-get_transfer_by_txid.json create mode 100644 tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-afaf0-get_transfer_by_txid.json create mode 100644 tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-eda89-get_transfer_by_txid.json diff --git a/monero/backends/jsonrpc.py b/monero/backends/jsonrpc.py index 9fe6576..b142560 100644 --- a/monero/backends/jsonrpc.py +++ b/monero/backends/jsonrpc.py @@ -183,6 +183,8 @@ class JSONRPCWallet(object): def transfers_in(self, account, pmtfilter): params = {'account_index': account, 'pending': False} method = 'get_transfers' + if pmtfilter.tx_ids: + method = 'get_transfer_by_txid' if pmtfilter.unconfirmed: params['in'] = pmtfilter.confirmed params['out'] = False @@ -196,7 +198,6 @@ class JSONRPCWallet(object): params['out'] = False params['pool'] = False if method == 'get_transfers': - arg = 'in' if pmtfilter.min_height: # NOTE: the API uses (min, max] range which is confusing params['min_height'] = pmtfilter.min_height - 1 @@ -204,29 +205,48 @@ class JSONRPCWallet(object): if pmtfilter.max_height: params['max_height'] = pmtfilter.max_height params['filter_by_height'] = True - # PR#3235 makes the following obsolete - # CRYPTONOTE_MAX_BLOCK_NUMBER = 500000000 - params['max_height'] = params.get('max_height', 500000000) + _pmts = self.raw_request(method, params) + pmts = _pmts.get('in', []) + elif method == 'get_transfer_by_txid': + pmts = [] + for txid in pmtfilter.tx_ids: + params['txid'] = txid + try: + _pmts = self.raw_request(method, params, squelch_error_logging=True) + except exceptions.TransactionNotFound: + continue + pmts.extend(_pmts['transfers']) else: - arg = 'payments' # NOTE: the API uses (min, max] range which is confusing params['min_block_height'] = (pmtfilter.min_height or 1) - 1 - _pmts = self.raw_request(method, params) - pmts = _pmts.get(arg, []) + _pmts = self.raw_request(method, params) + pmts = _pmts.get('payments', []) if pmtfilter.unconfirmed: pmts.extend(_pmts.get('pool', [])) return list(pmtfilter.filter(map(self._inpayment, pmts))) def transfers_out(self, account, pmtfilter): - _pmts = self.raw_request('get_transfers', { - 'account_index': account, - 'in': False, - 'out': pmtfilter.confirmed, - 'pool': False, - 'pending': pmtfilter.unconfirmed}) - pmts = _pmts.get('out', []) - if pmtfilter.unconfirmed: - pmts.extend(_pmts.get('pending', [])) + if pmtfilter.tx_ids: + pmts = [] + for txid in pmtfilter.tx_ids: + try: + _pmts = self.raw_request( + 'get_transfer_by_txid', + {'account_index': account, 'txid': txid}, + squelch_error_logging=True) + except exceptions.TransactionNotFound: + continue + pmts.extend(_pmts['transfers']) + else: + _pmts = self.raw_request('get_transfers', { + 'account_index': account, + 'in': False, + 'out': pmtfilter.confirmed, + 'pool': False, + 'pending': pmtfilter.unconfirmed}) + pmts = _pmts.get('out', []) + if pmtfilter.unconfirmed: + pmts.extend(_pmts.get('pending', [])) return list(pmtfilter.filter(map(self._outpayment, pmts))) def _paymentdict(self, data): @@ -363,7 +383,8 @@ class JSONRPCWallet(object): if 'error' in result: err = result['error'] - _log.error(u"JSON RPC error:\n{result}".format(result=_ppresult)) + if not squelch_error_logging: + _log.error(u"JSON RPC error:\n{result}".format(result=_ppresult)) if err['code'] in _err2exc: raise _err2exc[err['code']](err['message']) else: diff --git a/monero/transaction.py b/monero/transaction.py index 17d74f5..95b4287 100644 --- a/monero/transaction.py +++ b/monero/transaction.py @@ -1,3 +1,4 @@ +import re import sys import warnings from .address import address @@ -113,6 +114,13 @@ class PaymentManager(object): return fetch(self.account_idx, PaymentFilter(**filterparams)) +def _validate_tx_id(txid): + if not bool(re.compile('^[0-9a-f]{64}$').match(txid)): + raise ValueError("Transaction ID must be a 64-character hexadecimal string, not " + "'{}'".format(txid)) + return txid + + class _ByHeight(object): """A helper class used as key in sorting of payments by height. Mempool goes on top, blockchain payments are ordered with descending block numbers. @@ -158,6 +166,7 @@ class PaymentFilter(object): self.unconfirmed = filterparams.pop('unconfirmed', False) self.confirmed = filterparams.pop('confirmed', True) _local_address = filterparams.pop('local_address', None) + _tx_id = filterparams.pop('tx_id', None) _payment_id = filterparams.pop('payment_id', None) if len(filterparams) > 0: raise ValueError("Excessive arguments for payment query: {}".format(filterparams)) @@ -179,6 +188,18 @@ class PaymentFilter(object): except TypeError: local_addresses = [_local_address] self.local_addresses = list(map(address, local_addresses)) + if _tx_id is None: + self.tx_ids = [] + else: + if isinstance(_tx_id, _str_types): + tx_ids = [_tx_id] + else: + try: + iter(_tx_id) + tx_ids = _tx_id + except TypeError: + tx_ids = [_tx_id] + self.tx_ids = list(map(_validate_tx_id, tx_ids)) if _payment_id is None: self.payment_ids = [] else: @@ -209,6 +230,8 @@ class PaymentFilter(object): return False if self.payment_ids and payment.payment_id not in self.payment_ids: return False + if self.tx_ids and payment.transaction.hash not in self.tx_ids: + return False if self.local_addresses and payment.local_address not in self.local_addresses: return False return True diff --git a/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-00-get_accounts.json b/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-00-get_accounts.json new file mode 100644 index 0000000..7ec3821 --- /dev/null +++ b/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-00-get_accounts.json @@ -0,0 +1,26 @@ +{ + "id": 0, + "jsonrpc": "2.0", + "result": { + "subaddress_accounts": [ + { + "account_index": 0, + "balance": 119141601989972, + "base_address": "56cXYWG13YKaT9z1aEy2hb9TZNnxrW3zE9S4nTQVDux5Qq7UYsmjuux3Zstxkorj9HAufyWLU3FwHW4uERQF6tkeUVogGN3", + "label": "Primary account", + "tag": "", + "unlocked_balance": 119141601989972 + }, + { + "account_index": 1, + "balance": 1000000000000, + "base_address": "79kTZg96pMf2Dt9rLEWnLzTUB8XC1wMhxaJyxa79hJu6bK9CfFnfbSL1GJNZbqhv9xPqJhRj2Yfb7QUWa2zeEw56H4KiUfN", + "label": "Untitled account", + "tag": "", + "unlocked_balance": 1000000000000 + } + ], + "total_balance": 120141601989972, + "total_unlocked_balance": 120141601989972 + } +} diff --git a/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-30-get_transfer_by_txid.json b/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-30-get_transfer_by_txid.json new file mode 100644 index 0000000..d516656 --- /dev/null +++ b/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-30-get_transfer_by_txid.json @@ -0,0 +1,58 @@ +{ + "id": 0, + "jsonrpc": "2.0", + "result": { + "transfer": { + "address": "7AEBRUmNcjhUjiqdVpeKKYiAVZ216AYdhBFx8UUfjPhWdKujoosnsUtHCohLcYWUXFdNiqnBsMmCFCyDkSmat3Ys4H4yHUp", + "amount": 4000000000000, + "confirmations": 1, + "double_spend_seen": false, + "fee": 195890000, + "height": 409450, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 0, + "minor": 232 + }, + "subaddr_indices": [ + { + "major": 0, + "minor": 232 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568408341, + "txid": "55e758d7d259bb316551ddcdd4808711de99c30b8b5c52de3e95e563fd92d156", + "type": "in", + "unlock_time": 0 + }, + "transfers": [ + { + "address": "7AEBRUmNcjhUjiqdVpeKKYiAVZ216AYdhBFx8UUfjPhWdKujoosnsUtHCohLcYWUXFdNiqnBsMmCFCyDkSmat3Ys4H4yHUp", + "amount": 4000000000000, + "confirmations": 1, + "double_spend_seen": false, + "fee": 195890000, + "height": 409450, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 0, + "minor": 232 + }, + "subaddr_indices": [ + { + "major": 0, + "minor": 232 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568408341, + "txid": "55e758d7d259bb316551ddcdd4808711de99c30b8b5c52de3e95e563fd92d156", + "type": "in", + "unlock_time": 0 + } + ] + } +} diff --git a/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-31b52-get_transfer_by_txid.json b/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-31b52-get_transfer_by_txid.json new file mode 100644 index 0000000..dbbaa6b --- /dev/null +++ b/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-31b52-get_transfer_by_txid.json @@ -0,0 +1,58 @@ +{ + "id": 0, + "jsonrpc": "2.0", + "result": { + "transfer": { + "address": "7AwMU2kQkqseHgdVWPaD6J8QvUbomAR3ThBkyaBH3dFTTwT2CcQaZyrSetwq2TXtweHFpprTN1SmEKM2wG64oFdZQ5mqkLe", + "amount": 6000000000000, + "confirmations": 0, + "double_spend_seen": false, + "fee": 195990000, + "height": 0, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 0, + "minor": 233 + }, + "subaddr_indices": [ + { + "major": 0, + "minor": 233 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568409539, + "txid": "31b527fb9c27e759d56892fef93136df1057186c5cf4e3c93c5298b70160f562", + "type": "pool", + "unlock_time": 0 + }, + "transfers": [ + { + "address": "7AwMU2kQkqseHgdVWPaD6J8QvUbomAR3ThBkyaBH3dFTTwT2CcQaZyrSetwq2TXtweHFpprTN1SmEKM2wG64oFdZQ5mqkLe", + "amount": 6000000000000, + "confirmations": 0, + "double_spend_seen": false, + "fee": 195990000, + "height": 0, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 0, + "minor": 233 + }, + "subaddr_indices": [ + { + "major": 0, + "minor": 233 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568409539, + "txid": "31b527fb9c27e759d56892fef93136df1057186c5cf4e3c93c5298b70160f562", + "type": "pool", + "unlock_time": 0 + } + ] + } +} diff --git a/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-55e75-get_transfer_by_txid.json b/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-55e75-get_transfer_by_txid.json new file mode 100644 index 0000000..d516656 --- /dev/null +++ b/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-55e75-get_transfer_by_txid.json @@ -0,0 +1,58 @@ +{ + "id": 0, + "jsonrpc": "2.0", + "result": { + "transfer": { + "address": "7AEBRUmNcjhUjiqdVpeKKYiAVZ216AYdhBFx8UUfjPhWdKujoosnsUtHCohLcYWUXFdNiqnBsMmCFCyDkSmat3Ys4H4yHUp", + "amount": 4000000000000, + "confirmations": 1, + "double_spend_seen": false, + "fee": 195890000, + "height": 409450, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 0, + "minor": 232 + }, + "subaddr_indices": [ + { + "major": 0, + "minor": 232 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568408341, + "txid": "55e758d7d259bb316551ddcdd4808711de99c30b8b5c52de3e95e563fd92d156", + "type": "in", + "unlock_time": 0 + }, + "transfers": [ + { + "address": "7AEBRUmNcjhUjiqdVpeKKYiAVZ216AYdhBFx8UUfjPhWdKujoosnsUtHCohLcYWUXFdNiqnBsMmCFCyDkSmat3Ys4H4yHUp", + "amount": 4000000000000, + "confirmations": 1, + "double_spend_seen": false, + "fee": 195890000, + "height": 409450, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 0, + "minor": 232 + }, + "subaddr_indices": [ + { + "major": 0, + "minor": 232 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568408341, + "txid": "55e758d7d259bb316551ddcdd4808711de99c30b8b5c52de3e95e563fd92d156", + "type": "in", + "unlock_time": 0 + } + ] + } +} diff --git a/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-7ab84-get_transfer_by_txid.json b/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-7ab84-get_transfer_by_txid.json new file mode 100644 index 0000000..f3c2edb --- /dev/null +++ b/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-7ab84-get_transfer_by_txid.json @@ -0,0 +1,108 @@ +{ + "id": 0, + "jsonrpc": "2.0", + "result": { + "transfer": { + "address": "76SJ4sPWzgQKE3fBbAoRTC7HtewGAo37VEgMpmHPEfPMRssYQgdeVyfJt5rcEcHw9dJJ4cLSkZ9c5fPTnKFHKh43UKmJs25", + "amount": 1000000000000, + "confirmations": 208, + "double_spend_seen": false, + "fee": 292510000, + "height": 409227, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 0, + "minor": 8 + }, + "subaddr_indices": [ + { + "major": 0, + "minor": 8 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568388430, + "txid": "7ab84fe2fb34467c590cde2f7d6ba7de5928a2db6c84c6ccfff8962eca0ad99c", + "type": "in", + "unlock_time": 0 + }, + "transfers": [ + { + "address": "76SJ4sPWzgQKE3fBbAoRTC7HtewGAo37VEgMpmHPEfPMRssYQgdeVyfJt5rcEcHw9dJJ4cLSkZ9c5fPTnKFHKh43UKmJs25", + "amount": 1000000000000, + "confirmations": 208, + "double_spend_seen": false, + "fee": 292510000, + "height": 409227, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 0, + "minor": 8 + }, + "subaddr_indices": [ + { + "major": 0, + "minor": 8 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568388430, + "txid": "7ab84fe2fb34467c590cde2f7d6ba7de5928a2db6c84c6ccfff8962eca0ad99c", + "type": "in", + "unlock_time": 0 + }, + { + "address": "75LwnK3zfQS5mEzxgEdyep8SSwnvGSKcMLnCpzUqCF4CMFHDNrSnCojfoTRV9EWy2Y3ejeFLMPH9tAjagMAim8F8EKWjHos", + "amount": 1000000000000, + "confirmations": 208, + "double_spend_seen": false, + "fee": 292510000, + "height": 409227, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 0, + "minor": 19 + }, + "subaddr_indices": [ + { + "major": 0, + "minor": 19 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568388430, + "txid": "7ab84fe2fb34467c590cde2f7d6ba7de5928a2db6c84c6ccfff8962eca0ad99c", + "type": "in", + "unlock_time": 0 + }, + { + "address": "74sZRQ2sHs4YLh8PnW8fseUoUoM4bXQ3wQ6bfCr6YyxmK5QRawKLytF9CfRbuv581LEnXBj27Dwg6eNC4fhhrH9kUvpbWQ5", + "amount": 2000000000000, + "confirmations": 208, + "double_spend_seen": false, + "fee": 292510000, + "height": 409227, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 0, + "minor": 29 + }, + "subaddr_indices": [ + { + "major": 0, + "minor": 29 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568388430, + "txid": "7ab84fe2fb34467c590cde2f7d6ba7de5928a2db6c84c6ccfff8962eca0ad99c", + "type": "in", + "unlock_time": 0 + } + ] + } +} diff --git a/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-e0b15-get_transfer_by_txid.json b/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-e0b15-get_transfer_by_txid.json new file mode 100644 index 0000000..5ed0357 --- /dev/null +++ b/tests/data/test_jsonrpcwallet/test_incoming_by_tx_id-e0b15-get_transfer_by_txid.json @@ -0,0 +1,8 @@ +{ + "error": { + "code": -8, + "message": "Transaction not found." + }, + "id": 0, + "jsonrpc": "2.0" +} diff --git a/tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-00-get_accounts.json b/tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-00-get_accounts.json new file mode 100644 index 0000000..533ff8b --- /dev/null +++ b/tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-00-get_accounts.json @@ -0,0 +1,26 @@ +{ + "id": 0, + "jsonrpc": "2.0", + "result": { + "subaddress_accounts": [ + { + "account_index": 0, + "balance": 130141601989972, + "base_address": "56cXYWG13YKaT9z1aEy2hb9TZNnxrW3zE9S4nTQVDux5Qq7UYsmjuux3Zstxkorj9HAufyWLU3FwHW4uERQF6tkeUVogGN3", + "label": "Primary account", + "tag": "", + "unlocked_balance": 123141601989972 + }, + { + "account_index": 1, + "balance": 458323130000, + "base_address": "79kTZg96pMf2Dt9rLEWnLzTUB8XC1wMhxaJyxa79hJu6bK9CfFnfbSL1GJNZbqhv9xPqJhRj2Yfb7QUWa2zeEw56H4KiUfN", + "label": "Untitled account", + "tag": "", + "unlocked_balance": 458323130000 + } + ], + "total_balance": 130599925119972, + "total_unlocked_balance": 123599925119972 + } +} diff --git a/tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-362c3-get_transfer_by_txid.json b/tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-362c3-get_transfer_by_txid.json new file mode 100644 index 0000000..5a6a23f --- /dev/null +++ b/tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-362c3-get_transfer_by_txid.json @@ -0,0 +1,78 @@ +{ + "id": 0, + "jsonrpc": "2.0", + "result": { + "transfer": { + "address": "79kTZg96pMf2Dt9rLEWnLzTUB8XC1wMhxaJyxa79hJu6bK9CfFnfbSL1GJNZbqhv9xPqJhRj2Yfb7QUWa2zeEw56H4KiUfN", + "amount": 520000000000, + "confirmations": 23, + "destinations": [ + { + "address": "77yjHxBeLNq4mf4dhEB7ksD6WDaAEEguqHHvuFYyiLiMWwSrvYHftzT5c1HRS9iWa2UBn4MQLuz8jEiE6sPDfMzB81UMHaK", + "amount": 220000000000 + }, + { + "address": "787CXWuevtt2SdD9x6rB7hCk73pYVv7HYgAUAPsYQJ9zAmQN7Ksxr5KieQFXuEL6ZSMqMDNbbaUze365iF2DbkjB9bcL82t", + "amount": 300000000000 + } + ], + "double_spend_seen": false, + "fee": 280650000, + "height": 409449, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 1, + "minor": 0 + }, + "subaddr_indices": [ + { + "major": 1, + "minor": 1 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568408151, + "txid": "362c3a4e601d5847b3882c3debfd28a0ee31654e433c38498539677199c304c2", + "type": "out", + "unlock_time": 0 + }, + "transfers": [ + { + "address": "79kTZg96pMf2Dt9rLEWnLzTUB8XC1wMhxaJyxa79hJu6bK9CfFnfbSL1GJNZbqhv9xPqJhRj2Yfb7QUWa2zeEw56H4KiUfN", + "amount": 520000000000, + "confirmations": 23, + "destinations": [ + { + "address": "77yjHxBeLNq4mf4dhEB7ksD6WDaAEEguqHHvuFYyiLiMWwSrvYHftzT5c1HRS9iWa2UBn4MQLuz8jEiE6sPDfMzB81UMHaK", + "amount": 220000000000 + }, + { + "address": "787CXWuevtt2SdD9x6rB7hCk73pYVv7HYgAUAPsYQJ9zAmQN7Ksxr5KieQFXuEL6ZSMqMDNbbaUze365iF2DbkjB9bcL82t", + "amount": 300000000000 + } + ], + "double_spend_seen": false, + "fee": 280650000, + "height": 409449, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 1, + "minor": 0 + }, + "subaddr_indices": [ + { + "major": 1, + "minor": 1 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568408151, + "txid": "362c3a4e601d5847b3882c3debfd28a0ee31654e433c38498539677199c304c2", + "type": "out", + "unlock_time": 0 + } + ] + } +} diff --git a/tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-afaf0-get_transfer_by_txid.json b/tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-afaf0-get_transfer_by_txid.json new file mode 100644 index 0000000..b6ea637 --- /dev/null +++ b/tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-afaf0-get_transfer_by_txid.json @@ -0,0 +1,70 @@ +{ + "id": 0, + "jsonrpc": "2.0", + "result": { + "transfer": { + "address": "79kTZg96pMf2Dt9rLEWnLzTUB8XC1wMhxaJyxa79hJu6bK9CfFnfbSL1GJNZbqhv9xPqJhRj2Yfb7QUWa2zeEw56H4KiUfN", + "amount": 40000000000, + "confirmations": 0, + "destinations": [ + { + "address": "72nfCSqpigFWaMeKyZYjKMQRvYFxWvJaf8Nnb1KxVndeTuL7avqoCF5NME4WGMqwmK58i8BnKxCz543mFoZhuUMpGhN1dcm", + "amount": 40000000000 + } + ], + "double_spend_seen": false, + "fee": 979320000, + "height": 0, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 1, + "minor": 0 + }, + "subaddr_indices": [ + { + "major": 1, + "minor": 0 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568413596, + "txid": "afaf04e5e40c6b60fc7cc928a88843fc96031ec2b567c310ee61abf3d00020da", + "type": "pending", + "unlock_time": 0 + }, + "transfers": [ + { + "address": "79kTZg96pMf2Dt9rLEWnLzTUB8XC1wMhxaJyxa79hJu6bK9CfFnfbSL1GJNZbqhv9xPqJhRj2Yfb7QUWa2zeEw56H4KiUfN", + "amount": 40000000000, + "confirmations": 0, + "destinations": [ + { + "address": "72nfCSqpigFWaMeKyZYjKMQRvYFxWvJaf8Nnb1KxVndeTuL7avqoCF5NME4WGMqwmK58i8BnKxCz543mFoZhuUMpGhN1dcm", + "amount": 40000000000 + } + ], + "double_spend_seen": false, + "fee": 979320000, + "height": 0, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 1, + "minor": 0 + }, + "subaddr_indices": [ + { + "major": 1, + "minor": 0 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568413596, + "txid": "afaf04e5e40c6b60fc7cc928a88843fc96031ec2b567c310ee61abf3d00020da", + "type": "pending", + "unlock_time": 0 + } + ] + } +} diff --git a/tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-eda89-get_transfer_by_txid.json b/tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-eda89-get_transfer_by_txid.json new file mode 100644 index 0000000..1e3e9cf --- /dev/null +++ b/tests/data/test_jsonrpcwallet/test_outgoing_by_tx_id-eda89-get_transfer_by_txid.json @@ -0,0 +1,70 @@ +{ + "id": 0, + "jsonrpc": "2.0", + "result": { + "transfer": { + "address": "79kTZg96pMf2Dt9rLEWnLzTUB8XC1wMhxaJyxa79hJu6bK9CfFnfbSL1GJNZbqhv9xPqJhRj2Yfb7QUWa2zeEw56H4KiUfN", + "amount": 21200000000, + "confirmations": 23, + "destinations": [ + { + "address": "72quvHYJ8QzivQyV4NMZ9h1gyZXyJZvsWZTwgVFRCw9b1eJ4yibHEnU3CVCCXJ7evqXhSEKJL2rjzCMV3LpXirR5B8EnkaE", + "amount": 21200000000 + } + ], + "double_spend_seen": false, + "fee": 196220000, + "height": 409449, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 1, + "minor": 0 + }, + "subaddr_indices": [ + { + "major": 1, + "minor": 2 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568408151, + "txid": "eda891adf76993f9066abd56a8a5aa5c51a7618298cab59ec37739f1c960596d", + "type": "out", + "unlock_time": 0 + }, + "transfers": [ + { + "address": "79kTZg96pMf2Dt9rLEWnLzTUB8XC1wMhxaJyxa79hJu6bK9CfFnfbSL1GJNZbqhv9xPqJhRj2Yfb7QUWa2zeEw56H4KiUfN", + "amount": 21200000000, + "confirmations": 23, + "destinations": [ + { + "address": "72quvHYJ8QzivQyV4NMZ9h1gyZXyJZvsWZTwgVFRCw9b1eJ4yibHEnU3CVCCXJ7evqXhSEKJL2rjzCMV3LpXirR5B8EnkaE", + "amount": 21200000000 + } + ], + "double_spend_seen": false, + "fee": 196220000, + "height": 409449, + "note": "", + "payment_id": "0000000000000000", + "subaddr_index": { + "major": 1, + "minor": 0 + }, + "subaddr_indices": [ + { + "major": 1, + "minor": 2 + } + ], + "suggested_confirmations_threshold": 1, + "timestamp": 1568408151, + "txid": "eda891adf76993f9066abd56a8a5aa5c51a7618298cab59ec37739f1c960596d", + "type": "out", + "unlock_time": 0 + } + ] + } +} diff --git a/tests/test_jsonrpcwallet.py b/tests/test_jsonrpcwallet.py index a16f9f2..147aecd 100644 --- a/tests/test_jsonrpcwallet.py +++ b/tests/test_jsonrpcwallet.py @@ -462,6 +462,7 @@ class JSONRPCWalletTestCase(JSONTestCase): self.assertIsInstance(pmt.transaction.fee, Decimal) self.assertIsInstance(pmt.transaction.height, (int, type(None))) + @patch('monero.backends.jsonrpc.requests.post') def test_incoming_unconfirmed(self, mock_post): mock_post.return_value.status_code = 200 @@ -505,6 +506,165 @@ class JSONRPCWalletTestCase(JSONTestCase): self.assertIsInstance(pmt.transaction.fee, Decimal) self.assertIs(pmt.transaction.height, None) + @responses.activate + def test_incoming_by_tx_id(self): + # 3 payments in one transaction + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-00-get_accounts.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-7ab84-get_transfer_by_txid.json'), + status=200) + self.wallet = Wallet(JSONRPCWallet()) + pmts = self.wallet.incoming(tx_id='7ab84fe2fb34467c590cde2f7d6ba7de5928a2db6c84c6ccfff8962eca0ad99c') + self.assertEqual(len(pmts), 3) + self.assertEqual(pmts[0].amount, Decimal(1)) + self.assertEqual(pmts[1].amount, Decimal(1)) + self.assertEqual(pmts[2].amount, Decimal(2)) + + @responses.activate + def test_incoming_by_tx_id__with_min_height(self): + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-00-get_accounts.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-7ab84-get_transfer_by_txid.json'), + status=200) + self.wallet = Wallet(JSONRPCWallet()) + pmts = self.wallet.incoming(min_height=409223, + tx_id='7ab84fe2fb34467c590cde2f7d6ba7de5928a2db6c84c6ccfff8962eca0ad99c') + self.assertEqual(len(pmts), 3) + self.assertEqual(pmts[0].amount, Decimal(1)) + self.assertEqual(pmts[1].amount, Decimal(1)) + self.assertEqual(pmts[2].amount, Decimal(2)) + + @responses.activate + def test_incoming_by_tx_id__with_max_height(self): + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-00-get_accounts.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-7ab84-get_transfer_by_txid.json'), + status=200) + self.wallet = Wallet(JSONRPCWallet()) + pmts = self.wallet.incoming(max_height=409223, + tx_id='7ab84fe2fb34467c590cde2f7d6ba7de5928a2db6c84c6ccfff8962eca0ad99c') + self.assertEqual(len(pmts), 0) + + @responses.activate + def test_incoming_by_tx_id__not_found(self): + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-00-get_accounts.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-e0b15-get_transfer_by_txid.json'), + status=200) + self.wallet = Wallet(JSONRPCWallet()) + pmts = self.wallet.incoming(tx_id='e0b15ac819c94ed9ba81edb955a98c696f3216335960ccf90018d76a8dcb0e7e') + self.assertEqual(len(pmts), 0) + + @responses.activate + def test_incoming_by_tx_id__multiple_ids(self): + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-00-get_accounts.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-7ab84-get_transfer_by_txid.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-55e75-get_transfer_by_txid.json'), + status=200) + self.wallet = Wallet(JSONRPCWallet()) + pmts = self.wallet.incoming(tx_id=[ + '7ab84fe2fb34467c590cde2f7d6ba7de5928a2db6c84c6ccfff8962eca0ad99c', + '55e758d7d259bb316551ddcdd4808711de99c30b8b5c52de3e95e563fd92d156']) + self.assertEqual(len(pmts), 4) + self.assertEqual(pmts[0].amount, Decimal(4)) + self.assertEqual(pmts[1].amount, Decimal(1)) + self.assertEqual(pmts[2].amount, Decimal(1)) + self.assertEqual(pmts[3].amount, Decimal(2)) + + @responses.activate + def test_incoming_by_tx_id__mempool(self): + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-00-get_accounts.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-31b52-get_transfer_by_txid.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-31b52-get_transfer_by_txid.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_incoming_by_tx_id-31b52-get_transfer_by_txid.json'), + status=200) + self.wallet = Wallet(JSONRPCWallet()) + pmts = self.wallet.incoming( + tx_id='31b527fb9c27e759d56892fef93136df1057186c5cf4e3c93c5298b70160f562') + self.assertEqual(len(pmts), 0) + pmts = self.wallet.incoming( + tx_id='31b527fb9c27e759d56892fef93136df1057186c5cf4e3c93c5298b70160f562', + unconfirmed=True) + self.assertEqual(len(pmts), 1) + pmts = self.wallet.incoming( + tx_id='31b527fb9c27e759d56892fef93136df1057186c5cf4e3c93c5298b70160f562', + confirmed=False) + self.assertEqual(len(pmts), 0) + + @responses.activate + def test_outgoing_by_tx_id(self): + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_outgoing_by_tx_id-00-get_accounts.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_outgoing_by_tx_id-362c3-get_transfer_by_txid.json'), + status=200) + self.wallet = Wallet(JSONRPCWallet()) + acc = self.wallet.accounts[1] + pmts = acc.outgoing(tx_id='362c3a4e601d5847b3882c3debfd28a0ee31654e433c38498539677199c304c2') + self.assertEqual(len(pmts), 1) + self.assertEqual(pmts[0].amount, Decimal('0.52')) + + @responses.activate + def test_outgoing_by_tx_id__mempool(self): + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_outgoing_by_tx_id-00-get_accounts.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_outgoing_by_tx_id-afaf0-get_transfer_by_txid.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_outgoing_by_tx_id-afaf0-get_transfer_by_txid.json'), + status=200) + self.wallet = Wallet(JSONRPCWallet()) + acc = self.wallet.accounts[1] + pmts = acc.outgoing(tx_id='afaf04e5e40c6b60fc7cc928a88843fc96031ec2b567c310ee61abf3d00020da') + self.assertEqual(len(pmts), 0) + pmts = acc.outgoing( + tx_id='afaf04e5e40c6b60fc7cc928a88843fc96031ec2b567c310ee61abf3d00020da', + unconfirmed=True) + self.assertEqual(len(pmts), 1) + + @responses.activate + def test_outgoing_by_tx_id__multiple_ids(self): + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_outgoing_by_tx_id-00-get_accounts.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_outgoing_by_tx_id-362c3-get_transfer_by_txid.json'), + status=200) + responses.add(responses.POST, self.jsonrpc_url, + json=self._read('test_outgoing_by_tx_id-eda89-get_transfer_by_txid.json'), + status=200) + self.wallet = Wallet(JSONRPCWallet()) + acc = self.wallet.accounts[1] + pmts = acc.outgoing(tx_id=[ + '362c3a4e601d5847b3882c3debfd28a0ee31654e433c38498539677199c304c2', + 'eda891adf76993f9066abd56a8a5aa5c51a7618298cab59ec37739f1c960596d']) + self.assertEqual(len(pmts), 2) + self.assertEqual(pmts[0].amount, Decimal('0.52')) + self.assertEqual(pmts[1].amount, Decimal('0.0212')) + @patch('monero.backends.jsonrpc.requests.post') def test_incoming_by_payment_ids(self, mock_post): # These queries will use get_bulk_payments RPC method instead of get_transfers