Browse Source

do not include 'force_closing' in channel states, because it is not part of the peer protocol

master
ThomasV 7 years ago
parent
commit
3c0df28c98
  1. 16
      electrum/lnchannel.py
  2. 2
      electrum/lnpeer.py
  3. 12
      electrum/lnworker.py
  4. 2
      electrum/tests/test_lnpeer.py

16
electrum/lnchannel.py

@ -145,6 +145,7 @@ class Channel(Logger):
self.short_channel_id = bfh(state["short_channel_id"]) if type(state["short_channel_id"]) not in (bytes, type(None)) else state["short_channel_id"] self.short_channel_id = bfh(state["short_channel_id"]) if type(state["short_channel_id"]) not in (bytes, type(None)) else state["short_channel_id"]
self.short_channel_id_predicted = self.short_channel_id self.short_channel_id_predicted = self.short_channel_id
self.onion_keys = str_bytes_dict_from_save(state.get('onion_keys', {})) self.onion_keys = str_bytes_dict_from_save(state.get('onion_keys', {}))
self.force_closed = state.get('force_closed')
# FIXME this is a tx serialised in the custom electrum partial tx format. # FIXME this is a tx serialised in the custom electrum partial tx format.
# we should not persist txns in this format. we should persist htlcs, and be able to derive # we should not persist txns in this format. we should persist htlcs, and be able to derive
@ -162,11 +163,7 @@ class Channel(Logger):
self._is_funding_txo_spent = None # "don't know" self._is_funding_txo_spent = None # "don't know"
self._state = None self._state = None
if state.get('force_closed', False):
self.set_state('FORCE_CLOSING')
else:
self.set_state('DISCONNECTED') self.set_state('DISCONNECTED')
self.lnwatcher = None self.lnwatcher = None
self.local_commitment = None self.local_commitment = None
@ -203,18 +200,21 @@ class Channel(Logger):
self.config[LOCAL] = self.config[LOCAL]._replace(ctn=0, current_commitment_signature=remote_sig) self.config[LOCAL] = self.config[LOCAL]._replace(ctn=0, current_commitment_signature=remote_sig)
self.set_state('OPENING') self.set_state('OPENING')
def set_force_closed(self):
self.force_closed = True
def set_state(self, state: str): def set_state(self, state: str):
if self._state == 'FORCE_CLOSING':
assert state == 'FORCE_CLOSING', 'new state was not FORCE_CLOSING: ' + state
self._state = state self._state = state
def get_state(self): def get_state(self):
return self._state return self._state
def is_closed(self): def is_closed(self):
return self.get_state() in ['CLOSED', 'FORCE_CLOSING'] return self.force_closed or self.get_state() in ['CLOSED', 'CLOSING']
def _check_can_pay(self, amount_msat: int) -> None: def _check_can_pay(self, amount_msat: int) -> None:
if self.is_closed():
raise PaymentFailure('Channel closed')
if self.get_state() != 'OPEN': if self.get_state() != 'OPEN':
raise PaymentFailure('Channel not open') raise PaymentFailure('Channel not open')
if self.available_to_spend(LOCAL) < amount_msat: if self.available_to_spend(LOCAL) < amount_msat:
@ -672,7 +672,7 @@ class Channel(Logger):
"remote_commitment_to_be_revoked": str(self.remote_commitment_to_be_revoked), "remote_commitment_to_be_revoked": str(self.remote_commitment_to_be_revoked),
"log": self.hm.to_save(), "log": self.hm.to_save(),
"onion_keys": str_bytes_dict_to_save(self.onion_keys), "onion_keys": str_bytes_dict_to_save(self.onion_keys),
"force_closed": self.get_state() == 'FORCE_CLOSING', "force_closed": self.force_closed,
} }
return to_save return to_save

2
electrum/lnpeer.py

@ -1342,7 +1342,7 @@ class Peer(Logger):
while True: while True:
our_sig, closing_tx = chan.make_closing_tx(scriptpubkey, payload['scriptpubkey'], fee_sat=our_fee) 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) 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) cs_payload = await asyncio.wait_for(self.closing_signed[chan.channel_id].get(), 10)
their_fee = int.from_bytes(cs_payload['fee_satoshis'], 'big') their_fee = int.from_bytes(cs_payload['fee_satoshis'], 'big')
their_sig = cs_payload['signature'] their_sig = cs_payload['signature']
if our_fee == their_fee: if our_fee == their_fee:

12
electrum/lnworker.py

@ -334,7 +334,6 @@ class LNWallet(LNWorker):
def peer_closed(self, peer): def peer_closed(self, peer):
for chan in self.channels_for_peer(peer.pubkey).values(): for chan in self.channels_for_peer(peer.pubkey).values():
if chan.get_state() != 'FORCE_CLOSING':
chan.set_state('DISCONNECTED') chan.set_state('DISCONNECTED')
self.network.trigger_callback('channel', chan) self.network.trigger_callback('channel', chan)
self.peers.pop(peer.pubkey) self.peers.pop(peer.pubkey)
@ -485,7 +484,7 @@ class LNWallet(LNWorker):
chan = self.channel_by_txo(funding_outpoint) chan = self.channel_by_txo(funding_outpoint)
if not chan: if not chan:
return return
self.logger.info(f'on_channel_open {funding_outpoint}') self.logger.debug(f'on_channel_open {funding_outpoint}')
self.channel_timestamps[bh2u(chan.channel_id)] = funding_txid, funding_height.height, funding_height.timestamp, None, None, None self.channel_timestamps[bh2u(chan.channel_id)] = funding_txid, funding_height.height, funding_height.timestamp, None, None, None
self.storage.put('lightning_channel_timestamps', self.channel_timestamps) self.storage.put('lightning_channel_timestamps', self.channel_timestamps)
chan.set_funding_txo_spentness(False) chan.set_funding_txo_spentness(False)
@ -497,12 +496,11 @@ class LNWallet(LNWorker):
chan = self.channel_by_txo(funding_outpoint) chan = self.channel_by_txo(funding_outpoint)
if not chan: if not chan:
return return
self.logger.info(f'on_channel_closed {funding_outpoint}') self.logger.debug(f'on_channel_closed {funding_outpoint}')
self.channel_timestamps[bh2u(chan.channel_id)] = funding_txid, funding_height.height, funding_height.timestamp, closing_txid, closing_height.height, closing_height.timestamp self.channel_timestamps[bh2u(chan.channel_id)] = funding_txid, funding_height.height, funding_height.timestamp, closing_txid, closing_height.height, closing_height.timestamp
self.storage.put('lightning_channel_timestamps', self.channel_timestamps) self.storage.put('lightning_channel_timestamps', self.channel_timestamps)
chan.set_funding_txo_spentness(True) chan.set_funding_txo_spentness(True)
if chan.get_state() != 'FORCE_CLOSING': chan.set_state('CLOSED')
chan.set_state("CLOSED")
self.on_channels_updated() self.on_channels_updated()
self.network.trigger_callback('channel', chan) self.network.trigger_callback('channel', chan)
# remove from channel_db # remove from channel_db
@ -588,7 +586,7 @@ class LNWallet(LNWorker):
await peer.bitcoin_fee_update(chan) await peer.bitcoin_fee_update(chan)
conf = lnwatcher.get_tx_height(chan.funding_outpoint.txid).conf conf = lnwatcher.get_tx_height(chan.funding_outpoint.txid).conf
peer.on_network_update(chan, conf) peer.on_network_update(chan, conf)
elif chan.get_state() == 'FORCE_CLOSING': elif chan.force_closed and chan.get_state() != 'CLOSED':
txid = chan.force_close_tx().txid() txid = chan.force_close_tx().txid()
height = lnwatcher.get_tx_height(txid).height height = lnwatcher.get_tx_height(txid).height
self.logger.info(f"force closing tx {txid}, height {height}") self.logger.info(f"force closing tx {txid}, height {height}")
@ -871,7 +869,7 @@ class LNWallet(LNWorker):
async def force_close_channel(self, chan_id): async def force_close_channel(self, chan_id):
chan = self.channels[chan_id] chan = self.channels[chan_id]
tx = chan.force_close_tx() tx = chan.force_close_tx()
chan.set_state('FORCE_CLOSING') chan.set_force_closed()
self.save_channel(chan) self.save_channel(chan)
self.on_channels_updated() self.on_channels_updated()
await self.network.broadcast_transaction(tx) await self.network.broadcast_transaction(tx)

2
electrum/tests/test_lnpeer.py

@ -262,7 +262,7 @@ class TestPeer(SequentialTestCase):
# route finding should fail when channel is closed # route finding should fail when channel is closed
async def f(): async def f():
await asyncio.gather(w1._pay_to_route(route, addr, pay_req), p1._message_loop(), p2._message_loop()) await asyncio.gather(w1._pay_to_route(route, addr, pay_req), p1._message_loop(), p2._message_loop())
with self.assertRaises(AssertionError): with self.assertRaises(PaymentFailure):
run(f()) run(f())
def run(coro): def run(coro):

Loading…
Cancel
Save