URI: 
       tshutdown: - resend shutdown on reestablish - wait until no more pending updates before sending shutdown - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 1c5dc7929838fbb7307faeac6506c95821002049
   DIR parent 9b97469598c040f5ec6a0c8fc7d59888d47b3b02
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Wed, 26 Feb 2020 09:04:54 +0100
       
       shutdown:
        - resend shutdown on reestablish
        - wait until no more pending updates before sending shutdown
       
       Diffstat:
         M electrum/lnchannel.py               |       3 ++-
         M electrum/lnpeer.py                  |      22 ++++++++++++++--------
         M electrum/lnworker.py                |       2 +-
       
       3 files changed, 17 insertions(+), 10 deletions(-)
       ---
   DIR diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py
       t@@ -69,7 +69,7 @@ class channel_states(IntEnum):
            FUNDED          = 2 # Funding tx was mined (requires min_depth and tx verification)
            OPEN            = 3 # both parties have sent funding_locked
            FORCE_CLOSING   = 4 # force-close tx has been broadcast
       -    CLOSING         = 5 # closing negotiation
       +    CLOSING         = 5 # shutdown has been sent.
            CLOSED          = 6 # funding txo has been spent
            REDEEMED        = 7 # we can stop watching
        
       t@@ -94,6 +94,7 @@ state_transitions = [
            (cs.OPENING, cs.CLOSED),
            (cs.FUNDED, cs.CLOSED),
            (cs.OPEN, cs.CLOSED),
       +    (cs.CLOSING, cs.CLOSING), # if we reestablish
            (cs.CLOSING, cs.CLOSED),
            (cs.FORCE_CLOSING, cs.CLOSED),
            (cs.CLOSED, cs.REDEEMED),
   DIR diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py
       t@@ -870,6 +870,8 @@ class Peer(Logger):
                if chan.config[LOCAL].funding_locked_received and chan.short_channel_id:
                    self.mark_open(chan)
                self.network.trigger_callback('channel', chan)
       +        if chan.get_state() == channel_states.CLOSING:
       +            await self.send_shutdown(chan)
        
            def send_funding_locked(self, chan: Channel):
                channel_id = chan.channel_id
       t@@ -1009,15 +1011,16 @@ class Peer(Logger):
            def maybe_send_commitment(self, chan: Channel):
                # REMOTE should revoke first before we can sign a new ctx
                if chan.hm.is_revack_pending(REMOTE):
       -            return
       +            return False
                # if there are no changes, we will not (and must not) send a new commitment
                next_htlcs, latest_htlcs = chan.hm.get_htlcs_in_next_ctx(REMOTE), chan.hm.get_htlcs_in_latest_ctx(REMOTE)
                if next_htlcs == latest_htlcs and chan.get_next_feerate(REMOTE) == chan.get_latest_feerate(REMOTE):
       -            return
       +            return False
                self.logger.info(f'send_commitment. chan {chan.short_channel_id}. ctn: {chan.get_next_ctn(REMOTE)}. '
                                 f'old number htlcs: {len(latest_htlcs)}, new number htlcs: {len(next_htlcs)}')
                sig_64, htlc_sigs = chan.sign_next_commitment()
                self.send_message("commitment_signed", channel_id=chan.channel_id, signature=sig_64, num_htlcs=len(htlc_sigs), htlc_signature=b"".join(htlc_sigs))
       +        return True
        
            async def await_remote(self, chan: Channel, ctn: int):
                """Wait until remote 'ctn' gets revoked."""
       t@@ -1349,8 +1352,7 @@ class Peer(Logger):
            @log_exceptions
            async def close_channel(self, chan_id: bytes):
                chan = self.channels[chan_id]
       -        self.shutdown_received[chan_id] = asyncio.Future()
       -        self.send_shutdown(chan)
       +        await self.send_shutdown(chan)
                payload = await self.shutdown_received[chan_id]
                txid = await self._shutdown(chan, payload, True)
                self.logger.info(f'({chan.get_id_for_log()}) Channel closed {txid}')
       t@@ -1369,18 +1371,22 @@ class Peer(Logger):
                    self.shutdown_received[chan_id].set_result(payload)
                else:
                    chan = self.channels[chan_id]
       -            self.send_shutdown(chan)
       +            await self.send_shutdown(chan)
                    txid = await self._shutdown(chan, payload, False)
                    self.logger.info(f'({chan.get_id_for_log()}) Channel closed by remote peer {txid}')
        
       -    def send_shutdown(self, chan: Channel):
       +    async def send_shutdown(self, chan: Channel):
                scriptpubkey = bfh(bitcoin.address_to_script(chan.sweep_address))
       +        # wait until no more pending updates (bolt2)
       +        # TODO: stop sending updates during that time
       +        ctn = chan.get_latest_ctn(REMOTE)
       +        if self.maybe_send_commitment(chan):
       +            await self.await_remote(chan, ctn)
                self.send_message('shutdown', channel_id=chan.channel_id, len=len(scriptpubkey), scriptpubkey=scriptpubkey)
       +        chan.set_state(channel_states.CLOSING)
        
            @log_exceptions
            async def _shutdown(self, chan: Channel, payload, is_local):
       -        # set state so that we stop accepting HTLCs
       -        chan.set_state(channel_states.CLOSING)
                # wait until no HTLCs remain in either commitment transaction
                while len(chan.hm.htlcs(LOCAL)) + len(chan.hm.htlcs(REMOTE)) > 0:
                    self.logger.info(f'(chan: {chan.short_channel_id}) waiting for htlcs to settle...')
   DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py
       t@@ -1276,7 +1276,7 @@ class LNWallet(LNWorker):
                    with self.lock:
                        channels = list(self.channels.values())
                    for chan in channels:
       -                if chan.is_closed() or chan.is_closing():
       +                if chan.is_closed():
                            continue
                        if constants.net is not constants.BitcoinRegtest:
                            chan_feerate = chan.get_latest_feerate(LOCAL)