From 0fad07624f7afa31151975e001c4c8b82ee2cdee Mon Sep 17 00:00:00 2001 From: Adam Gibson Date: Fri, 10 Jan 2020 12:45:07 +0000 Subject: [PATCH] TxHistory tab in Qt records coinjoins while in maker mode, and deposits (although there is no sophisticated tx type parsing). The location of the corresponding history file is fixed to the datadir correctly. Also fixed Maker start/stop for non-Tor case. --- jmclient/jmclient/yieldgenerator.py | 1 + jmdaemon/jmdaemon/daemon_protocol.py | 1 - jmdaemon/jmdaemon/irc.py | 11 +++-- scripts/joinmarket-qt.py | 69 +++++++++++++++++++++------- 4 files changed, 62 insertions(+), 20 deletions(-) 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":