Browse Source

shutdown:

- resend shutdown on reestablish
 - wait until no more pending updates before sending shutdown
master
ThomasV 6 years ago
parent
commit
1c5dc79298
  1. 3
      electrum/lnchannel.py
  2. 22
      electrum/lnpeer.py
  3. 2
      electrum/lnworker.py

3
electrum/lnchannel.py

@ -69,7 +69,7 @@ class channel_states(IntEnum):
FUNDED = 2 # Funding tx was mined (requires min_depth and tx verification) FUNDED = 2 # Funding tx was mined (requires min_depth and tx verification)
OPEN = 3 # both parties have sent funding_locked OPEN = 3 # both parties have sent funding_locked
FORCE_CLOSING = 4 # force-close tx has been broadcast 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 CLOSED = 6 # funding txo has been spent
REDEEMED = 7 # we can stop watching REDEEMED = 7 # we can stop watching
@ -94,6 +94,7 @@ state_transitions = [
(cs.OPENING, cs.CLOSED), (cs.OPENING, cs.CLOSED),
(cs.FUNDED, cs.CLOSED), (cs.FUNDED, cs.CLOSED),
(cs.OPEN, cs.CLOSED), (cs.OPEN, cs.CLOSED),
(cs.CLOSING, cs.CLOSING), # if we reestablish
(cs.CLOSING, cs.CLOSED), (cs.CLOSING, cs.CLOSED),
(cs.FORCE_CLOSING, cs.CLOSED), (cs.FORCE_CLOSING, cs.CLOSED),
(cs.CLOSED, cs.REDEEMED), (cs.CLOSED, cs.REDEEMED),

22
electrum/lnpeer.py

@ -870,6 +870,8 @@ class Peer(Logger):
if chan.config[LOCAL].funding_locked_received and chan.short_channel_id: if chan.config[LOCAL].funding_locked_received and chan.short_channel_id:
self.mark_open(chan) self.mark_open(chan)
self.network.trigger_callback('channel', 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): def send_funding_locked(self, chan: Channel):
channel_id = chan.channel_id channel_id = chan.channel_id
@ -1009,15 +1011,16 @@ class Peer(Logger):
def maybe_send_commitment(self, chan: Channel): def maybe_send_commitment(self, chan: Channel):
# REMOTE should revoke first before we can sign a new ctx # REMOTE should revoke first before we can sign a new ctx
if chan.hm.is_revack_pending(REMOTE): 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 # 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) 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): 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)}. ' 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)}') f'old number htlcs: {len(latest_htlcs)}, new number htlcs: {len(next_htlcs)}')
sig_64, htlc_sigs = chan.sign_next_commitment() 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)) 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): async def await_remote(self, chan: Channel, ctn: int):
"""Wait until remote 'ctn' gets revoked.""" """Wait until remote 'ctn' gets revoked."""
@ -1349,8 +1352,7 @@ class Peer(Logger):
@log_exceptions @log_exceptions
async def close_channel(self, chan_id: bytes): async def close_channel(self, chan_id: bytes):
chan = self.channels[chan_id] chan = self.channels[chan_id]
self.shutdown_received[chan_id] = asyncio.Future() await self.send_shutdown(chan)
self.send_shutdown(chan)
payload = await self.shutdown_received[chan_id] payload = await self.shutdown_received[chan_id]
txid = await self._shutdown(chan, payload, True) txid = await self._shutdown(chan, payload, True)
self.logger.info(f'({chan.get_id_for_log()}) Channel closed {txid}') self.logger.info(f'({chan.get_id_for_log()}) Channel closed {txid}')
@ -1369,18 +1371,22 @@ class Peer(Logger):
self.shutdown_received[chan_id].set_result(payload) self.shutdown_received[chan_id].set_result(payload)
else: else:
chan = self.channels[chan_id] chan = self.channels[chan_id]
self.send_shutdown(chan) await self.send_shutdown(chan)
txid = await self._shutdown(chan, payload, False) txid = await self._shutdown(chan, payload, False)
self.logger.info(f'({chan.get_id_for_log()}) Channel closed by remote peer {txid}') 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)) 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) self.send_message('shutdown', channel_id=chan.channel_id, len=len(scriptpubkey), scriptpubkey=scriptpubkey)
chan.set_state(channel_states.CLOSING)
@log_exceptions @log_exceptions
async def _shutdown(self, chan: Channel, payload, is_local): 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 # wait until no HTLCs remain in either commitment transaction
while len(chan.hm.htlcs(LOCAL)) + len(chan.hm.htlcs(REMOTE)) > 0: 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...') self.logger.info(f'(chan: {chan.short_channel_id}) waiting for htlcs to settle...')

2
electrum/lnworker.py

@ -1276,7 +1276,7 @@ class LNWallet(LNWorker):
with self.lock: with self.lock:
channels = list(self.channels.values()) channels = list(self.channels.values())
for chan in channels: for chan in channels:
if chan.is_closed() or chan.is_closing(): if chan.is_closed():
continue continue
if constants.net is not constants.BitcoinRegtest: if constants.net is not constants.BitcoinRegtest:
chan_feerate = chan.get_latest_feerate(LOCAL) chan_feerate = chan.get_latest_feerate(LOCAL)

Loading…
Cancel
Save