Browse Source

Merge pull request #5825 from SomberNight/201912_local_tx_can_be_partial

wallet: allow saving partial tx as local (if it has a txid)
hard-fail-on-bad-server-string
ThomasV 5 years ago
committed by GitHub
parent
commit
2c6a1f55fb
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      electrum/address_synchronizer.py
  2. 1
      electrum/gui/qt/transaction_dialog.py
  3. 16
      electrum/json_db.py
  4. 2
      electrum/lnworker.py
  5. 13
      electrum/wallet.py

3
electrum/address_synchronizer.py

@ -216,7 +216,8 @@ class AddressSynchronizer(Logger):
def add_transaction(self, tx: Transaction, *, allow_unrelated=False) -> bool: def add_transaction(self, tx: Transaction, *, allow_unrelated=False) -> bool:
"""Returns whether the tx was successfully added to the wallet history.""" """Returns whether the tx was successfully added to the wallet history."""
assert tx, tx 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() tx_hash = tx.txid()
if tx_hash is None: if tx_hash is None:
raise Exception("cannot add tx without txid to wallet history") raise Exception("cannot add tx without txid to wallet history")

1
electrum/gui/qt/transaction_dialog.py

@ -282,7 +282,6 @@ class BaseTxDialog(QDialog, MessageBoxMixin):
def sign(self): def sign(self):
def sign_done(success): def sign_done(success):
# note: with segwit we could save partially signed tx, because they have a txid
if self.tx.is_complete(): if self.tx.is_complete():
self.prompt_if_unsaved = True self.prompt_if_unsaved = True
self.saved = False self.saved = False

16
electrum/json_db.py

@ -33,7 +33,7 @@ from typing import Dict, Optional, List, Tuple, Set, Iterable, NamedTuple, Seque
from . import util, bitcoin from . import util, bitcoin
from .util import profiler, WalletFileException, multisig_type, TxMinedInfo, bfh from .util import profiler, WalletFileException, multisig_type, TxMinedInfo, bfh
from .keystore import bip44_derivation from .keystore import bip44_derivation
from .transaction import Transaction, TxOutpoint from .transaction import Transaction, TxOutpoint, tx_from_any
from .logging import Logger from .logging import Logger
# seed_version is now used for the version of the wallet file # seed_version is now used for the version of the wallet file
@ -700,7 +700,15 @@ class JsonDB(Logger):
@modifier @modifier
def add_transaction(self, tx_hash: str, tx: Transaction) -> None: def add_transaction(self, tx_hash: str, tx: Transaction) -> None:
assert isinstance(tx, Transaction) 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 self.transactions[tx_hash] = tx
@modifier @modifier
@ -903,9 +911,9 @@ class JsonDB(Logger):
self.tx_fees = self.get_data_ref('tx_fees') # type: Dict[str, TxFeesValue] self.tx_fees = self.get_data_ref('tx_fees') # type: Dict[str, TxFeesValue]
# scripthash -> set of (outpoint, value) # scripthash -> set of (outpoint, value)
self._prevouts_by_scripthash = self.get_data_ref('prevouts_by_scripthash') # type: Dict[str, Set[Tuple[str, int]]] 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(): 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 # convert txi, txo: list to set
for t in self.txi, self.txo: for t in self.txi, self.txo:
for d in t.values(): for d in t.values():

2
electrum/lnworker.py

@ -832,6 +832,8 @@ class LNWallet(LNWorker):
self.save_channel(chan) self.save_channel(chan)
self.lnwatcher.add_channel(chan.funding_outpoint.to_str(), chan.get_funding_address()) self.lnwatcher.add_channel(chan.funding_outpoint.to_str(), chan.get_funding_address())
self.network.trigger_callback('channels_updated', self.wallet) 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(): if funding_tx.is_complete():
# TODO make more robust (timeout low? server returns error?) # TODO make more robust (timeout low? server returns error?)
await asyncio.wait_for(self.network.broadcast_transaction(funding_tx), LN_P2P_NETWORK_TIMEOUT) await asyncio.wait_for(self.network.broadcast_transaction(funding_tx), LN_P2P_NETWORK_TIMEOUT)

13
electrum/wallet.py

@ -356,7 +356,9 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
def is_deterministic(self) -> bool: def is_deterministic(self) -> bool:
return self.keystore.is_deterministic() 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 changed = False
old_text = self.labels.get(name) old_text = self.labels.get(name)
if text: if text:
@ -465,12 +467,14 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
exp_n = None exp_n = None
can_broadcast = False can_broadcast = False
can_bump = False can_bump = False
can_save_as_local = False
label = ''
tx_hash = tx.txid() 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) tx_mined_status = self.get_tx_height(tx_hash)
if tx.is_complete(): if tx.is_complete():
if self.db.get_transaction(tx_hash): if tx_we_already_have_in_db:
label = self.get_label(tx_hash) label = self.get_label(tx_hash)
if tx_mined_status.height > 0: if tx_mined_status.height > 0:
if tx_mined_status.conf: if tx_mined_status.conf:
@ -493,7 +497,6 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
else: else:
status = _("Signed") status = _("Signed")
can_broadcast = self.network is not None can_broadcast = self.network is not None
can_save_as_local = is_relevant
else: else:
s, r = tx.signature_count() s, r = tx.signature_count()
status = _("Unsigned") if s == 0 else _('Partially signed') + ' (%d/%d)'%(s,r) status = _("Unsigned") if s == 0 else _('Partially signed') + ' (%d/%d)'%(s,r)

Loading…
Cancel
Save