electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 1f60d5d8eee3a908ae22f58c29391ef0bab87c64
parent eda9097e894b4e94b17160701cf242ad8da76635
Author: ThomasV <thomasv@electrum.org>
Date:   Fri,  5 Mar 2021 13:00:24 +0100

test_lnpeer: add test for trampoline

Diffstat:
Melectrum/tests/test_lnpeer.py | 37+++++++++++++++++++++++++++++--------
1 file changed, 29 insertions(+), 8 deletions(-)

diff --git a/electrum/tests/test_lnpeer.py b/electrum/tests/test_lnpeer.py @@ -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() @@ -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 @@ -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 @@ -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 @@ -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: @@ -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) @@ -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) @@ -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: @@ -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)