diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicy.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicy.java index 5ff3829e..3bc8ee89 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicy.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicy.java @@ -3,11 +3,9 @@ package de.cotto.lndmanagej.grpc; import com.github.benmanes.caffeine.cache.LoadingCache; import de.cotto.lndmanagej.caching.CacheBuilder; import de.cotto.lndmanagej.model.ChannelId; -import de.cotto.lndmanagej.model.Coins; import de.cotto.lndmanagej.model.Policy; import de.cotto.lndmanagej.model.Pubkey; import lnrpc.ChannelEdge; -import lnrpc.RoutingPolicy; import org.springframework.stereotype.Component; import java.time.Duration; @@ -18,10 +16,12 @@ public class GrpcChannelPolicy { private final GrpcService grpcService; private final GrpcGetInfo grpcGetInfo; private final LoadingCache> channelEdgeCache; + private final GrpcPolicy grpcPolicy; - public GrpcChannelPolicy(GrpcService grpcService, GrpcGetInfo grpcGetInfo) { + public GrpcChannelPolicy(GrpcService grpcService, GrpcGetInfo grpcGetInfo, GrpcPolicy grpcPolicy) { this.grpcService = grpcService; this.grpcGetInfo = grpcGetInfo; + this.grpcPolicy = grpcPolicy; channelEdgeCache = new CacheBuilder() .withExpiry(Duration.ofMinutes(5)) .withRefresh(Duration.ofSeconds(150)) @@ -57,9 +57,9 @@ public class GrpcChannelPolicy { return getChannelEdge(channelId).map( channelEdge -> { if (sourcePubkey.equals(channelEdge.getNode1Pub())) { - return toPolicy(channelEdge.getNode1Policy()); + return grpcPolicy.toPolicy(channelEdge.getNode1Policy()); } else if (sourcePubkey.equals(channelEdge.getNode2Pub())) { - return toPolicy(channelEdge.getNode2Policy()); + return grpcPolicy.toPolicy(channelEdge.getNode2Policy()); } else { return null; } @@ -72,9 +72,9 @@ public class GrpcChannelPolicy { return getChannelEdge(channelId).map( channelEdge -> { if (targetPubkey.equals(channelEdge.getNode2Pub())) { - return toPolicy(channelEdge.getNode1Policy()); + return grpcPolicy.toPolicy(channelEdge.getNode1Policy()); } else if (targetPubkey.equals(channelEdge.getNode1Pub())) { - return toPolicy(channelEdge.getNode2Policy()); + return grpcPolicy.toPolicy(channelEdge.getNode2Policy()); } else { return null; } @@ -82,15 +82,6 @@ public class GrpcChannelPolicy { ); } - private Policy toPolicy(RoutingPolicy routingPolicy) { - return new Policy( - routingPolicy.getFeeRateMilliMsat(), - Coins.ofMilliSatoshis(routingPolicy.getFeeBaseMsat()), - !routingPolicy.getDisabled(), - routingPolicy.getTimeLockDelta() - ); - } - private Optional getChannelEdge(ChannelId channelId) { return channelEdgeCache.get(channelId); } diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcGraph.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcGraph.java index d6483ee2..627a25c8 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcGraph.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcGraph.java @@ -9,7 +9,6 @@ import de.cotto.lndmanagej.model.Policy; import de.cotto.lndmanagej.model.Pubkey; import lnrpc.ChannelEdge; import lnrpc.ChannelGraph; -import lnrpc.RoutingPolicy; import org.springframework.stereotype.Component; import java.time.Duration; @@ -19,12 +18,14 @@ import java.util.Set; @Component public class GrpcGraph { - private static final Policy DEFAULT_DISABLED_POLICY = new Policy(0, Coins.NONE, false, 0); + private static final Policy DEFAULT_DISABLED_POLICY = new Policy(0, Coins.NONE, false, 0, Coins.NONE); private final GrpcService grpcService; private final LoadingCache>> channelEdgeCache; + private final GrpcPolicy grpcPolicy; - public GrpcGraph(GrpcService grpcService) { + public GrpcGraph(GrpcService grpcService, GrpcPolicy grpcPolicy) { this.grpcService = grpcService; + this.grpcPolicy = grpcPolicy; channelEdgeCache = new CacheBuilder() .withExpiry(Duration.ofMinutes(2)) .withRefresh(Duration.ofMinutes(1)) @@ -69,24 +70,15 @@ public class GrpcGraph { private Policy getNode1Policy(ChannelEdge channelEdge) { if (channelEdge.hasNode1Policy()) { - return toPolicy(channelEdge.getNode1Policy()); + return grpcPolicy.toPolicy(channelEdge.getNode1Policy()); } return DEFAULT_DISABLED_POLICY; } private Policy getNode2Policy(ChannelEdge channelEdge) { if (channelEdge.hasNode2Policy()) { - return toPolicy(channelEdge.getNode2Policy()); + return grpcPolicy.toPolicy(channelEdge.getNode2Policy()); } return DEFAULT_DISABLED_POLICY; } - - private Policy toPolicy(RoutingPolicy routingPolicy) { - return new Policy( - routingPolicy.getFeeRateMilliMsat(), - Coins.ofMilliSatoshis(routingPolicy.getFeeBaseMsat()), - !routingPolicy.getDisabled(), - routingPolicy.getTimeLockDelta() - ); - } } diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcPolicy.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcPolicy.java new file mode 100644 index 00000000..3e11f681 --- /dev/null +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcPolicy.java @@ -0,0 +1,23 @@ +package de.cotto.lndmanagej.grpc; + +import de.cotto.lndmanagej.model.Coins; +import de.cotto.lndmanagej.model.Policy; +import lnrpc.RoutingPolicy; +import org.springframework.stereotype.Component; + +@Component +public class GrpcPolicy { + public GrpcPolicy() { + // default constructor + } + + public Policy toPolicy(RoutingPolicy routingPolicy) { + return new Policy( + routingPolicy.getFeeRateMilliMsat(), + Coins.ofMilliSatoshis(routingPolicy.getFeeBaseMsat()), + !routingPolicy.getDisabled(), + routingPolicy.getTimeLockDelta(), + Coins.ofMilliSatoshis(routingPolicy.getMaxHtlcMsat()) + ); + } +} diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicyTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicyTest.java index f7f74a80..4e7b23ea 100644 --- a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicyTest.java +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelPolicyTest.java @@ -20,6 +20,7 @@ import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_3; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_4; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; @@ -28,6 +29,7 @@ class GrpcChannelPolicyTest { private static final int FEE_RATE_FIRST = 123; private static final int FEE_RATE_SECOND = 456; private static final int TIME_LOCK_DELTA = 40; + private static final Coins MAX_HTLC = Coins.ofMilliSatoshis(5_432); @InjectMocks private GrpcChannelPolicy grpcChannelPolicy; @@ -38,23 +40,27 @@ class GrpcChannelPolicyTest { @Mock private GrpcGetInfo grpcGetInfo; + @Mock + private GrpcPolicy grpcPolicy; + @BeforeEach void setUp() { lenient().when(grpcGetInfo.getPubkey()).thenReturn(PUBKEY); + lenient().when(grpcPolicy.toPolicy(any())).thenCallRealMethod(); } @Test void getLocalPolicy_local_first() { when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY, PUBKEY_2))); assertThat(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID)) - .contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA)); + .contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); } @Test void getLocalPolicy_local_second() { when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY))); assertThat(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID)) - .contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA)); + .contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); } @Test @@ -72,14 +78,14 @@ class GrpcChannelPolicyTest { void getRemotePolicy_local_first() { when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY, PUBKEY_2))); assertThat(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID)) - .contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA)); + .contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); } @Test void getRemotePolicy_local_second() { when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY))); assertThat(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID)) - .contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA)); + .contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); } @Test @@ -92,14 +98,14 @@ class GrpcChannelPolicyTest { void getPolicyFrom_first() { when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3))); assertThat(grpcChannelPolicy.getPolicyFrom(CHANNEL_ID, PUBKEY_2)) - .contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA)); + .contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); } @Test void getPolicyFrom_second() { when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3))); assertThat(grpcChannelPolicy.getPolicyFrom(CHANNEL_ID, PUBKEY_3)) - .contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA)); + .contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); } @Test @@ -112,14 +118,14 @@ class GrpcChannelPolicyTest { void getPolicyTo_first() { when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3))); assertThat(grpcChannelPolicy.getPolicyTo(CHANNEL_ID, PUBKEY_3)) - .contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA)); + .contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); } @Test void getPolicyTo_second() { when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3))); assertThat(grpcChannelPolicy.getPolicyTo(CHANNEL_ID, PUBKEY_2)) - .contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA)); + .contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); } @Test @@ -166,6 +172,10 @@ class GrpcChannelPolicyTest { } private RoutingPolicy routingPolicy(int feeRate) { - return RoutingPolicy.newBuilder().setFeeRateMilliMsat(feeRate).setTimeLockDelta(TIME_LOCK_DELTA).build(); + return RoutingPolicy.newBuilder() + .setFeeRateMilliMsat(feeRate) + .setTimeLockDelta(TIME_LOCK_DELTA) + .setMaxHtlcMsat(5_432) + .build(); } } diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcGraphTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcGraphTest.java index e5f56873..6f668499 100644 --- a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcGraphTest.java +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcGraphTest.java @@ -6,6 +6,7 @@ import de.cotto.lndmanagej.model.Policy; import lnrpc.ChannelEdge; import lnrpc.ChannelGraph; import lnrpc.RoutingPolicy; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -24,15 +25,26 @@ import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_3; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_4; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class GrpcGraphTest { + private static final Coins MAX_HTLC = Coins.ofMilliSatoshis(5_432); + @InjectMocks + private GrpcGraph grpcGraph; + @Mock private GrpcService grpcService; - @InjectMocks - private GrpcGraph grpcGraph; + @Mock + private GrpcPolicy grpcPolicy; + + @BeforeEach + void setUp() { + lenient().when(grpcPolicy.toPolicy(any())).thenCallRealMethod(); + } @Test void empty() { @@ -62,14 +74,14 @@ class GrpcGraphTest { CAPACITY, PUBKEY, PUBKEY_2, - new Policy(0, Coins.NONE, false, 40) + new Policy(0, Coins.NONE, false, 40, MAX_HTLC) ); DirectedChannelEdge expectedEdge2 = new DirectedChannelEdge( CHANNEL_ID, CAPACITY, PUBKEY_2, PUBKEY, - new Policy(1, Coins.NONE, true, 144) + new Policy(1, Coins.NONE, true, 144, MAX_HTLC) ); ChannelEdge edge2 = ChannelEdge.newBuilder() .setChannelId(CHANNEL_ID_2.getShortChannelId()) @@ -84,14 +96,14 @@ class GrpcGraphTest { CAPACITY_2, PUBKEY_3, PUBKEY_4, - new Policy(456, Coins.NONE, true, 123) + new Policy(456, Coins.NONE, true, 123, MAX_HTLC) ); DirectedChannelEdge expectedEdge4 = new DirectedChannelEdge( CHANNEL_ID_2, CAPACITY_2, PUBKEY_4, PUBKEY_3, - new Policy(123, Coins.ofMilliSatoshis(1), true, 456) + new Policy(123, Coins.ofMilliSatoshis(1), true, 456, MAX_HTLC) ); ChannelGraph channelGraph = ChannelGraph.newBuilder() .addEdges(edge1) @@ -116,14 +128,14 @@ class GrpcGraphTest { CAPACITY, PUBKEY, PUBKEY_2, - new Policy(0, Coins.NONE, false, 0) + new Policy(0, Coins.NONE, false, 0, Coins.NONE) ); DirectedChannelEdge expectedPolicyForNode2 = new DirectedChannelEdge( CHANNEL_ID, CAPACITY, PUBKEY_2, PUBKEY, - new Policy(0, Coins.NONE, false, 0) + new Policy(0, Coins.NONE, false, 0, Coins.NONE) ); ChannelGraph channelGraph = ChannelGraph.newBuilder().addEdges(edgeWithMissingPolicy).build(); when(grpcService.describeGraph()).thenReturn(Optional.of(channelGraph)); @@ -137,6 +149,7 @@ class GrpcGraphTest { .setFeeBaseMsat(baseFee) .setDisabled(disabled) .setTimeLockDelta(timeLockDelta) + .setMaxHtlcMsat(5_432) .build(); } } diff --git a/model/src/main/java/de/cotto/lndmanagej/model/Policy.java b/model/src/main/java/de/cotto/lndmanagej/model/Policy.java index a4a630fe..3f5746d6 100644 --- a/model/src/main/java/de/cotto/lndmanagej/model/Policy.java +++ b/model/src/main/java/de/cotto/lndmanagej/model/Policy.java @@ -1,7 +1,7 @@ package de.cotto.lndmanagej.model; -public record Policy(long feeRate, Coins baseFee, boolean enabled, int timeLockDelta) { - public static final Policy UNKNOWN = new Policy(0, Coins.NONE, false, 0); +public record Policy(long feeRate, Coins baseFee, boolean enabled, int timeLockDelta, Coins maxHtlc) { + public static final Policy UNKNOWN = new Policy(0, Coins.NONE, false, 0, Coins.NONE); public boolean disabled() { return !enabled; diff --git a/model/src/test/java/de/cotto/lndmanagej/model/PolicyTest.java b/model/src/test/java/de/cotto/lndmanagej/model/PolicyTest.java index 40618954..dd2def23 100644 --- a/model/src/test/java/de/cotto/lndmanagej/model/PolicyTest.java +++ b/model/src/test/java/de/cotto/lndmanagej/model/PolicyTest.java @@ -30,8 +30,13 @@ class PolicyTest { assertThat(POLICY_DISABLED.disabled()).isTrue(); } + @Test + void maxHtlc() { + assertThat(POLICY_1.maxHtlc()).isEqualTo(Coins.ofSatoshis(10_000)); + } + @Test void unknown() { - assertThat(Policy.UNKNOWN).isEqualTo(new Policy(0, Coins.NONE, false, 0)); + assertThat(Policy.UNKNOWN).isEqualTo(new Policy(0, Coins.NONE, false, 0, Coins.NONE)); } } diff --git a/model/src/test/java/de/cotto/lndmanagej/model/RouteTest.java b/model/src/test/java/de/cotto/lndmanagej/model/RouteTest.java index 3938a2b5..14dc86c3 100644 --- a/model/src/test/java/de/cotto/lndmanagej/model/RouteTest.java +++ b/model/src/test/java/de/cotto/lndmanagej/model/RouteTest.java @@ -29,6 +29,7 @@ class RouteTest { private static final int ONE_MILLION = 1_000_000; private static final int TIME_LOCK_DELTA = 40; private static final int BLOCK_HEIGHT = 700_000; + private static final Coins MAX_HTLC = Coins.ofSatoshis(12_345); @Test void amount() { @@ -164,9 +165,9 @@ class RouteTest { Coins expectedFees = Coins.ofMilliSatoshis((long) (amount.milliSatoshis() * 1.0 * ppm2 / ONE_MILLION)) .add(baseFee2); - Policy policy1 = new Policy(ppm1, baseFee1, true, TIME_LOCK_DELTA); + Policy policy1 = policy(baseFee1, ppm1); Edge hop1 = new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy1); - Policy policy2 = new Policy(ppm2, baseFee2, true, TIME_LOCK_DELTA); + Policy policy2 = policy(baseFee2, ppm2); Edge hop2 = new Edge(CHANNEL_ID_2, PUBKEY_2, PUBKEY_3, CAPACITY, policy2); BasicRoute basicRoute = new BasicRoute(List.of(hop1, hop2), amount); Route route = new Route(basicRoute); @@ -179,7 +180,7 @@ class RouteTest { Coins baseFee = Coins.ofMilliSatoshis(10); int ppm = 100; BasicRoute basicRoute = new BasicRoute(List.of( - new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, new Policy(ppm, baseFee, true, TIME_LOCK_DELTA)) + new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy(baseFee, ppm)) ), amount); Route route = new Route(basicRoute); assertThat(route.getFees()).isEqualTo(Coins.NONE); @@ -198,8 +199,8 @@ class RouteTest { Coins expectedFees1 = Coins.NONE; Coins expectedFees = expectedFees1.add(expectedFees2); BasicRoute basicRoute = new BasicRoute(List.of( - new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, new Policy(ppm1, baseFee1, true, TIME_LOCK_DELTA)), - new Edge(CHANNEL_ID_2, PUBKEY, PUBKEY_2, CAPACITY, new Policy(ppm2, baseFee2, true, TIME_LOCK_DELTA)) + new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy(baseFee1, ppm1)), + new Edge(CHANNEL_ID_2, PUBKEY, PUBKEY_2, CAPACITY, policy(baseFee2, ppm2)) ), amount); Route route = new Route(basicRoute); assertThat(route.getFees()).isEqualTo(expectedFees); @@ -224,9 +225,9 @@ class RouteTest { ).add(baseFee2); Coins expectedFees = expectedFees1.add(expectedFees2).add(expectedFees3); BasicRoute basicRoute = new BasicRoute(List.of( - new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, new Policy(ppm1, baseFee1, true, TIME_LOCK_DELTA)), - new Edge(CHANNEL_ID_2, PUBKEY_2, PUBKEY_3, CAPACITY, new Policy(ppm2, baseFee2, true, TIME_LOCK_DELTA)), - new Edge(CHANNEL_ID_3, PUBKEY_3, PUBKEY_4, CAPACITY, new Policy(ppm3, baseFee3, true, TIME_LOCK_DELTA)) + new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy(baseFee1, ppm1)), + new Edge(CHANNEL_ID_2, PUBKEY_2, PUBKEY_3, CAPACITY, policy(baseFee2, ppm2)), + new Edge(CHANNEL_ID_3, PUBKEY_3, PUBKEY_4, CAPACITY, policy(baseFee3, ppm3)) ), amount); Route route = new Route(basicRoute); assertThat(route.getFees()).isEqualTo(expectedFees); @@ -246,7 +247,7 @@ class RouteTest { int ppm = 100; Coins expectedFees = Coins.ofMilliSatoshis(amount.milliSatoshis() * ppm / ONE_MILLION).add(baseFee); BasicRoute basicRoute = new BasicRoute(List.of( - new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, new Policy(ppm, baseFee, true, TIME_LOCK_DELTA)) + new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy(baseFee, ppm)) ), amount); Route route = new Route(basicRoute); assertThat(route.getFeesWithFirstHop()).isEqualTo(expectedFees); @@ -269,9 +270,9 @@ class RouteTest { Coins.ofMilliSatoshis(amountForFirstHop.milliSatoshis() * ppm1 / ONE_MILLION).add(baseFee1); Coins expectedFees = feesForFirstHop.add(feesForSecondHop).add(feesForThirdHop); BasicRoute basicRoute = new BasicRoute(List.of( - new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, new Policy(ppm1, baseFee1, true, TIME_LOCK_DELTA)), - new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, new Policy(ppm2, baseFee2, true, TIME_LOCK_DELTA)), - new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, new Policy(ppm3, baseFee3, true, TIME_LOCK_DELTA)) + new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy(baseFee1, ppm1)), + new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy(baseFee2, ppm2)), + new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy(baseFee3, ppm3)) ), amount); Route route = new Route(basicRoute); assertThat(route.getFeesWithFirstHop()).isEqualTo(expectedFees); @@ -399,8 +400,8 @@ class RouteTest { void feeRate_one_hop_with_base_fee() { int feeRate1 = 100; int feeRate2 = 987; - Policy policy1 = new Policy(feeRate1, Coins.ofSatoshis(100_000), true, TIME_LOCK_DELTA); - Policy policy2 = new Policy(feeRate2, Coins.ofSatoshis(10_000), true, TIME_LOCK_DELTA); + Policy policy1 = policy(Coins.ofSatoshis(100_000), feeRate1); + Policy policy2 = policy(Coins.ofSatoshis(10_000), feeRate2); Coins amount = Coins.ofSatoshis(1_234_567); Edge hop1 = new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy1); Edge hop2 = new Edge(CHANNEL_ID_2, PUBKEY_2, PUBKEY_3, CAPACITY, policy2); @@ -476,7 +477,7 @@ class RouteTest { private Route createRoute(Coins amount, int... feeRates) { List edges = Arrays.stream(feeRates) - .mapToObj(ppm -> new Policy(ppm, Coins.NONE, true, TIME_LOCK_DELTA)) + .mapToObj(ppm -> policy(Coins.NONE, ppm)) .map(policy -> new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy)) .toList(); return new Route(new BasicRoute(edges, amount)); @@ -484,8 +485,12 @@ class RouteTest { private List edgesWithTimeLockDeltas(int... timeLockDeltas) { return Arrays.stream(timeLockDeltas) - .mapToObj(timeLockDelta -> new Policy(0, Coins.NONE, true, timeLockDelta)) + .mapToObj(timeLockDelta -> new Policy(0, Coins.NONE, true, timeLockDelta, MAX_HTLC)) .map(policy -> new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy)) .toList(); } + + private Policy policy(Coins baseFee, int ppm) { + return new Policy(ppm, baseFee, true, TIME_LOCK_DELTA, MAX_HTLC); + } } diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/model/PolicyFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/model/PolicyFixtures.java index 5e6d6a0d..6b2b7c39 100644 --- a/model/src/testFixtures/java/de/cotto/lndmanagej/model/PolicyFixtures.java +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/model/PolicyFixtures.java @@ -1,10 +1,14 @@ package de.cotto.lndmanagej.model; public class PolicyFixtures { - public static final Policy POLICY_1 = new Policy(200, Coins.NONE, true, 40); - public static final Policy POLICY_WITH_BASE_FEE = new Policy(200, Coins.ofMilliSatoshis(10), true, 40); - public static final Policy POLICY_DISABLED = new Policy(200, Coins.NONE, false, 40); - public static final Policy POLICY_2 = new Policy(300, Coins.ofMilliSatoshis(0), true, 144); + public static final Policy POLICY_1 = + new Policy(200, Coins.NONE, true, 40, Coins.ofSatoshis(10_000)); + public static final Policy POLICY_WITH_BASE_FEE = + new Policy(200, Coins.ofMilliSatoshis(10), true, 40, Coins.ofSatoshis(10_000)); + public static final Policy POLICY_DISABLED = + new Policy(200, Coins.NONE, false, 40, Coins.ofSatoshis(0)); + public static final Policy POLICY_2 = + new Policy(300, Coins.ofMilliSatoshis(0), true, 144, Coins.ofSatoshis(22_222)); public static final PoliciesForLocalChannel POLICIES_FOR_LOCAL_CHANNEL = new PoliciesForLocalChannel(POLICY_DISABLED, POLICY_2); diff --git a/ui-demo/src/main/java/de/cotto/lndmanagej/ui/demo/data/DeriveDataUtil.java b/ui-demo/src/main/java/de/cotto/lndmanagej/ui/demo/data/DeriveDataUtil.java index bc33a599..eadf8df2 100644 --- a/ui-demo/src/main/java/de/cotto/lndmanagej/ui/demo/data/DeriveDataUtil.java +++ b/ui-demo/src/main/java/de/cotto/lndmanagej/ui/demo/data/DeriveDataUtil.java @@ -25,6 +25,7 @@ import static de.cotto.lndmanagej.model.OpenInitiator.REMOTE; public final class DeriveDataUtil { private static final Map RANDOM_GENERATOR = new HashMap<>(); + private static final Coins MAX_HTLC = Coins.ofSatoshis(1_000_000); private DeriveDataUtil() { // util class @@ -105,7 +106,7 @@ public final class DeriveDataUtil { Coins baseFee = Coins.ofMilliSatoshis(rand.nextLong(2) * 1000); boolean enabled = rand.nextInt(10) == 0; int timeLockDelta = (rand.nextInt(5) + 1) * 10; - return new Policy(feeRate, baseFee, enabled, timeLockDelta); + return new Policy(feeRate, baseFee, enabled, timeLockDelta, MAX_HTLC); } static Set deriveChannelWarnings(ChannelId channelId) { diff --git a/web/src/integrationTest/java/de/cotto/lndmanagej/controller/ChannelControllerIT.java b/web/src/integrationTest/java/de/cotto/lndmanagej/controller/ChannelControllerIT.java index 19482aac..1222694b 100644 --- a/web/src/integrationTest/java/de/cotto/lndmanagej/controller/ChannelControllerIT.java +++ b/web/src/integrationTest/java/de/cotto/lndmanagej/controller/ChannelControllerIT.java @@ -160,11 +160,15 @@ class ChannelControllerIT { .andExpect(jsonPath("$.balance.remoteReserveSat", is("10"))) .andExpect(jsonPath("$.balance.remoteAvailableSat", is("113"))) .andExpect(jsonPath("$.policies.local.enabled", is(false))) - .andExpect(jsonPath("$.policies.remote.enabled", is(true))) .andExpect(jsonPath("$.policies.local.feeRatePpm", is(200))) .andExpect(jsonPath("$.policies.local.baseFeeMilliSat", is(0))) + .andExpect(jsonPath("$.policies.local.timeLockDelta", is(40))) + .andExpect(jsonPath("$.policies.local.maxHtlcMilliSat", is("0"))) + .andExpect(jsonPath("$.policies.remote.enabled", is(true))) .andExpect(jsonPath("$.policies.remote.feeRatePpm", is(300))) .andExpect(jsonPath("$.policies.remote.baseFeeMilliSat", is(0))) + .andExpect(jsonPath("$.policies.remote.timeLockDelta", is(144))) + .andExpect(jsonPath("$.policies.remote.maxHtlcMilliSat", is("22222000"))) .andExpect(jsonPath("$.feeReport.earnedMilliSat", is("1234"))) .andExpect(jsonPath("$.feeReport.sourcedMilliSat", is("567"))) .andExpect(jsonPath("$.flowReport.forwardedSentMilliSat", is("1000"))) diff --git a/web/src/main/java/de/cotto/lndmanagej/controller/dto/PolicyDto.java b/web/src/main/java/de/cotto/lndmanagej/controller/dto/PolicyDto.java index 44fc658e..737149c3 100644 --- a/web/src/main/java/de/cotto/lndmanagej/controller/dto/PolicyDto.java +++ b/web/src/main/java/de/cotto/lndmanagej/controller/dto/PolicyDto.java @@ -5,13 +5,17 @@ import de.cotto.lndmanagej.model.Policy; public record PolicyDto( long feeRatePpm, long baseFeeMilliSat, - boolean enabled + boolean enabled, + int timeLockDelta, + String maxHtlcMilliSat ) { public static PolicyDto createFromModel(Policy policy) { return new PolicyDto( policy.feeRate(), policy.baseFee().milliSatoshis(), - policy.enabled() + policy.enabled(), + policy.timeLockDelta(), + String.valueOf(policy.maxHtlc().milliSatoshis()) ); } } diff --git a/web/src/test/java/de/cotto/lndmanagej/controller/dto/PolicyDtoTest.java b/web/src/test/java/de/cotto/lndmanagej/controller/dto/PolicyDtoTest.java index afe846af..34c614e3 100644 --- a/web/src/test/java/de/cotto/lndmanagej/controller/dto/PolicyDtoTest.java +++ b/web/src/test/java/de/cotto/lndmanagej/controller/dto/PolicyDtoTest.java @@ -9,14 +9,14 @@ import static org.assertj.core.api.Assertions.assertThat; class PolicyDtoTest { @Test void createFromModel_disabled() { - PolicyDto expected = new PolicyDto(200, 0, false); + PolicyDto expected = new PolicyDto(200, 0, false, 40, "0"); PolicyDto dto = PolicyDto.createFromModel(POLICY_DISABLED); assertThat(dto).isEqualTo(expected); } @Test void createFromModel_with_base_fee() { - PolicyDto expected = new PolicyDto(200, 10, true); + PolicyDto expected = new PolicyDto(200, 10, true, 40, "10000000"); PolicyDto dto = PolicyDto.createFromModel(POLICY_WITH_BASE_FEE); assertThat(dto).isEqualTo(expected); }