diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index eaaddacfa..ec1362944 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -2150,7 +2150,7 @@ class Peer(Logger, EventListener): Does additional checks on the incoming htlc and return the payment key if the tests pass, otherwise raises OnionRoutingError which will get the htlc failed. """ - _log_fail_reason = self._log_htlc_fail_reason_cb(chan.short_channel_id, htlc, processed_onion.hop_data.payload) + _log_fail_reason = self._log_htlc_fail_reason_cb(chan.channel_id, htlc, processed_onion.hop_data.payload) # Check that our blockchain tip is sufficiently recent so that we have an approx idea of the height. # We should not release the preimage for an HTLC that its sender could already time out as @@ -2269,7 +2269,7 @@ class Peer(Logger, EventListener): self.lnworker.dont_expire_htlcs.pop(payment_hash.hex(), None) # htlcs wont get expired anymore for mpp_htlc in list(htlc_set.htlcs): htlc_id = mpp_htlc.htlc.htlc_id - chan = self.lnworker.get_channel_by_short_id(mpp_htlc.scid) + chan = self.lnworker.channels[mpp_htlc.channel_id] if chan.channel_id not in self.channels: # this htlc belongs to another peer and has to be settled in their htlc_switch continue @@ -2311,7 +2311,7 @@ class Peer(Logger, EventListener): self.lnworker.dont_expire_htlcs.pop(payment_hash.hex(), None) self.lnworker.dont_settle_htlcs.pop(payment_hash.hex(), None) # already failed for mpp_htlc in list(htlc_set.htlcs): - chan = self.lnworker.get_channel_by_short_id(mpp_htlc.scid) + chan = self.lnworker.channels[mpp_htlc.channel_id] htlc_id = mpp_htlc.htlc.htlc_id if chan.channel_id not in self.channels: # this htlc belongs to another peer and has to be settled in their htlc_switch @@ -2854,7 +2854,7 @@ class Peer(Logger, EventListener): ) self.lnworker.update_or_create_mpp_with_received_htlc( payment_key=payment_key, - scid=chan.short_channel_id, + channel_id=chan.channel_id, htlc=htlc, unprocessed_onion_packet=onion_packet_hex, # outer onion if trampoline ) @@ -2938,11 +2938,12 @@ class Peer(Logger, EventListener): def _log_htlc_fail_reason_cb( self, - scid: ShortChannelID, + channel_id: bytes, htlc: UpdateAddHtlc, onion_payload: dict ) -> Callable[[str], None]: def _log_fail_reason(reason: str) -> None: + scid = self.lnworker.channels[channel_id].short_channel_id self.logger.info(f"will FAIL HTLC: {str(scid)=}. {reason=}. {str(htlc)=}. {onion_payload=}") return _log_fail_reason @@ -2960,7 +2961,7 @@ class Peer(Logger, EventListener): onion_payload = {} self._log_htlc_fail_reason_cb( - mpp_htlc.scid, + mpp_htlc.channel_id, mpp_htlc.htlc, onion_payload, )(f"mpp set {id(mpp_set)} failed: {reason}") @@ -3074,7 +3075,7 @@ class Peer(Logger, EventListener): if mpp_set.resolution == RecvMPPResolution.WAITING: # calculate the sum of just in time channel opening fees - htlc_channels = [self.lnworker.get_channel_by_short_id(scid) for scid in set(h.scid for h in mpp_set.htlcs)] + htlc_channels = [self.lnworker.channels[channel_id] for channel_id in set(h.channel_id for h in mpp_set.htlcs)] jit_opening_fees_msat = sum((c.jit_opening_fee or 0) for c in htlc_channels) # check if set is first stage multi-trampoline payment to us diff --git a/electrum/lnutil.py b/electrum/lnutil.py index 70256a74d..f670b4bb2 100644 --- a/electrum/lnutil.py +++ b/electrum/lnutil.py @@ -1965,18 +1965,18 @@ del r class ReceivedMPPHtlc(NamedTuple): - scid: ShortChannelID + channel_id: bytes htlc: UpdateAddHtlc unprocessed_onion: str def __repr__(self): - return f"{self.scid}, {self.htlc=}, {self.unprocessed_onion[:15]=}..." + return f"chan_id={self.channel_id.hex()}, {self.htlc=}, {self.unprocessed_onion[:15]=}..." @staticmethod - def from_tuple(scid, htlc, unprocessed_onion) -> 'ReceivedMPPHtlc': - assert is_hex_str(unprocessed_onion) and is_hex_str(scid) + def from_tuple(channel_id, htlc, unprocessed_onion) -> 'ReceivedMPPHtlc': + assert is_hex_str(unprocessed_onion) and is_hex_str(channel_id) return ReceivedMPPHtlc( - scid=ShortChannelID(bytes.fromhex(scid)), + channel_id=bytes.fromhex(channel_id), htlc=UpdateAddHtlc.from_tuple(*htlc), unprocessed_onion=unprocessed_onion, ) diff --git a/electrum/lnworker.py b/electrum/lnworker.py index f28c99a16..1db9d0de0 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -2583,7 +2583,7 @@ class LNWallet(LNWorker): self, *, payment_key: str, - scid: ShortChannelID, + channel_id: bytes, htlc: UpdateAddHtlc, unprocessed_onion_packet: str, ): @@ -2610,7 +2610,7 @@ class LNWallet(LNWorker): if mpp_status.resolution > RecvMPPResolution.WAITING: # we are getting a htlc for a set that is not in WAITING state, it cannot be safely added - self.logger.info(f"htlc set cannot accept htlc, failing htlc: {scid=} {htlc.htlc_id=}") + self.logger.info(f"htlc set cannot accept htlc, failing htlc: {channel_id=} {htlc.htlc_id=}") if mpp_status == RecvMPPResolution.EXPIRED: raise OnionRoutingFailure(code=OnionFailureCode.MPP_TIMEOUT, data=b'') raise OnionRoutingFailure( @@ -2619,7 +2619,7 @@ class LNWallet(LNWorker): ) new_htlc = ReceivedMPPHtlc( - scid=scid, + channel_id=channel_id, htlc=htlc, unprocessed_onion=unprocessed_onion_packet, ) @@ -2707,7 +2707,7 @@ class LNWallet(LNWorker): # only cleanup when channel is REDEEMED as mpp set is still required for lnsweep assert chan._state == ChannelState.REDEEMED for payment_key_hex, mpp_status in list(self.received_mpp_htlcs.items()): - htlcs_to_remove = [htlc for htlc in mpp_status.htlcs if htlc.scid == chan.short_channel_id] + htlcs_to_remove = [htlc for htlc in mpp_status.htlcs if htlc.channel_id == chan.channel_id] for stale_mpp_htlc in htlcs_to_remove: assert mpp_status.resolution != RecvMPPResolution.WAITING self.logger.info(f'maybe_cleanup_mpp: removing htlc of MPP {payment_key_hex}') @@ -3547,7 +3547,7 @@ class LNWallet(LNWorker): assert not any_outer_onion.are_we_final assert len(processed_htlc_set) == 1, processed_htlc_set forward_htlc = any_mpp_htlc.htlc - incoming_chan = self.get_channel_by_short_id(any_mpp_htlc.scid) + incoming_chan = self.channels[any_mpp_htlc.channel_id] next_htlc = await self._maybe_forward_htlc( incoming_chan=incoming_chan, htlc=forward_htlc, diff --git a/electrum/wallet_db.py b/electrum/wallet_db.py index 325eb36f3..068499c2d 100644 --- a/electrum/wallet_db.py +++ b/electrum/wallet_db.py @@ -69,7 +69,7 @@ class WalletUnfinished(WalletFileException): # seed_version is now used for the version of the wallet file OLD_SEED_VERSION = 4 # electrum versions < 2.0 NEW_SEED_VERSION = 11 # electrum versions >= 2.0 -FINAL_SEED_VERSION = 64 # electrum >= 2.7 will set this to prevent +FINAL_SEED_VERSION = 65 # electrum >= 2.7 will set this to prevent # old versions from overwriting new format @@ -236,6 +236,7 @@ class WalletDBUpgrader(Logger): self._convert_version_62() self._convert_version_63() self._convert_version_64() + self._convert_version_65() self.put('seed_version', FINAL_SEED_VERSION) # just to be sure def _convert_wallet_type(self): @@ -1288,6 +1289,32 @@ class WalletDBUpgrader(Logger): self.data['lightning_payments'] = new_payment_infos self.data['seed_version'] = 64 + def _convert_version_65(self): + """Store channel_id instead of short_channel_id in ReceivedMPPHtlc""" + if not self._is_upgrade_method_needed(64, 64): + return + + channels = self.data.get('channels', {}) + def scid_to_channel_id(scid): + for channel_id, channel_data in channels.items(): + if scid == channel_data.get('short_channel_id'): + return channel_id + raise KeyError(f"missing {scid=} in channels") + + mpp_sets = self.data.get('received_mpp_htlcs', {}) + new_mpp_sets = {} + for payment_key, mpp_set in mpp_sets.items(): + resolution, htlc_list, parent_set_key = mpp_set + new_htlc_list = [] + for htlc_data_tuple in htlc_list: + scid, update_add_htlc, onion = htlc_data_tuple + channel_id = scid_to_channel_id(scid) + new_htlc_list.append((channel_id, update_add_htlc, onion)) + new_mpp_sets[payment_key] = (resolution, new_htlc_list, parent_set_key) + + self.data['received_mpp_htlcs'] = new_mpp_sets + self.data['seed_version'] = 65 + def _convert_imported(self): if not self._is_upgrade_method_needed(0, 13): return