Skip to content

Commit 3ad4406

Browse files
committed
add future transactions to wallet, in order to provide user feedback
1 parent daadbb9 commit 3ad4406

File tree

3 files changed

+70
-27
lines changed

3 files changed

+70
-27
lines changed

electrum/lnwatcher.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ def inspect_tx_candidate(self, outpoint, n: int) -> Dict[str, str]:
248248
"""
249249
prev_txid, index = outpoint.split(':')
250250
spender_txid = self.adb.db.get_spent_outpoint(prev_txid, int(index))
251+
# discard local spenders
252+
tx_mined_status = self.adb.get_tx_height(spender_txid)
253+
if tx_mined_status.height in [TX_HEIGHT_LOCAL, TX_HEIGHT_FUTURE]:
254+
spender_txid = None
251255
result = {outpoint:spender_txid}
252256
if n == 0:
253257
if spender_txid is None:
@@ -263,7 +267,7 @@ def inspect_tx_candidate(self, outpoint, n: int) -> Dict[str, str]:
263267
# if tx input is not a first-stage HTLC, we can stop recursion
264268
if len(spender_tx.inputs()) != 1:
265269
return result
266-
o = spender_tx.inputs()[0]
270+
o = spender_tx.inputs()[0] # fixme?
267271
witness = o.witness_elements()
268272
if not witness:
269273
# This can happen if spender_tx is a local unsigned tx in the wallet history, e.g.:

electrum/submarine_swaps.py

+5-17
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from .lnaddr import lndecode
3333
from .json_db import StoredObject, stored_in
3434
from . import constants
35-
from .address_synchronizer import TX_HEIGHT_LOCAL
35+
from .address_synchronizer import TX_HEIGHT_LOCAL, TX_HEIGHT_FUTURE
3636
from .i18n import _
3737

3838
from .bitcoin import construct_script
@@ -346,29 +346,17 @@ async def _claim_swap(self, swap: SwapData) -> None:
346346
self._add_or_reindex_swap(swap) # to update _swaps_by_funding_outpoint
347347
funding_height = self.lnwatcher.adb.get_tx_height(txin.prevout.txid.hex())
348348
spent_height = txin.spent_height
349+
# discard local spenders
350+
if spent_height in [TX_HEIGHT_LOCAL, TX_HEIGHT_FUTURE]:
351+
spent_height = None
352+
349353
if spent_height is not None:
350354
swap.spending_txid = txin.spent_txid
351355
if spent_height > 0:
352356
if current_height - spent_height > REDEEM_AFTER_DOUBLE_SPENT_DELAY:
353357
self.logger.info(f'stop watching swap {swap.lockup_address}')
354358
self.lnwatcher.remove_callback(swap.lockup_address)
355359
swap.is_redeemed = True
356-
elif spent_height == TX_HEIGHT_LOCAL:
357-
if funding_height.conf > 0 or (swap.is_reverse and self.wallet.config.LIGHTNING_ALLOW_INSTANT_SWAPS):
358-
tx = self.lnwatcher.adb.get_transaction(txin.spent_txid)
359-
try:
360-
await self.network.broadcast_transaction(tx)
361-
except TxBroadcastError:
362-
self.logger.info(f'error broadcasting claim tx {txin.spent_txid}')
363-
elif funding_height.height == TX_HEIGHT_LOCAL:
364-
# the funding tx was double spent.
365-
# this will remove both funding and child (spending tx) from adb
366-
self.lnwatcher.adb.remove_transaction(swap.funding_txid)
367-
swap.funding_txid = None
368-
swap.spending_txid = None
369-
else:
370-
# spending tx is in mempool
371-
pass
372360

373361
if not swap.is_reverse:
374362
if swap.preimage is None and spent_height is not None:

electrum/wallet.py

+60-9
Original file line numberDiff line numberDiff line change
@@ -1151,9 +1151,8 @@ def get_onchain_history(self, *, domain=None):
11511151
'date': timestamp_to_datetime(hist_item.tx_mined_status.timestamp),
11521152
'label': self.get_label_for_txid(hist_item.txid),
11531153
'txpos_in_block': hist_item.tx_mined_status.txpos,
1154+
'wanted_height': hist_item.tx_mined_status.wanted_height,
11541155
}
1155-
if wanted_height := hist_item.tx_mined_status.wanted_height:
1156-
d['wanted_height'] = wanted_height
11571156
yield d
11581157

11591158
def create_invoice(self, *, outputs: List[PartialTxOutput], message, pr, URI) -> Invoice:
@@ -1412,6 +1411,7 @@ def sort_key(x):
14121411
parent['date'] = timestamp_to_datetime(tx_item['timestamp'])
14131412
parent['height'] = tx_item['height']
14141413
parent['confirmations'] = tx_item['confirmations']
1414+
parent['wanted_height'] = tx_item.get('wanted_height')
14151415
parent['children'].append(tx_item)
14161416

14171417
now = time.time()
@@ -3320,8 +3320,10 @@ def add_sweep_info(self, sweep_info: 'SweepInfo'):
33203320
# early return if it is spent, because self.processing is not persisted
33213321
prevout = txin.prevout.to_str()
33223322
prev_txid, index = prevout.split(':')
3323-
if self.adb.db.get_spent_outpoint(prev_txid, int(index)):
3324-
return
3323+
if spender_txid := self.adb.db.get_spent_outpoint(prev_txid, int(index)):
3324+
tx_mined_status = self.adb.get_tx_height(spender_txid)
3325+
if tx_mined_status.height not in [TX_HEIGHT_LOCAL, TX_HEIGHT_FUTURE]:
3326+
return
33253327
self.processing.add(txin.prevout)
33263328
self.logger.info(f'add_sweep_info: {sweep_info.name} {sweep_info.txin.prevout.to_str()}')
33273329
self.batch_inputs[txin.prevout] = sweep_info
@@ -3338,10 +3340,21 @@ def to_pay_after(self, tx):
33383340
return [x for x in self.batch_payments if x not in tx.outputs()]
33393341

33403342
def to_sweep_after(self, tx):
3341-
if not tx:
3342-
return self.batch_inputs
3343-
tx_prevouts = set(txin.prevout for txin in tx.inputs())
3344-
return dict((k,v) for k,v in self.batch_inputs.items() if k not in tx_prevouts)
3343+
tx_prevouts = set(txin.prevout for txin in tx.inputs()) if tx else set()
3344+
result = []
3345+
for k,v in self.batch_inputs.items():
3346+
prevout = v.txin.prevout
3347+
prev_txid, index = prevout.to_str().split(':')
3348+
if not self.adb.db.get_transaction(prev_txid):
3349+
continue
3350+
if spender_txid := self.adb.db.get_spent_outpoint(prev_txid, int(index)):
3351+
tx_mined_status = self.adb.get_tx_height(spender_txid)
3352+
if tx_mined_status.height not in [TX_HEIGHT_LOCAL, TX_HEIGHT_FUTURE]:
3353+
continue
3354+
if prevout in tx_prevouts:
3355+
continue
3356+
result.append((k,v))
3357+
return dict(result)
33453358

33463359
def should_bump_fee(self, base_tx):
33473360
# fixme: since batch_txs is not persisted, we do not bump after a restart
@@ -3401,7 +3414,13 @@ async def manage_batch_payments(self):
34013414
base_tx = self.batch_txs[-1] if self.batch_txs else None
34023415
to_pay = self.to_pay_after(base_tx)
34033416
to_sweep = self.to_sweep_after(base_tx)
3404-
to_sweep_now = dict([(k,v) for k,v in to_sweep.items() if self.can_broadcast(v)[0] is True])
3417+
to_sweep_now = {}
3418+
for k, v in to_sweep.items():
3419+
can_broadcast, wanted_height = self.can_broadcast(v)
3420+
if can_broadcast:
3421+
to_sweep_now[k] = v
3422+
else:
3423+
self.add_future_tx(v, wanted_height)
34053424
if not to_pay and not to_sweep_now and not self.should_bump_fee(base_tx):
34063425
continue
34073426
try:
@@ -3505,6 +3524,38 @@ async def maybe_broadcast_legacy_htlc_txs(self):
35053524
self.adb.add_transaction(tx)
35063525
self.batch_inputs.pop(sweep_info.txin.prevout)
35073526

3527+
def add_future_tx(self, sweep_info, wanted_height):
3528+
""" add local tx to provide user feedback """
3529+
txin = copy.deepcopy(sweep_info.txin)
3530+
prevout = txin.prevout.to_str()
3531+
prev_txid, index = prevout.split(':')
3532+
if self.adb.db.get_spent_outpoint(prev_txid, int(index)):
3533+
return
3534+
name = sweep_info.name
3535+
prevout = txin.prevout.to_str()
3536+
new_tx = self.create_transaction(
3537+
inputs=[txin],
3538+
outputs=[],
3539+
password=None,
3540+
fee=0,
3541+
)
3542+
# we may have a tx with a different fee, in which case it will be replaced
3543+
try:
3544+
tx_was_added = self.adb.add_transaction(new_tx)#, is_new=(old_tx is None))
3545+
except Exception as e:
3546+
self.logger.info(f'could not add future tx: {name}. prevout: {prevout} {str(e)}')
3547+
tx_was_added = False
3548+
if tx_was_added:
3549+
self.logger.info(f'added future tx: {name}. prevout: {prevout}')
3550+
3551+
# set future tx regardless of tx_was_added, because it is not persisted
3552+
# (and wanted_height can change if input of CSV was not mined before)
3553+
self.adb.set_future_tx(new_tx.txid(), wanted_height=wanted_height)
3554+
if tx_was_added:
3555+
self.set_label(new_tx.txid(), name)
3556+
#if old_tx and old_tx.txid() != new_tx.txid():
3557+
# self.lnworker.wallet.set_label(old_tx.txid(), None)
3558+
util.trigger_callback('wallet_updated', self.lnworker.wallet)
35083559

35093560
class Simple_Wallet(Abstract_Wallet):
35103561
# wallet with a single keystore

0 commit comments

Comments
 (0)