Merge pull request #7152 from bitromortac/2103-liquidity-hints
Liquidity hints for pathfinding
This commit is contained in:
@@ -33,7 +33,7 @@ from electrum import lnmsg
|
||||
from electrum.logging import console_stderr_handler, Logger
|
||||
from electrum.lnworker import PaymentInfo, RECEIVED
|
||||
from electrum.lnonion import OnionFailureCode
|
||||
from electrum.lnutil import ChannelBlackList, derive_payment_secret_from_payment_preimage
|
||||
from electrum.lnutil import derive_payment_secret_from_payment_preimage
|
||||
from electrum.lnutil import LOCAL, REMOTE
|
||||
from electrum.invoices import PR_PAID, PR_UNPAID
|
||||
|
||||
@@ -66,7 +66,6 @@ class MockNetwork:
|
||||
self.path_finder = LNPathFinder(self.channel_db)
|
||||
self.tx_queue = tx_queue
|
||||
self._blockchain = MockBlockchain()
|
||||
self.channel_blacklist = ChannelBlackList()
|
||||
|
||||
@property
|
||||
def callback_lock(self):
|
||||
@@ -807,7 +806,7 @@ class TestPeer(TestCaseForTestnet):
|
||||
run(f())
|
||||
|
||||
@needs_test_with_all_chacha20_implementations
|
||||
def test_payment_with_temp_channel_failure(self):
|
||||
def test_payment_with_temp_channel_failure_and_liquidty_hints(self):
|
||||
# prepare channels such that a temporary channel failure happens at c->d
|
||||
funds_distribution = {
|
||||
'ac': (200_000_000, 200_000_000), # low fees
|
||||
@@ -815,12 +814,11 @@ class TestPeer(TestCaseForTestnet):
|
||||
'ab': (200_000_000, 200_000_000), # high fees
|
||||
'bd': (200_000_000, 200_000_000), # high fees
|
||||
}
|
||||
# the payment happens in three attempts:
|
||||
# 1. along ac->cd due to low fees with temp channel failure:
|
||||
# the payment happens in two attempts:
|
||||
# 1. along a->c->d due to low fees with temp channel failure:
|
||||
# with chanupd: ORPHANED, private channel update
|
||||
# 2. along ac->cd with temp channel failure:
|
||||
# with chanupd: ORPHANED, private channel update, but already received, channel gets blacklisted
|
||||
# 3. along ab->bd with success
|
||||
# c->d gets a liquidity hint and gets blocked
|
||||
# 2. along a->b->d with success
|
||||
amount_to_pay = 100_000_000
|
||||
graph = self.prepare_chans_and_peers_in_square(funds_distribution)
|
||||
peers = graph.all_peers()
|
||||
@@ -828,9 +826,30 @@ class TestPeer(TestCaseForTestnet):
|
||||
self.assertEqual(PR_UNPAID, graph.w_d.get_payment_status(lnaddr.paymenthash))
|
||||
result, log = await graph.w_a.pay_invoice(pay_req, attempts=3)
|
||||
self.assertTrue(result)
|
||||
self.assertEqual(2, len(log))
|
||||
self.assertEqual(PR_PAID, graph.w_d.get_payment_status(lnaddr.paymenthash))
|
||||
self.assertEqual(OnionFailureCode.TEMPORARY_CHANNEL_FAILURE, log[0].failure_msg.code)
|
||||
self.assertEqual(OnionFailureCode.TEMPORARY_CHANNEL_FAILURE, log[1].failure_msg.code)
|
||||
|
||||
liquidity_hints = graph.w_a.network.path_finder.liquidity_hints
|
||||
pubkey_a = graph.w_a.node_keypair.pubkey
|
||||
pubkey_b = graph.w_b.node_keypair.pubkey
|
||||
pubkey_c = graph.w_c.node_keypair.pubkey
|
||||
pubkey_d = graph.w_d.node_keypair.pubkey
|
||||
# check liquidity hints for failing route:
|
||||
hint_ac = liquidity_hints.get_hint(graph.chan_ac.short_channel_id)
|
||||
hint_cd = liquidity_hints.get_hint(graph.chan_cd.short_channel_id)
|
||||
self.assertEqual(amount_to_pay, hint_ac.can_send(pubkey_a < pubkey_c))
|
||||
self.assertEqual(None, hint_ac.cannot_send(pubkey_a < pubkey_c))
|
||||
self.assertEqual(None, hint_cd.can_send(pubkey_c < pubkey_d))
|
||||
self.assertEqual(amount_to_pay, hint_cd.cannot_send(pubkey_c < pubkey_d))
|
||||
# check liquidity hints for successful route:
|
||||
hint_ab = liquidity_hints.get_hint(graph.chan_ab.short_channel_id)
|
||||
hint_bd = liquidity_hints.get_hint(graph.chan_bd.short_channel_id)
|
||||
self.assertEqual(amount_to_pay, hint_ab.can_send(pubkey_a < pubkey_b))
|
||||
self.assertEqual(None, hint_ab.cannot_send(pubkey_a < pubkey_b))
|
||||
self.assertEqual(amount_to_pay, hint_bd.can_send(pubkey_b < pubkey_d))
|
||||
self.assertEqual(None, hint_bd.cannot_send(pubkey_b < pubkey_d))
|
||||
|
||||
raise PaymentDone()
|
||||
async def f():
|
||||
async with TaskGroup() as group:
|
||||
|
||||
@@ -1,21 +1,31 @@
|
||||
from math import inf
|
||||
import unittest
|
||||
import tempfile
|
||||
import shutil
|
||||
import asyncio
|
||||
|
||||
from electrum.util import bh2u, bfh, create_and_start_event_loop
|
||||
from electrum.lnutil import ShortChannelID
|
||||
from electrum.lnonion import (OnionHopsDataSingle, new_onion_packet,
|
||||
process_onion_packet, _decode_onion_error, decode_onion_error,
|
||||
OnionFailureCode, OnionPacket)
|
||||
from electrum import bitcoin, lnrouter
|
||||
from electrum.constants import BitcoinTestnet
|
||||
from electrum.simple_config import SimpleConfig
|
||||
from electrum.lnrouter import PathEdge
|
||||
from electrum.lnrouter import PathEdge, LiquidityHintMgr, DEFAULT_PENALTY_PROPORTIONAL_MILLIONTH, DEFAULT_PENALTY_BASE_MSAT, fee_for_edge_msat
|
||||
|
||||
from . import TestCaseForTestnet
|
||||
from .test_bitcoin import needs_test_with_all_chacha20_implementations
|
||||
|
||||
|
||||
def channel(number: int) -> ShortChannelID:
|
||||
return ShortChannelID(bfh(format(number, '016x')))
|
||||
|
||||
|
||||
def node(character: str) -> bytes:
|
||||
return b'\x02' + f'{character}'.encode() * 32
|
||||
|
||||
|
||||
class Test_LNRouter(TestCaseForTestnet):
|
||||
|
||||
def setUp(self):
|
||||
@@ -28,7 +38,24 @@ class Test_LNRouter(TestCaseForTestnet):
|
||||
self._loop_thread.join(timeout=1)
|
||||
super().tearDown()
|
||||
|
||||
def test_find_path_for_payment(self):
|
||||
def prepare_graph(self):
|
||||
"""
|
||||
Network topology with channel ids:
|
||||
3
|
||||
A --- B
|
||||
| 2/ |
|
||||
6 | E | 1
|
||||
| /5 \7 |
|
||||
D --- C
|
||||
4
|
||||
valid routes from A -> E:
|
||||
A -3-> B -2-> E
|
||||
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
|
||||
"""
|
||||
class fake_network:
|
||||
config = self.config
|
||||
asyncio_loop = asyncio.get_event_loop()
|
||||
@@ -37,67 +64,193 @@ class Test_LNRouter(TestCaseForTestnet):
|
||||
interface = None
|
||||
fake_network.channel_db = lnrouter.ChannelDB(fake_network())
|
||||
fake_network.channel_db.data_loaded.set()
|
||||
cdb = fake_network.channel_db
|
||||
path_finder = lnrouter.LNPathFinder(cdb)
|
||||
self.assertEqual(cdb.num_channels, 0)
|
||||
cdb.add_channel_announcements({'node_id_1': b'\x02bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 'node_id_2': b'\x02cccccccccccccccccccccccccccccccc',
|
||||
'bitcoin_key_1': b'\x02bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 'bitcoin_key_2': b'\x02cccccccccccccccccccccccccccccccc',
|
||||
'short_channel_id': bfh('0000000000000001'),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''}, trusted=True)
|
||||
self.assertEqual(cdb.num_channels, 1)
|
||||
cdb.add_channel_announcements({'node_id_1': b'\x02bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 'node_id_2': b'\x02eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
||||
'bitcoin_key_1': b'\x02bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 'bitcoin_key_2': b'\x02eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
||||
'short_channel_id': bfh('0000000000000002'),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''}, trusted=True)
|
||||
cdb.add_channel_announcements({'node_id_1': b'\x02aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'node_id_2': b'\x02bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
|
||||
'bitcoin_key_1': b'\x02aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'bitcoin_key_2': b'\x02bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
|
||||
'short_channel_id': bfh('0000000000000003'),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''}, trusted=True)
|
||||
cdb.add_channel_announcements({'node_id_1': b'\x02cccccccccccccccccccccccccccccccc', 'node_id_2': b'\x02dddddddddddddddddddddddddddddddd',
|
||||
'bitcoin_key_1': b'\x02cccccccccccccccccccccccccccccccc', 'bitcoin_key_2': b'\x02dddddddddddddddddddddddddddddddd',
|
||||
'short_channel_id': bfh('0000000000000004'),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''}, trusted=True)
|
||||
cdb.add_channel_announcements({'node_id_1': b'\x02dddddddddddddddddddddddddddddddd', 'node_id_2': b'\x02eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
||||
'bitcoin_key_1': b'\x02dddddddddddddddddddddddddddddddd', 'bitcoin_key_2': b'\x02eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
||||
'short_channel_id': bfh('0000000000000005'),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''}, trusted=True)
|
||||
cdb.add_channel_announcements({'node_id_1': b'\x02aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'node_id_2': b'\x02dddddddddddddddddddddddddddddddd',
|
||||
'bitcoin_key_1': b'\x02aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'bitcoin_key_2': b'\x02dddddddddddddddddddddddddddddddd',
|
||||
'short_channel_id': bfh('0000000000000006'),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''}, trusted=True)
|
||||
self.cdb = fake_network.channel_db
|
||||
self.path_finder = lnrouter.LNPathFinder(self.cdb)
|
||||
self.assertEqual(self.cdb.num_channels, 0)
|
||||
self.cdb.add_channel_announcements({
|
||||
'node_id_1': node('b'), 'node_id_2': node('c'),
|
||||
'bitcoin_key_1': node('b'), 'bitcoin_key_2': node('c'),
|
||||
'short_channel_id': channel(1),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''
|
||||
}, trusted=True)
|
||||
self.assertEqual(self.cdb.num_channels, 1)
|
||||
self.cdb.add_channel_announcements({
|
||||
'node_id_1': node('b'), 'node_id_2': node('e'),
|
||||
'bitcoin_key_1': node('b'), 'bitcoin_key_2': node('e'),
|
||||
'short_channel_id': channel(2),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''
|
||||
}, trusted=True)
|
||||
self.cdb.add_channel_announcements({
|
||||
'node_id_1': node('a'), 'node_id_2': node('b'),
|
||||
'bitcoin_key_1': node('a'), 'bitcoin_key_2': node('b'),
|
||||
'short_channel_id': channel(3),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''
|
||||
}, trusted=True)
|
||||
self.cdb.add_channel_announcements({
|
||||
'node_id_1': node('c'), 'node_id_2': node('d'),
|
||||
'bitcoin_key_1': node('c'), 'bitcoin_key_2': node('d'),
|
||||
'short_channel_id': channel(4),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''
|
||||
}, trusted=True)
|
||||
self.cdb.add_channel_announcements({
|
||||
'node_id_1': node('d'), 'node_id_2': node('e'),
|
||||
'bitcoin_key_1': node('d'), 'bitcoin_key_2': node('e'),
|
||||
'short_channel_id': channel(5),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''
|
||||
}, trusted=True)
|
||||
self.cdb.add_channel_announcements({
|
||||
'node_id_1': node('a'), 'node_id_2': node('d'),
|
||||
'bitcoin_key_1': node('a'), 'bitcoin_key_2': node('d'),
|
||||
'short_channel_id': channel(6),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''
|
||||
}, trusted=True)
|
||||
self.cdb.add_channel_announcements({
|
||||
'node_id_1': node('c'), 'node_id_2': node('e'),
|
||||
'bitcoin_key_1': node('c'), 'bitcoin_key_2': node('e'),
|
||||
'short_channel_id': channel(7),
|
||||
'chain_hash': BitcoinTestnet.rev_genesis_bytes(),
|
||||
'len': 0, 'features': b''
|
||||
}, trusted=True)
|
||||
def add_chan_upd(payload):
|
||||
cdb.add_channel_update(payload, verify=False)
|
||||
add_chan_upd({'short_channel_id': bfh('0000000000000001'), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': bfh('0000000000000001'), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': bfh('0000000000000002'), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 99, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': bfh('0000000000000002'), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': bfh('0000000000000003'), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': bfh('0000000000000003'), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': bfh('0000000000000004'), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': bfh('0000000000000004'), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': bfh('0000000000000005'), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': bfh('0000000000000005'), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 999, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': bfh('0000000000000006'), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 99999999, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': bfh('0000000000000006'), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
path = path_finder.find_path_for_payment(
|
||||
nodeA=b'\x02aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
||||
nodeB=b'\x02eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
||||
invoice_amount_msat=100000)
|
||||
self.assertEqual([PathEdge(start_node=b'\x02aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', end_node=b'\x02bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', short_channel_id=bfh('0000000000000003')),
|
||||
PathEdge(start_node=b'\x02bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', end_node=b'\x02eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', short_channel_id=bfh('0000000000000002')),
|
||||
], path)
|
||||
route = path_finder.create_route_from_path(path)
|
||||
self.assertEqual(b'\x02bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', route[0].node_id)
|
||||
self.assertEqual(bfh('0000000000000003'), route[0].short_channel_id)
|
||||
self.cdb.add_channel_update(payload, verify=False)
|
||||
add_chan_upd({'short_channel_id': channel(1), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(1), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(2), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 99, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(2), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(3), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(3), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(4), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(4), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(5), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(5), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 999, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(6), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 200, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(6), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(7), 'message_flags': b'\x00', 'channel_flags': b'\x00', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
add_chan_upd({'short_channel_id': channel(7), 'message_flags': b'\x00', 'channel_flags': b'\x01', 'cltv_expiry_delta': 10, 'htlc_minimum_msat': 250, 'fee_base_msat': 100, 'fee_proportional_millionths': 150, 'chain_hash': BitcoinTestnet.rev_genesis_bytes(), 'timestamp': 0})
|
||||
|
||||
def test_find_path_for_payment(self):
|
||||
self.prepare_graph()
|
||||
amount_to_send = 100000
|
||||
|
||||
path = self.path_finder.find_path_for_payment(
|
||||
nodeA=node('a'),
|
||||
nodeB=node('e'),
|
||||
invoice_amount_msat=amount_to_send)
|
||||
self.assertEqual([
|
||||
PathEdge(start_node=node('a'), end_node=node('b'), short_channel_id=channel(3)),
|
||||
PathEdge(start_node=node('b'), end_node=node('e'), short_channel_id=channel(2)),
|
||||
], path)
|
||||
|
||||
route = self.path_finder.create_route_from_path(path)
|
||||
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):
|
||||
self.prepare_graph()
|
||||
amount_to_send = 100000
|
||||
|
||||
"""
|
||||
assume failure over channel 2, B -> E
|
||||
A -3-> B |-2-> 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.update_cannot_send(node('b'), node('e'), channel(2), amount_to_send - 1)
|
||||
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)
|
||||
|
||||
"""
|
||||
assume failure over channel 5, D -> E
|
||||
A -3-> B |-2-> E
|
||||
A -6-> D |-5-> E
|
||||
A -6-> D -4-> C -7-> E
|
||||
A -3-> B -1-> C -7-> E <= chosen path
|
||||
A -6-> D -4-> C -1-> B |-2-> E
|
||||
A -3-> B -1-> C -4-> D |-5-> E
|
||||
"""
|
||||
self.path_finder.liquidity_hints.update_cannot_send(node('d'), node('e'), channel(5), amount_to_send - 1)
|
||||
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(1), path[1].short_channel_id)
|
||||
self.assertEqual(channel(7), path[2].short_channel_id)
|
||||
|
||||
"""
|
||||
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 -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.update_can_send(node('d'), node('c'), channel(4), amount_to_send + 1000)
|
||||
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(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_liquidity_hints(self):
|
||||
liquidity_hints = LiquidityHintMgr()
|
||||
node_from = bytes(0)
|
||||
node_to = bytes(1)
|
||||
channel_id = ShortChannelID.from_components(0, 0, 0)
|
||||
amount_to_send = 1_000_000
|
||||
|
||||
# check default penalty
|
||||
self.assertEqual(
|
||||
fee_for_edge_msat(amount_to_send, DEFAULT_PENALTY_BASE_MSAT, DEFAULT_PENALTY_PROPORTIONAL_MILLIONTH),
|
||||
liquidity_hints.penalty(node_from, node_to, channel_id, amount_to_send)
|
||||
)
|
||||
liquidity_hints.update_can_send(node_from, node_to, channel_id, 1_000_000)
|
||||
liquidity_hints.update_cannot_send(node_from, node_to, channel_id, 2_000_000)
|
||||
hint = liquidity_hints.get_hint(channel_id)
|
||||
self.assertEqual(1_000_000, hint.can_send(node_from < node_to))
|
||||
self.assertEqual(None, hint.cannot_send(node_to < node_from))
|
||||
self.assertEqual(2_000_000, hint.cannot_send(node_from < node_to))
|
||||
# the can_send backward hint is set automatically
|
||||
self.assertEqual(2_000_000, hint.can_send(node_to < node_from))
|
||||
|
||||
# check penalties
|
||||
self.assertEqual(0., liquidity_hints.penalty(node_from, node_to, channel_id, 1_000_000))
|
||||
self.assertEqual(650, liquidity_hints.penalty(node_from, node_to, channel_id, 1_500_000))
|
||||
self.assertEqual(inf, liquidity_hints.penalty(node_from, node_to, channel_id, 2_000_000))
|
||||
|
||||
# test that we don't overwrite significant info with less significant info
|
||||
liquidity_hints.update_can_send(node_from, node_to, channel_id, 500_000)
|
||||
hint = liquidity_hints.get_hint(channel_id)
|
||||
self.assertEqual(1_000_000, hint.can_send(node_from < node_to))
|
||||
|
||||
# test case when can_send > cannot_send
|
||||
liquidity_hints.update_can_send(node_from, node_to, channel_id, 3_000_000)
|
||||
hint = liquidity_hints.get_hint(channel_id)
|
||||
self.assertEqual(3_000_000, hint.can_send(node_from < node_to))
|
||||
self.assertEqual(None, hint.cannot_send(node_from < node_to))
|
||||
|
||||
cdb.stop()
|
||||
asyncio.run_coroutine_threadsafe(cdb.stopped_event.wait(), self.asyncio_loop).result()
|
||||
|
||||
@needs_test_with_all_chacha20_implementations
|
||||
def test_new_onion_packet_legacy(self):
|
||||
|
||||
Reference in New Issue
Block a user