add model for on chain costs

This commit is contained in:
Carsten Otto
2021-12-10 14:24:26 +01:00
parent 5858a84ef9
commit 3c0fca7d5c
20 changed files with 234 additions and 125 deletions

View File

@@ -6,6 +6,7 @@ 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.OnChainCosts;
import de.cotto.lndmanagej.model.OpenInitiator;
import de.cotto.lndmanagej.model.Pubkey;
import de.cotto.lndmanagej.transactions.model.Transaction;
@@ -25,11 +26,25 @@ public class OnChainCostService {
}
@Timed
public Coins getOpenCostsWith(Pubkey pubkey) {
public OnChainCosts getOnChainCostsForChannelId(ChannelId channelId) {
return channelService.getLocalChannel(channelId)
.map(this::getOnChainCostsForChannel)
.orElse(OnChainCosts.NONE);
}
@Timed
public OnChainCosts getOnChainCostsForChannel(LocalChannel localChannel) {
return new OnChainCosts(
getOpenCostsForChannel(localChannel).orElse(Coins.NONE),
getCloseCostsForChannelId(localChannel.getId()).orElse(Coins.NONE)
);
}
@Timed
public OnChainCosts getOnChainCostsForPeer(Pubkey pubkey) {
return channelService.getAllChannelsWith(pubkey).parallelStream()
.map(this::getOpenCostsForChannel)
.flatMap(Optional::stream)
.reduce(Coins.NONE, Coins::add);
.map(this::getOnChainCostsForChannel)
.reduce(OnChainCosts.NONE, OnChainCosts::add);
}
@Timed
@@ -55,14 +70,6 @@ public class OnChainCostService {
return Optional.empty();
}
@Timed
public Coins getCloseCostsWith(Pubkey pubkey) {
return channelService.getClosedChannelsWith(pubkey).parallelStream()
.map(this::getCloseCostsForChannel)
.flatMap(Optional::stream)
.reduce(Coins.NONE, Coins::add);
}
@Timed
public Optional<Coins> getCloseCostsForChannelId(ChannelId channelId) {
if (channelService.isClosed(channelId)) {

View File

@@ -3,6 +3,7 @@ package de.cotto.lndmanagej.service;
import de.cotto.lndmanagej.model.ClosedChannel;
import de.cotto.lndmanagej.model.Coins;
import de.cotto.lndmanagej.model.LocalChannel;
import de.cotto.lndmanagej.model.OnChainCosts;
import de.cotto.lndmanagej.transactions.service.TransactionService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
@@ -20,7 +21,6 @@ import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID;
import static de.cotto.lndmanagej.model.CoopClosedChannelFixtures.CLOSED_CHANNEL;
import static de.cotto.lndmanagej.model.CoopClosedChannelFixtures.CLOSED_CHANNEL_2;
import static de.cotto.lndmanagej.model.CoopClosedChannelFixtures.CLOSED_CHANNEL_3;
import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL;
import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL_2;
import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL_OPEN_LOCAL;
import static de.cotto.lndmanagej.model.ForceClosedChannelFixtures.FORCE_CLOSED_CHANNEL_OPEN_REMOTE;
@@ -28,18 +28,22 @@ import static de.cotto.lndmanagej.model.ForceClosingChannelFixtures.FORCE_CLOSIN
import static de.cotto.lndmanagej.model.ForceClosingChannelFixtures.FORCE_CLOSING_CHANNEL_2;
import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHANNEL;
import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHANNEL_2;
import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHANNEL_TO_NODE_3;
import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY;
import static de.cotto.lndmanagej.model.WaitingCloseChannelFixtures.WAITING_CLOSE_CHANNEL;
import static de.cotto.lndmanagej.model.WaitingCloseChannelFixtures.WAITING_CLOSE_CHANNEL_2;
import static de.cotto.lndmanagej.transactions.model.TransactionFixtures.FEES;
import static de.cotto.lndmanagej.transactions.model.TransactionFixtures.FEES_2;
import static de.cotto.lndmanagej.transactions.model.TransactionFixtures.TRANSACTION;
import static de.cotto.lndmanagej.transactions.model.TransactionFixtures.TRANSACTION_2;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class OnChainCostServiceTest {
private static final Coins OPEN_COSTS = TRANSACTION.fees();
@InjectMocks
private OnChainCostService onChainCostService;
@@ -49,31 +53,60 @@ class OnChainCostServiceTest {
@Mock
private ChannelService channelService;
@Test
void getOnChainCostsForChannelId() {
mockOpenTransaction(FORCE_CLOSED_CHANNEL_OPEN_LOCAL);
mockCloseTransaction(FORCE_CLOSED_CHANNEL_OPEN_LOCAL);
when(channelService.isClosed(FORCE_CLOSED_CHANNEL_OPEN_LOCAL.getId())).thenReturn(true);
when(channelService.getLocalChannel(FORCE_CLOSED_CHANNEL_OPEN_LOCAL.getId()))
.thenReturn(Optional.of(FORCE_CLOSED_CHANNEL_OPEN_LOCAL));
when(channelService.getClosedChannel(FORCE_CLOSED_CHANNEL_OPEN_LOCAL.getId()))
.thenReturn(Optional.of(FORCE_CLOSED_CHANNEL_OPEN_LOCAL));
OnChainCosts expected = new OnChainCosts(
FEES,
FEES_2
);
assertThat(onChainCostService.getOnChainCostsForChannelId(CHANNEL_ID)).isEqualTo(expected);
}
@Test
void getOnChainCostsForChannel() {
mockOpenTransaction(FORCE_CLOSED_CHANNEL_OPEN_LOCAL);
mockCloseTransaction(FORCE_CLOSED_CHANNEL_OPEN_LOCAL);
when(channelService.isClosed(FORCE_CLOSED_CHANNEL_OPEN_LOCAL.getId())).thenReturn(true);
when(channelService.getClosedChannel(FORCE_CLOSED_CHANNEL_OPEN_LOCAL.getId()))
.thenReturn(Optional.of(FORCE_CLOSED_CHANNEL_OPEN_LOCAL));
OnChainCosts expected = new OnChainCosts(
FEES,
FEES_2
);
assertThat(onChainCostService.getOnChainCostsForChannel(FORCE_CLOSED_CHANNEL_OPEN_LOCAL)).isEqualTo(expected);
}
@Test
void getOnChainCostsForPeer() {
when(channelService.getAllLocalChannels())
.thenReturn(Stream.of(LOCAL_OPEN_CHANNEL_TO_NODE_3, FORCE_CLOSED_CHANNEL_2))
.thenReturn(Stream.of(LOCAL_OPEN_CHANNEL_TO_NODE_3, FORCE_CLOSED_CHANNEL_2));
when(transactionService.getTransaction(LOCAL_OPEN_CHANNEL_TO_NODE_3.getChannelPoint().getTransactionHash()))
.thenReturn(Optional.of(TRANSACTION));
when(transactionService.getTransaction(FORCE_CLOSED_CHANNEL_2.getChannelPoint().getTransactionHash()))
.thenReturn(Optional.of(TRANSACTION_2));
when(channelService.isClosed(LOCAL_OPEN_CHANNEL_TO_NODE_3.getId())).thenReturn(false);
when(channelService.isClosed(FORCE_CLOSED_CHANNEL_2.getId())).thenReturn(true);
when(channelService.getClosedChannel(FORCE_CLOSED_CHANNEL_2.getId()))
.thenReturn(Optional.of(FORCE_CLOSED_CHANNEL_2));
when(channelService.getAllChannelsWith(PUBKEY))
.thenReturn(Set.of(LOCAL_OPEN_CHANNEL_TO_NODE_3, FORCE_CLOSED_CHANNEL_2));
OnChainCosts expected = new OnChainCosts(
FEES.add(FEES_2),
FEES
);
assertThat(onChainCostService.getOnChainCostsForPeer(PUBKEY)).isEqualTo(expected);
}
@Nested
class GetOpenCosts {
private static final Coins OPEN_COSTS = TRANSACTION.fees();
@Test
void getOpenCosts_by_pubkey() {
when(transactionService.getTransaction(any())).thenReturn(Optional.of(TRANSACTION));
when(channelService.getAllChannelsWith(PUBKEY)).thenReturn(Set.of(
LOCAL_OPEN_CHANNEL,
CLOSED_CHANNEL,
WAITING_CLOSE_CHANNEL,
FORCE_CLOSING_CHANNEL,
FORCE_CLOSED_CHANNEL
));
when(channelService.getAllLocalChannels())
.thenReturn(Stream.of(LOCAL_OPEN_CHANNEL_2))
.thenReturn(Stream.of(LOCAL_OPEN_CHANNEL_2))
.thenReturn(Stream.of(LOCAL_OPEN_CHANNEL_2))
.thenReturn(Stream.of(LOCAL_OPEN_CHANNEL_2))
.thenReturn(Stream.of(LOCAL_OPEN_CHANNEL_2));
assertThat(onChainCostService.getOpenCostsWith(PUBKEY)).isEqualTo(
Coins.ofSatoshis(OPEN_COSTS.satoshis() * 5)
);
}
@Test
void getOpenCosts_by_channel_id_not_resolved() {
assertThat(onChainCostService.getOpenCostsForChannelId(CHANNEL_ID)).isEmpty();
@@ -185,12 +218,6 @@ class OnChainCostServiceTest {
assertThat(onChainCostService.getOpenCostsForChannel(LOCAL_OPEN_CHANNEL))
.contains(Coins.ofSatoshis(41));
}
private void mockOpenTransaction(LocalChannel channel) {
lenient().when(channelService.getAllLocalChannels()).thenReturn(Stream.of(channel));
lenient().when(transactionService.getTransaction(channel.getChannelPoint().getTransactionHash()))
.thenReturn(Optional.of(TRANSACTION));
}
}
@Nested
@@ -202,17 +229,6 @@ class OnChainCostServiceTest {
lenient().when(channelService.isClosed(CHANNEL_ID)).thenReturn(true);
}
@Test
void getOpenCosts_by_pubkey() {
when(transactionService.getTransaction(any())).thenReturn(Optional.of(TRANSACTION_2));
when(channelService.getClosedChannelsWith(PUBKEY)).thenReturn(
Set.of(CLOSED_CHANNEL, FORCE_CLOSED_CHANNEL_2)
);
assertThat(onChainCostService.getCloseCostsWith(PUBKEY)).isEqualTo(
Coins.ofSatoshis(CLOSE_COSTS.satoshis() * 2)
);
}
@Test
void getCloseCosts_by_channel_id_not_resolved() {
assertThat(onChainCostService.getCloseCostsForChannelId(CHANNEL_ID)).isEmpty();
@@ -268,10 +284,16 @@ class OnChainCostServiceTest {
assertThat(onChainCostService.getCloseCostsForChannel(FORCE_CLOSED_CHANNEL_OPEN_REMOTE))
.contains(Coins.NONE);
}
}
private void mockCloseTransaction(ClosedChannel channel) {
lenient().when(transactionService.getTransaction(channel.getCloseTransactionHash()))
.thenReturn(Optional.of(TRANSACTION_2));
}
private void mockOpenTransaction(LocalChannel channel) {
lenient().when(channelService.getAllLocalChannels()).thenReturn(Stream.of(channel));
lenient().when(transactionService.getTransaction(channel.getChannelPoint().getTransactionHash()))
.thenReturn(Optional.of(TRANSACTION));
}
private void mockCloseTransaction(ClosedChannel channel) {
lenient().when(transactionService.getTransaction(channel.getCloseTransactionHash()))
.thenReturn(Optional.of(TRANSACTION_2));
}
}

View File

@@ -0,0 +1,9 @@
package de.cotto.lndmanagej.model;
public record OnChainCosts(Coins open, Coins close) {
public static final OnChainCosts NONE = new OnChainCosts(Coins.NONE, Coins.NONE);
public OnChainCosts add(OnChainCosts other) {
return new OnChainCosts(open.add(other.open), close.add(other.close));
}
}

View File

@@ -0,0 +1,31 @@
package de.cotto.lndmanagej.model;
import org.junit.jupiter.api.Test;
import static de.cotto.lndmanagej.model.OnChainCostsFixtures.ON_CHAIN_COSTS;
import static org.assertj.core.api.Assertions.assertThat;
class OnChainCostsTest {
@Test
void add_none_to_none() {
assertThat(OnChainCosts.NONE.add(OnChainCosts.NONE)).isEqualTo(OnChainCosts.NONE);
}
@Test
void add() {
assertThat(ON_CHAIN_COSTS.add(ON_CHAIN_COSTS)).isEqualTo(new OnChainCosts(
Coins.ofSatoshis(2000),
Coins.ofSatoshis(4000)
));
}
@Test
void open() {
assertThat(ON_CHAIN_COSTS.open()).isEqualTo(Coins.ofSatoshis(1000));
}
@Test
void close() {
assertThat(ON_CHAIN_COSTS.close()).isEqualTo(Coins.ofSatoshis(2000));
}
}

View File

@@ -0,0 +1,21 @@
package de.cotto.lndmanagej.model;
import org.junit.jupiter.api.Test;
import static de.cotto.lndmanagej.model.ChannelPointFixtures.TRANSACTION_HASH;
import static de.cotto.lndmanagej.model.ResolutionFixtures.RESOLUTION;
import static de.cotto.lndmanagej.model.ResolutionFixtures.RESOLUTION_2;
import static org.assertj.core.api.Assertions.assertThat;
class ResolutionTest {
@Test
void sweepTransaction_empty() {
assertThat(RESOLUTION_2.sweepTransaction()).isEmpty();
}
@Test
void sweepTransaction() {
assertThat(RESOLUTION.sweepTransaction()).contains(TRANSACTION_HASH);
}
}

View File

@@ -9,6 +9,7 @@ import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2;
import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_3;
import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_4;
import static de.cotto.lndmanagej.model.ChannelPointFixtures.CHANNEL_POINT;
import static de.cotto.lndmanagej.model.ChannelPointFixtures.CHANNEL_POINT_3;
import static de.cotto.lndmanagej.model.OpenInitiator.LOCAL;
import static de.cotto.lndmanagej.model.OpenInitiator.REMOTE;
import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY;
@@ -95,7 +96,7 @@ public class LocalOpenChannelFixtures {
);
public static final LocalOpenChannel LOCAL_OPEN_CHANNEL_TO_NODE_3 =
new LocalOpenChannel(
new ChannelCoreInformation(CHANNEL_ID_4, CHANNEL_POINT, CAPACITY_2),
new ChannelCoreInformation(CHANNEL_ID_4, CHANNEL_POINT_3, CAPACITY_2),
PUBKEY,
PUBKEY_3,
BALANCE_INFORMATION,

View File

@@ -0,0 +1,8 @@
package de.cotto.lndmanagej.model;
public class OnChainCostsFixtures {
public static final OnChainCosts ON_CHAIN_COSTS = new OnChainCosts(
Coins.ofSatoshis(1000),
Coins.ofSatoshis(2000)
);
}

View File

@@ -3,6 +3,7 @@ package de.cotto.lndmanagej.controller;
import de.cotto.lndmanagej.model.ChannelIdResolver;
import de.cotto.lndmanagej.model.Coins;
import de.cotto.lndmanagej.model.FeeReport;
import de.cotto.lndmanagej.model.OnChainCosts;
import de.cotto.lndmanagej.service.BalanceService;
import de.cotto.lndmanagej.service.ChannelService;
import de.cotto.lndmanagej.service.FeeService;
@@ -32,6 +33,7 @@ import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.TOTAL_RECEIVED_
import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.TOTAL_SENT;
import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.TOTAL_SENT_2;
import static de.cotto.lndmanagej.model.NodeFixtures.ALIAS_2;
import static de.cotto.lndmanagej.model.OnChainCostsFixtures.ON_CHAIN_COSTS;
import static de.cotto.lndmanagej.model.PolicyFixtures.POLICIES;
import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2;
import static org.hamcrest.core.Is.is;
@@ -124,8 +126,7 @@ class ChannelControllerIT {
when(policyService.getPolicies(CHANNEL_ID)).thenReturn(POLICIES);
when(nodeService.getAlias(PUBKEY_2)).thenReturn(ALIAS_2);
when(channelService.getLocalChannel(CHANNEL_ID)).thenReturn(Optional.of(LOCAL_OPEN_CHANNEL_PRIVATE));
when(onChainCostService.getOpenCostsForChannelId(CHANNEL_ID)).thenReturn(Optional.of(Coins.ofSatoshis(1000)));
when(onChainCostService.getCloseCostsForChannelId(CHANNEL_ID)).thenReturn(Optional.of(Coins.ofSatoshis(2000)));
when(onChainCostService.getOnChainCostsForChannelId(CHANNEL_ID)).thenReturn(ON_CHAIN_COSTS);
when(offChainCostService.getRebalanceSourceCostsForChannel(CHANNEL_ID)).thenReturn(Coins.ofMilliSatoshis(1));
when(offChainCostService.getRebalanceTargetCostsForChannel(CHANNEL_ID)).thenReturn(Coins.ofMilliSatoshis(2));
when(balanceService.getBalanceInformation(CHANNEL_ID)).thenReturn(Optional.of(BALANCE_INFORMATION_2));
@@ -170,6 +171,7 @@ class ChannelControllerIT {
void getChannelDetails_closed_channel() throws Exception {
when(channelService.getLocalChannel(CHANNEL_ID)).thenReturn(Optional.of(CLOSED_CHANNEL));
when(feeService.getFeeReportForChannel(CHANNEL_ID)).thenReturn(new FeeReport(Coins.NONE, Coins.NONE));
when(onChainCostService.getOnChainCostsForChannelId(CHANNEL_ID)).thenReturn(OnChainCosts.NONE);
when(offChainCostService.getRebalanceSourceCostsForChannel(CHANNEL_ID)).thenReturn(Coins.ofMilliSatoshis(1));
when(offChainCostService.getRebalanceTargetCostsForChannel(CHANNEL_ID)).thenReturn(Coins.ofMilliSatoshis(2));
mockMvc.perform(get(DETAILS_PREFIX))

View File

@@ -30,6 +30,7 @@ import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHAN
import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHANNEL_2;
import static de.cotto.lndmanagej.model.LocalOpenChannelFixtures.LOCAL_OPEN_CHANNEL_3;
import static de.cotto.lndmanagej.model.NodeFixtures.ALIAS_2;
import static de.cotto.lndmanagej.model.OnChainCostsFixtures.ON_CHAIN_COSTS;
import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2;
import static de.cotto.lndmanagej.model.WaitingCloseChannelFixtures.WAITING_CLOSE_CHANNEL;
import static org.hamcrest.core.Is.is;
@@ -82,8 +83,7 @@ class NodeControllerIT {
when(channelService.getClosedChannelsWith(PUBKEY_2)).thenReturn(Set.of(CLOSED_CHANNEL, CLOSED_CHANNEL_3));
when(channelService.getWaitingCloseChannelsWith(PUBKEY_2)).thenReturn(Set.of(WAITING_CLOSE_CHANNEL));
when(channelService.getForceClosingChannelsWith(PUBKEY_2)).thenReturn(Set.of(FORCE_CLOSING_CHANNEL_2));
when(onChainCostService.getOpenCostsWith(PUBKEY_2)).thenReturn(Coins.ofSatoshis(123));
when(onChainCostService.getCloseCostsWith(PUBKEY_2)).thenReturn(Coins.ofSatoshis(456));
when(onChainCostService.getOnChainCostsForPeer(PUBKEY_2)).thenReturn(ON_CHAIN_COSTS);
when(offChainCostService.getRebalanceSourceCostsForPeer(PUBKEY_2)).thenReturn(Coins.ofMilliSatoshis(1));
when(offChainCostService.getRebalanceTargetCostsForPeer(PUBKEY_2)).thenReturn(Coins.ofMilliSatoshis(2));
when(balanceService.getBalanceInformationForPeer(PUBKEY_2)).thenReturn(BALANCE_INFORMATION);
@@ -99,8 +99,6 @@ class NodeControllerIT {
.andExpect(jsonPath("$.closedChannels", is(closedChannelIds)))
.andExpect(jsonPath("$.waitingCloseChannels", is(waitingCloseChannelIds)))
.andExpect(jsonPath("$.pendingForceClosingChannels", is(forceClosingChannelIds)))
.andExpect(jsonPath("$.onChainCosts.openCosts", is("123")))
.andExpect(jsonPath("$.onChainCosts.closeCosts", is("456")))
.andExpect(jsonPath("$.offChainCosts.rebalanceSource", is("1")))
.andExpect(jsonPath("$.offChainCosts.rebalanceTarget", is("2")))
.andExpect(jsonPath("$.balance.localBalance", is("1000")))
@@ -111,6 +109,8 @@ class NodeControllerIT {
.andExpect(jsonPath("$.balance.remoteAvailable", is("113")))
.andExpect(jsonPath("$.feeReport.earned", is("1234")))
.andExpect(jsonPath("$.feeReport.sourced", is("567")))
.andExpect(jsonPath("$.onChainCosts.openCosts", is("1000")))
.andExpect(jsonPath("$.onChainCosts.closeCosts", is("2000")))
.andExpect(jsonPath("$.online", is(true)));
}

View File

@@ -12,6 +12,7 @@ import org.springframework.test.web.servlet.MockMvc;
import java.util.Optional;
import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID;
import static de.cotto.lndmanagej.model.OnChainCostsFixtures.ON_CHAIN_COSTS;
import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY;
import static org.hamcrest.core.Is.is;
import static org.mockito.Mockito.when;
@@ -37,11 +38,10 @@ class OnChainCostsControllerIT {
@Test
void on_chain_costs_for_peer() throws Exception {
when(onChainCostService.getOpenCostsWith(PUBKEY)).thenReturn(Coins.ofSatoshis(123));
when(onChainCostService.getCloseCostsWith(PUBKEY)).thenReturn(Coins.ofSatoshis(456));
when(onChainCostService.getOnChainCostsForPeer(PUBKEY)).thenReturn(ON_CHAIN_COSTS);
mockMvc.perform(get(PEER_PREFIX + "/on-chain-costs"))
.andExpect(jsonPath("$.openCosts", is("123")))
.andExpect(jsonPath("$.closeCosts", is("456")));
.andExpect(jsonPath("$.openCosts", is("1000")))
.andExpect(jsonPath("$.closeCosts", is("2000")));
}
@Test

View File

@@ -8,12 +8,12 @@ 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.OffChainCostsDto;
import de.cotto.lndmanagej.controller.dto.OnChainCostsDto;
import de.cotto.lndmanagej.controller.dto.PoliciesDto;
import de.cotto.lndmanagej.model.BalanceInformation;
import de.cotto.lndmanagej.model.ChannelId;
import de.cotto.lndmanagej.model.ClosedChannel;
import de.cotto.lndmanagej.model.Coins;
import de.cotto.lndmanagej.model.FeeReport;
import de.cotto.lndmanagej.model.LocalChannel;
import de.cotto.lndmanagej.model.OpenCloseStatus;
import de.cotto.lndmanagej.model.Policies;
@@ -88,11 +88,11 @@ public class ChannelController {
localChannel,
remoteAlias,
getBalanceInformation(channelId),
getOnChainCosts(channelId),
onChainCostService.getOnChainCostsForChannelId(channelId),
getOffChainCosts(channelId),
getPoliciesForChannel(localChannel),
getCloseDetailsForChannel(localChannel),
getFeeReportDto(localChannel.getId())
getFeeReportFromService(localChannel.getId())
);
}
@@ -124,11 +124,11 @@ public class ChannelController {
@Timed
@GetMapping("/fee-report")
public FeeReportDto getFeeReport(@PathVariable ChannelId channelId) {
return getFeeReportDto(channelId);
return FeeReportDto.createFromModel(getFeeReportFromService(channelId));
}
private FeeReportDto getFeeReportDto(ChannelId channelId) {
return FeeReportDto.createFromModel(feeService.getFeeReportForChannel(channelId));
private FeeReport getFeeReportFromService(ChannelId channelId) {
return feeService.getFeeReportForChannel(channelId);
}
private PoliciesDto getPoliciesForChannel(@Nullable LocalChannel channel) {
@@ -144,12 +144,6 @@ public class ChannelController {
.orElse(BalanceInformation.EMPTY);
}
private OnChainCostsDto getOnChainCosts(ChannelId channelId) {
Coins openCosts = onChainCostService.getOpenCostsForChannelId(channelId).orElse(Coins.NONE);
Coins closeCosts = onChainCostService.getCloseCostsForChannelId(channelId).orElse(Coins.NONE);
return new OnChainCostsDto(openCosts, closeCosts);
}
private OffChainCostsDto getOffChainCosts(ChannelId channelId) {
Coins rebalanceSource = offChainCostService.getRebalanceSourceCostsForChannel(channelId);
Coins rebalanceTarget = offChainCostService.getRebalanceTargetCostsForChannel(channelId);

View File

@@ -13,6 +13,7 @@ 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.OnChainCosts;
import de.cotto.lndmanagej.model.Pubkey;
import de.cotto.lndmanagej.service.BalanceService;
import de.cotto.lndmanagej.service.ChannelService;
@@ -66,8 +67,7 @@ public class NodeController {
@GetMapping("/details")
public NodeDetailsDto getDetails(@PathVariable Pubkey pubkey) {
Node node = nodeService.getNode(pubkey);
Coins openCosts = onChainCostService.getOpenCostsWith(pubkey);
Coins closeCosts = onChainCostService.getCloseCostsWith(pubkey);
OnChainCosts onChainCosts = onChainCostService.getOnChainCostsForPeer(pubkey);
Coins rebalanceSourceCosts = offChainCostService.getRebalanceSourceCostsForPeer(pubkey);
Coins rebalanceTargetCosts = offChainCostService.getRebalanceTargetCostsForPeer(pubkey);
BalanceInformation balanceInformation = balanceService.getBalanceInformationForPeer(pubkey);
@@ -78,7 +78,7 @@ public class NodeController {
toSortedList(channelService.getClosedChannelsWith(pubkey)),
toSortedList(channelService.getWaitingCloseChannelsWith(pubkey)),
toSortedList(channelService.getForceClosingChannelsWith(pubkey)),
new OnChainCostsDto(openCosts, closeCosts),
OnChainCostsDto.createFromModel(onChainCosts),
new OffChainCostsDto(rebalanceSourceCosts, rebalanceTargetCosts),
BalanceInformationDto.createFromModel(balanceInformation),
node.online(),

View File

@@ -23,10 +23,7 @@ public class OnChainCostsController {
@Timed
@GetMapping("/node/{pubkey}/on-chain-costs")
public OnChainCostsDto getCostsForPeer(@PathVariable Pubkey pubkey) {
return new OnChainCostsDto(
onChainCostService.getOpenCostsWith(pubkey),
onChainCostService.getCloseCostsWith(pubkey)
);
return OnChainCostsDto.createFromModel(onChainCostService.getOnChainCostsForPeer(pubkey));
}
@Timed

View File

@@ -2,7 +2,9 @@ package de.cotto.lndmanagej.controller.dto;
import de.cotto.lndmanagej.model.BalanceInformation;
import de.cotto.lndmanagej.model.ChannelPoint;
import de.cotto.lndmanagej.model.FeeReport;
import de.cotto.lndmanagej.model.LocalChannel;
import de.cotto.lndmanagej.model.OnChainCosts;
import de.cotto.lndmanagej.model.OpenInitiator;
import de.cotto.lndmanagej.model.Pubkey;
@@ -30,10 +32,10 @@ public record ChannelDetailsDto(
ChannelDto channelDto,
String remoteAlias,
BalanceInformation balanceInformation,
OnChainCostsDto onChainCosts,
OnChainCosts onChainCosts,
OffChainCostsDto offChainCosts,
PoliciesDto policies,
FeeReportDto feeReport
FeeReport feeReport
) {
this(
channelDto.channelIdShort(),
@@ -49,11 +51,11 @@ public record ChannelDetailsDto(
channelDto.totalReceived(),
channelDto.status(),
BalanceInformationDto.createFromModel(balanceInformation),
onChainCosts,
OnChainCostsDto.createFromModel(onChainCosts),
offChainCosts,
policies,
channelDto.closeDetails(),
feeReport
FeeReportDto.createFromModel(feeReport)
);
}
@@ -61,11 +63,11 @@ public record ChannelDetailsDto(
LocalChannel localChannel,
String remoteAlias,
BalanceInformation balanceInformation,
OnChainCostsDto onChainCosts,
OnChainCosts onChainCosts,
OffChainCostsDto offChainCosts,
PoliciesDto policies,
ClosedChannelDetailsDto closeDetails,
FeeReportDto feeReport
FeeReport feeReport
) {
this(
new ChannelDto(localChannel, closeDetails),

View File

@@ -1,9 +1,11 @@
package de.cotto.lndmanagej.controller.dto;
import de.cotto.lndmanagej.model.Coins;
import de.cotto.lndmanagej.model.OnChainCosts;
public record OnChainCostsDto(String openCosts, String closeCosts) {
public OnChainCostsDto(Coins openCosts, Coins closeCosts) {
this(String.valueOf(openCosts.satoshis()), String.valueOf(closeCosts.satoshis()));
public static OnChainCostsDto createFromModel(OnChainCosts onChainCosts) {
long openSatoshi = onChainCosts.open().satoshis();
long closeSatoshi = onChainCosts.close().satoshis();
return new OnChainCostsDto(String.valueOf(openSatoshi), String.valueOf(closeSatoshi));
}
}

View File

@@ -6,13 +6,13 @@ 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.OffChainCostsDto;
import de.cotto.lndmanagej.controller.dto.OnChainCostsDto;
import de.cotto.lndmanagej.controller.dto.PoliciesDto;
import de.cotto.lndmanagej.model.BalanceInformation;
import de.cotto.lndmanagej.model.CloseInitiator;
import de.cotto.lndmanagej.model.Coins;
import de.cotto.lndmanagej.model.FeeReport;
import de.cotto.lndmanagej.model.LocalChannel;
import de.cotto.lndmanagej.model.OnChainCosts;
import de.cotto.lndmanagej.service.BalanceService;
import de.cotto.lndmanagej.service.ChannelService;
import de.cotto.lndmanagej.service.FeeService;
@@ -49,13 +49,12 @@ class ChannelControllerTest {
private static final Coins CLOSE_COSTS = Coins.ofSatoshis(2);
private static final Coins SOURCE_COSTS = Coins.ofSatoshis(3);
private static final Coins TARGET_COSTS = Coins.ofSatoshis(4);
private static final OnChainCostsDto ON_CHAIN_COSTS = new OnChainCostsDto(OPEN_COSTS, CLOSE_COSTS);
private static final OnChainCosts ON_CHAIN_COSTS = new OnChainCosts(OPEN_COSTS, CLOSE_COSTS);
private static final OffChainCostsDto OFF_CHAIN_COSTS = new OffChainCostsDto(SOURCE_COSTS, TARGET_COSTS);
private static final PoliciesDto FEE_CONFIGURATION_DTO = PoliciesDto.createFromModel(POLICIES);
private static final ClosedChannelDetailsDto CLOSED_CHANNEL_DETAILS_DTO =
new ClosedChannelDetailsDto(CloseInitiator.REMOTE, 987_654);
private static final FeeReport FEE_REPORT = new FeeReport(Coins.ofMilliSatoshis(1_234), Coins.ofMilliSatoshis(567));
private static final FeeReportDto FEE_REPORT_DTO = FeeReportDto.createFromModel(FEE_REPORT);
@InjectMocks
private ChannelController channelController;
@@ -83,8 +82,7 @@ class ChannelControllerTest {
@BeforeEach
void setUp() {
lenient().when(onChainCostService.getOpenCostsForChannelId(CHANNEL_ID)).thenReturn(Optional.of(OPEN_COSTS));
lenient().when(onChainCostService.getCloseCostsForChannelId(CHANNEL_ID)).thenReturn(Optional.of(CLOSE_COSTS));
lenient().when(onChainCostService.getOnChainCostsForChannelId(CHANNEL_ID)).thenReturn(ON_CHAIN_COSTS);
lenient().when(offChainCostService.getRebalanceSourceCostsForChannel(CHANNEL_ID)).thenReturn(SOURCE_COSTS);
lenient().when(offChainCostService.getRebalanceTargetCostsForChannel(CHANNEL_ID)).thenReturn(TARGET_COSTS);
lenient().when(policyService.getPolicies(CHANNEL_ID)).thenReturn(POLICIES);
@@ -127,7 +125,7 @@ class ChannelControllerTest {
OFF_CHAIN_COSTS,
FEE_CONFIGURATION_DTO,
ClosedChannelDetailsDto.UNKNOWN,
FEE_REPORT_DTO
FEE_REPORT
);
when(nodeService.getAlias(PUBKEY_2)).thenReturn(ALIAS_2);
when(channelService.getLocalChannel(CHANNEL_ID)).thenReturn(Optional.of(LOCAL_OPEN_CHANNEL));
@@ -147,7 +145,7 @@ class ChannelControllerTest {
OFF_CHAIN_COSTS,
FEE_CONFIGURATION_DTO,
ClosedChannelDetailsDto.UNKNOWN,
FEE_REPORT_DTO
FEE_REPORT
);
when(nodeService.getAlias(PUBKEY_2)).thenReturn(ALIAS_2);
when(channelService.getLocalChannel(CHANNEL_ID)).thenReturn(Optional.of(LOCAL_OPEN_CHANNEL_PRIVATE));
@@ -214,7 +212,7 @@ class ChannelControllerTest {
@Test
void getFeeReport() {
when(feeService.getFeeReportForChannel(CHANNEL_ID)).thenReturn(FEE_REPORT);
assertThat(channelController.getFeeReport(CHANNEL_ID)).isEqualTo(FEE_REPORT_DTO);
assertThat(channelController.getFeeReport(CHANNEL_ID)).isEqualTo(FeeReportDto.createFromModel(FEE_REPORT));
}
private ChannelDetailsDto mockForChannelWithoutPolicies(
@@ -233,7 +231,7 @@ class ChannelControllerTest {
OFF_CHAIN_COSTS,
PoliciesDto.EMPTY,
new ClosedChannelDetailsDto(closeInitiator, closeHeight),
FEE_REPORT_DTO
FEE_REPORT
);
}
}

View File

@@ -10,6 +10,7 @@ import de.cotto.lndmanagej.model.BalanceInformation;
import de.cotto.lndmanagej.model.Coins;
import de.cotto.lndmanagej.model.FeeReport;
import de.cotto.lndmanagej.model.Node;
import de.cotto.lndmanagej.model.OnChainCosts;
import de.cotto.lndmanagej.model.Pubkey;
import de.cotto.lndmanagej.service.BalanceService;
import de.cotto.lndmanagej.service.ChannelService;
@@ -81,8 +82,7 @@ class NodeControllerTest {
@Test
void getNodeDetails_no_channels() {
when(onChainCostService.getOpenCostsWith(any())).thenReturn(Coins.NONE);
when(onChainCostService.getCloseCostsWith(any())).thenReturn(Coins.NONE);
when(onChainCostService.getOnChainCostsForPeer(any())).thenReturn(OnChainCosts.NONE);
when(offChainCostService.getRebalanceSourceCostsForPeer(any())).thenReturn(Coins.NONE);
when(offChainCostService.getRebalanceTargetCostsForPeer(any())).thenReturn(Coins.NONE);
when(balanceService.getBalanceInformationForPeer(any(Pubkey.class))).thenReturn(BalanceInformation.EMPTY);
@@ -94,7 +94,7 @@ class NodeControllerTest {
List.of(),
List.of(),
List.of(),
new OnChainCostsDto(Coins.NONE, Coins.NONE),
OnChainCostsDto.createFromModel(OnChainCosts.NONE),
new OffChainCostsDto(Coins.NONE, Coins.NONE),
BalanceInformationDto.createFromModel(BalanceInformation.EMPTY),
true,
@@ -116,12 +116,13 @@ class NodeControllerTest {
when(channelService.getForceClosingChannelsWith(PUBKEY_2)).thenReturn(
Set.of(FORCE_CLOSING_CHANNEL, FORCE_CLOSING_CHANNEL_2, FORCE_CLOSING_CHANNEL_3)
);
Coins openCosts = Coins.ofSatoshis(123);
Coins closeCosts = Coins.ofSatoshis(456);
OnChainCosts onChainCosts = new OnChainCosts(
Coins.ofSatoshis(123),
Coins.ofSatoshis(456)
);
Coins rebalanceSourceCosts = Coins.ofMilliSatoshis(1);
Coins rebalanceTargetCosts = Coins.ofMilliSatoshis(2);
when(onChainCostService.getOpenCostsWith(PUBKEY_2)).thenReturn(openCosts);
when(onChainCostService.getCloseCostsWith(PUBKEY_2)).thenReturn(closeCosts);
when(onChainCostService.getOnChainCostsForPeer(PUBKEY_2)).thenReturn(onChainCosts);
when(offChainCostService.getRebalanceSourceCostsForPeer(PUBKEY_2)).thenReturn(rebalanceSourceCosts);
when(offChainCostService.getRebalanceTargetCostsForPeer(PUBKEY_2)).thenReturn(rebalanceTargetCosts);
when(balanceService.getBalanceInformationForPeer(PUBKEY_2)).thenReturn(BALANCE_INFORMATION);
@@ -133,7 +134,7 @@ class NodeControllerTest {
List.of(CHANNEL_ID_2, CHANNEL_ID_3),
List.of(CHANNEL_ID, CHANNEL_ID_2),
List.of(CHANNEL_ID, CHANNEL_ID_2, CHANNEL_ID_3),
new OnChainCostsDto(openCosts, closeCosts),
OnChainCostsDto.createFromModel(onChainCosts),
new OffChainCostsDto(rebalanceSourceCosts, rebalanceTargetCosts),
BalanceInformationDto.createFromModel(BALANCE_INFORMATION),
false,

View File

@@ -12,6 +12,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Optional;
import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID;
import static de.cotto.lndmanagej.model.OnChainCostsFixtures.ON_CHAIN_COSTS;
import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -27,12 +28,9 @@ class OnChainCostsControllerTest {
@Test
void getCostsForPeer() {
Coins openCosts = Coins.ofSatoshis(123);
Coins closeCosts = Coins.ofSatoshis(456);
when(onChainCostService.getOpenCostsWith(PUBKEY)).thenReturn(openCosts);
when(onChainCostService.getCloseCostsWith(PUBKEY)).thenReturn(closeCosts);
OnChainCostsDto expected = new OnChainCostsDto(openCosts, closeCosts);
assertThat(onChainCostsController.getCostsForPeer(PUBKEY)).isEqualTo(expected);
when(onChainCostService.getOnChainCostsForPeer(PUBKEY)).thenReturn(ON_CHAIN_COSTS);
assertThat(onChainCostsController.getCostsForPeer(PUBKEY))
.isEqualTo(OnChainCostsDto.createFromModel(ON_CHAIN_COSTS));
}
@Test

View File

@@ -2,6 +2,8 @@ package de.cotto.lndmanagej.controller.dto;
import de.cotto.lndmanagej.model.ChannelStatus;
import de.cotto.lndmanagej.model.Coins;
import de.cotto.lndmanagej.model.FeeReport;
import de.cotto.lndmanagej.model.OnChainCosts;
import de.cotto.lndmanagej.model.OpenInitiator;
import org.junit.jupiter.api.Test;
@@ -18,12 +20,12 @@ import static org.assertj.core.api.Assertions.assertThat;
class ChannelDetailsDtoTest {
private static final OnChainCostsDto ON_CHAIN_COSTS = new OnChainCostsDto(Coins.ofSatoshis(1), Coins.ofSatoshis(2));
private static final OnChainCosts ON_CHAIN_COSTS = new OnChainCosts(Coins.ofSatoshis(1), Coins.ofSatoshis(2));
private static final OffChainCostsDto OFF_CHAIN_COSTS =
new OffChainCostsDto(Coins.ofSatoshis(3), Coins.ofSatoshis(4));
private static final ClosedChannelDetailsDto CLOSE_DETAILS = new ClosedChannelDetailsDto("abc", 123);
private static final FeeReportDto FEE_REPORT =
new FeeReportDto(Coins.ofMilliSatoshis(1234), Coins.ofMilliSatoshis(567));
private static final FeeReport FEE_REPORT =
new FeeReport(Coins.ofMilliSatoshis(1234), Coins.ofMilliSatoshis(567));
private static final ChannelDetailsDto CHANNEL_DETAILS_DTO = new ChannelDetailsDto(
CLOSED_CHANNEL,
ALIAS,
@@ -109,12 +111,12 @@ class ChannelDetailsDtoTest {
@Test
void feeReport() {
assertThat(CHANNEL_DETAILS_DTO.feeReport()).isEqualTo(FEE_REPORT);
assertThat(CHANNEL_DETAILS_DTO.feeReport()).isEqualTo(FeeReportDto.createFromModel(FEE_REPORT));
}
@Test
void onChainCosts() {
assertThat(CHANNEL_DETAILS_DTO.onChainCosts()).isEqualTo(ON_CHAIN_COSTS);
assertThat(CHANNEL_DETAILS_DTO.onChainCosts()).isEqualTo(OnChainCostsDto.createFromModel(ON_CHAIN_COSTS));
}
@Test

View File

@@ -0,0 +1,14 @@
package de.cotto.lndmanagej.controller.dto;
import org.junit.jupiter.api.Test;
import static de.cotto.lndmanagej.model.OnChainCostsFixtures.ON_CHAIN_COSTS;
import static org.assertj.core.api.Assertions.assertThat;
class OnChainCostsDtoTest {
@Test
void createFromModel() {
assertThat(OnChainCostsDto.createFromModel(ON_CHAIN_COSTS))
.isEqualTo(new OnChainCostsDto("1000", "2000"));
}
}