From 1e397c926c8664e0d0fddca4a01508723e081ec7 Mon Sep 17 00:00:00 2001 From: Carsten Otto Date: Fri, 19 Nov 2021 13:02:08 +0100 Subject: [PATCH] get close costs via controller --- .../controller/OnChainCostsControllerIT.java | 20 +++++++++++++++++-- .../controller/OnChainCostsController.java | 7 +++++++ .../lndmanagej/service/ChannelService.java | 6 ++++++ .../service/OnChainCostService.java | 4 ++++ .../OnChainCostsControllerTest.java | 15 ++++++++++++++ .../service/ChannelServiceTest.java | 13 ++++++++---- .../service/OnChainCostServiceTest.java | 18 ++++++++++++++--- 7 files changed, 74 insertions(+), 9 deletions(-) diff --git a/application/src/integrationTest/java/de/cotto/lndmanagej/controller/OnChainCostsControllerIT.java b/application/src/integrationTest/java/de/cotto/lndmanagej/controller/OnChainCostsControllerIT.java index 089fd135..f6b646c9 100644 --- a/application/src/integrationTest/java/de/cotto/lndmanagej/controller/OnChainCostsControllerIT.java +++ b/application/src/integrationTest/java/de/cotto/lndmanagej/controller/OnChainCostsControllerIT.java @@ -19,6 +19,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @WebMvcTest(controllers = OnChainCostsController.class) class OnChainCostsControllerIT { + private static final String CHANNEL_PREFIX = "/api/channel/" + CHANNEL_ID.getShortChannelId(); + @Autowired private MockMvc mockMvc; @@ -32,14 +34,28 @@ class OnChainCostsControllerIT { @Test void open_costs_for_channel() throws Exception { when(onChainCostService.getOpenCosts(CHANNEL_ID)).thenReturn(Optional.of(Coins.ofSatoshis(123))); - mockMvc.perform(get("/api/channel/" + CHANNEL_ID.getShortChannelId() + "/open-costs")) + mockMvc.perform(get(CHANNEL_PREFIX + "/open-costs")) .andExpect(content().string("123")); } @Test void open_costs_for_channel_unknown() throws Exception { - mockMvc.perform(get("/api/channel/" + CHANNEL_ID.getShortChannelId() + "/open-costs")) + mockMvc.perform(get(CHANNEL_PREFIX + "/open-costs")) .andExpect(status().isBadRequest()) .andExpect(content().string("Unable to get open costs for channel with ID " + CHANNEL_ID)); } + + @Test + void close_costs_for_channel() throws Exception { + when(onChainCostService.getCloseCosts(CHANNEL_ID)).thenReturn(Optional.of(Coins.ofSatoshis(123))); + mockMvc.perform(get(CHANNEL_PREFIX + "/close-costs")) + .andExpect(content().string("123")); + } + + @Test + void close_costs_for_channel_unknown() throws Exception { + mockMvc.perform(get(CHANNEL_PREFIX + "/close-costs")) + .andExpect(status().isBadRequest()) + .andExpect(content().string("Unable to get close costs for channel with ID " + CHANNEL_ID)); + } } \ No newline at end of file diff --git a/application/src/main/java/de/cotto/lndmanagej/controller/OnChainCostsController.java b/application/src/main/java/de/cotto/lndmanagej/controller/OnChainCostsController.java index 590c545a..62f54b10 100644 --- a/application/src/main/java/de/cotto/lndmanagej/controller/OnChainCostsController.java +++ b/application/src/main/java/de/cotto/lndmanagej/controller/OnChainCostsController.java @@ -28,4 +28,11 @@ public class OnChainCostsController { .orElseThrow(() -> new CostException("Unable to get open costs for channel with ID " + channelId)); } + @GetMapping("/channel/{channelId}/close-costs") + public long getCloseCostsForChannel(@PathVariable ChannelId channelId) throws CostException { + metrics.mark(MetricRegistry.name(getClass(), "getCloseCostsForChannel")); + return onChainCostService.getCloseCosts(channelId).map(Coins::satoshis) + .orElseThrow(() -> new CostException("Unable to get close costs for channel with ID " + channelId)); + } + } diff --git a/application/src/main/java/de/cotto/lndmanagej/service/ChannelService.java b/application/src/main/java/de/cotto/lndmanagej/service/ChannelService.java index e9452e6d..f52bd4cc 100644 --- a/application/src/main/java/de/cotto/lndmanagej/service/ChannelService.java +++ b/application/src/main/java/de/cotto/lndmanagej/service/ChannelService.java @@ -51,6 +51,12 @@ public class ChannelService { return closedChannelsCache.getUnchecked(""); } + public Optional getClosedChannel(ChannelId channelId) { + return getClosedChannels().stream() + .filter(c -> channelId.equals(c.getId())) + .findFirst(); + } + public Set getForceClosingChannels() { return forceClosingChannelsCache.getUnchecked(""); } diff --git a/application/src/main/java/de/cotto/lndmanagej/service/OnChainCostService.java b/application/src/main/java/de/cotto/lndmanagej/service/OnChainCostService.java index 38ef5eac..d0e51522 100644 --- a/application/src/main/java/de/cotto/lndmanagej/service/OnChainCostService.java +++ b/application/src/main/java/de/cotto/lndmanagej/service/OnChainCostService.java @@ -43,6 +43,10 @@ public class OnChainCostService { return Optional.empty(); } + public Optional getCloseCosts(ChannelId channelId) { + return channelService.getClosedChannel(channelId).flatMap(this::getCloseCosts); + } + public Optional getCloseCosts(ClosedChannel closedChannel) { if (closedChannel.getOpenInitiator().equals(OpenInitiator.LOCAL)) { return transactionService.getTransaction(closedChannel.getCloseTransactionHash()) diff --git a/application/src/test/java/de/cotto/lndmanagej/controller/OnChainCostsControllerTest.java b/application/src/test/java/de/cotto/lndmanagej/controller/OnChainCostsControllerTest.java index 374e7b2f..1fbeb9a5 100644 --- a/application/src/test/java/de/cotto/lndmanagej/controller/OnChainCostsControllerTest.java +++ b/application/src/test/java/de/cotto/lndmanagej/controller/OnChainCostsControllerTest.java @@ -43,4 +43,19 @@ class OnChainCostsControllerTest { () -> onChainCostsController.getOpenCostsForChannel(CHANNEL_ID) ); } + + @Test + void getCloseCostsForChannel() throws CostException { + Coins coins = Coins.ofSatoshis(123); + when(onChainCostService.getCloseCosts(CHANNEL_ID)).thenReturn(Optional.of(coins)); + assertThat(onChainCostsController.getCloseCostsForChannel(CHANNEL_ID)).isEqualTo(coins.satoshis()); + verify(metrics).mark(argThat(name -> name.endsWith(".getCloseCostsForChannel"))); + } + + @Test + void getCloseCostsForChannel_unknown() { + assertThatExceptionOfType(CostException.class).isThrownBy( + () -> onChainCostsController.getCloseCostsForChannel(CHANNEL_ID) + ); + } } \ No newline at end of file diff --git a/application/src/test/java/de/cotto/lndmanagej/service/ChannelServiceTest.java b/application/src/test/java/de/cotto/lndmanagej/service/ChannelServiceTest.java index ed76854a..1057f383 100644 --- a/application/src/test/java/de/cotto/lndmanagej/service/ChannelServiceTest.java +++ b/application/src/test/java/de/cotto/lndmanagej/service/ChannelServiceTest.java @@ -11,6 +11,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import java.util.Set; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2; import static de.cotto.lndmanagej.model.CoopClosedChannelFixtures.CLOSED_CHANNEL; import static de.cotto.lndmanagej.model.CoopClosedChannelFixtures.CLOSED_CHANNEL_2; import static de.cotto.lndmanagej.model.CoopClosedChannelFixtures.CLOSED_CHANNEL_3; @@ -67,10 +68,14 @@ class ChannelServiceTest { @Test void getClosedChannels() { - when(grpcClosedChannels.getClosedChannels()) - .thenReturn(Set.of(CLOSED_CHANNEL, CLOSED_CHANNEL_2)); - assertThat(channelService.getClosedChannels()) - .containsExactlyInAnyOrder(CLOSED_CHANNEL, CLOSED_CHANNEL_2); + when(grpcClosedChannels.getClosedChannels()).thenReturn(Set.of(CLOSED_CHANNEL, CLOSED_CHANNEL_2)); + assertThat(channelService.getClosedChannels()).containsExactlyInAnyOrder(CLOSED_CHANNEL, CLOSED_CHANNEL_2); + } + + @Test + void getClosedChannel() { + when(grpcClosedChannels.getClosedChannels()).thenReturn(Set.of(CLOSED_CHANNEL, CLOSED_CHANNEL_2)); + assertThat(channelService.getClosedChannel(CHANNEL_ID_2)).contains(CLOSED_CHANNEL_2); } @Test diff --git a/application/src/test/java/de/cotto/lndmanagej/service/OnChainCostServiceTest.java b/application/src/test/java/de/cotto/lndmanagej/service/OnChainCostServiceTest.java index 35d1042a..72f8c61b 100644 --- a/application/src/test/java/de/cotto/lndmanagej/service/OnChainCostServiceTest.java +++ b/application/src/test/java/de/cotto/lndmanagej/service/OnChainCostServiceTest.java @@ -34,9 +34,6 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class OnChainCostServiceTest { - private static final Coins OPEN_COSTS = TRANSACTION.fees(); - private static final Coins CLOSE_COSTS = TRANSACTION_2.fees(); - @InjectMocks private OnChainCostService onChainCostService; @@ -48,6 +45,7 @@ class OnChainCostServiceTest { @Nested class GetOpenCosts { + private static final Coins OPEN_COSTS = TRANSACTION.fees(); @Test void getOpenCosts_by_channel_id_not_resolved() { @@ -170,6 +168,20 @@ class OnChainCostServiceTest { @Nested class GetCloseCosts { + private static final Coins CLOSE_COSTS = TRANSACTION_2.fees(); + + @Test + void getCloseCosts_by_channel_id_not_resolved() { + assertThat(onChainCostService.getCloseCosts(CHANNEL_ID)).isEmpty(); + } + + @Test + void getCloseCosts_by_channel_id_resolved() { + mockCloseTransaction(CLOSED_CHANNEL); + when(channelService.getClosedChannel(CHANNEL_ID)).thenReturn(Optional.of(CLOSED_CHANNEL)); + assertThat(onChainCostService.getCloseCosts(CHANNEL_ID)).contains(CLOSE_COSTS); + } + @Test void getOpenCosts_for_coop_closed_channel_initiator_local_transaction_not_found() { assertThat(onChainCostService.getCloseCosts(CLOSED_CHANNEL)).isEmpty();