Repository: spesmilo/electrum
Issue: qt gui won't appear: deadlock

On git master 180480099926946de19e6f5db8296fc5b56683dc.

Only happened once.
Started Electrum but the GUI would not appear. Waited a minute or so.

```
Traceback (most recent call last):
  File "/home/user/wspace/electrum/gui/qt/__init__.py", line 217, in start_new_window
    w = self.create_window_for_wallet(wallet)
  File "/home/user/wspace/electrum/gui/qt/__init__.py", line 179, in create_window_for_wallet
    w = ElectrumWindow(self, wallet)
  File "/home/user/wspace/electrum/gui/qt/main_window.py", line 201, in __init__
    self.load_wallet(wallet)
  File "/home/user/wspace/electrum/lib/util.py", line 275, in <lambda>
    return lambda *args, **kw_args: do_profile(func, args, kw_args)
  File "/home/user/wspace/electrum/lib/util.py", line 271, in do_profile
    o = func(*args, **kw_args)
  File "/home/user/wspace/electrum/gui/qt/main_window.py", line 344, in load_wallet
    self.history_list.update()
  File "/home/user/wspace/electrum/gui/qt/util.py", line 484, in update
    self.on_update()
  File "/home/user/wspace/electrum/lib/util.py", line 275, in <lambda>
    return lambda *args, **kw_args: do_profile(func, args, kw_args)
  File "/home/user/wspace/electrum/lib/util.py", line 271, in do_profile
    o = func(*args, **kw_args)
  File "/home/user/wspace/electrum/gui/qt/history_list.py", line 202, in on_update
    r = self.wallet.get_full_history(domain=self.get_domain(), from_timestamp=self.start_timestamp, to_timestamp=self.end_timestamp, fx=fx)
  File "/home/user/wspace/electrum/lib/util.py", line 275, in <lambda>
    return lambda *args, **kw_args: do_profile(func, args, kw_args)
  File "/home/user/wspace/electrum/lib/util.py", line 271, in do_profile
    o = func(*args, **kw_args)
  File "/home/user/wspace/electrum/lib/wallet.py", line 985, in get_full_history
    h = self.get_history(domain)
  File "/home/user/wspace/electrum/lib/wallet.py", line 953, in get_history
    c, u, x = self.get_balance(domain)
  File "/home/user/wspace/electrum/lib/wallet.py", line 713, in get_balance
    c, u, x = self.get_addr_balance(addr)
  File "/home/user/wspace/electrum/lib/wallet.py", line 659, in get_addr_balance
    received, sent = self.get_addr_io(address)
  File "/home/user/wspace/electrum/lib/wallet.py", line 620, in get_addr_io
    h = self.get_address_history(address)
  File "/home/user/wspace/electrum/lib/wallet.py", line 724, in get_address_history
    tx_height = self.get_tx_height(tx_hash)[0]
  File "/home/user/wspace/electrum/lib/wallet.py", line 459, in get_tx_height
    with self.lock:
KeyboardInterrupt
```

<details>
 <summary>full log</summary>

```
$ ./electrum -v --testnet
[SimpleConfig] electrum directory /home/user/.electrum/testnet
[get_server] [Errno 111] Connection refused
[Plugins] registering hardware digitalbitbox: ('hardware', 'digitalbitbox', 'Digital Bitbox wallet')
[Plugins] registering hardware keepkey: ('hardware', 'keepkey', 'KeepKey wallet')
[Plugins] registering hardware ledger: ('hardware', 'ledger', 'Ledger wallet')
[Plugins] registering hardware trezor: ('hardware', 'trezor', 'TREZOR wallet')
[Plugins] registering wallet type ('2fa', 'trustedcoin')
[profiler] __init__ 0.0031
cannot connect fork_0_1249088
cannot connect fork_0_1202455
cannot connect fork_0_1227602
cannot connect fork_0_1210726
cannot connect fork_0_1231244
cannot connect fork_0_1251380
cannot connect fork_0_1230884
cannot connect fork_0_1202487
cannot connect fork_0_1253281
cannot connect fork_0_1230299
cannot connect fork_0_1210502
cannot connect fork_0_1251316
cannot connect fork_0_1210720
cannot connect fork_0_1230929
cannot connect fork_0_1210707
cannot connect fork_0_1210467
cannot connect fork_0_1251387
cannot connect fork_0_1196375
cannot connect fork_0_1251366
cannot connect fork_0_1253410
cannot connect fork_0_1251305
cannot connect fork_0_1231249
cannot connect fork_0_1251344
cannot connect fork_0_1230934
[Network] blockchains dict_keys([0, 1266178, 1265667, 1266180, 1254742, 1266183, 1275912, 1280522, 1276431, 1262097, 1266195, 1266197, 1264518, 1266199, 1266200, 1254425, 1266204, 1257501, 1264475, 1264348, 1256746, 1276967, 1277996, 1270194, 1275955, 1266441, 1280568, 1254970, 1266173, 1275964, 1276989, 1276991, 1275975, 1262007, 1254988, 1264482, 1255503, 1261140, 1261156, 1266191, 1281628, 1255525, 1255522, 1276091, 1280612, 1256037, 1255375, 1257065, 1257069, 1257491, 1256126, 1258601, 1268856, 1276025, 1266026, 1261183, 1270208, 1276034, 1261187, 1276039, 1255563, 1257154, 1266318, 1255055, 1255571, 1256086, 1278105, 1266333, 1266336, 1278114, 1276070, 1266343, 1276073, 1266347, 1255597, 1266351, 1266354, 1257139, 1255094, 1254277, 1256820, 1255099, 1255612, 1264317, 1266366, 1258874, 1276096, 1266563, 1266370, 1254796, 1264473, 1254607, 1256824, 1266386, 1266389, 1264342, 1276123, 1255132, 1266397, 1257182, 1266400, 1266402, 1257189, 1266406, 1254119, 1264525, 1257193, 1266410, 1283995, 1261949, 1257201, 1254130, 1264371, 1266420, 1256063, 1265917, 1257173, 1257216, 1266433, 1265617, 1257220, 1266438, 1257223, 1264392, 1264428, 1264394, 1280271, 1266448, 1280273, 1261952, 1275667, 1266078, 1266456, 1261850, 1266460, 1266463, 1265952, 1254950, 1260836, 1266566, 1260839, 1255209, 1255722, 1275692, 1266478, 1260847, 1256240, 1265969, 1265971, 1258805, 1266487, 1260858, 1265979, 1266492, 1260861, 1258646, 1266497, 1275787, 1265989, 1256844, 1265994, 1254731, 1255244, 1255758, 1254282, 1254225, 1275730, 1260883, 1255252, 1264470, 1255769, 1275738, 1266011, 1264361, 1266013, 1254238, 1255776, 1256802, 1260899, 1264485, 1255271, 1266025, 1264490, 1256811, 1261970, 1261935, 1256818, 1258867, 1261940, 1266038, 1261943, 1258872, 1265556, 1266042, 1275773, 1254272, 1275777, 1257167, 1264515, 1256837, 1261958, 1270223, 1261961, 1257866, 1266059, 1275788, 1266061, 1264528, 1266065, 1266048, 1265987, 1266068, 1275799, 1275800, 1266074, 1269147, 1265566, 1270175, 1254304, 1266081, 1261979, 1265576, 1270185, 1280938, 1266091, 1255581, 1261855, 1266098, 1270195, 1265981, 1265590, 1254839, 1265592, 1275833, 1264458, 1275838, 1275840, 1275844, 1275846, 1265609, 1260867, 1270219, 1265613, 1254863, 1265616, 1275857, 1254358, 1254871, 1255385, 1264499, 1270237, 1275871, 1257442, 1255396, 1266151, 1265641, 1255405, 1265661, 1265648, 1266161, 1276914, 1264467, 1270262, 1266169, 1262076, 1265578, 1266174])
[Network] starting network
[Network] connecting to testnet.qtornado.com:51002:s as new interface
[FxThread] using exchange BitcoinAverage
[BitcoinAverage] getting fx quotes for USD
[207.154.223.80] connected
[daedalus.bauerj.eu] connected
[btc.cihar.com] SSL certificate signed by CA
[btc.cihar.com] connected
[WalletStorage] wallet path /home/user/.electrum/testnet/wallets/test_segwit_asdasd
[profiler] load_transactions 0.0040
[profiler] build_spent_outpoints 0.0001
[profiler] check_history 0.0006
[helicarrier.bauerj.eu] connected
[electrumx.westeurope.cloudapp.azure.com] connected
[testnet.qtornado.com] connected
[Network] switching to testnet.qtornado.com:51002:s
[Network] sending subscriptions to testnet.qtornado.com:51002:s 0 0
[aspinall.io] connected
[Network] []
[Network] fee_estimates[25] 1019
[Network] fee_estimates[10] 1019
[Network] fee_estimates[5] 1019
[Network] fee_estimates[2] 1019
[Network] relayfee 1000
[testnetnode.arihanc.com] connected
[E-X.not.fyi] connected
[Synchronizer] receiving history tb1q4g0ka0qclegdand85pvlgs8f3kpzg2v22dtzvh 1
[Synchronizer] receiving history tb1qzrw2asd20uasg76f732sg4w2sqjultkzpuu3d3 2
[SPV] requested merkle 1df36ccfc9bb633fde6a1d669ac65284ed7013280ca44dde779330f1a3474dca
[Synchronizer] receiving history tb1qpx95rr7mv0atyhnlk70sur3tawk5w6t62r8egt 1
[electrum.hsmiths.com] connected
[BitcoinAverage] received fx quotes
^CTraceback (most recent call last):
  File "/home/user/wspace/electrum/gui/qt/__init__.py", line 217, in start_new_window
    w = self.create_window_for_wallet(wallet)
  File "/home/user/wspace/electrum/gui/qt/__init__.py", line 179, in create_window_for_wallet
    w = ElectrumWindow(self, wallet)
  File "/home/user/wspace/electrum/gui/qt/main_window.py", line 201, in __init__
    self.load_wallet(wallet)
  File "/home/user/wspace/electrum/lib/util.py", line 275, in <lambda>
    return lambda *args, **kw_args: do_profile(func, args, kw_args)
  File "/home/user/wspace/electrum/lib/util.py", line 271, in do_profile
    o = func(*args, **kw_args)
  File "/home/user/wspace/electrum/gui/qt/main_window.py", line 344, in load_wallet
    self.history_list.update()
  File "/home/user/wspace/electrum/gui/qt/util.py", line 484, in update
    self.on_update()
  File "/home/user/wspace/electrum/lib/util.py", line 275, in <lambda>
    return lambda *args, **kw_args: do_profile(func, args, kw_args)
  File "/home/user/wspace/electrum/lib/util.py", line 271, in do_profile
    o = func(*args, **kw_args)
  File "/home/user/wspace/electrum/gui/qt/history_list.py", line 202, in on_update
    r = self.wallet.get_full_history(domain=self.get_domain(), from_timestamp=self.start_timestamp, to_timestamp=self.end_timestamp, fx=fx)
  File "/home/user/wspace/electrum/lib/util.py", line 275, in <lambda>
    return lambda *args, **kw_args: do_profile(func, args, kw_args)
  File "/home/user/wspace/electrum/lib/util.py", line 271, in do_profile
    o = func(*args, **kw_args)
  File "/home/user/wspace/electrum/lib/wallet.py", line 985, in get_full_history
    h = self.get_history(domain)
  File "/home/user/wspace/electrum/lib/wallet.py", line 953, in get_history
    c, u, x = self.get_balance(domain)
  File "/home/user/wspace/electrum/lib/wallet.py", line 713, in get_balance
    c, u, x = self.get_addr_balance(addr)
  File "/home/user/wspace/electrum/lib/wallet.py", line 659, in get_addr_balance
    received, sent = self.get_addr_io(address)
  File "/home/user/wspace/electrum/lib/wallet.py", line 620, in get_addr_io
    h = self.get_address_history(address)
  File "/home/user/wspace/electrum/lib/wallet.py", line 724, in get_address_history
    tx_height = self.get_tx_height(tx_hash)[0]
  File "/home/user/wspace/electrum/lib/wallet.py", line 459, in get_tx_height
    with self.lock:
KeyboardInterrupt
[WalletStorage] saved /home/user/.electrum/testnet/wallets/test_segwit_asdasd
[profiler] write 0.3164
[profiler] save_transactions 0.3329
[E-X.not.fyi] timeout 2
[testnet.qtornado.com] timeout 1
[testnetnode.arihanc.com] timeout 2
[Network] connecting to testnet.qtornado.com:51002:s as new interface
[testnet.qtornado.com] connected
[Network] switching to testnet.qtornado.com:51002:s
[Network] sending subscriptions to testnet.qtornado.com:51002:s 1 262
[SPV] verified 1df36ccfc9bb633fde6a1d669ac65284ed7013280ca44dde779330f1a3474dca
[Network] []
[Network] fee_estimates[25] 1019
[Network] fee_estimates[10] 1019
[Network] fee_estimates[5] 1019
[Network] fee_estimates[2] 1019
[Network] relayfee 1000
[electrum.poorcoding.com] connected
[Plugins] stopped
[Network] stopping network
[Network] stopped
[profiler] save_transactions 0.0018
[WalletStorage] saved /home/user/.electrum/testnet/wallets/test_segwit_asdasd
[profiler] write 0.0351
[Daemon] shutting down network
[Daemon] stopped
```
</details>


Changed files:
lib/wallet.py

diff --git a/lib/wallet.py b/lib/wallet.py
index 20a124909..3bfd3c08c 100644
--- a/lib/wallet.py
+++ b/lib/wallet.py
@@ -718,7 +718,9 @@ class Abstract_Wallet(PrintError):
 
     def get_address_history(self, addr):
         h = []
-        with self.transaction_lock:
+        # we need self.transaction_lock but get_tx_height will take self.lock
+        # so we need to take that too here, to enforce order of locks
+        with self.lock, self.transaction_lock:
             for tx_hash in self.transactions:
                 if addr in self.txi.get(tx_hash, []) or addr in self.txo.get(tx_hash, []):
                     tx_height = self.get_tx_height(tx_hash)[0]
@@ -775,7 +777,9 @@ class Abstract_Wallet(PrintError):
             return conflicting_txns
 
     def add_transaction(self, tx_hash, tx):
-        with self.transaction_lock:
+        # we need self.transaction_lock but get_tx_height will take self.lock
+        # so we need to take that too here, to enforce order of locks
+        with self.lock, self.transaction_lock:
             # NOTE: returning if tx in self.transactions might seem like a good idea
             # BUT we track is_mine inputs in a txn, and during subsequent calls
             # of add_transaction tx, we might learn of more-and-more inputs of