diff --git a/pickhardt-payments/src/main/java/de/cotto/lndmanagej/pickhardtpayments/ArcInitializer.java b/pickhardt-payments/src/main/java/de/cotto/lndmanagej/pickhardtpayments/ArcInitializer.java index 70341ff4..74953c97 100644 --- a/pickhardt-payments/src/main/java/de/cotto/lndmanagej/pickhardtpayments/ArcInitializer.java +++ b/pickhardt-payments/src/main/java/de/cotto/lndmanagej/pickhardtpayments/ArcInitializer.java @@ -64,7 +64,8 @@ class ArcInitializer { private Coins getMaximumCapacity(Collection edgesWithCapacityInformation) { return edgesWithCapacityInformation.stream() - .map(EdgeWithLiquidityInformation::availableLiquidityUpperBound) + .map(EdgeWithLiquidityInformation::edge) + .map(Edge::capacity) .max(Comparator.naturalOrder()) .orElse(Coins.NONE); } diff --git a/pickhardt-payments/src/main/java/de/cotto/lndmanagej/pickhardtpayments/model/Edge.java b/pickhardt-payments/src/main/java/de/cotto/lndmanagej/pickhardtpayments/model/Edge.java index c97e4f86..a78e4137 100644 --- a/pickhardt-payments/src/main/java/de/cotto/lndmanagej/pickhardtpayments/model/Edge.java +++ b/pickhardt-payments/src/main/java/de/cotto/lndmanagej/pickhardtpayments/model/Edge.java @@ -6,4 +6,7 @@ import de.cotto.lndmanagej.model.Policy; import de.cotto.lndmanagej.model.Pubkey; public record Edge(ChannelId channelId, Pubkey startNode, Pubkey endNode, Coins capacity, Policy policy) { + public Edge withCapacity(Coins capacity) { + return new Edge(channelId(), startNode(), endNode(), capacity, policy()); + } } diff --git a/pickhardt-payments/src/test/java/de/cotto/lndmanagej/pickhardtpayments/ArcInitializerTest.java b/pickhardt-payments/src/test/java/de/cotto/lndmanagej/pickhardtpayments/ArcInitializerTest.java index e809ad09..5b2bced8 100644 --- a/pickhardt-payments/src/test/java/de/cotto/lndmanagej/pickhardtpayments/ArcInitializerTest.java +++ b/pickhardt-payments/src/test/java/de/cotto/lndmanagej/pickhardtpayments/ArcInitializerTest.java @@ -47,7 +47,7 @@ class ArcInitializerTest { @Test void ignores_edge_with_zero_capacity() { - arcInitializer.addArcs(Set.of(EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.NONE))); + arcInitializer.addArcs(Set.of(edge(EDGE, Coins.NONE))); assertThat(minCostFlow.getNumArcs()).isZero(); } @@ -62,7 +62,7 @@ class ArcInitializerTest { PIECEWISE_LINEAR_APPROXIMATIONS ); EdgeWithLiquidityInformation edgeWithLiquidityInformation = - EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.ofSatoshis(quantization)); + edge(EDGE, Coins.ofSatoshis(quantization)); arcInitializer.addArcs(Set.of(edgeWithLiquidityInformation)); assertThat(minCostFlow.getNumArcs()).isOne(); } @@ -78,11 +78,11 @@ class ArcInitializerTest { piecesPerChannel ); arcInitializer.addArcs(List.of( - EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.ofSatoshis(100)), - EdgeWithLiquidityInformation.forUpperBound(EDGE_2_3, Coins.ofSatoshis(200)) + edge(EDGE, Coins.ofSatoshis(100)), + edge(EDGE_2_3, Coins.ofSatoshis(200)) )); - assertThat(edgeMapping.get(0)).isEqualTo(EDGE); - assertThat(edgeMapping.get(piecesPerChannel)).isEqualTo(EDGE_2_3); + assertThat(edgeMapping.get(0)).isEqualTo(EDGE.withCapacity(Coins.ofSatoshis(100))); + assertThat(edgeMapping.get(piecesPerChannel)).isEqualTo(EDGE_2_3.withCapacity(Coins.ofSatoshis(200))); } @Test @@ -96,7 +96,7 @@ class ArcInitializerTest { PIECEWISE_LINEAR_APPROXIMATIONS ); EdgeWithLiquidityInformation edgeWithLiquidityInformation = - EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.ofSatoshis(quantization - 1)); + edge(EDGE, Coins.ofSatoshis(quantization - 1)); arcInitializer.addArcs(Set.of(edgeWithLiquidityInformation)); assertThat(minCostFlow.getNumArcs()).isZero(); } @@ -111,7 +111,7 @@ class ArcInitializerTest { quantization, PIECEWISE_LINEAR_APPROXIMATIONS ); - arcInitializer.addArcs(Set.of(EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.ofSatoshis(20_123)))); + arcInitializer.addArcs(Set.of(edge(EDGE, Coins.ofSatoshis(20_123)))); assertThat(minCostFlow.getCapacity(0)).isEqualTo(201); } @@ -126,15 +126,15 @@ class ArcInitializerTest { PIECEWISE_LINEAR_APPROXIMATIONS ); arcInitializer.addArcs(List.of( - EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.ofSatoshis(20_123)), - EdgeWithLiquidityInformation.forUpperBound(EDGE_3_4, Coins.ofSatoshis(1_000_000)) + edge(EDGE, Coins.ofSatoshis(20_123)), + edge(EDGE_3_4, Coins.ofSatoshis(1_000_000)) )); assertThat(minCostFlow.getUnitCost(0)).isEqualTo(49); } @Test void one_edge() { - arcInitializer.addArcs(Set.of(EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.ofSatoshis(1)))); + arcInitializer.addArcs(Set.of(edge(EDGE, Coins.ofSatoshis(1)))); assertThat(minCostFlow.getNumArcs()).isOne(); } @@ -150,8 +150,8 @@ class ArcInitializerTest { piecewiseLinearApproximations ); arcInitializer.addArcs(List.of( - EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.ofSatoshis(10_000)), - EdgeWithLiquidityInformation.forUpperBound(EDGE_2_3, Coins.ofSatoshis(30_000)) + edge(EDGE, Coins.ofSatoshis(10_000)), + edge(EDGE_2_3, Coins.ofSatoshis(30_000)) )); assertThat(minCostFlow.getNumArcs()).isEqualTo(10); assertThat(minCostFlow.getUnitCost(0)).isEqualTo(3); @@ -168,42 +168,59 @@ class ArcInitializerTest { @Test void two_edges() { - EdgeWithLiquidityInformation edge1 = EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.ofSatoshis(1)); - EdgeWithLiquidityInformation edge2 = EdgeWithLiquidityInformation.forUpperBound(EDGE_1_3, Coins.ofSatoshis(2)); + EdgeWithLiquidityInformation edge1 = edge(EDGE, Coins.ofSatoshis(1)); + EdgeWithLiquidityInformation edge2 = edge(EDGE_1_3, Coins.ofSatoshis(2)); arcInitializer.addArcs(Set.of(edge1, edge2)); assertThat(minCostFlow.getNumArcs()).isEqualTo(2); } @Test void parallel_edges_are_not_combined() { - EdgeWithLiquidityInformation edge = EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.ofSatoshis(1)); + EdgeWithLiquidityInformation edge = edge(EDGE, Coins.ofSatoshis(1)); arcInitializer.addArcs(List.of(edge, edge)); assertThat(minCostFlow.getNumArcs()).isEqualTo(2); } @Test void computes_unit_cost_based_on_maximum_capacity() { - EdgeWithLiquidityInformation edge1 = EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.ofSatoshis(3)); - EdgeWithLiquidityInformation edge2 = EdgeWithLiquidityInformation.forUpperBound(EDGE_1_3, Coins.ofSatoshis(21)); + EdgeWithLiquidityInformation edge1 = edge(EDGE, Coins.ofSatoshis(3)); + EdgeWithLiquidityInformation edge2 = edge(EDGE_1_3, Coins.ofSatoshis(21)); arcInitializer.addArcs(List.of(edge1, edge2)); assertThat(minCostFlow.getUnitCost(0)).isEqualTo(7L); assertThat(minCostFlow.getUnitCost(1)).isEqualTo(1L); } + @Test + void computes_unit_cost_based_on_maximum_capacity_even_if_upper_bound_is_lower() { + EdgeWithLiquidityInformation edge1 = edge(EDGE, Coins.ofSatoshis(3)); + EdgeWithLiquidityInformation edge2 = EdgeWithLiquidityInformation.forUpperBound( + EDGE_1_3.withCapacity(Coins.ofSatoshis(42)), + Coins.ofSatoshis(8) + ); + arcInitializer.addArcs(List.of(edge1, edge2)); + assertThat(minCostFlow.getUnitCost(0)).isEqualTo(42 / 3); + assertThat(minCostFlow.getUnitCost(1)).isEqualTo(42 / 8); + } + @Test void unit_cost_is_rounded_down() { - EdgeWithLiquidityInformation edge1 = EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.ofSatoshis(3)); - EdgeWithLiquidityInformation edge2 = EdgeWithLiquidityInformation.forUpperBound(EDGE_1_3, Coins.ofSatoshis(20)); + EdgeWithLiquidityInformation edge1 = edge(EDGE, Coins.ofSatoshis(3)); + EdgeWithLiquidityInformation edge2 = edge(EDGE_1_3, Coins.ofSatoshis(20)); arcInitializer.addArcs(List.of(edge1, edge2)); assertThat(minCostFlow.getUnitCost(0)).isEqualTo(6L); } @Test void computes_unit_cost_based_on_maximum_capacity_without_combining_parallel_edges() { - EdgeWithLiquidityInformation edge1 = EdgeWithLiquidityInformation.forUpperBound(EDGE, Coins.ofSatoshis(2)); - EdgeWithLiquidityInformation edge2 = EdgeWithLiquidityInformation.forUpperBound(EDGE_1_3, Coins.ofSatoshis(20)); - EdgeWithLiquidityInformation edge3 = EdgeWithLiquidityInformation.forUpperBound(EDGE_1_3, Coins.ofSatoshis(10)); + EdgeWithLiquidityInformation edge1 = edge(EDGE, Coins.ofSatoshis(2)); + EdgeWithLiquidityInformation edge2 = edge(EDGE_1_3, Coins.ofSatoshis(20)); + EdgeWithLiquidityInformation edge3 = edge(EDGE_1_3, Coins.ofSatoshis(10)); arcInitializer.addArcs(List.of(edge1, edge2, edge3)); assertThat(minCostFlow.getUnitCost(0)).isEqualTo(10L); } + + private EdgeWithLiquidityInformation edge(Edge edge, Coins capacity) { + Edge edgeWithCapacity = edge.withCapacity(capacity); + return EdgeWithLiquidityInformation.forUpperBound(edgeWithCapacity, capacity); + } } diff --git a/pickhardt-payments/src/test/java/de/cotto/lndmanagej/pickhardtpayments/model/EdgeTest.java b/pickhardt-payments/src/test/java/de/cotto/lndmanagej/pickhardtpayments/model/EdgeTest.java index 604d7c36..3653d0ad 100644 --- a/pickhardt-payments/src/test/java/de/cotto/lndmanagej/pickhardtpayments/model/EdgeTest.java +++ b/pickhardt-payments/src/test/java/de/cotto/lndmanagej/pickhardtpayments/model/EdgeTest.java @@ -1,5 +1,6 @@ package de.cotto.lndmanagej.pickhardtpayments.model; +import de.cotto.lndmanagej.model.Coins; import org.junit.jupiter.api.Test; import static de.cotto.lndmanagej.model.ChannelFixtures.CAPACITY; @@ -35,4 +36,11 @@ class EdgeTest { void policy() { assertThat(EDGE.policy()).isEqualTo(POLICY_1); } + + @Test + void withCapacity() { + Coins newCapacity = Coins.ofSatoshis(1); + Edge expected = new Edge(EDGE.channelId(), EDGE.startNode(), EDGE.endNode(), newCapacity, EDGE.policy()); + assertThat(EDGE.withCapacity(newCapacity)).isEqualTo(expected); + } }