From 77496cdcc7259098b5bfe60cea8a2df958b7afdb Mon Sep 17 00:00:00 2001 From: Adam Gibson Date: Thu, 2 Jun 2022 15:42:23 -0500 Subject: [PATCH] Disallow RPC directsend if coinjoin state enabled Fixes #1215. Prior to this commit it was possible to send a non-coinjoin transaction while the maker service was running, over the RPC-API call direct-send. However this was not intentional, as it is not usually safe to perform UTXO database write actions when the effective 'lock' on write actions is already held by a long running service/action such as yield generator or tumbler. This commit prevents that call being successful if the current state of the JMWalletDaemon is not CJ_NOT_RUNNING. This commit also disables freeze actions in the same situation, and changes the HTTP error code for these cases to 400. --- jmclient/jmclient/wallet_rpc.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/jmclient/jmclient/wallet_rpc.py b/jmclient/jmclient/wallet_rpc.py index 04de244..a3f3afd 100644 --- a/jmclient/jmclient/wallet_rpc.py +++ b/jmclient/jmclient/wallet_rpc.py @@ -54,6 +54,11 @@ class InvalidRequestFormat(Exception): class BackendNotReady(Exception): pass +# error class for actions which are inconsistent with +# current state +class ActionNotAllowed(Exception): + pass + # error class for services which are only # started once: class ServiceAlreadyStarted(Exception): @@ -273,6 +278,11 @@ class JMWalletDaemon(Service): request.setHeader('Content-Type', 'application/json') return json.dumps({"message": message}) + @app.handle_errors(ActionNotAllowed) + def not_allowed(self, request, failure): + request.setResponseCode(400) + return self.err(request, "Action not allowed") + @app.handle_errors(NotAuthorized) def not_authorized(self, request, failure): request.setResponseCode(401) @@ -607,6 +617,13 @@ class JMWalletDaemon(Service): raise NoWalletFound() if not self.wallet_name == walletname: raise InvalidRequestFormat() + # This is a synchronous operation (no delay is expected), + # hence the reference to the CJ_* lock is really just a gate + # on performing the action (so simpler to not update the + # state, otherwise we would have to revert it correctly in + # all error conditions). + if not self.coinjoin_state == CJ_NOT_RUNNING: + raise ActionNotAllowed() try: tx = direct_send(self.services["wallet"], int(payment_info_json["amount_sats"]), @@ -981,6 +998,9 @@ class JMWalletDaemon(Service): valid, txidindex = utxostr_to_utxo(freeze_json["utxo-string"]) if not valid: raise InvalidRequestFormat() + # Do not update wallet state if coinjoin services are active + if not self.coinjoin_state == CJ_NOT_RUNNING: + raise ActionNotAllowed() txid, index = txidindex try: # note: this does not raise or fail if the applied