export "min_htlc" as part of channel details

This commit is contained in:
Carsten Otto
2023-09-10 14:47:31 +02:00
parent 2a8b782481
commit 5e61301e15
20 changed files with 73 additions and 45 deletions

View File

@@ -19,6 +19,7 @@ import java.util.stream.Collectors;
@Component @Component
public class RouteHintService { public class RouteHintService {
private static final Coins ONE_MILLI_SATOSHI = Coins.ofMilliSatoshis(1);
private static final Coins FIFTY_COINS = Coins.ofSatoshis(5_000_000_000L); private static final Coins FIFTY_COINS = Coins.ofSatoshis(5_000_000_000L);
private static final Duration MAX_AGE = Duration.ofHours(1); private static final Duration MAX_AGE = Duration.ofHours(1);
@@ -55,7 +56,14 @@ public class RouteHintService {
} }
private Policy toPolicy(RouteHint routeHint) { private Policy toPolicy(RouteHint routeHint) {
return new Policy(routeHint.feeRate(), routeHint.baseFee(), true, routeHint.cltvExpiryDelta(), FIFTY_COINS); return new Policy(
routeHint.feeRate(),
routeHint.baseFee(),
true,
routeHint.cltvExpiryDelta(),
ONE_MILLI_SATOSHI,
FIFTY_COINS
);
} }
@SuppressWarnings("UnusedVariable") @SuppressWarnings("UnusedVariable")

View File

@@ -77,7 +77,7 @@ class GraphServiceTest {
@Test @Test
void getNodesWithHighFeeRate_ignores_disabled_channels() { void getNodesWithHighFeeRate_ignores_disabled_channels() {
Set<DirectedChannelEdge> edges = edges(9, 200, PUBKEY_2); Set<DirectedChannelEdge> edges = edges(9, 200, PUBKEY_2);
Policy policy = new Policy(2_000, Coins.NONE, false, 40, Coins.ofSatoshis(10_000)); Policy policy = new Policy(2_000, Coins.NONE, false, 40, Coins.ofMilliSatoshis(1), Coins.ofSatoshis(10_000));
edges.add(edge(policy, Coins.ofSatoshis(10_000_000), PUBKEY_2)); edges.add(edge(policy, Coins.ofSatoshis(10_000_000), PUBKEY_2));
assertThat(graphService.getNodesWithHighFeeRate()).isEmpty(); assertThat(graphService.getNodesWithHighFeeRate()).isEmpty();
} }
@@ -135,7 +135,7 @@ class GraphServiceTest {
} }
private DirectedChannelEdge edge(long feeRate, long capacityMillionSat, Pubkey target) { private DirectedChannelEdge edge(long feeRate, long capacityMillionSat, Pubkey target) {
Policy policy = new Policy(feeRate, Coins.NONE, true, 40, Coins.ofSatoshis(10_000)); Policy policy = new Policy(feeRate, Coins.NONE, true, 40, Coins.ofMilliSatoshis(1), Coins.ofSatoshis(10_000));
Coins capacity = Coins.ofSatoshis(capacityMillionSat * 1_000_000); Coins capacity = Coins.ofSatoshis(capacityMillionSat * 1_000_000);
return edge(policy, capacity, target); return edge(policy, capacity, target);
} }

View File

@@ -108,8 +108,9 @@ class PolicyServiceTest {
} }
private void mockPolicy(ChannelId channelId, int feeRate) { private void mockPolicy(ChannelId channelId, int feeRate) {
Policy policy = new Policy(feeRate, Coins.NONE, false, 0, Coins.NONE, Coins.NONE);
when(grpcChannelPolicy.getPolicyFrom(channelId, PUBKEY)) when(grpcChannelPolicy.getPolicyFrom(channelId, PUBKEY))
.thenReturn(Optional.of(new Policy(feeRate, Coins.NONE, false, 0, Coins.NONE))); .thenReturn(Optional.of(policy));
} }
} }
@@ -137,8 +138,9 @@ class PolicyServiceTest {
} }
private void mockPolicy(ChannelId channelId, int feeRate) { private void mockPolicy(ChannelId channelId, int feeRate) {
Policy policy = new Policy(feeRate, Coins.NONE, false, 0, Coins.NONE, Coins.NONE);
when(grpcChannelPolicy.getPolicyTo(channelId, PUBKEY)) when(grpcChannelPolicy.getPolicyTo(channelId, PUBKEY))
.thenReturn(Optional.of(new Policy(feeRate, Coins.NONE, false, 0, Coins.NONE))); .thenReturn(Optional.of(policy));
} }
} }
} }

View File

@@ -23,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThatCode;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class RouteHintServiceTest { class RouteHintServiceTest {
private static final Coins ONE_MILLI_SATOSHI = Coins.ofMilliSatoshis(1);
private static final Coins FIFTY_COINS = Coins.ofSatoshis(5_000_000_000L); private static final Coins FIFTY_COINS = Coins.ofSatoshis(5_000_000_000L);
@InjectMocks @InjectMocks
@@ -36,8 +37,8 @@ class RouteHintServiceTest {
@Test @Test
void get_after_adding_decoded_payment_request() { void get_after_adding_decoded_payment_request() {
routeHintService.addDecodedPaymentRequest(DECODED_PAYMENT_REQUEST); routeHintService.addDecodedPaymentRequest(DECODED_PAYMENT_REQUEST);
Policy policy1 = new Policy(123, Coins.NONE, true, 9, FIFTY_COINS); Policy policy1 = new Policy(123, Coins.NONE, true, 9, ONE_MILLI_SATOSHI, FIFTY_COINS);
Policy policy2 = new Policy(1234, Coins.ofMilliSatoshis(1), true, 40, FIFTY_COINS); Policy policy2 = new Policy(1234, ONE_MILLI_SATOSHI, true, 40, ONE_MILLI_SATOSHI, FIFTY_COINS);
DirectedChannelEdge edge1 = new DirectedChannelEdge(CHANNEL_ID, FIFTY_COINS, PUBKEY, PUBKEY_4, policy1); DirectedChannelEdge edge1 = new DirectedChannelEdge(CHANNEL_ID, FIFTY_COINS, PUBKEY, PUBKEY_4, policy1);
DirectedChannelEdge edge2 = new DirectedChannelEdge(CHANNEL_ID_2, FIFTY_COINS, PUBKEY_3, PUBKEY_4, policy2); DirectedChannelEdge edge2 = new DirectedChannelEdge(CHANNEL_ID_2, FIFTY_COINS, PUBKEY_3, PUBKEY_4, policy2);
assertThat(routeHintService.getEdgesFromPaymentHints()).contains(edge1, edge2); assertThat(routeHintService.getEdgesFromPaymentHints()).contains(edge1, edge2);

View File

@@ -18,7 +18,7 @@ import java.util.Set;
@Component @Component
public class GrpcGraph { public class GrpcGraph {
private static final Policy DEFAULT_DISABLED_POLICY = new Policy(0, Coins.NONE, false, 0, Coins.NONE); private static final Policy DEFAULT_DISABLED_POLICY = new Policy(0, Coins.NONE, false, 0, Coins.NONE, Coins.NONE);
private final GrpcService grpcService; private final GrpcService grpcService;
private final LoadingCache<Object, Optional<Set<DirectedChannelEdge>>> channelEdgeCache; private final LoadingCache<Object, Optional<Set<DirectedChannelEdge>>> channelEdgeCache;
private final GrpcPolicy grpcPolicy; private final GrpcPolicy grpcPolicy;

View File

@@ -17,6 +17,7 @@ public class GrpcPolicy {
Coins.ofMilliSatoshis(routingPolicy.getFeeBaseMsat()), Coins.ofMilliSatoshis(routingPolicy.getFeeBaseMsat()),
!routingPolicy.getDisabled(), !routingPolicy.getDisabled(),
routingPolicy.getTimeLockDelta(), routingPolicy.getTimeLockDelta(),
Coins.ofMilliSatoshis(routingPolicy.getMinHtlc()),
Coins.ofMilliSatoshis(routingPolicy.getMaxHtlcMsat()) Coins.ofMilliSatoshis(routingPolicy.getMaxHtlcMsat())
); );
} }

View File

@@ -29,6 +29,7 @@ class GrpcChannelPolicyTest {
private static final int FEE_RATE_FIRST = 123; private static final int FEE_RATE_FIRST = 123;
private static final int FEE_RATE_SECOND = 456; private static final int FEE_RATE_SECOND = 456;
private static final int TIME_LOCK_DELTA = 40; private static final int TIME_LOCK_DELTA = 40;
private static final Coins MIN_HTLC = Coins.ofMilliSatoshis(159);
private static final Coins MAX_HTLC = Coins.ofMilliSatoshis(5_432); private static final Coins MAX_HTLC = Coins.ofMilliSatoshis(5_432);
@InjectMocks @InjectMocks
@@ -53,14 +54,14 @@ class GrpcChannelPolicyTest {
void getLocalPolicy_local_first() { void getLocalPolicy_local_first() {
when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY, PUBKEY_2))); when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY, PUBKEY_2)));
assertThat(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID)) assertThat(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID))
.contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); .contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA, MIN_HTLC, MAX_HTLC));
} }
@Test @Test
void getLocalPolicy_local_second() { void getLocalPolicy_local_second() {
when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY))); when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY)));
assertThat(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID)) assertThat(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID))
.contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); .contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA, MIN_HTLC, MAX_HTLC));
} }
@Test @Test
@@ -78,14 +79,14 @@ class GrpcChannelPolicyTest {
void getRemotePolicy_local_first() { void getRemotePolicy_local_first() {
when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY, PUBKEY_2))); when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY, PUBKEY_2)));
assertThat(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID)) assertThat(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID))
.contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); .contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA, MIN_HTLC, MAX_HTLC));
} }
@Test @Test
void getRemotePolicy_local_second() { void getRemotePolicy_local_second() {
when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY))); when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY)));
assertThat(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID)) assertThat(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID))
.contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); .contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA, MIN_HTLC, MAX_HTLC));
} }
@Test @Test
@@ -98,14 +99,14 @@ class GrpcChannelPolicyTest {
void getPolicyFrom_first() { void getPolicyFrom_first() {
when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3))); when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3)));
assertThat(grpcChannelPolicy.getPolicyFrom(CHANNEL_ID, PUBKEY_2)) assertThat(grpcChannelPolicy.getPolicyFrom(CHANNEL_ID, PUBKEY_2))
.contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); .contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA, MIN_HTLC, MAX_HTLC));
} }
@Test @Test
void getPolicyFrom_second() { void getPolicyFrom_second() {
when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3))); when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3)));
assertThat(grpcChannelPolicy.getPolicyFrom(CHANNEL_ID, PUBKEY_3)) assertThat(grpcChannelPolicy.getPolicyFrom(CHANNEL_ID, PUBKEY_3))
.contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); .contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA, MIN_HTLC, MAX_HTLC));
} }
@Test @Test
@@ -118,14 +119,14 @@ class GrpcChannelPolicyTest {
void getPolicyTo_first() { void getPolicyTo_first() {
when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3))); when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3)));
assertThat(grpcChannelPolicy.getPolicyTo(CHANNEL_ID, PUBKEY_3)) assertThat(grpcChannelPolicy.getPolicyTo(CHANNEL_ID, PUBKEY_3))
.contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); .contains(new Policy(FEE_RATE_FIRST, Coins.NONE, true, TIME_LOCK_DELTA, MIN_HTLC, MAX_HTLC));
} }
@Test @Test
void getPolicyTo_second() { void getPolicyTo_second() {
when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3))); when(grpcService.getChannelEdge(CHANNEL_ID)).thenReturn(Optional.of(channelEdge(PUBKEY_2, PUBKEY_3)));
assertThat(grpcChannelPolicy.getPolicyTo(CHANNEL_ID, PUBKEY_2)) assertThat(grpcChannelPolicy.getPolicyTo(CHANNEL_ID, PUBKEY_2))
.contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA, MAX_HTLC)); .contains(new Policy(FEE_RATE_SECOND, Coins.NONE, true, TIME_LOCK_DELTA, MIN_HTLC, MAX_HTLC));
} }
@Test @Test
@@ -175,7 +176,8 @@ class GrpcChannelPolicyTest {
return RoutingPolicy.newBuilder() return RoutingPolicy.newBuilder()
.setFeeRateMilliMsat(feeRate) .setFeeRateMilliMsat(feeRate)
.setTimeLockDelta(TIME_LOCK_DELTA) .setTimeLockDelta(TIME_LOCK_DELTA)
.setMaxHtlcMsat(5_432) .setMinHtlc(MIN_HTLC.milliSatoshis())
.setMaxHtlcMsat(MAX_HTLC.milliSatoshis())
.build(); .build();
} }
} }

View File

@@ -33,6 +33,7 @@ import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class GrpcGraphTest { class GrpcGraphTest {
private static final Coins MIN_HTLC = Coins.ofMilliSatoshis(159);
private static final Coins MAX_HTLC = Coins.ofMilliSatoshis(5_432); private static final Coins MAX_HTLC = Coins.ofMilliSatoshis(5_432);
@InjectMocks @InjectMocks
private GrpcGraph grpcGraph; private GrpcGraph grpcGraph;
@@ -76,14 +77,14 @@ class GrpcGraphTest {
CAPACITY, CAPACITY,
PUBKEY, PUBKEY,
PUBKEY_2, PUBKEY_2,
new Policy(0, Coins.NONE, false, 40, MAX_HTLC) new Policy(0, Coins.NONE, false, 40, MIN_HTLC, MAX_HTLC)
); );
DirectedChannelEdge expectedEdge2 = new DirectedChannelEdge( DirectedChannelEdge expectedEdge2 = new DirectedChannelEdge(
CHANNEL_ID, CHANNEL_ID,
CAPACITY, CAPACITY,
PUBKEY_2, PUBKEY_2,
PUBKEY, PUBKEY,
new Policy(1, Coins.NONE, true, 144, MAX_HTLC) new Policy(1, Coins.NONE, true, 144, MIN_HTLC, MAX_HTLC)
); );
ChannelEdge edge2 = ChannelEdge.newBuilder() ChannelEdge edge2 = ChannelEdge.newBuilder()
.setChannelId(CHANNEL_ID_2.getShortChannelId()) .setChannelId(CHANNEL_ID_2.getShortChannelId())
@@ -98,14 +99,14 @@ class GrpcGraphTest {
CAPACITY_2, CAPACITY_2,
PUBKEY_3, PUBKEY_3,
PUBKEY_4, PUBKEY_4,
new Policy(456, Coins.NONE, true, 123, MAX_HTLC) new Policy(456, Coins.NONE, true, 123, MIN_HTLC, MAX_HTLC)
); );
DirectedChannelEdge expectedEdge4 = new DirectedChannelEdge( DirectedChannelEdge expectedEdge4 = new DirectedChannelEdge(
CHANNEL_ID_2, CHANNEL_ID_2,
CAPACITY_2, CAPACITY_2,
PUBKEY_4, PUBKEY_4,
PUBKEY_3, PUBKEY_3,
new Policy(123, Coins.ofMilliSatoshis(1), true, 456, MAX_HTLC) new Policy(123, Coins.ofMilliSatoshis(1), true, 456, MIN_HTLC, MAX_HTLC)
); );
ChannelGraph channelGraph = ChannelGraph.newBuilder() ChannelGraph channelGraph = ChannelGraph.newBuilder()
.addEdges(edge1) .addEdges(edge1)
@@ -130,14 +131,14 @@ class GrpcGraphTest {
CAPACITY, CAPACITY,
PUBKEY, PUBKEY,
PUBKEY_2, PUBKEY_2,
new Policy(0, Coins.NONE, false, 0, Coins.NONE) new Policy(0, Coins.NONE, false, 0, Coins.NONE, Coins.NONE)
); );
DirectedChannelEdge expectedPolicyForNode2 = new DirectedChannelEdge( DirectedChannelEdge expectedPolicyForNode2 = new DirectedChannelEdge(
CHANNEL_ID, CHANNEL_ID,
CAPACITY, CAPACITY,
PUBKEY_2, PUBKEY_2,
PUBKEY, PUBKEY,
new Policy(0, Coins.NONE, false, 0, Coins.NONE) new Policy(0, Coins.NONE, false, 0, Coins.NONE, Coins.NONE)
); );
ChannelGraph channelGraph = ChannelGraph.newBuilder().addEdges(edgeWithMissingPolicy).build(); ChannelGraph channelGraph = ChannelGraph.newBuilder().addEdges(edgeWithMissingPolicy).build();
when(grpcService.describeGraph()).thenReturn(Optional.of(channelGraph)); when(grpcService.describeGraph()).thenReturn(Optional.of(channelGraph));
@@ -160,7 +161,8 @@ class GrpcGraphTest {
.setFeeBaseMsat(baseFee) .setFeeBaseMsat(baseFee)
.setDisabled(disabled) .setDisabled(disabled)
.setTimeLockDelta(timeLockDelta) .setTimeLockDelta(timeLockDelta)
.setMaxHtlcMsat(5_432) .setMinHtlc(MIN_HTLC.milliSatoshis())
.setMaxHtlcMsat(MAX_HTLC.milliSatoshis())
.build(); .build();
} }
} }

View File

@@ -1,7 +1,7 @@
package de.cotto.lndmanagej.model; package de.cotto.lndmanagej.model;
public record Policy(long feeRate, Coins baseFee, boolean enabled, int timeLockDelta, Coins maxHtlc) { public record Policy(long feeRate, Coins baseFee, boolean enabled, int timeLockDelta, Coins minHtlc, Coins maxHtlc) {
public static final Policy UNKNOWN = new Policy(0, Coins.NONE, false, 0, Coins.NONE); public static final Policy UNKNOWN = new Policy(0, Coins.NONE, false, 0, Coins.NONE, Coins.NONE);
public boolean disabled() { public boolean disabled() {
return !enabled; return !enabled;

View File

@@ -30,6 +30,11 @@ class PolicyTest {
assertThat(POLICY_DISABLED.disabled()).isTrue(); assertThat(POLICY_DISABLED.disabled()).isTrue();
} }
@Test
void minHtlc() {
assertThat(POLICY_1.minHtlc()).isEqualTo(Coins.ofSatoshis(159));
}
@Test @Test
void maxHtlc() { void maxHtlc() {
assertThat(POLICY_1.maxHtlc()).isEqualTo(Coins.ofSatoshis(10_000)); assertThat(POLICY_1.maxHtlc()).isEqualTo(Coins.ofSatoshis(10_000));
@@ -37,6 +42,6 @@ class PolicyTest {
@Test @Test
void unknown() { void unknown() {
assertThat(Policy.UNKNOWN).isEqualTo(new Policy(0, Coins.NONE, false, 0, Coins.NONE)); assertThat(Policy.UNKNOWN).isEqualTo(new Policy(0, Coins.NONE, false, 0, Coins.NONE, Coins.NONE));
} }
} }

View File

@@ -29,6 +29,7 @@ class RouteTest {
private static final int ONE_MILLION = 1_000_000; private static final int ONE_MILLION = 1_000_000;
private static final int TIME_LOCK_DELTA = 40; private static final int TIME_LOCK_DELTA = 40;
private static final int BLOCK_HEIGHT = 700_000; private static final int BLOCK_HEIGHT = 700_000;
private static final Coins MIN_HTLC = Coins.ofSatoshis(159);
private static final Coins MAX_HTLC = Coins.ofSatoshis(12_345); private static final Coins MAX_HTLC = Coins.ofSatoshis(12_345);
@Test @Test
@@ -495,12 +496,12 @@ class RouteTest {
private List<Edge> edgesWithTimeLockDeltas(int... timeLockDeltas) { private List<Edge> edgesWithTimeLockDeltas(int... timeLockDeltas) {
return Arrays.stream(timeLockDeltas) return Arrays.stream(timeLockDeltas)
.mapToObj(timeLockDelta -> new Policy(0, Coins.NONE, true, timeLockDelta, MAX_HTLC)) .mapToObj(timeLockDelta -> new Policy(0, Coins.NONE, true, timeLockDelta, MIN_HTLC, MAX_HTLC))
.map(policy -> new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy)) .map(policy -> new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy))
.toList(); .toList();
} }
private Policy policy(Coins baseFee, int ppm) { private Policy policy(Coins baseFee, int ppm) {
return new Policy(ppm, baseFee, true, TIME_LOCK_DELTA, MAX_HTLC); return new Policy(ppm, baseFee, true, TIME_LOCK_DELTA, MIN_HTLC, MAX_HTLC);
} }
} }

View File

@@ -2,13 +2,13 @@ package de.cotto.lndmanagej.model;
public class PolicyFixtures { public class PolicyFixtures {
public static final Policy POLICY_1 = public static final Policy POLICY_1 =
new Policy(200, Coins.NONE, true, 40, Coins.ofSatoshis(10_000)); new Policy(200, Coins.NONE, true, 40, Coins.ofSatoshis(159), Coins.ofSatoshis(10_000));
public static final Policy POLICY_WITH_BASE_FEE = public static final Policy POLICY_WITH_BASE_FEE =
new Policy(200, Coins.ofMilliSatoshis(10), true, 40, Coins.ofSatoshis(10_000)); new Policy(200, Coins.ofMilliSatoshis(10), true, 40, Coins.ofSatoshis(159), Coins.ofSatoshis(10_000));
public static final Policy POLICY_DISABLED = public static final Policy POLICY_DISABLED =
new Policy(200, Coins.NONE, false, 40, Coins.ofSatoshis(0)); new Policy(200, Coins.NONE, false, 40, Coins.NONE, Coins.NONE);
public static final Policy POLICY_2 = public static final Policy POLICY_2 =
new Policy(300, Coins.ofMilliSatoshis(0), true, 144, Coins.ofSatoshis(22_222)); new Policy(300, Coins.ofMilliSatoshis(0), true, 144, Coins.ofSatoshis(159), Coins.ofSatoshis(22_222));
public static final PoliciesForLocalChannel POLICIES_FOR_LOCAL_CHANNEL = public static final PoliciesForLocalChannel POLICIES_FOR_LOCAL_CHANNEL =
new PoliciesForLocalChannel(POLICY_DISABLED, POLICY_2); new PoliciesForLocalChannel(POLICY_DISABLED, POLICY_2);

View File

@@ -324,7 +324,7 @@ class ArcInitializerTest {
ArcInitializer arcInitializer = getArcInitializer(quantization, feeRateWeight); ArcInitializer arcInitializer = getArcInitializer(quantization, feeRateWeight);
addEdgeWithBaseFee(baseFee, quantization, arcInitializer); addEdgeWithBaseFee(baseFee, quantization, arcInitializer);
long expectedFeeRate = (long) Math.ceil(200 + 10); long expectedFeeRate = 210;
assertThat(minCostFlow.getUnitCost(0)).isEqualTo(expectedFeeRate); assertThat(minCostFlow.getUnitCost(0)).isEqualTo(expectedFeeRate);
} }
@@ -337,12 +337,12 @@ class ArcInitializerTest {
ArcInitializer arcInitializer = getArcInitializer(quantization, feeRateWeight); ArcInitializer arcInitializer = getArcInitializer(quantization, feeRateWeight);
addEdgeWithBaseFee(baseFee, quantization, arcInitializer); addEdgeWithBaseFee(baseFee, quantization, arcInitializer);
long expectedFeeRate = (long) Math.ceil(200 + 12); long expectedFeeRate = 212;
assertThat(minCostFlow.getUnitCost(0)).isEqualTo(expectedFeeRate); assertThat(minCostFlow.getUnitCost(0)).isEqualTo(expectedFeeRate);
} }
private void addEdgeWithBaseFee(Coins baseFee, int quantization, ArcInitializer arcInitializer) { private void addEdgeWithBaseFee(Coins baseFee, int quantization, ArcInitializer arcInitializer) {
Policy policy = new Policy(200, baseFee, true, 40, Coins.ofSatoshis(10_000)); Policy policy = new Policy(200, baseFee, true, 40, Coins.ofMilliSatoshis(1), Coins.ofSatoshis(10_000));
EdgeWithLiquidityInformation edge = EdgeWithLiquidityInformation.forKnownLiquidity( EdgeWithLiquidityInformation edge = EdgeWithLiquidityInformation.forKnownLiquidity(
new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy), new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policy),
Coins.ofSatoshis(30L * quantization) Coins.ofSatoshis(30L * quantization)

View File

@@ -117,7 +117,7 @@ class EdgeComputationTest {
void does_not_add_edge_exceeding_maximum_time_lock_delta() { void does_not_add_edge_exceeding_maximum_time_lock_delta() {
int edgeDelta = 40; int edgeDelta = 40;
int maximumTimeLockDelta = edgeDelta - 1; int maximumTimeLockDelta = edgeDelta - 1;
Policy policy = new Policy(0, Coins.NONE, true, edgeDelta, Coins.ofSatoshis(10_000)); Policy policy = new Policy(0, Coins.NONE, true, edgeDelta, Coins.ofMilliSatoshis(1), Coins.ofSatoshis(10_000));
DirectedChannelEdge edge = new DirectedChannelEdge(CHANNEL_ID, CAPACITY, PUBKEY, PUBKEY_2, policy); DirectedChannelEdge edge = new DirectedChannelEdge(CHANNEL_ID, CAPACITY, PUBKEY, PUBKEY_2, policy);
when(grpcGraph.getChannelEdges()).thenReturn(Optional.of(Set.of(edge))); when(grpcGraph.getChannelEdges()).thenReturn(Optional.of(Set.of(edge)));
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS, maximumTimeLockDelta).edges()).isEmpty(); assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS, maximumTimeLockDelta).edges()).isEmpty();
@@ -228,8 +228,9 @@ class EdgeComputationTest {
@Test @Test
void adds_edge_from_route_hint_service() { void adds_edge_from_route_hint_service() {
when(grpcGraph.getChannelEdges()).thenReturn(Optional.of(Set.of())); when(grpcGraph.getChannelEdges()).thenReturn(Optional.of(Set.of()));
Coins oneMilliSatoshi = Coins.ofMilliSatoshis(1);
Coins fiftyCoins = Coins.ofSatoshis(5_000_000_000L); Coins fiftyCoins = Coins.ofSatoshis(5_000_000_000L);
Policy policy = new Policy(200, Coins.NONE, true, 40, fiftyCoins); Policy policy = new Policy(200, Coins.NONE, true, 40, oneMilliSatoshi, fiftyCoins);
Edge edge = new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, fiftyCoins, policy); Edge edge = new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, fiftyCoins, policy);
when(routeHintService.getEdgesFromPaymentHints()).thenReturn(Set.of( when(routeHintService.getEdgesFromPaymentHints()).thenReturn(Set.of(
new DirectedChannelEdge( new DirectedChannelEdge(
@@ -447,7 +448,7 @@ class EdgeComputationTest {
} }
private static Policy policy(int feeRate) { private static Policy policy(int feeRate) {
return new Policy(feeRate, Coins.NONE, true, 40, Coins.ofSatoshis(0)); return new Policy(feeRate, Coins.NONE, true, 40, Coins.NONE, Coins.NONE);
} }
private void mockInactiveChannel() { private void mockInactiveChannel() {

View File

@@ -626,7 +626,7 @@ class MultiPathPaymentSplitterTest {
private Edge mockExtensionEdge(Pubkey destination, int feeRate) { private Edge mockExtensionEdge(Pubkey destination, int feeRate) {
Policy policyExtension = Policy policyExtension =
new Policy(feeRate, Coins.NONE, true, 40, Coins.ofSatoshis(10_000)); new Policy(feeRate, Coins.NONE, true, 40, Coins.ofMilliSatoshis(1), Coins.ofSatoshis(10_000));
when(channelService.getOpenChannelsWith(PUBKEY_2)).thenReturn(Set.of(LOCAL_OPEN_CHANNEL)); when(channelService.getOpenChannelsWith(PUBKEY_2)).thenReturn(Set.of(LOCAL_OPEN_CHANNEL));
when(policyService.getPolicyFrom(CHANNEL_ID, PUBKEY_2)).thenReturn(Optional.of(policyExtension)); when(policyService.getPolicyFrom(CHANNEL_ID, PUBKEY_2)).thenReturn(Optional.of(policyExtension));
Edge extensionEdge = Edge extensionEdge =
@@ -637,7 +637,7 @@ class MultiPathPaymentSplitterTest {
} }
private static Policy policyFor(int feeRate) { private static Policy policyFor(int feeRate) {
return new Policy(feeRate, Coins.NONE, true, 40, Coins.ofSatoshis(10_000)); return new Policy(feeRate, Coins.NONE, true, 40, Coins.ofMilliSatoshis(1), Coins.ofSatoshis(10_000));
} }
private EdgeWithLiquidityInformation noInformationFor(Edge edgeSmallCapacity) { private EdgeWithLiquidityInformation noInformationFor(Edge edgeSmallCapacity) {

View File

@@ -25,6 +25,7 @@ import static de.cotto.lndmanagej.model.OpenInitiator.REMOTE;
@SuppressWarnings("PMD.TooManyMethods") @SuppressWarnings("PMD.TooManyMethods")
public final class DeriveDataUtil { public final class DeriveDataUtil {
private static final Coins MIN_HTLC = Coins.ofMilliSatoshis(1);
private static final Coins MAX_HTLC = Coins.ofSatoshis(1_000_000); private static final Coins MAX_HTLC = Coins.ofSatoshis(1_000_000);
private DeriveDataUtil() { private DeriveDataUtil() {
@@ -98,7 +99,7 @@ public final class DeriveDataUtil {
Coins baseFee = Coins.ofMilliSatoshis(rand.nextLong(2) * 1000); Coins baseFee = Coins.ofMilliSatoshis(rand.nextLong(2) * 1000);
boolean enabled = rand.nextInt(10) != 0; boolean enabled = rand.nextInt(10) != 0;
int timeLockDelta = (rand.nextInt(5) + 1) * 10; int timeLockDelta = (rand.nextInt(5) + 1) * 10;
return new Policy(feeRate, baseFee, enabled, timeLockDelta, MAX_HTLC); return new Policy(feeRate, baseFee, enabled, timeLockDelta, MIN_HTLC, MAX_HTLC);
} }
static ChannelStatusDto deriveChannelStatus(ChannelId channelId) { static ChannelStatusDto deriveChannelStatus(ChannelId channelId) {

View File

@@ -397,7 +397,7 @@ class PageServiceTest {
} }
private static PolicyDto policy(int feeRate, int baseFee) { private static PolicyDto policy(int feeRate, int baseFee) {
return new PolicyDto(feeRate, String.valueOf(baseFee), true, 0, "0"); return new PolicyDto(feeRate, String.valueOf(baseFee), true, 0, "0", "0");
} }
private BalanceInformation balanceWithRemoteSat(int satoshis) { private BalanceInformation balanceWithRemoteSat(int satoshis) {

View File

@@ -170,11 +170,13 @@ class ChannelControllerIT {
.jsonPath("$.policies.local.feeRatePpm").value(is(200)) .jsonPath("$.policies.local.feeRatePpm").value(is(200))
.jsonPath("$.policies.local.baseFeeMilliSat").value(is("0")) .jsonPath("$.policies.local.baseFeeMilliSat").value(is("0"))
.jsonPath("$.policies.local.timeLockDelta").value(is(40)) .jsonPath("$.policies.local.timeLockDelta").value(is(40))
.jsonPath("$.policies.local.minHtlcMilliSat").value(is("0"))
.jsonPath("$.policies.local.maxHtlcMilliSat").value(is("0")) .jsonPath("$.policies.local.maxHtlcMilliSat").value(is("0"))
.jsonPath("$.policies.remote.enabled").value(is(true)) .jsonPath("$.policies.remote.enabled").value(is(true))
.jsonPath("$.policies.remote.feeRatePpm").value(is(300)) .jsonPath("$.policies.remote.feeRatePpm").value(is(300))
.jsonPath("$.policies.remote.baseFeeMilliSat").value(is("0")) .jsonPath("$.policies.remote.baseFeeMilliSat").value(is("0"))
.jsonPath("$.policies.remote.timeLockDelta").value(is(144)) .jsonPath("$.policies.remote.timeLockDelta").value(is(144))
.jsonPath("$.policies.remote.minHtlcMilliSat").value(is("159000"))
.jsonPath("$.policies.remote.maxHtlcMilliSat").value(is("22222000")) .jsonPath("$.policies.remote.maxHtlcMilliSat").value(is("22222000"))
.jsonPath("$.feeReport.earnedMilliSat").value(is("1234")) .jsonPath("$.feeReport.earnedMilliSat").value(is("1234"))
.jsonPath("$.feeReport.sourcedMilliSat").value(is("567")) .jsonPath("$.feeReport.sourcedMilliSat").value(is("567"))

View File

@@ -7,6 +7,7 @@ public record PolicyDto(
String baseFeeMilliSat, String baseFeeMilliSat,
boolean enabled, boolean enabled,
int timeLockDelta, int timeLockDelta,
String minHtlcMilliSat,
String maxHtlcMilliSat String maxHtlcMilliSat
) { ) {
public static PolicyDto createFromModel(Policy policy) { public static PolicyDto createFromModel(Policy policy) {
@@ -15,6 +16,7 @@ public record PolicyDto(
String.valueOf(policy.baseFee().milliSatoshis()), String.valueOf(policy.baseFee().milliSatoshis()),
policy.enabled(), policy.enabled(),
policy.timeLockDelta(), policy.timeLockDelta(),
String.valueOf(policy.minHtlc().milliSatoshis()),
String.valueOf(policy.maxHtlc().milliSatoshis()) String.valueOf(policy.maxHtlc().milliSatoshis())
); );
} }

View File

@@ -9,14 +9,14 @@ import static org.assertj.core.api.Assertions.assertThat;
class PolicyDtoTest { class PolicyDtoTest {
@Test @Test
void createFromModel_disabled() { void createFromModel_disabled() {
PolicyDto expected = new PolicyDto(200, "0", false, 40, "0"); PolicyDto expected = new PolicyDto(200, "0", false, 40, "0", "0");
PolicyDto dto = PolicyDto.createFromModel(POLICY_DISABLED); PolicyDto dto = PolicyDto.createFromModel(POLICY_DISABLED);
assertThat(dto).isEqualTo(expected); assertThat(dto).isEqualTo(expected);
} }
@Test @Test
void createFromModel_with_base_fee() { void createFromModel_with_base_fee() {
PolicyDto expected = new PolicyDto(200, "10", true, 40, "10000000"); PolicyDto expected = new PolicyDto(200, "10", true, 40, "159000", "10000000");
PolicyDto dto = PolicyDto.createFromModel(POLICY_WITH_BASE_FEE); PolicyDto dto = PolicyDto.createFromModel(POLICY_WITH_BASE_FEE);
assertThat(dto).isEqualTo(expected); assertThat(dto).isEqualTo(expected);
} }