diff --git a/jmclient/jmclient/yieldgenerator.py b/jmclient/jmclient/yieldgenerator.py index f3d74d965..7a4ba4fb7 100644 --- a/jmclient/jmclient/yieldgenerator.py +++ b/jmclient/jmclient/yieldgenerator.py @@ -8,6 +8,7 @@ import os import time import abc +import sys from twisted.python.log import startLogging from optparse import OptionParser from jmbase import get_log diff --git a/jmdaemon/jmdaemon/daemon_protocol.py b/jmdaemon/jmdaemon/daemon_protocol.py index fd3b3d230..c725e1e2e 100644 --- a/jmdaemon/jmdaemon/daemon_protocol.py +++ b/jmdaemon/jmdaemon/daemon_protocol.py @@ -214,7 +214,6 @@ def on_JM_MSGSIGNATURE_VERIFY(self, verif_result, nick, fullmsg, hostid): @JMShutdown.responder def on_JM_SHUTDOWN(self): - print("reached on shutdown in jmdaemonserverprotocol") self.mc_shutdown() self.jm_state = 0 return {'accepted': True} diff --git a/jmdaemon/jmdaemon/irc.py b/jmdaemon/jmdaemon/irc.py index 5b6a48c2c..b98335e51 100644 --- a/jmdaemon/jmdaemon/irc.py +++ b/jmdaemon/jmdaemon/irc.py @@ -104,6 +104,10 @@ def __init__(self, self.tx_irc_client = None #TODO can be configuration var, how long between reconnect attempts: self.reconnect_interval = 10 + + # service is used to wrap endpoint for Tor connections: + self.reconnecting_service = None + #implementation of abstract base class methods; #these are mostly but not exclusively acting as pass through #to the wrapped twisted IRC client protocol @@ -114,7 +118,8 @@ def run(self): def shutdown(self): self.tx_irc_client.quit() self.give_up = True - self.myRS.stopService() + if self.reconnecting_service: + self.reconnecting_service.stopService() def _pubmsg(self, msg): self.tx_irc_client._pubmsg(msg) @@ -157,8 +162,8 @@ def build_irc(self): use_tls = False ircEndpoint = TorSocksEndpoint(torEndpoint, self.serverport[0], self.serverport[1], tls=use_tls) - self.myRS = ClientService(ircEndpoint, factory) - self.myRS.startService() + self.reconnecting_service = ClientService(ircEndpoint, factory) + self.reconnecting_service.startService() else: try: factory = TxIRCFactory(self) diff --git a/scripts/joinmarket-qt.py b/scripts/joinmarket-qt.py index acbee4bba..71bc892c0 100644 --- a/scripts/joinmarket-qt.py +++ b/scripts/joinmarket-qt.py @@ -25,6 +25,7 @@ import sys, datetime, os, logging import platform, json, threading, time + import qrcode from optparse import OptionParser @@ -94,8 +95,9 @@ def update_config_for_gui(): ''' gui_config_names = ['gaplimit', 'history_file', 'check_high_fee', 'max_mix_depth', 'order_wait_time', 'checktx'] - gui_config_default_vals = ['6', 'jm-tx-history.txt', '2', '5', '30', - 'true'] + gui_config_default_vals = ['6', os.path.join(jm_single().datadir, + 'jm-tx-history.txt'), + '2', '5', '30', 'true'] if "GUI" not in jm_single().config.sections(): jm_single().config.add_section("GUI") gui_items = jm_single().config.items("GUI") @@ -638,7 +640,8 @@ def qt_directsend_callback(rtxd, rtxid, confs): mainWindow.wallet_service.active_txids.append(txid) mainWindow.wallet_service.register_callbacks([qt_directsend_callback], txid, cb_type="confirmed") - self.persistTxToHistory(destaddr, self.direct_send_amount, txid) + mainWindow.centralWidget().widget(3).persistTxToHistory(destaddr, + self.direct_send_amount, txid) self.cleanUp() return @@ -826,8 +829,8 @@ def takerFinished(self, res, fromtx=False, waittime=0.0, txdetails=None): title="Success") #TODO: theoretically possible to miss this if confirmed event #seen before unconfirmed. - self.persistTxToHistory(self.taker.my_cj_addr, self.taker.cjamount, - self.taker.txid) + mainWindow.centralWidget().widget(3).persistTxToHistory( + self.taker.my_cj_addr, self.taker.cjamount, self.taker.txid) #TODO prob best to completely fold multiple and tumble to reduce #complexity/duplication @@ -866,17 +869,6 @@ def takerFinished(self, res, fromtx=False, waittime=0.0, txdetails=None): else: self.giveUp() - def persistTxToHistory(self, addr, amt, txid): - #persist the transaction to history - with open(jm_single().config.get("GUI", "history_file"), 'ab') as f: - f.write((','.join([addr, btc.amount_to_btc_str(amt), txid, - datetime.datetime.now( - ).strftime("%Y/%m/%d %H:%M:%S")])).encode('utf-8')) - f.write(b'\n') #TODO: Windows - #update the TxHistory tab - txhist = mainWindow.centralWidget().widget(3) - txhist.updateTxInfo() - def toggleButtons(self): """Refreshes accessibility of buttons in the (single, multiple) join tabs based on the current state as defined by the SpendStateMgr instance. @@ -956,6 +948,7 @@ class TxHistoryTab(QWidget): def __init__(self): super(TxHistoryTab, self).__init__() + self.txids = set() self.initUI() def initUI(self): @@ -976,6 +969,19 @@ def getHeaders(self): '''Function included in case dynamic in future''' return ['Receiving address', 'Amount in BTC', 'Transaction id', 'Date'] + def persistTxToHistory(self, addr, amt, txid): + #persist the transaction to history + if txid in self.txids: + # don't repeat entries + return + self.txids.add(txid) + with open(jm_single().config.get("GUI", "history_file"), 'ab') as f: + f.write((','.join([addr, btc.amount_to_btc_str(amt), txid, + datetime.datetime.now( + ).strftime("%Y/%m/%d %H:%M:%S")])).encode('utf-8')) + f.write(b'\n') #TODO: Windows + self.updateTxInfo() + def updateTxInfo(self, txinfo=None): self.tHTW.clear() if not txinfo: @@ -997,6 +1003,7 @@ def getTxInfoFromFile(self): txlines = f.readlines() for tl in txlines: txhist.append(tl.decode('utf-8').strip().split(',')) + self.txids.add(txhist[-1][2]) if not len(txhist[-1]) == 4: JMQtMessageBox(self, "Incorrectedly formatted file " + hf, @@ -1687,6 +1694,7 @@ def loadWalletFromBlockchain(self, firstarg=None, pwd=None): # add information callbacks: self.wallet_service.add_restart_callback(self.restartWithMsg) self.wallet_service.autofreeze_warning_cb = self.autofreeze_warning_cb + self.wallet_service.register_callbacks([self.updateTxHistory], None, cb_type="all") self.wallet_service.startService() self.walletRefresh = task.LoopingCall(self.updateWalletInfo) @@ -1708,6 +1716,35 @@ def updateWalletInfo(self): self.syncmsg = newsyncmsg self.statusBar().showMessage(self.syncmsg) + def updateTxHistory(self, txd, txid): + """ If a transaction is seen by the wallet service + while running in maker mode, we update the tx history to + record the txid. We note *a* receiving address and the + corresponding amount; this is because a full analysis + requires detailed algos to account for custom deposit + patterns as well as coinjoin pattern analysis. + Note this is separate from the yigen log. + TODO implement these algos in WalletService. + TODO consolidate the multiple tx history functions. + """ + + if not self.maker_running: + # these transactions are recorded separately + # (note that this includes direct sends) + return + addr_val = [None, None] + for o in txd["outs"]: + bscript = btc.safe_from_hex(o["script"]) + if self.wallet_service.is_known_script(bscript): + addr_val = [self.wallet_service.script_to_addr(bscript), + o["value"]] + break + if addr_val[0] is None: + # it's possible to get notified of a transaction + # on this label that isn't in our wallet; ignore. + return + self.centralWidget().widget(3).persistTxToHistory(*addr_val, txid) + def generateWallet(self): log.debug('generating wallet') if jm_single().config.get("BLOCKCHAIN", "blockchain_source") == "regtest":