From 1a46a795a5cf510789f21c47771363a7b31f1b4a Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sun, 29 May 2016 19:53:04 +0200 Subject: [PATCH] detect non-final transactions, and transactions with unconfirmed inputs --- gui/kivy/uix/dialogs/tx_dialog.py | 8 ++-- gui/kivy/uix/screens.py | 26 +++++++----- gui/qt/history_list.py | 39 +++++++++-------- gui/qt/main_window.py | 8 ++-- gui/qt/transaction_dialog.py | 16 +++---- gui/text.py | 3 +- icons.qrc | 1 + icons/warning.png | Bin 0 -> 4839 bytes lib/commands.py | 25 +++++------ lib/verifier.py | 2 +- lib/wallet.py | 67 +++++++++++++----------------- plugins/exchange_rate/qt.py | 2 +- 12 files changed, 103 insertions(+), 94 deletions(-) create mode 100644 icons/warning.png diff --git a/gui/kivy/uix/dialogs/tx_dialog.py b/gui/kivy/uix/dialogs/tx_dialog.py index 8e1fcd95..3a55b0c0 100644 --- a/gui/kivy/uix/dialogs/tx_dialog.py +++ b/gui/kivy/uix/dialogs/tx_dialog.py @@ -109,10 +109,12 @@ class TxDialog(Factory.Popup): self.tx_hash = self.tx.hash() self.description = self.wallet.get_label(self.tx_hash) if self.tx_hash in self.wallet.transactions.keys(): - conf, timestamp = self.wallet.get_confirmations(self.tx_hash) - self.status_str = _("%d confirmations")%conf if conf else _('Pending') - if timestamp: + height, conf, timestamp = self.wallet.get_tx_height(self.tx_hash) + if conf: + self.status_str = _("%d confirmations")%conf self.date_str = datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] + else: + self.status_str = _('Unconfirmed') else: self.can_broadcast = self.app.network is not None self.status_str = _('Signed') diff --git a/gui/kivy/uix/screens.py b/gui/kivy/uix/screens.py index bb2a6695..1b41b694 100644 --- a/gui/kivy/uix/screens.py +++ b/gui/kivy/uix/screens.py @@ -118,19 +118,25 @@ class HistoryScreen(CScreen): def parse_history(self, items): for item in items: - tx_hash, conf, value, timestamp, balance = item - time_str = _("unknown") - if conf > 0: - try: - time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] - except Exception: - time_str = _("error") - if conf == -1: - time_str = _('Not Verified') + tx_hash, height, conf, timestamp, value, balance = item + time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] if timestamp else _("unknown") + if conf == 0: + tx = self.app.wallet.transactions.get(tx_hash) + is_final = tx.is_final() + else: + is_final = True + if not is_final: + time_str = _('Replaceable') icon = "atlas://gui/kivy/theming/light/close" - elif conf == 0: + elif height < 0: + time_str = _('Unconfirmed inputs') + icon = "atlas://gui/kivy/theming/light/close" + elif height == 0: time_str = _('Unconfirmed') icon = "atlas://gui/kivy/theming/light/unconfirmed" + elif conf == 0: + time_str = _('Not Verified') + icon = "atlas://gui/kivy/theming/light/close" elif conf < 6: conf = max(1, conf) icon = "atlas://gui/kivy/theming/light/clock{}".format(conf) diff --git a/gui/qt/history_list.py b/gui/qt/history_list.py index a9f266ed..e9f4857a 100644 --- a/gui/qt/history_list.py +++ b/gui/qt/history_list.py @@ -45,15 +45,19 @@ class HistoryList(MyTreeWidget): run_hook('history_tab_headers', headers) self.update_headers(headers) - def get_icon(self, conf, timestamp): - time_str = _("unknown") - if conf > 0: - time_str = format_time(timestamp) - if conf == -1: - time_str = _('Not Verified') + def get_icon(self, height, conf, timestamp, is_final): + time_str = format_time(timestamp) if timestamp else _("unknown") + if not is_final: + time_str = _('Replaceable') + icon = QIcon(":icons/warning.png") + elif height < 0: + time_str = _('Unconfirmed inputs') + icon = QIcon(":icons/warning.png") + elif height == 0: + time_str = _('Unconfirmed') icon = QIcon(":icons/unconfirmed.png") elif conf == 0: - time_str = _('Unconfirmed') + time_str = _('Not Verified') icon = QIcon(":icons/unconfirmed.png") elif conf < 6: icon = QIcon(":icons/clock%d.png"%conf) @@ -68,17 +72,18 @@ class HistoryList(MyTreeWidget): def on_update(self): self.wallet = self.parent.wallet h = self.wallet.get_history(self.get_domain()) - item = self.currentItem() current_tx = item.data(0, Qt.UserRole).toString() if item else None self.clear() run_hook('history_tab_update_begin') for h_item in h: - tx_hash, conf, value, timestamp, balance = h_item - if conf is None and timestamp is None: - continue # skip history in offline mode - - icon, time_str = self.get_icon(conf, timestamp) + tx_hash, height, conf, timestamp, value, balance = h_item + if conf == 0: + tx = self.wallet.transactions.get(tx_hash) + is_final = tx.is_final() + else: + is_final = True + icon, time_str = self.get_icon(height, conf, timestamp, is_final) v_str = self.parent.format_amount(value, True, whitespaces=True) balance_str = self.parent.format_amount(balance, whitespaces=True) label = self.wallet.get_label(tx_hash) @@ -100,8 +105,8 @@ class HistoryList(MyTreeWidget): if current_tx == tx_hash: self.setCurrentItem(item) - def update_item(self, tx_hash, conf, timestamp): - icon, time_str = self.get_icon(conf, timestamp) + def update_item(self, tx_hash, height, conf, timestamp): + icon, time_str = self.get_icon(height, conf, timestamp, True) items = self.findItems(tx_hash, Qt.UserRole|Qt.MatchContains|Qt.MatchRecursive, column=1) if items: item = items[0] @@ -125,10 +130,10 @@ class HistoryList(MyTreeWidget): column_data = item.text(column) tx_URL = block_explorer_URL(self.config, 'tx', tx_hash) - conf, timestamp = self.wallet.get_confirmations(tx_hash) + height, conf, timestamp = self.wallet.get_tx_height(tx_hash) tx = self.wallet.transactions.get(tx_hash) is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx) - rbf = is_mine and (conf == 0) and tx and not tx.is_final() + rbf = is_mine and height <=0 and tx and not tx.is_final() menu = QMenu() menu.addAction(_("Copy %s")%column_title, lambda: self.parent.app.clipboard().setText(column_data)) diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py index 556634a6..f7fa7572 100644 --- a/gui/qt/main_window.py +++ b/gui/qt/main_window.py @@ -2196,14 +2196,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): history = wallet.get_history() lines = [] for item in history: - tx_hash, confirmations, value, timestamp, balance = item - if confirmations: + tx_hash, height, conf, timestamp, value, balance = item + if height>0: if timestamp is not None: time_string = format_time(timestamp) else: - time_string = "unknown" + time_string = _("unverified") else: - time_string = "unconfirmed" + time_string = _("unconfirmed") if value is not None: value_string = format_satoshis(value, True) diff --git a/gui/qt/transaction_dialog.py b/gui/qt/transaction_dialog.py index 40b6b2d0..bf9c02c1 100644 --- a/gui/qt/transaction_dialog.py +++ b/gui/qt/transaction_dialog.py @@ -183,17 +183,19 @@ class TxDialog(QDialog, MessageBoxMixin): self.broadcast_button.hide() if self.tx.is_complete(): - status = _("Signed") - if tx_hash in self.wallet.transactions.keys(): desc = self.wallet.get_label(tx_hash) - conf, timestamp = self.wallet.get_confirmations(tx_hash) - if timestamp: - time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] + height, conf, timestamp = self.wallet.get_tx_height(tx_hash) + if height > 0: + if conf: + status = _("%d confirmations") % height + time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] + else: + status = _('Not verified') else: - time_str = _('Pending') - status = _("%d confirmations")%conf + status = _('Unconfirmed') else: + status = _("Signed") self.broadcast_button.show() # cannot broadcast when offline if self.main_window.network is None: diff --git a/gui/text.py b/gui/text.py index 710b76bb..f1927b1c 100644 --- a/gui/text.py +++ b/gui/text.py @@ -106,9 +106,8 @@ class ElectrumGui: b = 0 self.history = [] - for item in self.wallet.get_history(): - tx_hash, conf, value, timestamp, balance = item + tx_hash, height, conf, timestamp, value, balance = item if conf: try: time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] diff --git a/icons.qrc b/icons.qrc index 2c55a792..7caa8836 100644 --- a/icons.qrc +++ b/icons.qrc @@ -33,6 +33,7 @@ icons/unconfirmed.png icons/unpaid.png icons/unlock.png + icons/warning.png icons/zoom.png diff --git a/icons/warning.png b/icons/warning.png new file mode 100644 index 0000000000000000000000000000000000000000..ee3eacea9b796a301af438ef50b02afed3f36bb8 GIT binary patch literal 4839 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 N002ovPDHLkV1kx(OU(cP literal 0 HcmV?d00001 diff --git a/lib/commands.py b/lib/commands.py index 41b038c7..1ea5226d 100644 --- a/lib/commands.py +++ b/lib/commands.py @@ -450,20 +450,21 @@ class Commands: balance = 0 out = [] for item in self.wallet.get_history(): - tx_hash, conf, value, timestamp, balance = item - try: - time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] - except Exception: - time_str = "----" + tx_hash, height, conf, timestamp, value, balance = item + if timestamp: + date = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] + else: + date = "----" label = self.wallet.get_label(tx_hash) out.append({ - 'txid':tx_hash, - 'timestamp':timestamp, - 'date':"%16s"%time_str, - 'label':label, - 'value':float(value)/COIN if value is not None else None, - 'confirmations':conf} - ) + 'txid': tx_hash, + 'timestamp': timestamp, + 'date': date, + 'label': label, + 'value': float(value)/COIN if value is not None else None, + 'height': height, + 'confirmations': conf + }) return out @command('w') diff --git a/lib/verifier.py b/lib/verifier.py index ce8b4cc0..e407e437 100644 --- a/lib/verifier.py +++ b/lib/verifier.py @@ -43,7 +43,7 @@ class SPV(ThreadJob): unverified = self.wallet.get_unverified_txs() for tx_hash, tx_height in unverified.items(): # do not request merkle branch before headers are available - if tx_hash not in self.merkle_roots and tx_height <= lh: + if tx_height>0 and tx_hash not in self.merkle_roots and tx_height <= lh: request = ('blockchain.transaction.get_merkle', [tx_hash, tx_height]) self.network.send([request], self.verify_merkle) diff --git a/lib/wallet.py b/lib/wallet.py index 6b9eeee3..f07137b3 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -35,7 +35,7 @@ import re import stat from functools import partial from unicodedata import normalize -from collections import namedtuple +from collections import namedtuple, defaultdict from i18n import _ from util import NotEnoughFunds, PrintError, profiler @@ -193,7 +193,8 @@ class Abstract_Wallet(PrintError): # Transactions pending verification. A map from tx hash to transaction # height. Access is not contended so no lock is needed. - self.unverified_tx = {} + self.unverified_tx = defaultdict(int) + # Verified transactions. Each value is a (height, timestamp, block_pos) tuple. Access with self.lock. self.verified_tx = storage.get('verified_tx3',{}) @@ -455,8 +456,8 @@ class Abstract_Wallet(PrintError): return decrypted def add_unverified_tx(self, tx_hash, tx_height): - # Only add if confirmed and not verified - if tx_height > 0 and tx_hash not in self.verified_tx: + # tx will be verified only if height > 0 + if tx_hash not in self.verified_tx: self.unverified_tx[tx_hash] = tx_height def add_verified_tx(self, tx_hash, info): @@ -465,9 +466,8 @@ class Abstract_Wallet(PrintError): with self.lock: self.verified_tx[tx_hash] = info # (tx_height, timestamp, pos) self.storage.put('verified_tx3', self.verified_tx) - - conf, timestamp = self.get_confirmations(tx_hash) - self.network.trigger_callback('verified', tx_hash, conf, timestamp) + height, conf, timestamp = self.get_tx_height(tx_hash) + self.network.trigger_callback('verified', tx_hash, height, conf, timestamp) def get_unverified_txs(self): '''Returns a map from tx hash to transaction height''' @@ -488,34 +488,29 @@ class Abstract_Wallet(PrintError): """ return last known height if we are offline """ return self.network.get_local_height() if self.network else self.stored_height - def get_confirmations(self, tx): - """ return the number of confirmations of a monitored transaction. """ + def get_tx_height(self, tx_hash): + """ return the height and timestamp of a verified transaction. """ with self.lock: - if tx in self.verified_tx: - height, timestamp, pos = self.verified_tx[tx] - conf = (self.get_local_height() - height + 1) - if conf <= 0: timestamp = None - elif tx in self.unverified_tx: - conf = -1 - timestamp = None + if tx_hash in self.verified_tx: + height, timestamp, pos = self.verified_tx[tx_hash] + conf = max(self.get_local_height() - height + 1, 0) + return height, conf, timestamp else: - conf = 0 - timestamp = None - - return conf, timestamp + height = self.unverified_tx[tx_hash] + return height, 0, False def get_txpos(self, tx_hash): "return position, even if the tx is unverified" with self.lock: x = self.verified_tx.get(tx_hash) - y = self.unverified_tx.get(tx_hash) - if x: - height, timestamp, pos = x - return height, pos - elif y: - return y, 0 - else: - return 1e12, 0 + y = self.unverified_tx.get(tx_hash) + if x: + height, timestamp, pos = x + return height, pos + elif y > 0: + return y, 0 + else: + return 1e12, 0 def is_found(self): return self.history.values() != [[]] * len(self.history) @@ -827,7 +822,6 @@ class Abstract_Wallet(PrintError): self.tx_addr_hist[tx_hash].remove(addr) if not self.tx_addr_hist[tx_hash]: self.remove_transaction(tx_hash) - self.history[addr] = hist for tx_hash, tx_height in hist: @@ -846,7 +840,6 @@ class Abstract_Wallet(PrintError): self.save_transactions() def get_history(self, domain=None): - from collections import defaultdict # get domain if domain is None: domain = self.get_account_addresses(None) @@ -865,9 +858,10 @@ class Abstract_Wallet(PrintError): # 2. create sorted history history = [] - for tx_hash, delta in tx_deltas.items(): - conf, timestamp = self.get_confirmations(tx_hash) - history.append((tx_hash, conf, delta, timestamp)) + for tx_hash in tx_deltas: + delta = tx_deltas[tx_hash] + height, conf, timestamp = self.get_tx_height(tx_hash) + history.append((tx_hash, height, conf, timestamp, delta)) history.sort(key = lambda x: self.get_txpos(x[0])) history.reverse() @@ -875,9 +869,8 @@ class Abstract_Wallet(PrintError): c, u, x = self.get_balance(domain) balance = c + u + x h2 = [] - for item in history: - tx_hash, conf, delta, timestamp = item - h2.append((tx_hash, conf, delta, timestamp, balance)) + for tx_hash, height, conf, timestamp, delta in history: + h2.append((tx_hash, height, conf, timestamp, delta, balance)) if balance is None or delta is None: balance = None else: @@ -1076,7 +1069,7 @@ class Abstract_Wallet(PrintError): for addr, hist in self.history.items(): for tx_hash, tx_height in hist: # add it in case it was previously unconfirmed - self.add_unverified_tx (tx_hash, tx_height) + self.add_unverified_tx(tx_hash, tx_height) # if we are on a pruning server, remove unverified transactions with self.lock: diff --git a/plugins/exchange_rate/qt.py b/plugins/exchange_rate/qt.py index 393efad5..7571fe22 100644 --- a/plugins/exchange_rate/qt.py +++ b/plugins/exchange_rate/qt.py @@ -217,7 +217,7 @@ class Plugin(FxPlugin, QObject): def history_tab_update(self, tx, entry): if not self.show_history(): return - tx_hash, conf, value, timestamp, balance = tx + tx_hash, height, conf, timestamp, value, balance = tx if conf <= 0: date = timestamp_to_datetime(time.time()) else: