URI: 
       ttest_lnpeer: add test for trampoline - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 1f60d5d8eee3a908ae22f58c29391ef0bab87c64
   DIR parent eda9097e894b4e94b17160701cf242ad8da76635
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Fri,  5 Mar 2021 13:00:24 +0100
       
       ttest_lnpeer: add test for trampoline
       
       Diffstat:
         M electrum/tests/test_lnpeer.py       |      37 ++++++++++++++++++++++++-------
       
       1 file changed, 29 insertions(+), 8 deletions(-)
       ---
   DIR diff --git a/electrum/tests/test_lnpeer.py b/electrum/tests/test_lnpeer.py
       t@@ -117,9 +117,7 @@ class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
                NetworkRetryManager.__init__(self, max_retry_delay_normal=1, init_retry_delay_normal=1)
                self.node_keypair = local_keypair
                self.network = MockNetwork(tx_queue)
       -        self.channel_db = self.network.channel_db
       -        self._channels = {chan.channel_id: chan
       -                          for chan in chans}
       +        self._channels = {chan.channel_id: chan for chan in chans}
                self.payments = {}
                self.logs = defaultdict(list)
                self.wallet = MockWallet()
       t@@ -128,6 +126,7 @@ class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
                self.features |= LnFeatures.OPTION_UPFRONT_SHUTDOWN_SCRIPT_OPT
                self.features |= LnFeatures.VAR_ONION_OPT
                self.features |= LnFeatures.PAYMENT_SECRET_OPT
       +        self.features |= LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT
                self.pending_payments = defaultdict(asyncio.Future)
                for chan in chans:
                    chan.lnworker = self
       t@@ -139,6 +138,7 @@ class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
                self.sent_htlcs = defaultdict(asyncio.Queue)
                self.sent_htlcs_routes = dict()
                self.sent_buckets = defaultdict(set)
       +        self.trampoline_forwarding_failures = {}
        
            def get_invoice_status(self, key):
                pass
       t@@ -148,6 +148,10 @@ class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
                return noop_lock()
        
            @property
       +    def channel_db(self):
       +        return self.network.channel_db if self.network else None
       +
       +    @property
            def channels(self):
                return self._channels
        
       t@@ -195,6 +199,7 @@ class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
            channels_for_peer = LNWallet.channels_for_peer
            _calc_routing_hints_for_invoice = LNWallet._calc_routing_hints_for_invoice
            handle_error_code_from_failed_htlc = LNWallet.handle_error_code_from_failed_htlc
       +    is_trampoline_peer = LNWallet.is_trampoline_peer
        
        
        class MockTransport:
       t@@ -406,6 +411,11 @@ class TestPeer(ElectrumTestCase):
                    routing_hints = await w2._calc_routing_hints_for_invoice(amount_msat)
                else:
                    routing_hints = []
       +        trampoline_hints = []
       +        for r in routing_hints:
       +            node_id, short_channel_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta = r[1][0]
       +            if len(r[1])== 1 and w2.is_trampoline_peer(node_id):
       +                trampoline_hints.append(('t', (node_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta)))
                invoice_features = w2.features.for_invoice()
                if invoice_features.supports(LnFeatures.PAYMENT_SECRET_OPT):
                    payment_secret = derive_payment_secret_from_payment_preimage(payment_preimage)
       t@@ -417,7 +427,7 @@ class TestPeer(ElectrumTestCase):
                            tags=[('c', lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE),
                                  ('d', 'coffee'),
                                  ('9', invoice_features),
       -                         ] + routing_hints,
       +                         ] + routing_hints + trampoline_hints,
                            payment_secret=payment_secret,
                )
                return lnencode(lnaddr, w2.node_keypair.privkey)
       t@@ -716,16 +726,14 @@ class TestPeer(ElectrumTestCase):
                with self.assertRaises(PaymentDone):
                    run(f())
        
       -    @needs_test_with_all_chacha20_implementations
       -    def test_multipart_payment(self):
       -        graph = self.prepare_chans_and_peers_in_square()
       +    def _test_multipart_payment(self, graph, *, attempts):
                self.assertEqual(500_000_000_000, graph.chan_ab.balance(LOCAL))
                self.assertEqual(500_000_000_000, graph.chan_ac.balance(LOCAL))
                amount_to_pay = 600_000_000_000
                peers = graph.all_peers()
                async def pay():
                    pay_req = await self.prepare_invoice(graph.w_d, include_routing_hints=True, amount_msat=amount_to_pay)
       -            result, log = await graph.w_a.pay_invoice(pay_req, attempts=1)
       +            result, log = await graph.w_a.pay_invoice(pay_req, attempts=attempts)
                    if result:
                        raise PaymentDone()
                    else:
       t@@ -745,6 +753,19 @@ class TestPeer(ElectrumTestCase):
                    run(f())
        
            @needs_test_with_all_chacha20_implementations
       +    def test_multipart_payment(self):
       +        graph = self.prepare_chans_and_peers_in_square()
       +        self._test_multipart_payment(graph, attempts=1)
       +
       +    @needs_test_with_all_chacha20_implementations
       +    def test_multipart_payment_with_trampoline(self):
       +        graph = self.prepare_chans_and_peers_in_square()
       +        graph.w_a.network.channel_db.stop()
       +        graph.w_a.network.channel_db = None
       +        # Note: first attempt will fail with insufficient trampoline fee
       +        self._test_multipart_payment(graph, attempts=2)
       +
       +    @needs_test_with_all_chacha20_implementations
            def test_close(self):
                alice_channel, bob_channel = create_test_channels()
                p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)