Browse Source

lnchannel.get_close_opts: allow REQUEST_REMOTE_FCLOSE if WE_ARE_TOXIC

related https://github.com/spesmilo/electrum/issues/8770
master
SomberNight 2 years ago
parent
commit
df58dd1f25
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 11
      electrum/gui/qt/channels_list.py
  2. 4
      electrum/lnchannel.py
  3. 12
      electrum/lnpeer.py
  4. 8
      electrum/lnworker.py

11
electrum/gui/qt/channels_list.py

@ -119,9 +119,6 @@ class ChannelsList(MyTreeView):
def on_channel_closed(self, txid):
self.main_window.show_error('Channel closed' + '\n' + txid)
def on_request_sent(self, b):
self.main_window.show_message(_('Request sent'))
def on_failure(self, exc_info):
type_, e, tb = exc_info
traceback.print_tb(tb)
@ -137,7 +134,7 @@ class ChannelsList(MyTreeView):
on_success = self.on_channel_closed
def task():
return self.network.run_from_another_thread(coro)
WaitingDialog(self, 'please wait..', task, on_success, self.on_failure)
WaitingDialog(self, _('Please wait...'), task, on_success, self.on_failure)
def force_close(self, channel_id):
self.save_backup = True
@ -161,7 +158,7 @@ class ChannelsList(MyTreeView):
def task():
coro = self.lnworker.force_close_channel(channel_id)
return self.network.run_from_another_thread(coro)
WaitingDialog(self, 'please wait..', task, self.on_channel_closed, self.on_failure)
WaitingDialog(self, _('Please wait...'), task, self.on_channel_closed, self.on_failure)
def remove_channel(self, channel_id):
if self.main_window.question(_('Are you sure you want to delete this channel? This will purge associated transactions from your wallet history.')):
@ -191,7 +188,9 @@ class ChannelsList(MyTreeView):
def task():
coro = self.lnworker.request_force_close(channel_id)
return self.network.run_from_another_thread(coro)
WaitingDialog(self, 'please wait..', task, self.on_request_sent, self.on_failure)
def on_done(b):
self.main_window.show_message(_('Request scheduled'))
WaitingDialog(self, _('Please wait...'), task, on_done, self.on_failure)
def set_frozen(self, chan, *, for_sending, value):
if not self.lnworker.uses_trampoline() or self.lnworker.is_trampoline_peer(chan.node_id):

4
electrum/lnchannel.py

@ -1015,6 +1015,8 @@ class Channel(AbstractChannel):
def should_try_to_reestablish_peer(self) -> bool:
if self.peer_state != PeerState.DISCONNECTED:
return False
if self.should_request_force_close:
return True
return ChannelState.PREOPENING < self._state < ChannelState.CLOSING
def get_funding_address(self):
@ -1629,6 +1631,8 @@ class Channel(AbstractChannel):
if not self.has_unsettled_htlcs():
ret.append(ChanCloseOption.COOP_CLOSE)
ret.append(ChanCloseOption.REQUEST_REMOTE_FCLOSE)
if self.get_state() == ChannelState.WE_ARE_TOXIC:
ret.append(ChanCloseOption.REQUEST_REMOTE_FCLOSE)
if not self.is_closed() or self.get_state() == ChannelState.REQUESTED_FCLOSE:
ret.append(ChanCloseOption.LOCAL_FCLOSE)
assert not (self.get_state() == ChannelState.WE_ARE_TOXIC and ChanCloseOption.LOCAL_FCLOSE in ret), "local force-close unsafe if we are toxic"

12
electrum/lnpeer.py

@ -1113,6 +1113,7 @@ class Peer(Logger):
async def request_force_close(self, channel_id: bytes):
"""Try to trigger the remote peer to force-close."""
await self.initialized
self.logger.info(f"trying to get remote peer to force-close chan {channel_id.hex()}")
# First, we intentionally send a "channel_reestablish" msg with an old state.
# Many nodes (but not all) automatically force-close when seeing this.
latest_point = secret_to_pubkey(42) # we need a valid point (BOLT2)
@ -1270,6 +1271,12 @@ class Peer(Logger):
async def reestablish_channel(self, chan: Channel):
await self.initialized
chan_id = chan.channel_id
if chan.should_request_force_close:
if chan.get_state() != ChannelState.WE_ARE_TOXIC:
chan.set_state(ChannelState.REQUESTED_FCLOSE)
await self.request_force_close(chan_id)
chan.should_request_force_close = False
return
if chan.get_state() == ChannelState.WE_ARE_TOXIC:
# Depending on timing, the remote might not know we are behind.
# We should let them know, so that they force-close.
@ -1283,11 +1290,6 @@ class Peer(Logger):
# We should let them know:
self._send_channel_reestablish(chan)
return
if chan.should_request_force_close:
chan.set_state(ChannelState.REQUESTED_FCLOSE)
await self.request_force_close(chan_id)
chan.should_request_force_close = False
return
# if we get here, we will try to do a proper reestablish
if not (ChannelState.PREOPENING < chan.get_state() < ChannelState.FORCE_CLOSING):
raise Exception(f"unexpected {chan.get_state()=} for reestablish")

8
electrum/lnworker.py

@ -2904,9 +2904,8 @@ class LNWallet(LNWorker):
if self._can_retry_addr(peer, urgent=True):
await self._add_peer(peer.host, peer.port, peer.pubkey)
for chan in self.channels.values():
if chan.is_closed():
continue
# reestablish
# note: we delegate filtering out uninteresting chans to this:
if not chan.should_try_to_reestablish_peer():
continue
peer = self._peers.get(chan.node_id, None)
@ -2961,10 +2960,9 @@ class LNWallet(LNWorker):
if channel_id in self.channels:
chan = self.channels[channel_id]
peer = self._peers.get(chan.node_id)
if not peer:
raise Exception('Peer not found')
chan.should_request_force_close = True
peer.close_and_cleanup()
if peer:
peer.close_and_cleanup() # to force a reconnect
elif connect_str:
peer = await self.add_peer(connect_str)
await peer.request_force_close(channel_id)

Loading…
Cancel
Save