55
55
ShortChannelID , map_htlcs_to_ctx_output_idxs ,
56
56
fee_for_htlc_output , offered_htlc_trim_threshold_sat ,
57
57
received_htlc_trim_threshold_sat , make_commitment_output_to_remote_address , FIXED_ANCHOR_SAT ,
58
- ChannelType , LNProtocolWarning )
58
+ ChannelType , LNProtocolWarning , ZEROCONF_TIMEOUT )
59
59
from .lnsweep import sweep_our_ctx , sweep_their_ctx
60
60
from .lnsweep import sweep_their_htlctx_justice , sweep_our_htlctx , SweepInfo
61
61
from .lnsweep import sweep_their_ctx_to_remote_backup
@@ -345,7 +345,11 @@ def update_onchain_state(self, *, funding_txid: str, funding_height: TxMinedInfo
345
345
def update_unfunded_state (self ) -> None :
346
346
self .delete_funding_height ()
347
347
self .delete_closing_height ()
348
- if self .get_state () in [ChannelState .PREOPENING , ChannelState .OPENING , ChannelState .FORCE_CLOSING ] and self .lnworker :
348
+ if not self .lnworker :
349
+ return
350
+ chan_age = now () - self .storage .get ('init_timestamp' , 0 )
351
+ state = self .get_state ()
352
+ if state in [ChannelState .PREOPENING , ChannelState .OPENING , ChannelState .FORCE_CLOSING ]:
349
353
if self .is_initiator ():
350
354
# set channel state to REDEEMED so that it can be removed manually
351
355
# to protect ourselves against a server lying by omission,
@@ -365,15 +369,39 @@ def update_unfunded_state(self) -> None:
365
369
self .set_state (ChannelState .REDEEMED )
366
370
break
367
371
else :
368
- if self . lnworker and ( now () - self . storage . get ( 'init_timestamp' , 0 ) > CHANNEL_OPENING_TIMEOUT ) :
372
+ if chan_age > CHANNEL_OPENING_TIMEOUT :
369
373
self .lnworker .remove_channel (self .channel_id )
374
+ elif self .is_zeroconf () and state in [ChannelState .OPEN , ChannelState .CLOSING , ChannelState .FORCE_CLOSING ]:
375
+ assert self .storage .get ('init_timestamp' ) is not None , "init_timestamp not set for zeroconf channel"
376
+ # handling zeroconf channels with no funding tx, can happen if broadcasting fails on LSP side
377
+ # or if the LSP did double spent the funding tx/never published it intentionally
378
+ # only remove a timed out OPEN channel if we are connected to the network to prevent removing it if we went
379
+ # offline before seeing the funding tx
380
+ if state != ChannelState .OPEN or chan_age > ZEROCONF_TIMEOUT and self .lnworker .network .is_connected ():
381
+ # we delete the channel if its in closing state (either initiated manually by client or by LSP on failure)
382
+ # or if the channel is not seeing any funding tx after 10 minutes to prevent further usage (limit damage)
383
+ self .set_state (ChannelState .REDEEMED , force = True )
384
+ local_balance_sat = int (self .balance (LOCAL ) // 1000 )
385
+ if local_balance_sat > 0 :
386
+ self .logger .warning (
387
+ f"we may have been scammed out of { local_balance_sat } sat by our "
388
+ f"JIT provider: { self .lnworker .config .ZEROCONF_TRUSTED_NODE } or he didn't use our preimage" )
389
+ self .lnworker .config .ZEROCONF_TRUSTED_NODE = ''
390
+ self .lnworker .lnwatcher .unwatch_channel (self .get_funding_address (), self .funding_outpoint .to_str ())
391
+ # remove remaining local transactions from the wallet, this will also remove child transactions (closing tx)
392
+ self .lnworker .lnwatcher .adb .remove_transaction (self .funding_outpoint .txid )
393
+ self .lnworker .remove_channel (self .channel_id )
370
394
371
395
def update_funded_state (self , * , funding_txid : str , funding_height : TxMinedInfo ) -> None :
372
396
self .save_funding_height (txid = funding_txid , height = funding_height .height , timestamp = funding_height .timestamp )
373
397
self .delete_closing_height ()
374
398
if funding_height .conf > 0 :
375
399
self .set_short_channel_id (ShortChannelID .from_components (
376
400
funding_height .height , funding_height .txpos , self .funding_outpoint .output_index ))
401
+ if self .is_zeroconf ():
402
+ # remove zeroconf flag as we are now confirmed, this is to prevent an electrum server causing
403
+ # us to remove a channel later in update_unfunded_state by omitting its funding tx
404
+ self .remove_zeroconf_flag ()
377
405
if self .get_state () == ChannelState .OPENING :
378
406
if self .is_funding_tx_mined (funding_height ):
379
407
self .set_state (ChannelState .FUNDED )
@@ -411,6 +439,14 @@ def is_initiator(self) -> bool:
411
439
def is_public (self ) -> bool :
412
440
pass
413
441
442
+ @abstractmethod
443
+ def is_zeroconf (self ) -> bool :
444
+ pass
445
+
446
+ @abstractmethod
447
+ def remove_zeroconf_flag (self ) -> None :
448
+ pass
449
+
414
450
@abstractmethod
415
451
def is_funding_tx_mined (self , funding_height : TxMinedInfo ) -> bool :
416
452
pass
@@ -664,6 +700,12 @@ def get_sweep_address(self) -> str:
664
700
def has_anchors (self ) -> Optional [bool ]:
665
701
return None
666
702
703
+ def is_zeroconf (self ) -> bool :
704
+ return False
705
+
706
+ def remove_zeroconf_flag (self ) -> None :
707
+ pass
708
+
667
709
def get_local_pubkey (self ) -> bytes :
668
710
cb = self .cb
669
711
assert isinstance (cb , ChannelBackupStorage )
@@ -906,6 +948,12 @@ def is_zeroconf(self) -> bool:
906
948
channel_type = ChannelType (self .storage .get ('channel_type' ))
907
949
return bool (channel_type & ChannelType .OPTION_ZEROCONF )
908
950
951
+ def remove_zeroconf_flag (self ) -> None :
952
+ if not self .is_zeroconf ():
953
+ return
954
+ channel_type = ChannelType (self .storage .get ('channel_type' ))
955
+ self .storage ['channel_type' ] = channel_type & ~ ChannelType .OPTION_ZEROCONF
956
+
909
957
def get_sweep_address (self ) -> str :
910
958
# TODO: in case of unilateral close with pending HTLCs, this address will be reused
911
959
if self .has_anchors ():
0 commit comments