URI: 
       tfix channel closure: - add 'CLOSING' state - wait until channel has no inflight HTLC - end fee negocitation when both parties agree on the fee (previously code ended it only when the other party had broadcast) - broadcast the closing transaction - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit c0a1af2032117ba81e1eac4c1d67e51900b2f767
   DIR parent 5bc74772a24a2f911276f4a19fc0749241d4bff1
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Thu, 22 Nov 2018 16:18:28 +0100
       
       fix channel closure:
        - add 'CLOSING' state
        - wait until channel has no inflight HTLC
        - end fee negocitation when both parties agree on the fee
          (previously code ended it only when the other party had broadcast)
        - broadcast the closing transaction
       
       Diffstat:
         M electrum/lnbase.py                  |      37 +++++++++++++++++++------------
         M electrum/lnchan.py                  |      10 +++++-----
       
       2 files changed, 28 insertions(+), 19 deletions(-)
       ---
   DIR diff --git a/electrum/lnbase.py b/electrum/lnbase.py
       t@@ -1147,8 +1147,6 @@ class Peer(PrintError):
            @log_exceptions
            async def close_channel(self, chan_id: bytes):
                chan = self.channels[chan_id]
       -        if len(chan.htlcs(LOCAL, only_pending=True)) > 0:
       -            raise Exception('Can\'t co-operatively close channel with payments ongoing (pending HTLCs). Please wait, or force-close the channel.')
                self.shutdown_received[chan_id] = asyncio.Future()
                self.send_shutdown(chan)
                payload = await self.shutdown_received[chan_id]
       t@@ -1176,16 +1174,27 @@ class Peer(PrintError):
        
            @log_exceptions
            async def _shutdown(self, chan: Channel, payload):
       +        # set state so that we stop accepting HTLCs
       +        chan.set_state('CLOSING')
       +        while len(chan.htlcs(LOCAL, only_pending=True)) > 0:
       +            await asyncio.sleep(1)
       +        our_fee = chan.pending_local_fee()
                scriptpubkey = bfh(bitcoin.address_to_script(chan.sweep_address))
       -        signature, fee, txid = chan.make_closing_tx(scriptpubkey, payload['scriptpubkey'])
       -        self.send_message('closing_signed', channel_id=chan.channel_id, fee_satoshis=fee, signature=signature)
       -        while chan.get_state() != 'CLOSED':
       -            try:
       -                closing_signed = await asyncio.wait_for(self.closing_signed[chan.channel_id].get(), 1)
       -            except asyncio.TimeoutError:
       -                pass
       -            else:
       -                fee = int.from_bytes(closing_signed['fee_satoshis'], 'big')
       -                signature, _, txid = chan.make_closing_tx(scriptpubkey, payload['scriptpubkey'], fee_sat=fee)
       -                self.send_message('closing_signed', channel_id=chan.channel_id, fee_satoshis=fee, signature=signature)
       -        return txid
       +        # negociate fee
       +        while True:
       +            our_sig, closing_tx = chan.make_closing_tx(scriptpubkey, payload['scriptpubkey'], fee_sat=our_fee)
       +            self.send_message('closing_signed', channel_id=chan.channel_id, fee_satoshis=our_fee, signature=our_sig)
       +            cs_payload = await asyncio.wait_for(self.closing_signed[chan.channel_id].get(), 1)
       +            their_fee = int.from_bytes(cs_payload['fee_satoshis'], 'big')
       +            their_sig = cs_payload['signature']
       +            if our_fee == their_fee:
       +                break
       +            # TODO: negociate better
       +            our_fee = their_fee
       +        # add their signature
       +        i = chan.get_local_index()
       +        closing_tx.add_signature_to_txin(0, i, bh2u(our_sig))
       +        closing_tx.add_signature_to_txin(0, 1-i, bh2u(their_sig))
       +        # broadcast
       +        await self.network.broadcast_transaction(closing_tx)
       +        return closing_tx.txid()
   DIR diff --git a/electrum/lnchan.py b/electrum/lnchan.py
       t@@ -824,12 +824,12 @@ class Channel(PrintError):
                    ),
                    htlcs=htlcs)
        
       +    def get_local_index(self):
       +        return int(self.config[LOCAL].multisig_key.pubkey < self.config[REMOTE].multisig_key.pubkey)
       +
            def make_closing_tx(self, local_script: bytes, remote_script: bytes,
       -                        fee_sat: Optional[int]=None) -> Tuple[bytes, int, str]:
       +                        fee_sat: int) -> Tuple[bytes, int, str]:
                """ cooperative close """
       -        if fee_sat is None:
       -            fee_sat = self.pending_local_fee()
       -
                _, outputs = make_commitment_outputs({
                            LOCAL:  fee_sat * 1000 if     self.constraints.is_initiator else 0,
                            REMOTE: fee_sat * 1000 if not self.constraints.is_initiator else 0,
       t@@ -849,7 +849,7 @@ class Channel(PrintError):
        
                der_sig = bfh(closing_tx.sign_txin(0, self.config[LOCAL].multisig_key.privkey))
                sig = ecc.sig_string_from_der_sig(der_sig[:-1])
       -        return sig, fee_sat, closing_tx.txid()
       +        return sig, closing_tx
        
            def force_close_tx(self):
                # local_commitment always gives back the next expected local_commitment,