From f50d91251f138f32da7fb869008d94cd06638d84 Mon Sep 17 00:00:00 2001 From: Carsten Otto Date: Tue, 16 Nov 2021 22:01:27 +0100 Subject: [PATCH] get waiting-close channels --- .../lndmanagej/service/ChannelService.java | 24 ++++++--- .../service/ChannelServiceTest.java | 22 +++++++- .../cotto/lndmanagej/grpc/GrpcChannels.java | 36 +++++++++++-- .../de/cotto/lndmanagej/grpc/GrpcService.java | 29 ++++++++--- .../lndmanagej/grpc/GrpcChannelsTest.java | 42 ++++++++++++++-- .../lndmanagej/model/WaitingCloseChannel.java | 13 +++++ .../model/WaitingCloseChannelTest.java | 50 +++++++++++++++++++ .../model/WaitingCloseChannelFixtures.java | 22 ++++++++ 8 files changed, 215 insertions(+), 23 deletions(-) create mode 100644 model/src/main/java/de/cotto/lndmanagej/model/WaitingCloseChannel.java create mode 100644 model/src/test/java/de/cotto/lndmanagej/model/WaitingCloseChannelTest.java create mode 100644 model/src/testFixtures/java/de/cotto/lndmanagej/model/WaitingCloseChannelFixtures.java 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 4b4f9a0b..3abc4417 100644 --- a/application/src/main/java/de/cotto/lndmanagej/service/ChannelService.java +++ b/application/src/main/java/de/cotto/lndmanagej/service/ChannelService.java @@ -8,8 +8,10 @@ import de.cotto.lndmanagej.model.ForceClosingChannel; import de.cotto.lndmanagej.model.LocalChannel; import de.cotto.lndmanagej.model.LocalOpenChannel; import de.cotto.lndmanagej.model.Pubkey; +import de.cotto.lndmanagej.model.WaitingCloseChannel; import org.springframework.stereotype.Component; +import java.util.Collection; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -21,6 +23,7 @@ public class ChannelService { private final LoadingCache> channelsCache; private final LoadingCache> closedChannelsCache; private final LoadingCache> forceClosingChannelsCache; + private final LoadingCache> waitingCloseChannelsCache; public ChannelService(GrpcChannels grpcChannels) { channelsCache = new CacheBuilder() @@ -32,6 +35,9 @@ public class ChannelService { forceClosingChannelsCache = new CacheBuilder() .withExpiryMinutes(CACHE_EXPIRY_MINUTES) .build(grpcChannels::getForceClosingChannels); + waitingCloseChannelsCache = new CacheBuilder() + .withExpiryMinutes(CACHE_EXPIRY_MINUTES) + .build(grpcChannels::getWaitingCloseChannels); } public Set getOpenChannels() { @@ -46,6 +52,10 @@ public class ChannelService { return forceClosingChannelsCache.getUnchecked(""); } + public Set getWaitingCloseChannels() { + return waitingCloseChannelsCache.getUnchecked(""); + } + public Set getOpenChannelsWith(Pubkey peer) { return getOpenChannels().stream() .filter(c -> peer.equals(c.getRemotePubkey())) @@ -53,13 +63,13 @@ public class ChannelService { } public Set getAllChannelsWith(Pubkey pubkey) { - Stream openChannels = getOpenChannelsWith(pubkey).stream(); - Stream forceClosingChannels = getForceClosingChannels().stream() - .filter(c -> c.getRemotePubkey().equals(pubkey)); - Stream closedChannels = getClosedChannels().stream() - .filter(c -> c.getRemotePubkey().equals(pubkey)); - return Stream.of(openChannels, closedChannels, forceClosingChannels).flatMap(s -> s) + Set openChannels = getOpenChannelsWith(pubkey); + Set waitingCloseChannels = getWaitingCloseChannels(); + Set forceClosingChannels = getForceClosingChannels(); + Set closedChannels = getClosedChannels(); + return Stream.of(openChannels, closedChannels, waitingCloseChannels, forceClosingChannels) + .flatMap(Collection::stream) + .filter(c -> c.getRemotePubkey().equals(pubkey)) .collect(Collectors.toSet()); } - } 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 35a10df3..c0af8801 100644 --- a/application/src/test/java/de/cotto/lndmanagej/service/ChannelServiceTest.java +++ b/application/src/test/java/de/cotto/lndmanagej/service/ChannelServiceTest.java @@ -21,6 +21,9 @@ import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHAN import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHANNEL_3; import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHANNEL_TO_NODE_3; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_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.model.WaitingCloseChannelFixtures.WAITING_CLOSE_CHANNEL_TO_NODE_3; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; @@ -71,14 +74,29 @@ class ChannelServiceTest { .containsExactlyInAnyOrder(FORCE_CLOSING_CHANNEL, FORCE_CLOSING_CHANNEL_2); } + @Test + void getWaitingCloseChannels() { + when(grpcChannels.getWaitingCloseChannels()) + .thenReturn(Set.of(WAITING_CLOSE_CHANNEL, WAITING_CLOSE_CHANNEL_2)); + assertThat(channelService.getWaitingCloseChannels()) + .containsExactlyInAnyOrder(WAITING_CLOSE_CHANNEL, WAITING_CLOSE_CHANNEL_2); + } + @Test void getAllChannels_by_pubkey() { + when(grpcChannels.getWaitingCloseChannels()) + .thenReturn(Set.of(WAITING_CLOSE_CHANNEL, WAITING_CLOSE_CHANNEL_TO_NODE_3)); when(grpcChannels.getChannels()).thenReturn(Set.of(LOCAL_OPEN_CHANNEL, LOCAL_OPEN_CHANNEL_TO_NODE_3)); when(grpcChannels.getForceClosingChannels()) .thenReturn(Set.of(FORCE_CLOSING_CHANNEL, FORCE_CLOSING_CHANNEL_TO_NODE_3)); when(grpcChannels.getClosedChannels()) .thenReturn(Set.of(CLOSED_CHANNEL_3, CLOSED_CHANNEL_TO_NODE_3)); - assertThat(channelService.getAllChannelsWith(PUBKEY_2)) - .containsExactlyInAnyOrder(LOCAL_OPEN_CHANNEL, CLOSED_CHANNEL_3, FORCE_CLOSING_CHANNEL); + + assertThat(channelService.getAllChannelsWith(PUBKEY_2)).containsExactlyInAnyOrder( + LOCAL_OPEN_CHANNEL, + CLOSED_CHANNEL_3, + FORCE_CLOSING_CHANNEL, + WAITING_CLOSE_CHANNEL + ); } } \ No newline at end of file diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannels.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannels.java index e6aeff01..8531e1f3 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannels.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannels.java @@ -9,8 +9,10 @@ import de.cotto.lndmanagej.model.Coins; import de.cotto.lndmanagej.model.ForceClosingChannel; import de.cotto.lndmanagej.model.LocalOpenChannel; import de.cotto.lndmanagej.model.Pubkey; +import de.cotto.lndmanagej.model.WaitingCloseChannel; import lnrpc.ChannelCloseSummary; import lnrpc.ChannelCloseSummary.ClosureType; +import lnrpc.PendingChannelsResponse; import lnrpc.PendingChannelsResponse.ForceClosedChannel; import lnrpc.PendingChannelsResponse.PendingChannel; import org.springframework.stereotype.Component; @@ -60,16 +62,44 @@ public class GrpcChannels { .collect(toSet()); } + public Set getWaitingCloseChannels() { + Pubkey ownPubkey = grpcGetInfo.getPubkey(); + return grpcService.getWaitingCloseChannels().stream() + .map(waitingCloseChannel -> toWaitingCloseChannel(waitingCloseChannel, ownPubkey)) + .flatMap(Optional::stream) + .collect(toSet()); + } + + private Optional toWaitingCloseChannel( + PendingChannelsResponse.WaitingCloseChannel waitingCloseChannel, + Pubkey ownPubkey + ) { + PendingChannel pendingChannel = waitingCloseChannel.getChannel(); + ChannelPoint channelPoint = ChannelPoint.create(pendingChannel.getChannelPoint()); + return channelIdResolver.resolveFromChannelPoint(channelPoint) + .map(id -> new WaitingCloseChannel( + id, + channelPoint, + Coins.ofSatoshis(pendingChannel.getCapacity()), + ownPubkey, + Pubkey.create(pendingChannel.getRemoteNodePub()) + )); + } + private Optional toForceClosingChannel( ForceClosedChannel forceClosedChannel, Pubkey ownPubkey ) { PendingChannel pendingChannel = forceClosedChannel.getChannel(); ChannelPoint channelPoint = ChannelPoint.create(pendingChannel.getChannelPoint()); - Coins capacity = Coins.ofSatoshis(pendingChannel.getCapacity()); - Pubkey remotePubkey = Pubkey.create(pendingChannel.getRemoteNodePub()); return channelIdResolver.resolveFromChannelPoint(channelPoint) - .map(id -> new ForceClosingChannel(id, channelPoint, capacity, ownPubkey, remotePubkey)); + .map(id -> new ForceClosingChannel( + id, + channelPoint, + Coins.ofSatoshis(pendingChannel.getCapacity()), + ownPubkey, + Pubkey.create(pendingChannel.getRemoteNodePub()) + )); } public Optional getChannel(ChannelId channelId) { diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcService.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcService.java index fccd0ad7..0f9f2a40 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcService.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcService.java @@ -18,6 +18,7 @@ import lnrpc.ListChannelsRequest; import lnrpc.NodeInfo; import lnrpc.NodeInfoRequest; import lnrpc.PendingChannelsRequest; +import lnrpc.PendingChannelsResponse; import lnrpc.PendingChannelsResponse.ForceClosedChannel; import org.springframework.stereotype.Component; @@ -30,11 +31,14 @@ import java.util.Optional; public class GrpcService extends GrpcBase { private static final int CACHE_EXPIRY_MILLISECONDS = 200; + private final Metrics metrics; private final LightningGrpc.LightningBlockingStub lightningStub; private final LoadingCache> channelsCache = new CacheBuilder() .withExpiryMilliseconds(CACHE_EXPIRY_MILLISECONDS) .build(this::getChannelsWithoutCache); - private final Metrics metrics; + private final LoadingCache> pendingChannelsCache = new CacheBuilder() + .withExpiryMilliseconds(CACHE_EXPIRY_MILLISECONDS) + .build(this::getPendingChannelsWithoutCache); public GrpcService(LndConfiguration lndConfiguration, Metrics metrics) throws IOException { super(lndConfiguration); @@ -67,6 +71,10 @@ public class GrpcService extends GrpcBase { return channelsCache.getUnchecked(""); } + private Optional getPendingChannels() { + return pendingChannelsCache.getUnchecked(""); + } + public List getClosedChannels() { mark("closedChannels"); return get(() -> lightningStub.closedChannels(ClosedChannelsRequest.getDefaultInstance()).getChannelsList()) @@ -74,11 +82,20 @@ public class GrpcService extends GrpcBase { } public List getForceClosingChannels() { - mark("closedChannels"); - return get(() -> - lightningStub.pendingChannels(PendingChannelsRequest.getDefaultInstance()) - .getPendingForceClosingChannelsList() - ).orElse(List.of()); + return getPendingChannels() + .map(PendingChannelsResponse::getPendingForceClosingChannelsList) + .orElse(List.of()); + } + + public List getWaitingCloseChannels() { + return getPendingChannels() + .map(PendingChannelsResponse::getWaitingCloseChannelsList) + .orElse(List.of()); + } + + private Optional getPendingChannelsWithoutCache() { + mark("pendingChannels"); + return get(() -> lightningStub.pendingChannels(PendingChannelsRequest.getDefaultInstance())); } private List getChannelsWithoutCache() { 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 4eb46412..d5f5da01 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 @@ -35,6 +35,8 @@ import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHAN import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHANNEL_2; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY; import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2; +import static de.cotto.lndmanagej.model.WaitingCloseChannelFixtures.WAITING_CLOSE_CHANNEL; +import static de.cotto.lndmanagej.model.WaitingCloseChannelFixtures.WAITING_CLOSE_CHANNEL_2; import static lnrpc.ChannelCloseSummary.ClosureType.ABANDONED; import static lnrpc.ChannelCloseSummary.ClosureType.FUNDING_CANCELED; import static org.assertj.core.api.Assertions.assertThat; @@ -108,6 +110,26 @@ class GrpcChannelsTest { assertThat(grpcChannels.getForceClosingChannels()).containsExactlyInAnyOrder(FORCE_CLOSING_CHANNEL); } + @Test + 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)) + ); + assertThat(grpcChannels.getWaitingCloseChannels()) + .containsExactlyInAnyOrder(WAITING_CLOSE_CHANNEL, WAITING_CLOSE_CHANNEL_2); + } + + @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)) + ); + assertThat(grpcChannels.getWaitingCloseChannels()).containsExactlyInAnyOrder(WAITING_CLOSE_CHANNEL); + } + @Test void getClosedChannels_with_zero_channel_id_not_resolved() { when(grpcService.getClosedChannels()).thenReturn( @@ -185,11 +207,21 @@ class GrpcChannelsTest { private ForceClosedChannel forceClosingChannel(ChannelPoint channelPoint) { return ForceClosedChannel.newBuilder() - .setChannel(PendingChannelsResponse.PendingChannel.newBuilder() - .setRemoteNodePub(PUBKEY_2.toString()) - .setCapacity(CAPACITY.satoshis()) - .setChannelPoint(channelPoint.toString()) - .build()) + .setChannel(pendingChannel(channelPoint)) + .build(); + } + + private PendingChannelsResponse.WaitingCloseChannel waitingCloseChannel(ChannelPoint channelPoint) { + return PendingChannelsResponse.WaitingCloseChannel.newBuilder() + .setChannel(pendingChannel(channelPoint)) + .build(); + } + + private PendingChannelsResponse.PendingChannel pendingChannel(ChannelPoint channelPoint) { + return PendingChannelsResponse.PendingChannel.newBuilder() + .setRemoteNodePub(PUBKEY_2.toString()) + .setCapacity(CAPACITY.satoshis()) + .setChannelPoint(channelPoint.toString()) .build(); } diff --git a/model/src/main/java/de/cotto/lndmanagej/model/WaitingCloseChannel.java b/model/src/main/java/de/cotto/lndmanagej/model/WaitingCloseChannel.java new file mode 100644 index 00000000..0bbc8879 --- /dev/null +++ b/model/src/main/java/de/cotto/lndmanagej/model/WaitingCloseChannel.java @@ -0,0 +1,13 @@ +package de.cotto.lndmanagej.model; + +public class WaitingCloseChannel extends LocalChannel { + public WaitingCloseChannel( + ChannelId channelId, + ChannelPoint channelPoint, + Coins capacity, + Pubkey ownPubkey, + Pubkey remotePubkey + ) { + super(channelId, channelPoint, capacity, ownPubkey, remotePubkey); + } +} diff --git a/model/src/test/java/de/cotto/lndmanagej/model/WaitingCloseChannelTest.java b/model/src/test/java/de/cotto/lndmanagej/model/WaitingCloseChannelTest.java new file mode 100644 index 00000000..af9f5662 --- /dev/null +++ b/model/src/test/java/de/cotto/lndmanagej/model/WaitingCloseChannelTest.java @@ -0,0 +1,50 @@ +package de.cotto.lndmanagej.model; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +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.PubkeyFixtures.PUBKEY; +import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2; +import static de.cotto.lndmanagej.model.WaitingCloseChannelFixtures.WAITING_CLOSE_CHANNEL; +import static org.assertj.core.api.Assertions.assertThat; + +class WaitingCloseChannelTest { + @Test + void create() { + assertThat(new WaitingCloseChannel(CHANNEL_ID, CHANNEL_POINT, CAPACITY, PUBKEY, PUBKEY_2)) + .isEqualTo(WAITING_CLOSE_CHANNEL); + } + + @Test + void getId() { + assertThat(WAITING_CLOSE_CHANNEL.getId()).isEqualTo(CHANNEL_ID); + } + + @Test + void getRemotePubkey() { + assertThat(WAITING_CLOSE_CHANNEL.getRemotePubkey()).isEqualTo(PUBKEY_2); + } + + @Test + void getCapacity() { + assertThat(WAITING_CLOSE_CHANNEL.getCapacity()).isEqualTo(CAPACITY); + } + + @Test + void getChannelPoint() { + assertThat(WAITING_CLOSE_CHANNEL.getChannelPoint()).isEqualTo(CHANNEL_POINT); + } + + @Test + void getPubkeys() { + assertThat(WAITING_CLOSE_CHANNEL.getPubkeys()).containsExactlyInAnyOrder(PUBKEY, PUBKEY_2); + } + + @Test + void testEquals() { + EqualsVerifier.forClass(WaitingCloseChannel.class).usingGetClass().verify(); + } +} \ No newline at end of file diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/model/WaitingCloseChannelFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/model/WaitingCloseChannelFixtures.java new file mode 100644 index 00000000..4cc4b108 --- /dev/null +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/model/WaitingCloseChannelFixtures.java @@ -0,0 +1,22 @@ +package de.cotto.lndmanagej.model; + +import static de.cotto.lndmanagej.model.ChannelFixtures.CAPACITY; +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.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.PubkeyFixtures.PUBKEY; +import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2; +import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_3; + +public class WaitingCloseChannelFixtures { + public static final WaitingCloseChannel WAITING_CLOSE_CHANNEL = + new WaitingCloseChannel(CHANNEL_ID, CHANNEL_POINT, CAPACITY, PUBKEY, PUBKEY_2); + public static final WaitingCloseChannel WAITING_CLOSE_CHANNEL_2 + = new WaitingCloseChannel(CHANNEL_ID_2, CHANNEL_POINT_2, CAPACITY, PUBKEY, PUBKEY_2); + public static final WaitingCloseChannel WAITING_CLOSE_CHANNEL_3 = + new WaitingCloseChannel(CHANNEL_ID_3, CHANNEL_POINT, CAPACITY, PUBKEY, PUBKEY_2); + public static final WaitingCloseChannel WAITING_CLOSE_CHANNEL_TO_NODE_3 = + new WaitingCloseChannel(CHANNEL_ID_3, CHANNEL_POINT, CAPACITY, PUBKEY, PUBKEY_3); +}