Update checks and tests

This commit is contained in:
Michał Sałaban 2018-01-30 09:43:08 +01:00
parent a814197057
commit ed383d6040
12 changed files with 287 additions and 228 deletions

View File

@ -23,12 +23,12 @@ class Address(object):
_valid_netbytes = (18, 53) _valid_netbytes = (18, 53)
# NOTE: _valid_netbytes order is (real, testnet) # NOTE: _valid_netbytes order is (real, testnet)
def __init__(self, address, label=None): def __init__(self, addr, label=None):
address = str(address) addr = str(addr)
if not _ADDR_REGEX.match(address): if not _ADDR_REGEX.match(addr):
raise ValueError("Address must be 95 characters long base58-encoded string, " raise ValueError("Address must be 95 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)))
self._decode(address) self._decode(addr)
self.label = label or self.label self.label = label or self.label
def _decode(self, address): def _decode(self, address):
@ -86,7 +86,7 @@ class Address(object):
return str(self) == str(other) return str(self) == str(other)
if isinstance(other, str): if isinstance(other, str):
return str(self) == other return str(self) == other
return super() return super(Address, self).__eq__(other)
class SubAddress(Address): class SubAddress(Address):
@ -148,12 +148,12 @@ def address(addr, label=None):
return Address(addr, label=label) return Address(addr, label=label)
elif netbyte in SubAddress._valid_netbytes: elif netbyte in SubAddress._valid_netbytes:
return SubAddress(addr, label=label) return SubAddress(addr, label=label)
raise ValueError("Invalid address netbyte {nb}. Allowed values are: {allowed}".format( raise ValueError("Invalid address netbyte {nb:x}. Allowed values are: {allowed}".format(
nb=hexlify(chr(netbyte)), nb=netbyte,
allowed=", ".join(map( allowed=", ".join(map(
lambda b: '%02x' % b, lambda b: '%02x' % b,
sorted(Address._valid_netbytes + SubAddress._valid_netbytes))))) sorted(Address._valid_netbytes + SubAddress._valid_netbytes)))))
elif _IADDR_REGEX.match(addr): elif _IADDR_REGEX.match(addr):
return IntegratedAddress(addr) return IntegratedAddress(addr)
raise ValueError("Address must be either 95 or 106 characters long base58-encoded string, " 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)))

View File

@ -212,27 +212,18 @@ class JSONRPCWallet(object):
pmts.extend(_pmts.get('pool', [])) pmts.extend(_pmts.get('pool', []))
return list(pmtfilter.filter(map(self._outpayment, pmts))) 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): def _paymentdict(self, data):
pid = data.get('payment_id', None) pid = data.get('payment_id', None)
addr = data.get('address', None) laddr = data.get('address', None)
if addr: if laddr:
addr = address(addr) laddr = address(laddr)
return { return {
'txhash': data.get('txid', data.get('tx_hash')),
'payment_id': None if pid is None else PaymentID(pid), 'payment_id': None if pid is None else PaymentID(pid),
'amount': from_atomic(data['amount']), 'amount': from_atomic(data['amount']),
'timestamp': datetime.fromtimestamp(data['timestamp']) if 'timestamp' in data else None, 'timestamp': datetime.fromtimestamp(data['timestamp']) if 'timestamp' in data else None,
'note': data.get('note', None), 'note': data.get('note', None),
'transaction': self._tx(data), 'transaction': self._tx(data),
'address': addr, 'local_address': laddr,
} }
def _inpayment(self, data): def _inpayment(self, data):

View File

@ -59,4 +59,4 @@ class PaymentID(object):
return int(self) == other return int(self) == other
elif isinstance(other, _str_types): elif isinstance(other, _str_types):
return str(self) == other return str(self) == other
return super() return super(PaymentID, self).__eq__(other)

View File

@ -7,14 +7,18 @@ class Payment(object):
amount = None amount = None
timestamp = None timestamp = None
transaction = None transaction = None
address = None local_address = None
note = ''
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.amount = kwargs.get('amount', self.amount) self.amount = kwargs.pop('amount', self.amount)
self.timestamp = kwargs.get('timestamp', self.timestamp) self.timestamp = kwargs.pop('timestamp', self.timestamp)
self.payment_id = kwargs.get('payment_id', self.payment_id) self.payment_id = kwargs.pop('payment_id', self.payment_id)
self.transaction = kwargs.get('transaction', self.transaction) self.transaction = kwargs.pop('transaction', self.transaction)
self.address = kwargs.get('address', self.address) 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): def __repr__(self):
return "{} {:.12f} id={}".format(self.transaction.hash, self.amount, self.payment_id) return "{} {:.12f} id={}".format(self.transaction.hash, self.amount, self.payment_id)
@ -26,12 +30,6 @@ class IncomingPayment(Payment):
class OutgoingPayment(Payment): class OutgoingPayment(Payment):
note = ''
def __init__(self, **kwargs):
super(OutgoingPayment, self).__init__(**kwargs)
self.note = kwargs.get('note', self.note)
def __repr__(self): def __repr__(self):
return "out: {} {:.12f} id={}".format(self.transaction.hash, self.amount, self.payment_id) 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.max_height = filterparams.pop('max_height', None)
self.unconfirmed = filterparams.pop('unconfirmed', False) self.unconfirmed = filterparams.pop('unconfirmed', False)
self.confirmed = filterparams.pop('confirmed', True) 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) _payment_id = filterparams.pop('payment_id', None)
if len(filterparams) > 0: 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: if _local_address is None:
self.addresses = [] self.local_addresses = []
else: else:
if isinstance(_address, _str_types): if isinstance(_local_address, _str_types):
addresses = [_address] local_addresses = [_local_address]
else: else:
try: try:
iter(_address) iter(_local_address)
addresses = _address local_addresses = _local_address
except TypeError: except TypeError:
addresses = [_address] local_addresses = [_local_address]
self.addresses = list(map(address, addresses)) self.local_addresses = list(map(address, local_addresses))
if _payment_id is None: if _payment_id is None:
self.payment_ids = [] self.payment_ids = []
else: else:
@ -129,7 +127,7 @@ class PaymentFilter(object):
return False return False
if self.payment_ids and payment.payment_id not in self.payment_ids: if self.payment_ids and payment.payment_id not in self.payment_ids:
return False 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 False
return True return True

View File

@ -1,7 +1,7 @@
from . import address from . import address
from . import prio from . import prio
from . import account from . import account
from .transaction import PaymentManager from .transaction import Payment, PaymentManager
class Wallet(object): class Wallet(object):
accounts = None accounts = None
@ -54,14 +54,15 @@ class Wallet(object):
self.accounts.append(acc) self.accounts.append(acc)
return acc return acc
def get_transaction(self, hash): def confirmations(self, txn_or_pmt):
return self._backend.get_transaction(hash) if isinstance(txn_or_pmt, Payment):
txn = txn_or_pmt.transaction
def confirmations(self, txn): else:
txn = self._backend.get_transaction(txn) txn = txn_or_pmt
if txn.height is None: try:
return max(0, self.height() - txn.height)
except TypeError:
return 0 return 0
return max(0, self.height() - txn.height)
# Following methods operate on default account (index=0) # Following methods operate on default account (index=0)
def balances(self): def balances(self):
@ -79,15 +80,6 @@ class Wallet(object):
def new_address(self, label=None): def new_address(self, label=None):
return self.accounts[0].new_address(label=label) 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, def transfer(self, address, amount,
priority=prio.NORMAL, ringsize=5, payment_id=None, unlock_time=0, priority=prio.NORMAL, ringsize=5, payment_id=None, unlock_time=0,
relay=True): relay=True):

View File

@ -24,4 +24,5 @@ setup(
'Programming Language :: Python', 'Programming Language :: Python',
], ],
keywords = 'monero cryptocurrency', keywords = 'monero cryptocurrency',
test_suite='tests',
) )

View File

@ -1,3 +1,5 @@
from .address import * from . import test_address
from .numbers import * from . import test_numbers
from .wallet import * from . import test_transaction
from . import test_wallet
from . import test_jsonrpcwallet

View File

@ -58,6 +58,8 @@ class Tests(object):
self.assertEqual(sa.is_testnet(), self.testnet) self.assertEqual(sa.is_testnet(), self.testnet)
self.assertEqual(sa2.is_testnet(), self.testnet) self.assertEqual(sa2.is_testnet(), self.testnet)
self.assertNotEqual(a, 0)
def test_idempotence(self): def test_idempotence(self):
a = Address(self.addr) a = Address(self.addr)
a_idem = Address(a) a_idem = Address(a)
@ -83,6 +85,21 @@ class Tests(object):
self.assertRaises(TypeError, a.with_payment_id, "%x" % (2**64+1)) self.assertRaises(TypeError, a.with_payment_id, "%x" % (2**64+1))
s = SubAddress(self.subaddr) s = SubAddress(self.subaddr)
self.assertRaises(TypeError, s.with_payment_id, 0) 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): def test_type_mismatch(self):
self.assertRaises(ValueError, Address, self.iaddr) self.assertRaises(ValueError, Address, self.iaddr)

View File

@ -13,163 +13,6 @@ from monero.numbers import PaymentID
from monero.transaction import IncomingPayment, OutgoingPayment, Transaction from monero.transaction import IncomingPayment, OutgoingPayment, Transaction
from monero.backends.jsonrpc import JSONRPCWallet 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): class SubaddrWalletTestCase(unittest.TestCase):
accounts_result = {'id': 0, accounts_result = {'id': 0,
'jsonrpc': '2.0', 'jsonrpc': '2.0',
@ -279,7 +122,7 @@ class SubaddrWalletTestCase(unittest.TestCase):
self.assertEqual(len(self.wallet.accounts[0].addresses()), 8) self.assertEqual(len(self.wallet.accounts[0].addresses()), 8)
@patch('monero.backends.jsonrpc.requests.post') @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.status_code = 200
mock_post.return_value.json.return_value = self.accounts_result mock_post.return_value.json.return_value = self.accounts_result
self.wallet = Wallet(JSONRPCWallet()) self.wallet = Wallet(JSONRPCWallet())
@ -326,14 +169,14 @@ class SubaddrWalletTestCase(unittest.TestCase):
self.assertEqual(len(list(pay_in)), 3) self.assertEqual(len(list(pay_in)), 3)
for pmt in pay_in: for pmt in pay_in:
self.assertIsInstance(pmt, IncomingPayment) self.assertIsInstance(pmt, IncomingPayment)
self.assertIsInstance(pmt.address, Address) self.assertIsInstance(pmt.local_address, Address)
self.assertIsInstance(pmt.amount, Decimal) self.assertIsInstance(pmt.amount, Decimal)
self.assertIsInstance(pmt.transaction, Transaction) self.assertIsInstance(pmt.transaction, Transaction)
self.assertIsInstance(pmt.transaction.fee, Decimal) self.assertIsInstance(pmt.transaction.fee, Decimal)
self.assertIsInstance(pmt.transaction.height, int) self.assertIsInstance(pmt.transaction.height, int)
@patch('monero.backends.jsonrpc.requests.post') @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.status_code = 200
mock_post.return_value.json.return_value = self.accounts_result mock_post.return_value.json.return_value = self.accounts_result
self.wallet = Wallet(JSONRPCWallet()) self.wallet = Wallet(JSONRPCWallet())
@ -428,7 +271,7 @@ class SubaddrWalletTestCase(unittest.TestCase):
self.assertEqual(len(list(pay_out)), 6) self.assertEqual(len(list(pay_out)), 6)
for pmt in pay_out: for pmt in pay_out:
self.assertIsInstance(pmt, OutgoingPayment) self.assertIsInstance(pmt, OutgoingPayment)
self.assertIsInstance(pmt.address, Address) self.assertIsInstance(pmt.local_address, Address)
self.assertIsInstance(pmt.amount, Decimal) self.assertIsInstance(pmt.amount, Decimal)
self.assertIsInstance(pmt.timestamp, datetime) self.assertIsInstance(pmt.timestamp, datetime)
self.assertIsInstance(pmt.transaction, Transaction) self.assertIsInstance(pmt.transaction, Transaction)

View File

@ -1,7 +1,7 @@
from decimal import Decimal from decimal import Decimal
import unittest 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): class NumbersTestCase(unittest.TestCase):
def test_simple_numbers(self): def test_simple_numbers(self):
@ -14,6 +14,7 @@ class NumbersTestCase(unittest.TestCase):
def test_rounding(self): def test_rounding(self):
self.assertEqual(to_atomic(Decimal('1.0000000000004')), 1000000000000) self.assertEqual(to_atomic(Decimal('1.0000000000004')), 1000000000000)
self.assertEqual(as_monero(Decimal('1.0000000000014')), Decimal('1.000000000001'))
def test_payment_id(self): def test_payment_id(self):
pid = PaymentID('0') pid = PaymentID('0')
@ -21,6 +22,7 @@ class NumbersTestCase(unittest.TestCase):
self.assertEqual(pid, 0) self.assertEqual(pid, 0)
self.assertEqual(pid, '0000000000000000') self.assertEqual(pid, '0000000000000000')
self.assertEqual(PaymentID(pid), pid) self.assertEqual(PaymentID(pid), pid)
self.assertNotEqual(pid, None)
pid = PaymentID('abcdef') pid = PaymentID('abcdef')
self.assertTrue(pid.is_short()) self.assertTrue(pid.is_short())
self.assertEqual(pid, 0xabcdef) self.assertEqual(pid, 0xabcdef)

28
tests/test_transaction.py Normal file
View File

@ -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))

185
tests/test_wallet.py Normal file
View File

@ -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)