add basic fee report

This commit is contained in:
Carsten Otto
2021-11-30 12:34:22 +01:00
parent 30f109ad30
commit 113a96054e
7 changed files with 97 additions and 14 deletions

View File

@@ -5,6 +5,7 @@ import de.cotto.lndmanagej.model.ChannelIdResolver;
import de.cotto.lndmanagej.model.Coins;
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 de.cotto.lndmanagej.service.OnChainCostService;
import de.cotto.lndmanagej.service.PolicyService;
@@ -70,6 +71,9 @@ class ChannelControllerIT {
@MockBean
private PolicyService policyService;
@MockBean
private FeeService feeService;
@Test
void getBasicInformation_not_found() throws Exception {
mockMvc.perform(get(CHANNEL_PREFIX + "/"))
@@ -122,6 +126,7 @@ class ChannelControllerIT {
when(onChainCostService.getOpenCosts(CHANNEL_ID)).thenReturn(Optional.of(Coins.ofSatoshis(1000)));
when(onChainCostService.getCloseCosts(CHANNEL_ID)).thenReturn(Optional.of(Coins.ofSatoshis(2000)));
when(balanceService.getBalanceInformation(CHANNEL_ID)).thenReturn(Optional.of(BALANCE_INFORMATION_2));
when(feeService.getEarnedFeesForChannel(CHANNEL_ID)).thenReturn(Coins.ofMilliSatoshis(1_234));
mockMvc.perform(get(DETAILS_PREFIX))
.andExpect(jsonPath("$.channelIdShort", is(String.valueOf(CHANNEL_ID.getShortChannelId()))))
.andExpect(jsonPath("$.channelIdCompact", is(CHANNEL_ID.getCompactForm())))
@@ -151,12 +156,14 @@ class ChannelControllerIT {
.andExpect(jsonPath("$.policies.local.feeRatePpm", is(100)))
.andExpect(jsonPath("$.policies.local.baseFeeMilliSat", is(10)))
.andExpect(jsonPath("$.policies.remote.feeRatePpm", is(222)))
.andExpect(jsonPath("$.policies.remote.baseFeeMilliSat", is(0)));
.andExpect(jsonPath("$.policies.remote.baseFeeMilliSat", is(0)))
.andExpect(jsonPath("$.feeReport.earned", is("1234")));
}
@Test
void getChannelDetails_closed_channel() throws Exception {
when(channelService.getLocalChannel(CHANNEL_ID)).thenReturn(Optional.of(CLOSED_CHANNEL));
when(feeService.getEarnedFeesForChannel(CHANNEL_ID)).thenReturn(Coins.NONE);
mockMvc.perform(get(DETAILS_PREFIX))
.andExpect(jsonPath("$.closeDetails.initiator", is("REMOTE")))
.andExpect(jsonPath("$.closeDetails.height", is(987_654)))
@@ -214,4 +221,11 @@ class ChannelControllerIT {
.andExpect(jsonPath("$.initiator", is("REMOTE")))
.andExpect(jsonPath("$.height", is(987_654)));
}
@Test
void getFeeReport() throws Exception {
when(feeService.getEarnedFeesForChannel(CHANNEL_ID)).thenReturn(Coins.ofMilliSatoshis(1_234));
mockMvc.perform(get(CHANNEL_PREFIX + "/fee-report"))
.andExpect(jsonPath("$.earned", is("1234")));
}
}

View File

@@ -5,6 +5,7 @@ import de.cotto.lndmanagej.controller.dto.BalanceInformationDto;
import de.cotto.lndmanagej.controller.dto.ChannelDetailsDto;
import de.cotto.lndmanagej.controller.dto.ChannelDto;
import de.cotto.lndmanagej.controller.dto.ClosedChannelDetailsDto;
import de.cotto.lndmanagej.controller.dto.FeeReportDto;
import de.cotto.lndmanagej.controller.dto.ObjectMapperConfiguration;
import de.cotto.lndmanagej.controller.dto.OnChainCostsDto;
import de.cotto.lndmanagej.controller.dto.PoliciesDto;
@@ -19,6 +20,7 @@ import de.cotto.lndmanagej.model.Policies;
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 de.cotto.lndmanagej.service.OnChainCostService;
import de.cotto.lndmanagej.service.PolicyService;
@@ -31,8 +33,9 @@ import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Nullable;
@RestController
@RequestMapping("/api/channel/{channelId}")
@Import(ObjectMapperConfiguration.class)
@SuppressWarnings("PMD.ExcessiveImports")
@RequestMapping("/api/channel/{channelId}")
public class ChannelController {
private final ChannelService channelService;
private final NodeService nodeService;
@@ -40,6 +43,7 @@ public class ChannelController {
private final BalanceService balanceService;
private final OnChainCostService onChainCostService;
private final PolicyService policyService;
private final FeeService feeService;
public ChannelController(
ChannelService channelService,
@@ -47,14 +51,16 @@ public class ChannelController {
BalanceService balanceService,
OnChainCostService onChainCostService,
PolicyService policyService,
FeeService feeService,
Metrics metrics
) {
this.channelService = channelService;
this.nodeService = nodeService;
this.balanceService = balanceService;
this.onChainCostService = onChainCostService;
this.metrics = metrics;
this.policyService = policyService;
this.feeService = feeService;
this.metrics = metrics;
}
@GetMapping("/")
@@ -83,7 +89,8 @@ public class ChannelController {
getBalanceInformation(channelId),
getOnChainCosts(channelId),
getPoliciesForChannel(localChannel),
getCloseDetailsForChannel(localChannel)
getCloseDetailsForChannel(localChannel),
getFeeReportDto(localChannel.getId())
);
}
@@ -112,6 +119,17 @@ public class ChannelController {
return new ClosedChannelDetailsDto(closedChannel.getCloseInitiator(), closedChannel.getCloseHeight());
}
@GetMapping("/fee-report")
public FeeReportDto getFeeReport(@PathVariable ChannelId channelId) {
mark("getFeeReport");
return getFeeReportDto(channelId);
}
private FeeReportDto getFeeReportDto(ChannelId channelId) {
Coins earned = feeService.getEarnedFeesForChannel(channelId);
return new FeeReportDto(earned);
}
private PoliciesDto getPoliciesForChannel(@Nullable LocalChannel channel) {
if (channel == null || channel.getStatus().openCloseStatus() != OpenCloseStatus.OPEN) {
return PoliciesDto.EMPTY;

View File

@@ -22,14 +22,16 @@ public record ChannelDetailsDto(
BalanceInformationDto balance,
OnChainCostsDto onChainCosts,
PoliciesDto policies,
ClosedChannelDetailsDto closeDetails
ClosedChannelDetailsDto closeDetails,
FeeReportDto feeReport
) {
public ChannelDetailsDto(
ChannelDto channelDto,
String remoteAlias,
BalanceInformation balanceInformation,
OnChainCostsDto onChainCosts,
PoliciesDto policies
PoliciesDto policies,
FeeReportDto feeReport
) {
this(
channelDto.channelIdShort(),
@@ -47,7 +49,8 @@ public record ChannelDetailsDto(
BalanceInformationDto.createFrom(balanceInformation),
onChainCosts,
policies,
channelDto.closeDetails()
channelDto.closeDetails(),
feeReport
);
}
@@ -57,14 +60,16 @@ public record ChannelDetailsDto(
BalanceInformation balanceInformation,
OnChainCostsDto onChainCosts,
PoliciesDto policies,
ClosedChannelDetailsDto closeDetails
ClosedChannelDetailsDto closeDetails,
FeeReportDto feeReport
) {
this(
new ChannelDto(localChannel, closeDetails),
remoteAlias,
balanceInformation,
onChainCosts,
policies
policies,
feeReport
);
}
}

View File

@@ -0,0 +1,9 @@
package de.cotto.lndmanagej.controller.dto;
import de.cotto.lndmanagej.model.Coins;
public record FeeReportDto(String earned) {
public FeeReportDto(Coins earned) {
this(String.valueOf(earned.milliSatoshis()));
}
}

View File

@@ -4,6 +4,7 @@ import de.cotto.lndmanagej.controller.dto.BalanceInformationDto;
import de.cotto.lndmanagej.controller.dto.ChannelDetailsDto;
import de.cotto.lndmanagej.controller.dto.ChannelDto;
import de.cotto.lndmanagej.controller.dto.ClosedChannelDetailsDto;
import de.cotto.lndmanagej.controller.dto.FeeReportDto;
import de.cotto.lndmanagej.controller.dto.OnChainCostsDto;
import de.cotto.lndmanagej.controller.dto.PoliciesDto;
import de.cotto.lndmanagej.metrics.Metrics;
@@ -13,6 +14,7 @@ import de.cotto.lndmanagej.model.Coins;
import de.cotto.lndmanagej.model.LocalChannel;
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 de.cotto.lndmanagej.service.OnChainCostService;
import de.cotto.lndmanagej.service.PolicyService;
@@ -47,6 +49,7 @@ class ChannelControllerTest {
private static final Coins CLOSE_COSTS = Coins.ofSatoshis(2);
private static final OnChainCostsDto ON_CHAIN_COSTS = new OnChainCostsDto(OPEN_COSTS, CLOSE_COSTS);
private static final PoliciesDto FEE_CONFIGURATION_DTO = PoliciesDto.createFrom(POLICIES);
private static final FeeReportDto FEE_REPORT_DTO = new FeeReportDto(Coins.ofMilliSatoshis(1234));
private static final ClosedChannelDetailsDto CLOSED_CHANNEL_DETAILS_DTO =
new ClosedChannelDetailsDto(CloseInitiator.REMOTE, 987_654);
@@ -71,11 +74,15 @@ class ChannelControllerTest {
@Mock
private PolicyService policyService;
@Mock
private FeeService feeService;
@BeforeEach
void setUp() {
lenient().when(onChainCostService.getOpenCosts(CHANNEL_ID)).thenReturn(Optional.of(OPEN_COSTS));
lenient().when(onChainCostService.getCloseCosts(CHANNEL_ID)).thenReturn(Optional.of(CLOSE_COSTS));
lenient().when(policyService.getPolicies(CHANNEL_ID)).thenReturn(POLICIES);
lenient().when(feeService.getEarnedFeesForChannel(CHANNEL_ID)).thenReturn(Coins.ofMilliSatoshis(1_234));
}
@Test
@@ -114,7 +121,8 @@ class ChannelControllerTest {
LOCAL_OPEN_CHANNEL.getBalanceInformation(),
ON_CHAIN_COSTS,
FEE_CONFIGURATION_DTO,
ClosedChannelDetailsDto.UNKNOWN
ClosedChannelDetailsDto.UNKNOWN,
FEE_REPORT_DTO
);
when(nodeService.getAlias(PUBKEY_2)).thenReturn(ALIAS_2);
when(channelService.getLocalChannel(CHANNEL_ID)).thenReturn(Optional.of(LOCAL_OPEN_CHANNEL));
@@ -133,7 +141,8 @@ class ChannelControllerTest {
LOCAL_OPEN_CHANNEL_PRIVATE.getBalanceInformation(),
ON_CHAIN_COSTS,
FEE_CONFIGURATION_DTO,
ClosedChannelDetailsDto.UNKNOWN
ClosedChannelDetailsDto.UNKNOWN,
FEE_REPORT_DTO
);
when(nodeService.getAlias(PUBKEY_2)).thenReturn(ALIAS_2);
when(channelService.getLocalChannel(CHANNEL_ID)).thenReturn(Optional.of(LOCAL_OPEN_CHANNEL_PRIVATE));
@@ -201,6 +210,13 @@ class ChannelControllerTest {
.isThrownBy(() -> channelController.getCloseDetails(CHANNEL_ID));
}
@Test
void getFeeReport() {
when(feeService.getEarnedFeesForChannel(CHANNEL_ID)).thenReturn(Coins.ofSatoshis(123));
assertThat(channelController.getFeeReport(CHANNEL_ID)).isEqualTo(new FeeReportDto(Coins.ofSatoshis(123)));
verify(metrics).mark(argThat(name -> name.endsWith(".getFeeReport")));
}
private ChannelDetailsDto mockForChannelWithoutPolicies(
LocalChannel channel,
String closeInitiator,
@@ -215,7 +231,8 @@ class ChannelControllerTest {
BalanceInformation.EMPTY,
ON_CHAIN_COSTS,
PoliciesDto.EMPTY,
new ClosedChannelDetailsDto(closeInitiator, closeHeight)
new ClosedChannelDetailsDto(closeInitiator, closeHeight),
FEE_REPORT_DTO
);
}
}

View File

@@ -26,7 +26,8 @@ class ChannelDetailsDtoTest {
BALANCE_INFORMATION,
ON_CHAIN_COSTS,
PoliciesDto.EMPTY,
CLOSE_DETAILS
CLOSE_DETAILS,
new FeeReportDto(Coins.ofMilliSatoshis(1234))
);
@Test
@@ -82,7 +83,8 @@ class ChannelDetailsDtoTest {
BALANCE_INFORMATION,
ON_CHAIN_COSTS,
PoliciesDto.EMPTY,
CLOSE_DETAILS
CLOSE_DETAILS,
new FeeReportDto(Coins.ofMilliSatoshis(1234))
);
ChannelStatusDto channelStatusDto =
ChannelStatusDto.createFrom(new ChannelStatus(false, true, false, OPEN));
@@ -99,6 +101,11 @@ class ChannelDetailsDtoTest {
assertThat(CHANNEL_DETAILS_DTO.balance()).isEqualTo(BalanceInformationDto.createFrom(BALANCE_INFORMATION));
}
@Test
void feeReport() {
assertThat(CHANNEL_DETAILS_DTO.feeReport()).isEqualTo(new FeeReportDto(Coins.ofMilliSatoshis(1_234)));
}
@Test
void onChainCosts() {
assertThat(CHANNEL_DETAILS_DTO.onChainCosts()).isEqualTo(ON_CHAIN_COSTS);

View File

@@ -0,0 +1,13 @@
package de.cotto.lndmanagej.controller.dto;
import de.cotto.lndmanagej.model.Coins;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class FeeReportDtoTest {
@Test
void earned() {
assertThat(new FeeReportDto(Coins.ofMilliSatoshis(1_234)).earned()).isEqualTo("1234");
}
}