1
0

Merge pull request #7292 from bitromortac/2105-inflight-htlcs

lnrouter: add inflight htlcs to liquidity hints
This commit is contained in:
ghost43
2021-06-10 16:54:48 +00:00
committed by GitHub
4 changed files with 176 additions and 76 deletions

View File

@@ -192,6 +192,20 @@ class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
self.channel_db.stop()
await self.channel_db.stopped_event.wait()
async def create_routes_from_invoice(self, amount_msat: int, decoded_invoice: LnAddr, *, full_path=None):
return [r async for r in self.create_routes_for_payment(
amount_msat=amount_msat,
final_total_msat=amount_msat,
invoice_pubkey=decoded_invoice.pubkey.serialize(),
min_cltv_expiry=decoded_invoice.get_min_final_cltv_expiry(),
r_tags=decoded_invoice.get_routing_info('r'),
invoice_features=decoded_invoice.get_features(),
trampoline_fee_level=0,
use_two_trampolines=False,
payment_hash=decoded_invoice.paymenthash,
payment_secret=decoded_invoice.payment_secret,
full_path=full_path)]
get_payments = LNWallet.get_payments
get_payment_info = LNWallet.get_payment_info
save_payment_info = LNWallet.save_payment_info
@@ -206,7 +220,6 @@ class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
get_preimage = LNWallet.get_preimage
create_route_for_payment = LNWallet.create_route_for_payment
create_routes_for_payment = LNWallet.create_routes_for_payment
create_routes_from_invoice = LNWallet.create_routes_from_invoice
_check_invoice = staticmethod(LNWallet._check_invoice)
pay_to_route = LNWallet.pay_to_route
pay_to_node = LNWallet.pay_to_node
@@ -598,7 +611,7 @@ class TestPeer(TestCaseForTestnet):
q2 = w2.sent_htlcs[lnaddr1.paymenthash]
# alice sends htlc BUT NOT COMMITMENT_SIGNED
p1.maybe_send_commitment = lambda x: None
route1 = w1.create_routes_from_invoice(lnaddr2.get_amount_msat(), decoded_invoice=lnaddr2)[0][0]
route1 = (await w1.create_routes_from_invoice(lnaddr2.get_amount_msat(), decoded_invoice=lnaddr2))[0][0]
amount_msat = lnaddr2.get_amount_msat()
await w1.pay_to_route(
route=route1,
@@ -612,7 +625,7 @@ class TestPeer(TestCaseForTestnet):
p1.maybe_send_commitment = _maybe_send_commitment1
# bob sends htlc BUT NOT COMMITMENT_SIGNED
p2.maybe_send_commitment = lambda x: None
route2 = w2.create_routes_from_invoice(lnaddr1.get_amount_msat(), decoded_invoice=lnaddr1)[0][0]
route2 = (await w2.create_routes_from_invoice(lnaddr1.get_amount_msat(), decoded_invoice=lnaddr1))[0][0]
amount_msat = lnaddr1.get_amount_msat()
await w2.pay_to_route(
route=route2,
@@ -982,14 +995,14 @@ class TestPeer(TestCaseForTestnet):
await asyncio.wait_for(p1.initialized, 1)
await asyncio.wait_for(p2.initialized, 1)
# alice sends htlc
route, amount_msat = w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr)[0][0:2]
htlc = p1.pay(route=route,
chan=alice_channel,
amount_msat=lnaddr.get_amount_msat(),
total_msat=lnaddr.get_amount_msat(),
payment_hash=lnaddr.paymenthash,
min_final_cltv_expiry=lnaddr.get_min_final_cltv_expiry(),
payment_secret=lnaddr.payment_secret)
route, amount_msat = (await w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr))[0][0:2]
p1.pay(route=route,
chan=alice_channel,
amount_msat=lnaddr.get_amount_msat(),
total_msat=lnaddr.get_amount_msat(),
payment_hash=lnaddr.paymenthash,
min_final_cltv_expiry=lnaddr.get_min_final_cltv_expiry(),
payment_secret=lnaddr.payment_secret)
# alice closes
await p1.close_channel(alice_channel.channel_id)
gath.cancel()
@@ -1078,7 +1091,7 @@ class TestPeer(TestCaseForTestnet):
lnaddr, pay_req = run(self.prepare_invoice(w2))
lnaddr = w1._check_invoice(pay_req)
route, amount_msat = w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr)[0][0:2]
route, amount_msat = run(w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr))[0][0:2]
assert amount_msat == lnaddr.get_amount_msat()
run(w1.force_close_channel(alice_channel.channel_id))
@@ -1086,7 +1099,7 @@ class TestPeer(TestCaseForTestnet):
assert q1.qsize() == 1
with self.assertRaises(NoPathFound) as e:
w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr)
run(w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr))
peer = w1.peers[route[0].node_id]
# AssertionError is ok since we shouldn't use old routes, and the

View File

@@ -28,12 +28,18 @@ def node(character: str) -> bytes:
class Test_LNRouter(TestCaseForTestnet):
cdb = None
def setUp(self):
super().setUp()
self.asyncio_loop, self._stop_loop, self._loop_thread = create_and_start_event_loop()
self.config = SimpleConfig({'electrum_path': self.electrum_path})
def tearDown(self):
# if the test called prepare_graph(), channeldb needs to be cleaned up
if self.cdb:
self.cdb.stop()
asyncio.run_coroutine_threadsafe(self.cdb.stopped_event.wait(), self.asyncio_loop).result()
self.asyncio_loop.call_soon_threadsafe(self._stop_loop.set_result, 1)
self._loop_thread.join(timeout=1)
super().tearDown()
@@ -151,10 +157,7 @@ class Test_LNRouter(TestCaseForTestnet):
self.assertEqual(node('b'), route[0].node_id)
self.assertEqual(channel(3), route[0].short_channel_id)
self.cdb.stop()
asyncio.run_coroutine_threadsafe(self.cdb.stopped_event.wait(), self.asyncio_loop).result()
def test_find_path_liquidity_hints_failure(self):
def test_find_path_liquidity_hints(self):
self.prepare_graph()
amount_to_send = 100000
@@ -197,7 +200,7 @@ class Test_LNRouter(TestCaseForTestnet):
assume success over channel 4, D -> C
A -3-> B |-2-> E
A -6-> D |-5-> E
A -6-> D -4-> C -7-> E <= chosen path
A -6-> D -4-> C -7-> E <= smaller penalty: chosen path
A -3-> B -1-> C -7-> E
A -6-> D -4-> C -1-> B |-2-> E
A -3-> B -1-> C -4-> D |-5-> E
@@ -211,8 +214,43 @@ class Test_LNRouter(TestCaseForTestnet):
self.assertEqual(channel(4), path[1].short_channel_id)
self.assertEqual(channel(7), path[2].short_channel_id)
self.cdb.stop()
asyncio.run_coroutine_threadsafe(self.cdb.stopped_event.wait(), self.asyncio_loop).result()
def test_find_path_liquidity_hints_inflight_htlcs(self):
self.prepare_graph()
amount_to_send = 100000
"""
add inflight htlc to channel 2, B -> E
A -3-> B -2(1)-> E
A -6-> D -5-> E <= chosen path
A -6-> D -4-> C -7-> E
A -3-> B -1-> C -7-> E
A -6-> D -4-> C -1-> B -2-> E
A -3-> B -1-> C -4-> D -5-> E
"""
self.path_finder.liquidity_hints.add_htlc(node('b'), node('e'), channel(2))
path = self.path_finder.find_path_for_payment(
nodeA=node('a'),
nodeB=node('e'),
invoice_amount_msat=amount_to_send)
self.assertEqual(channel(6), path[0].short_channel_id)
self.assertEqual(channel(5), path[1].short_channel_id)
"""
remove inflight htlc from channel 2, B -> E
A -3-> B -2(0)-> E <= chosen path
A -6-> D -5-> E
A -6-> D -4-> C -7-> E
A -3-> B -1-> C -7-> E
A -6-> D -4-> C -1-> B -2-> E
A -3-> B -1-> C -4-> D -5-> E
"""
self.path_finder.liquidity_hints.remove_htlc(node('b'), node('e'), channel(2))
path = self.path_finder.find_path_for_payment(
nodeA=node('a'),
nodeB=node('e'),
invoice_amount_msat=amount_to_send)
self.assertEqual(channel(3), path[0].short_channel_id)
self.assertEqual(channel(2), path[1].short_channel_id)
def test_liquidity_hints(self):
liquidity_hints = LiquidityHintMgr()
@@ -251,6 +289,12 @@ class Test_LNRouter(TestCaseForTestnet):
self.assertEqual(3_000_000, hint.can_send(node_from < node_to))
self.assertEqual(None, hint.cannot_send(node_from < node_to))
# test inflight htlc
liquidity_hints.reset_liquidity_hints()
liquidity_hints.add_htlc(node_from, node_to, channel_id)
liquidity_hints.get_hint(channel_id)
# we have got 600 (attempt) + 600 (inflight) penalty
self.assertEqual(1200, liquidity_hints.penalty(node_from, node_to, channel_id, 1_000_000))
@needs_test_with_all_chacha20_implementations
def test_new_onion_packet_legacy(self):