electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 2343894e0f9b33641f43a4670949851ac59f1dd0
parent 0e7e7e3dc5fad0705bf64ead8350c12c1f432d0c
Author: ThomasV <thomasv@electrum.org>
Date:   Tue, 30 Jan 2018 00:22:02 +0100

Merge branch 'local_tx'

Diffstat:
Mgui/qt/history_list.py | 11++++++++---
Mgui/qt/transaction_dialog.py | 30+++++++++++++++++++++++++-----
Mlib/commands.py | 1-
Mlib/wallet.py | 40++++++++++++++++++++++++++++------------
4 files changed, 61 insertions(+), 21 deletions(-)

diff --git a/gui/qt/history_list.py b/gui/qt/history_list.py @@ -25,6 +25,7 @@ import webbrowser +from electrum.wallet import UnrelatedTransactionException from .util import * from electrum.i18n import _ from electrum.util import block_explorer_URL @@ -211,6 +212,10 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop): def onFileAdded(self, fn): with open(fn) as f: tx = self.parent.tx_from_text(f.read()) - self.wallet.add_transaction(tx.txid(), tx) - self.wallet.save_transactions(write=True) - self.on_update() + try: + self.wallet.add_transaction(tx.txid(), tx) + except UnrelatedTransactionException as e: + self.parent.show_error(e) + else: + self.wallet.save_transactions(write=True) + self.on_update() diff --git a/gui/qt/transaction_dialog.py b/gui/qt/transaction_dialog.py @@ -35,6 +35,8 @@ from electrum.i18n import _ from electrum.plugins import run_hook from electrum.util import bfh +from electrum.wallet import UnrelatedTransactionException + from .util import * dialogs = [] # Otherwise python randomly garbage collects the dialogs... @@ -98,8 +100,13 @@ class TxDialog(QDialog, MessageBoxMixin): self.broadcast_button = b = QPushButton(_("Broadcast")) b.clicked.connect(self.do_broadcast) - self.save_button = b = QPushButton(_("Save")) - b.clicked.connect(self.save) + self.save_button = QPushButton(_("Save")) + self.save_button.setDisabled(True) + self.save_button.setToolTip(_("Please sign this transaction in order to save it")) + self.save_button.clicked.connect(self.save) + + self.export_button = b = QPushButton(_("Export")) + b.clicked.connect(self.export) self.cancel_button = b = QPushButton(_("Close")) b.clicked.connect(self.close) @@ -112,9 +119,9 @@ class TxDialog(QDialog, MessageBoxMixin): self.copy_button = CopyButton(lambda: str(self.tx), parent.app) # Action buttons - self.buttons = [self.sign_button, self.broadcast_button, self.cancel_button] + self.buttons = [self.sign_button, self.broadcast_button, self.save_button, self.cancel_button] # Transaction sharing buttons - self.sharing_buttons = [self.copy_button, self.qr_button, self.save_button] + self.sharing_buttons = [self.copy_button, self.qr_button, self.export_button] run_hook('transaction_dialog', self) @@ -155,6 +162,8 @@ class TxDialog(QDialog, MessageBoxMixin): if success: self.prompt_if_unsaved = True self.saved = False + self.save_button.setDisabled(False) + self.save_button.setToolTip("") self.update() self.main_window.pop_top_level_window(self) @@ -163,12 +172,23 @@ class TxDialog(QDialog, MessageBoxMixin): self.main_window.sign_tx(self.tx, sign_done) def save(self): + self.wallet.add_transaction(self.tx.txid(), self.tx) + self.wallet.save_transactions(write=True) + + self.main_window.history_list.update() + + self.save_button.setDisabled(True) + self.show_message(_("Transaction saved successfully")) + self.saved = True + + + def export(self): name = 'signed_%s.txn' % (self.tx.txid()[0:8]) if self.tx.is_complete() else 'unsigned.txn' fileName = self.main_window.getSaveFileName(_("Select where to save your signed transaction"), name, "*.txn") if fileName: with open(fileName, "w+") as f: f.write(json.dumps(self.tx.as_dict(), indent=4) + '\n') - self.show_message(_("Transaction saved successfully")) + self.show_message(_("Transaction exported successfully")) self.saved = True def update(self): diff --git a/lib/commands.py b/lib/commands.py @@ -632,7 +632,6 @@ class Commands: @command('w') def addtransaction(self, tx): """ Add a transaction to the wallet history """ - #fixme: we should ensure that tx is related to wallet tx = Transaction(tx) self.wallet.add_transaction(tx.txid(), tx) self.wallet.save_transactions() diff --git a/lib/wallet.py b/lib/wallet.py @@ -72,6 +72,9 @@ TX_STATUS = [ _('Local only'), ] +TX_HEIGHT_LOCAL = -2 +TX_HEIGHT_UNCONF_PARENT = -1 +TX_HEIGHT_UNCONFIRMED = 0 def relayfee(network): @@ -154,6 +157,11 @@ def sweep(privkeys, network, config, recipient, fee=None, imax=100): return tx +class UnrelatedTransactionException(Exception): + def __init__(self): + self.args = ("Transaction is unrelated to this wallet ", ) + + class Abstract_Wallet(PrintError): """ Wallet classes are created to handle various address generation methods. @@ -366,7 +374,8 @@ class Abstract_Wallet(PrintError): return self.get_pubkeys(*sequence) def add_unverified_tx(self, tx_hash, tx_height): - if tx_height == 0 and tx_hash in self.verified_tx: + if tx_height in (TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT) \ + and tx_hash in self.verified_tx: self.verified_tx.pop(tx_hash) if self.verifier: self.verifier.merkle_roots.pop(tx_hash, None) @@ -417,7 +426,7 @@ class Abstract_Wallet(PrintError): return height, 0, False else: # local transaction - return -2, 0, False + return TX_HEIGHT_LOCAL, 0, False def get_txpos(self, tx_hash): "return position, even if the tx is unverified" @@ -427,7 +436,7 @@ class Abstract_Wallet(PrintError): return height, pos elif tx_hash in self.unverified_tx: height = self.unverified_tx[tx_hash] - return (height, 0) if height>0 else (1e9 - height), 0 + return (height, 0) if height > 0 else ((1e9 - height), 0) else: return (1e9+1, 0) @@ -524,7 +533,7 @@ class Abstract_Wallet(PrintError): status = _("%d confirmations") % conf else: status = _('Not verified') - elif height in [-1,0]: + elif height in (TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED): status = _('Unconfirmed') if fee is None: fee = self.tx_fees.get(tx_hash) @@ -603,7 +612,7 @@ class Abstract_Wallet(PrintError): x += v elif tx_height > 0: c += v - else: + elif tx_height != TX_HEIGHT_LOCAL: u += v if txo in sent: if sent[txo] > 0: @@ -675,6 +684,7 @@ class Abstract_Wallet(PrintError): def add_transaction(self, tx_hash, tx): is_coinbase = tx.inputs()[0]['type'] == 'coinbase' + related = False with self.transaction_lock: # add inputs self.txi[tx_hash] = d = {} @@ -688,6 +698,7 @@ class Abstract_Wallet(PrintError): addr = self.find_pay_to_pubkey_address(prevout_hash, prevout_n) # find value from prev output if addr and self.is_mine(addr): + related = True dd = self.txo.get(prevout_hash, {}) for n, v, is_cb in dd.get(addr, []): if n == prevout_n: @@ -710,6 +721,7 @@ class Abstract_Wallet(PrintError): else: addr = None if addr and self.is_mine(addr): + related = True if d.get(addr) is None: d[addr] = [] d[addr].append((n, v, is_coinbase)) @@ -721,6 +733,10 @@ class Abstract_Wallet(PrintError): if dd.get(addr) is None: dd[addr] = [] dd[addr].append((ser, v)) + + if not related: + raise UnrelatedTransactionException() + # save self.transactions[tx_hash] = tx @@ -813,7 +829,7 @@ class Abstract_Wallet(PrintError): h2.append((tx_hash, height, conf, timestamp, delta, balance)) if balance is None or delta is None: balance = None - else: + elif height != TX_HEIGHT_LOCAL: balance -= delta h2.reverse() @@ -855,15 +871,15 @@ class Abstract_Wallet(PrintError): is_lowfee = fee < low_fee * 0.5 else: is_lowfee = False - if height == -2: + if height == TX_HEIGHT_LOCAL: status = 5 - elif height == -1: + elif height == TX_HEIGHT_UNCONF_PARENT: status = 1 - elif height==0 and not is_final: + elif height == TX_HEIGHT_UNCONFIRMED and not is_final: status = 0 - elif height == 0 and is_lowfee: + elif height == TX_HEIGHT_UNCONFIRMED and is_lowfee: status = 2 - elif height == 0: + elif height == TX_HEIGHT_UNCONFIRMED: status = 3 else: status = 4 @@ -1045,7 +1061,7 @@ class Abstract_Wallet(PrintError): age = -1 h = self.history.get(address, []) for tx_hash, tx_height in h: - if tx_height == 0: + if tx_height <= 0: tx_age = 0 else: tx_age = self.get_local_height() - tx_height + 1