diff --git a/electrum/tests/test_lnrouter.py b/electrum/tests/test_lnrouter.py index b1db724df..339cda59e 100644 --- a/electrum/tests/test_lnrouter.py +++ b/electrum/tests/test_lnrouter.py @@ -4,6 +4,7 @@ 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) @@ -16,6 +17,14 @@ 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 +37,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 +63,95 @@ 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) - cdb.stop() - asyncio.run_coroutine_threadsafe(cdb.stopped_event.wait(), self.asyncio_loop).result() + self.cdb.stop() + asyncio.run_coroutine_threadsafe(self.cdb.stopped_event.wait(), self.asyncio_loop).result() @needs_test_with_all_chacha20_implementations def test_new_onion_packet_legacy(self):