tfix trampoline forwarding: add_received_htlc must be indexed by payment_secret - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 5207c40cc3ef782765e2a413248025d42c156edc DIR parent 59547d05138b0e90413ac4f60f81d5af8564516b HTML Author: ThomasV <thomasv@electrum.org> Date: Fri, 5 Mar 2021 17:02:10 +0100 fix trampoline forwarding: add_received_htlc must be indexed by payment_secret Diffstat: M electrum/lnpeer.py | 15 ++++++++------- M electrum/lnworker.py | 25 +++++++++++++++---------- 2 files changed, 23 insertions(+), 17 deletions(-) --- DIR diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py t@@ -1536,13 +1536,14 @@ class Peer(Logger): # TODO fail here if invoice has set PAYMENT_SECRET_REQ payment_secret_from_onion = None - mpp_status = self.lnworker.add_received_htlc(chan.short_channel_id, htlc, total_msat) - if mpp_status is None: - return None, None - if mpp_status is False: - log_fail_reason(f"MPP_TIMEOUT") - raise OnionRoutingFailure(code=OnionFailureCode.MPP_TIMEOUT, data=b'') - assert mpp_status is True + if total_msat > amt_to_forward: + mpp_status = self.lnworker.add_received_htlc(payment_secret_from_onion, chan.short_channel_id, htlc, total_msat) + if mpp_status is None: + return None, None + if mpp_status is False: + log_fail_reason(f"MPP_TIMEOUT") + raise OnionRoutingFailure(code=OnionFailureCode.MPP_TIMEOUT, data=b'') + assert mpp_status is True # if there is a trampoline_onion, maybe_fulfill_htlc will be called again if processed_onion.trampoline_onion_packet: DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py t@@ -1594,28 +1594,33 @@ class LNWallet(LNWorker): self.payments[key] = info.amount_msat, info.direction, info.status self.wallet.save_db() - def add_received_htlc(self, short_channel_id, htlc: UpdateAddHtlc, expected_msat: int) -> Optional[bool]: + def add_received_htlc(self, payment_secret, short_channel_id, htlc: UpdateAddHtlc, expected_msat: int) -> Optional[bool]: """ return MPP status: True (accepted), False (expired) or None """ payment_hash = htlc.payment_hash - is_accepted = (self.get_payment_status(payment_hash) == PR_PAID) - is_expired, htlc_set = self.received_htlcs.get(payment_hash, (False, set())) + is_expired, is_accepted, htlc_set = self.received_htlcs.get(payment_secret, (False, False, set())) + if self.get_payment_status(payment_hash) == PR_PAID: + # payment_status is persisted + is_accepted = True + is_expired = False key = (short_channel_id, htlc) if key not in htlc_set: htlc_set.add(key) if not is_accepted and not is_expired: total = sum([_htlc.amount_msat for scid, _htlc in htlc_set]) first_timestamp = min([_htlc.timestamp for scid, _htlc in htlc_set]) - is_expired = time.time() - first_timestamp > MPP_EXPIRY - if not is_expired and total == expected_msat: + if time.time() - first_timestamp > MPP_EXPIRY: + is_expired = True + elif total == expected_msat: is_accepted = True - self.set_payment_status(payment_hash, PR_PAID) - util.trigger_callback('request_status', self.wallet, payment_hash.hex(), PR_PAID) + if self.get_payment_info(payment_hash) is not None: + self.set_payment_status(payment_hash, PR_PAID) + util.trigger_callback('request_status', self.wallet, payment_hash.hex(), PR_PAID) if is_accepted or is_expired: htlc_set.remove(key) if len(htlc_set) > 0: - self.received_htlcs[payment_hash] = is_expired, htlc_set - elif payment_hash in self.received_htlcs: - self.received_htlcs.pop(payment_hash) + self.received_htlcs[payment_secret] = is_expired, is_accepted, htlc_set + elif payment_secret in self.received_htlcs: + self.received_htlcs.pop(payment_secret) return True if is_accepted else (False if is_expired else None) def get_payment_status(self, payment_hash):