URI: 
       ttest_mpp_split.py - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
       ttest_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))