add OnChainCostService

This commit is contained in:
Carsten Otto
2021-11-18 10:19:44 +01:00
parent f33138c645
commit 6e3d18790b
19 changed files with 617 additions and 38 deletions

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,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));
}
}

View File

@@ -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<LocalChannel> getAllChannelsWith(Pubkey pubkey) {
Set<LocalOpenChannel> openChannels = getOpenChannelsWith(pubkey);
Set<WaitingCloseChannel> waitingCloseChannels = getWaitingCloseChannels();
Set<ForceClosingChannel> forceClosingChannels = getForceClosingChannels();
Set<ClosedChannel> 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<LocalChannel> getLocalChannel(ChannelId channelId) {
return getAllLocalChannels()
.filter(c -> c.getId().equals(channelId))
.findFirst();
}
public Stream<LocalChannel> getAllLocalChannels() {
Set<LocalOpenChannel> openChannels = getOpenChannels();
Set<WaitingCloseChannel> waitingCloseChannels = getWaitingCloseChannels();
Set<ForceClosingChannel> forceClosingChannels = getForceClosingChannels();
Set<ClosedChannel> closedChannels = getClosedChannels();
return Stream.of(
openChannels,
closedChannels,
waitingCloseChannels,
forceClosingChannels
).flatMap(Collection::stream);
}
}

View File

@@ -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<Coins> getOpenCosts(ChannelId channelId) {
return channelService.getLocalChannel(channelId).flatMap(this::getOpenCosts);
}
public Optional<Coins> 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<Coins> 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();
}
}