Browse Source

support restart in Qt

master
Adam Gibson 9 years ago
parent
commit
04c8b6655f
No known key found for this signature in database
GPG Key ID: B3AE09F1E9A3197A
  1. 2
      jmclient/jmclient/__init__.py
  2. 21
      jmclient/jmclient/tumble_support.py
  3. 61
      scripts/joinmarket-qt.py

2
jmclient/jmclient/__init__.py

@ -35,7 +35,7 @@ from .schedule import (get_schedule, get_tumble_schedule, schedule_to_text,
schedule_to_text)
from .commitment_utils import get_utxo_info, validate_utxo_data, quit
from .tumble_support import (tumbler_taker_finished_update, restart_waiter,
get_tumble_log)
restart_wait, get_tumble_log)
# Set default logging handler to avoid "No handler found" warnings.
try:

21
jmclient/jmclient/tumble_support.py

@ -24,10 +24,22 @@ def get_tumble_log(logsdir):
tumble_log.addHandler(fileHandler)
return tumble_log
def restart_wait(txid):
"""Here txid is of form txid:N for direct utxo query.
Returns true only if the utxo is reported to have at least 1
confirm by the blockchain interface.
"""
res = jm_single().bc_interface.query_utxo_set(txid, includeconf=True)
if not res[0]:
return False
if res[0]['confirms'] > 0:
return True
return False
def restart_waiter(txid):
"""Given a txid, wait for confirmation by polling the blockchain
interface instance. Note that this is currently blocking, which is
fine for the CLI for now, but should be re-done using twisted/thread TODO.
interface instance. Note that this is currently blocking, so only used
by the CLI version; the Qt/GUI uses the underlying restart_wait() fn.
"""
ctr = 0
log.info("Waiting for confirmation of last transaction: " + str(txid))
@ -36,10 +48,7 @@ def restart_waiter(txid):
ctr += 1
if not (ctr % 12):
log.debug("Still waiting for confirmation of last transaction ...")
res = jm_single().bc_interface.query_utxo_set(txid, includeconf=True)
if not res[0]:
continue
if res[0]['confirms'] > 0:
if restart_wait(txid):
break
log.info("The previous transaction is now in a block; continuing.")

61
scripts/joinmarket-qt.py

@ -53,7 +53,7 @@ from jmclient import (load_program_config, get_network, Wallet,
get_blockchain_interface_instance, sync_wallet,
RegtestBitcoinCoreInterface, tweak_tumble_schedule,
human_readable_schedule_entry, tumbler_taker_finished_update,
get_tumble_log, restart_waiter)
get_tumble_log, restart_wait)
from qtsupport import (ScheduleWizard, warnings, config_tips, config_types,
TaskThread, QtHandler, XStream, Buttons, CloseButton,
@ -303,6 +303,7 @@ class SpendTab(QWidget):
self.tumbler_options = None
#signals from client backend to GUI
self.jmclient_obj = QtCore.QObject()
self.restartTimer = QtCore.QTimer()
#This signal/callback requires user acceptance decision.
self.jmclient_obj.connect(self.jmclient_obj, QtCore.SIGNAL('JMCLIENT:offers'),
self.checkOffers)
@ -353,6 +354,14 @@ class SpendTab(QWidget):
self.spendstate.loaded_schedule = schedule
self.spendstate.schedule_name = os.path.basename(str(firstarg))
self.updateSchedView()
if self.spendstate.schedule_name == "TUMBLE.schedule":
reply = JMQtMessageBox(self, "An incomplete tumble run detected. "
"\nDo you want to restart?",
title="Restart detected", mbtype='question')
if reply != QMessageBox.Yes:
self.giveUp()
return
self.tumbler_options = True
def updateSchedView(self):
self.sch_label2.setText(self.spendstate.schedule_name)
@ -501,6 +510,13 @@ class SpendTab(QWidget):
def resizeScroll(self, mini, maxi):
self.textedit.verticalScrollBar().setValue(maxi)
def restartWaitWrap(self):
if restart_wait(self.waitingtxid):
self.restartTimer.stop()
self.waitingtxid = None
w.statusBar().showMessage("Transaction in a block, now continuing.")
self.startJoin()
def startMultiple(self):
if not self.spendstate.runstate == 'ready':
log.info("Cannot start join, already running.")
@ -508,9 +524,48 @@ class SpendTab(QWidget):
if not self.spendstate.loaded_schedule:
log.info("Cannot start, no schedule loaded.")
return
#self.qtw.setTabEnabled(0, False)
self.spendstate.updateType('multiple')
self.spendstate.updateRun('running')
if self.tumbler_options:
#TODO: mincjamount is the only tumbler setting, except maxcjfee,
#that is not part of sched-generation, so request from user
if not hasattr(jm_single(), 'mincjamount'):
mincjamount, ok = QInputDialog.getInt(self,
"Set min coinjoin amount",
"Enter minimum allowable coinjoin amount in satoshis")
if not ok:
self.giveUp()
return
jm_single().mincjamount = mincjamount
#check for a partially-complete schedule; if so,
#follow restart logic
#1. filter out complete:
self.spendstate.loaded_schedule = [
s for s in self.spendstate.loaded_schedule if s[5] != 1]
#reload destination addresses
self.tumbler_destaddrs = [x[3] for x in self.spendstate.loaded_schedule
if x not in ["INTERNAL", "addrask"]]
#2 Check for unconfirmed
if isinstance(self.spendstate.loaded_schedule[0][5], str) and len(
self.spendstate.loaded_schedule[0][5]) == 64:
#ensure last transaction is confirmed before restart
tumble_log.info("WAITING TO RESTART...")
w.statusBar().showMessage("Waiting for confirmation to restart..")
txid = self.spendstate.loaded_schedule[0][5]
#remove the already-done entry (this connects to the other TODO,
#probably better *not* to truncate the done-already txs from file,
#but simplest for now.
self.spendstate.loaded_schedule = self.spendstate.loaded_schedule[1:]
#defers startJoin() call until tx seen on network. Note that
#since we already updated state to running, user cannot
#start another transactions while waiting. Also, use :0 because
#it always exists
self.waitingtxid=txid+":0"
self.restartTimer.timeout.connect(self.restartWaitWrap)
self.restartTimer.start(5000)
return
self.updateSchedView()
self.startJoin()
def startSingle(self):
@ -1525,6 +1580,8 @@ update_config_for_gui()
if isinstance(jm_single().bc_interface, RegtestBitcoinCoreInterface):
jm_single().bc_interface.tick_forward_chain_interval = 10
jm_single().maker_timeout_sec = 5
#trigger start with a fake tx
jm_single().bc_interface.pushtx("00"*20)
#prepare for logging
for dname in ['logs', 'wallets', 'cmtdata']:

Loading…
Cancel
Save