1
0

Refactor channel states:

- persisted states are saved
 - state transitions are checked
 - transient states are stored in channel.peer_state
 - new channel states: 'PREOPENING', 'FUNDED' and 'REDEEMED'
 - upgrade storage to version 21
This commit is contained in:
ThomasV
2019-10-29 08:02:14 +01:00
parent c31fa059fe
commit 61dfcba092
11 changed files with 151 additions and 79 deletions

View File

@@ -40,6 +40,7 @@ from .lnaddr import lnencode, LnAddr, lndecode
from .ecc import der_sig_from_sig_string
from .ecc_fast import is_using_fast_ecc
from .lnchannel import Channel, ChannelJsonEncoder
from .lnchannel import channel_states, peer_states
from . import lnutil
from .lnutil import funding_output_script
from .bitcoin import redeem_script_to_address
@@ -420,10 +421,21 @@ class LNWallet(LNWorker):
def peer_closed(self, peer):
for chan in self.channels_for_peer(peer.pubkey).values():
chan.set_state('DISCONNECTED')
chan.peer_state = peer_states.DISCONNECTED
self.network.trigger_callback('channel', chan)
self.peers.pop(peer.pubkey)
def get_channel_status(self, chan):
# status displayed in the GUI
cs = chan.get_state()
if chan.is_closed():
return cs.name
peer = self.peers.get(chan.node_id)
ps = chan.peer_state
if ps != peer_states.GOOD:
return ps.name
return cs.name
def payment_completed(self, chan: Channel, direction: Direction,
htlc: UpdateAddHtlc):
chan_id = chan.channel_id
@@ -629,7 +641,6 @@ class LNWallet(LNWorker):
block_height, tx_pos, chan.funding_outpoint.output_index)
self.logger.info(f"save_short_channel_id: {chan.short_channel_id}")
self.save_channel(chan)
self.on_channels_updated()
def channel_by_txo(self, txo):
with self.lock:
@@ -644,23 +655,25 @@ class LNWallet(LNWorker):
chan = self.channel_by_txo(funding_outpoint)
if not chan:
return
#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.storage.put('lightning_channel_timestamps', self.channel_timestamps)
chan.set_funding_txo_spentness(False)
# send event to GUI
self.network.trigger_callback('channel', chan)
if self.should_channel_be_closed_due_to_expiring_htlcs(chan):
if chan.get_state() == channel_states.OPEN and self.should_channel_be_closed_due_to_expiring_htlcs(chan):
self.logger.info(f"force-closing due to expiring htlcs")
await self.force_close_channel(chan.channel_id)
return
if chan.short_channel_id is None:
self.save_short_chan_id(chan)
if chan.get_state() == "OPENING" and chan.short_channel_id:
peer = self.peers[chan.node_id]
peer.send_funding_locked(chan)
elif chan.get_state() == "OPEN":
if chan.get_state() == channel_states.OPENING:
if chan.short_channel_id is None:
self.save_short_chan_id(chan)
if chan.short_channel_id:
chan.set_state(channel_states.FUNDED)
self.channel_timestamps[bh2u(chan.channel_id)] = chan.funding_outpoint.txid, funding_height.height, funding_height.timestamp, None, None, None
self.storage.put('lightning_channel_timestamps', self.channel_timestamps)
if chan.get_state() == channel_states.FUNDED:
peer = self.peers.get(chan.node_id)
if peer:
peer.send_funding_locked(chan)
elif chan.get_state() == channel_states.OPEN:
peer = self.peers.get(chan.node_id)
if peer is None:
self.logger.info("peer not found for {}".format(bh2u(chan.node_id)))
@@ -669,7 +682,8 @@ class LNWallet(LNWorker):
await peer.bitcoin_fee_update(chan)
conf = self.lnwatcher.get_tx_height(chan.funding_outpoint.txid).conf
peer.on_network_update(chan, conf)
elif chan.force_closed and chan.get_state() != 'CLOSED':
elif chan.get_state() == channel_states.FORCE_CLOSING:
txid = chan.force_close_tx().txid()
height = self.lnwatcher.get_tx_height(txid).height
self.logger.info(f"force closing tx {txid}, height {height}")
@@ -677,21 +691,27 @@ class LNWallet(LNWorker):
self.logger.info('REBROADCASTING CLOSING TX')
await self.force_close_channel(chan.channel_id)
@ignore_exceptions
@log_exceptions
async def on_update_closed_channel(self, event, funding_outpoint, spenders, funding_txid, funding_height, closing_txid, closing_height, closing_tx):
async def on_update_closed_channel(self, event, funding_outpoint, spenders, funding_txid, funding_height, closing_txid, closing_height, closing_tx, keep_watching):
chan = self.channel_by_txo(funding_outpoint)
if not chan:
return
#self.logger.debug(f'on_channel_closed {funding_outpoint}')
# fixme: this is wasteful
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)
chan.set_funding_txo_spentness(True)
chan.set_state('CLOSED')
self.on_channels_updated()
self.network.trigger_callback('channel', chan)
# remove from channel_db
if chan.short_channel_id is not None:
self.channel_db.remove_channel(chan.short_channel_id)
if chan.get_state() < channel_states.CLOSED:
chan.set_state(channel_states.CLOSED)
if chan.get_state() == channel_states.CLOSED and not keep_watching:
chan.set_state(channel_states.REDEEMED)
# detect who closed and set sweep_info
sweep_info_dict = chan.sweep_ctx(closing_tx)
self.logger.info(f'sweep_info_dict length: {len(sweep_info_dict)}')
@@ -800,11 +820,8 @@ class LNWallet(LNWorker):
temp_channel_id=os.urandom(32))
self.save_channel(chan)
self.lnwatcher.add_channel(chan.funding_outpoint.to_str(), chan.get_funding_address())
self.on_channels_updated()
return chan
def on_channels_updated(self):
self.network.trigger_callback('channels_updated', self.wallet)
return chan
@log_exceptions
async def add_peer(self, connect_str: str) -> Peer:
@@ -1149,7 +1166,7 @@ class LNWallet(LNWorker):
# note: currently we add *all* our channels; but this might be a privacy leak?
for chan in channels:
# check channel is open
if chan.get_state() != "OPEN":
if chan.get_state() != channel_states.OPEN:
continue
# check channel has sufficient balance
# FIXME because of on-chain fees of ctx, this check is insufficient
@@ -1206,7 +1223,7 @@ class LNWallet(LNWorker):
'channel_id': format_short_channel_id(chan.short_channel_id) if chan.short_channel_id else None,
'full_channel_id': bh2u(chan.channel_id),
'channel_point': chan.funding_outpoint.to_str(),
'state': chan.get_state(),
'state': chan.get_state().name,
'remote_pubkey': bh2u(chan.node_id),
'local_balance': chan.balance(LOCAL)//1000,
'remote_balance': chan.balance(REMOTE)//1000,
@@ -1220,9 +1237,7 @@ class LNWallet(LNWorker):
async def force_close_channel(self, chan_id):
chan = self.channels[chan_id]
tx = chan.force_close_tx()
chan.set_force_closed()
self.save_channel(chan)
self.on_channels_updated()
chan.set_state(channel_states.FORCE_CLOSING)
try:
await self.network.broadcast_transaction(tx)
except Exception as e:
@@ -1276,6 +1291,7 @@ class LNWallet(LNWorker):
if ratio < 0.5:
self.logger.warning(f"fee level for channel {bh2u(chan.channel_id)} is {chan_feerate} sat/kiloweight, "
f"current recommended feerate is {self.current_feerate_per_kw()} sat/kiloweight, consider force closing!")
# reestablish
if not chan.should_try_to_reestablish_peer():
continue
peer = self.peers.get(chan.node_id, None)