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) schedule_to_text)
from .commitment_utils import get_utxo_info, validate_utxo_data, quit from .commitment_utils import get_utxo_info, validate_utxo_data, quit
from .tumble_support import (tumbler_taker_finished_update, restart_waiter, 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. # Set default logging handler to avoid "No handler found" warnings.
try: try:

21
jmclient/jmclient/tumble_support.py

@ -24,10 +24,22 @@ def get_tumble_log(logsdir):
tumble_log.addHandler(fileHandler) tumble_log.addHandler(fileHandler)
return tumble_log 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): def restart_waiter(txid):
"""Given a txid, wait for confirmation by polling the blockchain """Given a txid, wait for confirmation by polling the blockchain
interface instance. Note that this is currently blocking, which is interface instance. Note that this is currently blocking, so only used
fine for the CLI for now, but should be re-done using twisted/thread TODO. by the CLI version; the Qt/GUI uses the underlying restart_wait() fn.
""" """
ctr = 0 ctr = 0
log.info("Waiting for confirmation of last transaction: " + str(txid)) log.info("Waiting for confirmation of last transaction: " + str(txid))
@ -36,10 +48,7 @@ def restart_waiter(txid):
ctr += 1 ctr += 1
if not (ctr % 12): if not (ctr % 12):
log.debug("Still waiting for confirmation of last transaction ...") log.debug("Still waiting for confirmation of last transaction ...")
res = jm_single().bc_interface.query_utxo_set(txid, includeconf=True) if restart_wait(txid):
if not res[0]:
continue
if res[0]['confirms'] > 0:
break break
log.info("The previous transaction is now in a block; continuing.") 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, get_blockchain_interface_instance, sync_wallet,
RegtestBitcoinCoreInterface, tweak_tumble_schedule, RegtestBitcoinCoreInterface, tweak_tumble_schedule,
human_readable_schedule_entry, tumbler_taker_finished_update, 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, from qtsupport import (ScheduleWizard, warnings, config_tips, config_types,
TaskThread, QtHandler, XStream, Buttons, CloseButton, TaskThread, QtHandler, XStream, Buttons, CloseButton,
@ -303,6 +303,7 @@ class SpendTab(QWidget):
self.tumbler_options = None self.tumbler_options = None
#signals from client backend to GUI #signals from client backend to GUI
self.jmclient_obj = QtCore.QObject() self.jmclient_obj = QtCore.QObject()
self.restartTimer = QtCore.QTimer()
#This signal/callback requires user acceptance decision. #This signal/callback requires user acceptance decision.
self.jmclient_obj.connect(self.jmclient_obj, QtCore.SIGNAL('JMCLIENT:offers'), self.jmclient_obj.connect(self.jmclient_obj, QtCore.SIGNAL('JMCLIENT:offers'),
self.checkOffers) self.checkOffers)
@ -353,6 +354,14 @@ class SpendTab(QWidget):
self.spendstate.loaded_schedule = schedule self.spendstate.loaded_schedule = schedule
self.spendstate.schedule_name = os.path.basename(str(firstarg)) self.spendstate.schedule_name = os.path.basename(str(firstarg))
self.updateSchedView() 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): def updateSchedView(self):
self.sch_label2.setText(self.spendstate.schedule_name) self.sch_label2.setText(self.spendstate.schedule_name)
@ -501,6 +510,13 @@ class SpendTab(QWidget):
def resizeScroll(self, mini, maxi): def resizeScroll(self, mini, maxi):
self.textedit.verticalScrollBar().setValue(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): def startMultiple(self):
if not self.spendstate.runstate == 'ready': if not self.spendstate.runstate == 'ready':
log.info("Cannot start join, already running.") log.info("Cannot start join, already running.")
@ -508,9 +524,48 @@ class SpendTab(QWidget):
if not self.spendstate.loaded_schedule: if not self.spendstate.loaded_schedule:
log.info("Cannot start, no schedule loaded.") log.info("Cannot start, no schedule loaded.")
return return
#self.qtw.setTabEnabled(0, False)
self.spendstate.updateType('multiple') self.spendstate.updateType('multiple')
self.spendstate.updateRun('running') 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() self.startJoin()
def startSingle(self): def startSingle(self):
@ -1525,6 +1580,8 @@ update_config_for_gui()
if isinstance(jm_single().bc_interface, RegtestBitcoinCoreInterface): if isinstance(jm_single().bc_interface, RegtestBitcoinCoreInterface):
jm_single().bc_interface.tick_forward_chain_interval = 10 jm_single().bc_interface.tick_forward_chain_interval = 10
jm_single().maker_timeout_sec = 5 jm_single().maker_timeout_sec = 5
#trigger start with a fake tx
jm_single().bc_interface.pushtx("00"*20)
#prepare for logging #prepare for logging
for dname in ['logs', 'wallets', 'cmtdata']: for dname in ['logs', 'wallets', 'cmtdata']:

Loading…
Cancel
Save