# Buggy code
def rpc(self, method, args):
    """Returns the result of an rpc call to the Bitcoin Core RPC API.
    If the connection is permanently or unrecognizably broken, None
    is returned *and the reactor is shutdown* (because we consider this
    condition unsafe - TODO possibly create a "freeze" mode that could
    restart when the connection is healed, but that is tricky).
    """
    if method not in [
        "importaddress",
        "walletpassphrase",
        "getaccount",
        "gettransaction",
        "getrawtransaction",
        "gettxout",
        "importmulti",
        "listtransactions",
        "getblockcount",
        "scantxoutset",
    ]:
        log.debug("rpc: " + method + " " + str(args))
    try:
        res = self.jsonRpc.call(method, args)
    except JsonRpcConnectionError as e:
        # note that we only raise this in case the connection error is
        # a refusal, or is unrecognized/unknown by our code. So this does
        # NOT happen in a reset or broken pipe scenario.
        # It is safest to simply shut down.
        # Why not sys.exit? sys.exit calls do *not* work inside delayedCalls
        # or deferreds in twisted, since a bare exception catch prevents
        # an actual system exit (i.e. SystemExit is caught as a
        # BareException type).
        log.error(
            "Failure of RPC connection to Bitcoin Core. "
            "Application cannot continue, shutting down."
        )
        if reactor.running:
            reactor.stop()
        return None
    return res


# Traceback (jmclient.storage.StorageError)
2020-08-20 13:51:53,349 [DEBUG]  >>privmsg on darkscience: nick=JXXX cmd=pubkey msg=8XXX
2020-08-20 13:52:02,337 [DEBUG]  mix depths that have enough = {0: XXX}
2020-08-20 13:52:02,338 [INFO]  filling offer, mixdepth=0, amount=2XXX
2020-08-20 13:52:02,893 [INFO]  sending output to address=3XXX
Unhandled Error
Traceback (most recent call last):
File "/home/nv01/joinmarket-clientserver/jmvenv/lib/python3.7/site-packages/twisted/protocols/amp.py", line 1041, in _commandReceived
deferred = self.dispatchCommand(box)
File "/home/nv01/joinmarket-clientserver/jmvenv/lib/python3.7/site-packages/twisted/protocols/amp.py", line 1099, in dispatchCommand
return maybeDeferred(responder, box)
File "/home/nv01/joinmarket-clientserver/jmvenv/lib/python3.7/site-packages/twisted/internet/defer.py", line 151, in maybeDeferred
result = f(*args, **kw)
File "/home/nv01/joinmarket-clientserver/jmvenv/lib/python3.7/site-packages/twisted/protocols/amp.py", line 1186, in doit
return maybeDeferred(aCallable, **kw).addCallback(
--- <exception caught here> ---
File "/home/nv01/joinmarket-clientserver/jmvenv/lib/python3.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
current.result = callback(current.result, *args, **kw)
File "/home/nv01/joinmarket-clientserver/jmvenv/lib/python3.7/site-packages/twisted/protocols/amp.py", line 1169, in checkKnownErrors
key = error.trap(*command.allErrors)
File "/home/nv01/joinmarket-clientserver/jmvenv/lib/python3.7/site-packages/twisted/python/failure.py", line 460, in trap
self.raiseException()
File "/home/nv01/joinmarket-clientserver/jmvenv/lib/python3.7/site-packages/twisted/python/failure.py", line 488, in raiseException
raise self.value.with_traceback(self.tb)
File "/home/nv01/joinmarket-clientserver/jmvenv/lib/python3.7/site-packages/twisted/internet/defer.py", line 151, in maybeDeferred
result = f(*args, **kw)
File "/home/nv01/joinmarket-clientserver/jmclient/jmclient/client_protocol.py", line 242, in on_JM_AUTH_RECEIVED
commitment, revelation, amount, kphex)
File "/home/nv01/joinmarket-clientserver/jmbase/jmbase/support.py", line 280, in func_wrapper
return func(inst, *newargs, **kwargs)
File "/home/nv01/joinmarket-clientserver/jmclient/jmclient/maker.py", line 105, in on_auth_received
self.wallet_service.save_wallet()
File "/home/nv01/joinmarket-clientserver/jmclient/jmclient/wallet_service.py", line 769, in save_wallet
self.wallet.save()
File "/home/nv01/joinmarket-clientserver/jmclient/jmclient/wallet.py", line 1531, in save
super().save()
File "/home/nv01/joinmarket-clientserver/jmclient/jmclient/wallet.py", line 1838, in save
super().save()
File "/home/nv01/joinmarket-clientserver/jmclient/jmclient/wallet.py", line 385, in save
self._utxos.save()
File "/home/nv01/joinmarket-clientserver/jmclient/jmclient/wallet.py", line 182, in save
self.storage.save()
File "/home/nv01/joinmarket-clientserver/jmclient/jmclient/storage.py", line 134, in save
raise StorageError("Read-only storage cannot be saved.")
jmclient.storage.StorageError: Read-only storage cannot be saved.

Amp server or network failure unhandled by client application.  Dropping connection!  To avoid, add errbacks to ALL remote commands!
Traceback (most recent call last):
--- <exception caught here> ---
File "/home/nv01/joinmarket-clientserver/jmvenv/lib/python3.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
current.result = callback(current.result, *args, **kw)
File "/home/nv01/joinmarket-clientserver/jmdaemon/jmdaemon/daemon_protocol.py", line 114, in defaultErrback
ConnectionDone, ConnectionLost)
File "/home/nv01/joinmarket-clientserver/jmvenv/lib/python3.7/site-packages/twisted/python/failure.py", line 460, in trap
self.raiseException()
File "/home/nv01/joinmarket-clientserver/jmvenv/lib/python3.7/site-packages/twisted/python/failure.py", line 488, in raiseException
raise self.value.with_traceback(self.tb)
twisted.protocols.amp.UnknownRemoteError: Code<UNKNOWN>: Unknown Error

2020-08-20 13:52:45,357 [DEBUG]  Found a new channel, setting to: JXXX,('nXXX.onion', 6667)
2020-08-20 13:52:45,434 [DEBUG]  Nick: JXXX has left.
2020-08-20 13:53:43,052 [DEBUG]  Dynamic switch nick: JXXX
2020-08-20 13:55:25,035 [DEBUG]  JXXX has dusty minsize, capping at 2730
2020-08-20 13:55:25,155 [DEBUG]  JXXX has dusty minsize, capping at 2730
(...)

# Fixed code
def rpc(self, method, args):
    """Returns the result of an rpc call to the Bitcoin Core RPC API.
    If the connection is permanently or unrecognizably broken, None
    is returned *and the reactor is shutdown* (because we consider this
    condition unsafe - TODO possibly create a "freeze" mode that could
    restart when the connection is healed, but that is tricky).
    """
    if method not in [
        "importaddress",
        "walletpassphrase",
        "getaccount",
        "gettransaction",
        "getrawtransaction",
        "gettxout",
        "importmulti",
        "listtransactions",
        "getblockcount",
        "scantxoutset",
    ]:
        log.debug("rpc: " + method + " " + str(args))
    try:
        res = self.jsonRpc.call(method, args)
    except JsonRpcConnectionError as e:
        # note that we only raise this in case the connection error is
        # a refusal, or is unrecognized/unknown by our code. So this does
        # NOT happen in a reset or broken pipe scenario.
        # It is safest to simply shut down.
        # Why not sys.exit? sys.exit calls do *not* work inside delayedCalls
        # or deferreds in twisted, since a bare exception catch prevents
        # an actual system exit (i.e. SystemExit is caught as a
        # BareException type).
        log.error(
            "Failure of RPC connection to Bitcoin Core. "
            "Application cannot continue, shutting down."
        )
        stop_reactor()
        return None
    # note that JsonRpcError is not caught here; for some calls, we
    # have specific behaviour requirements depending on these errors,
    # so this is handled elsewhere in BitcoinCoreInterface.
    return res