Merge pull request #5825 from SomberNight/201912_local_tx_can_be_partial
wallet: allow saving partial tx as local (if it has a txid)
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,8 +700,16 @@ 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
|
||||||
self.transactions[tx_hash] = 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
|
@modifier
|
||||||
def remove_transaction(self, tx_hash) -> Optional[Transaction]:
|
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]
|
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():
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user