# Buggy code
def taker_finished(res, fromtx=False, waittime=0.0, txdetails=None):
    if fromtx == "unconfirmed":
        # If final entry, stop *here*, don't wait for confirmation
        if taker.schedule_index + 1 == len(taker.schedule):
            reactor.stop()
        return
    if fromtx:
        if res:
            txd, txid = txdetails
            taker.wallet.remove_old_utxos(txd)
            taker.wallet.add_new_utxos(txd, txid)
            reactor.callLater(waittime * 60, clientfactory.getClient().clientStart)
        else:
            # a transaction failed; we'll try to repeat without the
            # troublemakers
            # Note that Taker.nonrespondants is always set to the full maker
            # list at the start of Taker.receive_utxos, so it is always updated
            # to a new list in each run.
            print(
                "We failed to complete the transaction. The following "
                "makers didn't respond: ",
                taker.nonrespondants,
                ", so we will retry without them.",
            )
            taker.add_ignored_makers(taker.nonrespondants)
            # Now we have to set the specific group we want to use, and hopefully
            # they will respond again as they showed honesty last time.
            taker.add_honest_makers(
                list(
                    set(taker.maker_utxo_data.keys()).symmetric_difference(
                        set(taker.nonrespondants)
                    )
                )
            )
            if len(taker.honest_makers) == 0:
                log.info(
                    "None of the makers responded honestly; giving up this attempt."
                )
                reactor.stop()
                return
            # we must reset the number of counterparties, as well as fix who they
            # are; this is because the number is used to e.g. calculate fees.
            # cleanest way is to reset the number in the schedule before restart.
            taker.schedule[taker.schedule_index][2] = len(taker.honest_makers)
            log.info(
                "Retrying with: "
                + str(taker.schedule[taker.schedule_index][2])
                + " counterparties."
            )
            # rewind to try again (index is incremented in Taker.initialize())
            taker.schedule_index -= 1
            taker.set_honest_only(True)
            reactor.callLater(5.0, clientfactory.getClient().clientStart)
    else:
        if not res:
            log.info("Did not complete successfully, shutting down")
        # Should usually be unreachable, unless conf received out of order;
        # because we should stop on 'unconfirmed' for last (see above)
        else:
            log.info("All transactions completed correctly")
        reactor.stop()


# Traceback (exceptions.AttributeError)
2018-07-21 00:03:59,900 [MainThread  ] [INFO ]  Makers didnt respond: [u'J57XBPc4Ub5b3xxO', u'J54nByhefebjZFkB']
2018-07-21 00:21:32,779 [MainThread  ] [INFO ]  STALL MONITOR:
2018-07-21 00:21:32,779 [MainThread  ] [INFO ]  Stall detected. Regenerating transactions and retrying.
Unhandled Error
Traceback (most recent call last):
File "sendpayment.py", line 233, in main
clientfactory, daemon=daemon)
File "/home/alex/Documents/joinmarket-dev/test_ranr-under-max/joinmarket-clientserver/jmvenv/local/lib/python2.7/site-packages/jmclient/client_protocol.py", line 495, in start_reactor
reactor.run(installSignalHandlers=ish)
File "/home/alex/Documents/joinmarket-dev/test_ranr-under-max/joinmarket-clientserver/jmvenv/local/lib/python2.7/site-packages/twisted/internet/base.py", line 1199, in run
self.mainLoop()
File "/home/alex/Documents/joinmarket-dev/test_ranr-under-max/joinmarket-clientserver/jmvenv/local/lib/python2.7/site-packages/twisted/internet/base.py", line 1208, in mainLoop
self.runUntilCurrent()
--- <exception caught here> ---
File "/home/alex/Documents/joinmarket-dev/test_ranr-under-max/joinmarket-clientserver/jmvenv/local/lib/python2.7/site-packages/twisted/internet/base.py", line 828, in runUntilCurrent
call.func(*call.args, **call.kw)
File "/home/alex/Documents/joinmarket-dev/test_ranr-under-max/joinmarket-clientserver/jmvenv/local/lib/python2.7/site-packages/jmclient/client_protocol.py", line 346, in stallMonitor
self.client.on_finished_callback(False, True, 0.0)
File "sendpayment.py", line 189, in taker_finished
"makers didn't respond: ", taker.nonrespondants,
exceptions.AttributeError: 'Taker' object has no attribute 'nonrespondants'

# Fixed code
def taker_finished(res, fromtx=False, waittime=0.0, txdetails=None):
    if fromtx == "unconfirmed":
        # If final entry, stop *here*, don't wait for confirmation
        if taker.schedule_index + 1 == len(taker.schedule):
            reactor.stop()
        return
    if fromtx:
        if res:
            txd, txid = txdetails
            taker.wallet.remove_old_utxos(txd)
            taker.wallet.add_new_utxos(txd, txid)
            reactor.callLater(waittime * 60, clientfactory.getClient().clientStart)
        else:
            # a transaction failed; we'll try to repeat without the
            # troublemakers.
            # If this error condition is reached from Phase 1 processing,
            # and there are less than minimum_makers honest responses, we
            # just give up (note that in tumbler we tweak and retry, but
            # for sendpayment the user is "online" and so can manually
            # try again).
            # However if the error is in Phase 2 and we have minimum_makers
            # or more responses, we do try to restart with the honest set, here.
            if taker.latest_tx is None:
                # can only happen with < minimum_makers; see above.
                log.info(
                    "A transaction failed but there are insufficient "
                    "honest respondants to continue; giving up."
                )
                reactor.stop()
                return
            # This is Phase 2; do we have enough to try again?
            taker.add_honest_makers(
                list(
                    set(taker.maker_utxo_data.keys()).symmetric_difference(
                        set(taker.nonrespondants)
                    )
                )
            )
            if len(taker.honest_makers) < jm_single().config.getint(
                "POLICY", "minimum_makers"
            ):
                log.info("Too few makers responded honestly; giving up this attempt.")
                reactor.stop()
                return
            print(
                "We failed to complete the transaction. The following "
                "makers responded honestly: ",
                taker.honest_makers,
                ", so we will retry with them.",
            )
            # Now we have to set the specific group we want to use, and hopefully
            # they will respond again as they showed honesty last time.
            # we must reset the number of counterparties, as well as fix who they
            # are; this is because the number is used to e.g. calculate fees.
            # cleanest way is to reset the number in the schedule before restart.
            taker.schedule[taker.schedule_index][2] = len(taker.honest_makers)
            log.info(
                "Retrying with: "
                + str(taker.schedule[taker.schedule_index][2])
                + " counterparties."
            )
            # rewind to try again (index is incremented in Taker.initialize())
            taker.schedule_index -= 1
            taker.set_honest_only(True)
            reactor.callLater(5.0, clientfactory.getClient().clientStart)
    else:
        if not res:
            log.info("Did not complete successfully, shutting down")
        # Should usually be unreachable, unless conf received out of order;
        # because we should stop on 'unconfirmed' for last (see above)
        else:
            log.info("All transactions completed correctly")
        reactor.stop()