restructure project

This commit is contained in:
Carsten Otto
2021-11-22 20:24:13 +01:00
parent a82cb13ef7
commit 874e8fffa6
55 changed files with 46 additions and 14 deletions

View File

@@ -0,0 +1,74 @@
package de.cotto.lndmanagej.controller;
import com.codahale.metrics.MetricRegistry;
import de.cotto.lndmanagej.controller.dto.ChannelDetailsDto;
import de.cotto.lndmanagej.controller.dto.ObjectMapperConfiguration;
import de.cotto.lndmanagej.controller.dto.OnChainCostsDto;
import de.cotto.lndmanagej.metrics.Metrics;
import de.cotto.lndmanagej.model.BalanceInformation;
import de.cotto.lndmanagej.model.ChannelId;
import de.cotto.lndmanagej.model.Coins;
import de.cotto.lndmanagej.model.LocalChannel;
import de.cotto.lndmanagej.model.Pubkey;
import de.cotto.lndmanagej.service.BalanceService;
import de.cotto.lndmanagej.service.ChannelService;
import de.cotto.lndmanagej.service.NodeService;
import de.cotto.lndmanagej.service.OnChainCostService;
import org.springframework.context.annotation.Import;
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/channel/{channelId}")
@Import(ObjectMapperConfiguration.class)
public class ChannelDetailsController {
private final ChannelService channelService;
private final NodeService nodeService;
private final Metrics metrics;
private final BalanceService balanceService;
private final OnChainCostService onChainCostService;
public ChannelDetailsController(
ChannelService channelService,
NodeService nodeService,
BalanceService balanceService,
OnChainCostService onChainCostService,
Metrics metrics
) {
this.channelService = channelService;
this.nodeService = nodeService;
this.balanceService = balanceService;
this.onChainCostService = onChainCostService;
this.metrics = metrics;
}
@GetMapping("/details")
public ChannelDetailsDto getDetails(@PathVariable ChannelId channelId) throws NotFoundException {
metrics.mark(MetricRegistry.name(getClass(), "getDetails"));
LocalChannel localChannel = channelService.getLocalChannel(channelId).orElse(null);
if (localChannel == null) {
throw new NotFoundException();
}
Pubkey remotePubkey = localChannel.getRemotePubkey();
String remoteAlias = nodeService.getAlias(remotePubkey);
return new ChannelDetailsDto(
localChannel,
remoteAlias,
getBalanceInformation(channelId),
getOnChainCosts(channelId)
);
}
private BalanceInformation getBalanceInformation(ChannelId channelId) {
return balanceService.getBalanceInformation(channelId)
.orElse(BalanceInformation.EMPTY);
}
private OnChainCostsDto getOnChainCosts(ChannelId channelId) {
Coins openCosts = onChainCostService.getOpenCosts(channelId).orElse(Coins.NONE);
Coins closeCosts = onChainCostService.getCloseCosts(channelId).orElse(Coins.NONE);
return new OnChainCostsDto(openCosts, closeCosts);
}
}

View File

@@ -0,0 +1,24 @@
package de.cotto.lndmanagej.controller;
import de.cotto.lndmanagej.model.ChannelId;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
@Component
public class ChannelIdConverter implements Converter<String, ChannelId> {
public ChannelIdConverter() {
// default constructor
}
@Override
public ChannelId convert(@Nonnull String source) {
try {
long shortChannelId = Long.parseLong(source);
return ChannelId.fromShortChannelId(shortChannelId);
} catch (NumberFormatException numberFormatException) {
return ChannelId.fromCompactForm(source);
}
}
}

View File

@@ -0,0 +1,7 @@
package de.cotto.lndmanagej.controller;
public class CostException extends Exception {
public CostException(String message) {
super(message);
}
}

View File

@@ -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<String> handleException(CostException exception) {
return ResponseEntity
.badRequest()
.body(exception.getMessage());
}
}

View File

@@ -0,0 +1,171 @@
package de.cotto.lndmanagej.controller;
import com.codahale.metrics.MetricRegistry;
import de.cotto.lndmanagej.metrics.Metrics;
import de.cotto.lndmanagej.model.Channel;
import de.cotto.lndmanagej.model.ChannelId;
import de.cotto.lndmanagej.model.LocalOpenChannel;
import de.cotto.lndmanagej.model.Pubkey;
import de.cotto.lndmanagej.service.BalanceService;
import de.cotto.lndmanagej.service.ChannelService;
import de.cotto.lndmanagej.service.FeeService;
import de.cotto.lndmanagej.service.NodeService;
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;
import java.util.Comparator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@RestController
@RequestMapping("/legacy")
public class LegacyController {
private static final String NEWLINE = "\n";
private final NodeService nodeService;
private final ChannelService channelService;
private final FeeService feeService;
private final BalanceService balanceService;
private final Metrics metrics;
public LegacyController(
NodeService nodeService,
ChannelService channelService,
FeeService feeService,
BalanceService balanceService,
Metrics metrics
) {
this.nodeService = nodeService;
this.channelService = channelService;
this.feeService = feeService;
this.balanceService = balanceService;
this.metrics = metrics;
}
@GetMapping("/node/{pubkey}/all-channels")
public String getAllChannelIdsForPubkey(@PathVariable Pubkey pubkey) {
mark("getAllChannelIdsForPubkey");
return channelService.getAllChannelsWith(pubkey).stream()
.map(Channel::getId)
.sorted()
.map(ChannelId::toString)
.collect(Collectors.joining(NEWLINE));
}
@GetMapping("/open-channels")
public String getOpenChannelIds() {
mark("getOpenChannelIds");
return getOpenChannelIdsSorted()
.map(ChannelId::toString)
.collect(Collectors.joining(NEWLINE));
}
@GetMapping("/open-channels/pretty")
public String getOpenChannelIdsPretty() {
mark("getOpenChannelIdsPretty");
return channelService.getOpenChannels().stream()
.sorted(Comparator.comparing(LocalOpenChannel::getId))
.map(localOpenChannel -> {
Pubkey pubkey = localOpenChannel.getRemotePubkey();
return localOpenChannel.getId().getCompactForm() +
"\t" + pubkey +
"\t" + localOpenChannel.getCapacity() +
"\t" + nodeService.getAlias(pubkey);
})
.collect(Collectors.joining(NEWLINE));
}
@GetMapping("/closed-channels")
public String getClosedChannelIds() {
mark("getClosedChannelIds");
return getClosedChannelIdsSorted()
.map(ChannelId::toString)
.collect(Collectors.joining(NEWLINE));
}
@GetMapping("/force-closing-channels")
public String getForceClosingChannelIds() {
mark("getForceClosingChannelIds");
return channelService.getForceClosingChannels().stream()
.map(Channel::getId)
.sorted()
.map(ChannelId::toString)
.collect(Collectors.joining(NEWLINE));
}
@GetMapping("/peer-pubkeys")
public String getPeerPubkeys() {
mark("getPeerPubkeys");
return channelService.getOpenChannels().stream()
.map(LocalOpenChannel::getRemotePubkey)
.map(Pubkey::toString)
.sorted()
.distinct()
.collect(Collectors.joining(NEWLINE));
}
@GetMapping("/channel/{channelId}/incoming-fee-rate")
public long getIncomingFeeRate(@PathVariable ChannelId channelId) {
mark("getIncomingFeeRate");
return feeService.getIncomingFeeRate(channelId);
}
@GetMapping("/channel/{channelId}/outgoing-fee-rate")
public long getOutgoingFeeRate(@PathVariable ChannelId channelId) {
mark("getOutgoingFeeRate");
return feeService.getOutgoingFeeRate(channelId);
}
@GetMapping("/channel/{channelId}/incoming-base-fee")
public long getIncomingBaseFee(@PathVariable ChannelId channelId) {
mark("getIncomingBaseFee");
return feeService.getIncomingBaseFee(channelId).milliSatoshis();
}
@GetMapping("/channel/{channelId}/outgoing-base-fee")
public long getOutgoingBaseFee(@PathVariable ChannelId channelId) {
mark("getOutgoingBaseFee");
return feeService.getOutgoingBaseFee(channelId).milliSatoshis();
}
@GetMapping("/channel/{channelId}/available-local-balance")
public long getAvailableLocalBalanceForChannel(@PathVariable ChannelId channelId) {
mark("getAvailableLocalBalanceForChannel");
return balanceService.getAvailableLocalBalance(channelId).satoshis();
}
@GetMapping("/channel/{channelId}/available-remote-balance")
public long getAvailableRemoteBalanceForChannel(@PathVariable ChannelId channelId) {
mark("getAvailableRemoteBalanceForChannel");
return balanceService.getAvailableRemoteBalance(channelId).satoshis();
}
@GetMapping("/node/{pubkey}/available-local-balance")
public long getAvailableLocalBalanceForPeer(@PathVariable Pubkey pubkey) {
mark("getAvailableLocalBalanceForPeer");
return balanceService.getAvailableLocalBalance(pubkey).satoshis();
}
@GetMapping("/node/{pubkey}/available-remote-balance")
public long getAvailableRemoteBalanceForPeer(@PathVariable Pubkey pubkey) {
mark("getAvailableRemoteBalanceForPeer");
return balanceService.getAvailableRemoteBalance(pubkey).satoshis();
}
private Stream<ChannelId> getOpenChannelIdsSorted() {
return channelService.getOpenChannels().stream()
.map(Channel::getId)
.sorted();
}
private Stream<ChannelId> getClosedChannelIdsSorted() {
return channelService.getClosedChannels().stream()
.map(Channel::getId)
.sorted();
}
private void mark(String name) {
metrics.mark(MetricRegistry.name(getClass(), name));
}
}

View File

@@ -0,0 +1,98 @@
package de.cotto.lndmanagej.controller;
import com.codahale.metrics.MetricRegistry;
import de.cotto.lndmanagej.controller.dto.BalanceInformationDto;
import de.cotto.lndmanagej.controller.dto.ChannelsForNodeDto;
import de.cotto.lndmanagej.controller.dto.NodeDetailsDto;
import de.cotto.lndmanagej.controller.dto.ObjectMapperConfiguration;
import de.cotto.lndmanagej.controller.dto.OnChainCostsDto;
import de.cotto.lndmanagej.metrics.Metrics;
import de.cotto.lndmanagej.model.BalanceInformation;
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 de.cotto.lndmanagej.model.Pubkey;
import de.cotto.lndmanagej.service.BalanceService;
import de.cotto.lndmanagej.service.ChannelService;
import de.cotto.lndmanagej.service.NodeService;
import de.cotto.lndmanagej.service.OnChainCostService;
import org.springframework.context.annotation.Import;
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;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/node/{pubkey}")
@Import(ObjectMapperConfiguration.class)
public class NodeController {
private final NodeService nodeService;
private final Metrics metrics;
private final ChannelService channelService;
private final OnChainCostService onChainCostService;
private final BalanceService balanceService;
public NodeController(
NodeService nodeService,
ChannelService channelService,
Metrics metrics,
OnChainCostService onChainCostService,
BalanceService balanceService
) {
this.nodeService = nodeService;
this.metrics = metrics;
this.channelService = channelService;
this.onChainCostService = onChainCostService;
this.balanceService = balanceService;
}
@GetMapping("/alias")
public String getAlias(Pubkey pubkey) {
mark("getAlias");
return nodeService.getAlias(pubkey);
}
@GetMapping("/details")
public NodeDetailsDto getDetails(@PathVariable Pubkey pubkey) {
mark("getDetails");
Node node = nodeService.getNode(pubkey);
Coins openCosts = onChainCostService.getOpenCostsWith(pubkey);
Coins closeCosts = onChainCostService.getCloseCostsWith(pubkey);
BalanceInformation balanceInformation = balanceService.getBalanceInformation(pubkey);
return new NodeDetailsDto(
pubkey,
node.alias(),
toSortedList(channelService.getOpenChannelsWith(pubkey)),
toSortedList(channelService.getClosedChannelsWith(pubkey)),
toSortedList(channelService.getWaitingCloseChannelsFor(pubkey)),
toSortedList(channelService.getForceClosingChannelsFor(pubkey)),
new OnChainCostsDto(openCosts, closeCosts),
BalanceInformationDto.createFrom(balanceInformation),
node.online()
);
}
@GetMapping("/open-channels")
public ChannelsForNodeDto getOpenChannelIdsForPubkey(@PathVariable Pubkey pubkey) {
mark("getOpenChannelIdsForPubkey");
List<ChannelId> channels = toSortedList(channelService.getOpenChannelsWith(pubkey));
return new ChannelsForNodeDto(pubkey, channels);
}
private List<ChannelId> toSortedList(Set<? extends Channel> channels) {
return channels.stream()
.map(Channel::getId)
.sorted()
.collect(Collectors.toList());
}
private void mark(String getDetails) {
metrics.mark(MetricRegistry.name(getClass(), getDetails));
}
}

View File

@@ -0,0 +1,7 @@
package de.cotto.lndmanagej.controller;
public class NotFoundException extends Exception {
public NotFoundException() {
super();
}
}

View File

@@ -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 NotFoundExceptionHandler extends ResponseEntityExceptionHandler {
public NotFoundExceptionHandler() {
super();
}
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<String> handleException(@SuppressWarnings("unused") NotFoundException exception) {
return ResponseEntity
.notFound()
.build();
}
}

View File

@@ -0,0 +1,49 @@
package de.cotto.lndmanagej.controller;
import com.codahale.metrics.MetricRegistry;
import de.cotto.lndmanagej.controller.dto.OnChainCostsDto;
import de.cotto.lndmanagej.metrics.Metrics;
import de.cotto.lndmanagej.model.ChannelId;
import de.cotto.lndmanagej.model.Coins;
import de.cotto.lndmanagej.model.Pubkey;
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("/node/{pubkey}/on-chain-costs")
public OnChainCostsDto getCostsForPeer(@PathVariable Pubkey pubkey) {
metrics.mark(MetricRegistry.name(getClass(), "getCostsForPeer"));
return new OnChainCostsDto(
onChainCostService.getOpenCostsWith(pubkey),
onChainCostService.getCloseCostsWith(pubkey)
);
}
@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));
}
@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));
}
}

View File

@@ -0,0 +1,19 @@
package de.cotto.lndmanagej.controller;
import de.cotto.lndmanagej.model.Pubkey;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
@Component
public class PubkeyConverter implements Converter<String, Pubkey> {
public PubkeyConverter() {
// default constructor
}
@Override
public Pubkey convert(@Nonnull String source) {
return Pubkey.create(source);
}
}

View File

@@ -0,0 +1,30 @@
package de.cotto.lndmanagej.controller;
import com.codahale.metrics.MetricRegistry;
import de.cotto.lndmanagej.controller.dto.ObjectMapperConfiguration;
import de.cotto.lndmanagej.metrics.Metrics;
import de.cotto.lndmanagej.service.OwnNodeService;
import org.springframework.context.annotation.Import;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/status/")
@Import(ObjectMapperConfiguration.class)
public class StatusController {
private final OwnNodeService ownNodeService;
private final Metrics metrics;
public StatusController(OwnNodeService ownNodeService, Metrics metrics) {
this.ownNodeService = ownNodeService;
this.metrics = metrics;
}
@GetMapping("/synced-to-chain")
public boolean isSyncedToChain() {
metrics.mark(MetricRegistry.name(getClass(), "isSyncedToChain"));
return ownNodeService.isSyncedToChain();
}
}

View File

@@ -0,0 +1,28 @@
package de.cotto.lndmanagej.controller.dto;
import de.cotto.lndmanagej.model.BalanceInformation;
import de.cotto.lndmanagej.model.Coins;
public record BalanceInformationDto(
String localBalance,
String localReserve,
String localAvailable,
String remoteBalance,
String remoteReserve,
String remoteAvailable
) {
public static BalanceInformationDto createFrom(BalanceInformation balanceInformation) {
return new BalanceInformationDto(
toString(balanceInformation.localBalance()),
toString(balanceInformation.localReserve()),
toString(balanceInformation.localAvailable()),
toString(balanceInformation.remoteBalance()),
toString(balanceInformation.remoteReserve()),
toString(balanceInformation.remoteAvailable())
);
}
private static String toString(Coins coins) {
return String.valueOf(coins.satoshis());
}
}

View File

@@ -0,0 +1,38 @@
package de.cotto.lndmanagej.controller.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.cotto.lndmanagej.model.BalanceInformation;
import de.cotto.lndmanagej.model.ChannelPoint;
import de.cotto.lndmanagej.model.LocalChannel;
import de.cotto.lndmanagej.model.Pubkey;
public record ChannelDetailsDto(
String channelIdShort,
String channelIdCompact,
String channelIdCompactLnd,
ChannelPoint channelPoint,
Pubkey remotePubkey,
String remoteAlias,
@JsonProperty("private") boolean privateChannel,
BalanceInformationDto balance,
OnChainCostsDto onChainCosts
) {
public ChannelDetailsDto(
LocalChannel localChannel,
String remoteAlias,
BalanceInformation balanceInformation,
OnChainCostsDto onChainCosts
) {
this(
String.valueOf(localChannel.getId().getShortChannelId()),
localChannel.getId().getCompactForm(),
localChannel.getId().getCompactFormLnd(),
localChannel.getChannelPoint(),
localChannel.getRemotePubkey(),
remoteAlias,
localChannel.isPrivateChannel(),
BalanceInformationDto.createFrom(balanceInformation),
onChainCosts
);
}
}

View File

@@ -0,0 +1,12 @@
package de.cotto.lndmanagej.controller.dto;
import de.cotto.lndmanagej.model.ChannelId;
import de.cotto.lndmanagej.model.Pubkey;
import java.util.List;
public record ChannelsForNodeDto(
Pubkey node,
List<ChannelId> channels
) {
}

View File

@@ -0,0 +1,21 @@
package de.cotto.lndmanagej.controller.dto;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import de.cotto.lndmanagej.model.ChannelId;
import de.cotto.lndmanagej.model.Pubkey;
import java.util.List;
public record NodeDetailsDto(
@JsonSerialize(using = ToStringSerializer.class) Pubkey node,
String alias,
List<ChannelId> channels,
List<ChannelId> closedChannels,
List<ChannelId> waitingCloseChannels,
List<ChannelId> pendingForceClosingChannels,
OnChainCostsDto onChainCosts,
BalanceInformationDto balance,
boolean online
) {
}

View File

@@ -0,0 +1,28 @@
package de.cotto.lndmanagej.controller.dto;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import de.cotto.lndmanagej.model.ChannelId;
import de.cotto.lndmanagej.model.ChannelPoint;
import de.cotto.lndmanagej.model.Pubkey;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class ObjectMapperConfiguration {
public ObjectMapperConfiguration() {
// default constructor
}
@Bean
@Primary
public ObjectMapper objectMapper() {
SimpleModule module = new SimpleModule("SimpleModule");
module.addSerializer(Pubkey.class, new ToStringSerializer());
module.addSerializer(ChannelId.class, new ToStringSerializer());
module.addSerializer(ChannelPoint.class, new ToStringSerializer());
return new ObjectMapper().registerModule(module);
}
}

View File

@@ -0,0 +1,9 @@
package de.cotto.lndmanagej.controller.dto;
import de.cotto.lndmanagej.model.Coins;
public record OnChainCostsDto(String openCosts, String closeCosts) {
public OnChainCostsDto(Coins openCosts, Coins closeCosts) {
this(String.valueOf(openCosts.satoshis()), String.valueOf(closeCosts.satoshis()));
}
}