tdo not include fee in the transaction amount shown in history. adapt history to the case where it was recovered from a pruning server - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 650a9b6074d3248a9146548e59cb98a639311872 DIR parent 8ec2b16e218c47d0932d80e3bf225207f9ffdc23 HTML Author: thomasv <thomasv@gitorious> Date: Fri, 16 Nov 2012 14:39:31 +0100 do not include fee in the transaction amount shown in history. adapt history to the case where it was recovered from a pruning server Diffstat: M electrum | 23 ++++++++++------------- M lib/gui.py | 37 ++++++++++--------------------- M lib/gui_lite.py | 11 ++++++----- M lib/gui_qt.py | 59 +++++++++++++------------------ M lib/gui_text.py | 20 ++++++++++---------- M lib/history_widget.py | 6 +----- M lib/wallet.py | 116 +++++++++++++++++++++++++++---- 7 files changed, 164 insertions(+), 108 deletions(-) --- DIR diff --git a/electrum b/electrum t@@ -484,23 +484,20 @@ if __name__ == '__main__': print flags, m_addr, b, label if cmd == 'history': - lines = wallet.get_tx_history() - b = 0 - for line in lines: - import datetime - v = wallet.get_tx_value(line['tx_hash']) - b += v + import datetime + for item in wallet.get_tx_history(): + tx_hash, conf, is_mine, value, fee, balance, timestamp = item try: - time_str = str( datetime.datetime.fromtimestamp( line['timestamp'])) + time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3] except: - print line['timestamp'] - time_str = 'pending' - label = line.get('label') - if not label: label = line['tx_hash'] + time_str = "----" + + label, is_default_label = wallet.get_label(tx_hash) + if not label: label = tx_hash else: label = label + ' '*(64 - len(label) ) - print time_str , " " + label + " " + format_satoshis(v)+ " "+ format_satoshis(b) - print "# balance: ", format_satoshis(b) + print "%17s"%time_str, " " + label + " " + format_satoshis(value)+ " "+ format_satoshis(balance) + print "# balance: ", format_satoshis(balance) elif cmd == 'label': try: DIR diff --git a/lib/gui.py b/lib/gui.py t@@ -1223,39 +1223,26 @@ class ElectrumWindow: def update_history_tab(self): cursor = self.history_treeview.get_cursor()[0] self.history_list.clear() - balance = 0 - for tx in self.wallet.get_tx_history(): - tx_hash = tx['tx_hash'] - conf = self.wallet.verifier.get_confirmations(tx_hash) + + for item in self.wallet.get_tx_history(): + tx_hash, conf, is_mine, value, fee, balance, timestamp = item if conf: - time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3] + try: + time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3] + except: + time_str = "------" conf_icon = gtk.STOCK_APPLY else: time_str = 'pending' conf_icon = gtk.STOCK_EXECUTE - v = self.wallet.get_tx_value(tx_hash) - balance += v + label, is_default_label = self.wallet.get_label(tx_hash) - tooltip = tx_hash + "\n%d confirmations"%conf - - inputs = map(lambda x: x.get('address'), tx['inputs']) - outputs = map(lambda x: x.get('address'), tx['outputs']) - details = "Transaction Details:\n\n" \ - + "Transaction ID:\n" + tx_hash + "\n\n" \ - + "Status: %d confirmations\n\n"%conf \ - + "Date: %s\n\n"%time_str \ - + "Inputs:\n-"+ '\n-'.join(inputs) + "\n\n" \ - + "Outputs:\n-"+ '\n-'.join(outputs) - r = self.wallet.receipts.get(tx_hash) - if r: - details += "\n_______________________________________" \ - + '\n\nSigned URI: ' + r[2] \ - + "\n\nSigned by: " + r[0] \ - + '\n\nSignature: ' + r[1] - + tooltip = tx_hash + "\n%d confirmations"%conf if tx_hash else '' + details = self.wallet.get_tx_details(tx_hash) self.history_list.prepend( [tx_hash, conf_icon, time_str, label, is_default_label, - format_satoshis(v,True,self.wallet.num_zeros), format_satoshis(balance,False,self.wallet.num_zeros), tooltip, details] ) + format_satoshis(value,True,self.wallet.num_zeros), + format_satoshis(balance,False,self.wallet.num_zeros), tooltip, details] ) if cursor: self.history_treeview.set_cursor( cursor ) DIR diff --git a/lib/gui_lite.py b/lib/gui_lite.py t@@ -438,12 +438,13 @@ class MiniWindow(QDialog): self.address_completions.setStringList(completions) def update_history(self, tx_history): - for tx in tx_history[-10:]: - tx_hash = tx['tx_hash'] + from util import format_satoshis + for item in tx_history[-10:]: + tx_hash, conf, is_mine, value, fee, balance, timestamp = item label = self.actuator.wallet.get_label(tx_hash)[0] - value = self.actuator.wallet.get_tx_value(tx_hash) - amount = D(value) / 10**8 - self.history_list.append(label, amount) + #amount = D(value) / 10**8 + v_str = format_satoshis(value, True) + self.history_list.append(label, v_str) def acceptbit(self): self.actuator.acceptbit(self.quote_currencies[0]) DIR diff --git a/lib/gui_qt.py b/lib/gui_qt.py t@@ -323,38 +323,16 @@ class ElectrumWindow(QMainWindow): item = self.history_list.currentItem() if not item: return tx_hash = str(item.toolTip(0)) + if not tx_hash: return menu = QMenu() menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash)) menu.addAction(_("Details"), lambda: self.tx_details(tx_hash)) menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2)) menu.exec_(self.contacts_list.viewport().mapToGlobal(position)) - def tx_details(self, tx_hash): - tx = self.wallet.transactions.get(tx_hash) - - conf = self.wallet.verifier.get_confirmations(tx_hash) - timestamp = tx.get('timestamp') - if conf and timestamp: - time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] - else: - time_str = 'pending' - - inputs = map(lambda x: x.get('address'), tx['inputs']) - outputs = map(lambda x: x.get('address'), tx['outputs']) - tx_details = _("Transaction Details") +"\n\n" \ - + "Transaction ID:\n" + tx_hash + "\n\n" \ - + "Status: %d confirmations\n\n"%conf \ - + "Date: %s\n\n"%time_str \ - + "Inputs:\n-"+ '\n-'.join(inputs) + "\n\n" \ - + "Outputs:\n-"+ '\n-'.join(outputs) - - r = self.wallet.receipts.get(tx_hash) - if r: - tx_details += "\n_______________________________________" \ - + '\n\nSigned URI: ' + r[2] \ - + "\n\nSigned by: " + r[0] \ - + '\n\nSignature: ' + r[1] + def tx_details(self, tx_hash): + tx_details = self.wallet.get_tx_details(tx_hash) QMessageBox.information(self, 'Details', tx_details, 'OK') t@@ -432,14 +410,13 @@ class ElectrumWindow(QMainWindow): def update_history_tab(self): + self.history_list.clear() - balance = 0 - for tx in self.wallet.get_tx_history(): - tx_hash = tx['tx_hash'] - conf = self.wallet.verifier.get_confirmations(tx_hash) + for item in self.wallet.get_tx_history(): + tx_hash, conf, is_mine, value, fee, balance, timestamp = item if conf: try: - time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3] + time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3] except: time_str = "unknown" if conf == -1: t@@ -453,20 +430,32 @@ class ElectrumWindow(QMainWindow): else: time_str = 'pending' icon = QIcon(":icons/unconfirmed.png") - v = self.wallet.get_tx_value(tx_hash) - balance += v - label, is_default_label = self.wallet.get_label(tx_hash) - item = QTreeWidgetItem( [ '', time_str, label, format_satoshis(v,True,self.wallet.num_zeros), format_satoshis(balance,False,self.wallet.num_zeros)] ) + if value is not None: + v_str = format_satoshis(value, True, self.wallet.num_zeros) + else: + v_str = '--' + + balance_str = format_satoshis(balance, False, self.wallet.num_zeros) + + if tx_hash: + label, is_default_label = self.wallet.get_label(tx_hash) + else: + label = _('Pruned transaction outputs') + is_default_label = False + + item = QTreeWidgetItem( [ '', time_str, label, v_str, balance_str] ) item.setFont(2, QFont(MONOSPACE_FONT)) item.setFont(3, QFont(MONOSPACE_FONT)) item.setFont(4, QFont(MONOSPACE_FONT)) - item.setToolTip(0, tx_hash) + if tx_hash: + item.setToolTip(0, tx_hash) if is_default_label: item.setForeground(2, QBrush(QColor('grey'))) item.setIcon(0, icon) self.history_list.insertTopLevelItem(0,item) + self.history_list.setCurrentItem(self.history_list.topLevelItem(0)) DIR diff --git a/lib/gui_text.py b/lib/gui_text.py t@@ -66,19 +66,19 @@ class ElectrumGui: b = 0 messages = [] - for tx in self.wallet.get_tx_history(): - v = self.wallet.get_tx_value(tx['tx_hash']) - b += v - try: - time_str = str( datetime.datetime.fromtimestamp( tx['timestamp'])) - except: - print tx['timestamp'] + + for item in self.wallet.get_tx_history(): + tx_hash, conf, is_mine, value, fee, balance, timestamp = item + if conf: + try: + time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3] + except: + time_str = "------" + else: time_str = 'pending' - tx_hash = tx['tx_hash'] label, is_default_label = self.wallet.get_label(tx_hash) - #label += ' '*(40 - len(label) ) - messages.append( format_str%( time_str, label, format_satoshis(v), format_satoshis(b) ) ) + messages.append( format_str%( time_str, label, format_satoshis(value), format_satoshis(balance) ) ) self.print_list(messages[::-1], format_str%( _("Date"), _("Description"), _("Amount"), _("Balance"))) DIR diff --git a/lib/history_widget.py b/lib/history_widget.py t@@ -10,10 +10,6 @@ class HistoryWidget(QTreeWidget): self.setIndentation(0) def append(self, address, amount): - if amount >= 0: - display_amount = "+%s" % amount - else: - display_amount = "-%s" % (-amount) - item = QTreeWidgetItem([display_amount, address]) + item = QTreeWidgetItem([amount, address]) self.insertTopLevelItem(0, item) DIR diff --git a/lib/wallet.py b/lib/wallet.py t@@ -76,7 +76,7 @@ class Wallet: self.transactions = config.get('transactions',{}) # txid -> deserialised # not saved - self.prevout_values = {} + self.prevout_values = {} # my own transaction outputs self.spent_outputs = [] self.receipt = None # next receipt self.banner = '' t@@ -355,7 +355,8 @@ class Wallet: def fill_addressbook(self): for tx_hash, tx in self.transactions.items(): - if self.get_tx_value(tx_hash)<0: + is_send, _, _ = self.get_tx_value(tx_hash) + if is_send: for o in tx['outputs']: addr = o.get('address') if not self.is_mine(addr) and addr not in self.addressbook: t@@ -374,24 +375,79 @@ class Wallet: # return the balance for that tx if addresses is None: addresses = self.all_addresses() with self.lock: - v = 0 + is_send = False + is_pruned = False + v_in = v_out = v = 0 d = self.transactions.get(tx_hash) if not d: return 0 for item in d.get('inputs'): addr = item.get('address') if addr in addresses: + is_send = True key = item['prevout_hash'] + ':%d'%item['prevout_n'] value = self.prevout_values.get( key ) - if value is None: continue + if value is None: + is_pruned = True + break + v -= value + v_in += value + + if is_pruned: v = 0 for item in d.get('outputs'): addr = item.get('address') - if addr in addresses: - value = item.get('value') - v += value - return v + value = item.get('value') + v_out += value + if not is_pruned: + if addr in addresses: v += value + else: + if addr not in addresses: v -= value + + # I can compute the fee only if I am the spender and inputs were not pruned + fee = v_in - v_out if is_send and not is_pruned else None + return is_send, v, fee + def get_tx_details(self, tx_hash): + import datetime + if not tx_hash: return '' + tx = self.transactions.get(tx_hash) + is_mine, v, fee = self.get_tx_value(tx_hash) + conf = self.verifier.get_confirmations(tx_hash) + timestamp = tx.get('timestamp') + if conf and timestamp: + time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3] + else: + time_str = 'pending' + + inputs = map(lambda x: x.get('address'), tx['inputs']) + outputs = map(lambda x: x.get('address'), tx['outputs']) + tx_details = "Transaction Details" +"\n\n" \ + + "Transaction ID:\n" + tx_hash + "\n\n" \ + + "Status: %d confirmations\n"%conf + if is_mine: + if fee: + tx_details += "Amount sent: %s\n"% format_satoshis(v+fee, False) \ + + "Transaction fee: %s\n"% format_satoshis(fee, False) + else: + tx_details += "Amount sent: %s\n"% format_satoshis(v, False) \ + + "Transaction fee: unknown\n" + else: + tx_details += "Amount received: %s\n"% format_satoshis(v, False) \ + + tx_details += "Date: %s\n\n"%time_str \ + + "Inputs:\n-"+ '\n-'.join(inputs) + "\n\n" \ + + "Outputs:\n-"+ '\n-'.join(outputs) + + r = self.receipts.get(tx_hash) + if r: + tx_details += "\n_______________________________________" \ + + '\n\nSigned URI: ' + r[2] \ + + "\n\nSigned by: " + r[0] \ + + '\n\nSignature: ' + r[1] + + return tx_details + def update_tx_outputs(self, tx_hash): tx = self.transactions.get(tx_hash) t@@ -412,8 +468,13 @@ class Wallet: h = self.history.get(addr,[]) if h == ['*']: return 0,0 c = u = 0 + for tx_hash, tx_height in h: - v = self.get_tx_value(tx_hash, [addr]) + # if the tx is mine, then I know its outputs and input values + # if it is not mine, I only need its outputs + is_mine, v, fee = self.get_tx_value(tx_hash, [addr]) + if v is None:raise + if tx_height: c += v else: t@@ -455,7 +516,6 @@ class Wallet: output['tx_hash'] = tx_hash coins.append(output) - #coins = sorted( coins, key = lambda x: x[1]['timestamp'] ) for addr in self.prioritized_addresses: h = self.history.get(addr, []) t@@ -468,7 +528,6 @@ class Wallet: output['tx_hash'] = tx_hash prioritized_coins.append(output) - #prioritized_coins = sorted( prioritized_coins, key = lambda x: x[1]['timestamp'] ) inputs = [] coins = prioritized_coins + coins t@@ -584,9 +643,35 @@ class Wallet: def get_tx_history(self): with self.lock: - lines = self.transactions.values() - lines.sort(key = lambda x: x.get('timestamp') if x.get('timestamp') else 1e12) - return lines + history = self.transactions.values() + history.sort(key = lambda x: x.get('timestamp') if x.get('timestamp') else 1e12) + result = [] + + balance = 0 + for tx in history: + is_mine, v, fee = self.get_tx_value(tx['tx_hash']) + if v is not None: balance += v + c, u = self.get_balance() + + if balance != c+u: + v_str = format_satoshis( c+u - balance, True, self.num_zeros) + result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) ) + + balance = c + u - balance + for tx in history: + tx_hash = tx['tx_hash'] + timestamp = tx.get('timestamp') + conf = self.verifier.get_confirmations(tx_hash) if self.verifier else None + is_mine, v, fee = self.get_tx_value(tx_hash) + if v is not None: + balance += v + value = v + fee if fee is not None else v + else: + value = None + + result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) ) + + return result def get_transactions_at_height(self, height): with self.lock: t@@ -609,7 +694,8 @@ class Wallet: tx = self.transactions.get(tx_hash) if tx: default_label = '' - if self.get_tx_value(tx_hash)<0: + is_mine, _, _ = self.get_tx_value(tx_hash) + if is_mine: for o in tx['outputs']: o_addr = o.get('address') if not self.is_mine(o_addr):