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. 31
      electrum/tests/__init__.py
  2. 125
      electrum/tests/test_bitcoin.py
  3. 82
      electrum/tests/test_commands.py
  4. 14
      electrum/tests/test_invoices.py
  5. 193
      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. 23
      electrum/util.py
  13. 10
      electrum/wallet.py

31
electrum/tests/__init__.py

@ -1,3 +1,4 @@
import asyncio
import unittest
import threading
import tempfile
@ -18,8 +19,10 @@ FAST_TESTS = False
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."""
TESTNET = False
@ -43,12 +46,9 @@ class ElectrumTestCase(unittest.TestCase):
def setUp(self):
self._test_lock.acquire()
super().setUp()
self.asyncio_loop, self._stop_loop, self._loop_thread = util.create_and_start_event_loop()
self.electrum_path = tempfile.mkdtemp()
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)
super().tearDown()
self._test_lock.release()
@ -59,12 +59,19 @@ def as_testnet(func):
NOTE: this is inherently sequential; tests running in parallel would break things
"""
def run_test(*args, **kwargs):
old_net = constants.net
try:
constants.set_testnet()
func(*args, **kwargs)
finally:
constants.net = old_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:
constants.set_testnet()
return func(*args, **kwargs)
finally:
constants.net = old_net
return run_test

125
electrum/tests/test_bitcoin.py

@ -1,3 +1,4 @@
import asyncio
import base64
import sys
@ -35,27 +36,43 @@ def needs_test_with_all_aes_implementations(func):
NOTE: this is inherently sequential;
tests running in parallel would break things
"""
def run_test(*args, **kwargs):
if FAST_TESTS: # if set, only run tests once, using fastest implementation
func(*args, **kwargs)
return
has_cryptodome = crypto.HAS_CRYPTODOME
has_cryptography = crypto.HAS_CRYPTOGRAPHY
has_pyaes = crypto.HAS_PYAES
try:
if has_pyaes:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = False, False, True
func(*args, **kwargs) # pyaes
if has_cryptodome:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = True, False, False
func(*args, **kwargs) # cryptodome
if has_cryptography:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = False, True, False
func(*args, **kwargs) # cryptography
finally:
crypto.HAS_CRYPTODOME = has_cryptodome
crypto.HAS_CRYPTOGRAPHY = has_cryptography
crypto.HAS_PYAES = has_pyaes
if FAST_TESTS: # if set, only run tests once, using fastest implementation
return func
has_cryptodome = crypto.HAS_CRYPTODOME
has_cryptography = crypto.HAS_CRYPTOGRAPHY
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:
if has_pyaes:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = False, False, True
func(*args, **kwargs) # pyaes
if has_cryptodome:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = True, False, False
func(*args, **kwargs) # cryptodome
if has_cryptography:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = False, True, False
func(*args, **kwargs) # cryptography
finally:
crypto.HAS_CRYPTODOME = has_cryptodome
crypto.HAS_CRYPTOGRAPHY = has_cryptography
crypto.HAS_PYAES = has_pyaes
return run_test
@ -66,22 +83,34 @@ def needs_test_with_all_chacha20_implementations(func):
NOTE: this is inherently sequential;
tests running in parallel would break things
"""
def run_test(*args, **kwargs):
if FAST_TESTS: # if set, only run tests once, using fastest implementation
func(*args, **kwargs)
return
has_cryptodome = crypto.HAS_CRYPTODOME
has_cryptography = crypto.HAS_CRYPTOGRAPHY
try:
if has_cryptodome:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = True, False
func(*args, **kwargs) # cryptodome
if has_cryptography:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = False, True
func(*args, **kwargs) # cryptography
finally:
crypto.HAS_CRYPTODOME = has_cryptodome
crypto.HAS_CRYPTOGRAPHY = has_cryptography
if FAST_TESTS: # if set, only run tests once, using fastest implementation
return func
has_cryptodome = crypto.HAS_CRYPTODOME
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:
if has_cryptodome:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = True, False
func(*args, **kwargs) # cryptodome
if has_cryptography:
(crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = False, True
func(*args, **kwargs) # cryptography
finally:
crypto.HAS_CRYPTODOME = has_cryptodome
crypto.HAS_CRYPTOGRAPHY = has_cryptography
return run_test
@ -93,13 +122,21 @@ def disable_ecdsa_r_value_grinding(func):
NOTE: this is inherently sequential;
tests running in parallel would break things
"""
def run_test(*args, **kwargs):
is_grinding = ecc.ENABLE_ECDSA_R_VALUE_GRINDING
try:
ecc.ENABLE_ECDSA_R_VALUE_GRINDING = False
func(*args, **kwargs)
finally:
ecc.ENABLE_ECDSA_R_VALUE_GRINDING = is_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:
ecc.ENABLE_ECDSA_R_VALUE_GRINDING = False
return func(*args, **kwargs)
finally:
ecc.ENABLE_ECDSA_R_VALUE_GRINDING = is_grinding
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("1"))
def test_convert_xkey(self):
async def test_convert_xkey(self):
cmds = Commands(config=self.config)
xpubs = {
("xpub6CCWFbvCbqF92kGwm9nV7t7RvVoQUKaq5USMdyVP6jvv1NgN52KAX6NNYCeE8Ca7JQC4K5tZcnQrubQcjJ6iixfPs4pwAQJAQgTt6hBjg11", "standard"),
@ -62,7 +62,7 @@ class TestCommands(ElectrumTestCase):
}
for xkey1, xtype1 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 = {
("xprv9yD9r6PJmTgqpGCUf8FUkkAhNTxv4rryiFWkqb5mYQPw8aMDXUzuyJ3tgv5vUqYkdK1E6Q5jKxPss4HkMBYV4q8AfG8t7rxgyS4xQX4ndAm", "standard"),
@ -71,40 +71,40 @@ class TestCommands(ElectrumTestCase):
}
for xkey1, xtype1 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')
def test_encrypt_decrypt(self, mock_save_db):
async def test_encrypt_decrypt(self, mock_save_db):
wallet = restore_wallet_from_text('p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN',
path='if_this_exists_mocking_failed_648151893',
config=self.config)['wallet']
cmds = Commands(config=self.config)
cleartext = "asdasd this is the message"
pubkey = "021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da"
ciphertext = cmds._run('encrypt', (pubkey, cleartext))
self.assertEqual(cleartext, cmds._run('decrypt', (pubkey, ciphertext), wallet=wallet))
ciphertext = await cmds.encrypt(pubkey, cleartext)
self.assertEqual(cleartext, await cmds.decrypt(pubkey, ciphertext, wallet=wallet))
@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',
path='if_this_exists_mocking_failed_648151893',
config=self.config)['wallet']
cmds = Commands(config=self.config)
# single address tests
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):
cmds._run('getprivatekeys', ("bc1qgfam82qk7uwh5j2xxmcd8cmklpe0zackyj6r23",), wallet=wallet) # not in wallet
await cmds.getprivatekeys("bc1qgfam82qk7uwh5j2xxmcd8cmklpe0zackyj6r23", wallet=wallet) # not in wallet
self.assertEqual("p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL",
cmds._run('getprivatekeys', ("bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw",), wallet=wallet))
await cmds.getprivatekeys("bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw", wallet=wallet))
# list of addresses tests
with self.assertRaises(Exception):
cmds._run('getprivatekeys', (['bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', 'asd'],), wallet=wallet)
await cmds.getprivatekeys(['bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', 'asd'], wallet=wallet)
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')
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',
gap_limit=2,
path='if_this_exists_mocking_failed_648151893',
@ -112,16 +112,16 @@ class TestCommands(ElectrumTestCase):
cmds = Commands(config=self.config)
# single address tests
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):
cmds._run('getprivatekeys', ("bc1qgfam82qk7uwh5j2xxmcd8cmklpe0zackyj6r23",), wallet=wallet) # not in wallet
await cmds.getprivatekeys("bc1qgfam82qk7uwh5j2xxmcd8cmklpe0zackyj6r23", wallet=wallet) # not in wallet
self.assertEqual("p2wpkh:L15oxP24NMNAXxq5r2aom24pHPtt3Fet8ZutgL155Bad93GSubM2",
cmds._run('getprivatekeys', ("bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af",), wallet=wallet))
await cmds.getprivatekeys("bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af", wallet=wallet))
# list of addresses tests
with self.assertRaises(Exception):
cmds._run('getprivatekeys', (['bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', 'asd'],), wallet=wallet)
await cmds.getprivatekeys(['bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', 'asd'], wallet=wallet)
self.assertEqual(['p2wpkh:L15oxP24NMNAXxq5r2aom24pHPtt3Fet8ZutgL155Bad93GSubM2', 'p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN'],
cmds._run('getprivatekeys', (['bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', 'bc1q9pzjpjq4nqx5ycnywekcmycqz0wjp2nq604y2n'],), wallet=wallet))
await cmds.getprivatekeys(['bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', 'bc1q9pzjpjq4nqx5ycnywekcmycqz0wjp2nq604y2n'], wallet=wallet))
class TestCommandsTestnet(ElectrumTestCase):
@ -131,7 +131,7 @@ class TestCommandsTestnet(ElectrumTestCase):
super().setUp()
self.config = SimpleConfig({'electrum_path': self.electrum_path})
def test_convert_xkey(self):
async def test_convert_xkey(self):
cmds = Commands(config=self.config)
xpubs = {
("tpubD8p5qNfjczgTGbh9qgNxsbFgyhv8GgfVkmp3L88qtRm5ibUYiDVCrn6WYfnGey5XVVw6Bc5QNQUZW5B4jFQsHjmaenvkFUgWtKtgj5AdPm9", "standard"),
@ -140,7 +140,7 @@ class TestCommandsTestnet(ElectrumTestCase):
}
for xkey1, xtype1 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 = {
("tprv8c83gxdVUcznP8fMx2iNUBbaQgQC7MUbBUDG3c6YU9xgt7Dn5pfcgHUeNZTAvuYmNgVHjyTzYzGWwJr7GvKCm2FkPaaJipyipbfJeB3tdPW", "standard"),
@ -149,9 +149,9 @@ class TestCommandsTestnet(ElectrumTestCase):
}
for xkey1, xtype1 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)
jsontx = {
"inputs": [
@ -170,9 +170,9 @@ class TestCommandsTestnet(ElectrumTestCase):
]
}
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)
jsontx = {
"inputs": [
@ -192,24 +192,24 @@ class TestCommandsTestnet(ElectrumTestCase):
]
}
self.assertEqual("0200000000010139c5375fe9da7bd377c1783002b129f8c57d3e724d62f5eacb9739ca691a229d0100000000fdffffff01301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb0247304402201c551df0458528d19ba1dd79b134dcf0055f7b029dfc3d0d024e6253d069d13e02206d03cfc85a6fc648acb6fc6be630e4567d1dd00ddbcdee551ee0711414e2f33f0121021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da00000000",
cmds._run('serialize', (jsontx,)))
await cmds.serialize(jsontx))
@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',
gap_limit=2,
path='if_this_exists_mocking_failed_648151893',
config=self.config)['wallet']
cmds = Commands(config=self.config)
self.assertEqual("p2wpkh:cUzm7zPpWgLYeURgff4EsoMjhskCpsviBH4Y3aZcrBX8UJSRPjC2",
cmds._run('getprivatekeyforpath', ([0, 10000],), wallet=wallet))
await cmds.getprivatekeyforpath([0, 10000], wallet=wallet))
self.assertEqual("p2wpkh:cUzm7zPpWgLYeURgff4EsoMjhskCpsviBH4Y3aZcrBX8UJSRPjC2",
cmds._run('getprivatekeyforpath', ("m/0/10000",), wallet=wallet))
await cmds.getprivatekeyforpath("m/0/10000", wallet=wallet))
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')
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',
gap_limit=2,
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)
cmds = Commands(config=self.config)
tx_str = cmds._run(
'payto', (),
tx_str = await cmds.payto(
destination="tb1qsyzgpwa0vg2940u5t6l97etuvedr5dejpf9tdy",
amount="0.00123456",
feerate=50,
@ -237,7 +236,7 @@ class TestCommandsTestnet(ElectrumTestCase):
tx_str)
@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',
gap_limit=2,
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)
cmds = Commands(config=self.config)
tx_str = cmds._run(
'paytomany', (),
tx_str = await cmds.paytomany(
outputs=[["tb1qk3g0t9pw5wctkzz7gh6k3ljfuukn729s67y54e", 0.002],
["tb1qr7evucrllljtryam6y2k3ntmlptq208pghql2h", "2!"],
["tb1qs3msqp0n0qade2haanjw2dkaa5lm77vwvce00h", 0.003],
@ -265,15 +263,15 @@ class TestCommandsTestnet(ElectrumTestCase):
tx_str)
@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)
unsigned_tx = "70736274ff0100a0020000000221d3645ba44f33fff6fe2666dc080279bc34b531c66888729712a80b204a32a10100000000fdffffffdd7f90d51acf98dc45ad7489316a983868c75e16bf14ffeb9eae01603a7b4da40100000000fdffffff02e8030000000000001976a9149a9ec2b35a7660c80dae38dd806fdf9b0fde68fd88ac74c11000000000001976a914f0dc093f7fb1b76cfd06610d5359d6595676cc2b88aca79b1d00000100e102000000018ba8cf9f0ff0b44c389e4a1cd25c0770636d95ccef161e313542647d435a5fd0000000006a4730440220373b3989905177f2e36d7e3d02b967d03092747fe7bbd3ba7b2c24623a88538c02207be79ee1d981060c2be6783f4946ce1bda1f64671b349ef14a4a6fecc047a71e0121030de43c5ed4c6272d20ce3becf3fb7afd5c3ccfb5d58ddfdf3047981e0b005e0dfdffffff02c0010700000000001976a9141cd3eb65bce2cae9f54544b65e46b3ad1f0b187288ac40420f00000000001976a914f0dc093f7fb1b76cfd06610d5359d6595676cc2b88ac979b1d00000100e102000000014e39236158716e91b0b2170ebe9d6b359d139e9ebfff163f2bafd0bec9890d04000000006a473044022070340deb95ca25ef86c4c7a9539b5c8f7b8351941635450311f914cd9c2f45ea02203fa7576e032ab5ae4763c78f5c2124573213c956286fd766582d9462515dc6540121033f6737e40a3a6087bc58bc5b82b427f9ed26d710b8fe2f70bfdd3d62abebcf74fdffffff02e8030000000000001976a91490350959750b3b38e451df16bd5957b7649bf5d288acac840100000000001976a914f0dc093f7fb1b76cfd06610d5359d6595676cc2b88ac979b1d00000000"
privkey = "cVtE728tULSA4gut4QWxo218q6PRsXHQAv84SXix83cuvScvGd1H"
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')
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',
gap_limit=2,
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="
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')
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',
gap_limit=2,
path='if_this_exists_mocking_failed_648151893',
@ -307,7 +305,7 @@ class TestCommandsTestnet(ElectrumTestCase):
cmds = Commands(config=self.config)
tx = "02000000000101b9723dfc69af058ef6613539a000d2cd098a2c8a74e802b6d8739db708ba8c9a0100000000fdffffff02a00f00000000000016001429e1fd187f0cac845946ae1b11dc136c536bfc0fe8b2000000000000160014100611bcb3aee7aad176936cf4ed56ade03027aa02473044022063c05e2347f16251922830ccc757231247b3c2970c225f988e9204844a1ab7b802204652d2c4816707e3d3bea2609b83b079001a435bad2a99cc2e730f276d07070c012102ee3f00141178006c78b0b458aab21588388335078c655459afe544211f15aee050721f00"
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",
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)
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'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=2, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet
@ -54,7 +54,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
wallet1.lnworker.set_request_status(bytes.fromhex(pr.rhash), PR_PAID)
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'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=2, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet
@ -84,7 +84,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
wallet1.adb.add_verified_tx(tx.txid(), tx_info)
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'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=2, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet
@ -114,7 +114,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
wallet1.adb.add_verified_tx(tx.txid(), tx_info)
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'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=2, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet
@ -143,7 +143,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
wallet1.adb.add_verified_tx(tx.txid(), tx_info)
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'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=5, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet
@ -198,7 +198,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
self.assertEqual(PR_UNPAID, wallet1.get_invoice_status(pr4))
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'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=3, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet
@ -226,7 +226,7 @@ class TestWalletPaymentRequests(ElectrumTestCase):
self.assertEqual(addr2, pr2.get_address())
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'
d = restore_wallet_from_text(text, path=self.wallet1_path, gap_limit=3, config=self.config)
wallet1 = d['wallet'] # type: Standard_Wallet

193
electrum/tests/test_lnpeer.py

@ -404,17 +404,16 @@ class TestPeer(ElectrumTestCase):
super().setUp()
self._lnworkers_created = [] # type: List[MockLNWallet]
def tearDown(self):
async def cleanup_lnworkers():
async with OldTaskGroup() as group:
for lnworker in self._lnworkers_created:
await group.spawn(lnworker.stop())
async def asyncTearDown(self):
# clean up lnworkers
async with OldTaskGroup() as group:
for lnworker in self._lnworkers_created:
shutil.rmtree(lnworker._user_dir)
self._lnworkers_created.clear()
run(cleanup_lnworkers())
await group.spawn(lnworker.stop())
for lnworker in self._lnworkers_created:
shutil.rmtree(lnworker._user_dir)
self._lnworkers_created.clear()
electrum.trampoline._TRAMPOLINE_NODES_UNITTESTS = {}
super().tearDown()
await super().asyncTearDown()
def prepare_peers(
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
return lnaddr2, invoice
def test_reestablish(self):
async def test_reestablish(self):
alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(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)
gath.cancel()
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
with self.assertRaises(concurrent.futures.CancelledError):
run(f())
@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)
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
@ -578,10 +575,8 @@ class TestPeer(ElectrumTestCase):
self.assertEqual(result, True)
gath.cancel()
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
with self.assertRaises(concurrent.futures.CancelledError):
run(f())
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel_0, bob_channel)
for chan in (alice_channel_0, bob_channel):
chan.peer_state = PeerState.DISCONNECTED
@ -590,10 +585,8 @@ class TestPeer(ElectrumTestCase):
p1.reestablish_channel(alice_channel_0),
p2.reestablish_channel(bob_channel))
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
with self.assertRaises(electrum.lnutil.RemoteMisbehaving):
run(f())
self.assertEqual(alice_channel_0.peer_state, PeerState.BAD)
self.assertEqual(bob_channel._state, ChannelState.FORCE_CLOSING)
@ -612,7 +605,7 @@ class TestPeer(ElectrumTestCase):
)
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
@ -664,9 +657,9 @@ class TestPeer(ElectrumTestCase):
self.assertEqual(chan_BA.peer_state, PeerState.GOOD)
raise 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
@ -719,9 +712,9 @@ class TestPeer(ElectrumTestCase):
self.assertEqual(chan_BA.peer_state, PeerState.GOOD)
raise 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_channel, bob_channel = create_test_channels()
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),
}
with self.assertRaises(PaymentDone):
run(f())
await f()
@needs_test_with_all_chacha20_implementations
def test_simple_payment(self):
self._test_simple_payment(trampoline=False)
async def test_simple_payment(self):
await self._test_simple_payment(trampoline=False)
@needs_test_with_all_chacha20_implementations
def test_simple_payment_trampoline(self):
self._test_simple_payment(trampoline=True)
async def test_simple_payment_trampoline(self):
await self._test_simple_payment(trampoline=True)
@needs_test_with_all_chacha20_implementations
def test_payment_race(self):
async def test_payment_race(self):
"""Alice and Bob pay each other simultaneously.
They both send 'update_add_htlc' and receive each other's update
before sending 'commitment_signed'. Neither party should fulfill
@ -837,11 +830,11 @@ class TestPeer(ElectrumTestCase):
await asyncio.sleep(0.01)
await group.spawn(pay())
with self.assertRaises(PaymentDone):
run(f())
await f()
#@unittest.skip("too expensive")
#@needs_test_with_all_chacha20_implementations
def test_payments_stresstest(self):
async def test_payments_stresstest(self):
alice_channel, bob_channel = create_test_channels()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
alice_init_balance_msat = alice_channel.balance(HTLCOwner.LOCAL)
@ -859,17 +852,15 @@ class TestPeer(ElectrumTestCase):
await group.spawn(single_payment(pay_req))
gath.cancel()
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
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, 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, alice_channel.balance(HTLCOwner.REMOTE))
@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'])
peers = graph.peers.values()
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)
await group.spawn(pay(lnaddr, pay_req))
with self.assertRaises(PaymentDone):
run(f())
await f()
@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'])
peers = graph.peers.values()
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)
await group.spawn(pay(pay_req))
with self.assertRaises(PaymentDone):
run(f())
await f()
@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.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)
@ -958,10 +949,10 @@ class TestPeer(ElectrumTestCase):
lnaddr, pay_req = self.prepare_invoice(graph.workers['dave'], include_routing_hints=True)
await group.spawn(pay(lnaddr, pay_req))
with self.assertRaises(PaymentDone):
run(f())
await f()
@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
# 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'])
@ -996,10 +987,10 @@ class TestPeer(ElectrumTestCase):
self.assertFalse(invoice_features.supports(LnFeatures.BASIC_MPP_OPT))
await group.spawn(pay(lnaddr, pay_req))
with self.assertRaises(PaymentDone):
run(f())
await f()
@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
graph_definition = GRAPH_DEFINITIONS['square_graph'].copy()
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)
await group.spawn(pay(lnaddr, pay_req))
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."""
self.assertEqual(500_000_000_000, graph.channels[('alice', 'bob')].balance(LOCAL))
self.assertEqual(500_000_000_000, graph.channels[('alice', 'carol')].balance(LOCAL))
@ -1110,22 +1101,23 @@ class TestPeer(ElectrumTestCase):
if fail_kwargs:
with self.assertRaises(NoPathFound):
run(f(fail_kwargs))
await f(fail_kwargs)
if success_kwargs:
with self.assertRaises(PaymentDone):
run(f(success_kwargs))
await f(success_kwargs)
@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'])
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
def test_payment_multipart(self):
async def test_payment_multipart(self):
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():
if graph.workers['alice'].network.channel_db:
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),
}
run(f())
await f()
@needs_test_with_all_chacha20_implementations
def test_payment_trampoline_legacy(self):
async def test_payment_trampoline_legacy(self):
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
def test_payment_trampoline_e2e_direct(self):
async def test_payment_trampoline_e2e_direct(self):
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
def test_payment_trampoline_e2e_indirect(self):
async def test_payment_trampoline_e2e_indirect(self):
# must use two trampolines
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
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
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'])
electrum.trampoline._TRAMPOLINE_NODES_UNITTESTS = {
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
# we need at least one trial because the initial fees are too low
# * a payment with several trials: should succeed
self._run_mpp(
await self._run_mpp(
graph,
fail_kwargs={'alice_uses_trampoline': True, 'attempts': 1},
success_kwargs={'alice_uses_trampoline': True, 'attempts': 30})
@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'])
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['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
self._run_mpp(
await self._run_mpp(
graph,
fail_kwargs={'alice_uses_trampoline': True, 'attempts': 30, 'disable_trampoline_receiving': True},
success_kwargs={})
@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.
Dave shuts down (stops wallet).
We test if Dave fails the pending HTLCs during shutdown.
@ -1270,19 +1262,19 @@ class TestPeer(ElectrumTestCase):
await group.spawn(stop())
with self.assertRaises(SuccessfulTest):
run(f())
await f()
@needs_test_with_all_chacha20_implementations
def test_legacy_shutdown_low(self):
self._test_shutdown(alice_fee=100, bob_fee=150)
async def test_legacy_shutdown_low(self):
await self._test_shutdown(alice_fee=100, bob_fee=150)
@needs_test_with_all_chacha20_implementations
def test_legacy_shutdown_high(self):
self._test_shutdown(alice_fee=2000, bob_fee=100)
async def test_legacy_shutdown_high(self):
await self._test_shutdown(alice_fee=2000, bob_fee=100)
@needs_test_with_all_chacha20_implementations
def test_modern_shutdown_with_overlap(self):
self._test_shutdown(
async def test_modern_shutdown_with_overlap(self):
await self._test_shutdown(
alice_fee=1,
bob_fee=200,
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})
# ))
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()
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
w1.network.config.set_key('test_shutdown_fee', alice_fee)
@ -1334,13 +1326,11 @@ class TestPeer(ElectrumTestCase):
await asyncio.sleep(0.1)
w2.enable_htlc_settle = True
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
with self.assertRaises(concurrent.futures.CancelledError):
run(f())
@needs_test_with_all_chacha20_implementations
def test_warning(self):
async def test_warning(self):
alice_channel, bob_channel = create_test_channels()
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 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())
async def f():
await gath
with self.assertRaises(GracefulDisconnect):
run(f())
await gath
@needs_test_with_all_chacha20_implementations
def test_error(self):
async def test_error(self):
alice_channel, bob_channel = create_test_channels()
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()
gath.cancel()
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):
run(f())
await gath
@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()
# create upfront shutdown script for bob, alice doesn't use upfront
@ -1411,7 +1397,7 @@ class TestPeer(ElectrumTestCase):
await gath
with self.assertRaises(GracefulDisconnect):
run(test())
await test()
# bob sends the same upfront_shutdown_script has he announced
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)]
gath = asyncio.gather(*coros)
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()
p1, p2, w1, w2, q1, q2 = self.prepare_peers(alice_channel, bob_channel)
lnaddr, pay_req = self.prepare_invoice(w2)
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()
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:
assert q1.qsize() == 1
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]
# 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())
with self.assertRaises(PaymentFailure):
run(f())
await f()
@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()
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())
with self.assertRaises(SuccessfulTest):
run(f())
await f()
@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()
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())
with self.assertRaises(GracefulDisconnect):
run(f())
await f()
self.assertTrue(isinstance(failing_task.exception().__cause__, lnmsg.UnknownMandatoryMsgType))
@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()
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())
with self.assertRaises(GracefulDisconnect):
run(f())
await f()
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 shutil
import asyncio
from typing import Optional
from electrum import util
from electrum.util import bfh
@ -30,18 +31,19 @@ def node(character: str) -> bytes:
class Test_LNRouter(ElectrumTestCase):
TESTNET = True
cdb = None
cdb = None # type: Optional[lnrouter.ChannelDB]
def setUp(self):
super().setUp()
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 self.cdb:
self.cdb.stop()
asyncio.run_coroutine_threadsafe(self.cdb.stopped_event.wait(), self.asyncio_loop).result()
super().tearDown()
await self.cdb.stopped_event.wait()
await super().asyncTearDown()
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'\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()
amount_to_send = 100000
@ -156,7 +158,7 @@ class Test_LNRouter(ElectrumTestCase):
self.assertEqual(node('b'), route[0].node_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()
amount_to_send = 100000
@ -213,7 +215,7 @@ class Test_LNRouter(ElectrumTestCase):
self.assertEqual(channel(4), path[1].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()
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):
@needs_test_with_all_chacha20_implementations
def test_responder(self):
async def test_responder(self):
# local static
ls_priv=bytes.fromhex('2121212121212121212121212121212121212121212121212121212121212121')
# ephemeral
@ -39,11 +39,10 @@ class TestLNTransport(ElectrumTestCase):
assert num_bytes == 66
return bytes.fromhex('00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba')
transport = LNResponderTransport(ls_priv, Reader(), Writer())
asyncio.run_coroutine_threadsafe(
transport.handshake(epriv=e_priv), self.asyncio_loop).result()
await transport.handshake(epriv=e_priv)
@needs_test_with_all_chacha20_implementations
def test_loop(self):
async def test_loop(self):
responder_shaked = asyncio.Event()
server_shaked = asyncio.Event()
responder_key = ECPrivkey.generate_random_key()
@ -98,4 +97,4 @@ class TestLNTransport(ElectrumTestCase):
server.close()
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()
constants.set_mainnet()
def setUp(self):
super().setUp()
async def asyncSetUp(self):
await super().asyncSetUp()
self.config = SimpleConfig({'electrum_path': self.electrum_path})
self.interface = MockInterface(self.config)
def test_fork_noconflict(self):
async def test_fork_noconflict(self):
blockchain.blockchains = {}
self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}})
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': 6, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}})
ifa = self.interface
fut = asyncio.run_coroutine_threadsafe(ifa.sync_until(8, next_height=7), util.get_asyncio_loop())
self.assertEqual(('fork', 8), fut.result())
res = await ifa.sync_until(8, next_height=7)
self.assertEqual(('fork', 8), res)
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}}
self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}})
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': 6, 'mock': {'binary':1,'check':lambda x: True, 'connect': lambda x: True}})
ifa = self.interface
fut = asyncio.run_coroutine_threadsafe(ifa.sync_until(8, next_height=7), util.get_asyncio_loop())
self.assertEqual(('fork', 8), fut.result())
res = await ifa.sync_until(8, next_height=7)
self.assertEqual(('fork', 8), res)
self.assertEqual(self.interface.q.qsize(), 0)
def test_can_connect_during_backward(self):
async def test_can_connect_during_backward(self):
blockchain.blockchains = {}
self.interface.q.put_nowait({'block_height': 8, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: False}})
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': 4, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}})
ifa = self.interface
fut = asyncio.run_coroutine_threadsafe(ifa.sync_until(8, next_height=4), util.get_asyncio_loop())
self.assertEqual(('catchup', 5), fut.result())
res = await ifa.sync_until(8, next_height=4)
self.assertEqual(('catchup', 5), res)
self.assertEqual(self.interface.q.qsize(), 0)
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())
return b
def test_chain_false_during_binary(self):
async def test_chain_false_during_binary(self):
blockchain.blockchains = {}
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
@ -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': 6, 'mock': {'catchup':1, 'check': lambda x: False, 'connect': lambda x: True}})
ifa = self.interface
fut = asyncio.run_coroutine_threadsafe(ifa.sync_until(8, next_height=6), util.get_asyncio_loop())
self.assertEqual(('catchup', 7), fut.result())
res = await ifa.sync_until(8, next_height=6)
self.assertEqual(('catchup', 7), res)
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):
def test_create_new_wallet(self):
async def test_create_new_wallet(self):
passphrase = 'mypassphrase'
password = 'mypassword'
encrypt_file = True
@ -178,7 +178,7 @@ class TestCreateRestoreWallet(WalletTestCase):
self.assertEqual(d['seed'], wallet.keystore.get_seed(password))
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'
passphrase = 'mypassphrase'
password = 'mypassword'
@ -196,7 +196,7 @@ class TestCreateRestoreWallet(WalletTestCase):
self.assertEqual(encrypt_file, wallet.storage.is_encrypted())
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'
d = restore_wallet_from_text(
text,
@ -209,28 +209,28 @@ class TestCreateRestoreWallet(WalletTestCase):
self.assertEqual(text, wallet.keystore.get_seed(None))
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'
d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config)
wallet = d['wallet'] # type: Standard_Wallet
self.assertEqual(text, wallet.keystore.get_master_public_key())
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'
d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config)
wallet = d['wallet'] # type: Standard_Wallet
self.assertEqual(text, wallet.keystore.get_master_private_key(password=None))
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'
d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config)
wallet = d['wallet'] # type: Standard_Wallet
self.assertEqual(text, wallet.keystore.get_master_private_key(password=None))
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'
d = restore_wallet_from_text(text, path=self.wallet_path, config=self.config)
wallet = d['wallet'] # type: Imported_Wallet
@ -240,7 +240,7 @@ class TestCreateRestoreWallet(WalletTestCase):
wallet.delete_address('bc1qnp78h78vp92pwdwq5xvh8eprlga5q8gu66960c')
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'
d = restore_wallet_from_text(text, path=self.wallet_path, config=self.config)
wallet = d['wallet'] # type: Imported_Wallet
@ -256,13 +256,7 @@ class TestCreateRestoreWallet(WalletTestCase):
class TestWalletPassword(WalletTestCase):
def setUp(self):
super().setUp()
def tearDown(self):
super().tearDown()
def test_update_password_of_imported_wallet(self):
async 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]}'
db = WalletDB(wallet_str, manual_upgrades=False)
storage = WalletStorage(self.wallet_path)
@ -278,7 +272,7 @@ class TestWalletPassword(WalletTestCase):
wallet.check_password("wrong password")
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]}'''
db = WalletDB(wallet_str, manual_upgrades=False)
storage = WalletStorage(self.wallet_path)
@ -293,12 +287,12 @@ class TestWalletPassword(WalletTestCase):
wallet.check_password("wrong password")
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]}'
db = WalletDB(wallet_str, manual_upgrades=False)
storage = WalletStorage(self.wallet_path)
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)
# 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})
@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'
self.assertEqual(seed_type(seed_words), 'standard')
@ -108,7 +108,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '1KSezYMhAJMWqFbVFB2JshYg69UpmEXR4D')
@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'
self.assertEqual(seed_type(seed_words), 'segwit')
@ -130,7 +130,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
ks.get_lightning_xprv(None))
@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'
self.assertEqual(seed_type(seed_words), 'segwit')
@ -152,7 +152,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
ks.get_lightning_xprv(None))
@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'
self.assertEqual(seed_type(seed_words), 'old')
@ -170,7 +170,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '1KRW8pH6HFHZh889VDq6fEKvmrsmApwNfe')
@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
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')
@ -205,7 +205,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '3Ke6pKrmtSyyQaMob1ES4pk8siAAkRmst9')
@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
seed_words = 'kiss live scene rude gate step hip quarter bunker oxygen motor glove'
self.assertEqual(seed_type(seed_words), '2fa')
@ -240,7 +240,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '3PeZEcumRqHSPNN43hd4yskGEBdzXgY8Cy')
@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'
self.assertEqual(seed_type(seed_words), '2fa_segwit')
@ -274,7 +274,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1qd4q50nft7kxm9yglfnpup9ed2ukj3tkxp793y0zya8dc9m39jcwq308dxz')
@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'
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')
@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'
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')
@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'
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')
@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
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))
@ -351,7 +351,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1q8c6fshw2dlwun7ekn9qwf37cu2rn755upcp6el')
@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'
self.assertEqual(seed_type(seed_words), 'standard')
@ -373,7 +373,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '36XWwEHrrVCLnhjK5MrVVGmUHghr9oWTN1')
@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'
self.assertEqual(seed_type(seed_words), 'segwit')
@ -395,7 +395,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1qxqf840dqswcmu7a8v82fj6ej0msx08flvuy6kngr7axstjcaq6us9hrehd')
@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'
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')
@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
# der: m/49'/0'/0'
# 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')
@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'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
bip32_seed = keystore.bip39_to_seed(seed_words, '')
@ -498,7 +498,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1q0fj5mra96hhnum80kllklc52zqn6kppt3hyzr49yhr3ecr42z3tsrkg3gs')
@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":
xprv9s21ZrQH143K2pMWi8jrTawHaj16uKk4CSbvo4Zt61tcrmuUDMx2o1Byzcr3saXNGNvHP8zZgXVdJHsXVdzYFPavxvCyaGyGr1WkAYG83ce
@ -525,7 +525,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '1Aw4wpXsAyEHSgMZqPdyewoAtJqH9Jaso3')
@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":
xprv9s21ZrQH143K2o6EXEHpVy8TCYoMmkBnDCCESLdR2ieKwmcNG48ck2XJQY4waS7RUQcXqR9N7HnQbUVEDMWYyREdF1idQqxFHuCfK7fqFni
@ -551,7 +551,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], '3FVvdRhR7racZhmcvrGAqX9eJoP8Sw3ypp')
@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":
xprv9s21ZrQH143K3dzDLfeY3cMp23u5vDeFYftu5RPYZPucKc99mNEddU4w99GxdgUGcSfMpVDxhnR1XpJzZNXRN1m6xNgnzFS5MwMP6QyBRKV
@ -581,7 +581,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase):
self.assertEqual(w.get_change_addresses()[0], 'bc1q8l6hcvlczu4mtjcnlwhczw7vdxnvwccpjl3cwz')
@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":
xprv9s21ZrQH143K2UspC9FRPfQC9NcDB4HPkx1XG9UEtuceYtpcCZ6ypNZWdgfxQ9dAFVeD1F4Zg4roY7nZm2LB7THPD6kaCege3M7EuS8v85c
@ -617,7 +617,7 @@ class TestWalletKeystoreAddressIntegrityForTestnet(ElectrumTestCase):
self.config = SimpleConfig({'electrum_path': self.electrum_path})
@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
# der: m/49'/1'/0'
# 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')
@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'
self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True))
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)
@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')
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())
@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(
[
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())
@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(
[
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())
@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(
[
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())
@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
config = SimpleConfig({'electrum_path': self.electrum_path})
config.set_key('coin_chooser_output_rounding', False)
@ -1266,7 +1266,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual((0, 18700, 0), wallet.get_balance())
@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')
# bootstrap wallet
@ -1366,6 +1366,9 @@ class TestWalletSending(ElectrumTestCase):
class NetworkMock:
relay_fee = 1000
async def get_transaction(self, txid, timeout=None):
return self._gettx(txid)
@staticmethod
def _gettx(txid):
if txid == "597098f9077cd2a7bf5bb2a03c9ae5fcd9d1f07c0891cb42cbb129cf9eaf57fd":
return "02000000000102a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540000000000fdffffffbdeb0175b1c51c96843d1952f7e1c49c1703717d7d020048d4de0a8eed94dad50000000000fdffffff03b2a00700000000001600140cd6c9f8ce0aa73d77fcf7f156c74f5cbec6906bb2a00700000000001600146435504ddc95e6019a90bb7dfc7ca81a88a8633106d790000000000016001444bd3017ee214370abf683abaa7f6204c9f40210024730440220652a04a2a301d9a031a034f3ae48174e204e17acf7bfc27f0dcab14243f73e2202207b29e964c434dfb2c515232d36566a40dccd4dd93ccb7fd15260ecbda10f0d9801210231994e564a0530068d17a9b0f85bec58d1352517a2861ea99e5b3070d2c5dbda02473044022072186473874919019da0e3d92b6e0aa4f88cba448ed5434615e5a3c8e2b7c42a02203ec05cef66960d5bc45d0f3d25675190cf8035b11a05ed4b719fd9c3a894899b012102f5fdca8c4e30ba0a1babf9cf9ebe62519b08aead351c349ed1ffc8316c24f542d7f61c00"
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',
config=config)
wallet.network = NetworkMock()
wallet._get_rawtx_from_network = NetworkMock._gettx
# bootstrap wallet
funding_tx = Transaction('02000000000101a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540100000000fdffffff0220a1070000000000160014db44724ac632ae47ee5765954d64796dd5fec72708de3c000000000016001424b32aadb42a89016c4de8f11741c3b29b15f21c02473044022045cc6c1cc875cbb0c0d8fe323dc1de9716e49ed5659741b0fb3dd9a196894066022077c242640071d12ec5763c5870f482a4823d8713e4bd14353dd621ed29a7f96d012102aea8d439a0f79d8b58e8d7bda83009f587e1f3da350adaa484329bf47cd03465fef61c00')
@ -1419,6 +1423,9 @@ class TestWalletSending(ElectrumTestCase):
class NetworkMock:
relay_fee = 1000
async def get_transaction(self, txid, timeout=None):
return self._gettx(txid)
@staticmethod
def _gettx(txid):
if txid == "08557327673db61cc921e1a30826608599b86457836be3021105c13940d9a9a3":
return "02000000000101a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540100000000fdffffff0220a1070000000000160014db44724ac632ae47ee5765954d64796dd5fec72708de3c000000000016001424b32aadb42a89016c4de8f11741c3b29b15f21c02473044022045cc6c1cc875cbb0c0d8fe323dc1de9716e49ed5659741b0fb3dd9a196894066022077c242640071d12ec5763c5870f482a4823d8713e4bd14353dd621ed29a7f96d012102aea8d439a0f79d8b58e8d7bda83009f587e1f3da350adaa484329bf47cd03465fef61c00"
else:
@ -1440,6 +1447,7 @@ class TestWalletSending(ElectrumTestCase):
gap_limit=4,
)
wallet.network = NetworkMock()
wallet._get_rawtx_from_network = NetworkMock._gettx
# bootstrap wallet
funding_tx = Transaction('02000000000102c247447533b530cacc3e716aae84621857f04a483252374cbdccfdf8b4ef816b0000000000fdffffffc247447533b530cacc3e716aae84621857f04a483252374cbdccfdf8b4ef816b0100000000fdffffff01d63f0f00000000001600141ef4658adb12ec745a1a1fef6ab8897f04bade060247304402201dc5be86749d8ce33571a6f1a2f8bbfceba89b9dbf2b4683e66c8c17cf7df6090220729199516cb894569ebbe3e998d47fc74030231ed30f110c9babd8a9dc361115012102728251a5f5f55375eef3c14fe59ab0755ba4d5f388619895238033ac9b51aad20247304402202e5d416489c20810e96e931b98a84b0c0c4fc32d2d34d3470b7ee16810246a4c022040f86cf8030d2117d6487bbe6e23d68d6d70408b002d8055de1f33d038d3a0550121039c009e7e7dad07e74ec5a8ac9f9e3499420dd9fe9709995525c714170152512620f71c00')
@ -1782,7 +1790,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual((0, 3_900_000, 0), wallet.get_balance())
@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')
# bootstrap wallet
@ -1814,7 +1822,7 @@ class TestWalletSending(ElectrumTestCase):
wallet.adb.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
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:
relay_fee = 1000
async def listunspent_for_scripthash(self, scripthash):
@ -1831,9 +1839,7 @@ class TestWalletSending(ElectrumTestCase):
privkeys = ['93NQ7CFbwTPyKDJLXe97jczw33fiLijam2SCZL3Uinz1NSbHrTu',]
network = NetworkMock()
dest_addr = 'tb1q3ws2p0qjk5vrravv065xqlnkckvzcpclk79eu2'
sweep_coro = 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 = await sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=5000, locktime=1325785, tx_version=1)
tx_copy = tx_from_any(tx.serialize())
self.assertEqual('010000000129349e5641d79915e9d0282fdbaee8c3df0b6731bab9d70bf626e8588bde24ac010000004847304402206bf0d0a93abae0d5873a62ebf277a5dd2f33837821e8b93e74d04e19d71b578002201a6d729bc159941ef5c4c9e5fe13ece9fc544351ba531b00f68ba549c8b38a9a01fdffffff01b82e0f00000000001600148ba0a0bc12b51831f58c7ea8607e76c5982c071fd93a1400',
@ -1841,7 +1847,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('7f827fc5256c274fd1094eb7e020c8ded0baf820356f61aa4f14a9093b0ea0ee', tx_copy.txid())
self.assertEqual('7f827fc5256c274fd1094eb7e020c8ded0baf820356f61aa4f14a9093b0ea0ee', tx_copy.wtxid())
def test_sweep_compressed_p2pk(self):
async def test_sweep_compressed_p2pk(self):
class NetworkMock:
relay_fee = 1000
async def listunspent_for_scripthash(self, scripthash):
@ -1858,9 +1864,7 @@ class TestWalletSending(ElectrumTestCase):
privkeys = ['cUygTZe4jZLVwE4G44NznCPTeGvgsgassqucUHkAJxGC71Rst2kH',]
network = NetworkMock()
dest_addr = 'tb1q5uy5xjcn55gwdkmghht8yp3vwz3088f6e3e0em'
sweep_coro = 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 = await sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=5000, locktime=2420006, tx_version=2)
tx_copy = tx_from_any(tx.serialize())
self.assertEqual('02000000015eb359ccfcd67c3e6b10bb937a796807007708c1f413840d0e627a3f94a1a48401000000484730440220043fc85a43e918ac41e494e309fdf204ca245d260cb5ea09108b196ca65d8a09022056f852f0f521e79ab2124d7e9f779c7290329ce5628ef8e92601980b065d3eb501fdffffff017f9e010000000000160014a709434b13a510e6db68bdd672062c70a2f39d3a26ed2400',
@ -1868,7 +1872,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('968a501350b954ecb51948202b8d0613aa84123ca9b745c14e208cb14feeff59', tx_copy.txid())
self.assertEqual('968a501350b954ecb51948202b8d0613aa84123ca9b745c14e208cb14feeff59', tx_copy.wtxid())
def test_sweep_uncompressed_p2pkh(self):
async def test_sweep_uncompressed_p2pkh(self):
class NetworkMock:
relay_fee = 1000
async def listunspent_for_scripthash(self, scripthash):
@ -1885,9 +1889,7 @@ class TestWalletSending(ElectrumTestCase):
privkeys = ['p2pkh:91gxDahzHiJ63HXmLP7pvZrkF8i5gKBXk4VqWfhbhJjtf6Ni5NU',]
network = NetworkMock()
dest_addr = 'tb1q3ws2p0qjk5vrravv065xqlnkckvzcpclk79eu2'
sweep_coro = 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 = await sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=5000, locktime=2420010, tx_version=2)
tx_copy = tx_from_any(tx.serialize())
self.assertEqual('02000000010615142d6c296f276c7da9fb2b09f655d594f73b76740404f1424c66c78ca715000000008a47304402206d2dae571ca2f51e0d4a8ce6a6335fa25ac09f4bbed26439124d93f035bdbb130220249dc2039f1da338a40679f0e79c25a2dc2983688e6c04753348f2aa8435e375014104b875ab889006d4a9be8467c9256cf54e1073f7f9a037604f571cc025bbf47b2987b4c862d5b687bb5328adccc69e67a17b109b6328228695a1c384573acd6199fdffffff0186500300000000001600148ba0a0bc12b51831f58c7ea8607e76c5982c071f2aed2400',
@ -1895,7 +1897,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('d62048493bf8459be5e1e3cab6caabc8f15661d02c364d8dc008297e573772bf', tx_copy.txid())
self.assertEqual('d62048493bf8459be5e1e3cab6caabc8f15661d02c364d8dc008297e573772bf', tx_copy.wtxid())
def test_sweep_compressed_p2pkh(self):
async def test_sweep_compressed_p2pkh(self):
class NetworkMock:
relay_fee = 1000
async def listunspent_for_scripthash(self, scripthash):
@ -1912,9 +1914,7 @@ class TestWalletSending(ElectrumTestCase):
privkeys = ['p2pkh:cN3LiXmurmGRF5xngYd8XS2ZsP2KeXFUh4SH7wpC8uJJzw52JPq1',]
network = NetworkMock()
dest_addr = 'tb1q782f750ekkxysp2rrscr6yknmn634e2pv8lktu'
sweep_coro = 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 = await sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=1000, locktime=2420010, tx_version=2)
tx_copy = tx_from_any(tx.serialize())
self.assertEqual('02000000016717835a2e1e152a69e7528a0f1346c1d37ee6e76c5e23b5d1c5a5b40241768a000000006a473044022038ad38003943bfd3ed39ba4340d545753fcad632a8fe882d01e4f0140ddb3cfb022019498260e29f5fbbcde9176bfb3553b7acec5fe284a9a3a33547a2d082b60355012103b875ab889006d4a9be8467c9256cf54e1073f7f9a037604f571cc025bbf47b29fdffffff0158de010000000000160014f1d49f51f9b58c4805431c303d12d3dcf51ae5412aed2400',
@ -1922,7 +1922,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('432c108626581fc6a7d3efc9dac5f3dec8286cec47dfaab86b4267d10381586c', tx_copy.txid())
self.assertEqual('432c108626581fc6a7d3efc9dac5f3dec8286cec47dfaab86b4267d10381586c', tx_copy.wtxid())
def test_sweep_p2wpkh_p2sh(self):
async def test_sweep_p2wpkh_p2sh(self):
class NetworkMock:
relay_fee = 1000
async def listunspent_for_scripthash(self, scripthash):
@ -1939,9 +1939,7 @@ class TestWalletSending(ElectrumTestCase):
privkeys = ['p2wpkh-p2sh:cQMRGsiEsFX5YoxVZaMEzBruAkCWnoFf1SG7SRm2tLHDEN165TrA',]
network = NetworkMock()
dest_addr = 'tb1qu7n2tzm90a3f29kvxlhzsc7t40ddk075ut5w44'
sweep_coro = 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 = await sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=500, locktime=2420010, tx_version=2)
tx_copy = tx_from_any(tx.serialize())
self.assertEqual('020000000001011d1725072a6e60687a59b878ecaf940ea0385880613d9d5502102bd78ef97b9a0000000017160014e7a6a58b657f629516cc37ee2863cbabdadb3fd4fdffffff01fc47020000000000160014e7a6a58b657f629516cc37ee2863cbabdadb3fd402473044022048ea4c558fd374f5d5066440a7f4933393cb377802cb949e3039fedf0378a29402204b4a58c591117cc1e37f07b03cc03cc6198dbf547e2bff813e2e2102bd2057e00121029f46ba81b3c6ad84e52841364dc54ca1097d0c30a68fb529766504c4b1c599352aed2400',
@ -1949,7 +1947,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('0680124954ccc158cbf24d289c93579f68fd75916509214066f69e09adda1861', tx_copy.txid())
self.assertEqual('da8567d9b28e9e0ed8b3dcef6e619eba330cec6cb0c55d57f658f5ca06e02eb0', tx_copy.wtxid())
def test_sweep_p2wpkh(self):
async def test_sweep_p2wpkh(self):
class NetworkMock:
relay_fee = 1000
async def listunspent_for_scripthash(self, scripthash):
@ -1966,9 +1964,7 @@ class TestWalletSending(ElectrumTestCase):
privkeys = ['p2wpkh:cV2BvgtpLNX328m4QrhqycBGA6EkZUFfHM9kKjVXjfyD53uNfC4q',]
network = NetworkMock()
dest_addr = 'tb1qhuy2e45lrdcp9s4ezeptx5kwxcnahzgpar9scc'
sweep_coro = 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 = await sweep(privkeys, network=network, config=self.config, to_address=dest_addr, fee=500, locktime=2420010, tx_version=2)
tx_copy = tx_from_any(tx.serialize())
self.assertEqual('02000000000101e328aeb4f9dc1b85a2709ce59b0478a15ed9fb5e7f84fb62422f99b8cd6ad7010000000000fdffffff01087e010000000000160014bf08acd69f1b7012c2b91642b352ce3627db89010247304402204993099c4663d92ef4c9a28b3f45a40a6585754fe22ecfdc0a76c43fda7c9d04022006a75e0fd3ad1862d8e81015a71d2a1489ec7a9264e6e63b8fe6bb90c27e799b0121038ca94e7c715152fd89803c2a40a934c7c4035fb87b3cba981cd1e407369cfe312aed2400',
@ -1977,7 +1973,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('b062d2e19880c66b36e80b823c2d00a2769658d1e574ff854dab15efd8fd7da8', tx_copy.wtxid())
@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(
keystore.from_seed('humor argue expand gain goat shiver remove morning security casual leopard degree', ''),
gap_limit=2,
@ -2061,7 +2057,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual((0, 10495000, 0), wallet2.get_balance())
@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,
then wallet.is_mine and wallet.can_sign should return False (e.g. multisig input for single-sig wallet).
(see issue #5948)
@ -2113,7 +2109,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertFalse(any([wallet_frost.is_mine(txout.address) for txout in tx.outputs()]))
@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
config = SimpleConfig({'electrum_path': self.electrum_path})
config.set_key('coin_chooser_output_rounding', False)
@ -2316,6 +2312,9 @@ class TestWalletSending(ElectrumTestCase):
class NetworkMock:
relay_fee = 1000
async def get_transaction(self, txid, timeout=None):
return self._gettx(txid)
@staticmethod
def _gettx(txid):
if txid == "597098f9077cd2a7bf5bb2a03c9ae5fcd9d1f07c0891cb42cbb129cf9eaf57fd":
return "02000000000102a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540000000000fdffffffbdeb0175b1c51c96843d1952f7e1c49c1703717d7d020048d4de0a8eed94dad50000000000fdffffff03b2a00700000000001600140cd6c9f8ce0aa73d77fcf7f156c74f5cbec6906bb2a00700000000001600146435504ddc95e6019a90bb7dfc7ca81a88a8633106d790000000000016001444bd3017ee214370abf683abaa7f6204c9f40210024730440220652a04a2a301d9a031a034f3ae48174e204e17acf7bfc27f0dcab14243f73e2202207b29e964c434dfb2c515232d36566a40dccd4dd93ccb7fd15260ecbda10f0d9801210231994e564a0530068d17a9b0f85bec58d1352517a2861ea99e5b3070d2c5dbda02473044022072186473874919019da0e3d92b6e0aa4f88cba448ed5434615e5a3c8e2b7c42a02203ec05cef66960d5bc45d0f3d25675190cf8035b11a05ed4b719fd9c3a894899b012102f5fdca8c4e30ba0a1babf9cf9ebe62519b08aead351c349ed1ffc8316c24f542d7f61c00"
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',
config=config)
wallet.network = NetworkMock()
wallet._get_rawtx_from_network = NetworkMock._gettx
# bootstrap wallet
funding_tx = Transaction('02000000000101a5883f3de780d260e6f26cf85144403c7744a65a44cd38f9ff45aecadf010c540100000000fdffffff0220a1070000000000160014db44724ac632ae47ee5765954d64796dd5fec72708de3c000000000016001424b32aadb42a89016c4de8f11741c3b29b15f21c02473044022045cc6c1cc875cbb0c0d8fe323dc1de9716e49ed5659741b0fb3dd9a196894066022077c242640071d12ec5763c5870f482a4823d8713e4bd14353dd621ed29a7f96d012102aea8d439a0f79d8b58e8d7bda83009f587e1f3da350adaa484329bf47cd03465fef61c00')
@ -2366,7 +2366,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual('3021a4fe24e33af9d0ccdf25c478387c97df671fe1fd8b4db0de4255b3a348c5', tx_copy.txid())
@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',
config=self.config, gap_limit=3)
@ -2408,7 +2408,7 @@ class TestWalletSending(ElectrumTestCase):
coins[0].prevout.to_str())
@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',
config=self.config, gap_limit=3)
@ -2432,7 +2432,7 @@ class TestWalletSending(ElectrumTestCase):
self.assertEqual(2, len(tx.outputs()))
@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(
"p2wpkh:cVcwSp488C8Riguq55Tuktgi6TpzuyLdDwUxkBDBz3yzV7FW4af2 p2wpkh:cPWyoPvnv2hiyyxbhMkhX3gPEENzB6DqoP9bbR8SDTg5njK5SL9n",
path='if_this_exists_mocking_failed_648151893',
@ -2468,7 +2468,7 @@ class TestWalletSending(ElectrumTestCase):
str(tx_copy))
@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(
"p2wpkh:cVcwSp488C8Riguq55Tuktgi6TpzuyLdDwUxkBDBz3yzV7FW4af2 p2wpkh:cPWyoPvnv2hiyyxbhMkhX3gPEENzB6DqoP9bbR8SDTg5njK5SL9n",
path='if_this_exists_mocking_failed_648151893',
@ -2503,7 +2503,7 @@ class TestWalletSending(ElectrumTestCase):
str(tx_copy))
@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(
"p2wpkh:cVcwSp488C8Riguq55Tuktgi6TpzuyLdDwUxkBDBz3yzV7FW4af2 p2wpkh:cPWyoPvnv2hiyyxbhMkhX3gPEENzB6DqoP9bbR8SDTg5njK5SL9n",
path='if_this_exists_mocking_failed_648151893',
@ -2548,7 +2548,7 @@ class TestWalletSending(ElectrumTestCase):
str(tx_copy))
@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',
config=self.config)
@ -2584,7 +2584,7 @@ class TestWalletSending(ElectrumTestCase):
{txi.prevout.to_str() for txi in wallet.get_spendable_coins()})
@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
the PSBT_GLOBAL_XPUB field with wallet xpubs.
"""
@ -2658,7 +2658,7 @@ class TestWalletSending(ElectrumTestCase):
tx.serialize_as_bytes().hex())
@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
the PSBT_GLOBAL_XPUB field with wallet xpubs.
"""
@ -2701,7 +2701,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.config = SimpleConfig({'electrum_path': self.electrum_path})
@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(
keystore.from_seed('alone body father children lead goodbye phone twist exist grass kick join', '', False),
gap_limit=4,
@ -2746,7 +2746,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('06032230d0bf6a277bc4f8c39e3311a712e0e614626d0dea7cc9f592abfae5d8', tx.wtxid())
@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(
# bip39: "qwe", der: m/44'/1'/0'
keystore.from_xprv('tprv8gfKwjuAaqtHgqxMh1tosAQ28XvBMkcY5NeFRA3pZMpz6MR4H4YZ3MJM4fvNPnRKeXR1Td2vQGgjorNXfo94WvT5CYDsPAqjHxSn436G1Eu'),
@ -2800,7 +2800,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('d9c21696eca80321933e7444ca928aaf25eeda81aaa2f4e5c085d4d0a9cf7aa7', tx.wtxid())
@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(
# bip39: "qwe", der: m/49'/1'/0'
keystore.from_xprv('uprv8zHHrMQMQ26utWwNJ5MK2SXpB9hbmy7pbPaneii69xT8cZTyFpxQFxkknGWKP8dxBTZhzy7yP6cCnLrRCQjzJDk3G61SjZpxhFQuB2NR8a5'),
@ -2845,7 +2845,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('27b78ec072a403b0545258e7a1a8d494e4b6fd48bf77f4251a12160c92207cbc', tx.wtxid())
@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(
# bip39: "qwe", der: m/84'/1'/0'
keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'),
@ -2900,7 +2900,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('484e350beaa722a744bb3e2aa38de005baa8526d86536d6143e5814355acf775', tx.wtxid())
@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(
# bip39: "qwe", der: m/84'/1'/0'
keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'),
@ -2945,7 +2945,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('484e350beaa722a744bb3e2aa38de005baa8526d86536d6143e5814355acf775', tx.wtxid())
@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.
# tx has input with key beyond gap limit
wallet_offline = WalletIntegrityHelper.create_standard_wallet(
@ -2966,7 +2966,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
str(tx))
@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.import_private_key('p2pkh:cQDxbmQfwRV3vP1mdnVHq37nJekHLsuD3wdSQseBRA2ct4MFk5Pq', password=None)
wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config)
@ -3003,7 +3003,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.wtxid())
@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.import_private_key('p2wpkh-p2sh:cU9hVzhpvfn91u2zTVn8uqF2ymS7ucYH8V5TmsTDmuyMHgRk9WsJ', password=None)
wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config)
@ -3040,7 +3040,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('9bb9949974954613945756c48ca5525cd5cba1b667ccb10c7a53e1ed076a1117', tx.wtxid())
@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.import_private_key('p2wpkh:cPuQzcNEgbeYZ5at9VdGkCwkPA9r34gvEVJjuoz384rTfYpahfe7', password=None)
wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config)
@ -3077,7 +3077,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('3b7cc3c3352bbb43ddc086487ac696e09f2863c3d9e8636721851b8008a83ffa', tx.wtxid())
@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(
# bip39: "qwe", der: m/44'/1'/0'
keystore.from_xprv('tprv8gfKwjuAaqtHgqxMh1tosAQ28XvBMkcY5NeFRA3pZMpz6MR4H4YZ3MJM4fvNPnRKeXR1Td2vQGgjorNXfo94WvT5CYDsPAqjHxSn436G1Eu'),
@ -3118,7 +3118,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('e56da664631b8c666c6df38ec80c954c4ac3c4f56f040faf0070e4681e937fc4', tx.wtxid())
@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(
# bip39: "qwe", der: m/49'/1'/0'
keystore.from_xprv('uprv8zHHrMQMQ26utWwNJ5MK2SXpB9hbmy7pbPaneii69xT8cZTyFpxQFxkknGWKP8dxBTZhzy7yP6cCnLrRCQjzJDk3G61SjZpxhFQuB2NR8a5'),
@ -3159,7 +3159,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('9bb9949974954613945756c48ca5525cd5cba1b667ccb10c7a53e1ed076a1117', tx.wtxid())
@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(
# bip39: "qwe", der: m/84'/1'/0'
keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'),
@ -3200,7 +3200,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('3b7cc3c3352bbb43ddc086487ac696e09f2863c3d9e8636721851b8008a83ffa', tx.wtxid())
@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
wallet_offline1 = WalletIntegrityHelper.create_multisig_wallet(
[
@ -3265,7 +3265,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('0e8fdc8257a85ebe7eeab14a53c2c258c61a511f64176b7f8fc016bc2263d307', tx.wtxid())
@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
wallet_offline1 = WalletIntegrityHelper.create_multisig_wallet(
[
@ -3334,7 +3334,7 @@ class TestWalletOfflineSigning(ElectrumTestCase):
self.assertEqual('96d0bca1001778c54e4c3a07929fab5562c5b5a23fd1ca3aa3870cc5df2bf97d', tx.wtxid())
@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
wallet_offline1 = WalletIntegrityHelper.create_multisig_wallet(
[
@ -3439,7 +3439,7 @@ class TestWalletHistory_SimpleRandomOrder(ElectrumTestCase):
return w
@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()
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]])
@ -3447,7 +3447,7 @@ class TestWalletHistory_SimpleRandomOrder(ElectrumTestCase):
self.assertEqual(27633300, sum(w.get_balance()))
@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()
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]])
@ -3455,7 +3455,7 @@ class TestWalletHistory_SimpleRandomOrder(ElectrumTestCase):
self.assertEqual(27633300, sum(w.get_balance()))
@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()
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]])
@ -3488,7 +3488,7 @@ class TestWalletHistory_EvilGapLimit(ElectrumTestCase):
return w
@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.db.put('stored_height', 1316917 + 100)
for txid in self.transactions:
@ -3537,7 +3537,7 @@ class TestWalletHistory_DoubleSpend(ElectrumTestCase):
self.config = SimpleConfig({'electrum_path': self.electrum_path})
@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",
path='if_this_exists_mocking_failed_648151893',
gap_limit=5,
@ -3551,7 +3551,7 @@ class TestWalletHistory_DoubleSpend(ElectrumTestCase):
self.assertEqual(999890, sum(w.get_balance()))
@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",
path='if_this_exists_mocking_failed_648151893',
gap_limit=5,
@ -3588,7 +3588,7 @@ class TestImportedWallet(ElectrumTestCase):
self.config = SimpleConfig({'electrum_path': self.electrum_path})
@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",
path='if_this_exists_mocking_failed_648151893',
config=self.config)['wallet'] # type: Abstract_Wallet

23
electrum/util.py

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

10
electrum/wallet.py

@ -295,6 +295,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
txin_type: str
wallet_type: str
lnworker: Optional['LNWallet']
network: Optional['Network']
def __init__(self, db: WalletDB, storage: Optional[WalletStorage], *, config: SimpleConfig):
@ -2187,6 +2188,12 @@ class Abstract_Wallet(ABC, Logger, EventListener):
return True
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]:
# First look up an input transaction in the wallet where it
# 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)
if not tx and self.network and self.network.has_internet_connection():
try:
raw_tx = self.network.run_from_another_thread(
self.network.get_transaction(tx_hash, timeout=10))
raw_tx = self._get_rawtx_from_network(tx_hash)
except NetworkException as e:
_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')

Loading…
Cancel
Save