diff --git a/application/src/integrationTest/java/de/cotto/lndmanagej/controller/OnChainCostsControllerIT.java b/application/src/integrationTest/java/de/cotto/lndmanagej/controller/OnChainCostsControllerIT.java new file mode 100644 index 00000000..089fd135 --- /dev/null +++ b/application/src/integrationTest/java/de/cotto/lndmanagej/controller/OnChainCostsControllerIT.java @@ -0,0 +1,45 @@ +package de.cotto.lndmanagej.controller; + +import de.cotto.lndmanagej.metrics.Metrics; +import de.cotto.lndmanagej.model.Coins; +import de.cotto.lndmanagej.service.OnChainCostService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.Optional; + +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(controllers = OnChainCostsController.class) +class OnChainCostsControllerIT { + @Autowired + private MockMvc mockMvc; + + @MockBean + private OnChainCostService onChainCostService; + + @MockBean + @SuppressWarnings("unused") + private Metrics metrics; + + @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")) + .andExpect(content().string("123")); + } + + @Test + void open_costs_for_channel_unknown() throws Exception { + mockMvc.perform(get("/api/channel/" + CHANNEL_ID.getShortChannelId() + "/open-costs")) + .andExpect(status().isBadRequest()) + .andExpect(content().string("Unable to get open costs for channel with ID " + CHANNEL_ID)); + } +} \ No newline at end of file diff --git a/application/src/main/java/de/cotto/lndmanagej/controller/CostException.java b/application/src/main/java/de/cotto/lndmanagej/controller/CostException.java new file mode 100644 index 00000000..c4e81973 --- /dev/null +++ b/application/src/main/java/de/cotto/lndmanagej/controller/CostException.java @@ -0,0 +1,7 @@ +package de.cotto.lndmanagej.controller; + +public class CostException extends Exception { + public CostException(String message) { + super(message); + } +} diff --git a/application/src/main/java/de/cotto/lndmanagej/controller/CostExceptionHandler.java b/application/src/main/java/de/cotto/lndmanagej/controller/CostExceptionHandler.java new file mode 100644 index 00000000..af31a3ea --- /dev/null +++ b/application/src/main/java/de/cotto/lndmanagej/controller/CostExceptionHandler.java @@ -0,0 +1,20 @@ +package de.cotto.lndmanagej.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +@ControllerAdvice +public class CostExceptionHandler extends ResponseEntityExceptionHandler { + public CostExceptionHandler() { + super(); + } + + @ExceptionHandler(CostException.class) + public ResponseEntity handleException(CostException exception) { + return ResponseEntity + .badRequest() + .body(exception.getMessage()); + } +} \ 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 new file mode 100644 index 00000000..590c545a --- /dev/null +++ b/application/src/main/java/de/cotto/lndmanagej/controller/OnChainCostsController.java @@ -0,0 +1,31 @@ +package de.cotto.lndmanagej.controller; + +import com.codahale.metrics.MetricRegistry; +import de.cotto.lndmanagej.metrics.Metrics; +import de.cotto.lndmanagej.model.ChannelId; +import de.cotto.lndmanagej.model.Coins; +import de.cotto.lndmanagej.service.OnChainCostService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/") +public class OnChainCostsController { + private final OnChainCostService onChainCostService; + private final Metrics metrics; + + public OnChainCostsController(OnChainCostService onChainCostService, Metrics metrics) { + this.onChainCostService = onChainCostService; + this.metrics = metrics; + } + + @GetMapping("/channel/{channelId}/open-costs") + public long getOpenCostsForChannel(@PathVariable ChannelId channelId) throws CostException { + metrics.mark(MetricRegistry.name(getClass(), "getOpenCostsForChannel")); + return onChainCostService.getOpenCosts(channelId).map(Coins::satoshis) + .orElseThrow(() -> new CostException("Unable to get open 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 108cf7a7..e9452e6d 100644 --- a/application/src/main/java/de/cotto/lndmanagej/service/ChannelService.java +++ b/application/src/main/java/de/cotto/lndmanagej/service/ChannelService.java @@ -4,6 +4,7 @@ import com.google.common.cache.LoadingCache; import de.cotto.lndmanagej.caching.CacheBuilder; import de.cotto.lndmanagej.grpc.GrpcChannels; import de.cotto.lndmanagej.grpc.GrpcClosedChannels; +import de.cotto.lndmanagej.model.ChannelId; import de.cotto.lndmanagej.model.ClosedChannel; import de.cotto.lndmanagej.model.ForceClosingChannel; import de.cotto.lndmanagej.model.LocalChannel; @@ -13,6 +14,7 @@ import de.cotto.lndmanagej.model.WaitingCloseChannel; import org.springframework.stereotype.Component; import java.util.Collection; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -64,13 +66,27 @@ public class ChannelService { } public Set getAllChannelsWith(Pubkey pubkey) { - Set openChannels = getOpenChannelsWith(pubkey); - Set waitingCloseChannels = getWaitingCloseChannels(); - Set forceClosingChannels = getForceClosingChannels(); - Set closedChannels = getClosedChannels(); - return Stream.of(openChannels, closedChannels, waitingCloseChannels, forceClosingChannels) - .flatMap(Collection::stream) + return getAllLocalChannels() .filter(c -> c.getRemotePubkey().equals(pubkey)) .collect(Collectors.toSet()); } + + public Optional getLocalChannel(ChannelId channelId) { + return getAllLocalChannels() + .filter(c -> c.getId().equals(channelId)) + .findFirst(); + } + + public Stream getAllLocalChannels() { + Set openChannels = getOpenChannels(); + Set waitingCloseChannels = getWaitingCloseChannels(); + Set forceClosingChannels = getForceClosingChannels(); + Set closedChannels = getClosedChannels(); + return Stream.of( + openChannels, + closedChannels, + waitingCloseChannels, + forceClosingChannels + ).flatMap(Collection::stream); + } } diff --git a/application/src/main/java/de/cotto/lndmanagej/service/OnChainCostService.java b/application/src/main/java/de/cotto/lndmanagej/service/OnChainCostService.java new file mode 100644 index 00000000..38ef5eac --- /dev/null +++ b/application/src/main/java/de/cotto/lndmanagej/service/OnChainCostService.java @@ -0,0 +1,64 @@ +package de.cotto.lndmanagej.service; + +import de.cotto.lndmanagej.model.ChannelId; +import de.cotto.lndmanagej.model.ChannelPoint; +import de.cotto.lndmanagej.model.ClosedChannel; +import de.cotto.lndmanagej.model.Coins; +import de.cotto.lndmanagej.model.LocalChannel; +import de.cotto.lndmanagej.model.OpenInitiator; +import de.cotto.lndmanagej.transactions.model.Transaction; +import de.cotto.lndmanagej.transactions.service.TransactionService; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +public class OnChainCostService { + private final TransactionService transactionService; + private final ChannelService channelService; + + public OnChainCostService(TransactionService transactionService, ChannelService channelService) { + this.transactionService = transactionService; + this.channelService = channelService; + } + + public Optional getOpenCosts(ChannelId channelId) { + return channelService.getLocalChannel(channelId).flatMap(this::getOpenCosts); + } + + public Optional getOpenCosts(LocalChannel localChannel) { + if (localChannel.getOpenInitiator().equals(OpenInitiator.LOCAL)) { + String openTransactionHash = localChannel.getChannelPoint().getTransactionHash(); + return transactionService.getTransaction(openTransactionHash) + .map(Transaction::fees) + .map(Coins::satoshis) + .map(sat -> { + long channels = getNumberOfChannelsWithOpenTransactionHash(openTransactionHash); + return Coins.ofSatoshis(sat / channels); + }); + } + if (localChannel.getOpenInitiator().equals(OpenInitiator.REMOTE)) { + return Optional.of(Coins.NONE); + } + return Optional.empty(); + } + + public Optional getCloseCosts(ClosedChannel closedChannel) { + if (closedChannel.getOpenInitiator().equals(OpenInitiator.LOCAL)) { + return transactionService.getTransaction(closedChannel.getCloseTransactionHash()) + .map(Transaction::fees); + } + if (closedChannel.getOpenInitiator().equals(OpenInitiator.REMOTE)) { + return Optional.of(Coins.NONE); + } + return Optional.empty(); + } + + private long getNumberOfChannelsWithOpenTransactionHash(String openTransactionHash) { + return channelService.getAllLocalChannels() + .map(LocalChannel::getChannelPoint) + .map(ChannelPoint::getTransactionHash) + .filter(x -> x.equals(openTransactionHash)) + .count(); + } +} diff --git a/application/src/test/java/de/cotto/lndmanagej/controller/CostExceptionHandlerTest.java b/application/src/test/java/de/cotto/lndmanagej/controller/CostExceptionHandlerTest.java new file mode 100644 index 00000000..43331c45 --- /dev/null +++ b/application/src/test/java/de/cotto/lndmanagej/controller/CostExceptionHandlerTest.java @@ -0,0 +1,29 @@ +package de.cotto.lndmanagej.controller; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpStatus; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(MockitoExtension.class) +class CostExceptionHandlerTest { + private static final CostException EXCEPTION = new CostException("abc"); + + @InjectMocks + private CostExceptionHandler costExceptionHandler; + + @Test + void mapsToBadRequest() { + assertThat(costExceptionHandler.handleException(EXCEPTION).getStatusCode()) + .isEqualTo(HttpStatus.BAD_REQUEST); + } + + @Test + void returnsMessageInBody() { + assertThat(costExceptionHandler.handleException(EXCEPTION).getBody()) + .isEqualTo(EXCEPTION.getMessage()); + } +} \ No newline at end of file diff --git a/application/src/test/java/de/cotto/lndmanagej/controller/OnChainCostsControllerTest.java b/application/src/test/java/de/cotto/lndmanagej/controller/OnChainCostsControllerTest.java new file mode 100644 index 00000000..374e7b2f --- /dev/null +++ b/application/src/test/java/de/cotto/lndmanagej/controller/OnChainCostsControllerTest.java @@ -0,0 +1,46 @@ +package de.cotto.lndmanagej.controller; + +import de.cotto.lndmanagej.metrics.Metrics; +import de.cotto.lndmanagej.model.Coins; +import de.cotto.lndmanagej.service.OnChainCostService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class OnChainCostsControllerTest { + @InjectMocks + private OnChainCostsController onChainCostsController; + + @Mock + private OnChainCostService onChainCostService; + + @Mock + private Metrics metrics; + + @Test + void getOpenCostsForChannel() throws CostException { + Coins coins = Coins.ofSatoshis(123); + when(onChainCostService.getOpenCosts(CHANNEL_ID)).thenReturn(Optional.of(coins)); + assertThat(onChainCostsController.getOpenCostsForChannel(CHANNEL_ID)).isEqualTo(coins.satoshis()); + verify(metrics).mark(argThat(name -> name.endsWith(".getOpenCostsForChannel"))); + } + + @Test + void getOpenCostsForChannel_unknown() { + assertThatExceptionOfType(CostException.class).isThrownBy( + () -> onChainCostsController.getOpenCostsForChannel(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 c67b07ff..ed76854a 100644 --- a/application/src/test/java/de/cotto/lndmanagej/service/ChannelServiceTest.java +++ b/application/src/test/java/de/cotto/lndmanagej/service/ChannelServiceTest.java @@ -10,10 +10,13 @@ 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.CoopClosedChannelFixtures.CLOSED_CHANNEL; import static de.cotto.lndmanagej.model.CoopClosedChannelFixtures.CLOSED_CHANNEL_2; import static de.cotto.lndmanagej.model.CoopClosedChannelFixtures.CLOSED_CHANNEL_3; import static de.cotto.lndmanagej.model.CoopClosedChannelFixtures.CLOSED_CHANNEL_TO_NODE_3; +import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL; +import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL_2; import static de.cotto.lndmanagej.model.ForceClosingChannelFixtures.FORCE_CLOSING_CHANNEL; import static de.cotto.lndmanagej.model.ForceClosingChannelFixtures.FORCE_CLOSING_CHANNEL_2; import static de.cotto.lndmanagej.model.ForceClosingChannelFixtures.FORCE_CLOSING_CHANNEL_TO_NODE_3; @@ -103,4 +106,39 @@ class ChannelServiceTest { WAITING_CLOSE_CHANNEL ); } + + @Test + void getLocalChannel_unknown() { + assertThat(channelService.getLocalChannel(CHANNEL_ID)).isEmpty(); + } + + @Test + void getLocalChannel_local_open_channel() { + when(grpcChannels.getChannels()).thenReturn(Set.of(LOCAL_OPEN_CHANNEL_2, LOCAL_OPEN_CHANNEL)); + assertThat(channelService.getLocalChannel(CHANNEL_ID)).contains(LOCAL_OPEN_CHANNEL); + } + + @Test + void getLocalChannel_waiting_close_channel() { + when(grpcChannels.getWaitingCloseChannels()).thenReturn(Set.of(WAITING_CLOSE_CHANNEL, WAITING_CLOSE_CHANNEL_2)); + assertThat(channelService.getLocalChannel(CHANNEL_ID)).contains(WAITING_CLOSE_CHANNEL); + } + + @Test + void getLocalChannel_coop_closed_channel() { + when(grpcClosedChannels.getClosedChannels()).thenReturn(Set.of(CLOSED_CHANNEL, CLOSED_CHANNEL_2)); + assertThat(channelService.getLocalChannel(CHANNEL_ID)).contains(CLOSED_CHANNEL); + } + + @Test + void getLocalChannel_force_closing_channel() { + when(grpcChannels.getForceClosingChannels()).thenReturn(Set.of(FORCE_CLOSING_CHANNEL, FORCE_CLOSING_CHANNEL_2)); + assertThat(channelService.getLocalChannel(CHANNEL_ID)).contains(FORCE_CLOSING_CHANNEL); + } + + @Test + void getLocalChannel_force_closed_channel() { + when(grpcClosedChannels.getClosedChannels()).thenReturn(Set.of(FORCE_CLOSED_CHANNEL, FORCE_CLOSED_CHANNEL_2)); + assertThat(channelService.getLocalChannel(CHANNEL_ID)).contains(FORCE_CLOSED_CHANNEL); + } } \ No newline at end of file diff --git a/application/src/test/java/de/cotto/lndmanagej/service/OnChainCostServiceTest.java b/application/src/test/java/de/cotto/lndmanagej/service/OnChainCostServiceTest.java new file mode 100644 index 00000000..35d1042a --- /dev/null +++ b/application/src/test/java/de/cotto/lndmanagej/service/OnChainCostServiceTest.java @@ -0,0 +1,216 @@ +package de.cotto.lndmanagej.service; + +import de.cotto.lndmanagej.model.ClosedChannel; +import de.cotto.lndmanagej.model.Coins; +import de.cotto.lndmanagej.model.LocalChannel; +import de.cotto.lndmanagej.transactions.service.TransactionService; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; +import java.util.stream.Stream; + +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +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; +import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL_OPEN_LOCAL; +import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL_OPEN_REMOTE; +import static de.cotto.lndmanagej.model.ForceClosingChannelFixtures.FORCE_CLOSING_CHANNEL; +import static de.cotto.lndmanagej.model.ForceClosingChannelFixtures.FORCE_CLOSING_CHANNEL_2; +import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHANNEL; +import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHANNEL_2; +import static de.cotto.lndmanagej.model.WaitingCloseChannelFixtures.WAITING_CLOSE_CHANNEL; +import static de.cotto.lndmanagej.model.WaitingCloseChannelFixtures.WAITING_CLOSE_CHANNEL_2; +import static de.cotto.lndmanagej.transactions.model.TransactionFixtures.TRANSACTION; +import static de.cotto.lndmanagej.transactions.model.TransactionFixtures.TRANSACTION_2; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.lenient; +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; + + @Mock + private TransactionService transactionService; + + @Mock + private ChannelService channelService; + + @Nested + class GetOpenCosts { + + @Test + void getOpenCosts_by_channel_id_not_resolved() { + assertThat(onChainCostService.getOpenCosts(CHANNEL_ID)).isEmpty(); + } + + @Test + void getOpenCosts_by_channel_id_resolved() { + mockOpenTransaction(LOCAL_OPEN_CHANNEL); + when(channelService.getLocalChannel(CHANNEL_ID)).thenReturn(Optional.of(LOCAL_OPEN_CHANNEL)); + assertThat(onChainCostService.getOpenCosts(CHANNEL_ID)).contains(OPEN_COSTS); + } + + @Test + void getOpenCosts_for_local_open_channel_initiator_local_transaction_not_found() { + assertThat(onChainCostService.getOpenCosts(LOCAL_OPEN_CHANNEL)).isEmpty(); + } + + @Test + void getOpenCosts_for_local_open_channel_initiator_local() { + mockOpenTransaction(LOCAL_OPEN_CHANNEL); + assertThat(onChainCostService.getOpenCosts(LOCAL_OPEN_CHANNEL)) + .contains(OPEN_COSTS); + } + + @Test + void getOpenCosts_for_local_open_channel_initiator_remote() { + mockOpenTransaction(LOCAL_OPEN_CHANNEL_2); + assertThat(onChainCostService.getOpenCosts(LOCAL_OPEN_CHANNEL_2)) + .contains(Coins.NONE); + } + + @Test + void getOpenCosts_for_coop_closed_channel_initiator_local() { + mockOpenTransaction(CLOSED_CHANNEL); + assertThat(onChainCostService.getOpenCosts(CLOSED_CHANNEL)) + .contains(OPEN_COSTS); + } + + @Test + void getOpenCosts_for_coop_closed_channel_initiator_unknown() { + assertThat(onChainCostService.getOpenCosts(CLOSED_CHANNEL_2)).isEmpty(); + } + + @Test + void getOpenCosts_for_coop_closed_channel_initiator_remote() { + mockOpenTransaction(CLOSED_CHANNEL_3); + assertThat(onChainCostService.getOpenCosts(CLOSED_CHANNEL_3)) + .contains(Coins.NONE); + } + + @Test + void getOpenCosts_for_force_closed_channel_initiator_local() { + mockOpenTransaction(FORCE_CLOSED_CHANNEL_OPEN_LOCAL); + assertThat(onChainCostService.getOpenCosts(FORCE_CLOSED_CHANNEL_OPEN_LOCAL)) + .contains(OPEN_COSTS); + } + + @Test + void getOpenCosts_for_force_closed_channel_initiator_remote() { + mockOpenTransaction(FORCE_CLOSED_CHANNEL_OPEN_REMOTE); + assertThat(onChainCostService.getOpenCosts(FORCE_CLOSED_CHANNEL_OPEN_REMOTE)) + .contains(Coins.NONE); + } + + @Test + void getOpenCosts_for_waiting_close_channel_initiator_local() { + mockOpenTransaction(WAITING_CLOSE_CHANNEL); + assertThat(onChainCostService.getOpenCosts(WAITING_CLOSE_CHANNEL)) + .contains(OPEN_COSTS); + } + + @Test + void getOpenCosts_for_waiting_close_channel_initiator_remote() { + mockOpenTransaction(WAITING_CLOSE_CHANNEL_2); + assertThat(onChainCostService.getOpenCosts(WAITING_CLOSE_CHANNEL_2)) + .contains(Coins.NONE); + } + + @Test + void getOpenCosts_for_force_closing_channel_initiator_local() { + mockOpenTransaction(FORCE_CLOSING_CHANNEL); + assertThat(onChainCostService.getOpenCosts(FORCE_CLOSING_CHANNEL)) + .contains(OPEN_COSTS); + } + + @Test + void getOpenCosts_for_force_closing_channel_initiator_remote() { + mockOpenTransaction(FORCE_CLOSING_CHANNEL_2); + assertThat(onChainCostService.getOpenCosts(FORCE_CLOSING_CHANNEL_2)) + .contains(Coins.NONE); + } + + @Test + void getOpenCosts_for_transaction_opening_several_channels_divisible() { + assertThat(OPEN_COSTS.satoshis()).isEqualTo(124); + mockOpenTransaction(LOCAL_OPEN_CHANNEL); + when(channelService.getAllLocalChannels()).thenReturn(Stream.of(LOCAL_OPEN_CHANNEL, LOCAL_OPEN_CHANNEL_2)); + assertThat(onChainCostService.getOpenCosts(LOCAL_OPEN_CHANNEL)) + .contains(Coins.ofSatoshis(62)); + } + + @Test + void getOpenCosts_for_transaction_opening_several_channels_not_divisible() { + assertThat(OPEN_COSTS.satoshis()).isEqualTo(124); + mockOpenTransaction(LOCAL_OPEN_CHANNEL); + when(channelService.getAllLocalChannels()).thenReturn( + Stream.of(LOCAL_OPEN_CHANNEL, LOCAL_OPEN_CHANNEL_2, CLOSED_CHANNEL, CLOSED_CHANNEL_3) + ); + assertThat(onChainCostService.getOpenCosts(LOCAL_OPEN_CHANNEL)) + .contains(Coins.ofSatoshis(41)); + } + + private void mockOpenTransaction(LocalChannel channel) { + lenient().when(channelService.getAllLocalChannels()).thenReturn(Stream.of(channel)); + lenient().when(transactionService.getTransaction(channel.getChannelPoint().getTransactionHash())) + .thenReturn(Optional.of(TRANSACTION)); + } + } + + @Nested + class GetCloseCosts { + @Test + void getOpenCosts_for_coop_closed_channel_initiator_local_transaction_not_found() { + assertThat(onChainCostService.getCloseCosts(CLOSED_CHANNEL)).isEmpty(); + } + + @Test + void getOpenCosts_for_coop_closed_channel_initiator_local() { + mockCloseTransaction(CLOSED_CHANNEL); + assertThat(onChainCostService.getCloseCosts(CLOSED_CHANNEL)) + .contains(CLOSE_COSTS); + } + + @Test + void getOpenCosts_for_coop_closed_channel_initiator_unknown() { + assertThat(onChainCostService.getCloseCosts(CLOSED_CHANNEL_2)).isEmpty(); + } + + @Test + void getOpenCosts_for_coop_closed_channel_initiator_remote() { + mockCloseTransaction(CLOSED_CHANNEL_3); + assertThat(onChainCostService.getCloseCosts(CLOSED_CHANNEL_3)) + .contains(Coins.NONE); + } + + @Test + void getOpenCosts_for_force_closed_channel_initiator_local() { + mockCloseTransaction(FORCE_CLOSED_CHANNEL_OPEN_LOCAL); + assertThat(onChainCostService.getCloseCosts(FORCE_CLOSED_CHANNEL_OPEN_LOCAL)) + .contains(CLOSE_COSTS); + } + + @Test + void getOpenCosts_for_force_closed_channel_initiator_remote() { + mockCloseTransaction(FORCE_CLOSED_CHANNEL_OPEN_REMOTE); + assertThat(onChainCostService.getCloseCosts(FORCE_CLOSED_CHANNEL_OPEN_REMOTE)) + .contains(Coins.NONE); + } + + private void mockCloseTransaction(ClosedChannel channel) { + lenient().when(transactionService.getTransaction(channel.getCloseTransactionHash())) + .thenReturn(Optional.of(TRANSACTION_2)); + } + } +} \ No newline at end of file diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcClosedChannels.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcClosedChannels.java index 687e7b59..a8524a75 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcClosedChannels.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcClosedChannels.java @@ -82,7 +82,7 @@ public class GrpcClosedChannels extends GrpcChannelsBase { ); } - CloseInitiator getCloseInitiator(ChannelCloseSummary channelCloseSummary) { + private CloseInitiator getCloseInitiator(ChannelCloseSummary channelCloseSummary) { Initiator closeInitiator = channelCloseSummary.getCloseInitiator(); ChannelCloseSummary.ClosureType closureType = channelCloseSummary.getCloseType(); if (closeInitiator.equals(INITIATOR_LOCAL) || closureType.equals(LOCAL_FORCE_CLOSE)) { diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelsTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelsTest.java index 650e816e..45551282 100644 --- a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelsTest.java +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelsTest.java @@ -76,9 +76,10 @@ class GrpcChannelsTest { void getForceClosingChannels_both_resolved() { when(channelIdResolver.resolveFromChannelPoint(CHANNEL_POINT)).thenReturn(Optional.of(CHANNEL_ID)); when(channelIdResolver.resolveFromChannelPoint(CHANNEL_POINT_2)).thenReturn(Optional.of(CHANNEL_ID_2)); - when(grpcService.getForceClosingChannels()).thenReturn( - List.of(forceClosingChannel(CHANNEL_POINT), forceClosingChannel(CHANNEL_POINT_2)) - ); + when(grpcService.getForceClosingChannels()).thenReturn(List.of( + forceClosingChannel(CHANNEL_POINT, Initiator.INITIATOR_LOCAL), + forceClosingChannel(CHANNEL_POINT_2, Initiator.INITIATOR_REMOTE) + )); assertThat(grpcChannels.getForceClosingChannels()) .containsExactlyInAnyOrder(FORCE_CLOSING_CHANNEL, FORCE_CLOSING_CHANNEL_2); } @@ -86,9 +87,10 @@ class GrpcChannelsTest { @Test void getForceClosingChannels_just_one_resolved() { when(channelIdResolver.resolveFromChannelPoint(CHANNEL_POINT)).thenReturn(Optional.of(CHANNEL_ID)); - when(grpcService.getForceClosingChannels()).thenReturn( - List.of(forceClosingChannel(CHANNEL_POINT), forceClosingChannel(CHANNEL_POINT_3)) - ); + when(grpcService.getForceClosingChannels()).thenReturn(List.of( + forceClosingChannel(CHANNEL_POINT, Initiator.INITIATOR_LOCAL), + forceClosingChannel(CHANNEL_POINT_3, Initiator.INITIATOR_LOCAL) + )); assertThat(grpcChannels.getForceClosingChannels()).containsExactlyInAnyOrder(FORCE_CLOSING_CHANNEL); } @@ -96,9 +98,10 @@ class GrpcChannelsTest { void getWaitingCloseChannels_both_resolved() { when(channelIdResolver.resolveFromChannelPoint(CHANNEL_POINT)).thenReturn(Optional.of(CHANNEL_ID)); when(channelIdResolver.resolveFromChannelPoint(CHANNEL_POINT_2)).thenReturn(Optional.of(CHANNEL_ID_2)); - when(grpcService.getWaitingCloseChannels()).thenReturn( - List.of(waitingCloseChannel(CHANNEL_POINT), waitingCloseChannel(CHANNEL_POINT_2)) - ); + when(grpcService.getWaitingCloseChannels()).thenReturn(List.of( + waitingCloseChannel(CHANNEL_POINT, Initiator.INITIATOR_LOCAL), + waitingCloseChannel(CHANNEL_POINT_2, Initiator.INITIATOR_REMOTE) + )); assertThat(grpcChannels.getWaitingCloseChannels()) .containsExactlyInAnyOrder(WAITING_CLOSE_CHANNEL, WAITING_CLOSE_CHANNEL_2); } @@ -106,9 +109,10 @@ class GrpcChannelsTest { @Test void getWaitingCloseChannels_just_one_resolved() { when(channelIdResolver.resolveFromChannelPoint(CHANNEL_POINT)).thenReturn(Optional.of(CHANNEL_ID)); - when(grpcService.getWaitingCloseChannels()).thenReturn( - List.of(waitingCloseChannel(CHANNEL_POINT), waitingCloseChannel(CHANNEL_POINT_3)) - ); + when(grpcService.getWaitingCloseChannels()).thenReturn(List.of( + waitingCloseChannel(CHANNEL_POINT, Initiator.INITIATOR_LOCAL), + waitingCloseChannel(CHANNEL_POINT_3, Initiator.INITIATOR_REMOTE) + )); assertThat(grpcChannels.getWaitingCloseChannels()).containsExactlyInAnyOrder(WAITING_CLOSE_CHANNEL); } @@ -146,26 +150,29 @@ class GrpcChannelsTest { .build(); } - private ForceClosedChannel forceClosingChannel(ChannelPoint channelPoint) { + private ForceClosedChannel forceClosingChannel(ChannelPoint channelPoint, Initiator initiator) { return ForceClosedChannel.newBuilder() - .setChannel(pendingChannel(channelPoint)) + .setChannel(pendingChannel(channelPoint, initiator)) .setClosingTxid(TRANSACTION_HASH_3) .addPendingHtlcs(PendingHTLC.newBuilder().setOutpoint(HTLC_OUTPOINT.toString()).build()) .build(); } - private PendingChannelsResponse.WaitingCloseChannel waitingCloseChannel(ChannelPoint channelPoint) { + private PendingChannelsResponse.WaitingCloseChannel waitingCloseChannel( + ChannelPoint channelPoint, + Initiator initiator + ) { return PendingChannelsResponse.WaitingCloseChannel.newBuilder() - .setChannel(pendingChannel(channelPoint)) + .setChannel(pendingChannel(channelPoint, initiator)) .build(); } - private PendingChannelsResponse.PendingChannel pendingChannel(ChannelPoint channelPoint) { + private PendingChannelsResponse.PendingChannel pendingChannel(ChannelPoint channelPoint, Initiator initiator) { return PendingChannelsResponse.PendingChannel.newBuilder() .setRemoteNodePub(PUBKEY_2.toString()) .setCapacity(CAPACITY.satoshis()) .setChannelPoint(channelPoint.toString()) - .setInitiator(Initiator.INITIATOR_LOCAL) + .setInitiator(initiator) .build(); } } \ No newline at end of file diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcClosedChannelsTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcClosedChannelsTest.java index 50d9a319..4056b029 100644 --- a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcClosedChannelsTest.java +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcClosedChannelsTest.java @@ -1,6 +1,10 @@ package de.cotto.lndmanagej.grpc; import de.cotto.lndmanagej.model.ChannelIdResolver; +import de.cotto.lndmanagej.model.CloseInitiator; +import de.cotto.lndmanagej.model.ClosedChannelFixtures; +import de.cotto.lndmanagej.model.ForceClosedChannelBuilder; +import de.cotto.lndmanagej.model.OpenInitiator; import lnrpc.ChannelCloseSummary; import lnrpc.Initiator; import org.junit.jupiter.api.BeforeEach; @@ -14,14 +18,13 @@ import java.util.List; import java.util.Optional; import static de.cotto.lndmanagej.model.ChannelFixtures.CAPACITY; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2_SHORT; -import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_3; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_SHORT; import static de.cotto.lndmanagej.model.ChannelPointFixtures.CHANNEL_POINT; import static de.cotto.lndmanagej.model.ChannelPointFixtures.TRANSACTION_HASH_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; import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL_BREACH; import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL_LOCAL; import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL_REMOTE; @@ -66,7 +69,7 @@ class GrpcClosedChannelsTest { when(grpcService.getClosedChannels()).thenReturn( List.of( closedChannel(CHANNEL_ID_SHORT, COOPERATIVE_CLOSE, INITIATOR_LOCAL, INITIATOR_REMOTE), - closedChannel(CHANNEL_ID_2_SHORT, COOPERATIVE_CLOSE, INITIATOR_UNKNOWN, INITIATOR_LOCAL) + closedChannel(CHANNEL_ID_2_SHORT, COOPERATIVE_CLOSE, INITIATOR_UNKNOWN, INITIATOR_UNKNOWN) ) ); assertThat(grpcClosedChannels.getClosedChannels()) @@ -74,6 +77,32 @@ class GrpcClosedChannelsTest { verify(channelIdResolver, never()).resolveFromChannelPoint(any()); } + @Test + void getClosedChannels_close_initiator_unknown_but_force_close_local() { + when(grpcService.getClosedChannels()).thenReturn(List.of( + closedChannel(CHANNEL_ID_SHORT, LOCAL_FORCE_CLOSE, INITIATOR_UNKNOWN, INITIATOR_UNKNOWN) + )); + assertThat(grpcClosedChannels.getClosedChannels()) + .containsExactlyInAnyOrder(ClosedChannelFixtures.getWithDefaults(new ForceClosedChannelBuilder()) + .withOpenInitiator(OpenInitiator.UNKNOWN) + .withCloseInitiator(CloseInitiator.LOCAL) + .build()); + verify(channelIdResolver, never()).resolveFromChannelPoint(any()); + } + + @Test + void getClosedChannels_close_initiator_unknown_but_force_close_remote() { + when(grpcService.getClosedChannels()).thenReturn(List.of( + closedChannel(CHANNEL_ID_SHORT, REMOTE_FORCE_CLOSE, INITIATOR_UNKNOWN, INITIATOR_UNKNOWN) + )); + assertThat(grpcClosedChannels.getClosedChannels()) + .containsExactlyInAnyOrder(ClosedChannelFixtures.getWithDefaults(new ForceClosedChannelBuilder()) + .withOpenInitiator(OpenInitiator.UNKNOWN) + .withCloseInitiator(CloseInitiator.REMOTE) + .build()); + verify(channelIdResolver, never()).resolveFromChannelPoint(any()); + } + @Test void getClosedChannels_force_closed_local() { when(grpcService.getClosedChannels()).thenReturn(List.of( @@ -113,11 +142,11 @@ class GrpcClosedChannelsTest { @Test void getClosedChannels_with_zero_channel_id_resolved() { - when(channelIdResolver.resolveFromChannelPoint(CHANNEL_POINT)).thenReturn(Optional.of(CHANNEL_ID_3)); + when(channelIdResolver.resolveFromChannelPoint(CHANNEL_POINT)).thenReturn(Optional.of(CHANNEL_ID_2)); when(grpcService.getClosedChannels()).thenReturn(List.of( - closedChannel(0, COOPERATIVE_CLOSE, INITIATOR_LOCAL, INITIATOR_UNKNOWN) + closedChannel(0, COOPERATIVE_CLOSE, INITIATOR_UNKNOWN, INITIATOR_UNKNOWN) )); - assertThat(grpcClosedChannels.getClosedChannels()).containsExactlyInAnyOrder(CLOSED_CHANNEL_3); + assertThat(grpcClosedChannels.getClosedChannels()).containsExactlyInAnyOrder(CLOSED_CHANNEL_2); } @Test diff --git a/model/src/test/java/de/cotto/lndmanagej/model/ForceClosedChannelTest.java b/model/src/test/java/de/cotto/lndmanagej/model/ForceClosedChannelTest.java index d8747849..07a3aded 100644 --- a/model/src/test/java/de/cotto/lndmanagej/model/ForceClosedChannelTest.java +++ b/model/src/test/java/de/cotto/lndmanagej/model/ForceClosedChannelTest.java @@ -7,6 +7,7 @@ import static de.cotto.lndmanagej.model.ChannelFixtures.CAPACITY; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; import static de.cotto.lndmanagej.model.ChannelPointFixtures.CHANNEL_POINT; import static de.cotto.lndmanagej.model.ChannelPointFixtures.TRANSACTION_HASH_2; +import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL; import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL_REMOTE; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2; @@ -25,7 +26,7 @@ class ForceClosedChannelTest { .withOpenInitiator(OpenInitiator.LOCAL) .withCloseInitiator(CloseInitiator.REMOTE) .build() - ).isEqualTo(FORCE_CLOSED_CHANNEL_REMOTE); + ).isEqualTo(FORCE_CLOSED_CHANNEL); } @Test diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/model/CoopClosedChannelFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/model/CoopClosedChannelFixtures.java index ed6c6ac7..a6bbe638 100644 --- a/model/src/testFixtures/java/de/cotto/lndmanagej/model/CoopClosedChannelFixtures.java +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/model/CoopClosedChannelFixtures.java @@ -2,6 +2,7 @@ package de.cotto.lndmanagej.model; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_3; +import static de.cotto.lndmanagej.model.ChannelPointFixtures.CHANNEL_POINT_3; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_3; public class CoopClosedChannelFixtures { @@ -13,13 +14,14 @@ public class CoopClosedChannelFixtures { ClosedChannelFixtures.getWithDefaults(new CoopClosedChannelBuilder()) .withChannelId(CHANNEL_ID_2) .withOpenInitiator(OpenInitiator.UNKNOWN) - .withCloseInitiator(CloseInitiator.LOCAL) + .withCloseInitiator(CloseInitiator.UNKNOWN) .build(); public static final CoopClosedChannel CLOSED_CHANNEL_3 = ClosedChannelFixtures.getWithDefaults(new CoopClosedChannelBuilder()) + .withChannelPoint(CHANNEL_POINT_3) .withChannelId(CHANNEL_ID_3) - .withCloseInitiator(CloseInitiator.UNKNOWN) + .withOpenInitiator(OpenInitiator.REMOTE) .build(); public static final CoopClosedChannel CLOSED_CHANNEL_TO_NODE_3 = diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/model/ForceClosedChannelFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/model/ForceClosedChannelFixtures.java index 9372d9fd..46c45062 100644 --- a/model/src/testFixtures/java/de/cotto/lndmanagej/model/ForceClosedChannelFixtures.java +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/model/ForceClosedChannelFixtures.java @@ -1,6 +1,17 @@ package de.cotto.lndmanagej.model; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2; + public class ForceClosedChannelFixtures { + public static final ForceClosedChannel FORCE_CLOSED_CHANNEL = + ClosedChannelFixtures.getWithDefaults(new ForceClosedChannelBuilder()) + .build(); + + public static final ForceClosedChannel FORCE_CLOSED_CHANNEL_2 = + ClosedChannelFixtures.getWithDefaults(new ForceClosedChannelBuilder()) + .withChannelId(CHANNEL_ID_2) + .build(); + public static final ForceClosedChannel FORCE_CLOSED_CHANNEL_REMOTE = ClosedChannelFixtures.getWithDefaults(new ForceClosedChannelBuilder()).build(); @@ -9,6 +20,16 @@ public class ForceClosedChannelFixtures { .withCloseInitiator(CloseInitiator.LOCAL) .build(); + public static final ForceClosedChannel FORCE_CLOSED_CHANNEL_OPEN_LOCAL = + ClosedChannelFixtures.getWithDefaults(new ForceClosedChannelBuilder()) + .withOpenInitiator(OpenInitiator.LOCAL) + .build(); + + public static final ForceClosedChannel FORCE_CLOSED_CHANNEL_OPEN_REMOTE = + ClosedChannelFixtures.getWithDefaults(new ForceClosedChannelBuilder()) + .withOpenInitiator(OpenInitiator.REMOTE) + .build(); + public static final BreachForceClosedChannel FORCE_CLOSED_CHANNEL_BREACH = ClosedChannelFixtures.getWithDefaults(new BreachForceClosedChannelBuilder()).build(); } diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/model/ForceClosingChannelFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/model/ForceClosingChannelFixtures.java index 463d88dd..21120a5c 100644 --- a/model/src/testFixtures/java/de/cotto/lndmanagej/model/ForceClosingChannelFixtures.java +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/model/ForceClosingChannelFixtures.java @@ -11,6 +11,8 @@ import static de.cotto.lndmanagej.model.ChannelPointFixtures.CHANNEL_POINT_2; import static de.cotto.lndmanagej.model.ChannelPointFixtures.CHANNEL_POINT_3; import static de.cotto.lndmanagej.model.ChannelPointFixtures.TRANSACTION_HASH_3; import static de.cotto.lndmanagej.model.OpenInitiator.LOCAL; +import static de.cotto.lndmanagej.model.OpenInitiator.REMOTE; +import static de.cotto.lndmanagej.model.OpenInitiator.UNKNOWN; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_3; @@ -21,9 +23,9 @@ public class ForceClosingChannelFixtures { public static final ForceClosingChannel FORCE_CLOSING_CHANNEL = new ForceClosingChannel( CHANNEL_ID, CHANNEL_POINT, CAPACITY, PUBKEY, PUBKEY_2, TRANSACTION_HASH_3, HTLC_OUTPOINTS, LOCAL); public static final ForceClosingChannel FORCE_CLOSING_CHANNEL_2 = new ForceClosingChannel( - CHANNEL_ID_2, CHANNEL_POINT_2, CAPACITY, PUBKEY, PUBKEY_2, TRANSACTION_HASH_3, HTLC_OUTPOINTS, LOCAL); + CHANNEL_ID_2, CHANNEL_POINT_2, CAPACITY, PUBKEY, PUBKEY_2, TRANSACTION_HASH_3, HTLC_OUTPOINTS, REMOTE); public static final ForceClosingChannel FORCE_CLOSING_CHANNEL_3 = new ForceClosingChannel( - CHANNEL_ID_3, CHANNEL_POINT, CAPACITY, PUBKEY, PUBKEY_2, TRANSACTION_HASH_3, HTLC_OUTPOINTS, LOCAL); + CHANNEL_ID_3, CHANNEL_POINT, CAPACITY, PUBKEY, PUBKEY_2, TRANSACTION_HASH_3, HTLC_OUTPOINTS, UNKNOWN); public static final ForceClosingChannel FORCE_CLOSING_CHANNEL_TO_NODE_3 = new ForceClosingChannel( CHANNEL_ID_3, CHANNEL_POINT, CAPACITY, PUBKEY, PUBKEY_3, TRANSACTION_HASH_3, HTLC_OUTPOINTS, LOCAL); } diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/model/WaitingCloseChannelFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/model/WaitingCloseChannelFixtures.java index d03bb798..3b0ea973 100644 --- a/model/src/testFixtures/java/de/cotto/lndmanagej/model/WaitingCloseChannelFixtures.java +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/model/WaitingCloseChannelFixtures.java @@ -7,6 +7,7 @@ import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_3; import static de.cotto.lndmanagej.model.ChannelPointFixtures.CHANNEL_POINT; import static de.cotto.lndmanagej.model.ChannelPointFixtures.CHANNEL_POINT_2; import static de.cotto.lndmanagej.model.OpenInitiator.LOCAL; +import static de.cotto.lndmanagej.model.OpenInitiator.REMOTE; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_3; @@ -15,7 +16,7 @@ public class WaitingCloseChannelFixtures { public static final WaitingCloseChannel WAITING_CLOSE_CHANNEL = new WaitingCloseChannel(CHANNEL_ID, CHANNEL_POINT, CAPACITY, PUBKEY, PUBKEY_2, LOCAL); public static final WaitingCloseChannel WAITING_CLOSE_CHANNEL_2 - = new WaitingCloseChannel(CHANNEL_ID_2, CHANNEL_POINT_2, CAPACITY, PUBKEY, PUBKEY_2, LOCAL); + = new WaitingCloseChannel(CHANNEL_ID_2, CHANNEL_POINT_2, CAPACITY, PUBKEY, PUBKEY_2, REMOTE); public static final WaitingCloseChannel WAITING_CLOSE_CHANNEL_TO_NODE_3 = new WaitingCloseChannel(CHANNEL_ID_3, CHANNEL_POINT, CAPACITY, PUBKEY, PUBKEY_3, LOCAL); } diff --git a/transactions/src/testFixtures/java/de/cotto/lndmanagej/transactions/model/TransactionFixtures.java b/transactions/src/testFixtures/java/de/cotto/lndmanagej/transactions/model/TransactionFixtures.java index 3e7815a1..734def29 100644 --- a/transactions/src/testFixtures/java/de/cotto/lndmanagej/transactions/model/TransactionFixtures.java +++ b/transactions/src/testFixtures/java/de/cotto/lndmanagej/transactions/model/TransactionFixtures.java @@ -3,11 +3,15 @@ package de.cotto.lndmanagej.transactions.model; import de.cotto.lndmanagej.model.Coins; import static de.cotto.lndmanagej.model.ChannelPointFixtures.TRANSACTION_HASH; +import static de.cotto.lndmanagej.model.ChannelPointFixtures.TRANSACTION_HASH_2; public class TransactionFixtures { public static final int BLOCK_HEIGHT = 700_123; public static final int POSITION_IN_BLOCK = 1234; - public static final Coins FEES = Coins.ofSatoshis(123); + public static final Coins FEES = Coins.ofSatoshis(124); + public static final Coins FEES_2 = Coins.ofSatoshis(456); public static final Transaction TRANSACTION = new Transaction(TRANSACTION_HASH, BLOCK_HEIGHT, POSITION_IN_BLOCK, FEES); + public static final Transaction TRANSACTION_2 = + new Transaction(TRANSACTION_HASH_2, BLOCK_HEIGHT, POSITION_IN_BLOCK, FEES_2); }