From ed383d6040b15c1ee248719c6c1415b8c4f67cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sa=C5=82aban?= Date: Tue, 30 Jan 2018 09:43:08 +0100 Subject: [PATCH] Update checks and tests --- monero/address.py | 18 +- monero/backends/jsonrpc.py | 17 +- monero/numbers.py | 2 +- monero/transaction.py | 44 +++-- monero/wallet.py | 26 +-- setup.py | 1 + tests/__init__.py | 8 +- tests/{address.py => test_address.py} | 17 ++ tests/{wallet.py => test_jsonrpcwallet.py} | 165 +----------------- tests/{numbers.py => test_numbers.py} | 4 +- tests/test_transaction.py | 28 ++++ tests/test_wallet.py | 185 +++++++++++++++++++++ 12 files changed, 287 insertions(+), 228 deletions(-) rename tests/{address.py => test_address.py} (88%) rename tests/{wallet.py => test_jsonrpcwallet.py} (72%) rename tests/{numbers.py => test_numbers.py} (88%) create mode 100644 tests/test_transaction.py create mode 100644 tests/test_wallet.py diff --git a/monero/address.py b/monero/address.py index 7ae6032..07d99ad 100644 --- a/monero/address.py +++ b/monero/address.py @@ -23,12 +23,12 @@ class Address(object): _valid_netbytes = (18, 53) # NOTE: _valid_netbytes order is (real, testnet) - def __init__(self, address, label=None): - address = str(address) - if not _ADDR_REGEX.match(address): + def __init__(self, addr, label=None): + addr = str(addr) + if not _ADDR_REGEX.match(addr): raise ValueError("Address must be 95 characters long base58-encoded string, " - "is {addr} ({len} chars length)".format(addr=address, len=len(address))) - self._decode(address) + "is {addr} ({len} chars length)".format(addr=addr, len=len(addr))) + self._decode(addr) self.label = label or self.label def _decode(self, address): @@ -86,7 +86,7 @@ class Address(object): return str(self) == str(other) if isinstance(other, str): return str(self) == other - return super() + return super(Address, self).__eq__(other) class SubAddress(Address): @@ -148,12 +148,12 @@ def address(addr, label=None): return Address(addr, label=label) elif netbyte in SubAddress._valid_netbytes: return SubAddress(addr, label=label) - raise ValueError("Invalid address netbyte {nb}. Allowed values are: {allowed}".format( - nb=hexlify(chr(netbyte)), + raise ValueError("Invalid address netbyte {nb:x}. Allowed values are: {allowed}".format( + nb=netbyte, allowed=", ".join(map( lambda b: '%02x' % b, sorted(Address._valid_netbytes + SubAddress._valid_netbytes))))) elif _IADDR_REGEX.match(addr): return IntegratedAddress(addr) raise ValueError("Address must be either 95 or 106 characters long base58-encoded string, " - "is {addr} ({len} chars length)".format(addr=address, len=len(address))) + "is {addr} ({len} chars length)".format(addr=addr, len=len(addr))) diff --git a/monero/backends/jsonrpc.py b/monero/backends/jsonrpc.py index 6b1fad9..aa88956 100644 --- a/monero/backends/jsonrpc.py +++ b/monero/backends/jsonrpc.py @@ -212,27 +212,18 @@ class JSONRPCWallet(object): pmts.extend(_pmts.get('pool', [])) return list(pmtfilter.filter(map(self._outpayment, pmts))) - def get_transaction(self, txhash): - _tx = self.raw_request('get_transfer_by_txid', {'txid': str(txhash)})['transfer'] - if _tx['type'] == 'in': - return self._inpayment(tx) - elif _tx['type'] == 'out': - return self._outpayment(tx) - return Payment(**self._paymentdict(tx)) - def _paymentdict(self, data): pid = data.get('payment_id', None) - addr = data.get('address', None) - if addr: - addr = address(addr) + laddr = data.get('address', None) + if laddr: + laddr = address(laddr) return { - 'txhash': data.get('txid', data.get('tx_hash')), 'payment_id': None if pid is None else PaymentID(pid), 'amount': from_atomic(data['amount']), 'timestamp': datetime.fromtimestamp(data['timestamp']) if 'timestamp' in data else None, 'note': data.get('note', None), 'transaction': self._tx(data), - 'address': addr, + 'local_address': laddr, } def _inpayment(self, data): diff --git a/monero/numbers.py b/monero/numbers.py index 6e09241..5b7c761 100644 --- a/monero/numbers.py +++ b/monero/numbers.py @@ -59,4 +59,4 @@ class PaymentID(object): return int(self) == other elif isinstance(other, _str_types): return str(self) == other - return super() + return super(PaymentID, self).__eq__(other) diff --git a/monero/transaction.py b/monero/transaction.py index c2cadf5..49632b8 100644 --- a/monero/transaction.py +++ b/monero/transaction.py @@ -7,14 +7,18 @@ class Payment(object): amount = None timestamp = None transaction = None - address = None + local_address = None + note = '' def __init__(self, **kwargs): - self.amount = kwargs.get('amount', self.amount) - self.timestamp = kwargs.get('timestamp', self.timestamp) - self.payment_id = kwargs.get('payment_id', self.payment_id) - self.transaction = kwargs.get('transaction', self.transaction) - self.address = kwargs.get('address', self.address) + self.amount = kwargs.pop('amount', self.amount) + self.timestamp = kwargs.pop('timestamp', self.timestamp) + self.payment_id = kwargs.pop('payment_id', self.payment_id) + self.transaction = kwargs.pop('transaction', self.transaction) + self.local_address = kwargs.pop('local_address', self.local_address) + self.note = kwargs.pop('note', self.note) + if len(kwargs): + raise ValueError("Excessive arguments for {}: {}".format(type(self), kwargs)) def __repr__(self): return "{} {:.12f} id={}".format(self.transaction.hash, self.amount, self.payment_id) @@ -26,12 +30,6 @@ class IncomingPayment(Payment): class OutgoingPayment(Payment): - note = '' - - def __init__(self, **kwargs): - super(OutgoingPayment, self).__init__(**kwargs) - self.note = kwargs.get('note', self.note) - def __repr__(self): return "out: {} {:.12f} id={}".format(self.transaction.hash, self.amount, self.payment_id) @@ -82,23 +80,23 @@ class PaymentFilter(object): self.max_height = filterparams.pop('max_height', None) self.unconfirmed = filterparams.pop('unconfirmed', False) self.confirmed = filterparams.pop('confirmed', True) - _address = filterparams.pop('address', None) + _local_address = filterparams.pop('local_address', None) _payment_id = filterparams.pop('payment_id', None) if len(filterparams) > 0: - raise ValueError("Excessive arguments for payment query: {:r}".format(filterparams)) + raise ValueError("Excessive arguments for payment query: {}".format(filterparams)) - if _address is None: - self.addresses = [] + if _local_address is None: + self.local_addresses = [] else: - if isinstance(_address, _str_types): - addresses = [_address] + if isinstance(_local_address, _str_types): + local_addresses = [_local_address] else: try: - iter(_address) - addresses = _address + iter(_local_address) + local_addresses = _local_address except TypeError: - addresses = [_address] - self.addresses = list(map(address, addresses)) + local_addresses = [_local_address] + self.local_addresses = list(map(address, local_addresses)) if _payment_id is None: self.payment_ids = [] else: @@ -129,7 +127,7 @@ class PaymentFilter(object): return False if self.payment_ids and payment.payment_id not in self.payment_ids: return False - if self.addresses and payment.address not in self.addresses: + if self.local_addresses and payment.local_address not in self.local_addresses: return False return True diff --git a/monero/wallet.py b/monero/wallet.py index c12bef2..6a343ae 100644 --- a/monero/wallet.py +++ b/monero/wallet.py @@ -1,7 +1,7 @@ from . import address from . import prio from . import account -from .transaction import PaymentManager +from .transaction import Payment, PaymentManager class Wallet(object): accounts = None @@ -54,14 +54,15 @@ class Wallet(object): self.accounts.append(acc) return acc - def get_transaction(self, hash): - return self._backend.get_transaction(hash) - - def confirmations(self, txn): - txn = self._backend.get_transaction(txn) - if txn.height is None: + def confirmations(self, txn_or_pmt): + if isinstance(txn_or_pmt, Payment): + txn = txn_or_pmt.transaction + else: + txn = txn_or_pmt + try: + return max(0, self.height() - txn.height) + except TypeError: return 0 - return max(0, self.height() - txn.height) # Following methods operate on default account (index=0) def balances(self): @@ -79,15 +80,6 @@ class Wallet(object): def new_address(self, label=None): return self.accounts[0].new_address(label=label) - def payments(self, payment_id): - return self.accounts[0].payments(payment_id) - - def transfers_in(self, confirmed=True, unconfirmed=False): - return self.accounts[0].transfers_in(confirmed=confirmed, unconfirmed=unconfirmed) - - def transfers_out(self, confirmed=True, unconfirmed=True): - return self.accounts[0].transfers_out(confirmed=confirmed, unconfirmed=unconfirmed) - def transfer(self, address, amount, priority=prio.NORMAL, ringsize=5, payment_id=None, unlock_time=0, relay=True): diff --git a/setup.py b/setup.py index e74983c..eb37253 100644 --- a/setup.py +++ b/setup.py @@ -24,4 +24,5 @@ setup( 'Programming Language :: Python', ], keywords = 'monero cryptocurrency', + test_suite='tests', ) diff --git a/tests/__init__.py b/tests/__init__.py index 7e818f3..ed42103 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1,5 @@ -from .address import * -from .numbers import * -from .wallet import * +from . import test_address +from . import test_numbers +from . import test_transaction +from . import test_wallet +from . import test_jsonrpcwallet diff --git a/tests/address.py b/tests/test_address.py similarity index 88% rename from tests/address.py rename to tests/test_address.py index 89d3da5..7ddcbde 100644 --- a/tests/address.py +++ b/tests/test_address.py @@ -58,6 +58,8 @@ class Tests(object): self.assertEqual(sa.is_testnet(), self.testnet) self.assertEqual(sa2.is_testnet(), self.testnet) + self.assertNotEqual(a, 0) + def test_idempotence(self): a = Address(self.addr) a_idem = Address(a) @@ -83,6 +85,21 @@ class Tests(object): self.assertRaises(TypeError, a.with_payment_id, "%x" % (2**64+1)) s = SubAddress(self.subaddr) self.assertRaises(TypeError, s.with_payment_id, 0) + self.assertRaises(ValueError, address, 'whatever') + self.assertRaises(ValueError, Address, 'whatever') + self.assertRaises(ValueError, SubAddress, 'whatever') + self.assertRaises(ValueError, IntegratedAddress, 'whatever') + # Aeon + self.assertRaises( + ValueError, + address, + 'Wmtj8UAJhdrhbKvwyBJmLEUZKHcffv2VHNBaq6oTxJFwJjUj3QwMUSS32mddSX7vchbxXdmb4QuZA9TsN47441f61yAYLQYTo') + # invalid netbyte + self.assertRaises( + ValueError, + address, + 'Cf6RinMUztY5otm6NEFjg3UWBBkXK6Lh23wKrLFMEcCY7i3A6aPLH9i4QMCkf6CdWk8Q9N7yoJf7ANKgtQMuPM6JANXgCWs') + def test_type_mismatch(self): self.assertRaises(ValueError, Address, self.iaddr) diff --git a/tests/wallet.py b/tests/test_jsonrpcwallet.py similarity index 72% rename from tests/wallet.py rename to tests/test_jsonrpcwallet.py index 2b73eb1..e0f63fa 100644 --- a/tests/wallet.py +++ b/tests/test_jsonrpcwallet.py @@ -13,163 +13,6 @@ from monero.numbers import PaymentID from monero.transaction import IncomingPayment, OutgoingPayment, Transaction from monero.backends.jsonrpc import JSONRPCWallet -class FiltersTestCase(unittest.TestCase): - def setUp(self): - class MockBackend(object): - def __init__(self): - self.transfers = [] - tx = Transaction( - timestamp=datetime(2018, 1, 29, 15, 0, 25), - height=1087606, - hash='a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14', - fee=Decimal('0.00352891')) - pm = IncomingPayment( - amount=Decimal('1'), - address=address('Bf6ngv7q2TBWup13nEm9AjZ36gLE6i4QCaZ7XScZUKDUeGbYEHmPRdegKGwLT8tBBK7P6L32RELNzCR6QzNFkmogDjvypyV'), - payment_id=PaymentID('0166d8da6c0045c51273dd65d6f63734beb8a84e0545a185b2cfd053fced9f5d'), - transaction=tx) - self.transfers.append(pm) - tx = Transaction( - timestamp=datetime(2018, 1, 29, 14, 57, 47), - height=1087601, - hash='f34b495cec77822a70f829ec8a5a7f1e727128d62e6b1438e9cb7799654d610e', - fee=Decimal('0.008661870000')) - pm = IncomingPayment( - amount=Decimal('3.000000000000'), - address=address('BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En'), - payment_id=PaymentID('f75ad90e25d71a12'), - transaction=tx) - self.transfers.append(pm) - tx = Transaction( - timestamp=datetime(2018, 1, 29, 13, 17, 18), - height=1087530, - hash='5c3ab739346e9d98d38dc7b8d36a4b7b1e4b6a16276946485a69797dbf887cd8', - fee=Decimal('0.000962550000')) - pm = IncomingPayment( - amount=Decimal('10.000000000000'), - address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'), - payment_id=PaymentID('f75ad90e25d71a12'), - transaction=tx) - self.transfers.append(pm) - tx = Transaction( - timestamp=datetime(2018, 1, 29, 13, 17, 18), - height=1087530, - hash='4ea70add5d0c7db33557551b15cd174972fcfc73bf0f6a6b47b7837564b708d3', - fee=Decimal('0.000962550000')) - pm = IncomingPayment( - amount=Decimal('4.000000000000'), - address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'), - payment_id=PaymentID('f75ad90e25d71a12'), - transaction=tx) - self.transfers.append(pm) - tx = Transaction( - timestamp=datetime(2018, 1, 29, 13, 17, 18), - height=1087530, - hash='e9a71c01875bec20812f71d155bfabf42024fde3ec82475562b817dcc8cbf8dc', - fee=Decimal('0.000962550000')) - pm = IncomingPayment( - amount=Decimal('2.120000000000'), - address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'), - payment_id=PaymentID('cb248105ea6a9189'), - transaction=tx) - self.transfers.append(pm) - tx = Transaction( - timestamp=datetime(2018, 1, 29, 14, 57, 47), - height=1087601, - hash='5ef7ead6a041101ed326568fbb59c128403cba46076c3f353cd110d969dac808', - fee=Decimal('0.000962430000')) - pm = IncomingPayment( - amount=Decimal('7.000000000000'), - address=address('BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En'), - payment_id=PaymentID('0000000000000000'), - transaction=tx) - self.transfers.append(pm) - tx = Transaction( - timestamp=datetime(2018, 1, 29, 13, 17, 18), - height=1087530, - hash='cc44568337a186c2e1ccc080b43b4ae9db26a07b7afd7edeed60ce2fc4a6477f', - fee=Decimal('0.000962550000')) - pm = IncomingPayment( - amount=Decimal('10.000000000000'), - address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'), - payment_id=PaymentID('0000000000000000'), - transaction=tx) - self.transfers.append(pm) - tx = Transaction( - timestamp=datetime(2018, 1, 29, 21, 13, 28), - height=None, - hash='d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c', - fee=Decimal('0.000961950000')) - pm = IncomingPayment( - amount=Decimal('3.140000000000'), - address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'), - payment_id=PaymentID('03f6649304ea4cb2'), - transaction=tx) - self.transfers.append(pm) - - def accounts(self): - return [Account(self, 0)] - - def transfers_in(self, account, pmtfilter): - return list(pmtfilter.filter(self.transfers)) - - self.wallet = Wallet(MockBackend()) - - def test_filter_none(self): - pmts = self.wallet.incoming() - self.assertEqual(len(pmts), 7) - - def test_filter_payment_id(self): - pmts = self.wallet.incoming(payment_id='cb248105ea6a9189') - self.assertEqual(len(pmts), 1) - self.assertEqual( - pmts[0].transaction.hash, - 'e9a71c01875bec20812f71d155bfabf42024fde3ec82475562b817dcc8cbf8dc') - pmts = self.wallet.incoming(payment_id='f75ad90e25d71a12') - self.assertEqual(len(pmts), 3) - pmts = self.wallet.incoming(payment_id=('cb248105ea6a9189', 'f75ad90e25d71a12')) - self.assertEqual(len(pmts), 4) - self.assertEqual( - pmts, - self.wallet.incoming(payment_id=(PaymentID('cb248105ea6a9189'), 'f75ad90e25d71a12'))) - - def test_filter_address(self): - pmts = self.wallet.incoming(address='BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En') - self.assertEqual(len(pmts), 2) - pmts = self.wallet.incoming(address=( - 'BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En', - 'Bf6ngv7q2TBWup13nEm9AjZ36gLE6i4QCaZ7XScZUKDUeGbYEHmPRdegKGwLT8tBBK7P6L32RELNzCR6QzNFkmogDjvypyV')) - self.assertEqual(len(pmts), 3) - - def test_filter_mempool(self): - pmts = self.wallet.incoming() - self.assertEqual(len(pmts), 7) - pmts = self.wallet.incoming(unconfirmed=True) - self.assertEqual(len(pmts), 8) - pmts = self.wallet.incoming(unconfirmed=True, confirmed=False) - self.assertEqual(len(pmts), 1) - self.assertEqual( - pmts[0].transaction.hash, - 'd29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c') - pmts = self.wallet.incoming(payment_id='03f6649304ea4cb2') - self.assertEqual(len(pmts), 0) - pmts = self.wallet.incoming(unconfirmed=True, payment_id='03f6649304ea4cb2') - self.assertEqual(len(pmts), 1) - pmts = self.wallet.incoming(address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC') - self.assertEqual(len(pmts), 4) - pmts = self.wallet.incoming(unconfirmed=True, address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC') - self.assertEqual(len(pmts), 5) - pmts = self.wallet.incoming( - address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC', - payment_id='03f6649304ea4cb2') - self.assertEqual(len(pmts), 0) - pmts = self.wallet.incoming( - unconfirmed=True, - address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC', - payment_id='03f6649304ea4cb2') - self.assertEqual(len(pmts), 1) - - class SubaddrWalletTestCase(unittest.TestCase): accounts_result = {'id': 0, 'jsonrpc': '2.0', @@ -279,7 +122,7 @@ class SubaddrWalletTestCase(unittest.TestCase): self.assertEqual(len(self.wallet.accounts[0].addresses()), 8) @patch('monero.backends.jsonrpc.requests.post') - def test_transfers_in(self, mock_post): + def test_incoming(self, mock_post): mock_post.return_value.status_code = 200 mock_post.return_value.json.return_value = self.accounts_result self.wallet = Wallet(JSONRPCWallet()) @@ -326,14 +169,14 @@ class SubaddrWalletTestCase(unittest.TestCase): self.assertEqual(len(list(pay_in)), 3) for pmt in pay_in: self.assertIsInstance(pmt, IncomingPayment) - self.assertIsInstance(pmt.address, Address) + self.assertIsInstance(pmt.local_address, Address) self.assertIsInstance(pmt.amount, Decimal) self.assertIsInstance(pmt.transaction, Transaction) self.assertIsInstance(pmt.transaction.fee, Decimal) self.assertIsInstance(pmt.transaction.height, int) @patch('monero.backends.jsonrpc.requests.post') - def test_transfers_out(self, mock_post): + def test_outgoing(self, mock_post): mock_post.return_value.status_code = 200 mock_post.return_value.json.return_value = self.accounts_result self.wallet = Wallet(JSONRPCWallet()) @@ -428,7 +271,7 @@ class SubaddrWalletTestCase(unittest.TestCase): self.assertEqual(len(list(pay_out)), 6) for pmt in pay_out: self.assertIsInstance(pmt, OutgoingPayment) - self.assertIsInstance(pmt.address, Address) + self.assertIsInstance(pmt.local_address, Address) self.assertIsInstance(pmt.amount, Decimal) self.assertIsInstance(pmt.timestamp, datetime) self.assertIsInstance(pmt.transaction, Transaction) diff --git a/tests/numbers.py b/tests/test_numbers.py similarity index 88% rename from tests/numbers.py rename to tests/test_numbers.py index f48460a..df96952 100644 --- a/tests/numbers.py +++ b/tests/test_numbers.py @@ -1,7 +1,7 @@ from decimal import Decimal import unittest -from monero.numbers import to_atomic, from_atomic, PaymentID +from monero.numbers import to_atomic, from_atomic, as_monero, PaymentID class NumbersTestCase(unittest.TestCase): def test_simple_numbers(self): @@ -14,6 +14,7 @@ class NumbersTestCase(unittest.TestCase): def test_rounding(self): self.assertEqual(to_atomic(Decimal('1.0000000000004')), 1000000000000) + self.assertEqual(as_monero(Decimal('1.0000000000014')), Decimal('1.000000000001')) def test_payment_id(self): pid = PaymentID('0') @@ -21,6 +22,7 @@ class NumbersTestCase(unittest.TestCase): self.assertEqual(pid, 0) self.assertEqual(pid, '0000000000000000') self.assertEqual(PaymentID(pid), pid) + self.assertNotEqual(pid, None) pid = PaymentID('abcdef') self.assertTrue(pid.is_short()) self.assertEqual(pid, 0xabcdef) diff --git a/tests/test_transaction.py b/tests/test_transaction.py new file mode 100644 index 0000000..d83c2d6 --- /dev/null +++ b/tests/test_transaction.py @@ -0,0 +1,28 @@ +from datetime import datetime +from decimal import Decimal +import unittest + +from monero.address import address +from monero.numbers import PaymentID +from monero.transaction import IncomingPayment, OutgoingPayment, Transaction + +class FiltersTestCase(unittest.TestCase): + def setUp(self): + self.tx1 = Transaction( + timestamp=datetime(2018, 1, 29, 15, 0, 25), + height=1087606, + hash='a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14', + fee=Decimal('0.00352891')) + self.pm1 = IncomingPayment( + amount=Decimal('1'), + local_address=address('Bf6ngv7q2TBWup13nEm9AjZ36gLE6i4QCaZ7XScZUKDUeGbYEHmPRdegKGwLT8tBBK7P6L32RELNzCR6QzNFkmogDjvypyV'), + payment_id=PaymentID('0166d8da6c0045c51273dd65d6f63734beb8a84e0545a185b2cfd053fced9f5d'), + transaction=self.tx1) + + def test_hash(self): + self.assertIn( + 'a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14', + repr(self.tx1)) + self.assertIn( + 'a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14', + repr(self.pm1)) diff --git a/tests/test_wallet.py b/tests/test_wallet.py new file mode 100644 index 0000000..5d98e8f --- /dev/null +++ b/tests/test_wallet.py @@ -0,0 +1,185 @@ +from datetime import datetime +from decimal import Decimal +import unittest + +from monero.wallet import Wallet +from monero.account import Account +from monero.address import address +from monero.numbers import PaymentID +from monero.transaction import IncomingPayment, OutgoingPayment, Transaction + +class FiltersTestCase(unittest.TestCase): + def setUp(self): + class MockBackend(object): + def __init__(self): + self.transfers = [] + tx = Transaction( + timestamp=datetime(2018, 1, 29, 15, 0, 25), + height=1087606, + hash='a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14', + fee=Decimal('0.00352891')) + pm = IncomingPayment( + amount=Decimal('1'), + local_address=address('Bf6ngv7q2TBWup13nEm9AjZ36gLE6i4QCaZ7XScZUKDUeGbYEHmPRdegKGwLT8tBBK7P6L32RELNzCR6QzNFkmogDjvypyV'), + payment_id=PaymentID('0166d8da6c0045c51273dd65d6f63734beb8a84e0545a185b2cfd053fced9f5d'), + transaction=tx) + self.transfers.append(pm) + tx = Transaction( + timestamp=datetime(2018, 1, 29, 14, 57, 47), + height=1087601, + hash='f34b495cec77822a70f829ec8a5a7f1e727128d62e6b1438e9cb7799654d610e', + fee=Decimal('0.008661870000')) + pm = IncomingPayment( + amount=Decimal('3.000000000000'), + local_address=address('BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En'), + payment_id=PaymentID('f75ad90e25d71a12'), + transaction=tx) + self.transfers.append(pm) + tx = Transaction( + timestamp=datetime(2018, 1, 29, 13, 17, 18), + height=1087530, + hash='5c3ab739346e9d98d38dc7b8d36a4b7b1e4b6a16276946485a69797dbf887cd8', + fee=Decimal('0.000962550000')) + pm = IncomingPayment( + amount=Decimal('10.000000000000'), + local_address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'), + payment_id=PaymentID('f75ad90e25d71a12'), + transaction=tx) + self.transfers.append(pm) + tx = Transaction( + timestamp=datetime(2018, 1, 29, 13, 17, 18), + height=1087530, + hash='4ea70add5d0c7db33557551b15cd174972fcfc73bf0f6a6b47b7837564b708d3', + fee=Decimal('0.000962550000')) + pm = IncomingPayment( + amount=Decimal('4.000000000000'), + local_address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'), + payment_id=PaymentID('f75ad90e25d71a12'), + transaction=tx) + self.transfers.append(pm) + tx = Transaction( + timestamp=datetime(2018, 1, 29, 13, 17, 18), + height=1087530, + hash='e9a71c01875bec20812f71d155bfabf42024fde3ec82475562b817dcc8cbf8dc', + fee=Decimal('0.000962550000')) + pm = IncomingPayment( + amount=Decimal('2.120000000000'), + local_address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'), + payment_id=PaymentID('cb248105ea6a9189'), + transaction=tx) + self.transfers.append(pm) + tx = Transaction( + timestamp=datetime(2018, 1, 29, 14, 57, 47), + height=1087601, + hash='5ef7ead6a041101ed326568fbb59c128403cba46076c3f353cd110d969dac808', + fee=Decimal('0.000962430000')) + pm = IncomingPayment( + amount=Decimal('7.000000000000'), + local_address=address('BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En'), + payment_id=PaymentID('0000000000000000'), + transaction=tx) + self.transfers.append(pm) + tx = Transaction( + timestamp=datetime(2018, 1, 29, 13, 17, 18), + height=1087530, + hash='cc44568337a186c2e1ccc080b43b4ae9db26a07b7afd7edeed60ce2fc4a6477f', + fee=Decimal('0.000962550000')) + pm = IncomingPayment( + amount=Decimal('10.000000000000'), + local_address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'), + payment_id=PaymentID('0000000000000000'), + transaction=tx) + self.transfers.append(pm) + tx = Transaction( + timestamp=datetime(2018, 1, 29, 21, 13, 28), + height=None, + hash='d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c', + fee=Decimal('0.000961950000')) + pm = IncomingPayment( + amount=Decimal('3.140000000000'), + local_address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'), + payment_id=PaymentID('03f6649304ea4cb2'), + transaction=tx) + self.transfers.append(pm) + + def height(self): + return 1087607 + + def accounts(self): + return [Account(self, 0)] + + def transfers_in(self, account, pmtfilter): + return list(pmtfilter.filter(self.transfers)) + + self.wallet = Wallet(MockBackend()) + + def test_filter_none(self): + pmts = self.wallet.incoming() + self.assertEqual(len(pmts), 7) + + def test_filter_payment_id(self): + pmts = self.wallet.incoming(payment_id='cb248105ea6a9189') + self.assertEqual(len(pmts), 1) + self.assertEqual( + pmts[0].transaction.hash, + 'e9a71c01875bec20812f71d155bfabf42024fde3ec82475562b817dcc8cbf8dc') + pmts = self.wallet.incoming(payment_id='f75ad90e25d71a12') + self.assertEqual(len(pmts), 3) + pmts = self.wallet.incoming(payment_id=('cb248105ea6a9189', 'f75ad90e25d71a12')) + self.assertEqual(len(pmts), 4) + self.assertEqual( + pmts, + self.wallet.incoming(payment_id=(PaymentID('cb248105ea6a9189'), 'f75ad90e25d71a12'))) + + def test_filter_address(self): + pmts = self.wallet.incoming( + local_address='BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En') + self.assertEqual(len(pmts), 2) + self.assertEqual( + pmts, + self.wallet.incoming( + local_address=address('BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En'))) + pmts = self.wallet.incoming( + local_address=( + 'BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En', + 'Bf6ngv7q2TBWup13nEm9AjZ36gLE6i4QCaZ7XScZUKDUeGbYEHmPRdegKGwLT8tBBK7P6L32RELNzCR6QzNFkmogDjvypyV')) + self.assertEqual(len(pmts), 3) + + def test_filter_mempool(self): + pmts = self.wallet.incoming() + self.assertEqual(len(pmts), 7) + for p in pmts: + self.assertGreater(self.wallet.confirmations(p.transaction), 0) + pmts = self.wallet.incoming(unconfirmed=True) + self.assertEqual(len(pmts), 8) + pmts = self.wallet.incoming(unconfirmed=True, confirmed=False) + self.assertEqual(len(pmts), 1) + self.assertEqual( + pmts[0].transaction.hash, + 'd29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c') + self.assertEqual(self.wallet.confirmations(pmts[0]), 0) + self.assertEqual(self.wallet.confirmations(pmts[0].transaction), 0) + pmts = self.wallet.incoming(unconfirmed=True, confirmed=False, min_height=1) + self.assertEqual(len(pmts), 0) + pmts = self.wallet.incoming(unconfirmed=True, confirmed=False, max_height=99999999999999) + self.assertEqual(len(pmts), 0) + pmts = self.wallet.incoming(payment_id='03f6649304ea4cb2') + self.assertEqual(len(pmts), 0) + pmts = self.wallet.incoming(unconfirmed=True, payment_id='03f6649304ea4cb2') + self.assertEqual(len(pmts), 1) + pmts = self.wallet.incoming( + local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC') + self.assertEqual(len(pmts), 4) + pmts = self.wallet.incoming( + unconfirmed=True, + local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC') + self.assertEqual(len(pmts), 5) + pmts = self.wallet.incoming( + local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC', + payment_id='03f6649304ea4cb2') + self.assertEqual(len(pmts), 0) + pmts = self.wallet.incoming( + unconfirmed=True, + local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC', + payment_id='03f6649304ea4cb2') + self.assertEqual(len(pmts), 1)