|
|
@ -601,7 +601,7 @@ class LNWallet(LNWorker): |
|
|
|
|
|
|
|
self.sent_htlcs = defaultdict(asyncio.Queue) # type: Dict[bytes, asyncio.Queue[HtlcLog]] |
|
|
|
self.sent_htlcs_routes = dict() # (RHASH, scid, htlc_id) -> route, payment_secret, amount_msat, bucket_msat |
|
|
|
self.sent_buckets = defaultdict(set) |
|
|
|
self.sent_buckets = dict() |
|
|
|
self.received_htlcs = dict() # RHASH -> mpp_status, htlc_set |
|
|
|
|
|
|
|
self.swap_manager = SwapManager(wallet=self.wallet, lnworker=self) |
|
|
@ -1074,19 +1074,21 @@ class LNWallet(LNWorker): |
|
|
|
use_two_trampolines=use_two_trampolines, |
|
|
|
fwd_trampoline_onion=fwd_trampoline_onion)) |
|
|
|
# 2. send htlcs |
|
|
|
for route, amount_msat, total_msat, cltv_delta, bucket_payment_secret, trampoline_onion in routes: |
|
|
|
for route, amount_msat, total_msat, amount_receiver_msat, cltv_delta, bucket_payment_secret, trampoline_onion in routes: |
|
|
|
await self.pay_to_route( |
|
|
|
route=route, |
|
|
|
amount_msat=amount_msat, |
|
|
|
total_msat=total_msat, |
|
|
|
amount_receiver_msat=amount_receiver_msat, |
|
|
|
payment_hash=payment_hash, |
|
|
|
payment_secret=bucket_payment_secret, |
|
|
|
min_cltv_expiry=cltv_delta, |
|
|
|
trampoline_onion=trampoline_onion) |
|
|
|
amount_inflight += amount_msat |
|
|
|
amount_inflight += amount_receiver_msat |
|
|
|
assert amount_inflight <= amount_to_pay, f"amount_inflight {amount_inflight} > amount_to_pay {amount_to_pay}" |
|
|
|
util.trigger_callback('invoice_status', self.wallet, payment_hash.hex()) |
|
|
|
# 3. await a queue |
|
|
|
self.logger.info(f"amount inflight {amount_inflight}") |
|
|
|
htlc_log = await self.sent_htlcs[payment_hash].get() |
|
|
|
amount_inflight -= htlc_log.amount_msat |
|
|
|
assert amount_inflight >= 0, f"amount_inflight={amount_inflight} < 0" |
|
|
@ -1124,6 +1126,7 @@ class LNWallet(LNWorker): |
|
|
|
route: LNPaymentRoute, |
|
|
|
amount_msat: int, |
|
|
|
total_msat: int, |
|
|
|
amount_receiver_msat:int, |
|
|
|
payment_hash: bytes, |
|
|
|
payment_secret: Optional[bytes], |
|
|
|
min_cltv_expiry: int, |
|
|
@ -1147,11 +1150,12 @@ class LNWallet(LNWorker): |
|
|
|
trampoline_onion=trampoline_onion) |
|
|
|
|
|
|
|
key = (payment_hash, short_channel_id, htlc.htlc_id) |
|
|
|
self.sent_htlcs_routes[key] = route, payment_secret, amount_msat, total_msat |
|
|
|
self.sent_htlcs_routes[key] = route, payment_secret, amount_msat, total_msat, amount_receiver_msat |
|
|
|
# if we sent MPP to a trampoline, add item to sent_buckets |
|
|
|
if not self.channel_db and amount_msat != total_msat: |
|
|
|
if payment_secret not in self.sent_buckets: |
|
|
|
self.sent_buckets[payment_secret] = total_msat |
|
|
|
self.sent_buckets[payment_secret] = 0 |
|
|
|
self.sent_buckets[payment_secret] += amount_receiver_msat |
|
|
|
util.trigger_callback('htlc_added', chan, htlc, SENT) |
|
|
|
|
|
|
|
|
|
|
@ -1309,7 +1313,7 @@ class LNWallet(LNWorker): |
|
|
|
continue |
|
|
|
if chan.is_frozen_for_sending(): |
|
|
|
continue |
|
|
|
trampoline_onion, trampoline_fee, amount_with_fees, cltv_delta = create_trampoline_route_and_onion( |
|
|
|
trampoline_onion, amount_with_fees, cltv_delta = create_trampoline_route_and_onion( |
|
|
|
amount_msat=amount_msat, |
|
|
|
total_msat=final_total_msat, |
|
|
|
min_cltv_expiry=min_cltv_expiry, |
|
|
@ -1325,8 +1329,7 @@ class LNWallet(LNWorker): |
|
|
|
trampoline_fee_level=trampoline_fee_level, |
|
|
|
use_two_trampolines=use_two_trampolines) |
|
|
|
trampoline_payment_secret = os.urandom(32) |
|
|
|
amount_to_send = amount_with_fees + trampoline_fee |
|
|
|
if chan.available_to_spend(LOCAL, strict=True) < amount_to_send: |
|
|
|
if chan.available_to_spend(LOCAL, strict=True) < amount_with_fees: |
|
|
|
continue |
|
|
|
route = [ |
|
|
|
RouteEdge( |
|
|
@ -1338,7 +1341,7 @@ class LNWallet(LNWorker): |
|
|
|
cltv_expiry_delta=0, |
|
|
|
node_features=trampoline_features) |
|
|
|
] |
|
|
|
routes = [(route, amount_to_send, amount_to_send, cltv_delta, trampoline_payment_secret, trampoline_onion)] |
|
|
|
routes = [(route, amount_with_fees, amount_with_fees, amount_msat, cltv_delta, trampoline_payment_secret, trampoline_onion)] |
|
|
|
break |
|
|
|
else: |
|
|
|
raise NoPathFound() |
|
|
@ -1350,7 +1353,7 @@ class LNWallet(LNWorker): |
|
|
|
r_tags=r_tags, t_tags=t_tags, |
|
|
|
invoice_features=invoice_features, |
|
|
|
outgoing_channel=None, full_path=full_path) |
|
|
|
routes = [(route, amount_msat, final_total_msat, min_cltv_expiry, payment_secret, fwd_trampoline_onion)] |
|
|
|
routes = [(route, amount_msat, final_total_msat, amount_msat, min_cltv_expiry, payment_secret, fwd_trampoline_onion)] |
|
|
|
except NoPathFound: |
|
|
|
if not invoice_features.supports(LnFeatures.BASIC_MPP_OPT): |
|
|
|
raise |
|
|
@ -1374,7 +1377,7 @@ class LNWallet(LNWorker): |
|
|
|
buckets[chan.node_id].append((chan_id, part_amount_msat)) |
|
|
|
for node_id, bucket in buckets.items(): |
|
|
|
bucket_amount_msat = sum([x[1] for x in bucket]) |
|
|
|
trampoline_onion, trampoline_fee, bucket_amount_with_fees, bucket_cltv_delta = create_trampoline_route_and_onion( |
|
|
|
trampoline_onion, bucket_amount_with_fees, bucket_cltv_delta = create_trampoline_route_and_onion( |
|
|
|
amount_msat=bucket_amount_msat, |
|
|
|
total_msat=final_total_msat, |
|
|
|
min_cltv_expiry=min_cltv_expiry, |
|
|
@ -1389,15 +1392,16 @@ class LNWallet(LNWorker): |
|
|
|
local_height=local_height, |
|
|
|
trampoline_fee_level=trampoline_fee_level, |
|
|
|
use_two_trampolines=use_two_trampolines) |
|
|
|
self.logger.info(f'trampoline fee {trampoline_fee}') |
|
|
|
# node_features is only used to determine is_tlv |
|
|
|
bucket_payment_secret = os.urandom(32) |
|
|
|
bucket_fees = bucket_amount_with_fees - bucket_amount_msat |
|
|
|
self.logger.info(f'bucket_fees {bucket_fees}') |
|
|
|
for chan_id, part_amount_msat in bucket: |
|
|
|
chan = self.channels[chan_id] |
|
|
|
margin = chan.available_to_spend(LOCAL, strict=True) - part_amount_msat |
|
|
|
delta_fee = min(trampoline_fee, margin) |
|
|
|
delta_fee = min(bucket_fees, margin) # fixme: we have to pay all trampolines |
|
|
|
part_amount_msat_with_fees = part_amount_msat + delta_fee |
|
|
|
trampoline_fee -= delta_fee |
|
|
|
bucket_fees -= delta_fee |
|
|
|
route = [ |
|
|
|
RouteEdge( |
|
|
|
start_node=self.node_keypair.pubkey, |
|
|
@ -1409,8 +1413,8 @@ class LNWallet(LNWorker): |
|
|
|
node_features=trampoline_features) |
|
|
|
] |
|
|
|
self.logger.info(f'adding route {part_amount_msat} {delta_fee} {margin}') |
|
|
|
routes.append((route, part_amount_msat_with_fees, bucket_amount_with_fees, bucket_cltv_delta, bucket_payment_secret, trampoline_onion)) |
|
|
|
if trampoline_fee > 0: |
|
|
|
routes.append((route, part_amount_msat_with_fees, bucket_amount_with_fees, part_amount_msat, bucket_cltv_delta, bucket_payment_secret, trampoline_onion)) |
|
|
|
if bucket_fees != 0: |
|
|
|
self.logger.info('not enough margin to pay trampoline fee') |
|
|
|
raise NoPathFound() |
|
|
|
else: |
|
|
@ -1424,7 +1428,7 @@ class LNWallet(LNWorker): |
|
|
|
r_tags=r_tags, t_tags=t_tags, |
|
|
|
invoice_features=invoice_features, |
|
|
|
outgoing_channel=channel, full_path=None) |
|
|
|
routes.append((route, part_amount_msat, final_total_msat, min_cltv_expiry, payment_secret, fwd_trampoline_onion)) |
|
|
|
routes.append((route, part_amount_msat, final_total_msat, part_amount_msat, min_cltv_expiry, payment_secret, fwd_trampoline_onion)) |
|
|
|
self.logger.info(f"found acceptable split configuration: {list(s[0].values())} rating: {s[1]}") |
|
|
|
break |
|
|
|
except NoPathFound: |
|
|
@ -1660,11 +1664,11 @@ class LNWallet(LNWorker): |
|
|
|
util.trigger_callback('htlc_fulfilled', payment_hash, chan.channel_id) |
|
|
|
q = self.sent_htlcs.get(payment_hash) |
|
|
|
if q: |
|
|
|
route, payment_secret, amount_msat, bucket_msat = self.sent_htlcs_routes[(payment_hash, chan.short_channel_id, htlc_id)] |
|
|
|
route, payment_secret, amount_msat, bucket_msat, amount_receiver_msat = self.sent_htlcs_routes[(payment_hash, chan.short_channel_id, htlc_id)] |
|
|
|
htlc_log = HtlcLog( |
|
|
|
success=True, |
|
|
|
route=route, |
|
|
|
amount_msat=amount_msat) |
|
|
|
amount_msat=amount_receiver_msat) |
|
|
|
q.put_nowait(htlc_log) |
|
|
|
else: |
|
|
|
key = payment_hash.hex() |
|
|
@ -1685,7 +1689,7 @@ class LNWallet(LNWorker): |
|
|
|
# detect if it is part of a bucket |
|
|
|
# if yes, wait until the bucket completely failed |
|
|
|
key = (payment_hash, chan.short_channel_id, htlc_id) |
|
|
|
route, payment_secret, amount_msat, bucket_msat = self.sent_htlcs_routes[key] |
|
|
|
route, payment_secret, amount_msat, bucket_msat, amount_receiver_msat = self.sent_htlcs_routes[key] |
|
|
|
if error_bytes: |
|
|
|
# TODO "decode_onion_error" might raise, catch and maybe blacklist/penalise someone? |
|
|
|
try: |
|
|
@ -1701,16 +1705,15 @@ class LNWallet(LNWorker): |
|
|
|
|
|
|
|
# check sent_buckets if we use trampoline |
|
|
|
if self.channel_db is None and payment_secret in self.sent_buckets: |
|
|
|
self.sent_buckets[payment_secret] -= amount_msat |
|
|
|
self.sent_buckets[payment_secret] -= amount_receiver_msat |
|
|
|
if self.sent_buckets[payment_secret] > 0: |
|
|
|
return |
|
|
|
else: |
|
|
|
amount_msat = bucket_msat |
|
|
|
assert self.sent_buckets[payment_secret] == 0 |
|
|
|
|
|
|
|
htlc_log = HtlcLog( |
|
|
|
success=False, |
|
|
|
route=route, |
|
|
|
amount_msat=amount_msat, |
|
|
|
amount_msat=amount_receiver_msat, |
|
|
|
error_bytes=error_bytes, |
|
|
|
failure_msg=failure_message, |
|
|
|
sender_idx=sender_idx) |
|
|
|