URI: 
       ttest_lnpeer.py - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
       ttest_lnpeer.py (46112B)
       ---
            1 import asyncio
            2 import tempfile
            3 from decimal import Decimal
            4 import os
            5 from contextlib import contextmanager
            6 from collections import defaultdict
            7 import logging
            8 import concurrent
            9 from concurrent import futures
           10 import unittest
           11 from typing import Iterable, NamedTuple, Tuple, List
           12 
           13 from aiorpcx import TaskGroup, timeout_after, TaskTimeout
           14 
           15 from electrum import bitcoin
           16 from electrum import constants
           17 from electrum.network import Network
           18 from electrum.ecc import ECPrivkey
           19 from electrum import simple_config, lnutil
           20 from electrum.lnaddr import lnencode, LnAddr, lndecode
           21 from electrum.bitcoin import COIN, sha256
           22 from electrum.util import bh2u, create_and_start_event_loop, NetworkRetryManager, bfh
           23 from electrum.lnpeer import Peer, UpfrontShutdownScriptViolation
           24 from electrum.lnutil import LNPeerAddr, Keypair, privkey_to_pubkey
           25 from electrum.lnutil import LightningPeerConnectionClosed, RemoteMisbehaving
           26 from electrum.lnutil import PaymentFailure, LnFeatures, HTLCOwner
           27 from electrum.lnchannel import ChannelState, PeerState, Channel
           28 from electrum.lnrouter import LNPathFinder, PathEdge, LNPathInconsistent
           29 from electrum.channel_db import ChannelDB
           30 from electrum.lnworker import LNWallet, NoPathFound
           31 from electrum.lnmsg import encode_msg, decode_msg
           32 from electrum.logging import console_stderr_handler, Logger
           33 from electrum.lnworker import PaymentInfo, RECEIVED
           34 from electrum.lnonion import OnionFailureCode
           35 from electrum.lnutil import ChannelBlackList, derive_payment_secret_from_payment_preimage
           36 from electrum.lnutil import LOCAL, REMOTE
           37 from electrum.invoices import PR_PAID, PR_UNPAID
           38 
           39 from .test_lnchannel import create_test_channels
           40 from .test_bitcoin import needs_test_with_all_chacha20_implementations
           41 from . import ElectrumTestCase
           42 
           43 def keypair():
           44     priv = ECPrivkey.generate_random_key().get_secret_bytes()
           45     k1 = Keypair(
           46             pubkey=privkey_to_pubkey(priv),
           47             privkey=priv)
           48     return k1
           49 
           50 @contextmanager
           51 def noop_lock():
           52     yield
           53 
           54 class MockNetwork:
           55     def __init__(self, tx_queue):
           56         self.callbacks = defaultdict(list)
           57         self.lnwatcher = None
           58         self.interface = None
           59         user_config = {}
           60         user_dir = tempfile.mkdtemp(prefix="electrum-lnpeer-test-")
           61         self.config = simple_config.SimpleConfig(user_config, read_user_dir_function=lambda: user_dir)
           62         self.asyncio_loop = asyncio.get_event_loop()
           63         self.channel_db = ChannelDB(self)
           64         self.channel_db.data_loaded.set()
           65         self.path_finder = LNPathFinder(self.channel_db)
           66         self.tx_queue = tx_queue
           67         self._blockchain = MockBlockchain()
           68         self.channel_blacklist = ChannelBlackList()
           69 
           70     @property
           71     def callback_lock(self):
           72         return noop_lock()
           73 
           74     def get_local_height(self):
           75         return 0
           76 
           77     def blockchain(self):
           78         return self._blockchain
           79 
           80     async def broadcast_transaction(self, tx):
           81         if self.tx_queue:
           82             await self.tx_queue.put(tx)
           83 
           84     async def try_broadcasting(self, tx, name):
           85         await self.broadcast_transaction(tx)
           86 
           87 
           88 class MockBlockchain:
           89 
           90     def height(self):
           91         return 0
           92 
           93     def is_tip_stale(self):
           94         return False
           95 
           96 
           97 class MockWallet:
           98 
           99     def set_label(self, x, y):
          100         pass
          101 
          102     def save_db(self):
          103         pass
          104 
          105     def add_transaction(self, tx):
          106         pass
          107 
          108     def is_lightning_backup(self):
          109         return False
          110 
          111     def is_mine(self, addr):
          112         return True
          113 
          114 
          115 class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
          116     MPP_EXPIRY = 2  # HTLC timestamps are cast to int, so this cannot be 1
          117     TIMEOUT_SHUTDOWN_FAIL_PENDING_HTLCS = 0
          118 
          119     def __init__(self, *, local_keypair: Keypair, chans: Iterable['Channel'], tx_queue, name):
          120         self.name = name
          121         Logger.__init__(self)
          122         NetworkRetryManager.__init__(self, max_retry_delay_normal=1, init_retry_delay_normal=1)
          123         self.node_keypair = local_keypair
          124         self.network = MockNetwork(tx_queue)
          125         self.taskgroup = TaskGroup()
          126         self.lnwatcher = None
          127         self.listen_server = None
          128         self._channels = {chan.channel_id: chan for chan in chans}
          129         self.payments = {}
          130         self.logs = defaultdict(list)
          131         self.wallet = MockWallet()
          132         self.features = LnFeatures(0)
          133         self.features |= LnFeatures.OPTION_DATA_LOSS_PROTECT_OPT
          134         self.features |= LnFeatures.OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT
          135         self.features |= LnFeatures.VAR_ONION_OPT
          136         self.features |= LnFeatures.PAYMENT_SECRET_OPT
          137         self.features |= LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT
          138         self.pending_payments = defaultdict(asyncio.Future)
          139         for chan in chans:
          140             chan.lnworker = self
          141         self._peers = {}  # bytes -> Peer
          142         # used in tests
          143         self.enable_htlc_settle = asyncio.Event()
          144         self.enable_htlc_settle.set()
          145         self.enable_htlc_forwarding = asyncio.Event()
          146         self.enable_htlc_forwarding.set()
          147         self.received_mpp_htlcs = dict()
          148         self.sent_htlcs = defaultdict(asyncio.Queue)
          149         self.sent_htlcs_routes = dict()
          150         self.sent_buckets = defaultdict(set)
          151         self.trampoline_forwarding_failures = {}
          152         self.inflight_payments = set()
          153         self.preimages = {}
          154         self.stopping_soon = False
          155 
          156     def get_invoice_status(self, key):
          157         pass
          158 
          159     @property
          160     def lock(self):
          161         return noop_lock()
          162 
          163     @property
          164     def channel_db(self):
          165         return self.network.channel_db if self.network else None
          166 
          167     @property
          168     def channels(self):
          169         return self._channels
          170 
          171     @property
          172     def peers(self):
          173         return self._peers
          174 
          175     def get_channel_by_short_id(self, short_channel_id):
          176         with self.lock:
          177             for chan in self._channels.values():
          178                 if chan.short_channel_id == short_channel_id:
          179                     return chan
          180 
          181     def channel_state_changed(self, chan):
          182         pass
          183 
          184     def save_channel(self, chan):
          185         print("Ignoring channel save")
          186 
          187     def diagnostic_name(self):
          188         return self.name
          189 
          190     async def stop(self):
          191         await LNWallet.stop(self)
          192         if self.channel_db:
          193             self.channel_db.stop()
          194             await self.channel_db.stopped_event.wait()
          195 
          196     get_payments = LNWallet.get_payments
          197     get_payment_info = LNWallet.get_payment_info
          198     save_payment_info = LNWallet.save_payment_info
          199     set_invoice_status = LNWallet.set_invoice_status
          200     set_request_status = LNWallet.set_request_status
          201     set_payment_status = LNWallet.set_payment_status
          202     get_payment_status = LNWallet.get_payment_status
          203     check_received_mpp_htlc = LNWallet.check_received_mpp_htlc
          204     htlc_fulfilled = LNWallet.htlc_fulfilled
          205     htlc_failed = LNWallet.htlc_failed
          206     save_preimage = LNWallet.save_preimage
          207     get_preimage = LNWallet.get_preimage
          208     create_route_for_payment = LNWallet.create_route_for_payment
          209     create_routes_for_payment = LNWallet.create_routes_for_payment
          210     create_routes_from_invoice = LNWallet.create_routes_from_invoice
          211     _check_invoice = staticmethod(LNWallet._check_invoice)
          212     pay_to_route = LNWallet.pay_to_route
          213     pay_to_node = LNWallet.pay_to_node
          214     pay_invoice = LNWallet.pay_invoice
          215     force_close_channel = LNWallet.force_close_channel
          216     try_force_closing = LNWallet.try_force_closing
          217     get_first_timestamp = lambda self: 0
          218     on_peer_successfully_established = LNWallet.on_peer_successfully_established
          219     get_channel_by_id = LNWallet.get_channel_by_id
          220     channels_for_peer = LNWallet.channels_for_peer
          221     _calc_routing_hints_for_invoice = LNWallet._calc_routing_hints_for_invoice
          222     handle_error_code_from_failed_htlc = LNWallet.handle_error_code_from_failed_htlc
          223     is_trampoline_peer = LNWallet.is_trampoline_peer
          224     wait_for_received_pending_htlcs_to_get_removed = LNWallet.wait_for_received_pending_htlcs_to_get_removed
          225     on_proxy_changed = LNWallet.on_proxy_changed
          226 
          227 
          228 class MockTransport:
          229     def __init__(self, name):
          230         self.queue = asyncio.Queue()
          231         self._name = name
          232 
          233     def name(self):
          234         return self._name
          235 
          236     async def read_messages(self):
          237         while True:
          238             yield await self.queue.get()
          239 
          240 class NoFeaturesTransport(MockTransport):
          241     """
          242     This answers the init message with a init that doesn't signal any features.
          243     Used for testing that we require DATA_LOSS_PROTECT.
          244     """
          245     def send_bytes(self, data):
          246         decoded = decode_msg(data)
          247         print(decoded)
          248         if decoded[0] == 'init':
          249             self.queue.put_nowait(encode_msg('init', lflen=1, gflen=1, localfeatures=b"\x00", globalfeatures=b"\x00"))
          250 
          251 class PutIntoOthersQueueTransport(MockTransport):
          252     def __init__(self, keypair, name):
          253         super().__init__(name)
          254         self.other_mock_transport = None
          255         self.privkey = keypair.privkey
          256 
          257     def send_bytes(self, data):
          258         self.other_mock_transport.queue.put_nowait(data)
          259 
          260 def transport_pair(k1, k2, name1, name2):
          261     t1 = PutIntoOthersQueueTransport(k1, name2)
          262     t2 = PutIntoOthersQueueTransport(k2, name1)
          263     t1.other_mock_transport = t2
          264     t2.other_mock_transport = t1
          265     return t1, t2
          266 
          267 
          268 class SquareGraph(NamedTuple):
          269     #                A
          270     #     high fee /   \ low fee
          271     #             B     C
          272     #     high fee \   / low fee
          273     #                D
          274     w_a: MockLNWallet
          275     w_b: MockLNWallet
          276     w_c: MockLNWallet
          277     w_d: MockLNWallet
          278     peer_ab: Peer
          279     peer_ac: Peer
          280     peer_ba: Peer
          281     peer_bd: Peer
          282     peer_ca: Peer
          283     peer_cd: Peer
          284     peer_db: Peer
          285     peer_dc: Peer
          286     chan_ab: Channel
          287     chan_ac: Channel
          288     chan_ba: Channel
          289     chan_bd: Channel
          290     chan_ca: Channel
          291     chan_cd: Channel
          292     chan_db: Channel
          293     chan_dc: Channel
          294 
          295     def all_peers(self) -> Iterable[Peer]:
          296         return self.peer_ab, self.peer_ac, self.peer_ba, self.peer_bd, self.peer_ca, self.peer_cd, self.peer_db, self.peer_dc
          297 
          298     def all_lnworkers(self) -> Iterable[MockLNWallet]:
          299         return self.w_a, self.w_b, self.w_c, self.w_d
          300 
          301 
          302 class PaymentDone(Exception): pass
          303 class TestSuccess(Exception): pass
          304 
          305 
          306 class TestPeer(ElectrumTestCase):
          307 
          308     @classmethod
          309     def setUpClass(cls):
          310         super().setUpClass()
          311         console_stderr_handler.setLevel(logging.DEBUG)
          312 
          313     def setUp(self):
          314         super().setUp()
          315         self.asyncio_loop, self._stop_loop, self._loop_thread = create_and_start_event_loop()
          316         self._lnworkers_created = []  # type: List[MockLNWallet]
          317 
          318     def tearDown(self):
          319         async def cleanup_lnworkers():
          320             async with TaskGroup() as group:
          321                 for lnworker in self._lnworkers_created:
          322                     await group.spawn(lnworker.stop())
          323             self._lnworkers_created.clear()
          324         run(cleanup_lnworkers())
          325 
          326         self.asyncio_loop.call_soon_threadsafe(self._stop_loop.set_result, 1)
          327         self._loop_thread.join(timeout=1)
          328         super().tearDown()
          329 
          330     def prepare_peers(self, alice_channel, bob_channel):
          331         k1, k2 = keypair(), keypair()
          332         alice_channel.node_id = k2.pubkey
          333         bob_channel.node_id = k1.pubkey
          334         t1, t2 = transport_pair(k1, k2, alice_channel.name, bob_channel.name)
          335         q1, q2 = asyncio.Queue(), asyncio.Queue()
          336         w1 = MockLNWallet(local_keypair=k1, chans=[alice_channel], tx_queue=q1, name=bob_channel.name)
          337         w2 = MockLNWallet(local_keypair=k2, chans=[bob_channel], tx_queue=q2, name=alice_channel.name)
          338         self._lnworkers_created.extend([w1, w2])
          339         p1 = Peer(w1, k2.pubkey, t1)
          340         p2 = Peer(w2, k1.pubkey, t2)
          341         w1._peers[p1.pubkey] = p1
          342         w2._peers[p2.pubkey] = p2
          343         # mark_open won't work if state is already OPEN.
          344         # so set it to FUNDED
          345         alice_channel._state = ChannelState.FUNDED
          346         bob_channel._state = ChannelState.FUNDED
          347         # this populates the channel graph:
          348         p1.mark_open(alice_channel)
          349         p2.mark_open(bob_channel)
          350         return p1, p2, w1, w2, q1, q2
          351 
          352     def prepare_chans_and_peers_in_square(self) -> SquareGraph:
          353         key_a, key_b, key_c, key_d = [keypair() for i in range(4)]
          354         chan_ab, chan_ba = create_test_channels(alice_name="alice", bob_name="bob", alice_pubkey=key_a.pubkey, bob_pubkey=key_b.pubkey)
          355         chan_ac, chan_ca = create_test_channels(alice_name="alice", bob_name="carol", alice_pubkey=key_a.pubkey, bob_pubkey=key_c.pubkey)
          356         chan_bd, chan_db = create_test_channels(alice_name="bob", bob_name="dave", alice_pubkey=key_b.pubkey, bob_pubkey=key_d.pubkey)
          357         chan_cd, chan_dc = create_test_channels(alice_name="carol", bob_name="dave", alice_pubkey=key_c.pubkey, bob_pubkey=key_d.pubkey)
          358         trans_ab, trans_ba = transport_pair(key_a, key_b, chan_ab.name, chan_ba.name)
          359         trans_ac, trans_ca = transport_pair(key_a, key_c, chan_ac.name, chan_ca.name)
          360         trans_bd, trans_db = transport_pair(key_b, key_d, chan_bd.name, chan_db.name)
          361         trans_cd, trans_dc = transport_pair(key_c, key_d, chan_cd.name, chan_dc.name)
          362         txq_a, txq_b, txq_c, txq_d = [asyncio.Queue() for i in range(4)]
          363         w_a = MockLNWallet(local_keypair=key_a, chans=[chan_ab, chan_ac], tx_queue=txq_a, name="alice")
          364         w_b = MockLNWallet(local_keypair=key_b, chans=[chan_ba, chan_bd], tx_queue=txq_b, name="bob")
          365         w_c = MockLNWallet(local_keypair=key_c, chans=[chan_ca, chan_cd], tx_queue=txq_c, name="carol")
          366         w_d = MockLNWallet(local_keypair=key_d, chans=[chan_db, chan_dc], tx_queue=txq_d, name="dave")
          367         self._lnworkers_created.extend([w_a, w_b, w_c, w_d])
          368         peer_ab = Peer(w_a, key_b.pubkey, trans_ab)
          369         peer_ac = Peer(w_a, key_c.pubkey, trans_ac)
          370         peer_ba = Peer(w_b, key_a.pubkey, trans_ba)
          371         peer_bd = Peer(w_b, key_d.pubkey, trans_bd)
          372         peer_ca = Peer(w_c, key_a.pubkey, trans_ca)
          373         peer_cd = Peer(w_c, key_d.pubkey, trans_cd)
          374         peer_db = Peer(w_d, key_b.pubkey, trans_db)
          375         peer_dc = Peer(w_d, key_c.pubkey, trans_dc)
          376         w_a._peers[peer_ab.pubkey] = peer_ab
          377         w_a._peers[peer_ac.pubkey] = peer_ac
          378         w_b._peers[peer_ba.pubkey] = peer_ba
          379         w_b._peers[peer_bd.pubkey] = peer_bd
          380         w_c._peers[peer_ca.pubkey] = peer_ca
          381         w_c._peers[peer_cd.pubkey] = peer_cd
          382         w_d._peers[peer_db.pubkey] = peer_db
          383         w_d._peers[peer_dc.pubkey] = peer_dc
          384 
          385         w_b.network.config.set_key('lightning_forward_payments', True)
          386         w_c.network.config.set_key('lightning_forward_payments', True)
          387 
          388         # forwarding fees, etc
          389         chan_ab.forwarding_fee_proportional_millionths *= 500
          390         chan_ab.forwarding_fee_base_msat *= 500
          391         chan_ba.forwarding_fee_proportional_millionths *= 500
          392         chan_ba.forwarding_fee_base_msat *= 500
          393         chan_bd.forwarding_fee_proportional_millionths *= 500
          394         chan_bd.forwarding_fee_base_msat *= 500
          395         chan_db.forwarding_fee_proportional_millionths *= 500
          396         chan_db.forwarding_fee_base_msat *= 500
          397 
          398         # mark_open won't work if state is already OPEN.
          399         # so set it to FUNDED
          400         for chan in [chan_ab, chan_ac, chan_ba, chan_bd, chan_ca, chan_cd, chan_db, chan_dc]:
          401             chan._state = ChannelState.FUNDED
          402         # this populates the channel graph:
          403         peer_ab.mark_open(chan_ab)
          404         peer_ac.mark_open(chan_ac)
          405         peer_ba.mark_open(chan_ba)
          406         peer_bd.mark_open(chan_bd)
          407         peer_ca.mark_open(chan_ca)
          408         peer_cd.mark_open(chan_cd)
          409         peer_db.mark_open(chan_db)
          410         peer_dc.mark_open(chan_dc)
          411         return SquareGraph(
          412             w_a=w_a,
          413             w_b=w_b,
          414             w_c=w_c,
          415             w_d=w_d,
          416             peer_ab=peer_ab,
          417             peer_ac=peer_ac,
          418             peer_ba=peer_ba,
          419             peer_bd=peer_bd,
          420             peer_ca=peer_ca,
          421             peer_cd=peer_cd,
          422             peer_db=peer_db,
          423             peer_dc=peer_dc,
          424             chan_ab=chan_ab,
          425             chan_ac=chan_ac,
          426             chan_ba=chan_ba,
          427             chan_bd=chan_bd,
          428             chan_ca=chan_ca,
          429             chan_cd=chan_cd,
          430             chan_db=chan_db,
          431             chan_dc=chan_dc,
          432         )
          433 
          434     @staticmethod
          435     async def prepare_invoice(
          436             w2: MockLNWallet,  # receiver
          437             *,
          438             amount_msat=100_000_000,
          439             include_routing_hints=False,
          440     ) -> Tuple[LnAddr, str]:
          441         amount_btc = amount_msat/Decimal(COIN*1000)
          442         payment_preimage = os.urandom(32)
          443         RHASH = sha256(payment_preimage)
          444         info = PaymentInfo(RHASH, amount_msat, RECEIVED, PR_UNPAID)
          445         w2.save_preimage(RHASH, payment_preimage)
          446         w2.save_payment_info(info)
          447         if include_routing_hints:
          448             routing_hints = await w2._calc_routing_hints_for_invoice(amount_msat)
          449         else:
          450             routing_hints = []
          451         trampoline_hints = []
          452         for r in routing_hints:
          453             node_id, short_channel_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta = r[1][0]
          454             if len(r[1])== 1 and w2.is_trampoline_peer(node_id):
          455                 trampoline_hints.append(('t', (node_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta)))
          456         invoice_features = w2.features.for_invoice()
          457         if invoice_features.supports(LnFeatures.PAYMENT_SECRET_OPT):
          458             payment_secret = derive_payment_secret_from_payment_preimage(payment_preimage)
          459         else:
          460             payment_secret = None
          461         lnaddr1 = LnAddr(
          462                     paymenthash=RHASH,
          463                     amount=amount_btc,
          464                     tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE),
          465                           ('d', 'coffee'),
          466                           ('9', invoice_features),
          467                          ] + routing_hints + trampoline_hints,
          468                     payment_secret=payment_secret,
          469         )
          470         invoice = lnencode(lnaddr1, w2.node_keypair.privkey)
          471         lnaddr2 = lndecode(invoice)  # unlike lnaddr1, this now has a pubkey set
          472         return lnaddr2, invoice
          473 
          474     def test_reestablish(self):
          475         alice_channel, bob_channel = create_test_channels()
          476         p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
          477         for chan in (alice_channel, bob_channel):
          478             chan.peer_state = PeerState.DISCONNECTED
          479         async def reestablish():
          480             await asyncio.gather(
          481                 p1.reestablish_channel(alice_channel),
          482                 p2.reestablish_channel(bob_channel))
          483             self.assertEqual(alice_channel.peer_state, PeerState.GOOD)
          484             self.assertEqual(bob_channel.peer_state, PeerState.GOOD)
          485             gath.cancel()
          486         gath = asyncio.gather(reestablish(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p1.htlc_switch())
          487         async def f():
          488             await gath
          489         with self.assertRaises(concurrent.futures.CancelledError):
          490             run(f())
          491 
          492     @needs_test_with_all_chacha20_implementations
          493     def test_reestablish_with_old_state(self):
          494         random_seed = os.urandom(32)
          495         alice_channel, bob_channel = create_test_channels(random_seed=random_seed)
          496         alice_channel_0, bob_channel_0 = create_test_channels(random_seed=random_seed)  # these are identical
          497         p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
          498         lnaddr, pay_req = run(self.prepare_invoice(w2))
          499         async def pay():
          500             result, log = await w1.pay_invoice(pay_req)
          501             self.assertEqual(result, True)
          502             gath.cancel()
          503         gath = asyncio.gather(pay(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
          504         async def f():
          505             await gath
          506         with self.assertRaises(concurrent.futures.CancelledError):
          507             run(f())
          508 
          509         p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel_0, bob_channel)
          510         for chan in (alice_channel_0, bob_channel):
          511             chan.peer_state = PeerState.DISCONNECTED
          512         async def reestablish():
          513             await asyncio.gather(
          514                 p1.reestablish_channel(alice_channel_0),
          515                 p2.reestablish_channel(bob_channel))
          516             self.assertEqual(alice_channel_0.peer_state, PeerState.BAD)
          517             self.assertEqual(bob_channel._state, ChannelState.FORCE_CLOSING)
          518             # wait so that pending messages are processed
          519             #await asyncio.sleep(1)
          520             gath.cancel()
          521         gath = asyncio.gather(reestablish(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
          522         async def f():
          523             await gath
          524         with self.assertRaises(concurrent.futures.CancelledError):
          525             run(f())
          526 
          527     @needs_test_with_all_chacha20_implementations
          528     def test_payment(self):
          529         """Alice pays Bob a single HTLC via direct channel."""
          530         alice_channel, bob_channel = create_test_channels()
          531         p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
          532         async def pay(lnaddr, pay_req):
          533             self.assertEqual(PR_UNPAID, w2.get_payment_status(lnaddr.paymenthash))
          534             result, log = await w1.pay_invoice(pay_req)
          535             self.assertTrue(result)
          536             self.assertEqual(PR_PAID, w2.get_payment_status(lnaddr.paymenthash))
          537             raise PaymentDone()
          538         async def f():
          539             async with TaskGroup() as group:
          540                 await group.spawn(p1._message_loop())
          541                 await group.spawn(p1.htlc_switch())
          542                 await group.spawn(p2._message_loop())
          543                 await group.spawn(p2.htlc_switch())
          544                 await asyncio.sleep(0.01)
          545                 lnaddr, pay_req = await self.prepare_invoice(w2)
          546                 invoice_features = lnaddr.get_features()
          547                 self.assertFalse(invoice_features.supports(LnFeatures.BASIC_MPP_OPT))
          548                 await group.spawn(pay(lnaddr, pay_req))
          549         with self.assertRaises(PaymentDone):
          550             run(f())
          551 
          552     @needs_test_with_all_chacha20_implementations
          553     def test_payment_race(self):
          554         """Alice and Bob pay each other simultaneously.
          555         They both send 'update_add_htlc' and receive each other's update
          556         before sending 'commitment_signed'. Neither party should fulfill
          557         the respective HTLCs until those are irrevocably committed to.
          558         """
          559         alice_channel, bob_channel = create_test_channels()
          560         p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
          561         async def pay():
          562             await asyncio.wait_for(p1.initialized, 1)
          563             await asyncio.wait_for(p2.initialized, 1)
          564             # prep
          565             _maybe_send_commitment1 = p1.maybe_send_commitment
          566             _maybe_send_commitment2 = p2.maybe_send_commitment
          567             lnaddr2, pay_req2 = await self.prepare_invoice(w2)
          568             lnaddr1, pay_req1 = await self.prepare_invoice(w1)
          569             # create the htlc queues now (side-effecting defaultdict)
          570             q1 = w1.sent_htlcs[lnaddr2.paymenthash]
          571             q2 = w2.sent_htlcs[lnaddr1.paymenthash]
          572             # alice sends htlc BUT NOT COMMITMENT_SIGNED
          573             p1.maybe_send_commitment = lambda x: None
          574             route1 = w1.create_routes_from_invoice(lnaddr2.get_amount_msat(), decoded_invoice=lnaddr2)[0][0]
          575             amount_msat = lnaddr2.get_amount_msat()
          576             await w1.pay_to_route(
          577                 route=route1,
          578                 amount_msat=amount_msat,
          579                 total_msat=amount_msat,
          580                 amount_receiver_msat=amount_msat,
          581                 payment_hash=lnaddr2.paymenthash,
          582                 min_cltv_expiry=lnaddr2.get_min_final_cltv_expiry(),
          583                 payment_secret=lnaddr2.payment_secret,
          584             )
          585             p1.maybe_send_commitment = _maybe_send_commitment1
          586             # bob sends htlc BUT NOT COMMITMENT_SIGNED
          587             p2.maybe_send_commitment = lambda x: None
          588             route2 = w2.create_routes_from_invoice(lnaddr1.get_amount_msat(), decoded_invoice=lnaddr1)[0][0]
          589             amount_msat = lnaddr1.get_amount_msat()
          590             await w2.pay_to_route(
          591                 route=route2,
          592                 amount_msat=amount_msat,
          593                 total_msat=amount_msat,
          594                 amount_receiver_msat=amount_msat,
          595                 payment_hash=lnaddr1.paymenthash,
          596                 min_cltv_expiry=lnaddr1.get_min_final_cltv_expiry(),
          597                 payment_secret=lnaddr1.payment_secret,
          598             )
          599             p2.maybe_send_commitment = _maybe_send_commitment2
          600             # sleep a bit so that they both receive msgs sent so far
          601             await asyncio.sleep(0.2)
          602             # now they both send COMMITMENT_SIGNED
          603             p1.maybe_send_commitment(alice_channel)
          604             p2.maybe_send_commitment(bob_channel)
          605 
          606             htlc_log1 = await q1.get()
          607             assert htlc_log1.success
          608             htlc_log2 = await q2.get()
          609             assert htlc_log2.success
          610             raise PaymentDone()
          611 
          612         async def f():
          613             async with TaskGroup() as group:
          614                 await group.spawn(p1._message_loop())
          615                 await group.spawn(p1.htlc_switch())
          616                 await group.spawn(p2._message_loop())
          617                 await group.spawn(p2.htlc_switch())
          618                 await asyncio.sleep(0.01)
          619                 await group.spawn(pay())
          620         with self.assertRaises(PaymentDone):
          621             run(f())
          622 
          623     #@unittest.skip("too expensive")
          624     #@needs_test_with_all_chacha20_implementations
          625     def test_payments_stresstest(self):
          626         alice_channel, bob_channel = create_test_channels()
          627         p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
          628         alice_init_balance_msat = alice_channel.balance(HTLCOwner.LOCAL)
          629         bob_init_balance_msat = bob_channel.balance(HTLCOwner.LOCAL)
          630         num_payments = 50
          631         payment_value_msat = 10_000_000  # make it large enough so that there are actually HTLCs on the ctx
          632         max_htlcs_in_flight = asyncio.Semaphore(5)
          633         async def single_payment(pay_req):
          634             async with max_htlcs_in_flight:
          635                 await w1.pay_invoice(pay_req)
          636         async def many_payments():
          637             async with TaskGroup() as group:
          638                 pay_reqs_tasks = [await group.spawn(self.prepare_invoice(w2, amount_msat=payment_value_msat))
          639                                   for i in range(num_payments)]
          640             async with TaskGroup() as group:
          641                 for pay_req_task in pay_reqs_tasks:
          642                     lnaddr, pay_req = pay_req_task.result()
          643                     await group.spawn(single_payment(pay_req))
          644             gath.cancel()
          645         gath = asyncio.gather(many_payments(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
          646         async def f():
          647             await gath
          648         with self.assertRaises(concurrent.futures.CancelledError):
          649             run(f())
          650         self.assertEqual(alice_init_balance_msat - num_payments * payment_value_msat, alice_channel.balance(HTLCOwner.LOCAL))
          651         self.assertEqual(alice_init_balance_msat - num_payments * payment_value_msat, bob_channel.balance(HTLCOwner.REMOTE))
          652         self.assertEqual(bob_init_balance_msat + num_payments * payment_value_msat, bob_channel.balance(HTLCOwner.LOCAL))
          653         self.assertEqual(bob_init_balance_msat + num_payments * payment_value_msat, alice_channel.balance(HTLCOwner.REMOTE))
          654 
          655     @needs_test_with_all_chacha20_implementations
          656     def test_payment_multihop(self):
          657         graph = self.prepare_chans_and_peers_in_square()
          658         peers = graph.all_peers()
          659         async def pay(lnaddr, pay_req):
          660             self.assertEqual(PR_UNPAID, graph.w_d.get_payment_status(lnaddr.paymenthash))
          661             result, log = await graph.w_a.pay_invoice(pay_req)
          662             self.assertTrue(result)
          663             self.assertEqual(PR_PAID, graph.w_d.get_payment_status(lnaddr.paymenthash))
          664             raise PaymentDone()
          665         async def f():
          666             async with TaskGroup() as group:
          667                 for peer in peers:
          668                     await group.spawn(peer._message_loop())
          669                     await group.spawn(peer.htlc_switch())
          670                 await asyncio.sleep(0.2)
          671                 lnaddr, pay_req = await self.prepare_invoice(graph.w_d, include_routing_hints=True)
          672                 await group.spawn(pay(lnaddr, pay_req))
          673         with self.assertRaises(PaymentDone):
          674             run(f())
          675 
          676     @needs_test_with_all_chacha20_implementations
          677     def test_payment_multihop_with_preselected_path(self):
          678         graph = self.prepare_chans_and_peers_in_square()
          679         peers = graph.all_peers()
          680         async def pay(pay_req):
          681             with self.subTest(msg="bad path: edges do not chain together"):
          682                 path = [PathEdge(start_node=graph.w_a.node_keypair.pubkey,
          683                                  end_node=graph.w_c.node_keypair.pubkey,
          684                                  short_channel_id=graph.chan_ab.short_channel_id),
          685                         PathEdge(start_node=graph.w_b.node_keypair.pubkey,
          686                                  end_node=graph.w_d.node_keypair.pubkey,
          687                                  short_channel_id=graph.chan_bd.short_channel_id)]
          688                 with self.assertRaises(LNPathInconsistent):
          689                     await graph.w_a.pay_invoice(pay_req, full_path=path)
          690             with self.subTest(msg="bad path: last node id differs from invoice pubkey"):
          691                 path = [PathEdge(start_node=graph.w_a.node_keypair.pubkey,
          692                                  end_node=graph.w_b.node_keypair.pubkey,
          693                                  short_channel_id=graph.chan_ab.short_channel_id)]
          694                 with self.assertRaises(LNPathInconsistent):
          695                     await graph.w_a.pay_invoice(pay_req, full_path=path)
          696             with self.subTest(msg="good path"):
          697                 path = [PathEdge(start_node=graph.w_a.node_keypair.pubkey,
          698                                  end_node=graph.w_b.node_keypair.pubkey,
          699                                  short_channel_id=graph.chan_ab.short_channel_id),
          700                         PathEdge(start_node=graph.w_b.node_keypair.pubkey,
          701                                  end_node=graph.w_d.node_keypair.pubkey,
          702                                  short_channel_id=graph.chan_bd.short_channel_id)]
          703                 result, log = await graph.w_a.pay_invoice(pay_req, full_path=path)
          704                 self.assertTrue(result)
          705                 self.assertEqual(
          706                     [edge.short_channel_id for edge in path],
          707                     [edge.short_channel_id for edge in log[0].route])
          708             raise PaymentDone()
          709         async def f():
          710             async with TaskGroup() as group:
          711                 for peer in peers:
          712                     await group.spawn(peer._message_loop())
          713                     await group.spawn(peer.htlc_switch())
          714                 await asyncio.sleep(0.2)
          715                 lnaddr, pay_req = await self.prepare_invoice(graph.w_d, include_routing_hints=True)
          716                 await group.spawn(pay(pay_req))
          717         with self.assertRaises(PaymentDone):
          718             run(f())
          719 
          720     @needs_test_with_all_chacha20_implementations
          721     def test_payment_multihop_temp_node_failure(self):
          722         graph = self.prepare_chans_and_peers_in_square()
          723         graph.w_b.network.config.set_key('test_fail_htlcs_with_temp_node_failure', True)
          724         graph.w_c.network.config.set_key('test_fail_htlcs_with_temp_node_failure', True)
          725         peers = graph.all_peers()
          726         async def pay(lnaddr, pay_req):
          727             self.assertEqual(PR_UNPAID, graph.w_d.get_payment_status(lnaddr.paymenthash))
          728             result, log = await graph.w_a.pay_invoice(pay_req)
          729             self.assertFalse(result)
          730             self.assertEqual(PR_UNPAID, graph.w_d.get_payment_status(lnaddr.paymenthash))
          731             self.assertEqual(OnionFailureCode.TEMPORARY_NODE_FAILURE, log[0].failure_msg.code)
          732             raise PaymentDone()
          733         async def f():
          734             async with TaskGroup() as group:
          735                 for peer in peers:
          736                     await group.spawn(peer._message_loop())
          737                     await group.spawn(peer.htlc_switch())
          738                 await asyncio.sleep(0.2)
          739                 lnaddr, pay_req = await self.prepare_invoice(graph.w_d, include_routing_hints=True)
          740                 await group.spawn(pay(lnaddr, pay_req))
          741         with self.assertRaises(PaymentDone):
          742             run(f())
          743 
          744     @needs_test_with_all_chacha20_implementations
          745     def test_payment_multihop_route_around_failure(self):
          746         # Alice will pay Dave. Alice first tries A->C->D route, due to lower fees, but Carol
          747         # will fail the htlc and get blacklisted. Alice will then try A->B->D and succeed.
          748         graph = self.prepare_chans_and_peers_in_square()
          749         graph.w_c.network.config.set_key('test_fail_htlcs_with_temp_node_failure', True)
          750         peers = graph.all_peers()
          751         async def pay(lnaddr, pay_req):
          752             self.assertEqual(500000000000, graph.chan_ab.balance(LOCAL))
          753             self.assertEqual(500000000000, graph.chan_db.balance(LOCAL))
          754             self.assertEqual(PR_UNPAID, graph.w_d.get_payment_status(lnaddr.paymenthash))
          755             result, log = await graph.w_a.pay_invoice(pay_req, attempts=2)
          756             self.assertEqual(2, len(log))
          757             self.assertTrue(result)
          758             self.assertEqual(PR_PAID, graph.w_d.get_payment_status(lnaddr.paymenthash))
          759             self.assertEqual([graph.chan_ac.short_channel_id, graph.chan_cd.short_channel_id],
          760                              [edge.short_channel_id for edge in log[0].route])
          761             self.assertEqual([graph.chan_ab.short_channel_id, graph.chan_bd.short_channel_id],
          762                              [edge.short_channel_id for edge in log[1].route])
          763             self.assertEqual(OnionFailureCode.TEMPORARY_NODE_FAILURE, log[0].failure_msg.code)
          764             self.assertEqual(499899450000, graph.chan_ab.balance(LOCAL))
          765             await asyncio.sleep(0.2)  # wait for COMMITMENT_SIGNED / REVACK msgs to update balance
          766             self.assertEqual(500100000000, graph.chan_db.balance(LOCAL))
          767             raise PaymentDone()
          768         async def f():
          769             async with TaskGroup() as group:
          770                 for peer in peers:
          771                     await group.spawn(peer._message_loop())
          772                     await group.spawn(peer.htlc_switch())
          773                 await asyncio.sleep(0.2)
          774                 lnaddr, pay_req = await self.prepare_invoice(graph.w_d, include_routing_hints=True)
          775                 invoice_features = lnaddr.get_features()
          776                 self.assertFalse(invoice_features.supports(LnFeatures.BASIC_MPP_OPT))
          777                 await group.spawn(pay(lnaddr, pay_req))
          778         with self.assertRaises(PaymentDone):
          779             run(f())
          780 
          781     def _run_mpp(self, graph, kwargs1, kwargs2):
          782         self.assertEqual(500_000_000_000, graph.chan_ab.balance(LOCAL))
          783         self.assertEqual(500_000_000_000, graph.chan_ac.balance(LOCAL))
          784         amount_to_pay = 600_000_000_000
          785         peers = graph.all_peers()
          786         async def pay(attempts=1,
          787                       alice_uses_trampoline=False,
          788                       bob_forwarding=True,
          789                       mpp_invoice=True):
          790             if mpp_invoice:
          791                 graph.w_d.features |= LnFeatures.BASIC_MPP_OPT
          792             if not bob_forwarding:
          793                 graph.w_b.enable_htlc_forwarding.clear()
          794             if alice_uses_trampoline:
          795                 if graph.w_a.network.channel_db:
          796                     graph.w_a.network.channel_db.stop()
          797                     await graph.w_a.network.channel_db.stopped_event.wait()
          798                     graph.w_a.network.channel_db = None
          799             else:
          800                 assert graph.w_a.network.channel_db is not None
          801             lnaddr, pay_req = await self.prepare_invoice(graph.w_d, include_routing_hints=True, amount_msat=amount_to_pay)
          802             self.assertEqual(PR_UNPAID, graph.w_d.get_payment_status(lnaddr.paymenthash))
          803             result, log = await graph.w_a.pay_invoice(pay_req, attempts=attempts)
          804             if not bob_forwarding:
          805                 # reset to previous state, sleep 2s so that the second htlc can time out
          806                 graph.w_b.enable_htlc_forwarding.set()
          807                 await asyncio.sleep(2)
          808             if result:
          809                 self.assertEqual(PR_PAID, graph.w_d.get_payment_status(lnaddr.paymenthash))
          810                 raise PaymentDone()
          811             else:
          812                 raise NoPathFound()
          813 
          814         async def f(kwargs):
          815             async with TaskGroup() as group:
          816                 for peer in peers:
          817                     await group.spawn(peer._message_loop())
          818                     await group.spawn(peer.htlc_switch())
          819                 await asyncio.sleep(0.2)
          820                 await group.spawn(pay(**kwargs))
          821 
          822         with self.assertRaises(NoPathFound):
          823             run(f(kwargs1))
          824         with self.assertRaises(PaymentDone):
          825             run(f(kwargs2))
          826 
          827     @needs_test_with_all_chacha20_implementations
          828     def test_multipart_payment_with_timeout(self):
          829         graph = self.prepare_chans_and_peers_in_square()
          830         self._run_mpp(graph, {'bob_forwarding':False}, {'bob_forwarding':True})
          831 
          832     @needs_test_with_all_chacha20_implementations
          833     def test_multipart_payment(self):
          834         graph = self.prepare_chans_and_peers_in_square()
          835         self._run_mpp(graph, {'mpp_invoice':False}, {'mpp_invoice':True})
          836 
          837     @needs_test_with_all_chacha20_implementations
          838     def test_multipart_payment_with_trampoline(self):
          839         # single attempt will fail with insufficient trampoline fee
          840         graph = self.prepare_chans_and_peers_in_square()
          841         self._run_mpp(graph, {'alice_uses_trampoline':True, 'attempts':1}, {'alice_uses_trampoline':True, 'attempts':3})
          842 
          843     @needs_test_with_all_chacha20_implementations
          844     def test_fail_pending_htlcs_on_shutdown(self):
          845         """Alice tries to pay Dave via MPP. Dave receives some HTLCs but not all.
          846         Dave shuts down (stops wallet).
          847         We test if Dave fails the pending HTLCs during shutdown.
          848         """
          849         graph = self.prepare_chans_and_peers_in_square()
          850         self.assertEqual(500_000_000_000, graph.chan_ab.balance(LOCAL))
          851         self.assertEqual(500_000_000_000, graph.chan_ac.balance(LOCAL))
          852         amount_to_pay = 600_000_000_000
          853         peers = graph.all_peers()
          854         graph.w_d.MPP_EXPIRY = 120
          855         graph.w_d.TIMEOUT_SHUTDOWN_FAIL_PENDING_HTLCS = 3
          856         async def pay():
          857             graph.w_d.features |= LnFeatures.BASIC_MPP_OPT
          858             graph.w_b.enable_htlc_forwarding.clear()  # Bob will hold forwarded HTLCs
          859             assert graph.w_a.network.channel_db is not None
          860             lnaddr, pay_req = await self.prepare_invoice(graph.w_d, include_routing_hints=True, amount_msat=amount_to_pay)
          861             try:
          862                 async with timeout_after(0.5):
          863                     result, log = await graph.w_a.pay_invoice(pay_req, attempts=1)
          864             except TaskTimeout:
          865                 # by now Dave hopefully received some HTLCs:
          866                 self.assertTrue(len(graph.chan_dc.hm.htlcs(LOCAL)) > 0)
          867                 self.assertTrue(len(graph.chan_dc.hm.htlcs(REMOTE)) > 0)
          868             else:
          869                 self.fail(f"pay_invoice finished but was not supposed to. result={result}")
          870             await graph.w_d.stop()
          871             # Dave is supposed to have failed the pending incomplete MPP HTLCs
          872             self.assertEqual(0, len(graph.chan_dc.hm.htlcs(LOCAL)))
          873             self.assertEqual(0, len(graph.chan_dc.hm.htlcs(REMOTE)))
          874             raise TestSuccess()
          875 
          876         async def f():
          877             async with TaskGroup() as group:
          878                 for peer in peers:
          879                     await group.spawn(peer._message_loop())
          880                     await group.spawn(peer.htlc_switch())
          881                 await asyncio.sleep(0.2)
          882                 await group.spawn(pay())
          883 
          884         with self.assertRaises(TestSuccess):
          885             run(f())
          886 
          887     @needs_test_with_all_chacha20_implementations
          888     def test_close(self):
          889         alice_channel, bob_channel = create_test_channels()
          890         p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
          891         w1.network.config.set_key('dynamic_fees', False)
          892         w2.network.config.set_key('dynamic_fees', False)
          893         w1.network.config.set_key('fee_per_kb', 5000)
          894         w2.network.config.set_key('fee_per_kb', 1000)
          895         w2.enable_htlc_settle.clear()
          896         lnaddr, pay_req = run(self.prepare_invoice(w2))
          897         async def pay():
          898             await asyncio.wait_for(p1.initialized, 1)
          899             await asyncio.wait_for(p2.initialized, 1)
          900             # alice sends htlc
          901             route, amount_msat = w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr)[0][0:2]
          902             htlc = p1.pay(route=route,
          903                           chan=alice_channel,
          904                           amount_msat=lnaddr.get_amount_msat(),
          905                           total_msat=lnaddr.get_amount_msat(),
          906                           payment_hash=lnaddr.paymenthash,
          907                           min_final_cltv_expiry=lnaddr.get_min_final_cltv_expiry(),
          908                           payment_secret=lnaddr.payment_secret)
          909             # alice closes
          910             await p1.close_channel(alice_channel.channel_id)
          911             gath.cancel()
          912         async def set_settle():
          913             await asyncio.sleep(0.1)
          914             w2.enable_htlc_settle.set()
          915         gath = asyncio.gather(pay(), set_settle(), p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
          916         async def f():
          917             await gath
          918         with self.assertRaises(concurrent.futures.CancelledError):
          919             run(f())
          920 
          921     @needs_test_with_all_chacha20_implementations
          922     def test_close_upfront_shutdown_script(self):
          923         alice_channel, bob_channel = create_test_channels()
          924 
          925         # create upfront shutdown script for bob, alice doesn't use upfront
          926         # shutdown script
          927         bob_uss_pub = lnutil.privkey_to_pubkey(os.urandom(32))
          928         bob_uss_addr = bitcoin.pubkey_to_address('p2wpkh', bh2u(bob_uss_pub))
          929         bob_uss = bfh(bitcoin.address_to_script(bob_uss_addr))
          930 
          931         # bob commits to close to bob_uss
          932         alice_channel.config[HTLCOwner.REMOTE].upfront_shutdown_script = bob_uss
          933         # but bob closes to some receiving address, which we achieve by not
          934         # setting the upfront shutdown script in the channel config
          935         bob_channel.config[HTLCOwner.LOCAL].upfront_shutdown_script = b''
          936 
          937         p1, p2, w1, w2, q1, q2 = self.prepare_peers(alice_channel, bob_channel)
          938         w1.network.config.set_key('dynamic_fees', False)
          939         w2.network.config.set_key('dynamic_fees', False)
          940         w1.network.config.set_key('fee_per_kb', 5000)
          941         w2.network.config.set_key('fee_per_kb', 1000)
          942 
          943         async def test():
          944             async def close():
          945                 await asyncio.wait_for(p1.initialized, 1)
          946                 await asyncio.wait_for(p2.initialized, 1)
          947                 # bob closes channel with different shutdown script
          948                 await p1.close_channel(alice_channel.channel_id)
          949                 gath.cancel()
          950 
          951             async def main_loop(peer):
          952                     async with peer.taskgroup as group:
          953                         await group.spawn(peer._message_loop())
          954                         await group.spawn(peer.htlc_switch())
          955 
          956             coros = [close(), main_loop(p1), main_loop(p2)]
          957             gath = asyncio.gather(*coros)
          958             await gath
          959 
          960         with self.assertRaises(UpfrontShutdownScriptViolation):
          961             run(test())
          962 
          963         # bob sends the same upfront_shutdown_script has he announced
          964         alice_channel.config[HTLCOwner.REMOTE].upfront_shutdown_script = bob_uss
          965         bob_channel.config[HTLCOwner.LOCAL].upfront_shutdown_script = bob_uss
          966 
          967         p1, p2, w1, w2, q1, q2 = self.prepare_peers(alice_channel, bob_channel)
          968         w1.network.config.set_key('dynamic_fees', False)
          969         w2.network.config.set_key('dynamic_fees', False)
          970         w1.network.config.set_key('fee_per_kb', 5000)
          971         w2.network.config.set_key('fee_per_kb', 1000)
          972 
          973         async def test():
          974             async def close():
          975                 await asyncio.wait_for(p1.initialized, 1)
          976                 await asyncio.wait_for(p2.initialized, 1)
          977                 await p1.close_channel(alice_channel.channel_id)
          978                 gath.cancel()
          979 
          980             async def main_loop(peer):
          981                 async with peer.taskgroup as group:
          982                     await group.spawn(peer._message_loop())
          983                     await group.spawn(peer.htlc_switch())
          984 
          985             coros = [close(), main_loop(p1), main_loop(p2)]
          986             gath = asyncio.gather(*coros)
          987             await gath
          988         with self.assertRaises(concurrent.futures.CancelledError):
          989             run(test())
          990 
          991     def test_channel_usage_after_closing(self):
          992         alice_channel, bob_channel = create_test_channels()
          993         p1, p2, w1, w2, q1, q2 = self.prepare_peers(alice_channel, bob_channel)
          994         lnaddr, pay_req = run(self.prepare_invoice(w2))
          995 
          996         lnaddr = w1._check_invoice(pay_req)
          997         route, amount_msat = w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr)[0][0:2]
          998         assert amount_msat == lnaddr.get_amount_msat()
          999 
         1000         run(w1.force_close_channel(alice_channel.channel_id))
         1001         # check if a tx (commitment transaction) was broadcasted:
         1002         assert q1.qsize() == 1
         1003 
         1004         with self.assertRaises(NoPathFound) as e:
         1005             w1.create_routes_from_invoice(lnaddr.get_amount_msat(), decoded_invoice=lnaddr)
         1006 
         1007         peer = w1.peers[route[0].node_id]
         1008         # AssertionError is ok since we shouldn't use old routes, and the
         1009         # route finding should fail when channel is closed
         1010         async def f():
         1011             min_cltv_expiry = lnaddr.get_min_final_cltv_expiry()
         1012             payment_hash = lnaddr.paymenthash
         1013             payment_secret = lnaddr.payment_secret
         1014             pay = w1.pay_to_route(
         1015                 route=route,
         1016                 amount_msat=amount_msat,
         1017                 total_msat=amount_msat,
         1018                 amount_receiver_msat=amount_msat,
         1019                 payment_hash=payment_hash,
         1020                 payment_secret=payment_secret,
         1021                 min_cltv_expiry=min_cltv_expiry)
         1022             await asyncio.gather(pay, p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
         1023         with self.assertRaises(PaymentFailure):
         1024             run(f())
         1025 
         1026 
         1027 def run(coro):
         1028     return asyncio.run_coroutine_threadsafe(coro, loop=asyncio.get_event_loop()).result()