From 8c160e1b97231c638071d8e7c9280afcdf878efe Mon Sep 17 00:00:00 2001 From: SomberNight Date: Thu, 17 Oct 2024 23:57:19 +0000 Subject: [PATCH] lnpeer: maybe send update_fee right away after reestablish I just had a channel force-closed over a fee-estimate-disagreement :( Scenario: 1. started electrum, opened wallet 2. waited around 10 seconds, opened the lightning channels overview, saw that channels are open/ready 3. after around 10 more seconds, scanned bolt11 invoice and tried to pay 4. channel got force-closed Before this commit, we only call maybe_update_fee via lnwatcher callbacks. These callbacks trigger on events such as "adb_set_up_to_date", "blockchain_updated", "network_updated", "fee". In my case there was a race that all these events triggered *before* the channel got reestablished (in fact before the peer handshake finished). And also, by chance there were none of these events after the reestablish but before I sent the HTLC. When I sent the HTLC, the channel counterparty (eclair) sent back an "error" msg that the feerates are too different, which led us to do a local-force-close. I have other channels in this wallet (with other peers), which reestablished faster and got lucky with timing: the lnwatcher callbacks came just in time to trigger update_fee for them. ``` 20241017T222847.598163Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | handshake done for 03ecef675be448b615e6176424070673ef8284e0fd19d8be062a6cb5b130a0a0d1@lightning.electrum.org:9740 20241017T222847.602594Z | DEBUG | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | Sending INIT 20241017T222847.641383Z | DEBUG | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | Received INIT 20241017T222847.655041Z | DEBUG | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | Sending CHANNEL_REESTABLISH 20241017T222847.658355Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | channel_reestablish (): sent channel_reestablish with (next_local_ctn=157, oldest_unrevoked_remote_ctn=156) 20241017T222847.659524Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | reestablish_channel was called but channel already in peer_state 20241017T222847.660491Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | reestablish_channel was called but channel already in peer_state 20241017T222847.661442Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | reestablish_channel was called but channel already in peer_state 20241017T222847.662768Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | reestablish_channel was called but channel already in peer_state 20241017T222847.669875Z | DEBUG | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | Received QUERY_CHANNEL_RANGE 20241017T222847.690318Z | DEBUG | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | Received GOSSIP_TIMESTAMP_FILTER 20241017T222847.705782Z | DEBUG | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | Received CHANNEL_REESTABLISH 20241017T222847.707932Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | channel_reestablish (): received channel_reestablish with (their_next_local_ctn=157, their_oldest_unrevoked_remote_ctn=156) 20241017T222847.712504Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | channel_reestablish (): replayed 0 unacked messages. [] 20241017T222847.716096Z | DEBUG | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | Sending CHANNEL_READY 20241017T222847.738709Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | saved remote channel_update gossip msg for chan 20241017T222907.627447Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | lnpeer.pay len(route)=1 20241017T222907.628927Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | 0: edge= hop_data=}. hmac=None> 20241017T222907.629184Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | adding trampoline onion to final payload 20241017T222907.629405Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | lnpeer.pay len(t_route)=2 20241017T222907.629653Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | 0: t_node=03ecef675be448b615e6176424070673ef8284e0fd19d8be062a6cb5b130a0a0d1 hop_data=}. hmac=> 20241017T222907.629894Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | 1: t_node= hop_data=}. hmac=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'> 20241017T222907.631495Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | starting payment. len(route)=1. 20241017T222907.633075Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | starting payment. htlc: UpdateAddHtlc(amount_msat=, payment_hash=, cltv_abs=, timestamp=1729204147, htlc_id=66) 20241017T222907.633385Z | DEBUG | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | Sending UPDATE_ADD_HTLC 20241017T222907.635118Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | send_commitment. chan . ctn: 157. 20241017T222907.643229Z | DEBUG | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | Sending COMMITMENT_SIGNED 20241017T222907.721929Z | DEBUG | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | Received ERROR 20241017T222907.722621Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | remote peer sent error [DO NOT TRUST THIS MESSAGE]: 'local/remote feerates are too different: remoteFeeratePerKw= localFeeratePerKw=50125'. chan_id=. is_known_chan_id=True 20241017T222907.734272Z | DEBUG | lnchannel.Channel.[] | Setting channel state: OPEN -> FORCE_CLOSING 20241017T222907.825540Z | INFO | lnpeer.Peer.[LNWallet, 03ecef675b-98960573] | Disconnecting: GracefulDisconnect() ``` --- electrum/lnpeer.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index 93c0a4c0f..27d750948 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -1394,6 +1394,7 @@ class Peer(Logger): self.send_channel_ready(chan) self.maybe_send_announcement_signatures(chan) + self.maybe_update_fee(chan) # if needed, update fee ASAP, to avoid force-closures from this # checks done util.trigger_callback('channel', self.lnworker.wallet, chan) # if we have sent a previous shutdown, it must be retransmitted (Bolt2) @@ -2263,6 +2264,8 @@ class Peer(Logger): """ if not chan.can_send_ctx_updates(): return + if chan.get_state() != ChannelState.OPEN: + return feerate_per_kw = self.lnworker.current_target_feerate_per_kw() def does_chan_fee_need_update(chan_feerate: Union[float, int]) -> bool: # We raise fees more aggressively than we lower them. Overpaying is not too bad,