take known liquidity into account, add as 0-cost arc

fixes #10
This commit is contained in:
Carsten Otto
2022-03-27 15:44:04 +02:00
parent 3d630e9073
commit abd5c1bc4e
6 changed files with 191 additions and 22 deletions

View File

@@ -7,6 +7,8 @@ import de.cotto.lndmanagej.model.Pubkey;
import de.cotto.lndmanagej.pickhardtpayments.model.Edge;
import de.cotto.lndmanagej.pickhardtpayments.model.EdgeWithLiquidityInformation;
import de.cotto.lndmanagej.pickhardtpayments.model.IntegerMapping;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.util.LinkedHashMap;
@@ -67,6 +69,118 @@ class ArcInitializerTest {
assertThat(minCostFlow.getNumArcs()).isOne();
}
@Test
@SuppressWarnings("PMD.JUnitTestContainsTooManyAsserts")
void edge_with_known_liquidity_is_added_as_arc_without_cost() {
Coins capacity = Coins.ofSatoshis(100);
Coins knownLiquidity = Coins.ofSatoshis(25);
EdgeWithLiquidityInformation edgeWithLiquidityInformation =
EdgeWithLiquidityInformation.forKnownLiquidity(EDGE.withCapacity(capacity), knownLiquidity);
arcInitializer.addArcs(Set.of(edgeWithLiquidityInformation));
assertThat(minCostFlow.getNumArcs()).isEqualTo(1);
assertThat(minCostFlow.getUnitCost(0)).isEqualTo(0);
assertThat(minCostFlow.getCapacity(0)).isEqualTo(25);
}
@Nested
class EdgeWithLowerAndUpperBound {
private EdgeWithLiquidityInformation edgeWithLiquidityInformation;
@BeforeEach
void setUp() {
Coins capacity = Coins.ofSatoshis(100);
Coins knownLiquidity = Coins.ofSatoshis(25);
edgeWithLiquidityInformation = EdgeWithLiquidityInformation.forLowerAndUpperBound(
EDGE.withCapacity(capacity),
knownLiquidity,
capacity
);
}
@Test
void added_as_arc_without_cost() {
arcInitializer.addArcs(Set.of(edgeWithLiquidityInformation));
assertThat(minCostFlow.getUnitCost(0)).isEqualTo(0);
assertThat(minCostFlow.getCapacity(0)).isEqualTo(25);
}
@Test
void adds_uncertain_liquidity_as_second_arc() {
arcInitializer.addArcs(Set.of(edgeWithLiquidityInformation));
assertThat(minCostFlow.getUnitCost(1)).isEqualTo(1);
assertThat(minCostFlow.getCapacity(1)).isEqualTo(75);
}
@Test
void splits_uncertain_liquidity_as_additional_arcs() {
ArcInitializer arcInitializer = new ArcInitializer(
minCostFlow,
integerMapping,
edgeMapping,
QUANTIZATION,
5
);
arcInitializer.addArcs(Set.of(edgeWithLiquidityInformation));
assertThat(minCostFlow.getNumArcs()).isEqualTo(6);
}
@Test
void known_amount_matches_quantization() {
ArcInitializer arcInitializer = new ArcInitializer(
minCostFlow,
integerMapping,
edgeMapping,
edgeWithLiquidityInformation.availableLiquidityLowerBound().satoshis(),
PIECEWISE_LINEAR_APPROXIMATIONS
);
arcInitializer.addArcs(Set.of(edgeWithLiquidityInformation));
assertThat(minCostFlow.getUnitCost(0)).isEqualTo(0);
}
@Test
void known_amount_rounded_due_to_quantization() {
ArcInitializer arcInitializer = new ArcInitializer(
minCostFlow,
integerMapping,
edgeMapping,
20,
PIECEWISE_LINEAR_APPROXIMATIONS
);
arcInitializer.addArcs(Set.of(edgeWithLiquidityInformation));
assertThat(minCostFlow.getCapacity(0)).isEqualTo(1);
}
@Test
void splits_remaining_liquidity_after_rounding_known_liquidity() {
ArcInitializer arcInitializer = new ArcInitializer(
minCostFlow,
integerMapping,
edgeMapping,
10,
QUANTIZATION
);
arcInitializer.addArcs(Set.of(edgeWithLiquidityInformation));
// one arc for the known liquidity (25 / 10 = 2), 100 / 10 - 2 = 8 remaining
assertThat(minCostFlow.getCapacity(1)).isEqualTo(8);
}
@Test
void does_not_add_arcs_without_capacity() {
ArcInitializer arcInitializer = new ArcInitializer(
minCostFlow,
integerMapping,
edgeMapping,
20,
5
);
arcInitializer.addArcs(Set.of(edgeWithLiquidityInformation));
// one arc for the known liquidity (25 / 20 = 1), 100 / 20 - 1 = 4 remaining: 4 < 5, no additional arc added
assertThat(minCostFlow.getNumArcs()).isEqualTo(1);
}
}
@Test
void adds_edge_to_edgeMapping() {
int piecesPerChannel = 2;

View File

@@ -100,11 +100,10 @@ class FlowComputationTest {
}
@Test
void solve_avoids_sending_from_local_channel_lacking_capacity() {
// TODO use balance of local channel as known balance, not upper bound
void solve_avoids_sending_from_depleted_local_channel() {
when(channelService.getLocalChannel(CHANNEL_ID)).thenReturn(Optional.of(LOCAL_OPEN_CHANNEL));
when(channelService.getLocalChannel(CHANNEL_ID_2)).thenReturn(Optional.empty());
when(balanceService.getAvailableLocalBalance(CHANNEL_ID)).thenReturn(Coins.ofSatoshis(1));
when(balanceService.getAvailableLocalBalance(CHANNEL_ID)).thenReturn(Coins.NONE);
Coins amount = Coins.ofSatoshis(100);
DirectedChannelEdge largerButDepletedChannel =
new DirectedChannelEdge(CHANNEL_ID, LARGE, PUBKEY, PUBKEY_2, POLICY_1);
@@ -119,11 +118,10 @@ class FlowComputationTest {
}
@Test
void solve_avoids_sending_to_local_channel_lacking_capacity() {
// TODO use balance of local channel as known balance, not upper bound
void solve_avoids_sending_to_depleted_local_channel() {
when(channelService.getLocalChannel(CHANNEL_ID)).thenReturn(Optional.of(LOCAL_OPEN_CHANNEL));
when(channelService.getLocalChannel(CHANNEL_ID_2)).thenReturn(Optional.empty());
when(balanceService.getAvailableRemoteBalance(CHANNEL_ID)).thenReturn(Coins.ofSatoshis(1));
when(balanceService.getAvailableRemoteBalance(CHANNEL_ID)).thenReturn(Coins.NONE);
Coins amount = Coins.ofSatoshis(100);
DirectedChannelEdge largerButDepletedChannel =
new DirectedChannelEdge(CHANNEL_ID, LARGE, PUBKEY_2, PUBKEY, POLICY_1);

View File

@@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test;
import static de.cotto.lndmanagej.pickhardtpayments.model.EdgeFixtures.EDGE;
import static de.cotto.lndmanagej.pickhardtpayments.model.EdgeWithLiquidityInformationFixtures.EDGE_WITH_LIQUIDITY_INFORMATION;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
class EdgeWithLiquidityInformationTest {
@Test
@@ -20,6 +21,13 @@ class EdgeWithLiquidityInformationTest {
.isEqualTo(new EdgeWithLiquidityInformation(EDGE, knownLiquidity, knownLiquidity));
}
@Test
void forLowerBound() {
Coins lowerBound = Coins.ofSatoshis(300);
assertThat(EdgeWithLiquidityInformation.forLowerBound(EDGE, lowerBound))
.isEqualTo(new EdgeWithLiquidityInformation(EDGE, lowerBound, EDGE.capacity()));
}
@Test
void forUpperBound() {
Coins upperBound = Coins.ofSatoshis(300);
@@ -27,6 +35,23 @@ class EdgeWithLiquidityInformationTest {
.isEqualTo(new EdgeWithLiquidityInformation(EDGE, Coins.NONE, upperBound));
}
@Test
void forLowerAndUpperBound() {
Coins lowerBound = Coins.ofSatoshis(100);
Coins upperBound = Coins.ofSatoshis(300);
assertThat(EdgeWithLiquidityInformation.forLowerAndUpperBound(EDGE, lowerBound, upperBound))
.isEqualTo(new EdgeWithLiquidityInformation(EDGE, lowerBound, upperBound));
}
@Test
void forLowerAndUpperBound_lower_more_than_upper() {
Coins lowerBound = Coins.ofSatoshis(301);
Coins upperBound = Coins.ofSatoshis(300);
assertThatIllegalArgumentException().isThrownBy(
() -> EdgeWithLiquidityInformation.forLowerAndUpperBound(EDGE, lowerBound, upperBound)
);
}
@Test
void availableLiquidityUpperBound() {
assertThat(EDGE_WITH_LIQUIDITY_INFORMATION.availableLiquidityUpperBound()).isEqualTo(Coins.ofSatoshis(123));