Merge branch 'master' into 0.5.x

This commit is contained in:
Michał Sałaban 2019-01-02 23:16:03 +00:00
commit 6c1f667840
10 changed files with 136 additions and 74 deletions

View File

@ -14,7 +14,7 @@ Python Monero module
A comprehensive Python module for handling Monero cryptocurrency. A comprehensive Python module for handling Monero cryptocurrency.
* release 0.4.3 * release 0.4.4
* open source: https://github.com/emesik/monero-python * open source: https://github.com/emesik/monero-python
* works with Monero 0.12.x and `the latest source`_ (at least we try to keep up) * works with Monero 0.12.x and `the latest source`_ (at least we try to keep up)
* Python 2.x and 3.x compatible * Python 2.x and 3.x compatible

View File

@ -55,9 +55,9 @@ author = 'Michal Salaban'
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.4.3' version = '0.4.4'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.4.3' release = '0.4.4'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
@ -102,9 +102,13 @@ html_static_path = ['_static']
# This is required for the alabaster theme # This is required for the alabaster theme
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
html_sidebars = { html_sidebars = {
'**': [ 'index': [
'relations.html', # needs 'show_related': True theme option to display 'relations.html', # needs 'show_related': True theme option to display
'searchbox.html'
],
'**': [
'searchbox.html', 'searchbox.html',
'globaltoc.html'
] ]
} }
@ -164,6 +168,3 @@ texinfo_documents = [
author, 'MoneroPythonmodule', 'One line description of project.', author, 'MoneroPythonmodule', 'One line description of project.',
'Miscellaneous'), 'Miscellaneous'),
] ]

View File

@ -177,6 +177,10 @@ and ``unconfirmed`` query parameters that accept boolean values:
You may as well query for both confirmed and unconfirmed transactions using You may as well query for both confirmed and unconfirmed transactions using
``wallet.incoming(unconfirmed=True)`` (the default value for ``confirmed`` is ``True``). ``wallet.incoming(unconfirmed=True)`` (the default value for ``confirmed`` is ``True``).
.. note:: Mempool transactions don't belong to the blockchain (yet), therefore they have no height.
Setting ``min_height`` or ``max_height`` arguments will **always exclude mempool
transactions**. If ``unconfirmed`` is also set to ``True``, a warning will be issued.
.. _sending-payments: .. _sending-payments:
Sending payments Sending payments

View File

@ -1,3 +1,3 @@
from . import address, account, daemon, wallet, numbers, prio, wordlists, seed from . import address, account, daemon, wallet, numbers, prio, wordlists, seed
__version__ = '0.4.3' __version__ = '0.4.4'

View File

@ -4,6 +4,7 @@ import struct
from sha3 import keccak_256 from sha3 import keccak_256
from . import base58 from . import base58
from . import ed25519
from . import numbers from . import numbers
_ADDR_REGEX = re.compile(r'^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{95}$') _ADDR_REGEX = re.compile(r'^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{95}$')
@ -90,6 +91,20 @@ class Address(BaseAddress):
""" """
return hexlify(self._decoded[1:33]).decode() return hexlify(self._decoded[1:33]).decode()
def check_private_view_key(self, key):
"""Checks if private view key matches this address.
:rtype: bool
"""
return ed25519.public_from_secret_hex(key) == self.view_key()
def check_private_spend_key(self, key):
"""Checks if private spend key matches this address.
:rtype: bool
"""
return ed25519.public_from_secret_hex(key) == self.spend_key()
def with_payment_id(self, payment_id=0): def with_payment_id(self, payment_id=0):
"""Integrates payment id into the address. """Integrates payment id into the address.

View File

@ -3,6 +3,7 @@
# #
# Parts Copyright (c) 2016 The MoneroPy Developers. Released under the BSD 3-Clause # Parts Copyright (c) 2016 The MoneroPy Developers. Released under the BSD 3-Clause
from binascii import hexlify, unhexlify
import hashlib import hashlib
import operator as _oper import operator as _oper
import sys as _sys import sys as _sys
@ -133,3 +134,10 @@ def scalarmultbase(e):
if e & 1: Q = edwards(Q, B) if e & 1: Q = edwards(Q, B)
return Q return Q
def public_from_secret(k):
keyInt = decodeint(k)
aG = scalarmultbase(keyInt)
return encodepoint(aG)
def public_from_secret_hex(hk):
return hexlify(public_from_secret(unhexlify(hk))).decode()

View File

@ -137,14 +137,10 @@ class Seed(object):
return self.sc_reduce(h.digest()) return self.sc_reduce(h.digest())
def public_spend_key(self): def public_spend_key(self):
keyInt = ed25519.decodeint(unhexlify(self.secret_spend_key())) return ed25519.public_from_secret_hex(self.secret_spend_key())
aG = ed25519.scalarmultbase(keyInt)
return hexlify(ed25519.encodepoint(aG)).decode()
def public_view_key(self): def public_view_key(self):
keyInt = ed25519.decodeint(unhexlify(self.secret_view_key())) return ed25519.public_from_secret_hex(self.secret_view_key())
aG = ed25519.scalarmultbase(keyInt)
return hexlify(ed25519.encodepoint(aG)).decode()
def public_address(self, net='mainnet'): def public_address(self, net='mainnet'):
"""Returns the master :class:`Address <monero.address.Address>` represented by the seed. """Returns the master :class:`Address <monero.address.Address>` represented by the seed.

View File

@ -1,4 +1,5 @@
import sys import sys
import warnings
from .address import address from .address import address
from .numbers import PaymentID from .numbers import PaymentID
@ -160,7 +161,12 @@ class PaymentFilter(object):
_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: {}".format(filterparams)) raise ValueError("Excessive arguments for payment query: {}".format(filterparams))
if self.unconfirmed and (self.min_height is not None or self.max_height is not None):
warnings.warn("Height filtering (min_height/max_height) has been requested while "
"also asking for unconfirmed transactions. These are mutually exclusive. "
"As mempool transactions have no height at all, they will be excluded "
"from the result.",
RuntimeWarning)
if _local_address is None: if _local_address is None:
self.local_addresses = [] self.local_addresses = []
else: else:

View File

@ -78,6 +78,22 @@ class Tests(object):
self.assertNotEqual(a, 0) self.assertNotEqual(a, 0)
def test_check_private_view_key(self):
a = Address(self.addr)
self.assertFalse(a.check_private_view_key(self.ssk))
self.assertTrue(a.check_private_view_key(self.svk))
self.assertFalse(a.check_private_view_key(self.psk))
self.assertFalse(a.check_private_view_key(self.pvk))
self.assertFalse(a.check_private_view_key('0000000000000000000000000000000000000000000000000000000000000000'))
def test_check_private_spend_key(self):
a = Address(self.addr)
self.assertTrue(a.check_private_spend_key(self.ssk))
self.assertFalse(a.check_private_spend_key(self.svk))
self.assertFalse(a.check_private_spend_key(self.psk))
self.assertFalse(a.check_private_spend_key(self.pvk))
self.assertFalse(a.check_private_spend_key('0000000000000000000000000000000000000000000000000000000000000000'))
def test_idempotence(self): def test_idempotence(self):
a = Address(self.addr) a = Address(self.addr)
a_idem = Address(a) a_idem = Address(a)
@ -130,46 +146,53 @@ class Tests(object):
sa = SubAddress(self.subaddr) sa = SubAddress(self.subaddr)
self.assertRaises(TypeError, sa.with_payment_id, self.pid) self.assertRaises(TypeError, sa.with_payment_id, self.pid)
class AddressTestCase(unittest.TestCase, Tests): class AddressTestCase(unittest.TestCase, Tests):
addr = '43aeKax1ts4BoEbSyzKVbbDRmc8nsnpZLUpQBYvhUxs3KVrodnaFaBEQMDp69u4VaiEG3LSQXA6M61mXPrztCLuh7PFUAmd' addr = '47ewoP19TN7JEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef'
psk = '33a7ceb933b793408d49e82c0a34664a4be7117243cb77a64ef280b866d8aa6e' ssk = 'e0fe01d5794e240a26609250c0d7e01673219eececa3f499d5cfa20a75739b0a'
pvk = '96f70d63d9d3558b97a5dd200a170b4f45b3177a274aa90496ea683896ff6438' svk = '6d9056aa2c096bfcd2f272759555e5764ba204dd362604a983fa3e0aafd35901'
psk = '9f2a76d879aaf0670039dc8dbdca01f0ca26a2f6d93268e3674666bfdc5957e4'
pvk = '716cfc7da7e6ce366935c55747839a85be798037ab189c7dd0f10b7f1690cb78'
pid = '4a6f686e47616c74' pid = '4a6f686e47616c74'
subaddr = '83bK2pMxCQXdRyd6W1haNWYRsF6Qb3iGa8gxKEynm9U7cYoXrMHFwRrFFuxRSgnLtGe7LM8SmrPY6L3TVBa3UV3YLuVJ7Rw' iaddr = '4HMcpBpe4ddJEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8M7yKhzQhKW3ECCLWQw'
iaddr = '4DHKLPmWW8aBoEbSyzKVbbDRmc8nsnpZLUpQBYvhUxs3KVrodnaFaBEQMDp69u4VaiEG3LSQXA6M61mXPrztCLuhAR6GpL18QNwE8h3TuF' subaddr = '84LooD7i35SFppgf4tQ453Vi3q5WexSUXaVgut69ro8MFnmHwuezAArEZTZyLr9fS6QotjqkSAxSF6d1aDgsPoX849izJ7m'
mainnet = True mainnet = True
testnet = False testnet = False
stagenet = False stagenet = False
addr_invalid = '43aeKax1ts4boEbSyzKVbbDRmc8nsnpZLUpQBYvhUxs3KVrodnaFaBEQMDp69u4VaiEG3LSQXA6M61mXPrztCLuh7PFUAmd' addr_invalid = '47ewoP19TN7JCEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef'
iaddr_invalid = '4DHKLpmWW8aBoEbSyzKVbbDRmc8nsnpZLUpQBYvhUxs3KVrodnaFaBEQMDp69u4VaiEG3LSQXA6M61mXPrztCLuhAR6GpL18QNwE8h3TuF' iaddr_invalid = '4HMcpBpe4ddJEEnFKUJHAYhGxkyTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8M7yKhzQhKW3ECCLWQw'
class TestnetAddressTestCase(AddressTestCase, Tests): class TestnetAddressTestCase(AddressTestCase, Tests):
addr = '9vgV48wWAPTWik5QSUSoGYicdvvsbSNHrT9Arsx1XBTz6VrWPSgfmnUKSPZDMyX4Ms8R9TkhB4uFqK9s5LUBbV6YQN2Q9ag' addr = '9wuKTHsxGiwEsMp2fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwP7hMs5N'
psk = '5cbcfbcea7cc62b1aeb76758ad8df5f8cbe0c63d40c8cd9c49377bbc9c9b9520' ssk = '4f5b7af2c1942067ba33d34318b9735cb46ab5d50b75294844c82a9dd872c201'
pvk = 'de048ca310ff7d6e3b6714bccdebd62c56d680a10272846c875241fa2c5fc1cf' svk = '60cf228f2bf7f6a70643afe9468fde254145dbd3aab4072ede14bf8bae914103'
psk = '7cf743dcfd23d452e9b2936caeb622c9849f1ff1ddfd62bfdfac64113c1a4e92'
pvk = 'e3924b14d99a9c088e5a45278d5218f2d053b1c03c480f00ed2ee3dce80806c4'
pid = '4a6f686e47616c74' pid = '4a6f686e47616c74'
iaddr = 'A6PA4wkzmeyWik5QSUSoGYicdvvsbSNHrT9Arsx1XBTz6VrWPSgfmnUKSPZDMyX4Ms8R9TkhB4uFqK9s5LUBbV6YbfyqvDecDn3E7cvp9b' subaddr = 'BaU3yLuDqdcETYzeF7vFSVEKNR4sSGxBV1Evrw5yNBf2VMiuAwfDmiF3RHqLHkaA5A6RGiNNRUqvtaqhMtdjA1SQ1tnQV8D'
subaddr = 'BbBjyYoYNNwFfL8RRVRTMiZUofBLpjRxdNnd5E4LyGcAK5CEsnL3gmE5QkrDRta7RPficGHcFdR6rUwWcjnwZVvCE3tLxhJ' iaddr = 'A7bzU6hSszTEsMp2fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwZqGSmLeBXqMEBnZVkh'
mainnet = False mainnet = False
testnet = True testnet = True
stagenet = False stagenet = False
addr_invalid = '9vgV48wWAPTWik5QSUSoGYicdvvsbSNHrT9Arsx1XBTz6VrWPSgfmnUKSPZDMyX4Ms8R9TkhB4uFqK9s5LUbbV6YQN2Q9ag' addr_invalid = '9wuKTHsxGiwEsMp3fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwP7hMs5N'
iaddr_invalid = 'A6PA4wkzmeyWik5qSUSoGYicdvvsbSNHrT9Arsx1XBTz6VrWPSgfmnUKSPZDMyX4Ms8R9TkhB4uFqK9s5LUBbV6YbfyqvDecDn3E7cvp9b' iaddr_invalid = 'A7bzU6hSszTEsMp2fYzJiVahyhU2aZi2oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwZqGSmLeBXqMEBnZVkh'
class StagenetAddressTestCase(AddressTestCase, Tests): class StagenetAddressTestCase(AddressTestCase, Tests):
addr = '56cXYWG13YKaT9z1aEy2hb9TZNnxrW3zE9S4nTQVDux5Qq7UYsmjuux3Zstxkorj9HAufyWLU3FwHW4uERQF6tkeUVogGN3' addr = '52jzuBBUMty3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8LdAqbp6'
psk = '7e33891fe6ea30c7fd79d48e250906329104dc77407cf732699f41564df8ca8e' ssk = 'a8733c61797115db4ec8a5ce39fb811f81dd4ec163b880526683e059c7e62503'
pvk = '77a3720428f91f0f58a196bb374f703b3ca45fa55f0764adc81ff241c4c797f3' svk = 'fd5c0d25f8f994268079a4f7844274dc870a7c2b88fbfc24ba318375e1d9430f'
psk = '180c1d7bbf7f2e11aa90d0f61bf49024370e01cd54f33f2d36bba0357c9c205f'
pvk = '94b66a81e646927b3da74392306f789c5024734b4ce6351ad74c4c7d7351b3ad'
pid = '4a6f686e47616c74' pid = '4a6f686e47616c74'
iaddr = '5GKCZK5VeoqaT9z1aEy2hb9TZNnxrW3zE9S4nTQVDux5Qq7UYsmjuux3Zstxkorj9HAufyWLU3FwHW4uERQF6tkehhE4RH8N7QfEAC8jMy' subaddr = '7AeQwvrLtPeYoXVPRkEu8oEL7N9wnqHjYKwSvTf6YKbHgYmw6AJMsjggzVLo21egMK9qcoV1mxCTfP4FbaGb7JEMDfpLetk'
subaddr = '7417qYoKBoYXCugU2KvJBZExmyjav4n1MVME74AeWNwxQ39wKtbWdyP6YGuMK6C7HkAjBuVcbUYmCWbxDLwk9GAX4qyb48U' iaddr = '5CSfuyzxyAV3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8Vz8ySmoqYgTE8dR1yS'
mainnet = False mainnet = False
testnet = False testnet = False
stagenet = True stagenet = True
addr_invalid ='7417qYoKBoYXCugU2KvJBZExmyjav4n1MVME74AeWNwxQ39wKtbWdyP6YGuMK6C7HkAjBuVcbUYmCWbyDLwk9GAX4qyb48U' addr_invalid = '52jzuBBUMty3xPL3JsQxGP74LDuV6H1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8LdAqbp6'
iaddr_invalid = '5GKCZK5VeuqaT9z1aEy2hb9TZNnxrW3zE9S4nTQVDux5Qq7UYsmjuux3Zstxkorj9HAufyWLU3FwHW4uERQF6tkehhE4RH8N7QfEAC8jMy' iaddr_invalid = '5CSfuyzxyAV3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKppEgC5VPhfoAiVj8Vz8ySmoqYgTE8dR1yS'
class KnownBugsTest(unittest.TestCase): class KnownBugsTest(unittest.TestCase):

View File

@ -1,12 +1,13 @@
from datetime import datetime from datetime import datetime
from decimal import Decimal from decimal import Decimal
import unittest import unittest
import warnings
from monero.wallet import Wallet from monero.wallet import Wallet
from monero.account import Account from monero.account import Account
from monero.address import address from monero.address import address
from monero.numbers import PaymentID from monero.numbers import PaymentID
from monero.transaction import IncomingPayment, OutgoingPayment, Transaction from monero.transaction import IncomingPayment, Transaction
class FiltersTestCase(unittest.TestCase): class FiltersTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
@ -146,43 +147,51 @@ class FiltersTestCase(unittest.TestCase):
self.assertEqual(len(pmts), 3) self.assertEqual(len(pmts), 3)
def test_filter_mempool(self): def test_filter_mempool(self):
pmts = self.wallet.incoming() with warnings.catch_warnings(record=True) as w:
self.assertEqual(len(pmts), 7) warnings.simplefilter('always')
for p in pmts: pmts = self.wallet.incoming()
self.assertGreater(self.wallet.confirmations(p.transaction), 0) self.assertEqual(len(pmts), 7)
pmts = self.wallet.incoming(unconfirmed=True) for p in pmts:
self.assertEqual(len(pmts), 8) self.assertGreater(self.wallet.confirmations(p.transaction), 0)
pmts = self.wallet.incoming(unconfirmed=True, confirmed=False) pmts = self.wallet.incoming(unconfirmed=True)
self.assertEqual(len(pmts), 1) self.assertEqual(len(pmts), 8)
self.assertEqual( pmts = self.wallet.incoming(unconfirmed=True, confirmed=False)
pmts[0].transaction.hash, self.assertEqual(len(pmts), 1)
'd29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c') self.assertEqual(
self.assertEqual(self.wallet.confirmations(pmts[0]), 0) pmts[0].transaction.hash,
self.assertEqual(self.wallet.confirmations(pmts[0].transaction), 0) 'd29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c')
pmts = self.wallet.incoming(unconfirmed=True, confirmed=False, min_height=1) self.assertEqual(self.wallet.confirmations(pmts[0]), 0)
self.assertEqual(len(pmts), 0) self.assertEqual(self.wallet.confirmations(pmts[0].transaction), 0)
pmts = self.wallet.incoming(unconfirmed=True, confirmed=False, max_height=99999999999999) self.assertEqual(len(w), 0)
self.assertEqual(len(pmts), 0) pmts = self.wallet.incoming(unconfirmed=True, confirmed=False, min_height=1)
pmts = self.wallet.incoming(payment_id='03f6649304ea4cb2') self.assertEqual(len(pmts), 0)
self.assertEqual(len(pmts), 0) self.assertEqual(len(w), 1)
pmts = self.wallet.incoming(unconfirmed=True, payment_id='03f6649304ea4cb2') self.assertIs(w[0].category, RuntimeWarning)
self.assertEqual(len(pmts), 1) pmts = self.wallet.incoming(unconfirmed=True, confirmed=False, max_height=99999999999999)
pmts = self.wallet.incoming( self.assertEqual(len(pmts), 0)
local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC') self.assertEqual(len(w), 2)
self.assertEqual(len(pmts), 4) self.assertIs(w[1].category, RuntimeWarning)
pmts = self.wallet.incoming( pmts = self.wallet.incoming(payment_id='03f6649304ea4cb2')
unconfirmed=True, self.assertEqual(len(pmts), 0)
local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC') pmts = self.wallet.incoming(unconfirmed=True, payment_id='03f6649304ea4cb2')
self.assertEqual(len(pmts), 5) self.assertEqual(len(pmts), 1)
pmts = self.wallet.incoming( pmts = self.wallet.incoming(
local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC', local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC')
payment_id='03f6649304ea4cb2') self.assertEqual(len(pmts), 4)
self.assertEqual(len(pmts), 0) pmts = self.wallet.incoming(
pmts = self.wallet.incoming( unconfirmed=True,
unconfirmed=True, local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC')
local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC', self.assertEqual(len(pmts), 5)
payment_id='03f6649304ea4cb2') pmts = self.wallet.incoming(
self.assertEqual(len(pmts), 1) 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)
self.assertEqual(len(w), 2)
def test_filter_excessive(self): def test_filter_excessive(self):
self.assertRaises(ValueError, self.wallet.incoming, excessive_argument='foo') self.assertRaises(ValueError, self.wallet.incoming, excessive_argument='foo')