URI: 
       tMerge pull request #5825 from SomberNight/201912_local_tx_can_be_partial - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 2c6a1f55fb5bef6e29415aac310f905dae522801
   DIR parent 61fc00fb9ec18faf6039f9da374722e1d4f487aa
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Sun, 15 Dec 2019 16:40:46 +0100
       
       Merge pull request #5825 from SomberNight/201912_local_tx_can_be_partial
       
       wallet: allow saving partial tx as local (if it has a txid)
       Diffstat:
         M electrum/address_synchronizer.py    |       3 ++-
         M electrum/gui/qt/transaction_dialog… |       1 -
         M electrum/json_db.py                 |      18 +++++++++++++-----
         M electrum/lnworker.py                |       2 ++
         M electrum/wallet.py                  |      13 ++++++++-----
       
       5 files changed, 25 insertions(+), 12 deletions(-)
       ---
   DIR diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py
       t@@ -216,7 +216,8 @@ class AddressSynchronizer(Logger):
            def add_transaction(self, tx: Transaction, *, allow_unrelated=False) -> bool:
                """Returns whether the tx was successfully added to the wallet history."""
                assert tx, tx
       -        assert tx.is_complete()
       +        # note: tx.is_complete() is not necessarily True; tx might be partial
       +        # but it *needs* to have a txid:
                tx_hash = tx.txid()
                if tx_hash is None:
                    raise Exception("cannot add tx without txid to wallet history")
   DIR diff --git a/electrum/gui/qt/transaction_dialog.py b/electrum/gui/qt/transaction_dialog.py
       t@@ -282,7 +282,6 @@ class BaseTxDialog(QDialog, MessageBoxMixin):
        
            def sign(self):
                def sign_done(success):
       -            # note: with segwit we could save partially signed tx, because they have a txid
                    if self.tx.is_complete():
                        self.prompt_if_unsaved = True
                        self.saved = False
   DIR diff --git a/electrum/json_db.py b/electrum/json_db.py
       t@@ -33,7 +33,7 @@ from typing import Dict, Optional, List, Tuple, Set, Iterable, NamedTuple, Seque
        from . import util, bitcoin
        from .util import profiler, WalletFileException, multisig_type, TxMinedInfo, bfh
        from .keystore import bip44_derivation
       -from .transaction import Transaction, TxOutpoint
       +from .transaction import Transaction, TxOutpoint, tx_from_any
        from .logging import Logger
        
        # seed_version is now used for the version of the wallet file
       t@@ -700,8 +700,16 @@ class JsonDB(Logger):
        
            @modifier
            def add_transaction(self, tx_hash: str, tx: Transaction) -> None:
       -        assert isinstance(tx, Transaction)
       -        self.transactions[tx_hash] = tx
       +        assert isinstance(tx, Transaction), tx
       +        # note that tx might be a PartialTransaction
       +        if not tx_hash:
       +            raise Exception("trying to add tx to db without txid")
       +        if tx_hash != tx.txid():
       +            raise Exception(f"trying to add tx to db with inconsistent txid: {tx_hash} != {tx.txid()}")
       +        # don't allow overwriting complete tx with partial tx
       +        tx_we_already_have = self.transactions.get(tx_hash, None)
       +        if tx_we_already_have is None or not tx_we_already_have.is_complete():
       +            self.transactions[tx_hash] = tx
        
            @modifier
            def remove_transaction(self, tx_hash) -> Optional[Transaction]:
       t@@ -903,9 +911,9 @@ class JsonDB(Logger):
                self.tx_fees = self.get_data_ref('tx_fees')  # type: Dict[str, TxFeesValue]
                # scripthash -> set of (outpoint, value)
                self._prevouts_by_scripthash = self.get_data_ref('prevouts_by_scripthash')  # type: Dict[str, Set[Tuple[str, int]]]
       -        # convert raw hex transactions to Transaction objects
       +        # convert raw transactions to Transaction objects
                for tx_hash, raw_tx in self.transactions.items():
       -            self.transactions[tx_hash] = Transaction(raw_tx)
       +            self.transactions[tx_hash] = tx_from_any(raw_tx)
                # convert txi, txo: list to set
                for t in self.txi, self.txo:
                    for d in t.values():
   DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py
       t@@ -832,6 +832,8 @@ class LNWallet(LNWorker):
                self.save_channel(chan)
                self.lnwatcher.add_channel(chan.funding_outpoint.to_str(), chan.get_funding_address())
                self.network.trigger_callback('channels_updated', self.wallet)
       +        self.wallet.add_transaction(funding_tx)  # save tx as local into the wallet
       +        self.wallet.set_label(funding_tx.txid(), _('Open channel'))
                if funding_tx.is_complete():
                    # TODO make more robust (timeout low? server returns error?)
                    await asyncio.wait_for(self.network.broadcast_transaction(funding_tx), LN_P2P_NETWORK_TIMEOUT)
   DIR diff --git a/electrum/wallet.py b/electrum/wallet.py
       t@@ -356,7 +356,9 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
            def is_deterministic(self) -> bool:
                return self.keystore.is_deterministic()
        
       -    def set_label(self, name, text = None):
       +    def set_label(self, name: str, text: str = None) -> bool:
       +        if not name:
       +            return False
                changed = False
                old_text = self.labels.get(name)
                if text:
       t@@ -465,12 +467,14 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
                exp_n = None
                can_broadcast = False
                can_bump = False
       -        can_save_as_local = False
       -        label = ''
                tx_hash = tx.txid()
       +        tx_we_already_have_in_db = self.db.get_transaction(tx_hash)
       +        can_save_as_local = (is_relevant and tx.txid() is not None
       +                             and (tx_we_already_have_in_db is None or not tx_we_already_have_in_db.is_complete()))
       +        label = ''
                tx_mined_status = self.get_tx_height(tx_hash)
                if tx.is_complete():
       -            if self.db.get_transaction(tx_hash):
       +            if tx_we_already_have_in_db:
                        label = self.get_label(tx_hash)
                        if tx_mined_status.height > 0:
                            if tx_mined_status.conf:
       t@@ -493,7 +497,6 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
                    else:
                        status = _("Signed")
                        can_broadcast = self.network is not None
       -                can_save_as_local = is_relevant
                else:
                    s, r = tx.signature_count()
                    status = _("Unsigned") if s == 0 else _('Partially signed') + ' (%d/%d)'%(s,r)