Filter nodes for receiving:
- increase MPP_RECEIVE_CUTOFF from 5 to 20 percent - filter channels by node_id, not channel_id - make num_sats_can_receive consistent with routing hints
This commit is contained in:
@@ -7,6 +7,7 @@ import os
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
|
import operator
|
||||||
from typing import (Optional, Sequence, Tuple, List, Set, Dict, TYPE_CHECKING,
|
from typing import (Optional, Sequence, Tuple, List, Set, Dict, TYPE_CHECKING,
|
||||||
NamedTuple, Union, Mapping, Any, Iterable, AsyncGenerator, DefaultDict)
|
NamedTuple, Union, Mapping, Any, Iterable, AsyncGenerator, DefaultDict)
|
||||||
import threading
|
import threading
|
||||||
@@ -91,6 +92,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
SAVED_PR_STATUS = [PR_PAID, PR_UNPAID, PR_SCHEDULED] # status that are persisted
|
SAVED_PR_STATUS = [PR_PAID, PR_UNPAID, PR_SCHEDULED] # status that are persisted
|
||||||
|
|
||||||
|
MPP_RECEIVE_CUTOFF = 0.2
|
||||||
|
|
||||||
NUM_PEERS_TARGET = 4
|
NUM_PEERS_TARGET = 4
|
||||||
|
|
||||||
@@ -1764,18 +1766,12 @@ class LNWallet(LNWorker):
|
|||||||
|
|
||||||
assert amount_msat is None or amount_msat > 0
|
assert amount_msat is None or amount_msat > 0
|
||||||
timestamp = int(time.time())
|
timestamp = int(time.time())
|
||||||
routing_hints = self.calc_routing_hints_for_invoice(amount_msat)
|
routing_hints, trampoline_hints = self.calc_routing_hints_for_invoice(amount_msat)
|
||||||
if not routing_hints:
|
if not routing_hints:
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
"Warning. No routing hints added to invoice. "
|
"Warning. No routing hints added to invoice. "
|
||||||
"Other clients will likely not be able to send to us.")
|
"Other clients will likely not be able to send to us.")
|
||||||
# if not all hints are trampoline, do not create trampoline invoice
|
|
||||||
invoice_features = self.features.for_invoice()
|
invoice_features = self.features.for_invoice()
|
||||||
trampoline_hints = []
|
|
||||||
for r in routing_hints:
|
|
||||||
node_id, short_channel_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta = r[1][0]
|
|
||||||
if len(r[1])== 1 and self.is_trampoline_peer(node_id):
|
|
||||||
trampoline_hints.append(('t', (node_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta)))
|
|
||||||
payment_preimage = os.urandom(32)
|
payment_preimage = os.urandom(32)
|
||||||
payment_hash = sha256(payment_preimage)
|
payment_hash = sha256(payment_preimage)
|
||||||
info = PaymentInfo(payment_hash, amount_msat, RECEIVED, PR_UNPAID)
|
info = PaymentInfo(payment_hash, amount_msat, RECEIVED, PR_UNPAID)
|
||||||
@@ -2002,16 +1998,12 @@ class LNWallet(LNWorker):
|
|||||||
def calc_routing_hints_for_invoice(self, amount_msat: Optional[int]):
|
def calc_routing_hints_for_invoice(self, amount_msat: Optional[int]):
|
||||||
"""calculate routing hints (BOLT-11 'r' field)"""
|
"""calculate routing hints (BOLT-11 'r' field)"""
|
||||||
routing_hints = []
|
routing_hints = []
|
||||||
channels = list(self.channels.values())
|
with self.lock:
|
||||||
# do minimal filtering of channels.
|
nodes = self.border_nodes_that_can_receive(amount_msat)
|
||||||
# we include channels that cannot *right now* receive (e.g. peer disconnected or balance insufficient)
|
channels = []
|
||||||
channels = [chan for chan in channels
|
for c in self.channels.values():
|
||||||
if (chan.is_open() and not chan.is_frozen_for_receiving())]
|
if c.node_id in nodes:
|
||||||
# Filter out channels that have very low receive capacity compared to invoice amt.
|
channels.append(c)
|
||||||
# Even with MPP, below a certain threshold, including these channels probably
|
|
||||||
# hurts more than help, as they lead to many failed attempts for the sender.
|
|
||||||
channels = [chan for chan in channels
|
|
||||||
if chan.available_to_spend(REMOTE) > (amount_msat or 0) * 0.05]
|
|
||||||
# cap max channels to include to keep QR code reasonably scannable
|
# cap max channels to include to keep QR code reasonably scannable
|
||||||
channels = sorted(channels, key=lambda chan: (not chan.is_active(), -chan.available_to_spend(REMOTE)))
|
channels = sorted(channels, key=lambda chan: (not chan.is_active(), -chan.available_to_spend(REMOTE)))
|
||||||
channels = channels[:15]
|
channels = channels[:15]
|
||||||
@@ -2047,7 +2039,12 @@ class LNWallet(LNWorker):
|
|||||||
fee_base_msat,
|
fee_base_msat,
|
||||||
fee_proportional_millionths,
|
fee_proportional_millionths,
|
||||||
cltv_expiry_delta)]))
|
cltv_expiry_delta)]))
|
||||||
return routing_hints
|
trampoline_hints = []
|
||||||
|
for r in routing_hints:
|
||||||
|
node_id, short_channel_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta = r[1][0]
|
||||||
|
if len(r[1])== 1 and self.is_trampoline_peer(node_id):
|
||||||
|
trampoline_hints.append(('t', (node_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta)))
|
||||||
|
return routing_hints, trampoline_hints
|
||||||
|
|
||||||
def delete_payment(self, payment_hash_hex: str):
|
def delete_payment(self, payment_hash_hex: str):
|
||||||
try:
|
try:
|
||||||
@@ -2085,14 +2082,38 @@ class LNWallet(LNWorker):
|
|||||||
can_send_minus_fees = max(0, can_send_minus_fees)
|
can_send_minus_fees = max(0, can_send_minus_fees)
|
||||||
return Decimal(can_send_minus_fees) / 1000
|
return Decimal(can_send_minus_fees) / 1000
|
||||||
|
|
||||||
def num_sats_can_receive(self) -> Decimal:
|
def border_nodes_that_can_receive(self, amount_msat=None):
|
||||||
|
# if amount_msat is None, use the max amount we can receive
|
||||||
|
#
|
||||||
|
# Filter out nodes that have very low receive capacity compared to invoice amt.
|
||||||
|
# Even with MPP, below a certain threshold, including these channels probably
|
||||||
|
# hurts more than help, as they lead to many failed attempts for the sender.
|
||||||
|
#
|
||||||
|
# We condider nodes instead of channels because both non-strict forwardring
|
||||||
|
# and trampoline end-to-end payments allow it
|
||||||
|
nodes_that_can_receive = defaultdict(int)
|
||||||
with self.lock:
|
with self.lock:
|
||||||
channels = [
|
for c in self.channels.values():
|
||||||
c for c in self.channels.values()
|
if not c.is_active() or c.is_frozen_for_receiving():
|
||||||
if c.is_active() and not c.is_frozen_for_receiving()
|
continue
|
||||||
]
|
nodes_that_can_receive[c.node_id] += c.available_to_spend(REMOTE)
|
||||||
can_receive = sum([c.available_to_spend(REMOTE) for c in channels]) if channels else 0
|
while True:
|
||||||
return Decimal(can_receive) / 1000
|
max_can_receive = sum(nodes_that_can_receive.values())
|
||||||
|
receive_amount = amount_msat or max_can_receive
|
||||||
|
items = sorted(list(nodes_that_can_receive.items()), key=operator.itemgetter(1))
|
||||||
|
for node_id, v in items:
|
||||||
|
if v < receive_amount * MPP_RECEIVE_CUTOFF:
|
||||||
|
nodes_that_can_receive.pop(node_id)
|
||||||
|
# break immediately because max_can_receive needs to be recomputed
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return nodes_that_can_receive
|
||||||
|
|
||||||
|
def num_sats_can_receive(self) -> Decimal:
|
||||||
|
can_receive_nodes = self.border_nodes_that_can_receive(None)
|
||||||
|
can_receive_msat = sum(can_receive_nodes.values())
|
||||||
|
return Decimal(can_receive_msat) / 1000
|
||||||
|
|
||||||
def num_sats_can_receive_no_mpp(self) -> Decimal:
|
def num_sats_can_receive_no_mpp(self) -> Decimal:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
|
|||||||
get_channel_by_id = LNWallet.get_channel_by_id
|
get_channel_by_id = LNWallet.get_channel_by_id
|
||||||
channels_for_peer = LNWallet.channels_for_peer
|
channels_for_peer = LNWallet.channels_for_peer
|
||||||
calc_routing_hints_for_invoice = LNWallet.calc_routing_hints_for_invoice
|
calc_routing_hints_for_invoice = LNWallet.calc_routing_hints_for_invoice
|
||||||
|
border_nodes_that_can_receive = LNWallet.border_nodes_that_can_receive
|
||||||
handle_error_code_from_failed_htlc = LNWallet.handle_error_code_from_failed_htlc
|
handle_error_code_from_failed_htlc = LNWallet.handle_error_code_from_failed_htlc
|
||||||
is_trampoline_peer = LNWallet.is_trampoline_peer
|
is_trampoline_peer = LNWallet.is_trampoline_peer
|
||||||
wait_for_received_pending_htlcs_to_get_removed = LNWallet.wait_for_received_pending_htlcs_to_get_removed
|
wait_for_received_pending_htlcs_to_get_removed = LNWallet.wait_for_received_pending_htlcs_to_get_removed
|
||||||
@@ -485,14 +486,10 @@ class TestPeer(TestCaseForTestnet):
|
|||||||
w2.save_preimage(RHASH, payment_preimage)
|
w2.save_preimage(RHASH, payment_preimage)
|
||||||
w2.save_payment_info(info)
|
w2.save_payment_info(info)
|
||||||
if include_routing_hints:
|
if include_routing_hints:
|
||||||
routing_hints = w2.calc_routing_hints_for_invoice(amount_msat)
|
routing_hints, trampoline_hints = w2.calc_routing_hints_for_invoice(amount_msat)
|
||||||
else:
|
else:
|
||||||
routing_hints = []
|
routing_hints = []
|
||||||
trampoline_hints = []
|
trampoline_hints = []
|
||||||
for r in routing_hints:
|
|
||||||
node_id, short_channel_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta = r[1][0]
|
|
||||||
if len(r[1])== 1 and w2.is_trampoline_peer(node_id):
|
|
||||||
trampoline_hints.append(('t', (node_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta)))
|
|
||||||
invoice_features = w2.features.for_invoice()
|
invoice_features = w2.features.for_invoice()
|
||||||
if invoice_features.supports(LnFeatures.PAYMENT_SECRET_OPT):
|
if invoice_features.supports(LnFeatures.PAYMENT_SECRET_OPT):
|
||||||
payment_secret = derive_payment_secret_from_payment_preimage(payment_preimage)
|
payment_secret = derive_payment_secret_from_payment_preimage(payment_preimage)
|
||||||
|
|||||||
Reference in New Issue
Block a user