Browse Source

test_lnpeer: add test for mpp_timeout

patch-4
ThomasV 4 years ago
parent
commit
549b9a95df
  1. 6
      electrum/lnpeer.py
  2. 6
      electrum/lnworker.py
  3. 46
      electrum/tests/test_lnpeer.py

6
electrum/lnpeer.py

@ -1820,7 +1820,7 @@ class Peer(Logger):
error_reason = e
else:
try:
preimage, fw_info, error_bytes = self.process_unfulfilled_htlc(
preimage, fw_info, error_bytes = await self.process_unfulfilled_htlc(
chan=chan,
htlc=htlc,
forwarding_info=forwarding_info,
@ -1849,7 +1849,7 @@ class Peer(Logger):
for htlc_id in done:
unfulfilled.pop(htlc_id)
def process_unfulfilled_htlc(
async def process_unfulfilled_htlc(
self, *,
chan: Channel,
htlc: UpdateAddHtlc,
@ -1885,6 +1885,7 @@ class Peer(Logger):
processed_onion=trampoline_onion,
is_trampoline=True)
else:
await self.lnworker.enable_htlc_forwarding.wait()
self.maybe_forward_trampoline(
chan=chan,
htlc=htlc,
@ -1899,6 +1900,7 @@ class Peer(Logger):
raise error_reason
elif not forwarding_info:
await self.lnworker.enable_htlc_forwarding.wait()
next_chan_id, next_htlc_id = self.maybe_forward_htlc(
htlc=htlc,
processed_onion=processed_onion)

6
electrum/lnworker.py

@ -91,7 +91,6 @@ SAVED_PR_STATUS = [PR_PAID, PR_UNPAID] # status that are persisted
NUM_PEERS_TARGET = 4
MPP_EXPIRY = 120
FALLBACK_NODE_LIST_TESTNET = (
@ -575,6 +574,7 @@ class LNGossip(LNWorker):
class LNWallet(LNWorker):
lnwatcher: Optional['LNWalletWatcher']
MPP_EXPIRY = 120
def __init__(self, wallet: 'Abstract_Wallet', xprv):
self.wallet = wallet
@ -592,6 +592,8 @@ class LNWallet(LNWorker):
# used in tests
self.enable_htlc_settle = asyncio.Event()
self.enable_htlc_settle.set()
self.enable_htlc_forwarding = asyncio.Event()
self.enable_htlc_forwarding.set()
# note: accessing channels (besides simple lookup) needs self.lock!
self._channels = {} # type: Dict[bytes, Channel]
@ -1633,7 +1635,7 @@ class LNWallet(LNWorker):
if not is_accepted and not is_expired:
total = sum([_htlc.amount_msat for scid, _htlc in htlc_set])
first_timestamp = min([_htlc.timestamp for scid, _htlc in htlc_set])
if time.time() - first_timestamp > MPP_EXPIRY:
if time.time() - first_timestamp > self.MPP_EXPIRY:
is_expired = True
elif total == expected_msat:
is_accepted = True

46
electrum/tests/test_lnpeer.py

@ -113,6 +113,7 @@ class MockWallet:
class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
MPP_EXPIRY = 1
def __init__(self, *, local_keypair: Keypair, chans: Iterable['Channel'], tx_queue, name):
self.name = name
Logger.__init__(self)
@ -136,6 +137,8 @@ class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
# used in tests
self.enable_htlc_settle = asyncio.Event()
self.enable_htlc_settle.set()
self.enable_htlc_forwarding = asyncio.Event()
self.enable_htlc_forwarding.set()
self.received_htlcs = dict()
self.sent_htlcs = defaultdict(asyncio.Queue)
self.sent_htlcs_routes = dict()
@ -747,7 +750,7 @@ class TestPeer(ElectrumTestCase):
with self.assertRaises(PaymentDone):
run(f())
def _test_multipart_payment(self, graph, *, attempts):
async def _run_mpp(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
@ -761,32 +764,45 @@ class TestPeer(ElectrumTestCase):
raise PaymentDone()
else:
raise NoPathFound()
async def f():
async with TaskGroup() as group:
for peer in peers:
await group.spawn(peer._message_loop())
await group.spawn(peer.htlc_switch())
await asyncio.sleep(0.2)
await group.spawn(pay())
self.assertFalse(graph.w_d.features.supports(LnFeatures.BASIC_MPP_OPT))
with self.assertRaises(NoPathFound):
run(f())
async with TaskGroup() as group:
for peer in peers:
await group.spawn(peer._message_loop())
await group.spawn(peer.htlc_switch())
await asyncio.sleep(0.2)
await group.spawn(pay())
@needs_test_with_all_chacha20_implementations
def test_multipart_payment_with_timeout(self):
graph = self.prepare_chans_and_peers_in_square()
graph.w_d.features |= LnFeatures.BASIC_MPP_OPT
graph.w_b.enable_htlc_forwarding.clear()
with self.assertRaises(NoPathFound):
run(self._run_mpp(graph, attempts=1))
graph.w_b.enable_htlc_forwarding.set()
with self.assertRaises(PaymentDone):
run(f())
run(self._run_mpp(graph, attempts=1))
@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)
self.assertFalse(graph.w_d.features.supports(LnFeatures.BASIC_MPP_OPT))
with self.assertRaises(NoPathFound):
run(self._run_mpp(graph, attempts=1))
graph.w_d.features |= LnFeatures.BASIC_MPP_OPT
with self.assertRaises(PaymentDone):
run(self._run_mpp(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_d.features |= LnFeatures.BASIC_MPP_OPT
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=3)
# Note: single attempt will fail with insufficient trampoline fee
with self.assertRaises(NoPathFound):
run(self._run_mpp(graph, attempts=1))
with self.assertRaises(PaymentDone):
run(self._run_mpp(graph, attempts=3))
@needs_test_with_all_chacha20_implementations
def test_close(self):

Loading…
Cancel
Save