test_mpp_split.py (4552B)
1 import random 2 3 import electrum.mpp_split as mpp_split # side effect for PART_PENALTY 4 from electrum.lnutil import NoPathFound 5 6 from . import ElectrumTestCase 7 8 PART_PENALTY = mpp_split.PART_PENALTY 9 10 11 class TestMppSplit(ElectrumTestCase): 12 def setUp(self): 13 super().setUp() 14 # to make tests reproducible: 15 random.seed(0) 16 self.channels_with_funds = { 17 0: 1_000_000_000, 18 1: 500_000_000, 19 2: 302_000_000, 20 3: 101_000_000, 21 } 22 23 def tearDown(self): 24 super().tearDown() 25 # undo side effect 26 mpp_split.PART_PENALTY = PART_PENALTY 27 28 def test_suggest_splits(self): 29 with self.subTest(msg="do a payment with the maximal amount spendable over a single channel"): 30 splits = mpp_split.suggest_splits(1_000_000_000, self.channels_with_funds, exclude_single_parts=True) 31 self.assertEqual({0: 660_000_000, 1: 340_000_000, 2: 0, 3: 0}, splits[0][0]) 32 33 with self.subTest(msg="do a payment with a larger amount than what is supported by a single channel"): 34 splits = mpp_split.suggest_splits(1_100_000_000, self.channels_with_funds, exclude_single_parts=True) 35 self.assertEqual(2, mpp_split.number_nonzero_parts(splits[0][0])) 36 37 with self.subTest(msg="do a payment with the maximal amount spendable over all channels"): 38 splits = mpp_split.suggest_splits(sum(self.channels_with_funds.values()), self.channels_with_funds, exclude_single_parts=True) 39 self.assertEqual({0: 1_000_000_000, 1: 500_000_000, 2: 302_000_000, 3: 101_000_000}, splits[0][0]) 40 41 with self.subTest(msg="do a payment with the amount supported by all channels"): 42 splits = mpp_split.suggest_splits(101_000_000, self.channels_with_funds, exclude_single_parts=False) 43 for s in splits[:4]: 44 self.assertEqual(1, mpp_split.number_nonzero_parts(s[0])) 45 46 def test_saturation(self): 47 """Split configurations which spend the full amount in a channel should be avoided.""" 48 channels_with_funds = {0: 159_799_733_076, 1: 499_986_152_000} 49 splits = mpp_split.suggest_splits(600_000_000_000, channels_with_funds, exclude_single_parts=True) 50 51 uses_full_amount = False 52 for c, a in splits[0][0].items(): 53 if a == channels_with_funds[c]: 54 uses_full_amount |= True 55 56 self.assertFalse(uses_full_amount) 57 58 def test_payment_below_min_part_size(self): 59 amount = mpp_split.MIN_PART_MSAT // 2 60 splits = mpp_split.suggest_splits(amount, self.channels_with_funds, exclude_single_parts=False) 61 # we only get four configurations that end up spending the full amount 62 # in a single channel 63 self.assertEqual(4, len(splits)) 64 65 def test_suggest_part_penalty(self): 66 """Test is mainly for documentation purposes. 67 Decreasing the part penalty from 1.0 towards 0.0 leads to an increase 68 in the number of parts a payment is split. A configuration which has 69 about equally distributed amounts will result.""" 70 with self.subTest(msg="split payments with intermediate part penalty"): 71 mpp_split.PART_PENALTY = 1.0 72 splits = mpp_split.suggest_splits(1_100_000_000, self.channels_with_funds) 73 self.assertEqual(2, mpp_split.number_nonzero_parts(splits[0][0])) 74 75 with self.subTest(msg="split payments with intermediate part penalty"): 76 mpp_split.PART_PENALTY = 0.3 77 splits = mpp_split.suggest_splits(1_100_000_000, self.channels_with_funds) 78 self.assertEqual(3, mpp_split.number_nonzero_parts(splits[0][0])) 79 80 with self.subTest(msg="split payments with no part penalty"): 81 mpp_split.PART_PENALTY = 0.0 82 splits = mpp_split.suggest_splits(1_100_000_000, self.channels_with_funds) 83 self.assertEqual(4, mpp_split.number_nonzero_parts(splits[0][0])) 84 85 def test_suggest_splits_single_channel(self): 86 channels_with_funds = { 87 0: 1_000_000_000, 88 } 89 90 with self.subTest(msg="do a payment with the maximal amount spendable on a single channel"): 91 splits = mpp_split.suggest_splits(1_000_000_000, channels_with_funds, exclude_single_parts=False) 92 self.assertEqual({0: 1_000_000_000}, splits[0][0]) 93 with self.subTest(msg="test sending an amount greater than what we have available"): 94 self.assertRaises(NoPathFound, mpp_split.suggest_splits, *(1_100_000_000, channels_with_funds))