From b64fcfb9e3704eb63b168453612959b718aef110 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 26 Mar 2021 09:21:49 +0100 Subject: [PATCH] lnpeer: if close_channel times out, check unconfirmed_closing_txid before raising an exception --- electrum/lnchannel.py | 10 +++++----- electrum/lnpeer.py | 15 ++++++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py index 7a0c0269f..bb8a81b76 100644 --- a/electrum/lnchannel.py +++ b/electrum/lnchannel.py @@ -304,7 +304,7 @@ class AbstractChannel(Logger, ABC): # we must not trust the server with unconfirmed transactions, # because the state transition is irreversible. if the remote # force closed, we remain OPEN until the closing tx is confirmed - self.closing_detected = True + self.unconfirmed_closing_txid = closing_txid if self.lnworker: util.trigger_callback('channel', self.lnworker.wallet, self) @@ -339,7 +339,7 @@ class AbstractChannel(Logger, ABC): def get_state_for_GUI(self) -> str: cs = self.get_state() - if cs == ChannelState.OPEN and self.closing_detected: + if cs == ChannelState.OPEN and self.unconfirmed_closing_txid: return 'FORCE-CLOSING' return cs.name @@ -419,7 +419,7 @@ class ChannelBackup(AbstractChannel): self.config = {} if self.is_imported: self.init_config(cb) - self.closing_detected = False # not a state, only for GUI + self.unconfirmed_closing_txid = None # not a state, only for GUI def init_config(self, cb): self.config[LOCAL] = LocalConfig.from_seed( @@ -553,7 +553,7 @@ class Channel(AbstractChannel): self._receive_fail_reasons = {} # type: Dict[int, (bytes, OnionRoutingFailure)] self._ignore_max_htlc_value = False # used in tests self.should_request_force_close = False - self.closing_detected = False # not a state, only for GUI + self.unconfirmed_closing_txid = None # not a state, only for GUI def has_onchain_backup(self): return self.storage.get('has_onchain_backup', False) @@ -735,7 +735,7 @@ class Channel(AbstractChannel): def get_state_for_GUI(self): cs_name = super().get_state_for_GUI() - if self.is_closed() or self.closing_detected: + if self.is_closed() or self.unconfirmed_closing_txid: return cs_name ps = self.peer_state if ps != PeerState.GOOD: diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index c7f72ef8b..d0d3c4244 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -1692,19 +1692,23 @@ class Peer(Logger): self.shutdown_received[chan_id] = asyncio.Future() await self.send_shutdown(chan) payload = await self.shutdown_received[chan_id] - txid = await self._shutdown(chan, payload, is_local=True) - self.logger.info(f'({chan.get_id_for_log()}) Channel closed {txid}') + try: + txid = await self._shutdown(chan, payload, is_local=True) + self.logger.info(f'({chan.get_id_for_log()}) Channel closed {txid}') + except asyncio.TimeoutError: + txid = chan.unconfirmed_closing_txid + self.logger.info(f'({chan.get_id_for_log()}) did not send closing_signed, {txid}') + if txid is None: + raise Exception('The remote peer did not send their final signature. The channel may not have been be closed') return txid async def on_shutdown(self, chan: Channel, payload): their_scriptpubkey = payload['scriptpubkey'] their_upfront_scriptpubkey = chan.config[REMOTE].upfront_shutdown_script - # BOLT-02 check if they use the upfront shutdown script they advertized if their_upfront_scriptpubkey: if not (their_scriptpubkey == their_upfront_scriptpubkey): raise UpfrontShutdownScriptViolation("remote didn't use upfront shutdown script it commited to in channel opening") - # BOLT-02 restrict the scriptpubkey to some templates: if not (match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_WITNESS_V0) or match_script_against_template(their_scriptpubkey, transaction.SCRIPTPUBKEY_TEMPLATE_P2SH) @@ -1731,13 +1735,11 @@ class Peer(Logger): async def send_shutdown(self, chan: Channel): if not self.can_send_shutdown(chan): raise Exception('cannot send shutdown') - if chan.config[LOCAL].upfront_shutdown_script: scriptpubkey = chan.config[LOCAL].upfront_shutdown_script else: scriptpubkey = bfh(bitcoin.address_to_script(chan.sweep_address)) assert scriptpubkey - # wait until no more pending updates (bolt2) chan.set_can_send_ctx_updates(False) while chan.has_pending_changes(REMOTE): @@ -1761,7 +1763,6 @@ class Peer(Logger): else: our_scriptpubkey = bfh(bitcoin.address_to_script(chan.sweep_address)) assert our_scriptpubkey - # estimate fee of closing tx our_sig, closing_tx = chan.make_closing_tx(our_scriptpubkey, their_scriptpubkey, fee_sat=0) fee_rate = self.network.config.fee_per_kb()