diff --git a/application/src/main/java/de/cotto/lndmanagej/ChannelLogger.java b/application/src/main/java/de/cotto/lndmanagej/ChannelLogger.java new file mode 100644 index 00000000..dc4a38cd --- /dev/null +++ b/application/src/main/java/de/cotto/lndmanagej/ChannelLogger.java @@ -0,0 +1,23 @@ +package de.cotto.lndmanagej; + +import de.cotto.lndmanagej.grpc.GrpcChannels; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class ChannelLogger { + private final Logger logger = LoggerFactory.getLogger(getClass()); + private final GrpcChannels grpcChannels; + + public ChannelLogger(GrpcChannels grpcChannels) { + this.grpcChannels = grpcChannels; + } + + @Scheduled(fixedDelay = 60_000) + public void logChannels() { + grpcChannels.getChannels() + .forEach(channel -> logger.info("Channel: {}", channel)); + } +} diff --git a/application/src/test/java/de/cotto/lndmanagej/ChannelLoggerTest.java b/application/src/test/java/de/cotto/lndmanagej/ChannelLoggerTest.java new file mode 100644 index 00000000..5d9363ae --- /dev/null +++ b/application/src/test/java/de/cotto/lndmanagej/ChannelLoggerTest.java @@ -0,0 +1,36 @@ +package de.cotto.lndmanagej; + +import de.cotto.lndmanagej.grpc.GrpcChannels; +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 uk.org.lidalia.slf4jtest.TestLogger; +import uk.org.lidalia.slf4jtest.TestLoggerFactory; + +import java.util.Set; + +import static de.cotto.lndmanagej.model.ChannelFixtures.CHANNEL; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; +import static uk.org.lidalia.slf4jtest.LoggingEvent.info; + +@ExtendWith(MockitoExtension.class) +class ChannelLoggerTest { + private final TestLogger logger = TestLoggerFactory.getTestLogger(ChannelLogger.class); + + @InjectMocks + private ChannelLogger channelLogger; + + @Mock + @SuppressWarnings("unused") + private GrpcChannels grpcChannels; + + @Test + void logChannels() { + when(grpcChannels.getChannels()).thenReturn(Set.of(CHANNEL)); + channelLogger.logChannels(); + assertThat(logger.getLoggingEvents()).contains(info("Channel: {}", CHANNEL)); + } +} \ No newline at end of file diff --git a/application/src/test/java/de/cotto/lndmanagej/InfoLoggerTest.java b/application/src/test/java/de/cotto/lndmanagej/InfoLoggerTest.java index 9b75dfeb..c4e8b4ad 100644 --- a/application/src/test/java/de/cotto/lndmanagej/InfoLoggerTest.java +++ b/application/src/test/java/de/cotto/lndmanagej/InfoLoggerTest.java @@ -22,7 +22,6 @@ class InfoLoggerTest { private InfoLogger infoLogger; @Mock - @SuppressWarnings("unused") private GrpcGetInfo grpcGetInfo; @Test 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 new file mode 100644 index 00000000..8fc3b493 --- /dev/null +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcChannels.java @@ -0,0 +1,34 @@ +package de.cotto.lndmanagej.grpc; + +import de.cotto.lndmanagej.model.Channel; +import de.cotto.lndmanagej.model.ChannelId; +import de.cotto.lndmanagej.model.Coins; +import de.cotto.lndmanagej.model.Node; +import org.springframework.stereotype.Component; + +import java.util.Set; +import java.util.stream.Collectors; + +@Component +public class GrpcChannels { + private final GrpcService grpcService; + private final Node ownNode; + + public GrpcChannels(GrpcService grpcService, GrpcGetInfo grpcGetInfo) { + this.grpcService = grpcService; + ownNode = grpcGetInfo.getNode(); + } + + public Set getChannels() { + return grpcService.getChannels().stream().map(this::toChannel).collect(Collectors.toSet()); + } + + private Channel toChannel(lnrpc.Channel lndChannel) { + return Channel.builder() + .withChannelId(ChannelId.fromShortChannelId(lndChannel.getChanId())) + .withCapacity(Coins.ofSatoshis(lndChannel.getCapacity())) + .withNode1(ownNode) + .withNode2(Node.builder().withPubkey(lndChannel.getRemotePubkey()).build()) + .build(); + } +} diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcGetInfo.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcGetInfo.java index efc548af..35abd4aa 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcGetInfo.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcGetInfo.java @@ -1,5 +1,6 @@ package de.cotto.lndmanagej.grpc; +import de.cotto.lndmanagej.model.Node; import lnrpc.GetInfoResponse; import lnrpc.GetInfoResponseOrBuilder; import org.springframework.scheduling.annotation.Scheduled; @@ -21,6 +22,10 @@ public class GrpcGetInfo { refreshInfo(); } + public Node getNode() { + return Node.builder().withPubkey(getPubkey()).withAlias(getAlias()).build(); + } + public String getPubkey() { return getInfo().getIdentityPubkey(); } 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 cb2a90bf..7793b12f 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 @@ -2,8 +2,10 @@ package de.cotto.lndmanagej.grpc; import de.cotto.lndmanagej.LndConfiguration; import io.grpc.StatusRuntimeException; +import lnrpc.Channel; import lnrpc.GetInfoResponse; import lnrpc.LightningGrpc; +import lnrpc.ListChannelsRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @@ -15,6 +17,7 @@ import javax.annotation.PreDestroy; import java.io.IOException; import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Optional; import java.util.function.Supplier; @@ -59,4 +62,8 @@ public class GrpcService { return Optional.empty(); } } + + public List getChannels() { + return lightningStub.listChannels(ListChannelsRequest.getDefaultInstance()).getChannelsList(); + } } 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 new file mode 100644 index 00000000..14cd16c4 --- /dev/null +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcChannelsTest.java @@ -0,0 +1,54 @@ +package de.cotto.lndmanagej.grpc; + +import lnrpc.Channel; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static de.cotto.lndmanagej.model.ChannelFixtures.CAPACITY; +import static de.cotto.lndmanagej.model.ChannelFixtures.CHANNEL; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static de.cotto.lndmanagej.model.NodeFixtures.NODE; +import static de.cotto.lndmanagej.model.NodeFixtures.NODE_2; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class GrpcChannelsTest { + private GrpcChannels grpcChannels; + + @Mock + private GrpcService grpcService; + + @Mock + private GrpcGetInfo grpcGetInfo; + + @BeforeEach + void setUp() { + when(grpcGetInfo.getNode()).thenReturn(NODE); + grpcChannels = new GrpcChannels(grpcService, grpcGetInfo); + } + + @Test + void no_channels() { + assertThat(grpcChannels.getChannels()).isEmpty(); + } + + @Test + void one_channel() { + when(grpcService.getChannels()).thenReturn(List.of(channel())); + assertThat(grpcChannels.getChannels()).containsExactly(CHANNEL); + } + + private Channel channel() { + return Channel.newBuilder() + .setChanId(CHANNEL_ID.shortChannelId()) + .setCapacity(CAPACITY.satoshis()) + .setRemotePubkey(NODE_2.pubkey()) + .build(); + } +} \ No newline at end of file diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcGetInfoTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcGetInfoTest.java index 928e8cb5..6dcbbf93 100644 --- a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcGetInfoTest.java +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcGetInfoTest.java @@ -1,5 +1,6 @@ package de.cotto.lndmanagej.grpc; +import de.cotto.lndmanagej.model.Node; import lnrpc.GetInfoResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -55,6 +56,11 @@ class GrpcGetInfoTest { .build(); } + @Test + void getNode() { + assertThat(grpcGetInfo.getNode()).isEqualTo(Node.builder().withPubkey(PUBKEY).withAlias(ALIAS).build()); + } + @Test void getPubkey() { assertThat(grpcGetInfo.getPubkey()).isEqualTo(PUBKEY); diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcHtlcEventsTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcHtlcEventsTest.java index dd0be8a9..f3b7efa7 100644 --- a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcHtlcEventsTest.java +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcHtlcEventsTest.java @@ -32,7 +32,6 @@ class GrpcHtlcEventsTest { private GrpcHtlcEvents grpcHtlcEvents; @Mock - @SuppressWarnings("unused") private GrpcService grpcService; private final ForwardEvent forwardEvent = ForwardEvent.newBuilder() diff --git a/model/src/main/java/de/cotto/lndmanagej/model/Channel.java b/model/src/main/java/de/cotto/lndmanagej/model/Channel.java index 1b7df532..b46dce9d 100644 --- a/model/src/main/java/de/cotto/lndmanagej/model/Channel.java +++ b/model/src/main/java/de/cotto/lndmanagej/model/Channel.java @@ -97,4 +97,13 @@ public class Channel { public int hashCode() { return Objects.hash(channelId, capacity, nodes); } + + @Override + public String toString() { + return "Channel[" + + "channelId=" + channelId + + ", capacity=" + capacity + + ", nodes=" + nodes + + ']'; + } } diff --git a/model/src/test/java/de/cotto/lndmanagej/model/ChannelTest.java b/model/src/test/java/de/cotto/lndmanagej/model/ChannelTest.java index 8a8c63c1..0d723600 100644 --- a/model/src/test/java/de/cotto/lndmanagej/model/ChannelTest.java +++ b/model/src/test/java/de/cotto/lndmanagej/model/ChannelTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import static de.cotto.lndmanagej.model.ChannelFixtures.CAPACITY; import static de.cotto.lndmanagej.model.ChannelFixtures.CHANNEL; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; import static de.cotto.lndmanagej.model.NodeFixtures.NODE; import static de.cotto.lndmanagej.model.NodeFixtures.NODE_2; import static org.assertj.core.api.Assertions.assertThat; @@ -33,7 +34,7 @@ class ChannelTest { void builder_without_capacity() { assertThatNullPointerException().isThrownBy( () -> Channel.builder() - .withChannelId(ChannelIdFixtures.CHANNEL_ID) + .withChannelId(CHANNEL_ID) .withNode1(NODE) .withNode2(NODE_2) .build() @@ -44,7 +45,7 @@ class ChannelTest { void builder_without_node1() { assertThatNullPointerException().isThrownBy( () -> Channel.builder() - .withChannelId(ChannelIdFixtures.CHANNEL_ID) + .withChannelId(CHANNEL_ID) .withCapacity(CAPACITY) .withNode2(NODE_2) .build() @@ -55,7 +56,7 @@ class ChannelTest { void builder_without_node2() { assertThatNullPointerException().isThrownBy( () -> Channel.builder() - .withChannelId(ChannelIdFixtures.CHANNEL_ID) + .withChannelId(CHANNEL_ID) .withCapacity(CAPACITY) .withNode1(NODE) .build() @@ -65,7 +66,7 @@ class ChannelTest { @Test void builder_with_all_arguments() { Channel channel = Channel.builder() - .withChannelId(ChannelIdFixtures.CHANNEL_ID) + .withChannelId(CHANNEL_ID) .withCapacity(CAPACITY) .withNode1(NODE) .withNode2(NODE_2) @@ -75,7 +76,7 @@ class ChannelTest { @Test void getId() { - assertThat(CHANNEL.getId()).isEqualTo(ChannelIdFixtures.CHANNEL_ID); + assertThat(CHANNEL.getId()).isEqualTo(CHANNEL_ID); } @Test @@ -96,11 +97,22 @@ class ChannelTest { @Test void testEquals_reversed_nodes() { Channel channel = Channel.builder() - .withChannelId(ChannelIdFixtures.CHANNEL_ID) + .withChannelId(CHANNEL_ID) .withCapacity(CAPACITY) .withNode1(NODE_2) .withNode2(NODE) .build(); assertThat(CHANNEL).isEqualTo(channel); } + + @Test + void testToString() { + assertThat(CHANNEL).hasToString( + "Channel[" + + "channelId=" + CHANNEL_ID + + ", capacity=" + CAPACITY + + ", nodes=[" + NODE.toString() + ", " + NODE_2.toString() + "]" + + "]" + ); + } } \ No newline at end of file