lnworker: use channel_id instead of scid in ReceivedMPPHtlc
Store the channel id instead of the scid in ReceivedMPPHtlc. The scid can be None, in theory even for multiple channels at the same time. Using the channel_id which is always available and unique seems less error prone at the cost of temporarily higher storage requirements in the db for the duration of the pending htlcs. Alternatively we could use the local scid alias however using the channel_id seems less complex and leaves less room for ambiguity.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user