From d493343858fd5b1256bd30455e6334dc1e6a3053 Mon Sep 17 00:00:00 2001 From: Adam Gibson Date: Sat, 5 Feb 2022 14:46:49 +0000 Subject: [PATCH] Ensure coinjoin state is reset if wallet switches. Prior to this commit, a lock of one wallet followed by an unlock of another, or an unlock of a new wallet, overriding the old one, if it occurred during a running taker-side coinjoin, would not reset the coinjoin_state to CJ_NOT_RUNNING, resulting in an inability of the newly loaded wallet to function correctly. After this commit, all subservices, including the quasi-service of taker-side coinjoin, are fully shut down whenever a wallet is locked, or a new wallet is unlocked. This may be suboptimal (see TODO) but is logical for now. The main effect is to ensure that a new wallet will always start in the correct coinjoin state (CJ_NOT_RUNNING). Also worth noting, a running Taker will have its abort parameter set to True on the wallet lock event, meaning that it will not proceed to the next step on the next asynchronous message coming from counterparties. --- jmclient/jmclient/wallet_rpc.py | 41 ++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/jmclient/jmclient/wallet_rpc.py b/jmclient/jmclient/wallet_rpc.py index df5c4f5..583904c 100644 --- a/jmclient/jmclient/wallet_rpc.py +++ b/jmclient/jmclient/wallet_rpc.py @@ -217,19 +217,40 @@ class JMWalletDaemon(Service): return (listener_rpc, listener_ws) def stopService(self): - """ Encapsulates shut down actions. + """ Top-level service (JMWalletDaemon itself) shutdown. + """ + self.stopSubServices() + super().stopService() + + def stopSubServices(self): + """ This: + - shuts down the wallet service, and deletes its name. + - removes the currently valid auth token. + - shuts down any other running sub-services, such as yieldgenerator. + - shuts down (aborts) any taker-side coinjoining happening. """ # Currently valid authorization tokens must be removed # from the daemon: self.cookie = None if self.wss_factory: self.wss_factory.valid_token = None + self.wallet_name = None # if the wallet-daemon is shut down, all services # it encapsulates must also be shut down. for name, service in self.services.items(): if service: service.stopService() - super().stopService() + # these Services cannot be guaranteed to be + # re-startable (the WalletService for example, + # is explicitly not). So we remove these references + # after stopping. + for n in self.services: + self.services[n] = None + # taker is not currently encapsulated with a Service; + # if it is running, shut down: + if self.coinjoin_state == CJ_TAKER_RUNNING: + self.taker.aborted = True + self.taker_finished(False) def err(self, request, message): """ Return errors in a standard format. @@ -370,7 +391,7 @@ class JMWalletDaemon(Service): # are any. # This will stop all supporting services and wipe # state (so wallet, maker service and cookie/token): - self.stopService() + self.stopSubServices() self.services["wallet"] = WalletService(wallet) # restart callback needed, otherwise wallet creation will @@ -656,11 +677,12 @@ class JMWalletDaemon(Service): # lock multiple times: already_locked = True else: - self.services["wallet"].stopService() - self.cookie = None - self.wss_factory.valid_token = None - self.services["wallet"] = None - self.wallet_name = None + # notice that here a wallet locking event shuts down + # everything. + # TODO: changing this so a maker can run in the background + # while locked, will require auto-detection of coinjoin + # state on future unlock. + self.stopSubServices() already_locked = False return make_jmwalletd_response(request, walletname=walletname, already_locked=already_locked) @@ -926,6 +948,9 @@ class JMWalletDaemon(Service): raise InvalidRequestFormat() if not self.coinjoin_state == CJ_TAKER_RUNNING: raise ServiceNotStarted() + # prevent the next step, responding to AMP messages + # from jmdaemon backend, from continuing: + self.taker.aborted = True self.taker_finished(False) return make_jmwalletd_response(request, status=202)