You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
101 lines
4.9 KiB
101 lines
4.9 KiB
import random |
|
|
|
import electrum.mpp_split as mpp_split # side effect for PART_PENALTY |
|
from electrum.lnutil import NoPathFound |
|
|
|
from . import ElectrumTestCase |
|
|
|
PART_PENALTY = mpp_split.PART_PENALTY |
|
|
|
|
|
class TestMppSplit(ElectrumTestCase): |
|
def setUp(self): |
|
super().setUp() |
|
# to make tests reproducible: |
|
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, |
|
} |
|
|
|
def tearDown(self): |
|
super().tearDown() |
|
# undo side effect |
|
mpp_split.PART_PENALTY = PART_PENALTY |
|
|
|
def test_suggest_splits(self): |
|
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_parts=True) |
|
self.assertEqual({(0, 0): 660_000_000, (1, 1): 340_000_000, (2, 0): 0, (3, 2): 0}, splits[0][0]) |
|
|
|
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_parts=True) |
|
self.assertEqual(2, mpp_split.number_nonzero_parts(splits[0][0])) |
|
|
|
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_parts=True) |
|
self.assertEqual({(0, 0): 1_000_000_000, (1, 1): 500_000_000, (2, 0): 302_000_000, (3, 2): 101_000_000}, splits[0][0]) |
|
|
|
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_parts=False) |
|
for s in splits[:4]: |
|
self.assertEqual(1, mpp_split.number_nonzero_parts(s[0])) |
|
|
|
def test_send_to_single_node(self): |
|
splits = mpp_split.suggest_splits(1_000_000_000, self.channels_with_funds, exclude_single_parts=True, single_node=True) |
|
self.assertEqual({(0, 0): 738_000_000, (1, 1): 0, (2, 0): 262_000_000, (3, 2): 0}, splits[0][0]) |
|
for split in splits: |
|
assert mpp_split.number_nonzero_nodes(split[0]) == 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} |
|
splits = mpp_split.suggest_splits(600_000_000_000, channels_with_funds, exclude_single_parts=True) |
|
|
|
uses_full_amount = False |
|
for c, a in splits[0][0].items(): |
|
if a == channels_with_funds[c]: |
|
uses_full_amount |= True |
|
|
|
self.assertFalse(uses_full_amount) |
|
|
|
def test_payment_below_min_part_size(self): |
|
amount = mpp_split.MIN_PART_MSAT // 2 |
|
splits = mpp_split.suggest_splits(amount, self.channels_with_funds, exclude_single_parts=False) |
|
# we only get four configurations that end up spending the full amount |
|
# in a single channel |
|
self.assertEqual(4, len(splits)) |
|
|
|
def test_suggest_part_penalty(self): |
|
"""Test is mainly for documentation purposes. |
|
Decreasing the part penalty from 1.0 towards 0.0 leads to an increase |
|
in the number of parts a payment is split. A configuration which has |
|
about equally distributed amounts will result.""" |
|
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_nonzero_parts(splits[0][0])) |
|
|
|
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(3, mpp_split.number_nonzero_parts(splits[0][0])) |
|
|
|
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(4, mpp_split.number_nonzero_parts(splits[0][0])) |
|
|
|
def test_suggest_splits_single_channel(self): |
|
channels_with_funds = { |
|
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_parts=False) |
|
self.assertEqual({0: 1_000_000_000}, splits[0][0]) |
|
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))
|
|
|