mirror of
https://github.com/aljazceru/lnd-manageJ.git
synced 2026-01-20 22:44:31 +01:00
add in flight at start of payment attempt
This commit is contained in:
@@ -17,12 +17,6 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static de.cotto.lndmanagej.model.FailureCode.CHANNEL_DISABLED;
|
||||
import static de.cotto.lndmanagej.model.FailureCode.INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS;
|
||||
import static de.cotto.lndmanagej.model.FailureCode.MPP_TIMEOUT;
|
||||
import static de.cotto.lndmanagej.model.FailureCode.TEMPORARY_CHANNEL_FAILURE;
|
||||
import static de.cotto.lndmanagej.model.FailureCode.UNKNOWN_NEXT_PEER;
|
||||
|
||||
@Service
|
||||
public class LiquidityInformationUpdater implements PaymentListener {
|
||||
private final GrpcGetInfo grpcGetInfo;
|
||||
@@ -40,6 +34,11 @@ public class LiquidityInformationUpdater implements PaymentListener {
|
||||
this.liquidityBoundsService = liquidityBoundsService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forNewPaymentAttempt(List<PaymentAttemptHop> paymentAttemptHops) {
|
||||
addInFlight(paymentAttemptHops);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void success(HexString preimage, List<PaymentAttemptHop> paymentAttemptHops) {
|
||||
removeInFlight(paymentAttemptHops);
|
||||
@@ -68,14 +67,27 @@ public class LiquidityInformationUpdater implements PaymentListener {
|
||||
}
|
||||
}
|
||||
|
||||
private void addInFlight(List<PaymentAttemptHop> paymentAttemptHops) {
|
||||
updateInFlight(paymentAttemptHops, false);
|
||||
}
|
||||
|
||||
private void removeInFlight(List<PaymentAttemptHop> paymentAttemptHops) {
|
||||
updateInFlight(paymentAttemptHops, true);
|
||||
}
|
||||
|
||||
private void updateInFlight(List<PaymentAttemptHop> paymentAttemptHops, boolean negate) {
|
||||
Pubkey startNode = grpcGetInfo.getPubkey();
|
||||
for (PaymentAttemptHop hop : paymentAttemptHops) {
|
||||
Pubkey endNode = getOtherNode(hop, startNode).orElse(null);
|
||||
if (endNode == null) {
|
||||
return;
|
||||
}
|
||||
liquidityBoundsService.markAsInFlight(startNode, endNode, hop.amount().negate());
|
||||
Coins amount = hop.amount();
|
||||
if (negate) {
|
||||
liquidityBoundsService.markAsInFlight(startNode, endNode, amount.negate());
|
||||
} else {
|
||||
liquidityBoundsService.markAsInFlight(startNode, endNode, amount);
|
||||
}
|
||||
startNode = endNode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,14 @@ class LiquidityInformationUpdaterTest {
|
||||
assertThat(liquidityInformationUpdater).isInstanceOf(PaymentListener.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void forNewPayment_adds_in_flight() {
|
||||
liquidityInformationUpdater.forNewPaymentAttempt(hopsWithChannelIds);
|
||||
verify(liquidityBoundsService).markAsInFlight(PUBKEY, PUBKEY_2, Coins.ofSatoshis(100));
|
||||
verify(liquidityBoundsService).markAsInFlight(PUBKEY_2, PUBKEY_3, Coins.ofSatoshis(90));
|
||||
verify(liquidityBoundsService).markAsInFlight(PUBKEY_3, PUBKEY_4, Coins.ofSatoshis(80));
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Success {
|
||||
private static final HexString PREIMAGE = new HexString("00");
|
||||
|
||||
@@ -15,7 +15,7 @@ public class HtlcAttemptListener extends AbstractResponseListener<HTLCAttempt> {
|
||||
|
||||
@Override
|
||||
public void acceptResponse(HTLCAttempt response, long requestId) {
|
||||
paymentListenerUpdater.update(response.getPreimage(), response.getRoute(), response.getFailure());
|
||||
paymentListenerUpdater.forResponse(response.getPreimage(), response.getRoute(), response.getFailure());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,10 +24,13 @@ public class PaymentListenerUpdater {
|
||||
this.paymentListeners = paymentListeners;
|
||||
}
|
||||
|
||||
public void update(ByteString preimage, Route route, Failure failure) {
|
||||
List<PaymentAttemptHop> paymentAttemptHops = route.getHopsList().stream()
|
||||
.map(this::toPaymentAttemptHop)
|
||||
.toList();
|
||||
public void forNewPaymentAttempt(Route route) {
|
||||
List<PaymentAttemptHop> hops = toPaymentAttemptHops(route);
|
||||
paymentListeners.forEach(paymentListener -> paymentListener.forNewPaymentAttempt(hops));
|
||||
}
|
||||
|
||||
public void forResponse(ByteString preimage, Route route, Failure failure) {
|
||||
List<PaymentAttemptHop> paymentAttemptHops = toPaymentAttemptHops(route);
|
||||
if (preimage.isEmpty()) {
|
||||
FailureCode failureCode = FailureCode.getFor(failure.getCodeValue());
|
||||
int failureSourceIndex = failure.getFailureSourceIndex();
|
||||
@@ -41,6 +44,10 @@ public class PaymentListenerUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
private List<PaymentAttemptHop> toPaymentAttemptHops(Route route) {
|
||||
return route.getHopsList().stream().map(this::toPaymentAttemptHop).toList();
|
||||
}
|
||||
|
||||
private PaymentAttemptHop toPaymentAttemptHop(Hop hop) {
|
||||
Optional<Pubkey> optionalPubkey;
|
||||
if (hop.getPubKey().isBlank()) {
|
||||
@@ -61,5 +68,4 @@ public class PaymentListenerUpdater {
|
||||
optionalPubkey
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ public class SendToRouteListener extends RequestResponseListener<SendToRouteRequ
|
||||
|
||||
@Override
|
||||
public void acceptRequest(SendToRouteRequest request, long requestId) {
|
||||
paymentListenerUpdater.forNewPaymentAttempt(request.getRoute());
|
||||
requests.put(requestId, request);
|
||||
}
|
||||
|
||||
@@ -34,6 +35,6 @@ public class SendToRouteListener extends RequestResponseListener<SendToRouteRequ
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
paymentListenerUpdater.update(response.getPreimage(), request.getRoute(), response.getFailure());
|
||||
paymentListenerUpdater.forResponse(response.getPreimage(), request.getRoute(), response.getFailure());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class HtlcAttemptListenerTest {
|
||||
.setRoute(route)
|
||||
.build();
|
||||
htlcAttemptListener.acceptResponse(response, REQUEST_ID);
|
||||
verify(paymentListenerUpdater).update(PREIMAGE_BYTESTRING, route, NO_FAILURE);
|
||||
verify(paymentListenerUpdater).forResponse(PREIMAGE_BYTESTRING, route, NO_FAILURE);
|
||||
verifyNoMoreInteractions(paymentListenerUpdater);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class HtlcAttemptListenerTest {
|
||||
.setRoute(route)
|
||||
.build();
|
||||
htlcAttemptListener.acceptResponse(response, REQUEST_ID);
|
||||
verify(paymentListenerUpdater).update(NO_PREIMAGE_BYTESTRING, route, failure);
|
||||
verify(paymentListenerUpdater).forResponse(NO_PREIMAGE_BYTESTRING, route, failure);
|
||||
verifyNoMoreInteractions(paymentListenerUpdater);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,8 +46,15 @@ class PaymentListenerUpdaterTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void notifiesListenerForSuccess() {
|
||||
paymentListenerUpdater.update(PREIMAGE_BYTESTRING, getRoute(), NO_FAILURE);
|
||||
void notifies_listener_for_new_payment_attempt() {
|
||||
paymentListenerUpdater.forNewPaymentAttempt(getRoute());
|
||||
verify(paymentListener).forNewPaymentAttempt(getPaymentHops());
|
||||
verifyNoMoreInteractions(paymentListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
void notifies_listener_for_success() {
|
||||
paymentListenerUpdater.forResponse(PREIMAGE_BYTESTRING, getRoute(), NO_FAILURE);
|
||||
|
||||
verify(paymentListener).success(PREIMAGE, getPaymentHops());
|
||||
verifyNoMoreInteractions(paymentListener);
|
||||
@@ -56,7 +63,7 @@ class PaymentListenerUpdaterTest {
|
||||
@Test
|
||||
void hop_without_pubkey() {
|
||||
Route route = Route.newBuilder().addHops(hop(CHANNEL_ID_2, 456_000)).build();
|
||||
paymentListenerUpdater.update(PREIMAGE_BYTESTRING, route, NO_FAILURE);
|
||||
paymentListenerUpdater.forResponse(PREIMAGE_BYTESTRING, route, NO_FAILURE);
|
||||
List<PaymentAttemptHop> paymentAttemptHops = List.of(
|
||||
new PaymentAttemptHop(Optional.of(CHANNEL_ID_2), Coins.ofSatoshis(456), Optional.empty())
|
||||
);
|
||||
@@ -67,7 +74,7 @@ class PaymentListenerUpdaterTest {
|
||||
@Test
|
||||
void hop_without_channel_id_and_pubkey() {
|
||||
Route route = Route.newBuilder().addHops(hop(100)).build();
|
||||
paymentListenerUpdater.update(PREIMAGE_BYTESTRING, route, NO_FAILURE);
|
||||
paymentListenerUpdater.forResponse(PREIMAGE_BYTESTRING, route, NO_FAILURE);
|
||||
List<PaymentAttemptHop> paymentAttemptHops = List.of(
|
||||
new PaymentAttemptHop(Optional.empty(), Coins.ofMilliSatoshis(100), Optional.empty())
|
||||
);
|
||||
@@ -78,7 +85,7 @@ class PaymentListenerUpdaterTest {
|
||||
@Test
|
||||
void hop_without_channel_id() {
|
||||
Route route = Route.newBuilder().addHops(hop(123_000, PUBKEY)).build();
|
||||
paymentListenerUpdater.update(PREIMAGE_BYTESTRING, route, NO_FAILURE);
|
||||
paymentListenerUpdater.forResponse(PREIMAGE_BYTESTRING, route, NO_FAILURE);
|
||||
List<PaymentAttemptHop> paymentAttemptHops = List.of(
|
||||
new PaymentAttemptHop(Optional.empty(), Coins.ofSatoshis(123), Optional.of(PUBKEY))
|
||||
);
|
||||
@@ -92,7 +99,7 @@ class PaymentListenerUpdaterTest {
|
||||
.setCode(Failure.FailureCode.TEMPORARY_CHANNEL_FAILURE)
|
||||
.setFailureSourceIndex(3)
|
||||
.build();
|
||||
paymentListenerUpdater.update(NO_PREIMAGE_BYTESTRING, getRoute(), failure);
|
||||
paymentListenerUpdater.forResponse(NO_PREIMAGE_BYTESTRING, getRoute(), failure);
|
||||
|
||||
List<PaymentAttemptHop> paymentAttemptHops = getPaymentHops();
|
||||
verify(paymentListener).failure(paymentAttemptHops, FailureCode.TEMPORARY_CHANNEL_FAILURE, 3);
|
||||
|
||||
@@ -22,7 +22,6 @@ import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SendToRouteListenerTest {
|
||||
@@ -49,6 +48,13 @@ class SendToRouteListenerTest {
|
||||
assertThat(sendToRouteListener.getResponseType()).isEqualTo("routerrpc.SendToRouteResponse");
|
||||
}
|
||||
|
||||
@Test
|
||||
void notifies_for_request_before_response_arrives() {
|
||||
Route route = getRoute();
|
||||
acceptRequestWithRoute(route);
|
||||
verify(paymentListenerUpdater).forNewPaymentAttempt(route);
|
||||
}
|
||||
|
||||
@Test
|
||||
void responseWithoutRequest() {
|
||||
sendToRouteListener.acceptResponse(SendToRouteResponse.newBuilder().build(), REQUEST_ID);
|
||||
@@ -63,8 +69,7 @@ class SendToRouteListenerTest {
|
||||
.setPreimage(PREIMAGE_BYTESTRING)
|
||||
.build();
|
||||
sendToRouteListener.acceptResponse(response, REQUEST_ID);
|
||||
verify(paymentListenerUpdater).update(PREIMAGE_BYTESTRING, route, NO_FAILURE);
|
||||
verifyNoMoreInteractions(paymentListenerUpdater);
|
||||
verify(paymentListenerUpdater).forResponse(PREIMAGE_BYTESTRING, route, NO_FAILURE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,8 +85,7 @@ class SendToRouteListenerTest {
|
||||
.setFailure(failure)
|
||||
.build();
|
||||
sendToRouteListener.acceptResponse(response, REQUEST_ID);
|
||||
verify(paymentListenerUpdater).update(NO_PREIMAGE_BYTESTRING, route, failure);
|
||||
verifyNoMoreInteractions(paymentListenerUpdater);
|
||||
verify(paymentListenerUpdater).forResponse(NO_PREIMAGE_BYTESTRING, route, failure);
|
||||
}
|
||||
|
||||
private void acceptRequestWithRoute(Route route) {
|
||||
|
||||
@@ -3,6 +3,8 @@ package de.cotto.lndmanagej.model;
|
||||
import java.util.List;
|
||||
|
||||
public interface PaymentListener {
|
||||
void forNewPaymentAttempt(List<PaymentAttemptHop> route);
|
||||
|
||||
void success(HexString preimage, List<PaymentAttemptHop> paymentAttemptHops);
|
||||
|
||||
void failure(List<PaymentAttemptHop> paymentAttemptHops, FailureCode failureCode, int failureSourceIndex);
|
||||
|
||||
Reference in New Issue
Block a user