Browse Source

tests: clean-up use of asyncio

master
SomberNight 3 years ago
parent
commit
d4338fb503
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 21
      electrum/tests/__init__.py
  2. 53
      electrum/tests/test_bitcoin.py
  3. 82
      electrum/tests/test_commands.py
  4. 14
      electrum/tests/test_invoices.py
  5. 183
      electrum/tests/test_lnpeer.py
  6. 16
      electrum/tests/test_lnrouter.py
  7. 9
      electrum/tests/test_lntransport.py
  8. 28
      electrum/tests/test_network.py
  9. 264
      electrum/tests/test_storage_upgrade.py
  10. 30
      electrum/tests/test_wallet.py
  11. 172
      electrum/tests/test_wallet_vertical.py
  12. 21
      electrum/util.py
  13. 10
      electrum/wallet.py

21
electrum/tests/__init__.py

@ -1,3 +1,4 @@
import asyncio
import unittest import unittest
import threading import threading
import tempfile import tempfile
@ -18,8 +19,10 @@ FAST_TESTS = False
electrum.logging._configure_stderr_logging() electrum.logging._configure_stderr_logging()
electrum.util.AS_LIB_USER_I_WANT_TO_MANAGE_MY_OWN_ASYNCIO_LOOP = True
class ElectrumTestCase(unittest.TestCase):
class ElectrumTestCase(unittest.IsolatedAsyncioTestCase):
"""Base class for our unit tests.""" """Base class for our unit tests."""
TESTNET = False TESTNET = False
@ -43,12 +46,9 @@ class ElectrumTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self._test_lock.acquire() self._test_lock.acquire()
super().setUp() super().setUp()
self.asyncio_loop, self._stop_loop, self._loop_thread = util.create_and_start_event_loop()
self.electrum_path = tempfile.mkdtemp() self.electrum_path = tempfile.mkdtemp()
def tearDown(self): def tearDown(self):
self.asyncio_loop.call_soon_threadsafe(self._stop_loop.set_result, 1)
self._loop_thread.join(timeout=1)
shutil.rmtree(self.electrum_path) shutil.rmtree(self.electrum_path)
super().tearDown() super().tearDown()
self._test_lock.release() self._test_lock.release()
@ -59,12 +59,19 @@ def as_testnet(func):
NOTE: this is inherently sequential; tests running in parallel would break things NOTE: this is inherently sequential; tests running in parallel would break things
""" """
def run_test(*args, **kwargs):
old_net = constants.net old_net = constants.net
if asyncio.iscoroutinefunction(func):
async def run_test(*args, **kwargs):
try:
constants.set_testnet()
return await func(*args, **kwargs)
finally:
constants.net = old_net
else:
def run_test(*args, **kwargs):
try: try:
constants.set_testnet() constants.set_testnet()
func(*args, **kwargs) return func(*args, **kwargs)
finally: finally:
constants.net = old_net constants.net = old_net
return run_test return run_test

53
electrum/tests/test_bitcoin.py

@ -1,3 +1,4 @@
import asyncio
import base64 import base64
import sys import sys
@ -35,13 +36,29 @@ def needs_test_with_all_aes_implementations(func):
NOTE: this is inherently sequential; NOTE: this is inherently sequential;
tests running in parallel would break things tests running in parallel would break things
""" """
def run_test(*args, **kwargs):
if FAST_TESTS: # if set, only run tests once, using fastest implementation if FAST_TESTS: # if set, only run tests once, using fastest implementation
func(*args, **kwargs) return func
return
has_cryptodome = crypto.HAS_CRYPTODOME has_cryptodome = crypto.HAS_CRYPTODOME
has_cryptography = crypto.HAS_CRYPTOGRAPHY has_cryptography = crypto.HAS_CRYPTOGRAPHY
has_pyaes = crypto.HAS_PYAES has_pyaes = crypto.HAS_PYAES
if asyncio.iscoroutinefunction(func):
async def run_test(*args, **kwargs):
try:
if has_pyaes:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = False, False, True
await func(*args, **kwargs) # pyaes
if has_cryptodome:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = True, False, False
await func(*args, **kwargs) # cryptodome
if has_cryptography:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = False, True, False
await func(*args, **kwargs) # cryptography
finally:
crypto.HAS_CRYPTODOME = has_cryptodome
crypto.HAS_CRYPTOGRAPHY = has_cryptography
crypto.HAS_PYAES = has_pyaes
else:
def run_test(*args, **kwargs):
try: try:
if has_pyaes: if has_pyaes:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = False, False, True (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = False, False, True
@ -66,12 +83,24 @@ def needs_test_with_all_chacha20_implementations(func):
NOTE: this is inherently sequential; NOTE: this is inherently sequential;
tests running in parallel would break things tests running in parallel would break things
""" """
def run_test(*args, **kwargs):
if FAST_TESTS: # if set, only run tests once, using fastest implementation if FAST_TESTS: # if set, only run tests once, using fastest implementation
func(*args, **kwargs) return func
return
has_cryptodome = crypto.HAS_CRYPTODOME has_cryptodome = crypto.HAS_CRYPTODOME
has_cryptography = crypto.HAS_CRYPTOGRAPHY has_cryptography = crypto.HAS_CRYPTOGRAPHY
if asyncio.iscoroutinefunction(func):
async def run_test(*args, **kwargs):
try:
if has_cryptodome:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = True, False
await func(*args, **kwargs) # cryptodome
if has_cryptography:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = False, True
await func(*args, **kwargs) # cryptography
finally:
crypto.HAS_CRYPTODOME = has_cryptodome
crypto.HAS_CRYPTOGRAPHY = has_cryptography
else:
def run_test(*args, **kwargs):
try: try:
if has_cryptodome: if has_cryptodome:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = True, False (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = True, False
@ -93,11 +122,19 @@ def disable_ecdsa_r_value_grinding(func):
NOTE: this is inherently sequential; NOTE: this is inherently sequential;
tests running in parallel would break things tests running in parallel would break things
""" """
def run_test(*args, **kwargs):
is_grinding = ecc.ENABLE_ECDSA_R_VALUE_GRINDING is_grinding = ecc.ENABLE_ECDSA_R_VALUE_GRINDING
if asyncio.iscoroutinefunction(func):
async def run_test(*args, **kwargs):
try:
ecc.ENABLE_ECDSA_R_VALUE_GRINDING = False
return await func(*args, **kwargs)
finally:
ecc.ENABLE_ECDSA_R_VALUE_GRINDING = is_grinding
else:
def run_test(*args, **kwargs):
try: try:
ecc.ENABLE_ECDSA_R_VALUE_GRINDING = False ecc.ENABLE_ECDSA_R_VALUE_GRINDING = False
func(*args, **kwargs) return func(*args, **kwargs)
finally: finally:
ecc.ENABLE_ECDSA_R_VALUE_GRINDING = is_grinding ecc.ENABLE_ECDSA_R_VALUE_GRINDING = is_grinding
return run_test return run_test

82
electrum/tests/test_commands.py

@ -53,7 +53,7 @@ class TestCommands(ElectrumTestCase):
self.assertTrue(eval_bool("true")) self.assertTrue(eval_bool("true"))
self.assertTrue(eval_bool("1")) self.assertTrue(eval_bool("1"))
def test_convert_xkey(self): async def test_convert_xkey(self):
cmds = Commands(config=self.config) cmds = Commands(config=self.config)
xpubs = { xpubs = {
("xpub6CCWFbvCbqF92kGwm9nV7t7RvVoQUKaq5USMdyVP6jvv1NgN52KAX6NNYCeE8Ca7JQC4K5tZcnQrubQcjJ6iixfPs4pwAQJAQgTt6hBjg11", "standard"), ("xpub6CCWFbvCbqF92kGwm9nV7t7RvVoQUKaq5USMdyVP6jvv1NgN52KAX6NNYCeE8Ca7JQC4K5tZcnQrubQcjJ6iixfPs4pwAQJAQgTt6hBjg11", "standard"),
@ -62,7 +62,7 @@ class TestCommands(ElectrumTestCase):
} }
for xkey1, xtype1 in xpubs: for xkey1, xtype1 in xpubs:
for xkey2, xtype2 in xpubs: for xkey2, xtype2 in xpubs:
self.assertEqual(xkey2, cmds._run('convert_xkey', (xkey1, xtype2))) self.assertEqual(xkey2, await cmds.convert_xkey(xkey1, xtype2))
xprvs = { xprvs = {
("xprv9yD9r6PJmTgqpGCUf8FUkkAhNTxv4rryiFWkqb5mYQPw8aMDXUzuyJ3tgv5vUqYkdK1E6Q5jKxPss4HkMBYV4q8AfG8t7rxgyS4xQX4ndAm", "standard"), ("xprv9yD9r6PJmTgqpGCUf8FUkkAhNTxv4rryiFWkqb5mYQPw8aMDXUzuyJ3tgv5vUqYkdK1E6Q5jKxPss4HkMBYV4q8AfG8t7rxgyS4xQX4ndAm", "standard"),
@ -71,40 +71,40 @@ class TestCommands(ElectrumTestCase):
} }
for xkey1, xtype1 in xprvs: for xkey1, xtype1 in xprvs:
for xkey2, xtype2 in xprvs: for xkey2, xtype2 in xprvs:
self.assertEqual(xkey2, cmds._run('convert_xkey', (xkey1, xtype2))) self.assertEqual(xkey2, await cmds.convert_xkey(xkey1, xtype2))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_encrypt_decrypt(self, mock_save_db): async def test_encrypt_decrypt(self, mock_save_db):
wallet = restore_wallet_from_text('p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN', wallet = restore_wallet_from_text('p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN',
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
config=self.config)['wallet'] config=self.config)['wallet']
cmds = Commands(config=self.config) cmds = Commands(config=self.config)
cleartext = "asdasd this is the message" cleartext = "asdasd this is the message"
pubkey = "021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da" pubkey = "021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da"
ciphertext = cmds._run('encrypt', (pubkey, cleartext)) ciphertext = await cmds.encrypt(pubkey, cleartext)
self.assertEqual(cleartext, cmds._run('decrypt', (pubkey, ciphertext), wallet=wallet)) self.assertEqual(cleartext, await cmds.decrypt(pubkey, ciphertext, wallet=wallet))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_export_private_key_imported(self, mock_save_db): async def test_export_private_key_imported(self, mock_save_db):
wallet = restore_wallet_from_text('p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL', wallet = restore_wallet_from_text('p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL',
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
config=self.config)['wallet'] config=self.config)['wallet']
cmds = Commands(config=self.config) cmds = Commands(config=self.config)
# single address tests # single address tests
with self.assertRaises(Exception): with self.assertRaises(Exception):
cmds._run('getprivatekeys', ("asdasd",), wallet=wallet) # invalid addr, though might raise "not in wallet" await cmds.getprivatekeys("asdasd", wallet=wallet) # invalid addr, though might raise "not in wallet"
with self.assertRaises(Exception): with self.assertRaises(Exception):
cmds._run('getprivatekeys', ("bc1qgfam82qk7uwh5j2xxmcd8cmklpe0zackyj6r23",), wallet=wallet) # not in wallet await cmds.getprivatekeys("bc1qgfam82qk7uwh5j2xxmcd8cmklpe0zackyj6r23", wallet=wallet) # not in wallet
self.assertEqual("p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL", self.assertEqual("p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL",
cmds._run('getprivatekeys', ("bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw",), wallet=wallet)) await cmds.getprivatekeys("bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw", wallet=wallet))
# list of addresses tests # list of addresses tests
with self.assertRaises(Exception): with self.assertRaises(Exception):
cmds._run('getprivatekeys', (['bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', 'asd'],), wallet=wallet) await cmds.getprivatekeys(['bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', 'asd'], wallet=wallet)
self.assertEqual(['p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL', 'p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN'], self.assertEqual(['p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL', 'p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN'],
cmds._run('getprivatekeys', (['bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', 'bc1q9pzjpjq4nqx5ycnywekcmycqz0wjp2nq604y2n'],), wallet=wallet)) await cmds.getprivatekeys(['bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', 'bc1q9pzjpjq4nqx5ycnywekcmycqz0wjp2nq604y2n'], wallet=wallet))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_export_private_key_deterministic(self, mock_save_db): async def test_export_private_key_deterministic(self, mock_save_db):
wallet = restore_wallet_from_text('bitter grass shiver impose acquire brush forget axis eager alone wine silver', wallet = restore_wallet_from_text('bitter grass shiver impose acquire brush forget axis eager alone wine silver',
gap_limit=2, gap_limit=2,
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
@ -112,16 +112,16 @@ class TestCommands(ElectrumTestCase):
cmds = Commands(config=self.config) cmds = Commands(config=self.config)
# single address tests # single address tests
with self.assertRaises(Exception): with self.assertRaises(Exception):
cmds._run('getprivatekeys', ("asdasd",), wallet=wallet) # invalid addr, though might raise "not in wallet" await cmds.getprivatekeys("asdasd", wallet=wallet) # invalid addr, though might raise "not in wallet"
with self.assertRaises(Exception): with self.assertRaises(Exception):
cmds._run('getprivatekeys', ("bc1qgfam82qk7uwh5j2xxmcd8cmklpe0zackyj6r23",), wallet=wallet) # not in wallet await cmds.getprivatekeys("bc1qgfam82qk7uwh5j2xxmcd8cmklpe0zackyj6r23", wallet=wallet) # not in wallet
self.assertEqual("p2wpkh:L15oxP24NMNAXxq5r2aom24pHPtt3Fet8ZutgL155Bad93GSubM2", self.assertEqual("p2wpkh:L15oxP24NMNAXxq5r2aom24pHPtt3Fet8ZutgL155Bad93GSubM2",
cmds._run('getprivatekeys', ("bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af",), wallet=wallet)) await cmds.getprivatekeys("bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af", wallet=wallet))
# list of addresses tests # list of addresses tests
with self.assertRaises(Exception): with self.assertRaises(Exception):
cmds._run('getprivatekeys', (['bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', 'asd'],), wallet=wallet) await cmds.getprivatekeys(['bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', 'asd'], wallet=wallet)
self.assertEqual(['p2wpkh:L15oxP24NMNAXxq5r2aom24pHPtt3Fet8ZutgL155Bad93GSubM2', 'p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN'], self.assertEqual(['p2wpkh:L15oxP24NMNAXxq5r2aom24pHPtt3Fet8ZutgL155Bad93GSubM2', 'p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN'],
cmds._run('getprivatekeys', (['bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', 'bc1q9pzjpjq4nqx5ycnywekcmycqz0wjp2nq604y2n'],), wallet=wallet)) await cmds.getprivatekeys(['bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', 'bc1q9pzjpjq4nqx5ycnywekcmycqz0wjp2nq604y2n'], wallet=wallet))
class TestCommandsTestnet(ElectrumTestCase): class TestCommandsTestnet(ElectrumTestCase):
@ -131,7 +131,7 @@ class TestCommandsTestnet(ElectrumTestCase):
super().setUp() super().setUp()
self.config = SimpleConfig({'electrum_path': self.electrum_path}) self.config = SimpleConfig({'electrum_path': self.electrum_path})
def test_convert_xkey(self): async def test_convert_xkey(self):
cmds = Commands(config=self.config) cmds = Commands(config=self.config)
xpubs = { xpubs = {
("tpubD8p5qNfjczgTGbh9qgNxsbFgyhv8GgfVkmp3L88qtRm5ibUYiDVCrn6WYfnGey5XVVw6Bc5QNQUZW5B4jFQsHjmaenvkFUgWtKtgj5AdPm9", "standard"), ("tpubD8p5qNfjczgTGbh9qgNxsbFgyhv8GgfVkmp3L88qtRm5ibUYiDVCrn6WYfnGey5XVVw6Bc5QNQUZW5B4jFQsHjmaenvkFUgWtKtgj5AdPm9", "standard"),
@ -140,7 +140,7 @@ class TestCommandsTestnet(ElectrumTestCase):
} }
for xkey1, xtype1 in xpubs: for xkey1, xtype1 in xpubs:
for xkey2, xtype2 in xpubs: for xkey2, xtype2 in xpubs:
self.assertEqual(xkey2, cmds._run('convert_xkey', (xkey1, xtype2))) self.assertEqual(xkey2, await cmds.convert_xkey(xkey1, xtype2))
xprvs = { xprvs = {
("tprv8c83gxdVUcznP8fMx2iNUBbaQgQC7MUbBUDG3c6YU9xgt7Dn5pfcgHUeNZTAvuYmNgVHjyTzYzGWwJr7GvKCm2FkPaaJipyipbfJeB3tdPW", "standard"), ("tprv8c83gxdVUcznP8fMx2iNUBbaQgQC7MUbBUDG3c6YU9xgt7Dn5pfcgHUeNZTAvuYmNgVHjyTzYzGWwJr7GvKCm2FkPaaJipyipbfJeB3tdPW", "standard"),
@ -149,9 +149,9 @@ class TestCommandsTestnet(ElectrumTestCase):
} }
for xkey1, xtype1 in xprvs: for xkey1, xtype1 in xprvs:
for xkey2, xtype2 in xprvs: for xkey2, xtype2 in xprvs:
self.assertEqual(xkey2, cmds._run('convert_xkey', (xkey1, xtype2))) self.assertEqual(xkey2, await cmds.convert_xkey(xkey1, xtype2))
def test_serialize(self): async def test_serialize(self):
cmds = Commands(config=self.config) cmds = Commands(config=self.config)
jsontx = { jsontx = {
"inputs": [ "inputs": [
@ -170,9 +170,9 @@ class TestCommandsTestnet(ElectrumTestCase):
] ]
} }
self.assertEqual("0200000000010139c5375fe9da7bd377c1783002b129f8c57d3e724d62f5eacb9739ca691a229d0100000000feffffff01301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb0247304402206367fb2ddd723985f5f51e0f2435084c0a66f5c26f4403a75d3dd417b71a20450220545dc3637bcb49beedbbdf5063e05cad63be91af4f839886451c30ecd6edf1d20121021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da00000000", self.assertEqual("0200000000010139c5375fe9da7bd377c1783002b129f8c57d3e724d62f5eacb9739ca691a229d0100000000feffffff01301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb0247304402206367fb2ddd723985f5f51e0f2435084c0a66f5c26f4403a75d3dd417b71a20450220545dc3637bcb49beedbbdf5063e05cad63be91af4f839886451c30ecd6edf1d20121021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da00000000",
cmds._run('serialize', (jsontx,))) await cmds.serialize(jsontx))
def test_serialize_custom_nsequence(self): async def test_serialize_custom_nsequence(self):
cmds = Commands(config=self.config) cmds = Commands(config=self.config)
jsontx = { jsontx = {
"inputs": [ "inputs": [
@ -192,24 +192,24 @@ class TestCommandsTestnet(ElectrumTestCase):
] ]
} }
self.assertEqual("0200000000010139c5375fe9da7bd377c1783002b129f8c57d3e724d62f5eacb9739ca691a229d0100000000fdffffff01301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb0247304402201c551df0458528d19ba1dd79b134dcf0055f7b029dfc3d0d024e6253d069d13e02206d03cfc85a6fc648acb6fc6be630e4567d1dd00ddbcdee551ee0711414e2f33f0121021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da00000000", self.assertEqual("0200000000010139c5375fe9da7bd377c1783002b129f8c57d3e724d62f5eacb9739ca691a229d0100000000fdffffff01301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb0247304402201c551df0458528d19ba1dd79b134dcf0055f7b029dfc3d0d024e6253d069d13e02206d03cfc85a6fc648acb6fc6be630e4567d1dd00ddbcdee551ee0711414e2f33f0121021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da00000000",
cmds._run('serialize', (jsontx,))) await cmds.serialize(jsontx))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_getprivatekeyforpath(self, mock_save_db): async def test_getprivatekeyforpath(self, mock_save_db):
wallet = restore_wallet_from_text('north rent dawn bunker hamster invest wagon market romance pig either squeeze', wallet = restore_wallet_from_text('north rent dawn bunker hamster invest wagon market romance pig either squeeze',
gap_limit=2, gap_limit=2,
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
config=self.config)['wallet'] config=self.config)['wallet']
cmds = Commands(config=self.config) cmds = Commands(config=self.config)
self.assertEqual("p2wpkh:cUzm7zPpWgLYeURgff4EsoMjhskCpsviBH4Y3aZcrBX8UJSRPjC2", self.assertEqual("p2wpkh:cUzm7zPpWgLYeURgff4EsoMjhskCpsviBH4Y3aZcrBX8UJSRPjC2",
cmds._run('getprivatekeyforpath', ([0, 10000],), wallet=wallet)) await cmds.getprivatekeyforpath([0, 10000], wallet=wallet))
self.assertEqual("p2wpkh:cUzm7zPpWgLYeURgff4EsoMjhskCpsviBH4Y3aZcrBX8UJSRPjC2", self.assertEqual("p2wpkh:cUzm7zPpWgLYeURgff4EsoMjhskCpsviBH4Y3aZcrBX8UJSRPjC2",
cmds._run('getprivatekeyforpath', ("m/0/10000",), wallet=wallet)) await cmds.getprivatekeyforpath("m/0/10000", wallet=wallet))
self.assertEqual("p2wpkh:cQAj4WGf1socCPCJNMjXYCJ8Bs5JUAk5pbDr4ris44QdgAXcV24S", self.assertEqual("p2wpkh:cQAj4WGf1socCPCJNMjXYCJ8Bs5JUAk5pbDr4ris44QdgAXcV24S",
cmds._run('getprivatekeyforpath', ("m/5h/100000/88h/7",), wallet=wallet)) await cmds.getprivatekeyforpath("m/5h/100000/88h/7", wallet=wallet))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_payto(self, mock_save_db): async def test_payto(self, mock_save_db):
wallet = restore_wallet_from_text('disagree rug lemon bean unaware square alone beach tennis exhibit fix mimic', wallet = restore_wallet_from_text('disagree rug lemon bean unaware square alone beach tennis exhibit fix mimic',
gap_limit=2, gap_limit=2,
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
@ -221,8 +221,7 @@ class TestCommandsTestnet(ElectrumTestCase):
wallet.adb.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED) wallet.adb.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
cmds = Commands(config=self.config) cmds = Commands(config=self.config)
tx_str = cmds._run( tx_str = await cmds.payto(
'payto', (),
destination="tb1qsyzgpwa0vg2940u5t6l97etuvedr5dejpf9tdy", destination="tb1qsyzgpwa0vg2940u5t6l97etuvedr5dejpf9tdy",
amount="0.00123456", amount="0.00123456",
feerate=50, feerate=50,
@ -237,7 +236,7 @@ class TestCommandsTestnet(ElectrumTestCase):
tx_str) tx_str)
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_paytomany_multiple_max_spends(self, mock_save_db): async def test_paytomany_multiple_max_spends(self, mock_save_db):
wallet = restore_wallet_from_text('kit virtual quantum festival fortune inform ladder saddle filter soldier start ghost', wallet = restore_wallet_from_text('kit virtual quantum festival fortune inform ladder saddle filter soldier start ghost',
gap_limit=2, gap_limit=2,
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
@ -249,8 +248,7 @@ class TestCommandsTestnet(ElectrumTestCase):
wallet.adb.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED) wallet.adb.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
cmds = Commands(config=self.config) cmds = Commands(config=self.config)
tx_str = cmds._run( tx_str = await cmds.paytomany(
'paytomany', (),
outputs=[["tb1qk3g0t9pw5wctkzz7gh6k3ljfuukn729s67y54e", 0.002], outputs=[["tb1qk3g0t9pw5wctkzz7gh6k3ljfuukn729s67y54e", 0.002],
["tb1qr7evucrllljtryam6y2k3ntmlptq208pghql2h", "2!"], ["tb1qr7evucrllljtryam6y2k3ntmlptq208pghql2h", "2!"],
["tb1qs3msqp0n0qade2haanjw2dkaa5lm77vwvce00h", 0.003], ["tb1qs3msqp0n0qade2haanjw2dkaa5lm77vwvce00h", 0.003],
@ -265,15 +263,15 @@ class TestCommandsTestnet(ElectrumTestCase):
tx_str) tx_str)
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_signtransaction_without_wallet(self, mock_save_db): async def test_signtransaction_without_wallet(self, mock_save_db):
cmds = Commands(config=self.config) cmds = Commands(config=self.config)
unsigned_tx = "70736274ff0100a0020000000221d3645ba44f33fff6fe2666dc080279bc34b531c66888729712a80b204a32a10100000000fdffffffdd7f90d51acf98dc45ad7489316a983868c75e16bf14ffeb9eae01603a7b4da40100000000fdffffff02e8030000000000001976a9149a9ec2b35a7660c80dae38dd806fdf9b0fde68fd88ac74c11000000000001976a914f0dc093f7fb1b76cfd06610d5359d6595676cc2b88aca79b1d00000100e102000000018ba8cf9f0ff0b44c389e4a1cd25c0770636d95ccef161e313542647d435a5fd0000000006a4730440220373b3989905177f2e36d7e3d02b967d03092747fe7bbd3ba7b2c24623a88538c02207be79ee1d981060c2be6783f4946ce1bda1f64671b349ef14a4a6fecc047a71e0121030de43c5ed4c6272d20ce3becf3fb7afd5c3ccfb5d58ddfdf3047981e0b005e0dfdffffff02c0010700000000001976a9141cd3eb65bce2cae9f54544b65e46b3ad1f0b187288ac40420f00000000001976a914f0dc093f7fb1b76cfd06610d5359d6595676cc2b88ac979b1d00000100e102000000014e39236158716e91b0b2170ebe9d6b359d139e9ebfff163f2bafd0bec9890d04000000006a473044022070340deb95ca25ef86c4c7a9539b5c8f7b8351941635450311f914cd9c2f45ea02203fa7576e032ab5ae4763c78f5c2124573213c956286fd766582d9462515dc6540121033f6737e40a3a6087bc58bc5b82b427f9ed26d710b8fe2f70bfdd3d62abebcf74fdffffff02e8030000000000001976a91490350959750b3b38e451df16bd5957b7649bf5d288acac840100000000001976a914f0dc093f7fb1b76cfd06610d5359d6595676cc2b88ac979b1d00000000" unsigned_tx = "70736274ff0100a0020000000221d3645ba44f33fff6fe2666dc080279bc34b531c66888729712a80b204a32a10100000000fdffffffdd7f90d51acf98dc45ad7489316a983868c75e16bf14ffeb9eae01603a7b4da40100000000fdffffff02e8030000000000001976a9149a9ec2b35a7660c80dae38dd806fdf9b0fde68fd88ac74c11000000000001976a914f0dc093f7fb1b76cfd06610d5359d6595676cc2b88aca79b1d00000100e102000000018ba8cf9f0ff0b44c389e4a1cd25c0770636d95ccef161e313542647d435a5fd0000000006a4730440220373b3989905177f2e36d7e3d02b967d03092747fe7bbd3ba7b2c24623a88538c02207be79ee1d981060c2be6783f4946ce1bda1f64671b349ef14a4a6fecc047a71e0121030de43c5ed4c6272d20ce3becf3fb7afd5c3ccfb5d58ddfdf3047981e0b005e0dfdffffff02c0010700000000001976a9141cd3eb65bce2cae9f54544b65e46b3ad1f0b187288ac40420f00000000001976a914f0dc093f7fb1b76cfd06610d5359d6595676cc2b88ac979b1d00000100e102000000014e39236158716e91b0b2170ebe9d6b359d139e9ebfff163f2bafd0bec9890d04000000006a473044022070340deb95ca25ef86c4c7a9539b5c8f7b8351941635450311f914cd9c2f45ea02203fa7576e032ab5ae4763c78f5c2124573213c956286fd766582d9462515dc6540121033f6737e40a3a6087bc58bc5b82b427f9ed26d710b8fe2f70bfdd3d62abebcf74fdffffff02e8030000000000001976a91490350959750b3b38e451df16bd5957b7649bf5d288acac840100000000001976a914f0dc093f7fb1b76cfd06610d5359d6595676cc2b88ac979b1d00000000"
privkey = "cVtE728tULSA4gut4QWxo218q6PRsXHQAv84SXix83cuvScvGd1H" privkey = "cVtE728tULSA4gut4QWxo218q6PRsXHQAv84SXix83cuvScvGd1H"
self.assertEqual("020000000221d3645ba44f33fff6fe2666dc080279bc34b531c66888729712a80b204a32a1010000006a47304402205b30e188e30c846f98dacc714c16b7cd3a58a3fa24973d289683c9d32813e24c0220153855a29e96fb083084417ba3e3873ccaeb08435dad93773ab60716f94a36160121033f6737e40a3a6087bc58bc5b82b427f9ed26d710b8fe2f70bfdd3d62abebcf74fdffffffdd7f90d51acf98dc45ad7489316a983868c75e16bf14ffeb9eae01603a7b4da4010000006a473044022010daa3dadf53bdcb071c6eff6b8787e3f675ed61feb4fef72d0bf9d99c0162f802200e73abd880b6f2ee5fe8c0abab731f1dddeb0f60df5e050a79c365bd718da1c80121033f6737e40a3a6087bc58bc5b82b427f9ed26d710b8fe2f70bfdd3d62abebcf74fdffffff02e8030000000000001976a9149a9ec2b35a7660c80dae38dd806fdf9b0fde68fd88ac74c11000000000001976a914f0dc093f7fb1b76cfd06610d5359d6595676cc2b88aca79b1d00", self.assertEqual("020000000221d3645ba44f33fff6fe2666dc080279bc34b531c66888729712a80b204a32a1010000006a47304402205b30e188e30c846f98dacc714c16b7cd3a58a3fa24973d289683c9d32813e24c0220153855a29e96fb083084417ba3e3873ccaeb08435dad93773ab60716f94a36160121033f6737e40a3a6087bc58bc5b82b427f9ed26d710b8fe2f70bfdd3d62abebcf74fdffffffdd7f90d51acf98dc45ad7489316a983868c75e16bf14ffeb9eae01603a7b4da4010000006a473044022010daa3dadf53bdcb071c6eff6b8787e3f675ed61feb4fef72d0bf9d99c0162f802200e73abd880b6f2ee5fe8c0abab731f1dddeb0f60df5e050a79c365bd718da1c80121033f6737e40a3a6087bc58bc5b82b427f9ed26d710b8fe2f70bfdd3d62abebcf74fdffffff02e8030000000000001976a9149a9ec2b35a7660c80dae38dd806fdf9b0fde68fd88ac74c11000000000001976a914f0dc093f7fb1b76cfd06610d5359d6595676cc2b88aca79b1d00",
cmds._run('signtransaction_with_privkey', (), tx=unsigned_tx, privkey=privkey)) await cmds.signtransaction_with_privkey(tx=unsigned_tx, privkey=privkey))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_signtransaction_with_wallet(self, mock_save_db): async def test_signtransaction_with_wallet(self, mock_save_db):
wallet = restore_wallet_from_text('bitter grass shiver impose acquire brush forget axis eager alone wine silver', wallet = restore_wallet_from_text('bitter grass shiver impose acquire brush forget axis eager alone wine silver',
gap_limit=2, gap_limit=2,
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
@ -291,10 +289,10 @@ class TestCommandsTestnet(ElectrumTestCase):
unsigned_tx = "cHNidP8BAHECAAAAAQOSwZQOLsnyNykZyjiHMn/luYuGYCLMebq1y+1aU9KtAAAAAAD+////AigjAAAAAAAAFgAUaQtZqBQGAvsjzCkE7OnMTa82EFIwGw8AAAAAABYAFKwOLSKSAL/7IWftb9GWrvnWh9i7AAAAAAABAN8BAAAAAUV22sziZMJNgYh2Qrcm9dZKp4JbIbNQx7daV/M32mhFAQAAAGtIMEUCIQCj+LYVXHGpitmYbt1hYbINJPrZm2RjwjtGOFbA7lSCbQIgD2BgF/2YdpbrvlIA2u3eki7uJkMloYTVu9qWW6UWCCEBIQLlxHPAUdrjEEPDNSZtDvicHaqy802IXMdwayZ/MmnGCf////8CQEIPAAAAAAAWABSKKL3bf2GGS9z1iyrRPVrrOrw8QqLduQ4AAAAAGXapFMOElQNCy2+N9VF1tIWGg4sDEw+tiKwAAAAAIgYDD67ptKJbfbggI8qYkZJxLN1MtT09kzhZHHkJ5YGuHAwQsuNafQAAAIAAAAAAAAAAAAAiAgKFhOeJ459BORsvJ4UsoYq+wGpUEcIb41D+1h7scSDeUxCy41p9AAAAgAEAAAAAAAAAAAA=" unsigned_tx = "cHNidP8BAHECAAAAAQOSwZQOLsnyNykZyjiHMn/luYuGYCLMebq1y+1aU9KtAAAAAAD+////AigjAAAAAAAAFgAUaQtZqBQGAvsjzCkE7OnMTa82EFIwGw8AAAAAABYAFKwOLSKSAL/7IWftb9GWrvnWh9i7AAAAAAABAN8BAAAAAUV22sziZMJNgYh2Qrcm9dZKp4JbIbNQx7daV/M32mhFAQAAAGtIMEUCIQCj+LYVXHGpitmYbt1hYbINJPrZm2RjwjtGOFbA7lSCbQIgD2BgF/2YdpbrvlIA2u3eki7uJkMloYTVu9qWW6UWCCEBIQLlxHPAUdrjEEPDNSZtDvicHaqy802IXMdwayZ/MmnGCf////8CQEIPAAAAAAAWABSKKL3bf2GGS9z1iyrRPVrrOrw8QqLduQ4AAAAAGXapFMOElQNCy2+N9VF1tIWGg4sDEw+tiKwAAAAAIgYDD67ptKJbfbggI8qYkZJxLN1MtT09kzhZHHkJ5YGuHAwQsuNafQAAAIAAAAAAAAAAAAAiAgKFhOeJ459BORsvJ4UsoYq+wGpUEcIb41D+1h7scSDeUxCy41p9AAAAgAEAAAAAAAAAAAA="
self.assertEqual("020000000001010392c1940e2ec9f2372919ca3887327fe5b98b866022cc79bab5cbed5a53d2ad0000000000feffffff022823000000000000160014690b59a8140602fb23cc2904ece9cc4daf361052301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb02473044022027e1e37172e52b2d84106663cff5bcf6e447dcb41f6483f99584cfb4de2785f4022005c72f6324ad130c78fca43fe5fc565526d1723f2c9dc3efea78f66d7ae9d4360121030faee9b4a25b7db82023ca989192712cdd4cb53d3d9338591c7909e581ae1c0c00000000", self.assertEqual("020000000001010392c1940e2ec9f2372919ca3887327fe5b98b866022cc79bab5cbed5a53d2ad0000000000feffffff022823000000000000160014690b59a8140602fb23cc2904ece9cc4daf361052301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb02473044022027e1e37172e52b2d84106663cff5bcf6e447dcb41f6483f99584cfb4de2785f4022005c72f6324ad130c78fca43fe5fc565526d1723f2c9dc3efea78f66d7ae9d4360121030faee9b4a25b7db82023ca989192712cdd4cb53d3d9338591c7909e581ae1c0c00000000",
cmds._run('signtransaction', (), tx=unsigned_tx, wallet=wallet)) await cmds.signtransaction(tx=unsigned_tx, wallet=wallet))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_bumpfee(self, mock_save_db): async def test_bumpfee(self, mock_save_db):
wallet = restore_wallet_from_text('right nominee cheese afford exotic pilot mask illness rug fringe degree pottery', wallet = restore_wallet_from_text('right nominee cheese afford exotic pilot mask illness rug fringe degree pottery',
gap_limit=2, gap_limit=2,
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
@ -307,7 +305,7 @@ class TestCommandsTestnet(ElectrumTestCase):
cmds = Commands(config=self.config) cmds = Commands(config=self.config)
tx = "02000000000101b9723dfc69af058ef6613539a000d2cd098a2c8a74e802b6d8739db708ba8c9a0100000000fdffffff02a00f00000000000016001429e1fd187f0cac845946ae1b11dc136c536bfc0fe8b2000000000000160014100611bcb3aee7aad176936cf4ed56ade03027aa02473044022063c05e2347f16251922830ccc757231247b3c2970c225f988e9204844a1ab7b802204652d2c4816707e3d3bea2609b83b079001a435bad2a99cc2e730f276d07070c012102ee3f00141178006c78b0b458aab21588388335078c655459afe544211f15aee050721f00" tx = "02000000000101b9723dfc69af058ef6613539a000d2cd098a2c8a74e802b6d8739db708ba8c9a0100000000fdffffff02a00f00000000000016001429e1fd187f0cac845946ae1b11dc136c536bfc0fe8b2000000000000160014100611bcb3aee7aad176936cf4ed56ade03027aa02473044022063c05e2347f16251922830ccc757231247b3c2970c225f988e9204844a1ab7b802204652d2c4816707e3d3bea2609b83b079001a435bad2a99cc2e730f276d07070c012102ee3f00141178006c78b0b458aab21588388335078c655459afe544211f15aee050721f00"
self.assertEqual("02000000000101b9723dfc69af058ef6613539a000d2cd098a2c8a74e802b6d8739db708ba8c9a0100000000fdffffff02a00f00000000000016001429e1fd187f0cac845946ae1b11dc136c536bfc0f84b2000000000000160014100611bcb3aee7aad176936cf4ed56ade03027aa0247304402203aa63539b673a3bd70a76482b17f35f8843974fab28f84143a00450789010bc40220779c2ce2d0217f973f1f6c9f718e19fc7ebd14dd8821a962f002437cda3082ec012102ee3f00141178006c78b0b458aab21588388335078c655459afe544211f15aee000000000", self.assertEqual("02000000000101b9723dfc69af058ef6613539a000d2cd098a2c8a74e802b6d8739db708ba8c9a0100000000fdffffff02a00f00000000000016001429e1fd187f0cac845946ae1b11dc136c536bfc0f84b2000000000000160014100611bcb3aee7aad176936cf4ed56ade03027aa0247304402203aa63539b673a3bd70a76482b17f35f8843974fab28f84143a00450789010bc40220779c2ce2d0217f973f1f6c9f718e19fc7ebd14dd8821a962f002437cda3082ec012102ee3f00141178006c78b0b458aab21588388335078c655459afe544211f15aee000000000",
cmds._run('bumpfee', (), tx=tx, new_fee_rate='1.6', wallet=wallet)) await cmds.bumpfee(tx=tx, new_fee_rate='1.6', wallet=wallet))
self.assertEqual("02000000000101b9723dfc69af058ef6613539a000d2cd098a2c8a74e802b6d8739db708ba8c9a0100000000fdffffff02a00f00000000000016001429e1fd187f0cac845946ae1b11dc136c536bfc0f84b2000000000000160014100611bcb3aee7aad176936cf4ed56ade03027aa0247304402203aa63539b673a3bd70a76482b17f35f8843974fab28f84143a00450789010bc40220779c2ce2d0217f973f1f6c9f718e19fc7ebd14dd8821a962f002437cda3082ec012102ee3f00141178006c78b0b458aab21588388335078c655459afe544211f15aee000000000", self.assertEqual("02000000000101b9723dfc69af058ef6613539a000d2cd098a2c8a74e802b6d8739db708ba8c9a0100000000fdffffff02a00f00000000000016001429e1fd187f0cac845946ae1b11dc136c536bfc0f84b2000000000000160014100611bcb3aee7aad176936cf4ed56ade03027aa0247304402203aa63539b673a3bd70a76482b17f35f8843974fab28f84143a00450789010bc40220779c2ce2d0217f973f1f6c9f718e19fc7ebd14dd8821a962f002437cda3082ec012102ee3f00141178006c78b0b458aab21588388335078c655459afe544211f15aee000000000",
cmds._run('bumpfee', (), tx=tx, new_fee_rate='1.6', from_coins="9a8cba08b79d73d8b602e8748a2c8a09cdd200a0393561f68e05af69fc3d72b9:1", wallet=wallet)) await cmds.bumpfee(tx=tx, new_fee_rate='1.6', from_coins="9a8cba08b79d73d8b602e8748a2c8a09cdd200a0393561f68e05af69fc3d72b9:1", wallet=wallet))

14
electrum/tests/test_invoices.py

@ -37,7 +37,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
wallet2.adb.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED) wallet2.adb.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
return wallet2 return wallet2
def test_wallet_with_ln_creates_payreq_and_gets_paid_on_ln(self): async def test_wallet_with_ln_creates_payreq_and_gets_paid_on_ln(self):
text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=2, config=self.config) d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=2, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet wallet1 = d['wallet'] # type: Standard_Wallet
@ -54,7 +54,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
wallet1.lnworker.set_request_status(bytes.fromhex(pr.rhash), PR_PAID) wallet1.lnworker.set_request_status(bytes.fromhex(pr.rhash), PR_PAID)
self.assertEqual(PR_PAID, wallet1.get_invoice_status(pr)) self.assertEqual(PR_PAID, wallet1.get_invoice_status(pr))
def test_wallet_with_ln_creates_payreq_and_gets_paid_onchain(self): async def test_wallet_with_ln_creates_payreq_and_gets_paid_onchain(self):
text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=2, config=self.config) d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=2, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet wallet1 = d['wallet'] # type: Standard_Wallet
@ -84,7 +84,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
wallet1.adb.add_verified_tx(tx.txid(), tx_info) wallet1.adb.add_verified_tx(tx.txid(), tx_info)
self.assertEqual(PR_PAID, wallet1.get_invoice_status(pr)) self.assertEqual(PR_PAID, wallet1.get_invoice_status(pr))
def test_wallet_without_ln_creates_payreq_and_gets_paid_onchain(self): async def test_wallet_without_ln_creates_payreq_and_gets_paid_onchain(self):
text = 'cycle rocket west magnet parrot shuffle foot correct salt library feed song' text = 'cycle rocket west magnet parrot shuffle foot correct salt library feed song'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=2, config=self.config) d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=2, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet wallet1 = d['wallet'] # type: Standard_Wallet
@ -114,7 +114,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
wallet1.adb.add_verified_tx(tx.txid(), tx_info) wallet1.adb.add_verified_tx(tx.txid(), tx_info)
self.assertEqual(PR_PAID, wallet1.get_invoice_status(pr)) self.assertEqual(PR_PAID, wallet1.get_invoice_status(pr))
def test_wallet_gets_paid_onchain_in_the_past(self): async def test_wallet_gets_paid_onchain_in_the_past(self):
text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=2, config=self.config) d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=2, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet wallet1 = d['wallet'] # type: Standard_Wallet
@ -143,7 +143,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
wallet1.adb.add_verified_tx(tx.txid(), tx_info) wallet1.adb.add_verified_tx(tx.txid(), tx_info)
self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr)) self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr))
def test_wallet_reuse_unused_fallback_onchain_addr_when_getting_paid_with_lightning(self): async def test_wallet_reuse_unused_fallback_onchain_addr_when_getting_paid_with_lightning(self):
text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=5, config=self.config) d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=5, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet wallet1 = d['wallet'] # type: Standard_Wallet
@ -198,7 +198,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr4)) self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr4))
self.assertEqual(addr4, pr4.get_address()) self.assertEqual(addr4, pr4.get_address())
def test_wallet_reuse_addr_of_expired_request(self): async def test_wallet_reuse_addr_of_expired_request(self):
text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=3, config=self.config) d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=3, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet wallet1 = d['wallet'] # type: Standard_Wallet
@ -226,7 +226,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
self.assertEqual(addr2, pr2.get_address()) self.assertEqual(addr2, pr2.get_address())
self.assertFalse(pr2.has_expired()) self.assertFalse(pr2.has_expired())
def test_wallet_get_request_by_addr(self): async def test_wallet_get_request_by_addr(self):
text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=3, config=self.config) d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=3, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet wallet1 = d['wallet'] # type: Standard_Wallet

183
electrum/tests/test_lnpeer.py

@ -404,17 +404,16 @@ class TestPeer(ElectrumTestCase):
super().setUp() super().setUp()
self._lnworkers_created = [] # type: List[MockLNWallet] self._lnworkers_created = [] # type: List[MockLNWallet]
def tearDown(self): async def asyncTearDown(self):
async def cleanup_lnworkers(): # clean up lnworkers
async with OldTaskGroup() as group: async with OldTaskGroup() as group:
for lnworker in self._lnworkers_created: for lnworker in self._lnworkers_created:
await group.spawn(lnworker.stop()) await group.spawn(lnworker.stop())
for lnworker in self._lnworkers_created: for lnworker in self._lnworkers_created:
shutil.rmtree(lnworker._user_dir) shutil.rmtree(lnworker._user_dir)
self._lnworkers_created.clear() self._lnworkers_created.clear()
run(cleanup_lnworkers())
electrum.trampoline._TRAMPOLINE_NODES_UNITTESTS = {} electrum.trampoline._TRAMPOLINE_NODES_UNITTESTS = {}
super().tearDown() await super().asyncTearDown()
def prepare_peers( def prepare_peers(
self, alice_channel: Channel, bob_channel: Channel, self, alice_channel: Channel, bob_channel: Channel,
@ -548,7 +547,7 @@ class TestPeer(ElectrumTestCase):
lnaddr2 = lndecode(invoice) # unlike lnaddr1, this now has a pubkey set lnaddr2 = lndecode(invoice) # unlike lnaddr1, this now has a pubkey set
return lnaddr2, invoice return lnaddr2, invoice
def test_reestablish(self): async def test_reestablish(self):
alice_channel, bob_channel = create_test_channels() alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel) p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
for chan in (alice_channel, bob_channel): for chan in (alice_channel, bob_channel):
@ -561,13 +560,11 @@ class TestPeer(ElectrumTestCase):
self.assertEqual(bob_channel.peer_state, PeerState.GOOD) self.assertEqual(bob_channel.peer_state, PeerState.GOOD)
gath.cancel() gath.cancel()
gath = asyncio.gather(reestablish(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p1.htlc_switch()) gath = asyncio.gather(reestablish(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p1.htlc_switch())
async def f(): with self.assertRaises(asyncio.CancelledError):
await gath await gath
with self.assertRaises(concurrent.futures.CancelledError):
run(f())
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_reestablish_with_old_state(self): async def test_reestablish_with_old_state(self):
random_seed = os.urandom(32) random_seed = os.urandom(32)
alice_channel, bob_channel = create_test_channels(random_seed=random_seed) alice_channel, bob_channel = create_test_channels(random_seed=random_seed)
alice_channel_0, bob_channel_0 = create_test_channels(random_seed=random_seed) # these are identical alice_channel_0, bob_channel_0 = create_test_channels(random_seed=random_seed) # these are identical
@ -578,10 +575,8 @@ class TestPeer(ElectrumTestCase):
self.assertEqual(result, True) self.assertEqual(result, True)
gath.cancel() gath.cancel()
gath = asyncio.gather(pay(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch()) gath = asyncio.gather(pay(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
async def f(): with self.assertRaises(asyncio.CancelledError):
await gath await gath
with self.assertRaises(concurrent.futures.CancelledError):
run(f())
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel_0, bob_channel) p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel_0, bob_channel)
for chan in (alice_channel_0, bob_channel): for chan in (alice_channel_0, bob_channel):
chan.peer_state = PeerState.DISCONNECTED chan.peer_state = PeerState.DISCONNECTED
@ -590,10 +585,8 @@ class TestPeer(ElectrumTestCase):
p1.reestablish_channel(alice_channel_0), p1.reestablish_channel(alice_channel_0),
p2.reestablish_channel(bob_channel)) p2.reestablish_channel(bob_channel))
gath = asyncio.gather(reestablish(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch()) gath = asyncio.gather(reestablish(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
async def f(): with self.assertRaises(lnutil.RemoteMisbehaving):
await gath await gath
with self.assertRaises(electrum.lnutil.RemoteMisbehaving):
run(f())
self.assertEqual(alice_channel_0.peer_state, PeerState.BAD) self.assertEqual(alice_channel_0.peer_state, PeerState.BAD)
self.assertEqual(bob_channel._state, ChannelState.FORCE_CLOSING) self.assertEqual(bob_channel._state, ChannelState.FORCE_CLOSING)
@ -612,7 +605,7 @@ class TestPeer(ElectrumTestCase):
) )
return htlc return htlc
def test_reestablish_replay_messages_rev_then_sig(self): async def test_reestablish_replay_messages_rev_then_sig(self):
""" """
See https://github.com/lightning/bolts/pull/810#issue-728299277 See https://github.com/lightning/bolts/pull/810#issue-728299277
@ -664,9 +657,9 @@ class TestPeer(ElectrumTestCase):
self.assertEqual(chan_BA.peer_state, PeerState.GOOD) self.assertEqual(chan_BA.peer_state, PeerState.GOOD)
raise SuccessfulTest() raise SuccessfulTest()
with self.assertRaises(SuccessfulTest): with self.assertRaises(SuccessfulTest):
run(f()) await f()
def test_reestablish_replay_messages_sig_then_rev(self): async def test_reestablish_replay_messages_sig_then_rev(self):
""" """
See https://github.com/lightning/bolts/pull/810#issue-728299277 See https://github.com/lightning/bolts/pull/810#issue-728299277
@ -719,9 +712,9 @@ class TestPeer(ElectrumTestCase):
self.assertEqual(chan_BA.peer_state, PeerState.GOOD) self.assertEqual(chan_BA.peer_state, PeerState.GOOD)
raise SuccessfulTest() raise SuccessfulTest()
with self.assertRaises(SuccessfulTest): with self.assertRaises(SuccessfulTest):
run(f()) await f()
def _test_simple_payment(self, trampoline: bool): async def _test_simple_payment(self, trampoline: bool):
"""Alice pays Bob a single HTLC via direct channel.""" """Alice pays Bob a single HTLC via direct channel."""
alice_channel, bob_channel = create_test_channels() alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel) p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
@ -754,18 +747,18 @@ class TestPeer(ElectrumTestCase):
'bob': LNPeerAddr(host="127.0.0.1", port=9735, pubkey=w2.node_keypair.pubkey), 'bob': LNPeerAddr(host="127.0.0.1", port=9735, pubkey=w2.node_keypair.pubkey),
} }
with self.assertRaises(PaymentDone): with self.assertRaises(PaymentDone):
run(f()) await f()
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_simple_payment(self): async def test_simple_payment(self):
self._test_simple_payment(trampoline=False) await self._test_simple_payment(trampoline=False)
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_simple_payment_trampoline(self): async def test_simple_payment_trampoline(self):
self._test_simple_payment(trampoline=True) await self._test_simple_payment(trampoline=True)
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_race(self): async def test_payment_race(self):
"""Alice and Bob pay each other simultaneously. """Alice and Bob pay each other simultaneously.
They both send 'update_add_htlc' and receive each other's update They both send 'update_add_htlc' and receive each other's update
before sending 'commitment_signed'. Neither party should fulfill before sending 'commitment_signed'. Neither party should fulfill
@ -837,11 +830,11 @@ class TestPeer(ElectrumTestCase):
await asyncio.sleep(0.01) await asyncio.sleep(0.01)
await group.spawn(pay()) await group.spawn(pay())
with self.assertRaises(PaymentDone): with self.assertRaises(PaymentDone):
run(f()) await f()
#@unittest.skip("too expensive") #@unittest.skip("too expensive")
#@needs_test_with_all_chacha20_implementations #@needs_test_with_all_chacha20_implementations
def test_payments_stresstest(self): async def test_payments_stresstest(self):
alice_channel, bob_channel = create_test_channels() alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel) p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
alice_init_balance_msat = alice_channel.balance(HTLCOwner.LOCAL) alice_init_balance_msat = alice_channel.balance(HTLCOwner.LOCAL)
@ -859,17 +852,15 @@ class TestPeer(ElectrumTestCase):
await group.spawn(single_payment(pay_req)) await group.spawn(single_payment(pay_req))
gath.cancel() gath.cancel()
gath = asyncio.gather(many_payments(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch()) gath = asyncio.gather(many_payments(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
async def f(): with self.assertRaises(asyncio.CancelledError):
await gath await gath
with self.assertRaises(concurrent.futures.CancelledError):
run(f())
self.assertEqual(alice_init_balance_msat - num_payments * payment_value_msat, alice_channel.balance(HTLCOwner.LOCAL)) self.assertEqual(alice_init_balance_msat - num_payments * payment_value_msat, alice_channel.balance(HTLCOwner.LOCAL))
self.assertEqual(alice_init_balance_msat - num_payments * payment_value_msat, bob_channel.balance(HTLCOwner.REMOTE)) self.assertEqual(alice_init_balance_msat - num_payments * payment_value_msat, bob_channel.balance(HTLCOwner.REMOTE))
self.assertEqual(bob_init_balance_msat + num_payments * payment_value_msat, bob_channel.balance(HTLCOwner.LOCAL)) self.assertEqual(bob_init_balance_msat + num_payments * payment_value_msat, bob_channel.balance(HTLCOwner.LOCAL))
self.assertEqual(bob_init_balance_msat + num_payments * payment_value_msat, alice_channel.balance(HTLCOwner.REMOTE)) self.assertEqual(bob_init_balance_msat + num_payments * payment_value_msat, alice_channel.balance(HTLCOwner.REMOTE))
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_multihop(self): async def test_payment_multihop(self):
graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph']) graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph'])
peers = graph.peers.values() peers = graph.peers.values()
async def pay(lnaddr, pay_req): async def pay(lnaddr, pay_req):
@ -888,10 +879,10 @@ class TestPeer(ElectrumTestCase):
lnaddr, pay_req = self.prepare_invoice(graph.workers['dave'], include_routing_hints=True) lnaddr, pay_req = self.prepare_invoice(graph.workers['dave'], include_routing_hints=True)
await group.spawn(pay(lnaddr, pay_req)) await group.spawn(pay(lnaddr, pay_req))
with self.assertRaises(PaymentDone): with self.assertRaises(PaymentDone):
run(f()) await f()
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_multihop_with_preselected_path(self): async def test_payment_multihop_with_preselected_path(self):
graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph']) graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph'])
peers = graph.peers.values() peers = graph.peers.values()
async def pay(pay_req): async def pay(pay_req):
@ -933,10 +924,10 @@ class TestPeer(ElectrumTestCase):
lnaddr, pay_req = self.prepare_invoice(graph.workers['dave'], include_routing_hints=True) lnaddr, pay_req = self.prepare_invoice(graph.workers['dave'], include_routing_hints=True)
await group.spawn(pay(pay_req)) await group.spawn(pay(pay_req))
with self.assertRaises(PaymentDone): with self.assertRaises(PaymentDone):
run(f()) await f()
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_multihop_temp_node_failure(self): async def test_payment_multihop_temp_node_failure(self):
graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph']) graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph'])
graph.workers['bob'].network.config.set_key('test_fail_htlcs_with_temp_node_failure', True) graph.workers['bob'].network.config.set_key('test_fail_htlcs_with_temp_node_failure', True)
graph.workers['carol'].network.config.set_key('test_fail_htlcs_with_temp_node_failure', True) graph.workers['carol'].network.config.set_key('test_fail_htlcs_with_temp_node_failure', True)
@ -958,10 +949,10 @@ class TestPeer(ElectrumTestCase):
lnaddr, pay_req = self.prepare_invoice(graph.workers['dave'], include_routing_hints=True) lnaddr, pay_req = self.prepare_invoice(graph.workers['dave'], include_routing_hints=True)
await group.spawn(pay(lnaddr, pay_req)) await group.spawn(pay(lnaddr, pay_req))
with self.assertRaises(PaymentDone): with self.assertRaises(PaymentDone):
run(f()) await f()
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_multihop_route_around_failure(self): async def test_payment_multihop_route_around_failure(self):
# Alice will pay Dave. Alice first tries A->C->D route, due to lower fees, but Carol # Alice will pay Dave. Alice first tries A->C->D route, due to lower fees, but Carol
# will fail the htlc and get blacklisted. Alice will then try A->B->D and succeed. # will fail the htlc and get blacklisted. Alice will then try A->B->D and succeed.
graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph']) graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph'])
@ -996,10 +987,10 @@ class TestPeer(ElectrumTestCase):
self.assertFalse(invoice_features.supports(LnFeatures.BASIC_MPP_OPT)) self.assertFalse(invoice_features.supports(LnFeatures.BASIC_MPP_OPT))
await group.spawn(pay(lnaddr, pay_req)) await group.spawn(pay(lnaddr, pay_req))
with self.assertRaises(PaymentDone): with self.assertRaises(PaymentDone):
run(f()) await f()
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_with_temp_channel_failure_and_liquidity_hints(self): async def test_payment_with_temp_channel_failure_and_liquidity_hints(self):
# prepare channels such that a temporary channel failure happens at c->d # prepare channels such that a temporary channel failure happens at c->d
graph_definition = GRAPH_DEFINITIONS['square_graph'].copy() graph_definition = GRAPH_DEFINITIONS['square_graph'].copy()
graph_definition['alice']['channels']['carol']['local_balance_msat'] = 200_000_000 graph_definition['alice']['channels']['carol']['local_balance_msat'] = 200_000_000
@ -1058,9 +1049,9 @@ class TestPeer(ElectrumTestCase):
lnaddr, pay_req = self.prepare_invoice(graph.workers['dave'], amount_msat=amount_to_pay, include_routing_hints=True) lnaddr, pay_req = self.prepare_invoice(graph.workers['dave'], amount_msat=amount_to_pay, include_routing_hints=True)
await group.spawn(pay(lnaddr, pay_req)) await group.spawn(pay(lnaddr, pay_req))
with self.assertRaises(PaymentDone): with self.assertRaises(PaymentDone):
run(f()) await f()
def _run_mpp(self, graph, fail_kwargs, success_kwargs): async def _run_mpp(self, graph, fail_kwargs, success_kwargs):
"""Tests a multipart payment scenario for failing and successful cases.""" """Tests a multipart payment scenario for failing and successful cases."""
self.assertEqual(500_000_000_000, graph.channels[('alice', 'bob')].balance(LOCAL)) self.assertEqual(500_000_000_000, graph.channels[('alice', 'bob')].balance(LOCAL))
self.assertEqual(500_000_000_000, graph.channels[('alice', 'carol')].balance(LOCAL)) self.assertEqual(500_000_000_000, graph.channels[('alice', 'carol')].balance(LOCAL))
@ -1110,22 +1101,23 @@ class TestPeer(ElectrumTestCase):
if fail_kwargs: if fail_kwargs:
with self.assertRaises(NoPathFound): with self.assertRaises(NoPathFound):
run(f(fail_kwargs)) await f(fail_kwargs)
if success_kwargs: if success_kwargs:
with self.assertRaises(PaymentDone): with self.assertRaises(PaymentDone):
run(f(success_kwargs)) await f(success_kwargs)
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_multipart_with_timeout(self): async def test_payment_multipart_with_timeout(self):
graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph']) graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph'])
self._run_mpp(graph, {'bob_forwarding': False}, {'bob_forwarding': True}) await self._run_mpp(graph, {'bob_forwarding': False}, {'bob_forwarding': True})
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_multipart(self): async def test_payment_multipart(self):
graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph']) graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph'])
self._run_mpp(graph, {'mpp_invoice': False}, {'mpp_invoice': True}) await self._run_mpp(graph, {'mpp_invoice': False}, {'mpp_invoice': True})
def _run_trampoline_payment(self, is_legacy, direct, drop_dave= []): async def _run_trampoline_payment(self, is_legacy, direct, drop_dave=None):
if drop_dave is None: drop_dave = []
async def turn_on_trampoline_alice(): async def turn_on_trampoline_alice():
if graph.workers['alice'].network.channel_db: if graph.workers['alice'].network.channel_db:
graph.workers['alice'].network.channel_db.stop() graph.workers['alice'].network.channel_db.stop()
@ -1178,29 +1170,29 @@ class TestPeer(ElectrumTestCase):
graph.workers['carol'].name: LNPeerAddr(host="127.0.0.1", port=9735, pubkey=graph.workers['carol'].node_keypair.pubkey), graph.workers['carol'].name: LNPeerAddr(host="127.0.0.1", port=9735, pubkey=graph.workers['carol'].node_keypair.pubkey),
} }
run(f()) await f()
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_trampoline_legacy(self): async def test_payment_trampoline_legacy(self):
with self.assertRaises(PaymentDone): with self.assertRaises(PaymentDone):
self._run_trampoline_payment(is_legacy=True, direct=False) await self._run_trampoline_payment(is_legacy=True, direct=False)
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_trampoline_e2e_direct(self): async def test_payment_trampoline_e2e_direct(self):
with self.assertRaises(PaymentDone): with self.assertRaises(PaymentDone):
self._run_trampoline_payment(is_legacy=False, direct=True) await self._run_trampoline_payment(is_legacy=False, direct=True)
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_trampoline_e2e_indirect(self): async def test_payment_trampoline_e2e_indirect(self):
# must use two trampolines # must use two trampolines
with self.assertRaises(PaymentDone): with self.assertRaises(PaymentDone):
self._run_trampoline_payment(is_legacy=False, direct=False, drop_dave=['bob']) await self._run_trampoline_payment(is_legacy=False, direct=False, drop_dave=['bob'])
# both trampolines drop dave # both trampolines drop dave
with self.assertRaises(NoPathFound): with self.assertRaises(NoPathFound):
self._run_trampoline_payment(is_legacy=False, direct=False, drop_dave=['bob', 'carol']) await self._run_trampoline_payment(is_legacy=False, direct=False, drop_dave=['bob', 'carol'])
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_multipart_trampoline_e2e(self): async def test_payment_multipart_trampoline_e2e(self):
graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph']) graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph'])
electrum.trampoline._TRAMPOLINE_NODES_UNITTESTS = { electrum.trampoline._TRAMPOLINE_NODES_UNITTESTS = {
graph.workers['bob'].name: LNPeerAddr(host="127.0.0.1", port=9735, pubkey=graph.workers['bob'].node_keypair.pubkey), graph.workers['bob'].name: LNPeerAddr(host="127.0.0.1", port=9735, pubkey=graph.workers['bob'].node_keypair.pubkey),
@ -1210,26 +1202,26 @@ class TestPeer(ElectrumTestCase):
# * a payment with one trial: fails, because # * a payment with one trial: fails, because
# we need at least one trial because the initial fees are too low # we need at least one trial because the initial fees are too low
# * a payment with several trials: should succeed # * a payment with several trials: should succeed
self._run_mpp( await self._run_mpp(
graph, graph,
fail_kwargs={'alice_uses_trampoline': True, 'attempts': 1}, fail_kwargs={'alice_uses_trampoline': True, 'attempts': 1},
success_kwargs={'alice_uses_trampoline': True, 'attempts': 30}) success_kwargs={'alice_uses_trampoline': True, 'attempts': 30})
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_payment_multipart_trampoline_legacy(self): async def test_payment_multipart_trampoline_legacy(self):
graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph']) graph = self.prepare_chans_and_peers_in_graph(GRAPH_DEFINITIONS['square_graph'])
electrum.trampoline._TRAMPOLINE_NODES_UNITTESTS = { electrum.trampoline._TRAMPOLINE_NODES_UNITTESTS = {
graph.workers['bob'].name: LNPeerAddr(host="127.0.0.1", port=9735, pubkey=graph.workers['bob'].node_keypair.pubkey), graph.workers['bob'].name: LNPeerAddr(host="127.0.0.1", port=9735, pubkey=graph.workers['bob'].node_keypair.pubkey),
graph.workers['carol'].name: LNPeerAddr(host="127.0.0.1", port=9735, pubkey=graph.workers['carol'].node_keypair.pubkey), graph.workers['carol'].name: LNPeerAddr(host="127.0.0.1", port=9735, pubkey=graph.workers['carol'].node_keypair.pubkey),
} }
# trampoline-to-legacy: this is restricted, as there are no forwarders capable of doing this # trampoline-to-legacy: this is restricted, as there are no forwarders capable of doing this
self._run_mpp( await self._run_mpp(
graph, graph,
fail_kwargs={'alice_uses_trampoline': True, 'attempts': 30, 'disable_trampoline_receiving': True}, fail_kwargs={'alice_uses_trampoline': True, 'attempts': 30, 'disable_trampoline_receiving': True},
success_kwargs={}) success_kwargs={})
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_fail_pending_htlcs_on_shutdown(self): async def test_fail_pending_htlcs_on_shutdown(self):
"""Alice tries to pay Dave via MPP. Dave receives some HTLCs but not all. """Alice tries to pay Dave via MPP. Dave receives some HTLCs but not all.
Dave shuts down (stops wallet). Dave shuts down (stops wallet).
We test if Dave fails the pending HTLCs during shutdown. We test if Dave fails the pending HTLCs during shutdown.
@ -1270,19 +1262,19 @@ class TestPeer(ElectrumTestCase):
await group.spawn(stop()) await group.spawn(stop())
with self.assertRaises(SuccessfulTest): with self.assertRaises(SuccessfulTest):
run(f()) await f()
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_legacy_shutdown_low(self): async def test_legacy_shutdown_low(self):
self._test_shutdown(alice_fee=100, bob_fee=150) await self._test_shutdown(alice_fee=100, bob_fee=150)
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_legacy_shutdown_high(self): async def test_legacy_shutdown_high(self):
self._test_shutdown(alice_fee=2000, bob_fee=100) await self._test_shutdown(alice_fee=2000, bob_fee=100)
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_modern_shutdown_with_overlap(self): async def test_modern_shutdown_with_overlap(self):
self._test_shutdown( await self._test_shutdown(
alice_fee=1, alice_fee=1,
bob_fee=200, bob_fee=200,
alice_fee_range={'min_fee_satoshis': 1, 'max_fee_satoshis': 10}, alice_fee_range={'min_fee_satoshis': 1, 'max_fee_satoshis': 10},
@ -1300,7 +1292,7 @@ class TestPeer(ElectrumTestCase):
# bob_fee_range={'min_fee_satoshis': 50, 'max_fee_satoshis': 300}) # bob_fee_range={'min_fee_satoshis': 50, 'max_fee_satoshis': 300})
# )) # ))
def _test_shutdown(self, alice_fee, bob_fee, alice_fee_range=None, bob_fee_range=None): async def _test_shutdown(self, alice_fee, bob_fee, alice_fee_range=None, bob_fee_range=None):
alice_channel, bob_channel = create_test_channels() alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel) p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
w1.network.config.set_key('test_shutdown_fee', alice_fee) w1.network.config.set_key('test_shutdown_fee', alice_fee)
@ -1334,13 +1326,11 @@ class TestPeer(ElectrumTestCase):
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
w2.enable_htlc_settle = True w2.enable_htlc_settle = True
gath = asyncio.gather(pay(), set_settle(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch()) gath = asyncio.gather(pay(), set_settle(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
async def f(): with self.assertRaises(asyncio.CancelledError):
await gath await gath
with self.assertRaises(concurrent.futures.CancelledError):
run(f())
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_warning(self): async def test_warning(self):
alice_channel, bob_channel = create_test_channels() alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel) p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
@ -1349,13 +1339,11 @@ class TestPeer(ElectrumTestCase):
await asyncio.wait_for(p2.initialized, 1) await asyncio.wait_for(p2.initialized, 1)
await p1.send_warning(alice_channel.channel_id, 'be warned!', close_connection=True) await p1.send_warning(alice_channel.channel_id, 'be warned!', close_connection=True)
gath = asyncio.gather(action(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch()) gath = asyncio.gather(action(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
async def f():
await gath
with self.assertRaises(GracefulDisconnect): with self.assertRaises(GracefulDisconnect):
run(f()) await gath
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_error(self): async def test_error(self):
alice_channel, bob_channel = create_test_channels() alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel) p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
@ -1366,13 +1354,11 @@ class TestPeer(ElectrumTestCase):
assert alice_channel.is_closed() assert alice_channel.is_closed()
gath.cancel() gath.cancel()
gath = asyncio.gather(action(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch()) gath = asyncio.gather(action(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
async def f():
await gath
with self.assertRaises(GracefulDisconnect): with self.assertRaises(GracefulDisconnect):
run(f()) await gath
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_close_upfront_shutdown_script(self): async def test_close_upfront_shutdown_script(self):
alice_channel, bob_channel = create_test_channels() alice_channel, bob_channel = create_test_channels()
# create upfront shutdown script for bob, alice doesn't use upfront # create upfront shutdown script for bob, alice doesn't use upfront
@ -1411,7 +1397,7 @@ class TestPeer(ElectrumTestCase):
await gath await gath
with self.assertRaises(GracefulDisconnect): with self.assertRaises(GracefulDisconnect):
run(test()) await test()
# bob sends the same upfront_shutdown_script has he announced # bob sends the same upfront_shutdown_script has he announced
alice_channel.config[HTLCOwner.REMOTE].upfront_shutdown_script = bob_uss alice_channel.config[HTLCOwner.REMOTE].upfront_shutdown_script = bob_uss
@ -1438,24 +1424,25 @@ class TestPeer(ElectrumTestCase):
coros = [close(), main_loop(p1), main_loop(p2)] coros = [close(), main_loop(p1), main_loop(p2)]
gath = asyncio.gather(*coros) gath = asyncio.gather(*coros)
await gath await gath
with self.assertRaises(concurrent.futures.CancelledError):
run(test())
def test_channel_usage_after_closing(self): with self.assertRaises(asyncio.CancelledError):
await test()
async def test_channel_usage_after_closing(self):
alice_channel, bob_channel = create_test_channels() alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, q1, q2 = self.prepare_peers(alice_channel, bob_channel) p1, p2, w1, w2, q1, q2 = self.prepare_peers(alice_channel, bob_channel)
lnaddr, pay_req = self.prepare_invoice(w2) lnaddr, pay_req = self.prepare_invoice(w2)
lnaddr = w1._check_invoice(pay_req) lnaddr = w1._check_invoice(pay_req)
route, amount_msat = run(w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr))[0][0:2] route, amount_msat = (await w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr))[0][0:2]
assert amount_msat == lnaddr.get_amount_msat() assert amount_msat == lnaddr.get_amount_msat()
run(w1.force_close_channel(alice_channel.channel_id)) await w1.force_close_channel(alice_channel.channel_id)
# check if a tx (commitment transaction) was broadcasted: # check if a tx (commitment transaction) was broadcasted:
assert q1.qsize() == 1 assert q1.qsize() == 1
with self.assertRaises(NoPathFound) as e: with self.assertRaises(NoPathFound) as e:
run(w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr)) await w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr)
peer = w1.peers[route[0].node_id] peer = w1.peers[route[0].node_id]
# AssertionError is ok since we shouldn't use old routes, and the # AssertionError is ok since we shouldn't use old routes, and the
@ -1477,10 +1464,10 @@ class TestPeer(ElectrumTestCase):
) )
await asyncio.gather(pay, p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch()) await asyncio.gather(pay, p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
with self.assertRaises(PaymentFailure): with self.assertRaises(PaymentFailure):
run(f()) await f()
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_sending_weird_messages_that_should_be_ignored(self): async def test_sending_weird_messages_that_should_be_ignored(self):
alice_channel, bob_channel = create_test_channels() alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel) p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
@ -1509,10 +1496,10 @@ class TestPeer(ElectrumTestCase):
await group.spawn(send_weird_messages()) await group.spawn(send_weird_messages())
with self.assertRaises(SuccessfulTest): with self.assertRaises(SuccessfulTest):
run(f()) await f()
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_sending_weird_messages__unknown_even_type(self): async def test_sending_weird_messages__unknown_even_type(self):
alice_channel, bob_channel = create_test_channels() alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel) p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
@ -1538,11 +1525,11 @@ class TestPeer(ElectrumTestCase):
await group.spawn(send_weird_messages()) await group.spawn(send_weird_messages())
with self.assertRaises(GracefulDisconnect): with self.assertRaises(GracefulDisconnect):
run(f()) await f()
self.assertTrue(isinstance(failing_task.exception().__cause__, lnmsg.UnknownMandatoryMsgType)) self.assertTrue(isinstance(failing_task.exception().__cause__, lnmsg.UnknownMandatoryMsgType))
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_sending_weird_messages__known_msg_with_insufficient_length(self): async def test_sending_weird_messages__known_msg_with_insufficient_length(self):
alice_channel, bob_channel = create_test_channels() alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel) p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
@ -1568,9 +1555,5 @@ class TestPeer(ElectrumTestCase):
await group.spawn(send_weird_messages()) await group.spawn(send_weird_messages())
with self.assertRaises(GracefulDisconnect): with self.assertRaises(GracefulDisconnect):
run(f()) await f()
self.assertTrue(isinstance(failing_task.exception().__cause__, lnmsg.UnexpectedEndOfStream)) self.assertTrue(isinstance(failing_task.exception().__cause__, lnmsg.UnexpectedEndOfStream))
def run(coro):
return asyncio.run_coroutine_threadsafe(coro, loop=util.get_asyncio_loop()).result()

16
electrum/tests/test_lnrouter.py

@ -3,6 +3,7 @@ import unittest
import tempfile import tempfile
import shutil import shutil
import asyncio import asyncio
from typing import Optional
from electrum import util from electrum import util
from electrum.util import bfh from electrum.util import bfh
@ -30,18 +31,19 @@ def node(character: str) -> bytes:
class Test_LNRouter(ElectrumTestCase): class Test_LNRouter(ElectrumTestCase):
TESTNET = True TESTNET = True
cdb = None cdb = None # type: Optional[lnrouter.ChannelDB]
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.config = SimpleConfig({'electrum_path': self.electrum_path}) self.config = SimpleConfig({'electrum_path': self.electrum_path})
self.assertIsNone(self.cdb) # sanity-check side effects from previous tests
def tearDown(self): async def asyncTearDown(self):
# if the test called prepare_graph(), channeldb needs to be cleaned up # if the test called prepare_graph(), channeldb needs to be cleaned up
if self.cdb: if self.cdb:
self.cdb.stop() self.cdb.stop()
asyncio.run_coroutine_threadsafe(self.cdb.stopped_event.wait(), self.asyncio_loop).result() await self.cdb.stopped_event.wait()
super().tearDown() await super().asyncTearDown()
def prepare_graph(self): def prepare_graph(self):
""" """
@ -139,7 +141,7 @@ class Test_LNRouter(ElectrumTestCase):
add_chan_upd({'short_channel_id': channel(7), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0}) add_chan_upd({'short_channel_id': channel(7), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
add_chan_upd({'short_channel_id': channel(7), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0}) add_chan_upd({'short_channel_id': channel(7), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
def test_find_path_for_payment(self): async def test_find_path_for_payment(self):
self.prepare_graph() self.prepare_graph()
amount_to_send = 100000 amount_to_send = 100000
@ -156,7 +158,7 @@ class Test_LNRouter(ElectrumTestCase):
self.assertEqual(node('b'), route[0].node_id) self.assertEqual(node('b'), route[0].node_id)
self.assertEqual(channel(3), route[0].short_channel_id) self.assertEqual(channel(3), route[0].short_channel_id)
def test_find_path_liquidity_hints(self): async def test_find_path_liquidity_hints(self):
self.prepare_graph() self.prepare_graph()
amount_to_send = 100000 amount_to_send = 100000
@ -213,7 +215,7 @@ class Test_LNRouter(ElectrumTestCase):
self.assertEqual(channel(4), path[1].short_channel_id) self.assertEqual(channel(4), path[1].short_channel_id)
self.assertEqual(channel(7), path[2].short_channel_id) self.assertEqual(channel(7), path[2].short_channel_id)
def test_find_path_liquidity_hints_inflight_htlcs(self): async def test_find_path_liquidity_hints_inflight_htlcs(self):
self.prepare_graph() self.prepare_graph()
amount_to_send = 100000 amount_to_send = 100000

9
electrum/tests/test_lntransport.py

@ -13,7 +13,7 @@ from .test_bitcoin import needs_test_with_all_chacha20_implementations
class TestLNTransport(ElectrumTestCase): class TestLNTransport(ElectrumTestCase):
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_responder(self): async def test_responder(self):
# local static # local static
ls_priv=bytes.fromhex('2121212121212121212121212121212121212121212121212121212121212121') ls_priv=bytes.fromhex('2121212121212121212121212121212121212121212121212121212121212121')
# ephemeral # ephemeral
@ -39,11 +39,10 @@ class TestLNTransport(ElectrumTestCase):
assert num_bytes == 66 assert num_bytes == 66
return bytes.fromhex('00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba') return bytes.fromhex('00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba')
transport = LNResponderTransport(ls_priv, Reader(), Writer()) transport = LNResponderTransport(ls_priv, Reader(), Writer())
asyncio.run_coroutine_threadsafe( await transport.handshake(epriv=e_priv)
transport.handshake(epriv=e_priv), self.asyncio_loop).result()
@needs_test_with_all_chacha20_implementations @needs_test_with_all_chacha20_implementations
def test_loop(self): async def test_loop(self):
responder_shaked = asyncio.Event() responder_shaked = asyncio.Event()
server_shaked = asyncio.Event() server_shaked = asyncio.Event()
responder_key = ECPrivkey.generate_random_key() responder_key = ECPrivkey.generate_random_key()
@ -98,4 +97,4 @@ class TestLNTransport(ElectrumTestCase):
server.close() server.close()
await server.wait_closed() await server.wait_closed()
asyncio.run_coroutine_threadsafe(f(), self.asyncio_loop).result() await f()

28
electrum/tests/test_network.py

@ -52,12 +52,12 @@ class TestNetwork(ElectrumTestCase):
super().tearDownClass() super().tearDownClass()
constants.set_mainnet() constants.set_mainnet()
def setUp(self): async def asyncSetUp(self):
super().setUp() await super().asyncSetUp()
self.config = SimpleConfig({'electrum_path': self.electrum_path}) self.config = SimpleConfig({'electrum_path': self.electrum_path})
self.interface = MockInterface(self.config) self.interface = MockInterface(self.config)
def test_fork_noconflict(self): async def test_fork_noconflict(self):
blockchain.blockchains = {} blockchain.blockchains = {}
self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}}) self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}})
def mock_connect(height): def mock_connect(height):
@ -68,11 +68,11 @@ class TestNetwork(ElectrumTestCase):
self.interface.q.put_nowait({'block_height': 5, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}}) self.interface.q.put_nowait({'block_height': 5, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}})
self.interface.q.put_nowait({'block_height': 6, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}}) self.interface.q.put_nowait({'block_height': 6, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}})
ifa = self.interface ifa = self.interface
fut = asyncio.run_coroutine_threadsafe(ifa.sync_until(8, next_height=7), util.get_asyncio_loop()) res = await ifa.sync_until(8, next_height=7)
self.assertEqual(('fork', 8), fut.result()) self.assertEqual(('fork', 8), res)
self.assertEqual(self.interface.q.qsize(), 0) self.assertEqual(self.interface.q.qsize(), 0)
def test_fork_conflict(self): async def test_fork_conflict(self):
blockchain.blockchains = {7: {'check': lambda bad_header: False}} blockchain.blockchains = {7: {'check': lambda bad_header: False}}
self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}}) self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}})
def mock_connect(height): def mock_connect(height):
@ -83,11 +83,11 @@ class TestNetwork(ElectrumTestCase):
self.interface.q.put_nowait({'block_height': 5, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}}) self.interface.q.put_nowait({'block_height': 5, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}})
self.interface.q.put_nowait({'block_height': 6, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}}) self.interface.q.put_nowait({'block_height': 6, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}})
ifa = self.interface ifa = self.interface
fut = asyncio.run_coroutine_threadsafe(ifa.sync_until(8, next_height=7), util.get_asyncio_loop()) res = await ifa.sync_until(8, next_height=7)
self.assertEqual(('fork', 8), fut.result()) self.assertEqual(('fork', 8), res)
self.assertEqual(self.interface.q.qsize(), 0) self.assertEqual(self.interface.q.qsize(), 0)
def test_can_connect_during_backward(self): async def test_can_connect_during_backward(self):
blockchain.blockchains = {} blockchain.blockchains = {}
self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}}) self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}})
def mock_connect(height): def mock_connect(height):
@ -97,8 +97,8 @@ class TestNetwork(ElectrumTestCase):
self.interface.q.put_nowait({'block_height': 3, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}}) self.interface.q.put_nowait({'block_height': 3, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}})
self.interface.q.put_nowait({'block_height': 4, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}}) self.interface.q.put_nowait({'block_height': 4, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}})
ifa = self.interface ifa = self.interface
fut = asyncio.run_coroutine_threadsafe(ifa.sync_until(8, next_height=4), util.get_asyncio_loop()) res = await ifa.sync_until(8, next_height=4)
self.assertEqual(('catchup', 5), fut.result()) self.assertEqual(('catchup', 5), res)
self.assertEqual(self.interface.q.qsize(), 0) self.assertEqual(self.interface.q.qsize(), 0)
def mock_fork(self, bad_header): def mock_fork(self, bad_header):
@ -107,7 +107,7 @@ class TestNetwork(ElectrumTestCase):
forkpoint_hash=sha256(str(forkpoint)).hex(), prev_hash=sha256(str(forkpoint-1)).hex()) forkpoint_hash=sha256(str(forkpoint)).hex(), prev_hash=sha256(str(forkpoint-1)).hex())
return b return b
def test_chain_false_during_binary(self): async def test_chain_false_during_binary(self):
blockchain.blockchains = {} blockchain.blockchains = {}
self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}}) self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}})
mock_connect = lambda height: height == 3 mock_connect = lambda height: height == 3
@ -118,8 +118,8 @@ class TestNetwork(ElectrumTestCase):
self.interface.q.put_nowait({'block_height': 5, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}}) self.interface.q.put_nowait({'block_height': 5, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}})
self.interface.q.put_nowait({'block_height': 6, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}}) self.interface.q.put_nowait({'block_height': 6, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}})
ifa = self.interface ifa = self.interface
fut = asyncio.run_coroutine_threadsafe(ifa.sync_until(8, next_height=6), util.get_asyncio_loop()) res = await ifa.sync_until(8, next_height=6)
self.assertEqual(('catchup', 7), fut.result()) self.assertEqual(('catchup', 7), res)
self.assertEqual(self.interface.q.qsize(), 0) self.assertEqual(self.interface.q.qsize(), 0)

264
electrum/tests/test_storage_upgrade.py

File diff suppressed because one or more lines are too long

30
electrum/tests/test_wallet.py

@ -158,7 +158,7 @@ class TestFiat(ElectrumTestCase):
class TestCreateRestoreWallet(WalletTestCase): class TestCreateRestoreWallet(WalletTestCase):
def test_create_new_wallet(self): async def test_create_new_wallet(self):
passphrase = 'mypassphrase' passphrase = 'mypassphrase'
password = 'mypassword' password = 'mypassword'
encrypt_file = True encrypt_file = True
@ -178,7 +178,7 @@ class TestCreateRestoreWallet(WalletTestCase):
self.assertEqual(d['seed'], wallet.keystore.get_seed(password)) self.assertEqual(d['seed'], wallet.keystore.get_seed(password))
self.assertEqual(encrypt_file, wallet.storage.is_encrypted()) self.assertEqual(encrypt_file, wallet.storage.is_encrypted())
def test_restore_wallet_from_text_mnemonic(self): async def test_restore_wallet_from_text_mnemonic(self):
text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
passphrase = 'mypassphrase' passphrase = 'mypassphrase'
password = 'mypassword' password = 'mypassword'
@ -196,7 +196,7 @@ class TestCreateRestoreWallet(WalletTestCase):
self.assertEqual(encrypt_file, wallet.storage.is_encrypted()) self.assertEqual(encrypt_file, wallet.storage.is_encrypted())
self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', wallet.get_receiving_addresses()[0]) self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', wallet.get_receiving_addresses()[0])
def test_restore_wallet_from_text_no_storage(self): async def test_restore_wallet_from_text_no_storage(self):
text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
d = restore_wallet_from_text( d = restore_wallet_from_text(
text, text,
@ -209,28 +209,28 @@ class TestCreateRestoreWallet(WalletTestCase):
self.assertEqual(text, wallet.keystore.get_seed(None)) self.assertEqual(text, wallet.keystore.get_seed(None))
self.assertEqual('bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', wallet.get_receiving_addresses()[0]) self.assertEqual('bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', wallet.get_receiving_addresses()[0])
def test_restore_wallet_from_text_xpub(self): async def test_restore_wallet_from_text_xpub(self):
text = 'zpub6nydoME6CFdJtMpzHW5BNoPz6i6XbeT9qfz72wsRqGdgGEYeivso6xjfw8cGcCyHwF7BNW4LDuHF35XrZsovBLWMF4qXSjmhTXYiHbWqGLt' text = 'zpub6nydoME6CFdJtMpzHW5BNoPz6i6XbeT9qfz72wsRqGdgGEYeivso6xjfw8cGcCyHwF7BNW4LDuHF35XrZsovBLWMF4qXSjmhTXYiHbWqGLt'
d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config) d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config)
wallet = d['wallet'] # type: Standard_Wallet wallet = d['wallet'] # type: Standard_Wallet
self.assertEqual(text, wallet.keystore.get_master_public_key()) self.assertEqual(text, wallet.keystore.get_master_public_key())
self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', wallet.get_receiving_addresses()[0]) self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', wallet.get_receiving_addresses()[0])
def test_restore_wallet_from_text_xkey_that_is_also_a_valid_electrum_seed_by_chance(self): async def test_restore_wallet_from_text_xkey_that_is_also_a_valid_electrum_seed_by_chance(self):
text = 'yprvAJBpuoF4FKpK92ofzQ7ge6VJMtorow3maAGPvPGj38ggr2xd1xCrC9ojUVEf9jhW5L9SPu6fU2U3o64cLrRQ83zaQGNa6YP3ajZS6hHNPXj' text = 'yprvAJBpuoF4FKpK92ofzQ7ge6VJMtorow3maAGPvPGj38ggr2xd1xCrC9ojUVEf9jhW5L9SPu6fU2U3o64cLrRQ83zaQGNa6YP3ajZS6hHNPXj'
d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config) d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config)
wallet = d['wallet'] # type: Standard_Wallet wallet = d['wallet'] # type: Standard_Wallet
self.assertEqual(text, wallet.keystore.get_master_private_key(password=None)) self.assertEqual(text, wallet.keystore.get_master_private_key(password=None))
self.assertEqual('3Pa4hfP3LFWqa2nfphYaF7PZfdJYNusAnp', wallet.get_receiving_addresses()[0]) self.assertEqual('3Pa4hfP3LFWqa2nfphYaF7PZfdJYNusAnp', wallet.get_receiving_addresses()[0])
def test_restore_wallet_from_text_xprv(self): async def test_restore_wallet_from_text_xprv(self):
text = 'zprvAZzHPqhCMt51fskXBUYB1fTFYgG3CBjJUT4WEZTpGw6hPSDWBPZYZARC5sE9xAcX8NeWvvucFws8vZxEa65RosKAhy7r5MsmKTxr3hmNmea' text = 'zprvAZzHPqhCMt51fskXBUYB1fTFYgG3CBjJUT4WEZTpGw6hPSDWBPZYZARC5sE9xAcX8NeWvvucFws8vZxEa65RosKAhy7r5MsmKTxr3hmNmea'
d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config) d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config)
wallet = d['wallet'] # type: Standard_Wallet wallet = d['wallet'] # type: Standard_Wallet
self.assertEqual(text, wallet.keystore.get_master_private_key(password=None)) self.assertEqual(text, wallet.keystore.get_master_private_key(password=None))
self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', wallet.get_receiving_addresses()[0]) self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', wallet.get_receiving_addresses()[0])
def test_restore_wallet_from_text_addresses(self): async def test_restore_wallet_from_text_addresses(self):
text = 'bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw bc1qnp78h78vp92pwdwq5xvh8eprlga5q8gu66960c' text = 'bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw bc1qnp78h78vp92pwdwq5xvh8eprlga5q8gu66960c'
d = restore_wallet_from_text(text, path=self.wallet_path, config=self.config) d = restore_wallet_from_text(text, path=self.wallet_path, config=self.config)
wallet = d['wallet'] # type: Imported_Wallet wallet = d['wallet'] # type: Imported_Wallet
@ -240,7 +240,7 @@ class TestCreateRestoreWallet(WalletTestCase):
wallet.delete_address('bc1qnp78h78vp92pwdwq5xvh8eprlga5q8gu66960c') wallet.delete_address('bc1qnp78h78vp92pwdwq5xvh8eprlga5q8gu66960c')
self.assertEqual(1, len(wallet.get_receiving_addresses())) self.assertEqual(1, len(wallet.get_receiving_addresses()))
def test_restore_wallet_from_text_privkeys(self): async def test_restore_wallet_from_text_privkeys(self):
text = 'p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL p2wpkh:L24GxnN7NNUAfCXA6hFzB1jt59fYAAiFZMcLaJ2ZSawGpM3uqhb1' text = 'p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL p2wpkh:L24GxnN7NNUAfCXA6hFzB1jt59fYAAiFZMcLaJ2ZSawGpM3uqhb1'
d = restore_wallet_from_text(text, path=self.wallet_path, config=self.config) d = restore_wallet_from_text(text, path=self.wallet_path, config=self.config)
wallet = d['wallet'] # type: Imported_Wallet wallet = d['wallet'] # type: Imported_Wallet
@ -256,13 +256,7 @@ class TestCreateRestoreWallet(WalletTestCase):
class TestWalletPassword(WalletTestCase): class TestWalletPassword(WalletTestCase):
def setUp(self): async def test_update_password_of_imported_wallet(self):
super().setUp()
def tearDown(self):
super().tearDown()
def test_update_password_of_imported_wallet(self):
wallet_str = '{"addr_history":{"1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr":[],"15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA":[],"1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6":[]},"addresses":{"change":[],"receiving":["1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr","1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6","15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA"]},"keystore":{"keypairs":{"0344b1588589958b0bcab03435061539e9bcf54677c104904044e4f8901f4ebdf5":"L2sED74axVXC4H8szBJ4rQJrkfem7UMc6usLCPUoEWxDCFGUaGUM","0389508c13999d08ffae0f434a085f4185922d64765c0bff2f66e36ad7f745cc5f":"L3Gi6EQLvYw8gEEUckmqawkevfj9s8hxoQDFveQJGZHTfyWnbk1U","04575f52b82f159fa649d2a4c353eb7435f30206f0a6cb9674fbd659f45082c37d559ffd19bea9c0d3b7dcc07a7b79f4cffb76026d5d4dff35341efe99056e22d2":"5JyVyXU1LiRXATvRTQvR9Kp8Rx1X84j2x49iGkjSsXipydtByUq"},"type":"imported"},"pruned_txo":{},"seed_version":13,"stored_height":-1,"transactions":{},"tx_fees":{},"txi":{},"txo":{},"use_encryption":false,"verified_tx3":{},"wallet_type":"standard","winpos-qt":[100,100,840,405]}' wallet_str = '{"addr_history":{"1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr":[],"15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA":[],"1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6":[]},"addresses":{"change":[],"receiving":["1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr","1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6","15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA"]},"keystore":{"keypairs":{"0344b1588589958b0bcab03435061539e9bcf54677c104904044e4f8901f4ebdf5":"L2sED74axVXC4H8szBJ4rQJrkfem7UMc6usLCPUoEWxDCFGUaGUM","0389508c13999d08ffae0f434a085f4185922d64765c0bff2f66e36ad7f745cc5f":"L3Gi6EQLvYw8gEEUckmqawkevfj9s8hxoQDFveQJGZHTfyWnbk1U","04575f52b82f159fa649d2a4c353eb7435f30206f0a6cb9674fbd659f45082c37d559ffd19bea9c0d3b7dcc07a7b79f4cffb76026d5d4dff35341efe99056e22d2":"5JyVyXU1LiRXATvRTQvR9Kp8Rx1X84j2x49iGkjSsXipydtByUq"},"type":"imported"},"pruned_txo":{},"seed_version":13,"stored_height":-1,"transactions":{},"tx_fees":{},"txi":{},"txo":{},"use_encryption":false,"verified_tx3":{},"wallet_type":"standard","winpos-qt":[100,100,840,405]}'
db = WalletDB(wallet_str, manual_upgrades=False) db = WalletDB(wallet_str, manual_upgrades=False)
storage = WalletStorage(self.wallet_path) storage = WalletStorage(self.wallet_path)
@ -278,7 +272,7 @@ class TestWalletPassword(WalletTestCase):
wallet.check_password("wrong password") wallet.check_password("wrong password")
wallet.check_password("1234") wallet.check_password("1234")
def test_update_password_of_standard_wallet(self): async def test_update_password_of_standard_wallet(self):
wallet_str = '''{"addr_history":{"12ECgkzK6gHouKAZ7QiooYBuk1CgJLJxes":[],"12iR43FPb5M7sw4Mcrr5y1nHKepg9EtZP1":[],"13HT1pfWctsSXVFzF76uYuVdQvcAQ2MAgB":[],"13kG9WH9JqS7hyCcVL1ssLdNv4aXocQY9c":[],"14Tf3qiiHJXStSU4KmienAhHfHq7FHpBpz":[],"14gmBxYV97mzYwWdJSJ3MTLbTHVegaKrcA":[],"15FGuHvRssu1r8fCw98vrbpfc3M4xs5FAV":[],"17oJzweA2gn6SDjsKgA9vUD5ocT1sSnr2Z":[],"18hNcSjZzRcRP6J2bfFRxp9UfpMoC4hGTv":[],"18n9PFxBjmKCGhd4PCDEEqYsi2CsnEfn2B":[],"19a98ZfEezDNbCwidVigV5PAJwrR2kw4Jz":[],"19z3j2ELqbg2pR87byCCt3BCyKR7rc3q8G":[],"1A3XSmvLQvePmvm7yctsGkBMX9ZKKXLrVq":[],"1CmhFe2BN1h9jheFpJf4v39XNPj8F9U6d":[],"1DuphhHUayKzbkdvjVjf5dtjn2ACkz4zEs":[],"1E4ygSNJpWL2uPXZHBptmU2LqwZTqb1Ado":[],"1GTDSjkVc9vaaBBBGNVqTANHJBcoT5VW9z":[],"1GWqgpThAuSq3tDg6uCoLQxPXQNnU8jZ52":[],"1GhmpwqSF5cqNgdr9oJMZx8dKxPRo4pYPP":[],"1J5TTUQKhwehEACw6Jjte1E22FVrbeDmpv":[],"1JWySzjzJhsETUUcqVZHuvQLA7pfFfmesb":[],"1KQHxcy3QUHAWMHKUtJjqD9cMKXcY2RTwZ":[],"1KoxZfc2KsgovjGDxwqanbFEA76uxgYH4G":[],"1KqVEPXdpbYvEbwsZcEKkrA4A2jsgj9hYN":[],"1N16yDSYe76c5A3CoVoWAKxHeAUc8Jhf9J":[],"1Pm8JBhzUJDqeQQKrmnop1Frr4phe1jbTt":[]},"addresses":{"change":["1GhmpwqSF5cqNgdr9oJMZx8dKxPRo4pYPP","1GTDSjkVc9vaaBBBGNVqTANHJBcoT5VW9z","15FGuHvRssu1r8fCw98vrbpfc3M4xs5FAV","1A3XSmvLQvePmvm7yctsGkBMX9ZKKXLrVq","19z3j2ELqbg2pR87byCCt3BCyKR7rc3q8G","1JWySzjzJhsETUUcqVZHuvQLA7pfFfmesb"],"receiving":["14gmBxYV97mzYwWdJSJ3MTLbTHVegaKrcA","13HT1pfWctsSXVFzF76uYuVdQvcAQ2MAgB","19a98ZfEezDNbCwidVigV5PAJwrR2kw4Jz","1J5TTUQKhwehEACw6Jjte1E22FVrbeDmpv","1Pm8JBhzUJDqeQQKrmnop1Frr4phe1jbTt","13kG9WH9JqS7hyCcVL1ssLdNv4aXocQY9c","1KQHxcy3QUHAWMHKUtJjqD9cMKXcY2RTwZ","12ECgkzK6gHouKAZ7QiooYBuk1CgJLJxes","12iR43FPb5M7sw4Mcrr5y1nHKepg9EtZP1","14Tf3qiiHJXStSU4KmienAhHfHq7FHpBpz","1KqVEPXdpbYvEbwsZcEKkrA4A2jsgj9hYN","17oJzweA2gn6SDjsKgA9vUD5ocT1sSnr2Z","1E4ygSNJpWL2uPXZHBptmU2LqwZTqb1Ado","18hNcSjZzRcRP6J2bfFRxp9UfpMoC4hGTv","1KoxZfc2KsgovjGDxwqanbFEA76uxgYH4G","18n9PFxBjmKCGhd4PCDEEqYsi2CsnEfn2B","1CmhFe2BN1h9jheFpJf4v39XNPj8F9U6d","1DuphhHUayKzbkdvjVjf5dtjn2ACkz4zEs","1GWqgpThAuSq3tDg6uCoLQxPXQNnU8jZ52","1N16yDSYe76c5A3CoVoWAKxHeAUc8Jhf9J"]},"keystore":{"seed":"cereal wise two govern top pet frog nut rule sketch bundle logic","type":"bip32","xprv":"xprv9s21ZrQH143K29XjRjUs6MnDB9wXjXbJP2kG1fnRk8zjdDYWqVkQYUqaDtgZp5zPSrH5PZQJs8sU25HrUgT1WdgsPU8GbifKurtMYg37d4v","xpub":"xpub661MyMwAqRbcEdcCXm1sTViwjBn28zK9kFfrp4C3JUXiW1sfP34f6HA45B9yr7EH5XGzWuTfMTdqpt9XPrVQVUdgiYb5NW9m8ij1FSZgGBF"},"pruned_txo":{},"seed_type":"standard","seed_version":13,"stored_height":-1,"transactions":{},"tx_fees":{},"txi":{},"txo":{},"use_encryption":false,"verified_tx3":{},"wallet_type":"standard","winpos-qt":[619,310,840,405]}''' wallet_str = '''{"addr_history":{"12ECgkzK6gHouKAZ7QiooYBuk1CgJLJxes":[],"12iR43FPb5M7sw4Mcrr5y1nHKepg9EtZP1":[],"13HT1pfWctsSXVFzF76uYuVdQvcAQ2MAgB":[],"13kG9WH9JqS7hyCcVL1ssLdNv4aXocQY9c":[],"14Tf3qiiHJXStSU4KmienAhHfHq7FHpBpz":[],"14gmBxYV97mzYwWdJSJ3MTLbTHVegaKrcA":[],"15FGuHvRssu1r8fCw98vrbpfc3M4xs5FAV":[],"17oJzweA2gn6SDjsKgA9vUD5ocT1sSnr2Z":[],"18hNcSjZzRcRP6J2bfFRxp9UfpMoC4hGTv":[],"18n9PFxBjmKCGhd4PCDEEqYsi2CsnEfn2B":[],"19a98ZfEezDNbCwidVigV5PAJwrR2kw4Jz":[],"19z3j2ELqbg2pR87byCCt3BCyKR7rc3q8G":[],"1A3XSmvLQvePmvm7yctsGkBMX9ZKKXLrVq":[],"1CmhFe2BN1h9jheFpJf4v39XNPj8F9U6d":[],"1DuphhHUayKzbkdvjVjf5dtjn2ACkz4zEs":[],"1E4ygSNJpWL2uPXZHBptmU2LqwZTqb1Ado":[],"1GTDSjkVc9vaaBBBGNVqTANHJBcoT5VW9z":[],"1GWqgpThAuSq3tDg6uCoLQxPXQNnU8jZ52":[],"1GhmpwqSF5cqNgdr9oJMZx8dKxPRo4pYPP":[],"1J5TTUQKhwehEACw6Jjte1E22FVrbeDmpv":[],"1JWySzjzJhsETUUcqVZHuvQLA7pfFfmesb":[],"1KQHxcy3QUHAWMHKUtJjqD9cMKXcY2RTwZ":[],"1KoxZfc2KsgovjGDxwqanbFEA76uxgYH4G":[],"1KqVEPXdpbYvEbwsZcEKkrA4A2jsgj9hYN":[],"1N16yDSYe76c5A3CoVoWAKxHeAUc8Jhf9J":[],"1Pm8JBhzUJDqeQQKrmnop1Frr4phe1jbTt":[]},"addresses":{"change":["1GhmpwqSF5cqNgdr9oJMZx8dKxPRo4pYPP","1GTDSjkVc9vaaBBBGNVqTANHJBcoT5VW9z","15FGuHvRssu1r8fCw98vrbpfc3M4xs5FAV","1A3XSmvLQvePmvm7yctsGkBMX9ZKKXLrVq","19z3j2ELqbg2pR87byCCt3BCyKR7rc3q8G","1JWySzjzJhsETUUcqVZHuvQLA7pfFfmesb"],"receiving":["14gmBxYV97mzYwWdJSJ3MTLbTHVegaKrcA","13HT1pfWctsSXVFzF76uYuVdQvcAQ2MAgB","19a98ZfEezDNbCwidVigV5PAJwrR2kw4Jz","1J5TTUQKhwehEACw6Jjte1E22FVrbeDmpv","1Pm8JBhzUJDqeQQKrmnop1Frr4phe1jbTt","13kG9WH9JqS7hyCcVL1ssLdNv4aXocQY9c","1KQHxcy3QUHAWMHKUtJjqD9cMKXcY2RTwZ","12ECgkzK6gHouKAZ7QiooYBuk1CgJLJxes","12iR43FPb5M7sw4Mcrr5y1nHKepg9EtZP1","14Tf3qiiHJXStSU4KmienAhHfHq7FHpBpz","1KqVEPXdpbYvEbwsZcEKkrA4A2jsgj9hYN","17oJzweA2gn6SDjsKgA9vUD5ocT1sSnr2Z","1E4ygSNJpWL2uPXZHBptmU2LqwZTqb1Ado","18hNcSjZzRcRP6J2bfFRxp9UfpMoC4hGTv","1KoxZfc2KsgovjGDxwqanbFEA76uxgYH4G","18n9PFxBjmKCGhd4PCDEEqYsi2CsnEfn2B","1CmhFe2BN1h9jheFpJf4v39XNPj8F9U6d","1DuphhHUayKzbkdvjVjf5dtjn2ACkz4zEs","1GWqgpThAuSq3tDg6uCoLQxPXQNnU8jZ52","1N16yDSYe76c5A3CoVoWAKxHeAUc8Jhf9J"]},"keystore":{"seed":"cereal wise two govern top pet frog nut rule sketch bundle logic","type":"bip32","xprv":"xprv9s21ZrQH143K29XjRjUs6MnDB9wXjXbJP2kG1fnRk8zjdDYWqVkQYUqaDtgZp5zPSrH5PZQJs8sU25HrUgT1WdgsPU8GbifKurtMYg37d4v","xpub":"xpub661MyMwAqRbcEdcCXm1sTViwjBn28zK9kFfrp4C3JUXiW1sfP34f6HA45B9yr7EH5XGzWuTfMTdqpt9XPrVQVUdgiYb5NW9m8ij1FSZgGBF"},"pruned_txo":{},"seed_type":"standard","seed_version":13,"stored_height":-1,"transactions":{},"tx_fees":{},"txi":{},"txo":{},"use_encryption":false,"verified_tx3":{},"wallet_type":"standard","winpos-qt":[619,310,840,405]}'''
db = WalletDB(wallet_str, manual_upgrades=False) db = WalletDB(wallet_str, manual_upgrades=False)
storage = WalletStorage(self.wallet_path) storage = WalletStorage(self.wallet_path)
@ -293,12 +287,12 @@ class TestWalletPassword(WalletTestCase):
wallet.check_password("wrong password") wallet.check_password("wrong password")
wallet.check_password("1234") wallet.check_password("1234")
def test_update_password_with_app_restarts(self): async def test_update_password_with_app_restarts(self):
wallet_str = '{"addr_history":{"1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr":[],"15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA":[],"1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6":[]},"addresses":{"change":[],"receiving":["1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr","1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6","15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA"]},"keystore":{"keypairs":{"0344b1588589958b0bcab03435061539e9bcf54677c104904044e4f8901f4ebdf5":"L2sED74axVXC4H8szBJ4rQJrkfem7UMc6usLCPUoEWxDCFGUaGUM","0389508c13999d08ffae0f434a085f4185922d64765c0bff2f66e36ad7f745cc5f":"L3Gi6EQLvYw8gEEUckmqawkevfj9s8hxoQDFveQJGZHTfyWnbk1U","04575f52b82f159fa649d2a4c353eb7435f30206f0a6cb9674fbd659f45082c37d559ffd19bea9c0d3b7dcc07a7b79f4cffb76026d5d4dff35341efe99056e22d2":"5JyVyXU1LiRXATvRTQvR9Kp8Rx1X84j2x49iGkjSsXipydtByUq"},"type":"imported"},"pruned_txo":{},"seed_version":13,"stored_height":-1,"transactions":{},"tx_fees":{},"txi":{},"txo":{},"use_encryption":false,"verified_tx3":{},"wallet_type":"standard","winpos-qt":[100,100,840,405]}' wallet_str = '{"addr_history":{"1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr":[],"15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA":[],"1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6":[]},"addresses":{"change":[],"receiving":["1364Js2VG66BwRdkaoxAaFtdPb1eQgn8Dr","1Exet2BhHsFxKTwhnfdsBMkPYLGvobxuW6","15CyDgLffJsJgQrhcyooFH4gnVDG82pUrA"]},"keystore":{"keypairs":{"0344b1588589958b0bcab03435061539e9bcf54677c104904044e4f8901f4ebdf5":"L2sED74axVXC4H8szBJ4rQJrkfem7UMc6usLCPUoEWxDCFGUaGUM","0389508c13999d08ffae0f434a085f4185922d64765c0bff2f66e36ad7f745cc5f":"L3Gi6EQLvYw8gEEUckmqawkevfj9s8hxoQDFveQJGZHTfyWnbk1U","04575f52b82f159fa649d2a4c353eb7435f30206f0a6cb9674fbd659f45082c37d559ffd19bea9c0d3b7dcc07a7b79f4cffb76026d5d4dff35341efe99056e22d2":"5JyVyXU1LiRXATvRTQvR9Kp8Rx1X84j2x49iGkjSsXipydtByUq"},"type":"imported"},"pruned_txo":{},"seed_version":13,"stored_height":-1,"transactions":{},"tx_fees":{},"txi":{},"txo":{},"use_encryption":false,"verified_tx3":{},"wallet_type":"standard","winpos-qt":[100,100,840,405]}'
db = WalletDB(wallet_str, manual_upgrades=False) db = WalletDB(wallet_str, manual_upgrades=False)
storage = WalletStorage(self.wallet_path) storage = WalletStorage(self.wallet_path)
wallet = Wallet(db, storage, config=self.config) wallet = Wallet(db, storage, config=self.config)
asyncio.run_coroutine_threadsafe(wallet.stop(), self.asyncio_loop).result() await wallet.stop()
storage = WalletStorage(self.wallet_path) storage = WalletStorage(self.wallet_path)
# if storage.is_encrypted(): # if storage.is_encrypted():

172
electrum/tests/test_wallet_vertical.py

@ -89,7 +89,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.config = SimpleConfig({'electrum_path': self.electrum_path}) self.config = SimpleConfig({'electrum_path': self.electrum_path})
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_electrum_seed_standard(self, mock_save_db): async def test_electrum_seed_standard(self, mock_save_db):
seed_words = 'cycle rocket west magnet parrot shuffle foot correct salt library feed song' seed_words = 'cycle rocket west magnet parrot shuffle foot correct salt library feed song'
self.assertEqual(seed_type(seed_words), 'standard') self.assertEqual(seed_type(seed_words), 'standard')
@ -108,7 +108,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '1KSezYMhAJMWqFbVFB2JshYg69UpmEXR4D') self.assertEqual(w.get_change_addresses()[0], '1KSezYMhAJMWqFbVFB2JshYg69UpmEXR4D')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_electrum_seed_segwit(self, mock_save_db): async def test_electrum_seed_segwit(self, mock_save_db):
seed_words = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' seed_words = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
self.assertEqual(seed_type(seed_words), 'segwit') self.assertEqual(seed_type(seed_words), 'segwit')
@ -130,7 +130,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
ks.get_lightning_xprv(None)) ks.get_lightning_xprv(None))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_electrum_seed_segwit_passphrase(self, mock_save_db): async def test_electrum_seed_segwit_passphrase(self, mock_save_db):
seed_words = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' seed_words = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver'
self.assertEqual(seed_type(seed_words), 'segwit') self.assertEqual(seed_type(seed_words), 'segwit')
@ -152,7 +152,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
ks.get_lightning_xprv(None)) ks.get_lightning_xprv(None))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_electrum_seed_old(self, mock_save_db): async def test_electrum_seed_old(self, mock_save_db):
seed_words = 'powerful random nobody notice nothing important anyway look away hidden message over' seed_words = 'powerful random nobody notice nothing important anyway look away hidden message over'
self.assertEqual(seed_type(seed_words), 'old') self.assertEqual(seed_type(seed_words), 'old')
@ -170,7 +170,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '1KRW8pH6HFHZh889VDq6fEKvmrsmApwNfe') self.assertEqual(w.get_change_addresses()[0], '1KRW8pH6HFHZh889VDq6fEKvmrsmApwNfe')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_electrum_seed_2fa_legacy_pre27(self, mock_save_db): async def test_electrum_seed_2fa_legacy_pre27(self, mock_save_db):
# pre-version-2.7 2fa seed # pre-version-2.7 2fa seed
seed_words = 'bind clever room kidney crucial sausage spy edit canvas soul liquid ribbon slam open alpha suffer gate relax voice carpet law hill woman tonight abstract' seed_words = 'bind clever room kidney crucial sausage spy edit canvas soul liquid ribbon slam open alpha suffer gate relax voice carpet law hill woman tonight abstract'
self.assertEqual(seed_type(seed_words), '2fa') self.assertEqual(seed_type(seed_words), '2fa')
@ -205,7 +205,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '3Ke6pKrmtSyyQaMob1ES4pk8siAAkRmst9') self.assertEqual(w.get_change_addresses()[0], '3Ke6pKrmtSyyQaMob1ES4pk8siAAkRmst9')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_electrum_seed_2fa_legacy_post27(self, mock_save_db): async def test_electrum_seed_2fa_legacy_post27(self, mock_save_db):
# post-version-2.7 2fa seed # post-version-2.7 2fa seed
seed_words = 'kiss live scene rude gate step hip quarter bunker oxygen motor glove' seed_words = 'kiss live scene rude gate step hip quarter bunker oxygen motor glove'
self.assertEqual(seed_type(seed_words), '2fa') self.assertEqual(seed_type(seed_words), '2fa')
@ -240,7 +240,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '3PeZEcumRqHSPNN43hd4yskGEBdzXgY8Cy') self.assertEqual(w.get_change_addresses()[0], '3PeZEcumRqHSPNN43hd4yskGEBdzXgY8Cy')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_electrum_seed_2fa_segwit(self, mock_save_db): async def test_electrum_seed_2fa_segwit(self, mock_save_db):
seed_words = 'universe topic remind silver february ranch shine worth innocent cattle enhance wise' seed_words = 'universe topic remind silver february ranch shine worth innocent cattle enhance wise'
self.assertEqual(seed_type(seed_words), '2fa_segwit') self.assertEqual(seed_type(seed_words), '2fa_segwit')
@ -274,7 +274,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1qd4q50nft7kxm9yglfnpup9ed2ukj3tkxp793y0zya8dc9m39jcwq308dxz') self.assertEqual(w.get_change_addresses()[0], 'bc1qd4q50nft7kxm9yglfnpup9ed2ukj3tkxp793y0zya8dc9m39jcwq308dxz')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_bip39_seed_bip44_standard(self, mock_save_db): async def test_bip39_seed_bip44_standard(self, mock_save_db):
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial' seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
@ -293,7 +293,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '1GG5bVeWgAp5XW7JLCphse14QaC4qiHyWn') self.assertEqual(w.get_change_addresses()[0], '1GG5bVeWgAp5XW7JLCphse14QaC4qiHyWn')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_bip39_seed_bip44_standard_passphrase(self, mock_save_db): async def test_bip39_seed_bip44_standard_passphrase(self, mock_save_db):
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial' seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
@ -312,7 +312,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '1H4QD1rg2zQJ4UjuAVJr5eW1fEM8WMqyxh') self.assertEqual(w.get_change_addresses()[0], '1H4QD1rg2zQJ4UjuAVJr5eW1fEM8WMqyxh')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_bip39_seed_bip49_p2sh_segwit(self, mock_save_db): async def test_bip39_seed_bip49_p2sh_segwit(self, mock_save_db):
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial' seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
@ -331,7 +331,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '3KaBTcviBLEJajTEMstsA2GWjYoPzPK7Y7') self.assertEqual(w.get_change_addresses()[0], '3KaBTcviBLEJajTEMstsA2GWjYoPzPK7Y7')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_bip39_seed_bip84_native_segwit(self, mock_save_db): async def test_bip39_seed_bip84_native_segwit(self, mock_save_db):
# test case from bip84 # test case from bip84
seed_words = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' seed_words = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
@ -351,7 +351,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1q8c6fshw2dlwun7ekn9qwf37cu2rn755upcp6el') self.assertEqual(w.get_change_addresses()[0], 'bc1q8c6fshw2dlwun7ekn9qwf37cu2rn755upcp6el')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_electrum_multisig_seed_standard(self, mock_save_db): async def test_electrum_multisig_seed_standard(self, mock_save_db):
seed_words = 'blast uniform dragon fiscal ensure vast young utility dinosaur abandon rookie sure' seed_words = 'blast uniform dragon fiscal ensure vast young utility dinosaur abandon rookie sure'
self.assertEqual(seed_type(seed_words), 'standard') self.assertEqual(seed_type(seed_words), 'standard')
@ -373,7 +373,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '36XWwEHrrVCLnhjK5MrVVGmUHghr9oWTN1') self.assertEqual(w.get_change_addresses()[0], '36XWwEHrrVCLnhjK5MrVVGmUHghr9oWTN1')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_electrum_multisig_seed_segwit(self, mock_save_db): async def test_electrum_multisig_seed_segwit(self, mock_save_db):
seed_words = 'snow nest raise royal more walk demise rotate smooth spirit canyon gun' seed_words = 'snow nest raise royal more walk demise rotate smooth spirit canyon gun'
self.assertEqual(seed_type(seed_words), 'segwit') self.assertEqual(seed_type(seed_words), 'segwit')
@ -395,7 +395,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1qxqf840dqswcmu7a8v82fj6ej0msx08flvuy6kngr7axstjcaq6us9hrehd') self.assertEqual(w.get_change_addresses()[0], 'bc1qxqf840dqswcmu7a8v82fj6ej0msx08flvuy6kngr7axstjcaq6us9hrehd')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_bip39_multisig_seed_bip45_standard(self, mock_save_db): async def test_bip39_multisig_seed_bip45_standard(self, mock_save_db):
seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial' seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
@ -418,7 +418,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '3FGyDuxgUDn2pSZe5xAJH1yUwSdhzDMyEE') self.assertEqual(w.get_change_addresses()[0], '3FGyDuxgUDn2pSZe5xAJH1yUwSdhzDMyEE')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_bip39_multisig_seed_p2sh_segwit(self, mock_save_db): async def test_bip39_multisig_seed_p2sh_segwit(self, mock_save_db):
# bip39 seed: pulse mixture jazz invite dune enrich minor weapon mosquito flight fly vapor # bip39 seed: pulse mixture jazz invite dune enrich minor weapon mosquito flight fly vapor
# der: m/49'/0'/0' # der: m/49'/0'/0'
# NOTE: there is currently no bip43 standard derivation path for p2wsh-p2sh # NOTE: there is currently no bip43 standard derivation path for p2wsh-p2sh
@ -439,7 +439,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '39RhtDchc6igmx5tyoimhojFL1ZbQBrXa6') self.assertEqual(w.get_change_addresses()[0], '39RhtDchc6igmx5tyoimhojFL1ZbQBrXa6')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_bip32_extended_version_bytes(self, mock_save_db): async def test_bip32_extended_version_bytes(self, mock_save_db):
seed_words = 'crouch dumb relax small truck age shine pink invite spatial object tenant' seed_words = 'crouch dumb relax small truck age shine pink invite spatial object tenant'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
bip32_seed = keystore.bip39_to_seed(seed_words, '') bip32_seed = keystore.bip39_to_seed(seed_words, '')
@ -498,7 +498,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1q0fj5mra96hhnum80kllklc52zqn6kppt3hyzr49yhr3ecr42z3tsrkg3gs') self.assertEqual(w.get_change_addresses()[0], 'bc1q0fj5mra96hhnum80kllklc52zqn6kppt3hyzr49yhr3ecr42z3tsrkg3gs')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_slip39_basic_3of6_bip44_standard(self, mock_save_db): async def test_slip39_basic_3of6_bip44_standard(self, mock_save_db):
""" """
BIP32 Root Key for passphrase "TREZOR": BIP32 Root Key for passphrase "TREZOR":
xprv9s21ZrQH143K2pMWi8jrTawHaj16uKk4CSbvo4Zt61tcrmuUDMx2o1Byzcr3saXNGNvHP8zZgXVdJHsXVdzYFPavxvCyaGyGr1WkAYG83ce xprv9s21ZrQH143K2pMWi8jrTawHaj16uKk4CSbvo4Zt61tcrmuUDMx2o1Byzcr3saXNGNvHP8zZgXVdJHsXVdzYFPavxvCyaGyGr1WkAYG83ce
@ -525,7 +525,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '1Aw4wpXsAyEHSgMZqPdyewoAtJqH9Jaso3') self.assertEqual(w.get_change_addresses()[0], '1Aw4wpXsAyEHSgMZqPdyewoAtJqH9Jaso3')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_slip39_basic_2of5_bip49_p2sh_segwit(self, mock_save_db): async def test_slip39_basic_2of5_bip49_p2sh_segwit(self, mock_save_db):
""" """
BIP32 Root Key for passphrase "TREZOR": BIP32 Root Key for passphrase "TREZOR":
xprv9s21ZrQH143K2o6EXEHpVy8TCYoMmkBnDCCESLdR2ieKwmcNG48ck2XJQY4waS7RUQcXqR9N7HnQbUVEDMWYyREdF1idQqxFHuCfK7fqFni xprv9s21ZrQH143K2o6EXEHpVy8TCYoMmkBnDCCESLdR2ieKwmcNG48ck2XJQY4waS7RUQcXqR9N7HnQbUVEDMWYyREdF1idQqxFHuCfK7fqFni
@ -551,7 +551,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '3FVvdRhR7racZhmcvrGAqX9eJoP8Sw3ypp') self.assertEqual(w.get_change_addresses()[0], '3FVvdRhR7racZhmcvrGAqX9eJoP8Sw3ypp')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_slip39_groups_128bit_bip84_native_segwit(self, mock_save_db): async def test_slip39_groups_128bit_bip84_native_segwit(self, mock_save_db):
""" """
BIP32 Root Key for passphrase "TREZOR": BIP32 Root Key for passphrase "TREZOR":
xprv9s21ZrQH143K3dzDLfeY3cMp23u5vDeFYftu5RPYZPucKc99mNEddU4w99GxdgUGcSfMpVDxhnR1XpJzZNXRN1m6xNgnzFS5MwMP6QyBRKV xprv9s21ZrQH143K3dzDLfeY3cMp23u5vDeFYftu5RPYZPucKc99mNEddU4w99GxdgUGcSfMpVDxhnR1XpJzZNXRN1m6xNgnzFS5MwMP6QyBRKV
@ -581,7 +581,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1q8l6hcvlczu4mtjcnlwhczw7vdxnvwccpjl3cwz') self.assertEqual(w.get_change_addresses()[0], 'bc1q8l6hcvlczu4mtjcnlwhczw7vdxnvwccpjl3cwz')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_slip39_groups_256bit_bip49_p2sh_segwit(self, mock_save_db): async def test_slip39_groups_256bit_bip49_p2sh_segwit(self, mock_save_db):
""" """
BIP32 Root Key for passphrase "TREZOR": BIP32 Root Key for passphrase "TREZOR":
xprv9s21ZrQH143K2UspC9FRPfQC9NcDB4HPkx1XG9UEtuceYtpcCZ6ypNZWdgfxQ9dAFVeD1F4Zg4roY7nZm2LB7THPD6kaCege3M7EuS8v85c xprv9s21ZrQH143K2UspC9FRPfQC9NcDB4HPkx1XG9UEtuceYtpcCZ6ypNZWdgfxQ9dAFVeD1F4Zg4roY7nZm2LB7THPD6kaCege3M7EuS8v85c
@ -617,7 +617,7 @@ class TestWalletKeystoreAddressIntegrityForTestnet(ElectrumTestCase):
self.config = SimpleConfig({'electrum_path': self.electrum_path}) self.config = SimpleConfig({'electrum_path': self.electrum_path})
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_bip39_multisig_seed_p2sh_segwit_testnet(self, mock_save_db): async def test_bip39_multisig_seed_p2sh_segwit_testnet(self, mock_save_db):
# bip39 seed: finish seminar arrange erosion sunny coil insane together pretty lunch lunch rose # bip39 seed: finish seminar arrange erosion sunny coil insane together pretty lunch lunch rose
# der: m/49'/1'/0' # der: m/49'/1'/0'
# NOTE: there is currently no bip43 standard derivation path for p2wsh-p2sh # NOTE: there is currently no bip43 standard derivation path for p2wsh-p2sh
@ -638,7 +638,7 @@ class TestWalletKeystoreAddressIntegrityForTestnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '2NFp9w8tbYYP9Ze2xQpeYBJQjx3gbXymHX7') self.assertEqual(w.get_change_addresses()[0], '2NFp9w8tbYYP9Ze2xQpeYBJQjx3gbXymHX7')
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_bip32_extended_version_bytes(self, mock_save_db): async def test_bip32_extended_version_bytes(self, mock_save_db):
seed_words = 'crouch dumb relax small truck age shine pink invite spatial object tenant' seed_words = 'crouch dumb relax small truck age shine pink invite spatial object tenant'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
bip32_seed = keystore.bip39_to_seed(seed_words, '') bip32_seed = keystore.bip39_to_seed(seed_words, '')
@ -711,7 +711,7 @@ class TestWalletSending(ElectrumTestCase):
return WalletIntegrityHelper.create_standard_wallet(ks, gap_limit=gap_limit, config=config) return WalletIntegrityHelper.create_standard_wallet(ks, gap_limit=gap_limit, config=config)
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_between_p2wpkh_and_compressed_p2pkh(self, mock_save_db): async def test_sending_between_p2wpkh_and_compressed_p2pkh(self, mock_save_db):
wallet1 = self.create_standard_wallet_from_seed('bitter grass shiver impose acquire brush forget axis eager alone wine silver') wallet1 = self.create_standard_wallet_from_seed('bitter grass shiver impose acquire brush forget axis eager alone wine silver')
wallet2 = self.create_standard_wallet_from_seed('cycle rocket west magnet parrot shuffle foot correct salt library feed song') wallet2 = self.create_standard_wallet_from_seed('cycle rocket west magnet parrot shuffle foot correct salt library feed song')
@ -767,7 +767,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual((0, 250000 - 5000 - 100000, 0), wallet2.get_balance()) self.assertEqual((0, 250000 - 5000 - 100000, 0), wallet2.get_balance())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_between_p2sh_2of3_and_uncompressed_p2pkh(self, mock_save_db): async def test_sending_between_p2sh_2of3_and_uncompressed_p2pkh(self, mock_save_db):
wallet1a = WalletIntegrityHelper.create_multisig_wallet( wallet1a = WalletIntegrityHelper.create_multisig_wallet(
[ [
keystore.from_seed('blast uniform dragon fiscal ensure vast young utility dinosaur abandon rookie sure', '', True), keystore.from_seed('blast uniform dragon fiscal ensure vast young utility dinosaur abandon rookie sure', '', True),
@ -847,7 +847,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual((0, 370000 - 5000 - 100000, 0), wallet2.get_balance()) self.assertEqual((0, 370000 - 5000 - 100000, 0), wallet2.get_balance())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_between_p2wsh_2of3_and_p2wsh_p2sh_2of2(self, mock_save_db): async def test_sending_between_p2wsh_2of3_and_p2wsh_p2sh_2of2(self, mock_save_db):
wallet1a = WalletIntegrityHelper.create_multisig_wallet( wallet1a = WalletIntegrityHelper.create_multisig_wallet(
[ [
keystore.from_seed('bitter grass shiver impose acquire brush forget axis eager alone wine silver', '', True), keystore.from_seed('bitter grass shiver impose acquire brush forget axis eager alone wine silver', '', True),
@ -956,7 +956,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual((0, 165000 - 5000 - 100000, 0), wallet2a.get_balance()) self.assertEqual((0, 165000 - 5000 - 100000, 0), wallet2a.get_balance())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_between_p2sh_1of2_and_p2wpkh_p2sh(self, mock_save_db): async def test_sending_between_p2sh_1of2_and_p2wpkh_p2sh(self, mock_save_db):
wallet1a = WalletIntegrityHelper.create_multisig_wallet( wallet1a = WalletIntegrityHelper.create_multisig_wallet(
[ [
keystore.from_seed('phone guilt ancient scan defy gasp off rotate approve ill word exchange', '', True), keystore.from_seed('phone guilt ancient scan defy gasp off rotate approve ill word exchange', '', True),
@ -1025,7 +1025,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual((0, 1000000 - 5000 - 300000, 0), wallet2.get_balance()) self.assertEqual((0, 1000000 - 5000 - 300000, 0), wallet2.get_balance())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_rbf(self, mock_save_db): async def test_rbf(self, mock_save_db):
self.maxDiff = None self.maxDiff = None
config = SimpleConfig({'electrum_path': self.electrum_path}) config = SimpleConfig({'electrum_path': self.electrum_path})
config.set_key('coin_chooser_output_rounding', False) config.set_key('coin_chooser_output_rounding', False)
@ -1266,7 +1266,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual((0, 18700, 0), wallet.get_balance()) self.assertEqual((0, 18700, 0), wallet.get_balance())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_cpfp_p2pkh(self, mock_save_db): async def test_cpfp_p2pkh(self, mock_save_db):
wallet = self.create_standard_wallet_from_seed('fold object utility erase deputy output stadium feed stereo usage modify bean') wallet = self.create_standard_wallet_from_seed('fold object utility erase deputy output stadium feed stereo usage modify bean')
# bootstrap wallet # bootstrap wallet
@ -1366,6 +1366,9 @@ class TestWalletSending(ElectrumTestCase):
class NetworkMock: class NetworkMock:
relay_fee = 1000 relay_fee = 1000
async def get_transaction(self, txid, timeout=None): async def get_transaction(self, txid, timeout=None):
return self._gettx(txid)
@staticmethod
def _gettx(txid):
if txid == "597098f9077cd2a7bf5bb2a03c9ae5fcd9d1f07c0891cb42cbb129cf9eaf57fd": if txid == "597098f9077cd2a7bf5bb2a03c9ae5fcd9d1f07c0891cb42cbb129cf9eaf57fd":
return "02000000000102a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540000000000fdffffffbdeb0175b1c51c96843d1952f7e1c49c1703717d7d020048d4de0a8eed94dad50000000000fdffffff03b2a00700000000001600140cd6c9f8ce0aa73d77fcf7f156c74f5cbec6906bb2a00700000000001600146435504ddc95e6019a90bb7dfc7ca81a88a8633106d790000000000016001444bd3017ee214370abf683abaa7f6204c9f40210024730440220652a04a2a301d9a031a034f3ae48174e204e17acf7bfc27f0dcab14243f73e2202207b29e964c434dfb2c515232d36566a40dccd4dd93ccb7fd15260ecbda10f0d9801210231994e564a0530068d17a9b0f85bec58d1352517a2861ea99e5b3070d2c5dbda02473044022072186473874919019da0e3d92b6e0aa4f88cba448ed5434615e5a3c8e2b7c42a02203ec05cef66960d5bc45d0f3d25675190cf8035b11a05ed4b719fd9c3a894899b012102f5fdca8c4e30ba0a1babf9cf9ebe62519b08aead351c349ed1ffc8316c24f542d7f61c00" return "02000000000102a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540000000000fdffffffbdeb0175b1c51c96843d1952f7e1c49c1703717d7d020048d4de0a8eed94dad50000000000fdffffff03b2a00700000000001600140cd6c9f8ce0aa73d77fcf7f156c74f5cbec6906bb2a00700000000001600146435504ddc95e6019a90bb7dfc7ca81a88a8633106d790000000000016001444bd3017ee214370abf683abaa7f6204c9f40210024730440220652a04a2a301d9a031a034f3ae48174e204e17acf7bfc27f0dcab14243f73e2202207b29e964c434dfb2c515232d36566a40dccd4dd93ccb7fd15260ecbda10f0d9801210231994e564a0530068d17a9b0f85bec58d1352517a2861ea99e5b3070d2c5dbda02473044022072186473874919019da0e3d92b6e0aa4f88cba448ed5434615e5a3c8e2b7c42a02203ec05cef66960d5bc45d0f3d25675190cf8035b11a05ed4b719fd9c3a894899b012102f5fdca8c4e30ba0a1babf9cf9ebe62519b08aead351c349ed1ffc8316c24f542d7f61c00"
else: else:
@ -1384,6 +1387,7 @@ class TestWalletSending(ElectrumTestCase):
wallet = self.create_standard_wallet_from_seed('mix total present junior leader live state athlete mistake crack wall valve', wallet = self.create_standard_wallet_from_seed('mix total present junior leader live state athlete mistake crack wall valve',
config=config) config=config)
wallet.network = NetworkMock() wallet.network = NetworkMock()
wallet._get_rawtx_from_network = NetworkMock._gettx
# bootstrap wallet # bootstrap wallet
funding_tx = Transaction('02000000000101a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540100000000fdffffff0220a1070000000000160014db44724ac632ae47ee5765954d64796dd5fec72708de3c000000000016001424b32aadb42a89016c4de8f11741c3b29b15f21c02473044022045cc6c1cc875cbb0c0d8fe323dc1de9716e49ed5659741b0fb3dd9a196894066022077c242640071d12ec5763c5870f482a4823d8713e4bd14353dd621ed29a7f96d012102aea8d439a0f79d8b58e8d7bda83009f587e1f3da350adaa484329bf47cd03465fef61c00') funding_tx = Transaction('02000000000101a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540100000000fdffffff0220a1070000000000160014db44724ac632ae47ee5765954d64796dd5fec72708de3c000000000016001424b32aadb42a89016c4de8f11741c3b29b15f21c02473044022045cc6c1cc875cbb0c0d8fe323dc1de9716e49ed5659741b0fb3dd9a196894066022077c242640071d12ec5763c5870f482a4823d8713e4bd14353dd621ed29a7f96d012102aea8d439a0f79d8b58e8d7bda83009f587e1f3da350adaa484329bf47cd03465fef61c00')
@ -1419,6 +1423,9 @@ class TestWalletSending(ElectrumTestCase):
class NetworkMock: class NetworkMock:
relay_fee = 1000 relay_fee = 1000
async def get_transaction(self, txid, timeout=None): async def get_transaction(self, txid, timeout=None):
return self._gettx(txid)
@staticmethod
def _gettx(txid):
if txid == "08557327673db61cc921e1a30826608599b86457836be3021105c13940d9a9a3": if txid == "08557327673db61cc921e1a30826608599b86457836be3021105c13940d9a9a3":
return "02000000000101a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540100000000fdffffff0220a1070000000000160014db44724ac632ae47ee5765954d64796dd5fec72708de3c000000000016001424b32aadb42a89016c4de8f11741c3b29b15f21c02473044022045cc6c1cc875cbb0c0d8fe323dc1de9716e49ed5659741b0fb3dd9a196894066022077c242640071d12ec5763c5870f482a4823d8713e4bd14353dd621ed29a7f96d012102aea8d439a0f79d8b58e8d7bda83009f587e1f3da350adaa484329bf47cd03465fef61c00" return "02000000000101a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540100000000fdffffff0220a1070000000000160014db44724ac632ae47ee5765954d64796dd5fec72708de3c000000000016001424b32aadb42a89016c4de8f11741c3b29b15f21c02473044022045cc6c1cc875cbb0c0d8fe323dc1de9716e49ed5659741b0fb3dd9a196894066022077c242640071d12ec5763c5870f482a4823d8713e4bd14353dd621ed29a7f96d012102aea8d439a0f79d8b58e8d7bda83009f587e1f3da350adaa484329bf47cd03465fef61c00"
else: else:
@ -1440,6 +1447,7 @@ class TestWalletSending(ElectrumTestCase):
gap_limit=4, gap_limit=4,
) )
wallet.network = NetworkMock() wallet.network = NetworkMock()
wallet._get_rawtx_from_network = NetworkMock._gettx
# bootstrap wallet # bootstrap wallet
funding_tx = Transaction('02000000000102c247447533b530cacc3e716aae84621857f04a483252374cbdccfdf8b4ef816b0000000000fdffffffc247447533b530cacc3e716aae84621857f04a483252374cbdccfdf8b4ef816b0100000000fdffffff01d63f0f00000000001600141ef4658adb12ec745a1a1fef6ab8897f04bade060247304402201dc5be86749d8ce33571a6f1a2f8bbfceba89b9dbf2b4683e66c8c17cf7df6090220729199516cb894569ebbe3e998d47fc74030231ed30f110c9babd8a9dc361115012102728251a5f5f55375eef3c14fe59ab0755ba4d5f388619895238033ac9b51aad20247304402202e5d416489c20810e96e931b98a84b0c0c4fc32d2d34d3470b7ee16810246a4c022040f86cf8030d2117d6487bbe6e23d68d6d70408b002d8055de1f33d038d3a0550121039c009e7e7dad07e74ec5a8ac9f9e3499420dd9fe9709995525c714170152512620f71c00') funding_tx = Transaction('02000000000102c247447533b530cacc3e716aae84621857f04a483252374cbdccfdf8b4ef816b0000000000fdffffffc247447533b530cacc3e716aae84621857f04a483252374cbdccfdf8b4ef816b0100000000fdffffff01d63f0f00000000001600141ef4658adb12ec745a1a1fef6ab8897f04bade060247304402201dc5be86749d8ce33571a6f1a2f8bbfceba89b9dbf2b4683e66c8c17cf7df6090220729199516cb894569ebbe3e998d47fc74030231ed30f110c9babd8a9dc361115012102728251a5f5f55375eef3c14fe59ab0755ba4d5f388619895238033ac9b51aad20247304402202e5d416489c20810e96e931b98a84b0c0c4fc32d2d34d3470b7ee16810246a4c022040f86cf8030d2117d6487bbe6e23d68d6d70408b002d8055de1f33d038d3a0550121039c009e7e7dad07e74ec5a8ac9f9e3499420dd9fe9709995525c714170152512620f71c00')
@ -1782,7 +1790,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual((0, 3_900_000, 0), wallet.get_balance()) self.assertEqual((0, 3_900_000, 0), wallet.get_balance())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_cpfp_p2wpkh(self, mock_save_db): async def test_cpfp_p2wpkh(self, mock_save_db):
wallet = self.create_standard_wallet_from_seed('frost repair depend effort salon ring foam oak cancel receive save usage') wallet = self.create_standard_wallet_from_seed('frost repair depend effort salon ring foam oak cancel receive save usage')
# bootstrap wallet # bootstrap wallet
@ -1814,7 +1822,7 @@ class TestWalletSending(ElectrumTestCase):
wallet.adb.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED) wallet.adb.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
self.assertEqual((0, funding_output_value - 50000, 0), wallet.get_balance()) self.assertEqual((0, funding_output_value - 50000, 0), wallet.get_balance())
def test_sweep_uncompressed_p2pk(self): async def test_sweep_uncompressed_p2pk(self):
class NetworkMock: class NetworkMock:
relay_fee = 1000 relay_fee = 1000
async def listunspent_for_scripthash(self, scripthash): async def listunspent_for_scripthash(self, scripthash):
@ -1831,9 +1839,7 @@ class TestWalletSending(ElectrumTestCase):
privkeys = ['93NQ7CFbwTPyKDJLXe97jczw33fiLijam2SCZL3Uinz1NSbHrTu',] privkeys = ['93NQ7CFbwTPyKDJLXe97jczw33fiLijam2SCZL3Uinz1NSbHrTu',]
network = NetworkMock() network = NetworkMock()
dest_addr = 'tb1q3ws2p0qjk5vrravv065xqlnkckvzcpclk79eu2' dest_addr = 'tb1q3ws2p0qjk5vrravv065xqlnkckvzcpclk79eu2'
sweep_coro = sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=5000, locktime=1325785, tx_version=1) tx = await sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=5000, locktime=1325785, tx_version=1)
loop = util.get_asyncio_loop()
tx = asyncio.run_coroutine_threadsafe(sweep_coro, loop).result()
tx_copy = tx_from_any(tx.serialize()) tx_copy = tx_from_any(tx.serialize())
self.assertEqual('010000000129349e5641d79915e9d0282fdbaee8c3df0b6731bab9d70bf626e8588bde24ac010000004847304402206bf0d0a93abae0d5873a62ebf277a5dd2f33837821e8b93e74d04e19d71b578002201a6d729bc159941ef5c4c9e5fe13ece9fc544351ba531b00f68ba549c8b38a9a01fdffffff01b82e0f00000000001600148ba0a0bc12b51831f58c7ea8607e76c5982c071fd93a1400', self.assertEqual('010000000129349e5641d79915e9d0282fdbaee8c3df0b6731bab9d70bf626e8588bde24ac010000004847304402206bf0d0a93abae0d5873a62ebf277a5dd2f33837821e8b93e74d04e19d71b578002201a6d729bc159941ef5c4c9e5fe13ece9fc544351ba531b00f68ba549c8b38a9a01fdffffff01b82e0f00000000001600148ba0a0bc12b51831f58c7ea8607e76c5982c071fd93a1400',
@ -1841,7 +1847,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('7f827fc5256c274fd1094eb7e020c8ded0baf820356f61aa4f14a9093b0ea0ee', tx_copy.txid()) self.assertEqual('7f827fc5256c274fd1094eb7e020c8ded0baf820356f61aa4f14a9093b0ea0ee', tx_copy.txid())
self.assertEqual('7f827fc5256c274fd1094eb7e020c8ded0baf820356f61aa4f14a9093b0ea0ee', tx_copy.wtxid()) self.assertEqual('7f827fc5256c274fd1094eb7e020c8ded0baf820356f61aa4f14a9093b0ea0ee', tx_copy.wtxid())
def test_sweep_compressed_p2pk(self): async def test_sweep_compressed_p2pk(self):
class NetworkMock: class NetworkMock:
relay_fee = 1000 relay_fee = 1000
async def listunspent_for_scripthash(self, scripthash): async def listunspent_for_scripthash(self, scripthash):
@ -1858,9 +1864,7 @@ class TestWalletSending(ElectrumTestCase):
privkeys = ['cUygTZe4jZLVwE4G44NznCPTeGvgsgassqucUHkAJxGC71Rst2kH',] privkeys = ['cUygTZe4jZLVwE4G44NznCPTeGvgsgassqucUHkAJxGC71Rst2kH',]
network = NetworkMock() network = NetworkMock()
dest_addr = 'tb1q5uy5xjcn55gwdkmghht8yp3vwz3088f6e3e0em' dest_addr = 'tb1q5uy5xjcn55gwdkmghht8yp3vwz3088f6e3e0em'
sweep_coro = sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=5000, locktime=2420006, tx_version=2) tx = await sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=5000, locktime=2420006, tx_version=2)
loop = util.get_asyncio_loop()
tx = asyncio.run_coroutine_threadsafe(sweep_coro, loop).result()
tx_copy = tx_from_any(tx.serialize()) tx_copy = tx_from_any(tx.serialize())
self.assertEqual('02000000015eb359ccfcd67c3e6b10bb937a796807007708c1f413840d0e627a3f94a1a48401000000484730440220043fc85a43e918ac41e494e309fdf204ca245d260cb5ea09108b196ca65d8a09022056f852f0f521e79ab2124d7e9f779c7290329ce5628ef8e92601980b065d3eb501fdffffff017f9e010000000000160014a709434b13a510e6db68bdd672062c70a2f39d3a26ed2400', self.assertEqual('02000000015eb359ccfcd67c3e6b10bb937a796807007708c1f413840d0e627a3f94a1a48401000000484730440220043fc85a43e918ac41e494e309fdf204ca245d260cb5ea09108b196ca65d8a09022056f852f0f521e79ab2124d7e9f779c7290329ce5628ef8e92601980b065d3eb501fdffffff017f9e010000000000160014a709434b13a510e6db68bdd672062c70a2f39d3a26ed2400',
@ -1868,7 +1872,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('968a501350b954ecb51948202b8d0613aa84123ca9b745c14e208cb14feeff59', tx_copy.txid()) self.assertEqual('968a501350b954ecb51948202b8d0613aa84123ca9b745c14e208cb14feeff59', tx_copy.txid())
self.assertEqual('968a501350b954ecb51948202b8d0613aa84123ca9b745c14e208cb14feeff59', tx_copy.wtxid()) self.assertEqual('968a501350b954ecb51948202b8d0613aa84123ca9b745c14e208cb14feeff59', tx_copy.wtxid())
def test_sweep_uncompressed_p2pkh(self): async def test_sweep_uncompressed_p2pkh(self):
class NetworkMock: class NetworkMock:
relay_fee = 1000 relay_fee = 1000
async def listunspent_for_scripthash(self, scripthash): async def listunspent_for_scripthash(self, scripthash):
@ -1885,9 +1889,7 @@ class TestWalletSending(ElectrumTestCase):
privkeys = ['p2pkh:91gxDahzHiJ63HXmLP7pvZrkF8i5gKBXk4VqWfhbhJjtf6Ni5NU',] privkeys = ['p2pkh:91gxDahzHiJ63HXmLP7pvZrkF8i5gKBXk4VqWfhbhJjtf6Ni5NU',]
network = NetworkMock() network = NetworkMock()
dest_addr = 'tb1q3ws2p0qjk5vrravv065xqlnkckvzcpclk79eu2' dest_addr = 'tb1q3ws2p0qjk5vrravv065xqlnkckvzcpclk79eu2'
sweep_coro = sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=5000, locktime=2420010, tx_version=2) tx = await sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=5000, locktime=2420010, tx_version=2)
loop = util.get_asyncio_loop()
tx = asyncio.run_coroutine_threadsafe(sweep_coro, loop).result()
tx_copy = tx_from_any(tx.serialize()) tx_copy = tx_from_any(tx.serialize())
self.assertEqual('02000000010615142d6c296f276c7da9fb2b09f655d594f73b76740404f1424c66c78ca715000000008a47304402206d2dae571ca2f51e0d4a8ce6a6335fa25ac09f4bbed26439124d93f035bdbb130220249dc2039f1da338a40679f0e79c25a2dc2983688e6c04753348f2aa8435e375014104b875ab889006d4a9be8467c9256cf54e1073f7f9a037604f571cc025bbf47b2987b4c862d5b687bb5328adccc69e67a17b109b6328228695a1c384573acd6199fdffffff0186500300000000001600148ba0a0bc12b51831f58c7ea8607e76c5982c071f2aed2400', self.assertEqual('02000000010615142d6c296f276c7da9fb2b09f655d594f73b76740404f1424c66c78ca715000000008a47304402206d2dae571ca2f51e0d4a8ce6a6335fa25ac09f4bbed26439124d93f035bdbb130220249dc2039f1da338a40679f0e79c25a2dc2983688e6c04753348f2aa8435e375014104b875ab889006d4a9be8467c9256cf54e1073f7f9a037604f571cc025bbf47b2987b4c862d5b687bb5328adccc69e67a17b109b6328228695a1c384573acd6199fdffffff0186500300000000001600148ba0a0bc12b51831f58c7ea8607e76c5982c071f2aed2400',
@ -1895,7 +1897,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('d62048493bf8459be5e1e3cab6caabc8f15661d02c364d8dc008297e573772bf', tx_copy.txid()) self.assertEqual('d62048493bf8459be5e1e3cab6caabc8f15661d02c364d8dc008297e573772bf', tx_copy.txid())
self.assertEqual('d62048493bf8459be5e1e3cab6caabc8f15661d02c364d8dc008297e573772bf', tx_copy.wtxid()) self.assertEqual('d62048493bf8459be5e1e3cab6caabc8f15661d02c364d8dc008297e573772bf', tx_copy.wtxid())
def test_sweep_compressed_p2pkh(self): async def test_sweep_compressed_p2pkh(self):
class NetworkMock: class NetworkMock:
relay_fee = 1000 relay_fee = 1000
async def listunspent_for_scripthash(self, scripthash): async def listunspent_for_scripthash(self, scripthash):
@ -1912,9 +1914,7 @@ class TestWalletSending(ElectrumTestCase):
privkeys = ['p2pkh:cN3LiXmurmGRF5xngYd8XS2ZsP2KeXFUh4SH7wpC8uJJzw52JPq1',] privkeys = ['p2pkh:cN3LiXmurmGRF5xngYd8XS2ZsP2KeXFUh4SH7wpC8uJJzw52JPq1',]
network = NetworkMock() network = NetworkMock()
dest_addr = 'tb1q782f750ekkxysp2rrscr6yknmn634e2pv8lktu' dest_addr = 'tb1q782f750ekkxysp2rrscr6yknmn634e2pv8lktu'
sweep_coro = sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=1000, locktime=2420010, tx_version=2) tx = await sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=1000, locktime=2420010, tx_version=2)
loop = util.get_asyncio_loop()
tx = asyncio.run_coroutine_threadsafe(sweep_coro, loop).result()
tx_copy = tx_from_any(tx.serialize()) tx_copy = tx_from_any(tx.serialize())
self.assertEqual('02000000016717835a2e1e152a69e7528a0f1346c1d37ee6e76c5e23b5d1c5a5b40241768a000000006a473044022038ad38003943bfd3ed39ba4340d545753fcad632a8fe882d01e4f0140ddb3cfb022019498260e29f5fbbcde9176bfb3553b7acec5fe284a9a3a33547a2d082b60355012103b875ab889006d4a9be8467c9256cf54e1073f7f9a037604f571cc025bbf47b29fdffffff0158de010000000000160014f1d49f51f9b58c4805431c303d12d3dcf51ae5412aed2400', self.assertEqual('02000000016717835a2e1e152a69e7528a0f1346c1d37ee6e76c5e23b5d1c5a5b40241768a000000006a473044022038ad38003943bfd3ed39ba4340d545753fcad632a8fe882d01e4f0140ddb3cfb022019498260e29f5fbbcde9176bfb3553b7acec5fe284a9a3a33547a2d082b60355012103b875ab889006d4a9be8467c9256cf54e1073f7f9a037604f571cc025bbf47b29fdffffff0158de010000000000160014f1d49f51f9b58c4805431c303d12d3dcf51ae5412aed2400',
@ -1922,7 +1922,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('432c108626581fc6a7d3efc9dac5f3dec8286cec47dfaab86b4267d10381586c', tx_copy.txid()) self.assertEqual('432c108626581fc6a7d3efc9dac5f3dec8286cec47dfaab86b4267d10381586c', tx_copy.txid())
self.assertEqual('432c108626581fc6a7d3efc9dac5f3dec8286cec47dfaab86b4267d10381586c', tx_copy.wtxid()) self.assertEqual('432c108626581fc6a7d3efc9dac5f3dec8286cec47dfaab86b4267d10381586c', tx_copy.wtxid())
def test_sweep_p2wpkh_p2sh(self): async def test_sweep_p2wpkh_p2sh(self):
class NetworkMock: class NetworkMock:
relay_fee = 1000 relay_fee = 1000
async def listunspent_for_scripthash(self, scripthash): async def listunspent_for_scripthash(self, scripthash):
@ -1939,9 +1939,7 @@ class TestWalletSending(ElectrumTestCase):
privkeys = ['p2wpkh-p2sh:cQMRGsiEsFX5YoxVZaMEzBruAkCWnoFf1SG7SRm2tLHDEN165TrA',] privkeys = ['p2wpkh-p2sh:cQMRGsiEsFX5YoxVZaMEzBruAkCWnoFf1SG7SRm2tLHDEN165TrA',]
network = NetworkMock() network = NetworkMock()
dest_addr = 'tb1qu7n2tzm90a3f29kvxlhzsc7t40ddk075ut5w44' dest_addr = 'tb1qu7n2tzm90a3f29kvxlhzsc7t40ddk075ut5w44'
sweep_coro = sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=500, locktime=2420010, tx_version=2) tx = await sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=500, locktime=2420010, tx_version=2)
loop = util.get_asyncio_loop()
tx = asyncio.run_coroutine_threadsafe(sweep_coro, loop).result()
tx_copy = tx_from_any(tx.serialize()) tx_copy = tx_from_any(tx.serialize())
self.assertEqual('020000000001011d1725072a6e60687a59b878ecaf940ea0385880613d9d5502102bd78ef97b9a0000000017160014e7a6a58b657f629516cc37ee2863cbabdadb3fd4fdffffff01fc47020000000000160014e7a6a58b657f629516cc37ee2863cbabdadb3fd402473044022048ea4c558fd374f5d5066440a7f4933393cb377802cb949e3039fedf0378a29402204b4a58c591117cc1e37f07b03cc03cc6198dbf547e2bff813e2e2102bd2057e00121029f46ba81b3c6ad84e52841364dc54ca1097d0c30a68fb529766504c4b1c599352aed2400', self.assertEqual('020000000001011d1725072a6e60687a59b878ecaf940ea0385880613d9d5502102bd78ef97b9a0000000017160014e7a6a58b657f629516cc37ee2863cbabdadb3fd4fdffffff01fc47020000000000160014e7a6a58b657f629516cc37ee2863cbabdadb3fd402473044022048ea4c558fd374f5d5066440a7f4933393cb377802cb949e3039fedf0378a29402204b4a58c591117cc1e37f07b03cc03cc6198dbf547e2bff813e2e2102bd2057e00121029f46ba81b3c6ad84e52841364dc54ca1097d0c30a68fb529766504c4b1c599352aed2400',
@ -1949,7 +1947,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('0680124954ccc158cbf24d289c93579f68fd75916509214066f69e09adda1861', tx_copy.txid()) self.assertEqual('0680124954ccc158cbf24d289c93579f68fd75916509214066f69e09adda1861', tx_copy.txid())
self.assertEqual('da8567d9b28e9e0ed8b3dcef6e619eba330cec6cb0c55d57f658f5ca06e02eb0', tx_copy.wtxid()) self.assertEqual('da8567d9b28e9e0ed8b3dcef6e619eba330cec6cb0c55d57f658f5ca06e02eb0', tx_copy.wtxid())
def test_sweep_p2wpkh(self): async def test_sweep_p2wpkh(self):
class NetworkMock: class NetworkMock:
relay_fee = 1000 relay_fee = 1000
async def listunspent_for_scripthash(self, scripthash): async def listunspent_for_scripthash(self, scripthash):
@ -1966,9 +1964,7 @@ class TestWalletSending(ElectrumTestCase):
privkeys = ['p2wpkh:cV2BvgtpLNX328m4QrhqycBGA6EkZUFfHM9kKjVXjfyD53uNfC4q',] privkeys = ['p2wpkh:cV2BvgtpLNX328m4QrhqycBGA6EkZUFfHM9kKjVXjfyD53uNfC4q',]
network = NetworkMock() network = NetworkMock()
dest_addr = 'tb1qhuy2e45lrdcp9s4ezeptx5kwxcnahzgpar9scc' dest_addr = 'tb1qhuy2e45lrdcp9s4ezeptx5kwxcnahzgpar9scc'
sweep_coro = sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=500, locktime=2420010, tx_version=2) tx = await sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=500, locktime=2420010, tx_version=2)
loop = util.get_asyncio_loop()
tx = asyncio.run_coroutine_threadsafe(sweep_coro, loop).result()
tx_copy = tx_from_any(tx.serialize()) tx_copy = tx_from_any(tx.serialize())
self.assertEqual('02000000000101e328aeb4f9dc1b85a2709ce59b0478a15ed9fb5e7f84fb62422f99b8cd6ad7010000000000fdffffff01087e010000000000160014bf08acd69f1b7012c2b91642b352ce3627db89010247304402204993099c4663d92ef4c9a28b3f45a40a6585754fe22ecfdc0a76c43fda7c9d04022006a75e0fd3ad1862d8e81015a71d2a1489ec7a9264e6e63b8fe6bb90c27e799b0121038ca94e7c715152fd89803c2a40a934c7c4035fb87b3cba981cd1e407369cfe312aed2400', self.assertEqual('02000000000101e328aeb4f9dc1b85a2709ce59b0478a15ed9fb5e7f84fb62422f99b8cd6ad7010000000000fdffffff01087e010000000000160014bf08acd69f1b7012c2b91642b352ce3627db89010247304402204993099c4663d92ef4c9a28b3f45a40a6585754fe22ecfdc0a76c43fda7c9d04022006a75e0fd3ad1862d8e81015a71d2a1489ec7a9264e6e63b8fe6bb90c27e799b0121038ca94e7c715152fd89803c2a40a934c7c4035fb87b3cba981cd1e407369cfe312aed2400',
@ -1977,7 +1973,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('b062d2e19880c66b36e80b823c2d00a2769658d1e574ff854dab15efd8fd7da8', tx_copy.wtxid()) self.assertEqual('b062d2e19880c66b36e80b823c2d00a2769658d1e574ff854dab15efd8fd7da8', tx_copy.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_coinjoin_between_two_p2wpkh_electrum_seeds(self, mock_save_db): async def test_coinjoin_between_two_p2wpkh_electrum_seeds(self, mock_save_db):
wallet1 = WalletIntegrityHelper.create_standard_wallet( wallet1 = WalletIntegrityHelper.create_standard_wallet(
keystore.from_seed('humor argue expand gain goat shiver remove morning security casual leopard degree', ''), keystore.from_seed('humor argue expand gain goat shiver remove morning security casual leopard degree', ''),
gap_limit=2, gap_limit=2,
@ -2061,7 +2057,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual((0, 10495000, 0), wallet2.get_balance()) self.assertEqual((0, 10495000, 0), wallet2.get_balance())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_standard_wallet_cannot_sign_multisig_input_even_if_cosigner(self, mock_save_db): async def test_standard_wallet_cannot_sign_multisig_input_even_if_cosigner(self, mock_save_db):
"""Just because our keystore recognizes the pubkeys in a txin, if the prevout does not belong to the wallet, """Just because our keystore recognizes the pubkeys in a txin, if the prevout does not belong to the wallet,
then wallet.is_mine and wallet.can_sign should return False (e.g. multisig input for single-sig wallet). then wallet.is_mine and wallet.can_sign should return False (e.g. multisig input for single-sig wallet).
(see issue #5948) (see issue #5948)
@ -2113,7 +2109,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertFalse(any([wallet_frost.is_mine(txout.address) for txout in tx.outputs()])) self.assertFalse(any([wallet_frost.is_mine(txout.address) for txout in tx.outputs()]))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_dscancel(self, mock_save_db): async def test_dscancel(self, mock_save_db):
self.maxDiff = None self.maxDiff = None
config = SimpleConfig({'electrum_path': self.electrum_path}) config = SimpleConfig({'electrum_path': self.electrum_path})
config.set_key('coin_chooser_output_rounding', False) config.set_key('coin_chooser_output_rounding', False)
@ -2316,6 +2312,9 @@ class TestWalletSending(ElectrumTestCase):
class NetworkMock: class NetworkMock:
relay_fee = 1000 relay_fee = 1000
async def get_transaction(self, txid, timeout=None): async def get_transaction(self, txid, timeout=None):
return self._gettx(txid)
@staticmethod
def _gettx(txid):
if txid == "597098f9077cd2a7bf5bb2a03c9ae5fcd9d1f07c0891cb42cbb129cf9eaf57fd": if txid == "597098f9077cd2a7bf5bb2a03c9ae5fcd9d1f07c0891cb42cbb129cf9eaf57fd":
return "02000000000102a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540000000000fdffffffbdeb0175b1c51c96843d1952f7e1c49c1703717d7d020048d4de0a8eed94dad50000000000fdffffff03b2a00700000000001600140cd6c9f8ce0aa73d77fcf7f156c74f5cbec6906bb2a00700000000001600146435504ddc95e6019a90bb7dfc7ca81a88a8633106d790000000000016001444bd3017ee214370abf683abaa7f6204c9f40210024730440220652a04a2a301d9a031a034f3ae48174e204e17acf7bfc27f0dcab14243f73e2202207b29e964c434dfb2c515232d36566a40dccd4dd93ccb7fd15260ecbda10f0d9801210231994e564a0530068d17a9b0f85bec58d1352517a2861ea99e5b3070d2c5dbda02473044022072186473874919019da0e3d92b6e0aa4f88cba448ed5434615e5a3c8e2b7c42a02203ec05cef66960d5bc45d0f3d25675190cf8035b11a05ed4b719fd9c3a894899b012102f5fdca8c4e30ba0a1babf9cf9ebe62519b08aead351c349ed1ffc8316c24f542d7f61c00" return "02000000000102a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540000000000fdffffffbdeb0175b1c51c96843d1952f7e1c49c1703717d7d020048d4de0a8eed94dad50000000000fdffffff03b2a00700000000001600140cd6c9f8ce0aa73d77fcf7f156c74f5cbec6906bb2a00700000000001600146435504ddc95e6019a90bb7dfc7ca81a88a8633106d790000000000016001444bd3017ee214370abf683abaa7f6204c9f40210024730440220652a04a2a301d9a031a034f3ae48174e204e17acf7bfc27f0dcab14243f73e2202207b29e964c434dfb2c515232d36566a40dccd4dd93ccb7fd15260ecbda10f0d9801210231994e564a0530068d17a9b0f85bec58d1352517a2861ea99e5b3070d2c5dbda02473044022072186473874919019da0e3d92b6e0aa4f88cba448ed5434615e5a3c8e2b7c42a02203ec05cef66960d5bc45d0f3d25675190cf8035b11a05ed4b719fd9c3a894899b012102f5fdca8c4e30ba0a1babf9cf9ebe62519b08aead351c349ed1ffc8316c24f542d7f61c00"
else: else:
@ -2334,6 +2333,7 @@ class TestWalletSending(ElectrumTestCase):
wallet = self.create_standard_wallet_from_seed('mix total present junior leader live state athlete mistake crack wall valve', wallet = self.create_standard_wallet_from_seed('mix total present junior leader live state athlete mistake crack wall valve',
config=config) config=config)
wallet.network = NetworkMock() wallet.network = NetworkMock()
wallet._get_rawtx_from_network = NetworkMock._gettx
# bootstrap wallet # bootstrap wallet
funding_tx = Transaction('02000000000101a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540100000000fdffffff0220a1070000000000160014db44724ac632ae47ee5765954d64796dd5fec72708de3c000000000016001424b32aadb42a89016c4de8f11741c3b29b15f21c02473044022045cc6c1cc875cbb0c0d8fe323dc1de9716e49ed5659741b0fb3dd9a196894066022077c242640071d12ec5763c5870f482a4823d8713e4bd14353dd621ed29a7f96d012102aea8d439a0f79d8b58e8d7bda83009f587e1f3da350adaa484329bf47cd03465fef61c00') funding_tx = Transaction('02000000000101a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540100000000fdffffff0220a1070000000000160014db44724ac632ae47ee5765954d64796dd5fec72708de3c000000000016001424b32aadb42a89016c4de8f11741c3b29b15f21c02473044022045cc6c1cc875cbb0c0d8fe323dc1de9716e49ed5659741b0fb3dd9a196894066022077c242640071d12ec5763c5870f482a4823d8713e4bd14353dd621ed29a7f96d012102aea8d439a0f79d8b58e8d7bda83009f587e1f3da350adaa484329bf47cd03465fef61c00')
@ -2366,7 +2366,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('3021a4fe24e33af9d0ccdf25c478387c97df671fe1fd8b4db0de4255b3a348c5', tx_copy.txid()) self.assertEqual('3021a4fe24e33af9d0ccdf25c478387c97df671fe1fd8b4db0de4255b3a348c5', tx_copy.txid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_wallet_history_chain_of_unsigned_transactions(self, mock_save_db): async def test_wallet_history_chain_of_unsigned_transactions(self, mock_save_db):
wallet = self.create_standard_wallet_from_seed('cross end slow expose giraffe fuel track awake turtle capital ranch pulp', wallet = self.create_standard_wallet_from_seed('cross end slow expose giraffe fuel track awake turtle capital ranch pulp',
config=self.config, gap_limit=3) config=self.config, gap_limit=3)
@ -2408,7 +2408,7 @@ class TestWalletSending(ElectrumTestCase):
coins[0].prevout.to_str()) coins[0].prevout.to_str())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_wallet_does_not_create_zero_input_tx(self, mock_save_db): async def test_wallet_does_not_create_zero_input_tx(self, mock_save_db):
wallet = self.create_standard_wallet_from_seed('cross end slow expose giraffe fuel track awake turtle capital ranch pulp', wallet = self.create_standard_wallet_from_seed('cross end slow expose giraffe fuel track awake turtle capital ranch pulp',
config=self.config, gap_limit=3) config=self.config, gap_limit=3)
@ -2432,7 +2432,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual(2, len(tx.outputs())) self.assertEqual(2, len(tx.outputs()))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_imported_wallet_usechange_off(self, mock_save_db): async def test_imported_wallet_usechange_off(self, mock_save_db):
wallet = restore_wallet_from_text( wallet = restore_wallet_from_text(
"p2wpkh:cVcwSp488C8Riguq55Tuktgi6TpzuyLdDwUxkBDBz3yzV7FW4af2 p2wpkh:cPWyoPvnv2hiyyxbhMkhX3gPEENzB6DqoP9bbR8SDTg5njK5SL9n", "p2wpkh:cVcwSp488C8Riguq55Tuktgi6TpzuyLdDwUxkBDBz3yzV7FW4af2 p2wpkh:cPWyoPvnv2hiyyxbhMkhX3gPEENzB6DqoP9bbR8SDTg5njK5SL9n",
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
@ -2468,7 +2468,7 @@ class TestWalletSending(ElectrumTestCase):
str(tx_copy)) str(tx_copy))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_imported_wallet_usechange_on(self, mock_save_db): async def test_imported_wallet_usechange_on(self, mock_save_db):
wallet = restore_wallet_from_text( wallet = restore_wallet_from_text(
"p2wpkh:cVcwSp488C8Riguq55Tuktgi6TpzuyLdDwUxkBDBz3yzV7FW4af2 p2wpkh:cPWyoPvnv2hiyyxbhMkhX3gPEENzB6DqoP9bbR8SDTg5njK5SL9n", "p2wpkh:cVcwSp488C8Riguq55Tuktgi6TpzuyLdDwUxkBDBz3yzV7FW4af2 p2wpkh:cPWyoPvnv2hiyyxbhMkhX3gPEENzB6DqoP9bbR8SDTg5njK5SL9n",
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
@ -2503,7 +2503,7 @@ class TestWalletSending(ElectrumTestCase):
str(tx_copy)) str(tx_copy))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_imported_wallet_usechange_on__no_more_unused_addresses(self, mock_save_db): async def test_imported_wallet_usechange_on__no_more_unused_addresses(self, mock_save_db):
wallet = restore_wallet_from_text( wallet = restore_wallet_from_text(
"p2wpkh:cVcwSp488C8Riguq55Tuktgi6TpzuyLdDwUxkBDBz3yzV7FW4af2 p2wpkh:cPWyoPvnv2hiyyxbhMkhX3gPEENzB6DqoP9bbR8SDTg5njK5SL9n", "p2wpkh:cVcwSp488C8Riguq55Tuktgi6TpzuyLdDwUxkBDBz3yzV7FW4af2 p2wpkh:cPWyoPvnv2hiyyxbhMkhX3gPEENzB6DqoP9bbR8SDTg5njK5SL9n",
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
@ -2548,7 +2548,7 @@ class TestWalletSending(ElectrumTestCase):
str(tx_copy)) str(tx_copy))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_get_spendable_coins(self, mock_save_db): async def test_get_spendable_coins(self, mock_save_db):
wallet = self.create_standard_wallet_from_seed('frost repair depend effort salon ring foam oak cancel receive save usage', wallet = self.create_standard_wallet_from_seed('frost repair depend effort salon ring foam oak cancel receive save usage',
config=self.config) config=self.config)
@ -2584,7 +2584,7 @@ class TestWalletSending(ElectrumTestCase):
{txi.prevout.to_str() for txi in wallet.get_spendable_coins()}) {txi.prevout.to_str() for txi in wallet.get_spendable_coins()})
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_export_psbt_with_xpubs__multisig(self, mock_save_db): async def test_export_psbt_with_xpubs__multisig(self, mock_save_db):
"""When exporting a PSBT to be signed by a hw device, test that we populate """When exporting a PSBT to be signed by a hw device, test that we populate
the PSBT_GLOBAL_XPUB field with wallet xpubs. the PSBT_GLOBAL_XPUB field with wallet xpubs.
""" """
@ -2658,7 +2658,7 @@ class TestWalletSending(ElectrumTestCase):
tx.serialize_as_bytes().hex()) tx.serialize_as_bytes().hex())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_export_psbt_with_xpubs__singlesig(self, mock_save_db): async def test_export_psbt_with_xpubs__singlesig(self, mock_save_db):
"""When exporting a PSBT to be signed by a hw device, test that we populate """When exporting a PSBT to be signed by a hw device, test that we populate
the PSBT_GLOBAL_XPUB field with wallet xpubs. the PSBT_GLOBAL_XPUB field with wallet xpubs.
""" """
@ -2701,7 +2701,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.config = SimpleConfig({'electrum_path': self.electrum_path}) self.config = SimpleConfig({'electrum_path': self.electrum_path})
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_old_electrum_seed_online_mpk(self, mock_save_db): async def test_sending_offline_old_electrum_seed_online_mpk(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet( wallet_offline = WalletIntegrityHelper.create_standard_wallet(
keystore.from_seed('alone body father children lead goodbye phone twist exist grass kick join', '', False), keystore.from_seed('alone body father children lead goodbye phone twist exist grass kick join', '', False),
gap_limit=4, gap_limit=4,
@ -2746,7 +2746,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('06032230d0bf6a277bc4f8c39e3311a712e0e614626d0dea7cc9f592abfae5d8', tx.wtxid()) self.assertEqual('06032230d0bf6a277bc4f8c39e3311a712e0e614626d0dea7cc9f592abfae5d8', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_xprv_online_xpub_p2pkh(self, mock_save_db): async def test_sending_offline_xprv_online_xpub_p2pkh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet( wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/44'/1'/0' # bip39: "qwe", der: m/44'/1'/0'
keystore.from_xprv('tprv8gfKwjuAaqtHgqxMh1tosAQ28XvBMkcY5NeFRA3pZMpz6MR4H4YZ3MJM4fvNPnRKeXR1Td2vQGgjorNXfo94WvT5CYDsPAqjHxSn436G1Eu'), keystore.from_xprv('tprv8gfKwjuAaqtHgqxMh1tosAQ28XvBMkcY5NeFRA3pZMpz6MR4H4YZ3MJM4fvNPnRKeXR1Td2vQGgjorNXfo94WvT5CYDsPAqjHxSn436G1Eu'),
@ -2800,7 +2800,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('d9c21696eca80321933e7444ca928aaf25eeda81aaa2f4e5c085d4d0a9cf7aa7', tx.wtxid()) self.assertEqual('d9c21696eca80321933e7444ca928aaf25eeda81aaa2f4e5c085d4d0a9cf7aa7', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_xprv_online_xpub_p2wpkh_p2sh(self, mock_save_db): async def test_sending_offline_xprv_online_xpub_p2wpkh_p2sh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet( wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/49'/1'/0' # bip39: "qwe", der: m/49'/1'/0'
keystore.from_xprv('uprv8zHHrMQMQ26utWwNJ5MK2SXpB9hbmy7pbPaneii69xT8cZTyFpxQFxkknGWKP8dxBTZhzy7yP6cCnLrRCQjzJDk3G61SjZpxhFQuB2NR8a5'), keystore.from_xprv('uprv8zHHrMQMQ26utWwNJ5MK2SXpB9hbmy7pbPaneii69xT8cZTyFpxQFxkknGWKP8dxBTZhzy7yP6cCnLrRCQjzJDk3G61SjZpxhFQuB2NR8a5'),
@ -2845,7 +2845,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('27b78ec072a403b0545258e7a1a8d494e4b6fd48bf77f4251a12160c92207cbc', tx.wtxid()) self.assertEqual('27b78ec072a403b0545258e7a1a8d494e4b6fd48bf77f4251a12160c92207cbc', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_xprv_online_xpub_p2wpkh(self, mock_save_db): async def test_sending_offline_xprv_online_xpub_p2wpkh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet( wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/84'/1'/0' # bip39: "qwe", der: m/84'/1'/0'
keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'), keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'),
@ -2900,7 +2900,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('484e350beaa722a744bb3e2aa38de005baa8526d86536d6143e5814355acf775', tx.wtxid()) self.assertEqual('484e350beaa722a744bb3e2aa38de005baa8526d86536d6143e5814355acf775', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_offline_signing_beyond_gap_limit(self, mock_save_db): async def test_offline_signing_beyond_gap_limit(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet( wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/84'/1'/0' # bip39: "qwe", der: m/84'/1'/0'
keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'), keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'),
@ -2945,7 +2945,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('484e350beaa722a744bb3e2aa38de005baa8526d86536d6143e5814355acf775', tx.wtxid()) self.assertEqual('484e350beaa722a744bb3e2aa38de005baa8526d86536d6143e5814355acf775', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_signing_where_offline_ks_does_not_have_keyorigin_but_psbt_contains_it(self, mock_save_db): async def test_signing_where_offline_ks_does_not_have_keyorigin_but_psbt_contains_it(self, mock_save_db):
# keystore has intermediate xprv without root fp; tx contains root fp and full path. # keystore has intermediate xprv without root fp; tx contains root fp and full path.
# tx has input with key beyond gap limit # tx has input with key beyond gap limit
wallet_offline = WalletIntegrityHelper.create_standard_wallet( wallet_offline = WalletIntegrityHelper.create_standard_wallet(
@ -2966,7 +2966,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
str(tx)) str(tx))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_wif_online_addr_p2pkh(self, mock_save_db): # compressed pubkey async def test_sending_offline_wif_online_addr_p2pkh(self, mock_save_db): # compressed pubkey
wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config) wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config)
wallet_offline.import_private_key('p2pkh:cQDxbmQfwRV3vP1mdnVHq37nJekHLsuD3wdSQseBRA2ct4MFk5Pq', password=None) wallet_offline.import_private_key('p2pkh:cQDxbmQfwRV3vP1mdnVHq37nJekHLsuD3wdSQseBRA2ct4MFk5Pq', password=None)
wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config) wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config)
@ -3003,7 +3003,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.wtxid()) self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_wif_online_addr_p2wpkh_p2sh(self, mock_save_db): async def test_sending_offline_wif_online_addr_p2wpkh_p2sh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config) wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config)
wallet_offline.import_private_key('p2wpkh-p2sh:cU9hVzhpvfn91u2zTVn8uqF2ymS7ucYH8V5TmsTDmuyMHgRk9WsJ', password=None) wallet_offline.import_private_key('p2wpkh-p2sh:cU9hVzhpvfn91u2zTVn8uqF2ymS7ucYH8V5TmsTDmuyMHgRk9WsJ', password=None)
wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config) wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config)
@ -3040,7 +3040,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('9bb9949974954613945756c48ca5525cd5cba1b667ccb10c7a53e1ed076a1117', tx.wtxid()) self.assertEqual('9bb9949974954613945756c48ca5525cd5cba1b667ccb10c7a53e1ed076a1117', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_wif_online_addr_p2wpkh(self, mock_save_db): async def test_sending_offline_wif_online_addr_p2wpkh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config) wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config)
wallet_offline.import_private_key('p2wpkh:cPuQzcNEgbeYZ5at9VdGkCwkPA9r34gvEVJjuoz384rTfYpahfe7', password=None) wallet_offline.import_private_key('p2wpkh:cPuQzcNEgbeYZ5at9VdGkCwkPA9r34gvEVJjuoz384rTfYpahfe7', password=None)
wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config) wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config)
@ -3077,7 +3077,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('3b7cc3c3352bbb43ddc086487ac696e09f2863c3d9e8636721851b8008a83ffa', tx.wtxid()) self.assertEqual('3b7cc3c3352bbb43ddc086487ac696e09f2863c3d9e8636721851b8008a83ffa', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_xprv_online_addr_p2pkh(self, mock_save_db): # compressed pubkey async def test_sending_offline_xprv_online_addr_p2pkh(self, mock_save_db): # compressed pubkey
wallet_offline = WalletIntegrityHelper.create_standard_wallet( wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/44'/1'/0' # bip39: "qwe", der: m/44'/1'/0'
keystore.from_xprv('tprv8gfKwjuAaqtHgqxMh1tosAQ28XvBMkcY5NeFRA3pZMpz6MR4H4YZ3MJM4fvNPnRKeXR1Td2vQGgjorNXfo94WvT5CYDsPAqjHxSn436G1Eu'), keystore.from_xprv('tprv8gfKwjuAaqtHgqxMh1tosAQ28XvBMkcY5NeFRA3pZMpz6MR4H4YZ3MJM4fvNPnRKeXR1Td2vQGgjorNXfo94WvT5CYDsPAqjHxSn436G1Eu'),
@ -3118,7 +3118,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.wtxid()) self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_xprv_online_addr_p2wpkh_p2sh(self, mock_save_db): async def test_sending_offline_xprv_online_addr_p2wpkh_p2sh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet( wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/49'/1'/0' # bip39: "qwe", der: m/49'/1'/0'
keystore.from_xprv('uprv8zHHrMQMQ26utWwNJ5MK2SXpB9hbmy7pbPaneii69xT8cZTyFpxQFxkknGWKP8dxBTZhzy7yP6cCnLrRCQjzJDk3G61SjZpxhFQuB2NR8a5'), keystore.from_xprv('uprv8zHHrMQMQ26utWwNJ5MK2SXpB9hbmy7pbPaneii69xT8cZTyFpxQFxkknGWKP8dxBTZhzy7yP6cCnLrRCQjzJDk3G61SjZpxhFQuB2NR8a5'),
@ -3159,7 +3159,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('9bb9949974954613945756c48ca5525cd5cba1b667ccb10c7a53e1ed076a1117', tx.wtxid()) self.assertEqual('9bb9949974954613945756c48ca5525cd5cba1b667ccb10c7a53e1ed076a1117', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_xprv_online_addr_p2wpkh(self, mock_save_db): async def test_sending_offline_xprv_online_addr_p2wpkh(self, mock_save_db):
wallet_offline = WalletIntegrityHelper.create_standard_wallet( wallet_offline = WalletIntegrityHelper.create_standard_wallet(
# bip39: "qwe", der: m/84'/1'/0' # bip39: "qwe", der: m/84'/1'/0'
keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'), keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'),
@ -3200,7 +3200,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('3b7cc3c3352bbb43ddc086487ac696e09f2863c3d9e8636721851b8008a83ffa', tx.wtxid()) self.assertEqual('3b7cc3c3352bbb43ddc086487ac696e09f2863c3d9e8636721851b8008a83ffa', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_hd_multisig_online_addr_p2sh(self, mock_save_db): async def test_sending_offline_hd_multisig_online_addr_p2sh(self, mock_save_db):
# 2-of-3 legacy p2sh multisig # 2-of-3 legacy p2sh multisig
wallet_offline1 = WalletIntegrityHelper.create_multisig_wallet( wallet_offline1 = WalletIntegrityHelper.create_multisig_wallet(
[ [
@ -3265,7 +3265,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('0e8fdc8257a85ebe7eeab14a53c2c258c61a511f64176b7f8fc016bc2263d307', tx.wtxid()) self.assertEqual('0e8fdc8257a85ebe7eeab14a53c2c258c61a511f64176b7f8fc016bc2263d307', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_hd_multisig_online_addr_p2wsh_p2sh(self, mock_save_db): async def test_sending_offline_hd_multisig_online_addr_p2wsh_p2sh(self, mock_save_db):
# 2-of-2 p2sh-embedded segwit multisig # 2-of-2 p2sh-embedded segwit multisig
wallet_offline1 = WalletIntegrityHelper.create_multisig_wallet( wallet_offline1 = WalletIntegrityHelper.create_multisig_wallet(
[ [
@ -3334,7 +3334,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('96d0bca1001778c54e4c3a07929fab5562c5b5a23fd1ca3aa3870cc5df2bf97d', tx.wtxid()) self.assertEqual('96d0bca1001778c54e4c3a07929fab5562c5b5a23fd1ca3aa3870cc5df2bf97d', tx.wtxid())
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_sending_offline_hd_multisig_online_addr_p2wsh(self, mock_save_db): async def test_sending_offline_hd_multisig_online_addr_p2wsh(self, mock_save_db):
# 2-of-3 p2wsh multisig # 2-of-3 p2wsh multisig
wallet_offline1 = WalletIntegrityHelper.create_multisig_wallet( wallet_offline1 = WalletIntegrityHelper.create_multisig_wallet(
[ [
@ -3439,7 +3439,7 @@ class TestWalletHistory_SimpleRandomOrder(ElectrumTestCase):
return w return w
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_restoring_old_wallet_txorder1(self, mock_save_db): async def test_restoring_old_wallet_txorder1(self, mock_save_db):
w = self.create_old_wallet() w = self.create_old_wallet()
for i in [2, 12, 7, 9, 11, 10, 16, 6, 17, 1, 13, 15, 5, 8, 4, 0, 14, 18, 3]: for i in [2, 12, 7, 9, 11, 10, 16, 6, 17, 1, 13, 15, 5, 8, 4, 0, 14, 18, 3]:
tx = Transaction(self.transactions[self.txid_list[i]]) tx = Transaction(self.transactions[self.txid_list[i]])
@ -3447,7 +3447,7 @@ class TestWalletHistory_SimpleRandomOrder(ElectrumTestCase):
self.assertEqual(27633300, sum(w.get_balance())) self.assertEqual(27633300, sum(w.get_balance()))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_restoring_old_wallet_txorder2(self, mock_save_db): async def test_restoring_old_wallet_txorder2(self, mock_save_db):
w = self.create_old_wallet() w = self.create_old_wallet()
for i in [9, 18, 2, 0, 13, 3, 1, 11, 4, 17, 7, 14, 12, 15, 10, 8, 5, 6, 16]: for i in [9, 18, 2, 0, 13, 3, 1, 11, 4, 17, 7, 14, 12, 15, 10, 8, 5, 6, 16]:
tx = Transaction(self.transactions[self.txid_list[i]]) tx = Transaction(self.transactions[self.txid_list[i]])
@ -3455,7 +3455,7 @@ class TestWalletHistory_SimpleRandomOrder(ElectrumTestCase):
self.assertEqual(27633300, sum(w.get_balance())) self.assertEqual(27633300, sum(w.get_balance()))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_restoring_old_wallet_txorder3(self, mock_save_db): async def test_restoring_old_wallet_txorder3(self, mock_save_db):
w = self.create_old_wallet() w = self.create_old_wallet()
for i in [5, 8, 17, 0, 9, 10, 12, 3, 15, 18, 2, 11, 14, 7, 16, 1, 4, 6, 13]: for i in [5, 8, 17, 0, 9, 10, 12, 3, 15, 18, 2, 11, 14, 7, 16, 1, 4, 6, 13]:
tx = Transaction(self.transactions[self.txid_list[i]]) tx = Transaction(self.transactions[self.txid_list[i]])
@ -3488,7 +3488,7 @@ class TestWalletHistory_EvilGapLimit(ElectrumTestCase):
return w return w
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_restoring_wallet_txorder1(self, mock_save_db): async def test_restoring_wallet_txorder1(self, mock_save_db):
w = self.create_wallet() w = self.create_wallet()
w.db.put('stored_height', 1316917 + 100) w.db.put('stored_height', 1316917 + 100)
for txid in self.transactions: for txid in self.transactions:
@ -3537,7 +3537,7 @@ class TestWalletHistory_DoubleSpend(ElectrumTestCase):
self.config = SimpleConfig({'electrum_path': self.electrum_path}) self.config = SimpleConfig({'electrum_path': self.electrum_path})
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_restoring_wallet_without_manual_delete(self, mock_save_db): async def test_restoring_wallet_without_manual_delete(self, mock_save_db):
w = restore_wallet_from_text("small rapid pattern language comic denial donate extend tide fever burden barrel", w = restore_wallet_from_text("small rapid pattern language comic denial donate extend tide fever burden barrel",
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
gap_limit=5, gap_limit=5,
@ -3551,7 +3551,7 @@ class TestWalletHistory_DoubleSpend(ElectrumTestCase):
self.assertEqual(999890, sum(w.get_balance())) self.assertEqual(999890, sum(w.get_balance()))
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_restoring_wallet_with_manual_delete(self, mock_save_db): async def test_restoring_wallet_with_manual_delete(self, mock_save_db):
w = restore_wallet_from_text("small rapid pattern language comic denial donate extend tide fever burden barrel", w = restore_wallet_from_text("small rapid pattern language comic denial donate extend tide fever burden barrel",
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
gap_limit=5, gap_limit=5,
@ -3588,7 +3588,7 @@ class TestImportedWallet(ElectrumTestCase):
self.config = SimpleConfig({'electrum_path': self.electrum_path}) self.config = SimpleConfig({'electrum_path': self.electrum_path})
@mock.patch.object(wallet.Abstract_Wallet, 'save_db') @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
def test_importing_and_deleting_addresses(self, mock_save_db): async def test_importing_and_deleting_addresses(self, mock_save_db):
w = restore_wallet_from_text("tb1q7648a2pm2se425lvun0g3vlf4ahmflcthegz63", w = restore_wallet_from_text("tb1q7648a2pm2se425lvun0g3vlf4ahmflcthegz63",
path='if_this_exists_mocking_failed_648151893', path='if_this_exists_mocking_failed_648151893',
config=self.config)['wallet'] # type: Abstract_Wallet config=self.config)['wallet'] # type: Abstract_Wallet

21
electrum/util.py

@ -1561,12 +1561,17 @@ def is_tor_socks_port(host: str, port: int) -> bool:
return False return False
AS_LIB_USER_I_WANT_TO_MANAGE_MY_OWN_ASYNCIO_LOOP = False # used by unit tests
_asyncio_event_loop = None # type: Optional[asyncio.AbstractEventLoop] _asyncio_event_loop = None # type: Optional[asyncio.AbstractEventLoop]
def get_asyncio_loop() -> asyncio.AbstractEventLoop: def get_asyncio_loop() -> asyncio.AbstractEventLoop:
"""Returns the global asyncio event loop we use.""" """Returns the global asyncio event loop we use."""
if _asyncio_event_loop is None: if loop := _asyncio_event_loop:
return loop
if AS_LIB_USER_I_WANT_TO_MANAGE_MY_OWN_ASYNCIO_LOOP:
if loop := get_running_loop():
return loop
raise Exception("event loop not created yet") raise Exception("event loop not created yet")
return _asyncio_event_loop
def create_and_start_event_loop() -> Tuple[asyncio.AbstractEventLoop, def create_and_start_event_loop() -> Tuple[asyncio.AbstractEventLoop,
@ -1777,7 +1782,6 @@ class CallbackManager:
def __init__(self): def __init__(self):
self.callback_lock = threading.Lock() self.callback_lock = threading.Lock()
self.callbacks = defaultdict(list) # note: needs self.callback_lock self.callbacks = defaultdict(list) # note: needs self.callback_lock
self.asyncio_loop = None
def register_callback(self, func, events): def register_callback(self, func, events):
with self.callback_lock: with self.callback_lock:
@ -1795,21 +1799,20 @@ class CallbackManager:
Can be called from any thread. The callback itself will get scheduled Can be called from any thread. The callback itself will get scheduled
on the event loop. on the event loop.
""" """
if self.asyncio_loop is None: loop = get_asyncio_loop()
self.asyncio_loop = get_asyncio_loop() assert loop.is_running(), "event loop not running"
assert self.asyncio_loop.is_running(), "event loop not running"
with self.callback_lock: with self.callback_lock:
callbacks = self.callbacks[event][:] callbacks = self.callbacks[event][:]
for callback in callbacks: for callback in callbacks:
# FIXME: if callback throws, we will lose the traceback # FIXME: if callback throws, we will lose the traceback
if asyncio.iscoroutinefunction(callback): if asyncio.iscoroutinefunction(callback):
asyncio.run_coroutine_threadsafe(callback(*args), self.asyncio_loop) asyncio.run_coroutine_threadsafe(callback(*args), loop)
elif get_running_loop() == self.asyncio_loop: elif get_running_loop() == loop:
# run callback immediately, so that it is guaranteed # run callback immediately, so that it is guaranteed
# to have been executed when this method returns # to have been executed when this method returns
callback(*args) callback(*args)
else: else:
self.asyncio_loop.call_soon_threadsafe(callback, *args) loop.call_soon_threadsafe(callback, *args)
callback_mgr = CallbackManager() callback_mgr = CallbackManager()

10
electrum/wallet.py

@ -295,6 +295,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
txin_type: str txin_type: str
wallet_type: str wallet_type: str
lnworker: Optional['LNWallet'] lnworker: Optional['LNWallet']
network: Optional['Network']
def __init__(self, db: WalletDB, storage: Optional[WalletStorage], *, config: SimpleConfig): def __init__(self, db: WalletDB, storage: Optional[WalletStorage], *, config: SimpleConfig):
@ -2187,6 +2188,12 @@ class Abstract_Wallet(ABC, Logger, EventListener):
return True return True
return False return False
def _get_rawtx_from_network(self, txid: str) -> str:
"""legacy hack. do not use in new code."""
assert self.network
return self.network.run_from_another_thread(
self.network.get_transaction(txid, timeout=10))
def get_input_tx(self, tx_hash: str, *, ignore_network_issues=False) -> Optional[Transaction]: def get_input_tx(self, tx_hash: str, *, ignore_network_issues=False) -> Optional[Transaction]:
# First look up an input transaction in the wallet where it # First look up an input transaction in the wallet where it
# will likely be. If co-signing a transaction it may not have # will likely be. If co-signing a transaction it may not have
@ -2194,8 +2201,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
tx = self.db.get_transaction(tx_hash) tx = self.db.get_transaction(tx_hash)
if not tx and self.network and self.network.has_internet_connection(): if not tx and self.network and self.network.has_internet_connection():
try: try:
raw_tx = self.network.run_from_another_thread( raw_tx = self._get_rawtx_from_network(tx_hash)
self.network.get_transaction(tx_hash, timeout=10))
except NetworkException as e: except NetworkException as e:
_logger.info(f'got network error getting input txn. err: {repr(e)}. txid: {tx_hash}. ' _logger.info(f'got network error getting input txn. err: {repr(e)}. txid: {tx_hash}. '
f'if you are intentionally offline, consider using the --offline flag') f'if you are intentionally offline, consider using the --offline flag')

Loading…
Cancel
Save