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 74a63ee3..500dda55 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 @@ -3,6 +3,7 @@ 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.Pubkey; import org.springframework.stereotype.Component; import java.util.Set; @@ -33,7 +34,7 @@ public class GrpcChannels { .withChannelId(ChannelId.fromShortChannelId(lndChannel.getChanId())) .withCapacity(Coins.ofSatoshis(lndChannel.getCapacity())) .withNode1(grpcGetInfo.getNode()) - .withNode2(grpcNodeInfo.getNode(lndChannel.getRemotePubkey())) + .withNode2(grpcNodeInfo.getNode(Pubkey.create(lndChannel.getRemotePubkey()))) .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 176feaf0..7e0f3541 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,6 +1,7 @@ package de.cotto.lndmanagej.grpc; import de.cotto.lndmanagej.model.Node; +import de.cotto.lndmanagej.model.Pubkey; import lnrpc.GetInfoResponse; import lnrpc.GetInfoResponseOrBuilder; import org.springframework.scheduling.annotation.Scheduled; @@ -26,8 +27,8 @@ public class GrpcGetInfo { return Node.builder().withPubkey(getPubkey()).withAlias(getAlias()).build(); } - public String getPubkey() { - return getInfo().getIdentityPubkey(); + public Pubkey getPubkey() { + return Pubkey.create(getInfo().getIdentityPubkey()); } public String getAlias() { diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcNodeInfo.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcNodeInfo.java index 1f1a27b2..66ea64d8 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcNodeInfo.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcNodeInfo.java @@ -4,6 +4,7 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import de.cotto.lndmanagej.model.Node; +import de.cotto.lndmanagej.model.Pubkey; import lnrpc.LightningNode; import lnrpc.NodeInfo; import org.springframework.stereotype.Component; @@ -17,13 +18,13 @@ public class GrpcNodeInfo { private static final int CACHE_EXPIRY_MINUTES = 30; private final GrpcService grpcService; - private final LoadingCache cache; + private final LoadingCache cache; public GrpcNodeInfo(GrpcService grpcService) { this.grpcService = grpcService; - CacheLoader loader = new CacheLoader<>() { + CacheLoader loader = new CacheLoader<>() { @Override - public Node load(@Nonnull String pubkey) { + public Node load(@Nonnull Pubkey pubkey) { return getNodeWithoutCache(pubkey); } }; @@ -33,11 +34,11 @@ public class GrpcNodeInfo { .build(loader); } - public Node getNode(String pubkey) { + public Node getNode(Pubkey pubkey) { return cache.getUnchecked(pubkey); } - private Node getNodeWithoutCache(String pubkey) { + private Node getNodeWithoutCache(Pubkey pubkey) { NodeInfo nodeInfo = grpcService.getNodeInfo(pubkey).orElse(null); if (nodeInfo == null) { return Node.builder().withPubkey(pubkey).build(); 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 e65eff32..d0ba0f1b 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 @@ -1,6 +1,7 @@ package de.cotto.lndmanagej.grpc; import de.cotto.lndmanagej.LndConfiguration; +import de.cotto.lndmanagej.model.Pubkey; import io.grpc.StatusRuntimeException; import lnrpc.Channel; import lnrpc.GetInfoResponse; @@ -70,7 +71,7 @@ public class GrpcService { .orElse(List.of()); } - public Optional getNodeInfo(String pubkey) { - return get(() -> lightningStub.getNodeInfo(NodeInfoRequest.newBuilder().setPubKey(pubkey).build())); + public Optional getNodeInfo(Pubkey pubkey) { + return get(() -> lightningStub.getNodeInfo(NodeInfoRequest.newBuilder().setPubKey(pubkey.toString()).build())); } } 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 46ecc114..32f6b193 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 @@ -48,7 +48,7 @@ class GrpcChannelsTest { return Channel.newBuilder() .setChanId(CHANNEL_ID.shortChannelId()) .setCapacity(CAPACITY.satoshis()) - .setRemotePubkey(NODE_2.pubkey()) + .setRemotePubkey(NODE_2.pubkey().toString()) .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 5709f669..26632e6e 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 @@ -8,13 +8,12 @@ import org.junit.jupiter.api.Test; import java.time.Instant; import java.util.Optional; +import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class GrpcGetInfoTest { - - private static final String PUBKEY = "pubkey"; private static final String ALIAS = "alias"; private static final String VERSION = "version"; private static final String COMMIT_HASH = "commit"; @@ -40,7 +39,7 @@ class GrpcGetInfoTest { private GetInfoResponse createResponse(int blockHeight, boolean syncedToChain, boolean syncedToGraph) { return GetInfoResponse.newBuilder() - .setIdentityPubkey(PUBKEY) + .setIdentityPubkey(PUBKEY.toString()) .setAlias(ALIAS) .setNumActiveChannels(NUMBER_OF_ACTIVE_CHANNELS) .setNumInactiveChannels(NUMBER_OF_INACTIVE_CHANNELS) diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcNodeInfoTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcNodeInfoTest.java index abdaa57f..3e877c97 100644 --- a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcNodeInfoTest.java +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcNodeInfoTest.java @@ -50,6 +50,6 @@ class GrpcNodeInfoTest { @Test void getNode_error() { when(grpcService.getNodeInfo(NODE.pubkey())).thenReturn(Optional.empty()); - assertThat(grpcNodeInfo.getNode(NODE.pubkey()).alias()).isEqualTo(NODE.pubkey()); + assertThat(grpcNodeInfo.getNode(NODE.pubkey()).alias()).isEqualTo(NODE.pubkey().toString()); } } \ No newline at end of file diff --git a/model/src/main/java/de/cotto/lndmanagej/model/Node.java b/model/src/main/java/de/cotto/lndmanagej/model/Node.java index ce3bb12c..052090b6 100644 --- a/model/src/main/java/de/cotto/lndmanagej/model/Node.java +++ b/model/src/main/java/de/cotto/lndmanagej/model/Node.java @@ -5,7 +5,7 @@ import java.util.Objects; import static java.util.Objects.requireNonNull; -public record Node(String alias, int lastUpdate, String pubkey) implements Comparable { +public record Node(String alias, int lastUpdate, Pubkey pubkey) implements Comparable { public static Builder builder() { return new Builder(); @@ -45,7 +45,7 @@ public record Node(String alias, int lastUpdate, String pubkey) implements Compa private int lastUpdate; @Nullable - private String pubkey; + private Pubkey pubkey; public Builder withAlias(String alias) { this.alias = alias; @@ -57,10 +57,10 @@ public record Node(String alias, int lastUpdate, String pubkey) implements Compa return this; } - public Builder withPubkey(String pubkey) { + public Builder withPubkey(Pubkey pubkey) { this.pubkey = pubkey; if (alias == null) { - alias = pubkey; + alias = pubkey.toString(); } return this; } diff --git a/model/src/main/java/de/cotto/lndmanagej/model/Pubkey.java b/model/src/main/java/de/cotto/lndmanagej/model/Pubkey.java new file mode 100644 index 00000000..82b244ce --- /dev/null +++ b/model/src/main/java/de/cotto/lndmanagej/model/Pubkey.java @@ -0,0 +1,48 @@ +package de.cotto.lndmanagej.model; + +import java.util.Locale; +import java.util.Objects; +import java.util.regex.Pattern; + +public final class Pubkey implements Comparable { + private static final Pattern PATTERN = Pattern.compile("[0-9a-fA-F]{66}"); + private final String string; + + private Pubkey(String string) { + this.string = string; + } + + public static Pubkey create(String string) { + if (!PATTERN.matcher(string).matches()) { + throw new IllegalArgumentException("Pubkey must have 66 hex characters"); + } + return new Pubkey(string.toLowerCase(Locale.US)); + } + + @Override + public int compareTo(Pubkey other) { + return string.compareTo(other.string); + } + + @Override + public String toString() { + return string; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + Pubkey pubkey = (Pubkey) other; + return Objects.equals(string, pubkey.string); + } + + @Override + public int hashCode() { + return Objects.hash(string); + } +} diff --git a/model/src/test/java/de/cotto/lndmanagej/model/NodeTest.java b/model/src/test/java/de/cotto/lndmanagej/model/NodeTest.java index 98f91d82..0896aa2c 100644 --- a/model/src/test/java/de/cotto/lndmanagej/model/NodeTest.java +++ b/model/src/test/java/de/cotto/lndmanagej/model/NodeTest.java @@ -3,17 +3,15 @@ package de.cotto.lndmanagej.model; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.jupiter.api.Test; +import static de.cotto.lndmanagej.model.NodeFixtures.ALIAS; +import static de.cotto.lndmanagej.model.NodeFixtures.LAST_UPDATE; import static de.cotto.lndmanagej.model.NodeFixtures.NODE; import static de.cotto.lndmanagej.model.NodeFixtures.NODE_WITHOUT_ALIAS; +import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNullPointerException; class NodeTest { - - private static final String PUBKEY = NodeFixtures.PUBKEY; - private static final String ALIAS = NodeFixtures.ALIAS; - private static final int LAST_UPDATE = NodeFixtures.LAST_UPDATE; - @Test void builder_without_arguments() { assertThatNullPointerException().isThrownBy( @@ -79,42 +77,42 @@ class NodeTest { @Test void compareTo_by_pubkey_same() { - Node node1 = forPubkey("pubkey"); - Node node2 = forPubkey("pubkey"); + Node node1 = forPubkey("aaa000aaa000abc000abc000abc000abc000abc000abc000abc000abc000abc000"); + Node node2 = forPubkey("aaa000aaa000abc000abc000abc000abc000abc000abc000abc000abc000abc000"); assertThat(node1.compareTo(node2)).isEqualTo(0); } @Test void compareTo_by_pubkey_smaller() { - Node node1 = forPubkey("aaa"); - Node node2 = forPubkey("zzz"); + Node node1 = forPubkey("aaa00abc000abc000abc000abc000abc000abc000abc000abc000abc000abc000a"); + Node node2 = forPubkey("fff00abc000abc000abc000abc000abc000abc000abc000abc000abc000abc000a"); assertThat(node1.compareTo(node2)).isLessThan(0); } @Test void compareTo_by_pubkey_larger() { - Node node1 = forPubkey("0c123"); - Node node2 = forPubkey("0b123"); + Node node1 = forPubkey("0c123abc000abc000abc000abc000abc000abc000abc000abc000abc000abc000a"); + Node node2 = forPubkey("0b123abc000abc000abc000abc000abc000abc000abc000abc000abc000abc000a"); assertThat(node1.compareTo(node2)).isGreaterThan(0); } @Test void getAlias() { - assertThat(NODE.alias()).isEqualTo(NodeFixtures.ALIAS); + assertThat(NODE.alias()).isEqualTo(ALIAS); } @Test void getPubkey() { - assertThat(NODE.pubkey()).isEqualTo(NodeFixtures.PUBKEY); + assertThat(NODE.pubkey()).isEqualTo(PUBKEY); } @Test void getLastUpdate() { - assertThat(NODE.lastUpdate()).isEqualTo(NodeFixtures.LAST_UPDATE); + assertThat(NODE.lastUpdate()).isEqualTo(LAST_UPDATE); } private Node forPubkey(String pubkey) { - return Node.builder().withPubkey(pubkey).withAlias(ALIAS).withLastUpdate(LAST_UPDATE).build(); + return Node.builder().withPubkey(Pubkey.create(pubkey)).withAlias(ALIAS).withLastUpdate(LAST_UPDATE).build(); } } diff --git a/model/src/test/java/de/cotto/lndmanagej/model/PubkeyTest.java b/model/src/test/java/de/cotto/lndmanagej/model/PubkeyTest.java new file mode 100644 index 00000000..95008f86 --- /dev/null +++ b/model/src/test/java/de/cotto/lndmanagej/model/PubkeyTest.java @@ -0,0 +1,84 @@ +package de.cotto.lndmanagej.model; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +class PubkeyTest { + private static final String VALID_PUBKEY_STRING = + "027abc123abc123abc123abc123123abc123abc123abc123abc123abc123abc123"; + + @Test + void empty() { + assertThatIllegalArgumentException().isThrownBy( + () -> Pubkey.create("") + ); + } + + @Test + void too_short() { + assertThatIllegalArgumentException().isThrownBy( + () -> Pubkey.create("027abc123abc123abc123abc123123abc123abc123abc123abc123abc123abc12") + ); + } + + @Test + void too_long() { + assertThatIllegalArgumentException().isThrownBy( + () -> Pubkey.create("027abc123abc123abc123abc123123abc123abc123abc123abc123abc123abc123a") + ); + } + + @Test + void not_just_hex() { + assertThatIllegalArgumentException().isThrownBy( + () -> Pubkey.create("x27abc123abc123abc123abc123123abc123abc123abc123abc123abc123abc123") + ); + } + + @Test + void testToString() { + assertThat(PUBKEY).hasToString(VALID_PUBKEY_STRING); + } + + @Test + void testToString_from_uppercase() { + assertThat(Pubkey.create("027ABC123ABC123ABC123ABC123123ABC123ABC123ABC123ABC123ABC123ABC123")) + .hasToString(VALID_PUBKEY_STRING); + } + + @Test + void testCompareTo_smaller() { + Pubkey one = Pubkey.create(VALID_PUBKEY_STRING); + Pubkey two = Pubkey.create("100000000000000000000000000000000000000000000000000000000000000000"); + assertThat(one.compareTo(two)).isLessThan(0); + } + + @Test + void testCompareTo_same() { + Pubkey one = Pubkey.create(VALID_PUBKEY_STRING); + Pubkey two = Pubkey.create(VALID_PUBKEY_STRING); + assertThat(one.compareTo(two)).isEqualTo(0); + } + + @Test + void testCompareTo_larger() { + Pubkey one = Pubkey.create(VALID_PUBKEY_STRING); + Pubkey two = Pubkey.create("100000000000000000000000000000000000000000000000000000000000000000"); + assertThat(two.compareTo(one)).isGreaterThan(0); + } + + @Test + void testEquals_from_uppercase() { + assertThat(Pubkey.create("027ABC123ABC123ABC123ABC123123ABC123ABC123ABC123ABC123ABC123ABC123")) + .isEqualTo(PUBKEY); + } + + @Test + void testEquals() { + EqualsVerifier.forClass(Pubkey.class).usingGetClass().verify(); + } +} \ No newline at end of file diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/model/NodeFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/model/NodeFixtures.java index 9e01bbff..7a6acab3 100644 --- a/model/src/testFixtures/java/de/cotto/lndmanagej/model/NodeFixtures.java +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/model/NodeFixtures.java @@ -2,9 +2,10 @@ package de.cotto.lndmanagej.model; import java.time.Instant; +import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY; +import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2; + public class NodeFixtures { - public static final String PUBKEY = "027abc123abc123abc123abc123123abc123abc123abc123abc123abc123abc123"; - public static final String PUBKEY_2 = "03fff0000000000000000000000000000000000000000000000000000000000000"; public static final String ALIAS = "Node"; public static final String ALIAS_2 = "Another Node"; public static final int LAST_UPDATE = (int) Instant.now().getEpochSecond(); diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/model/PubkeyFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/model/PubkeyFixtures.java new file mode 100644 index 00000000..32e7b843 --- /dev/null +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/model/PubkeyFixtures.java @@ -0,0 +1,8 @@ +package de.cotto.lndmanagej.model; + +public class PubkeyFixtures { + public static final Pubkey PUBKEY = + Pubkey.create("027abc123abc123abc123abc123123abc123abc123abc123abc123abc123abc123"); + public static final Pubkey PUBKEY_2 = + Pubkey.create("03fff0000000000000000000000000000000000000000000000000000000000000"); +}