diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py index ab32ccbb0..3af00368c 100644 --- a/electrum/address_synchronizer.py +++ b/electrum/address_synchronizer.py @@ -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") diff --git a/electrum/gui/qt/transaction_dialog.py b/electrum/gui/qt/transaction_dialog.py index 3b1d85989..d9a4af73c 100644 --- a/electrum/gui/qt/transaction_dialog.py +++ b/electrum/gui/qt/transaction_dialog.py @@ -281,7 +281,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 diff --git a/electrum/json_db.py b/electrum/json_db.py index 3a1519f1b..8b7239201 100644 --- a/electrum/json_db.py +++ b/electrum/json_db.py @@ -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 @@ -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]: @@ -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(): diff --git a/electrum/wallet.py b/electrum/wallet.py index dc6dd7d76..88017db9b 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -353,7 +353,9 @@ class Abstract_Wallet(AddressSynchronizer): def is_deterministic(self): 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: @@ -469,12 +471,14 @@ class Abstract_Wallet(AddressSynchronizer): 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: @@ -497,7 +501,6 @@ class Abstract_Wallet(AddressSynchronizer): 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)