URI: 
       tMerge branch 'local_tx' - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 2343894e0f9b33641f43a4670949851ac59f1dd0
   DIR parent 0e7e7e3dc5fad0705bf64ead8350c12c1f432d0c
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Tue, 30 Jan 2018 00:22:02 +0100
       
       Merge branch 'local_tx'
       
       Diffstat:
         M gui/qt/history_list.py              |      11 ++++++++---
         M gui/qt/transaction_dialog.py        |      30 +++++++++++++++++++++++++-----
         M lib/commands.py                     |       1 -
         M lib/wallet.py                       |      40 ++++++++++++++++++++++----------
       
       4 files changed, 61 insertions(+), 21 deletions(-)
       ---
   DIR diff --git a/gui/qt/history_list.py b/gui/qt/history_list.py
       t@@ -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
       t@@ -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()
   DIR diff --git a/gui/qt/transaction_dialog.py b/gui/qt/transaction_dialog.py
       t@@ -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...
       t@@ -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)
       t@@ -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)
        
       t@@ -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)
        
       t@@ -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):
   DIR diff --git a/lib/commands.py b/lib/commands.py
       t@@ -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()
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -72,6 +72,9 @@ TX_STATUS = [
            _('Local only'),
        ]
        
       +TX_HEIGHT_LOCAL = -2
       +TX_HEIGHT_UNCONF_PARENT = -1
       +TX_HEIGHT_UNCONFIRMED = 0
        
        
        def relayfee(network):
       t@@ -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.
       t@@ -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)
       t@@ -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"
       t@@ -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)
        
       t@@ -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)
       t@@ -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:
       t@@ -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 = {}
       t@@ -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:
       t@@ -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))
       t@@ -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
        
       t@@ -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()
        
       t@@ -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
       t@@ -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