@@ -80,6 +80,13 @@ def add_callback(self, address, callback):
80
80
async def on_event_blockchain_updated (self , * args ):
81
81
await self .trigger_callbacks ()
82
82
83
+ @event_listener
84
+ async def on_event_wallet_updated (self , wallet ):
85
+ # called if we add local tx
86
+ if wallet .adb != self .adb :
87
+ return
88
+ await self .trigger_callbacks ()
89
+
83
90
@event_listener
84
91
async def on_event_adb_added_verified_tx (self , adb , tx_hash ):
85
92
if adb != self .adb :
@@ -146,6 +153,10 @@ def get_spender(self, outpoint) -> str:
146
153
"""
147
154
prev_txid , index = outpoint .split (':' )
148
155
spender_txid = self .adb .db .get_spent_outpoint (prev_txid , int (index ))
156
+ # discard local spenders
157
+ tx_mined_status = self .adb .get_tx_height (spender_txid )
158
+ if tx_mined_status .height in [TX_HEIGHT_LOCAL , TX_HEIGHT_FUTURE ]:
159
+ spender_txid = None
149
160
if not spender_txid :
150
161
return
151
162
spender_tx = self .adb .get_transaction (spender_txid )
@@ -241,9 +252,8 @@ async def sweep_commitment_transaction(self, funding_outpoint, closing_tx) -> bo
241
252
return False
242
253
# detect who closed and get information about how to claim outputs
243
254
sweep_info_dict = chan .sweep_ctx (closing_tx )
244
- self .logger .info (f"do_breach_remedy: { [x .name for x in sweep_info_dict .values ()]} " )
255
+ # self.logger.info(f"do_breach_remedy: {[x.name for x in sweep_info_dict.values()]}")
245
256
keep_watching = False if sweep_info_dict else not self .is_deeply_mined (closing_tx .txid ())
246
-
247
257
# create and broadcast transactions
248
258
for prevout , sweep_info in sweep_info_dict .items ():
249
259
if self .is_dust (sweep_info ):
@@ -253,7 +263,7 @@ async def sweep_commitment_transaction(self, funding_outpoint, closing_tx) -> bo
253
263
self .lnworker .wallet .set_default_label (prevout , name )
254
264
if not self .adb .get_transaction (prev_txid ):
255
265
# do not keep watching if prevout does not exist
256
- self .logger .info (f'prevout does not exist for { name } : { prev_txid } ' )
266
+ self .logger .info (f'prevout does not exist for { name } : { prevout } ' )
257
267
continue
258
268
spender_txid = self .get_spender (prevout )
259
269
spender_tx = self .adb .get_transaction (spender_txid ) if spender_txid else None
@@ -267,7 +277,7 @@ async def sweep_commitment_transaction(self, funding_outpoint, closing_tx) -> bo
267
277
keep_watching |= not self .is_deeply_mined (htlc_tx_spender )
268
278
else :
269
279
keep_watching = True
270
- await self .maybe_redeem (prevout2 , htlc_sweep_info , name )
280
+ await self .maybe_redeem (htlc_sweep_info )
271
281
# extract preimage
272
282
keep_watching |= not self .is_deeply_mined (spender_txid )
273
283
txin_idx = spender_tx .get_input_idx_that_spent_prevout (TxOutpoint .from_str (prevout ))
@@ -276,111 +286,31 @@ async def sweep_commitment_transaction(self, funding_outpoint, closing_tx) -> bo
276
286
chan .extract_preimage_from_htlc_txin (spender_txin )
277
287
else :
278
288
keep_watching = True
279
- # broadcast or maybe update our own tx
280
- await self .maybe_redeem (prevout , sweep_info , name )
281
-
289
+ # broadcast or maybe update our own tx
290
+ await self .maybe_redeem (sweep_info )
282
291
return keep_watching
283
292
284
- def get_redeem_tx (self , prevout : str , sweep_info : 'SweepInfo' , name : str ):
285
- # check if redeem tx needs to be updated
286
- # if it is in the mempool, we need to check fee rise
287
- txid = self .get_spender (prevout )
288
- old_tx = self .adb .get_transaction (txid )
289
- assert old_tx is not None or txid is None
290
- tx_depth = self .get_tx_mined_depth (txid ) if txid else None
291
- if txid and tx_depth not in [TxMinedDepth .FREE , TxMinedDepth .MEMPOOL ]:
292
- assert old_tx is not None
293
- return old_tx , None
294
- # fixme: deepcopy is needed because tx.serialize() is destructive
295
- inputs = [copy .deepcopy (sweep_info .txin )]
296
- outputs = [sweep_info .txout ] if sweep_info .txout else []
297
- if sweep_info .name == 'first-stage-htlc' :
298
- new_tx = PartialTransaction .from_io (inputs , outputs , locktime = sweep_info .cltv_abs , version = 2 )
299
- self .lnworker .wallet .sign_transaction (new_tx , password = None , ignore_warnings = True )
300
- else :
301
- # password is needed for 1st stage htlc tx with anchors because we add inputs
302
- password = self .lnworker .wallet .get_unlocked_password ()
303
- new_tx = self .lnworker .wallet .create_transaction (
304
- fee_policy = self .fee_policy ,
305
- inputs = inputs ,
306
- outputs = outputs ,
307
- password = password ,
308
- locktime = sweep_info .cltv_abs ,
309
- BIP69_sort = False ,
310
- )
311
- if new_tx is None :
312
- self .logger .info (f'{ name } could not claim output: { prevout } , dust' )
313
- assert old_tx is not None
314
- return old_tx , None
315
- if txid is None :
316
- return None , new_tx
317
- elif tx_depth == TxMinedDepth .MEMPOOL :
318
- delta = new_tx .get_fee () - self .adb .get_tx_fee (txid )
319
- if delta > 1 :
320
- self .logger .info (f'increasing fee of mempool tx { name } : { prevout } ' )
321
- return old_tx , new_tx
322
- else :
323
- assert old_tx is not None
324
- return old_tx , None
325
- elif tx_depth == TxMinedDepth .FREE :
326
- # return new tx, even if it is equal to old_tx,
327
- # because we need to test if it can be broadcast
328
- return old_tx , new_tx
329
- else :
330
- assert old_tx is not None
331
- return old_tx , None
332
-
333
- async def maybe_redeem (self , prevout , sweep_info : 'SweepInfo' , name : str ) -> None :
334
- old_tx , new_tx = self .get_redeem_tx (prevout , sweep_info , name )
335
- if new_tx is None :
336
- return
337
- prev_txid , prev_index = prevout .split (':' )
338
- can_broadcast = True
339
- local_height = self .network .get_local_height ()
340
- if sweep_info .cltv_abs :
341
- wanted_height = sweep_info .cltv_abs
342
- if wanted_height - local_height > 0 :
343
- can_broadcast = False
344
- # self.logger.debug(f"pending redeem for {prevout}. waiting for {name}: CLTV ({local_height=}, {wanted_height=})")
345
- if sweep_info .csv_delay :
346
- prev_height = self .adb .get_tx_height (prev_txid )
347
- if prev_height .height > 0 :
348
- wanted_height = prev_height .height + sweep_info .csv_delay - 1
349
- else :
350
- wanted_height = local_height + sweep_info .csv_delay
351
- if wanted_height - local_height > 0 :
352
- can_broadcast = False
353
- # self.logger.debug(
354
- # f"pending redeem for {prevout}. waiting for {name}: CSV "
355
- # f"({local_height=}, {wanted_height=}, {prev_height.height=}, {sweep_info.csv_delay=})")
293
+ async def maybe_redeem (self , sweep_info : 'SweepInfo' ) -> None :
356
294
if not (sweep_info .cltv_abs or sweep_info .csv_delay ):
357
295
# used to control settling of htlcs onchain for testing purposes
358
296
# careful, this prevents revocation as well
359
297
if not self .lnworker .enable_htlc_settle_onchain :
360
298
return
361
- if can_broadcast :
362
- self .logger .info (f'we can broadcast: { name } ' )
363
- if await self .network .try_broadcasting (new_tx , name ):
364
- tx_was_added = self .adb .add_transaction (new_tx , is_new = (old_tx is None ))
365
- else :
366
- tx_was_added = False
367
- else :
368
- # we may have a tx with a different fee, in which case it will be replaced
369
- if not old_tx or (old_tx and old_tx .txid () != new_tx .txid ()):
370
- try :
371
- tx_was_added = self .adb .add_transaction (new_tx , is_new = (old_tx is None ))
372
- except Exception as e :
373
- self .logger .info (f'could not add future tx: { name } . prevout: { prevout } { str (e )} ' )
374
- tx_was_added = False
375
- if tx_was_added :
376
- self .logger .info (f'added redeem tx: { name } . prevout: { prevout } ' )
377
- else :
378
- tx_was_added = False
379
- # set future tx regardless of tx_was_added, because it is not persisted
380
- # (and wanted_height can change if input of CSV was not mined before)
381
- self .adb .set_future_tx (new_tx .txid (), wanted_height = wanted_height )
382
- if tx_was_added :
383
- self .lnworker .wallet .set_label (new_tx .txid (), name )
384
- if old_tx and old_tx .txid () != new_tx .txid ():
385
- self .lnworker .wallet .set_label (old_tx .txid (), None )
386
- util .trigger_callback ('wallet_updated' , self .lnworker .wallet )
299
+ # pre-anchor htlc txs cannot be batched
300
+ if sweep_info .name == 'first-stage-htlc' :
301
+ await self ._maybe_redeem_legacy_htlc_tx (sweep_info )
302
+ return
303
+ # if it cannot be batched, we still send it to txbatcher, so that the fee will be bumped
304
+ batch_key = 'lnwatcher' if sweep_info .can_be_batched else sweep_info .txin .prevout .to_str ()
305
+ self .lnworker .wallet .txbatcher .add_sweep_input (batch_key , sweep_info , self .fee_policy )
306
+
307
+ async def _maybe_redeem_legacy_htlc_tx (self , sweep_info ):
308
+ local_height = self .network .get_local_height ()
309
+ wanted_height = sweep_info .cltv_abs
310
+ if wanted_height - local_height > 0 :
311
+ return
312
+ self .logger .info ('legacy first-stage htlc tx' )
313
+ tx = PartialTransaction .from_io ([sweep_info .txin ], [sweep_info .txout ], locktime = sweep_info .cltv_abs , version = 2 )
314
+ self .lnworker .wallet .sign_transaction (tx , password = None , ignore_warnings = True )
315
+ if await self .network .try_broadcasting (tx , sweep_info .name ):
316
+ self .lnworker .wallet .adb .add_transaction (tx )
0 commit comments