diff --git a/electrum/plugins/joinmarket/jm_main.py b/electrum/plugins/joinmarket/jm_main.py index 26224ffa9..b0a605237 100644 --- a/electrum/plugins/joinmarket/jm_main.py +++ b/electrum/plugins/joinmarket/jm_main.py @@ -35,6 +35,7 @@ class JMManager(Logger): self.tumble_log = get_tumble_log(self, logsdir, config) self._state = JMStates.Unsupported + self._stopped = False self.jmw = JMWallet(self) self.jmconf = JMConf(self) self.jmw.jmconf = self.jmconf @@ -111,6 +112,10 @@ class JMManager(Logger): '''JM was enabled on this wallet''' return self.state not in [JMStates.Unsupported, JMStates.Disabled] + @property + def stopped(self): + return self._stopped + def enable_jm(self): '''Enables JM on this wallet, store changes in db.''' if not self.enabled: @@ -151,8 +156,9 @@ class JMManager(Logger): self.jmw.on_network_start(network) asyncio.ensure_future(self.trigger_postponed_notifications()) - def on_stop_threads(self): + def stop(self): '''Run when the wallet is unloaded/stopped''' + self._stopped = True self.jmw.unregister_callbacks() def postpone_notification(self, event, *args): diff --git a/electrum/plugins/joinmarket/jm_wallet.py b/electrum/plugins/joinmarket/jm_wallet.py index d219a3052..944c21fce 100644 --- a/electrum/plugins/joinmarket/jm_wallet.py +++ b/electrum/plugins/joinmarket/jm_wallet.py @@ -430,7 +430,7 @@ class JMWallet(KeyPairsMixin, WalletDBMixin, JMBaseCodeMixin, EventListener): result = [] w = self.wallet with w.lock: - while len(result) < addrs_count: + while len(result) < addrs_count and not self.jmman.stopped: if internal: unused = w.calc_unused_change_addresses() else: @@ -467,7 +467,7 @@ class JMWallet(KeyPairsMixin, WalletDBMixin, JMBaseCodeMixin, EventListener): gen_cnt = 0 # num new addresses we generated limit = self.jmconf.gaplimit - while True: + while True and not self.jmman.stopped: jm_addrs = self.get_jm_addresses(mixdepth=mixdepth, internal=internal) addr_cnt = len(jm_addrs) @@ -515,7 +515,7 @@ class JMWallet(KeyPairsMixin, WalletDBMixin, JMBaseCodeMixin, EventListener): self.logger.info("taskgroup stopped.") async def do_synchronize_loop(self): - while True: + while True and not self.jmman.stopped: if self.jmman.enabled: # note: we only generate new HD addresses if the existing ones # have history that are mined and SPV-verified. diff --git a/electrum/plugins/joinmarket/plugin.py b/electrum/plugins/joinmarket/plugin.py index 3c2c7efda..ddc552e6c 100644 --- a/electrum/plugins/joinmarket/plugin.py +++ b/electrum/plugins/joinmarket/plugin.py @@ -36,6 +36,6 @@ class JoinMarketPlugin(BasePlugin): def close_wallet(self, wallet): if wallet not in self._wallets: return - wallet.jmman.on_stop_threads() + wallet.jmman.stop() del wallet.jmman self._wallets.remove(wallet) diff --git a/electrum/plugins/joinmarket/tests/__init__.py b/electrum/plugins/joinmarket/tests/__init__.py index bb73d4972..b543bdfb1 100644 --- a/electrum/plugins/joinmarket/tests/__init__.py +++ b/electrum/plugins/joinmarket/tests/__init__.py @@ -283,9 +283,8 @@ class JMTestCase(ElectrumTestCase): async def asyncSetUp(self): await super().asyncSetUp() - patcher = mock.patch.object(storage.WalletStorage, 'write') - self.mock_save_db = patcher.start() - self.addCleanup(patcher.stop) + self.patcher = mock.patch.object(storage.WalletStorage, 'write') + self.patcher.start() self.asyncio_loop = util.get_asyncio_loop() self.config = SimpleConfig({'electrum_path': self.electrum_path}) @@ -318,3 +317,8 @@ class JMTestCase(ElectrumTestCase): w.adb.add_transaction(Transaction(tx1_str)) w.adb.add_verified_tx(tx1_txid, util.TxMinedInfo( int(1e6), '', '', '', '')) + + async def asyncTearDown(self): + self.jmman.stop() + await self.w.stop() + self.patcher.stop() diff --git a/electrum/plugins/joinmarket/tests/test_jm_main.py b/electrum/plugins/joinmarket/tests/test_jm_main.py index cc1e9a19d..71f5ecd9e 100644 --- a/electrum/plugins/joinmarket/tests/test_jm_main.py +++ b/electrum/plugins/joinmarket/tests/test_jm_main.py @@ -21,9 +21,8 @@ class JMManagerInitTestCase(ElectrumTestCase): async def asyncSetUp(self): await super().asyncSetUp() - patcher = mock.patch.object(storage.WalletStorage, 'write') - self.mock_save_db = patcher.start() - self.addCleanup(patcher.stop) + self.patcher = mock.patch.object(storage.WalletStorage, 'write') + self.patcher.start() self.asyncio_loop = util.get_asyncio_loop() self.config = SimpleConfig({'electrum_path': self.electrum_path}) @@ -42,6 +41,10 @@ class JMManagerInitTestCase(ElectrumTestCase): self.w.db.put('stored_height', int(1e7)) self.network = NetworkMock(self.asyncio_loop, self.config, w) + async def asyncTearDown(self): + await self.w.stop() + self.patcher.stop() + async def test_init(self): w = self.w keystore = w.db.get('keystore') @@ -73,6 +76,7 @@ class JMManagerInitTestCase(ElectrumTestCase): await jmman._enable_jm() assert jmman.enabled assert jmman.state == JMStates.Ready + jmman.stop() async def test_init_on_mainnet(self): w = self.w @@ -105,6 +109,7 @@ class JMManagerInitTestCase(ElectrumTestCase): assert db.get('jm_addresses') is not None assert db.get('jm_commitments') is not None assert db.get('jm_txs') is not None + jmman.stop() async def test_enable_jm(self): w = self.w @@ -123,3 +128,4 @@ class JMManagerInitTestCase(ElectrumTestCase): enabled = await jmman._enable_jm() assert jmman.enabled assert not enabled + jmman.stop()