diff --git a/application/src/integrationTest/java/de/cotto/lndmanagej/controller/LegacyControllerIT.java b/application/src/integrationTest/java/de/cotto/lndmanagej/controller/LegacyControllerIT.java index 81563e45..56490216 100644 --- a/application/src/integrationTest/java/de/cotto/lndmanagej/controller/LegacyControllerIT.java +++ b/application/src/integrationTest/java/de/cotto/lndmanagej/controller/LegacyControllerIT.java @@ -1,5 +1,6 @@ package de.cotto.lndmanagej.controller; +import de.cotto.lndmanagej.model.Coins; import de.cotto.lndmanagej.service.ChannelService; import de.cotto.lndmanagej.service.FeeService; import de.cotto.lndmanagej.service.NodeService; @@ -28,6 +29,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @WebMvcTest(controllers = LegacyController.class) class LegacyControllerIT { + private static final String PUBKEY_BASE = "/legacy/node/" + PUBKEY; + private static final String CHANNEL_BASE = "/legacy/channel/" + CHANNEL_ID; + private static final long FEE_RATE = 123L; + private static final Coins BASE_FEE = Coins.ofMilliSatoshis(10L); + @Autowired private MockMvc mockMvc; @@ -46,7 +52,7 @@ class LegacyControllerIT { @Test void getAlias() throws Exception { when(nodeService.getAlias(PUBKEY)).thenReturn(ALIAS); - mockMvc.perform(get("/legacy/node/" + PUBKEY + "/alias")).andExpect(content().string(ALIAS)); + mockMvc.perform(get(PUBKEY_BASE + "/alias")).andExpect(content().string(ALIAS)); } @Test @@ -57,7 +63,7 @@ class LegacyControllerIT { @Test void getOpenChannelIds() throws Exception { when(channelService.getOpenChannelsWith(PUBKEY)).thenReturn(Set.of(LOCAL_CHANNEL, LOCAL_CHANNEL_3)); - mockMvc.perform(get("/legacy/node/" + PUBKEY + "/open-channels")) + mockMvc.perform(get(PUBKEY_BASE + "/open-channels")) .andExpect(content().string(CHANNEL_ID + "\n" + CHANNEL_ID_3)); } @@ -82,15 +88,29 @@ class LegacyControllerIT { @Test void getIncomingFeeRate() throws Exception { - when(feeService.getIncomingFeeRate(CHANNEL_ID)).thenReturn(123L); - mockMvc.perform(get("/legacy/channel/" + CHANNEL_ID + "/incoming-fee-rate")) - .andExpect(content().string("123")); + when(feeService.getIncomingFeeRate(CHANNEL_ID)).thenReturn(FEE_RATE); + mockMvc.perform(get(CHANNEL_BASE + "/incoming-fee-rate")) + .andExpect(content().string(Long.toString(FEE_RATE))); } @Test void getOutgoingFeeRate() throws Exception { - when(feeService.getOutgoingFeeRate(CHANNEL_ID)).thenReturn(123L); - mockMvc.perform(get("/legacy/channel/" + CHANNEL_ID + "/outgoing-fee-rate")) - .andExpect(content().string("123")); + when(feeService.getOutgoingFeeRate(CHANNEL_ID)).thenReturn(FEE_RATE); + mockMvc.perform(get(CHANNEL_BASE + "/outgoing-fee-rate")) + .andExpect(content().string(Long.toString(FEE_RATE))); + } + + @Test + void getIncomingBaseFee() throws Exception { + when(feeService.getIncomingBaseFee(CHANNEL_ID)).thenReturn(BASE_FEE); + mockMvc.perform(get(CHANNEL_BASE + "/incoming-base-fee")) + .andExpect(content().string(String.valueOf(BASE_FEE.milliSatoshis()))); + } + + @Test + void getOutgoingBaseFee() throws Exception { + when(feeService.getOutgoingBaseFee(CHANNEL_ID)).thenReturn(BASE_FEE); + mockMvc.perform(get(CHANNEL_BASE + "/outgoing-base-fee")) + .andExpect(content().string(String.valueOf(BASE_FEE.milliSatoshis()))); } } \ No newline at end of file diff --git a/application/src/main/java/de/cotto/lndmanagej/controller/LegacyController.java b/application/src/main/java/de/cotto/lndmanagej/controller/LegacyController.java index f9853b40..ac945af1 100644 --- a/application/src/main/java/de/cotto/lndmanagej/controller/LegacyController.java +++ b/application/src/main/java/de/cotto/lndmanagej/controller/LegacyController.java @@ -74,4 +74,14 @@ public class LegacyController { public long getOutgoingFeeRate(@PathVariable ChannelId channelId) { return feeService.getOutgoingFeeRate(channelId); } + + @GetMapping("/channel/{channelId}/incoming-base-fee") + public long getIncomingBaseFee(@PathVariable ChannelId channelId) { + return feeService.getIncomingBaseFee(channelId).milliSatoshis(); + } + + @GetMapping("/channel/{channelId}/outgoing-base-fee") + public long getOutgoingBaseFee(@PathVariable ChannelId channelId) { + return feeService.getOutgoingBaseFee(channelId).milliSatoshis(); + } } diff --git a/application/src/main/java/de/cotto/lndmanagej/service/FeeService.java b/application/src/main/java/de/cotto/lndmanagej/service/FeeService.java index e2510a68..80ef9604 100644 --- a/application/src/main/java/de/cotto/lndmanagej/service/FeeService.java +++ b/application/src/main/java/de/cotto/lndmanagej/service/FeeService.java @@ -2,6 +2,7 @@ package de.cotto.lndmanagej.service; import de.cotto.lndmanagej.grpc.GrpcFees; import de.cotto.lndmanagej.model.ChannelId; +import de.cotto.lndmanagej.model.Coins; import org.springframework.stereotype.Component; @Component @@ -19,4 +20,12 @@ public class FeeService { public long getOutgoingFeeRate(ChannelId channelId) { return grpcFees.getOutgoingFeeRate(channelId).orElseThrow(IllegalStateException::new); } + + public Coins getOutgoingBaseFee(ChannelId channelId) { + return grpcFees.getOutgoingBaseFee(channelId).orElseThrow(IllegalStateException::new); + } + + public Coins getIncomingBaseFee(ChannelId channelId) { + return grpcFees.getIncomingBaseFee(channelId).orElseThrow(IllegalStateException::new); + } } diff --git a/application/src/test/java/de/cotto/lndmanagej/controller/LegacyControllerTest.java b/application/src/test/java/de/cotto/lndmanagej/controller/LegacyControllerTest.java index 5205aa83..a580ec27 100644 --- a/application/src/test/java/de/cotto/lndmanagej/controller/LegacyControllerTest.java +++ b/application/src/test/java/de/cotto/lndmanagej/controller/LegacyControllerTest.java @@ -1,6 +1,7 @@ package de.cotto.lndmanagej.controller; import de.cotto.lndmanagej.model.ChannelFixtures; +import de.cotto.lndmanagej.model.Coins; import de.cotto.lndmanagej.model.LocalChannel; import de.cotto.lndmanagej.service.ChannelService; import de.cotto.lndmanagej.service.FeeService; @@ -109,4 +110,16 @@ class LegacyControllerTest { when(feeService.getOutgoingFeeRate(CHANNEL_ID)).thenReturn(123L); assertThat(legacyController.getOutgoingFeeRate(CHANNEL_ID)).isEqualTo(123); } + + @Test + void getIncomingBaseFee() { + when(feeService.getIncomingBaseFee(CHANNEL_ID)).thenReturn(Coins.ofMilliSatoshis(10L)); + assertThat(legacyController.getIncomingBaseFee(CHANNEL_ID)).isEqualTo(10); + } + + @Test + void getOutgoingBaseFee() { + when(feeService.getOutgoingBaseFee(CHANNEL_ID)).thenReturn(Coins.ofMilliSatoshis(10L)); + assertThat(legacyController.getOutgoingBaseFee(CHANNEL_ID)).isEqualTo(10); + } } \ No newline at end of file diff --git a/application/src/test/java/de/cotto/lndmanagej/service/FeeServiceTest.java b/application/src/test/java/de/cotto/lndmanagej/service/FeeServiceTest.java index 409340f1..a51b81ae 100644 --- a/application/src/test/java/de/cotto/lndmanagej/service/FeeServiceTest.java +++ b/application/src/test/java/de/cotto/lndmanagej/service/FeeServiceTest.java @@ -1,6 +1,7 @@ package de.cotto.lndmanagej.service; import de.cotto.lndmanagej.grpc.GrpcFees; +import de.cotto.lndmanagej.model.Coins; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -45,4 +46,28 @@ class FeeServiceTest { when(grpcFees.getOutgoingFeeRate(CHANNEL_ID)).thenReturn(Optional.empty()); assertThatIllegalStateException().isThrownBy(() -> feeService.getOutgoingFeeRate(CHANNEL_ID)); } + + @Test + void getIncomingBaseFee() { + when(grpcFees.getIncomingBaseFee(CHANNEL_ID)).thenReturn(Optional.of(Coins.ofMilliSatoshis(123L))); + assertThat(feeService.getIncomingBaseFee(CHANNEL_ID)).isEqualTo(Coins.ofMilliSatoshis(123L)); + } + + @Test + void getIncomingBaseFee_empty() { + when(grpcFees.getIncomingBaseFee(CHANNEL_ID)).thenReturn(Optional.empty()); + assertThatIllegalStateException().isThrownBy(() -> feeService.getIncomingBaseFee(CHANNEL_ID)); + } + + @Test + void getOutgoingBaseFee() { + when(grpcFees.getOutgoingBaseFee(CHANNEL_ID)).thenReturn(Optional.of(Coins.ofMilliSatoshis(123L))); + assertThat(feeService.getOutgoingBaseFee(CHANNEL_ID)).isEqualTo(Coins.ofMilliSatoshis(123L)); + } + + @Test + void getOutgoingBaseFee_empty() { + when(grpcFees.getOutgoingBaseFee(CHANNEL_ID)).thenReturn(Optional.empty()); + assertThatIllegalStateException().isThrownBy(() -> feeService.getOutgoingBaseFee(CHANNEL_ID)); + } } \ No newline at end of file diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcFees.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcFees.java index 97e4bf03..9870f19c 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcFees.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcFees.java @@ -1,6 +1,7 @@ package de.cotto.lndmanagej.grpc; import de.cotto.lndmanagej.model.ChannelId; +import de.cotto.lndmanagej.model.Coins; import lnrpc.RoutingPolicy; import org.springframework.stereotype.Component; @@ -21,4 +22,16 @@ public class GrpcFees { public Optional getIncomingFeeRate(ChannelId channelId) { return grpcChannelPolicy.getRemotePolicy(channelId).map(RoutingPolicy::getFeeRateMilliMsat); } + + public Optional getOutgoingBaseFee(ChannelId channelId) { + return grpcChannelPolicy.getLocalPolicy(channelId) + .map(RoutingPolicy::getFeeBaseMsat) + .map(Coins::ofMilliSatoshis); + } + + public Optional getIncomingBaseFee(ChannelId channelId) { + return grpcChannelPolicy.getRemotePolicy(channelId) + .map(RoutingPolicy::getFeeBaseMsat) + .map(Coins::ofMilliSatoshis); + } } diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcFeesTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcFeesTest.java index 8661fac6..aa9e3dd1 100644 --- a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcFeesTest.java +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcFeesTest.java @@ -1,5 +1,6 @@ package de.cotto.lndmanagej.grpc; +import de.cotto.lndmanagej.model.Coins; import lnrpc.RoutingPolicy; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -15,6 +16,9 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class GrpcFeesTest { + private static final Coins BASE_FEE = Coins.ofMilliSatoshis(1L); + private static final long FEE_RATE = 123L; + @InjectMocks private GrpcFees grpcFees; @@ -24,7 +28,7 @@ class GrpcFeesTest { @Test void getOutgoingFeeRate() { when(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID)).thenReturn(Optional.of(routingPolicy())); - assertThat(grpcFees.getOutgoingFeeRate(CHANNEL_ID)).contains(123L); + assertThat(grpcFees.getOutgoingFeeRate(CHANNEL_ID)).contains(FEE_RATE); } @Test @@ -35,7 +39,7 @@ class GrpcFeesTest { @Test void getIncomingFeeRate() { when(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID)).thenReturn(Optional.of(routingPolicy())); - assertThat(grpcFees.getIncomingFeeRate(CHANNEL_ID)).contains(123L); + assertThat(grpcFees.getIncomingFeeRate(CHANNEL_ID)).contains(FEE_RATE); } @Test @@ -43,8 +47,33 @@ class GrpcFeesTest { assertThat(grpcFees.getIncomingFeeRate(CHANNEL_ID)).isEmpty(); } + @Test + void getOutgoingBaseFee() { + when(grpcChannelPolicy.getLocalPolicy(CHANNEL_ID)).thenReturn(Optional.of(routingPolicy())); + assertThat(grpcFees.getOutgoingBaseFee(CHANNEL_ID)).contains(BASE_FEE); + } + + @Test + void getOutgoingBaseFee_empty() { + assertThat(grpcFees.getOutgoingBaseFee(CHANNEL_ID)).isEmpty(); + } + + @Test + void getIncomingBaseFee() { + when(grpcChannelPolicy.getRemotePolicy(CHANNEL_ID)).thenReturn(Optional.of(routingPolicy())); + assertThat(grpcFees.getIncomingBaseFee(CHANNEL_ID)).contains(BASE_FEE); + } + + @Test + void getIncomingBaseFee_empty() { + assertThat(grpcFees.getIncomingBaseFee(CHANNEL_ID)).isEmpty(); + } + private RoutingPolicy routingPolicy() { - return RoutingPolicy.newBuilder().setFeeRateMilliMsat(123).build(); + return RoutingPolicy.newBuilder() + .setFeeRateMilliMsat(FEE_RATE) + .setFeeBaseMsat(BASE_FEE.milliSatoshis()) + .build(); } } \ No newline at end of file