|
|
|
|
@ -15,10 +15,10 @@ class TestMppSplit(ElectrumTestCase):
|
|
|
|
|
random.seed(0) |
|
|
|
|
# key tuple denotes (channel_id, node_id) |
|
|
|
|
self.channels_with_funds = { |
|
|
|
|
(0, 0): 1_000_000_000, |
|
|
|
|
(1, 1): 500_000_000, |
|
|
|
|
(2, 0): 302_000_000, |
|
|
|
|
(3, 2): 101_000_000, |
|
|
|
|
(b"0", b"0"): 1_000_000_000, |
|
|
|
|
(b"1", b"1"): 500_000_000, |
|
|
|
|
(b"2", b"0"): 302_000_000, |
|
|
|
|
(b"3", b"2"): 101_000_000, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
def tearDown(self): |
|
|
|
|
@ -30,52 +30,52 @@ class TestMppSplit(ElectrumTestCase):
|
|
|
|
|
with self.subTest(msg="do a payment with the maximal amount spendable over a single channel"): |
|
|
|
|
splits = mpp_split.suggest_splits(1_000_000_000, self.channels_with_funds, exclude_single_part_payments=True) |
|
|
|
|
self.assertEqual({ |
|
|
|
|
(0, 0): [671_020_676], |
|
|
|
|
(1, 1): [328_979_324], |
|
|
|
|
(2, 0): [], |
|
|
|
|
(3, 2): []}, |
|
|
|
|
(b"0", b"0"): [671_020_676], |
|
|
|
|
(b"1", b"1"): [328_979_324], |
|
|
|
|
(b"2", b"0"): [], |
|
|
|
|
(b"3", b"2"): []}, |
|
|
|
|
splits[0].config |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
with self.subTest(msg="payment amount that does not require to be split"): |
|
|
|
|
splits = mpp_split.suggest_splits(50_000_000, self.channels_with_funds, exclude_single_part_payments=False) |
|
|
|
|
self.assertEqual({(0, 0): [50_000_000]}, splits[0].config) |
|
|
|
|
self.assertEqual({(1, 1): [50_000_000]}, splits[1].config) |
|
|
|
|
self.assertEqual({(2, 0): [50_000_000]}, splits[2].config) |
|
|
|
|
self.assertEqual({(3, 2): [50_000_000]}, splits[3].config) |
|
|
|
|
self.assertEqual(2, mpp_split.number_parts(splits[4].config)) |
|
|
|
|
self.assertEqual({(b"0", b"0"): [50_000_000]}, splits[0].config) |
|
|
|
|
self.assertEqual({(b"1", b"1"): [50_000_000]}, splits[1].config) |
|
|
|
|
self.assertEqual({(b"2", b"0"): [50_000_000]}, splits[2].config) |
|
|
|
|
self.assertEqual({(b"3", b"2"): [50_000_000]}, splits[3].config) |
|
|
|
|
self.assertEqual(2, splits[4].config.number_parts()) |
|
|
|
|
|
|
|
|
|
with self.subTest(msg="do a payment with a larger amount than what is supported by a single channel"): |
|
|
|
|
splits = mpp_split.suggest_splits(1_100_000_000, self.channels_with_funds, exclude_single_part_payments=False) |
|
|
|
|
self.assertEqual(2, mpp_split.number_parts(splits[0].config)) |
|
|
|
|
self.assertEqual(2, splits[0].config.number_parts()) |
|
|
|
|
|
|
|
|
|
with self.subTest(msg="do a payment with the maximal amount spendable over all channels"): |
|
|
|
|
splits = mpp_split.suggest_splits( |
|
|
|
|
sum(self.channels_with_funds.values()), self.channels_with_funds, exclude_single_part_payments=True) |
|
|
|
|
self.assertEqual({ |
|
|
|
|
(0, 0): [1_000_000_000], |
|
|
|
|
(1, 1): [500_000_000], |
|
|
|
|
(2, 0): [302_000_000], |
|
|
|
|
(3, 2): [101_000_000]}, |
|
|
|
|
(b"0", b"0"): [1_000_000_000], |
|
|
|
|
(b"1", b"1"): [500_000_000], |
|
|
|
|
(b"2", b"0"): [302_000_000], |
|
|
|
|
(b"3", b"2"): [101_000_000]}, |
|
|
|
|
splits[0].config |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
with self.subTest(msg="do a payment with the amount supported by all channels"): |
|
|
|
|
splits = mpp_split.suggest_splits(101_000_000, self.channels_with_funds, exclude_single_part_payments=False) |
|
|
|
|
for split in splits[:3]: |
|
|
|
|
self.assertEqual(1, mpp_split.number_nonzero_channels(split.config)) |
|
|
|
|
self.assertEqual(1, split.config.number_nonzero_channels()) |
|
|
|
|
# due to exhaustion of the smallest channel, the algorithm favors |
|
|
|
|
# a splitting of the parts into two |
|
|
|
|
self.assertEqual(2, mpp_split.number_parts(splits[4].config)) |
|
|
|
|
self.assertEqual(2, splits[4].config.number_parts()) |
|
|
|
|
|
|
|
|
|
def test_send_to_single_node(self): |
|
|
|
|
splits = mpp_split.suggest_splits(1_000_000_000, self.channels_with_funds, exclude_single_part_payments=False, exclude_multinode_payments=True) |
|
|
|
|
for split in splits: |
|
|
|
|
assert mpp_split.number_nonzero_nodes(split.config) == 1 |
|
|
|
|
assert split.config.number_nonzero_nodes() == 1 |
|
|
|
|
|
|
|
|
|
def test_saturation(self): |
|
|
|
|
"""Split configurations which spend the full amount in a channel should be avoided.""" |
|
|
|
|
channels_with_funds = {(0, 0): 159_799_733_076, (1, 1): 499_986_152_000} |
|
|
|
|
channels_with_funds = {(b"0", b"0"): 159_799_733_076, (b"1", b"1"): 499_986_152_000} |
|
|
|
|
splits = mpp_split.suggest_splits(600_000_000_000, channels_with_funds, exclude_single_part_payments=True) |
|
|
|
|
|
|
|
|
|
uses_full_amount = False |
|
|
|
|
@ -100,37 +100,37 @@ class TestMppSplit(ElectrumTestCase):
|
|
|
|
|
with self.subTest(msg="split payments with intermediate part penalty"): |
|
|
|
|
mpp_split.PART_PENALTY = 1.0 |
|
|
|
|
splits = mpp_split.suggest_splits(1_100_000_000, self.channels_with_funds) |
|
|
|
|
self.assertEqual(2, mpp_split.number_parts(splits[0].config)) |
|
|
|
|
self.assertEqual(2, splits[0].config.number_parts()) |
|
|
|
|
|
|
|
|
|
with self.subTest(msg="split payments with intermediate part penalty"): |
|
|
|
|
mpp_split.PART_PENALTY = 0.3 |
|
|
|
|
splits = mpp_split.suggest_splits(1_100_000_000, self.channels_with_funds) |
|
|
|
|
self.assertEqual(4, mpp_split.number_parts(splits[0].config)) |
|
|
|
|
self.assertEqual(4, splits[0].config.number_parts()) |
|
|
|
|
|
|
|
|
|
with self.subTest(msg="split payments with no part penalty"): |
|
|
|
|
mpp_split.PART_PENALTY = 0.0 |
|
|
|
|
splits = mpp_split.suggest_splits(1_100_000_000, self.channels_with_funds) |
|
|
|
|
self.assertEqual(5, mpp_split.number_parts(splits[0].config)) |
|
|
|
|
self.assertEqual(5, splits[0].config.number_parts()) |
|
|
|
|
|
|
|
|
|
def test_suggest_splits_single_channel(self): |
|
|
|
|
channels_with_funds = { |
|
|
|
|
(0, 0): 1_000_000_000, |
|
|
|
|
(b"0", b"0"): 1_000_000_000, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
with self.subTest(msg="do a payment with the maximal amount spendable on a single channel"): |
|
|
|
|
splits = mpp_split.suggest_splits(1_000_000_000, channels_with_funds, exclude_single_part_payments=False) |
|
|
|
|
self.assertEqual({(0, 0): [1_000_000_000]}, splits[0].config) |
|
|
|
|
self.assertEqual({(b"0", b"0"): [1_000_000_000]}, splits[0].config) |
|
|
|
|
with self.subTest(msg="test sending an amount greater than what we have available"): |
|
|
|
|
self.assertRaises(NoPathFound, mpp_split.suggest_splits, *(1_100_000_000, channels_with_funds)) |
|
|
|
|
with self.subTest(msg="test sending a large amount over a single channel in chunks"): |
|
|
|
|
mpp_split.PART_PENALTY = 0.5 |
|
|
|
|
splits = mpp_split.suggest_splits(1_000_000_000, channels_with_funds, exclude_single_part_payments=False) |
|
|
|
|
self.assertEqual(2, len(splits[0].config[(0, 0)])) |
|
|
|
|
self.assertEqual(2, len(splits[0].config[(b"0", b"0")])) |
|
|
|
|
with self.subTest(msg="test sending a large amount over a single channel in chunks"): |
|
|
|
|
mpp_split.PART_PENALTY = 0.3 |
|
|
|
|
splits = mpp_split.suggest_splits(1_000_000_000, channels_with_funds, exclude_single_part_payments=False) |
|
|
|
|
self.assertEqual(3, len(splits[0].config[(0, 0)])) |
|
|
|
|
self.assertEqual(3, len(splits[0].config[(b"0", b"0")])) |
|
|
|
|
with self.subTest(msg="exclude all single channel splits"): |
|
|
|
|
mpp_split.PART_PENALTY = 0.3 |
|
|
|
|
splits = mpp_split.suggest_splits(1_000_000_000, channels_with_funds, exclude_single_channel_splits=True) |
|
|
|
|
self.assertEqual(1, len(splits[0].config[(0, 0)])) |
|
|
|
|
self.assertEqual(1, len(splits[0].config[(b"0", b"0")])) |
|
|
|
|
|