From 04382383677d2dd9ef2bef9328da04dc3ccc5df5 Mon Sep 17 00:00:00 2001 From: Carsten Otto Date: Tue, 3 May 2022 20:57:48 +0200 Subject: [PATCH] add in flight at start of payment attempt --- .../service/LiquidityInformationUpdater.java | 26 ++++++++++++++----- .../LiquidityInformationUpdaterTest.java | 8 ++++++ .../grpc/middleware/HtlcAttemptListener.java | 2 +- .../middleware/PaymentListenerUpdater.java | 16 ++++++++---- .../grpc/middleware/SendToRouteListener.java | 3 ++- .../middleware/HtlcAttemptListenerTest.java | 4 +-- .../PaymentListenerUpdaterTest.java | 19 +++++++++----- .../middleware/SendToRouteListenerTest.java | 14 ++++++---- .../lndmanagej/model/PaymentListener.java | 2 ++ 9 files changed, 67 insertions(+), 27 deletions(-) diff --git a/backend/src/main/java/de/cotto/lndmanagej/service/LiquidityInformationUpdater.java b/backend/src/main/java/de/cotto/lndmanagej/service/LiquidityInformationUpdater.java index 7da094db..13f1a1b6 100644 --- a/backend/src/main/java/de/cotto/lndmanagej/service/LiquidityInformationUpdater.java +++ b/backend/src/main/java/de/cotto/lndmanagej/service/LiquidityInformationUpdater.java @@ -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 paymentAttemptHops) { + addInFlight(paymentAttemptHops); + } + @Override public void success(HexString preimage, List paymentAttemptHops) { removeInFlight(paymentAttemptHops); @@ -68,14 +67,27 @@ public class LiquidityInformationUpdater implements PaymentListener { } } + private void addInFlight(List paymentAttemptHops) { + updateInFlight(paymentAttemptHops, false); + } + private void removeInFlight(List paymentAttemptHops) { + updateInFlight(paymentAttemptHops, true); + } + + private void updateInFlight(List 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; } } diff --git a/backend/src/test/java/de/cotto/lndmanagej/service/LiquidityInformationUpdaterTest.java b/backend/src/test/java/de/cotto/lndmanagej/service/LiquidityInformationUpdaterTest.java index 7ea61d0b..bb2887cd 100644 --- a/backend/src/test/java/de/cotto/lndmanagej/service/LiquidityInformationUpdaterTest.java +++ b/backend/src/test/java/de/cotto/lndmanagej/service/LiquidityInformationUpdaterTest.java @@ -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"); diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/middleware/HtlcAttemptListener.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/middleware/HtlcAttemptListener.java index 6a1aceaf..e4c5cbae 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/middleware/HtlcAttemptListener.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/middleware/HtlcAttemptListener.java @@ -15,7 +15,7 @@ public class HtlcAttemptListener extends AbstractResponseListener { @Override public void acceptResponse(HTLCAttempt response, long requestId) { - paymentListenerUpdater.update(response.getPreimage(), response.getRoute(), response.getFailure()); + paymentListenerUpdater.forResponse(response.getPreimage(), response.getRoute(), response.getFailure()); } } diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/middleware/PaymentListenerUpdater.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/middleware/PaymentListenerUpdater.java index 32059438..281876f2 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/middleware/PaymentListenerUpdater.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/middleware/PaymentListenerUpdater.java @@ -24,10 +24,13 @@ public class PaymentListenerUpdater { this.paymentListeners = paymentListeners; } - public void update(ByteString preimage, Route route, Failure failure) { - List paymentAttemptHops = route.getHopsList().stream() - .map(this::toPaymentAttemptHop) - .toList(); + public void forNewPaymentAttempt(Route route) { + List hops = toPaymentAttemptHops(route); + paymentListeners.forEach(paymentListener -> paymentListener.forNewPaymentAttempt(hops)); + } + + public void forResponse(ByteString preimage, Route route, Failure failure) { + List 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 toPaymentAttemptHops(Route route) { + return route.getHopsList().stream().map(this::toPaymentAttemptHop).toList(); + } + private PaymentAttemptHop toPaymentAttemptHop(Hop hop) { Optional optionalPubkey; if (hop.getPubKey().isBlank()) { @@ -61,5 +68,4 @@ public class PaymentListenerUpdater { optionalPubkey ); } - } diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/middleware/SendToRouteListener.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/middleware/SendToRouteListener.java index d124fdb3..1eb19fe6 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/middleware/SendToRouteListener.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/middleware/SendToRouteListener.java @@ -25,6 +25,7 @@ public class SendToRouteListener extends RequestResponseListener 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 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 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 paymentAttemptHops = getPaymentHops(); verify(paymentListener).failure(paymentAttemptHops, FailureCode.TEMPORARY_CHANNEL_FAILURE, 3); diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/middleware/SendToRouteListenerTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/middleware/SendToRouteListenerTest.java index 01650da5..33f3fb67 100644 --- a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/middleware/SendToRouteListenerTest.java +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/middleware/SendToRouteListenerTest.java @@ -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) { diff --git a/model/src/main/java/de/cotto/lndmanagej/model/PaymentListener.java b/model/src/main/java/de/cotto/lndmanagej/model/PaymentListener.java index 6408b21f..eccd815b 100644 --- a/model/src/main/java/de/cotto/lndmanagej/model/PaymentListener.java +++ b/model/src/main/java/de/cotto/lndmanagej/model/PaymentListener.java @@ -3,6 +3,8 @@ package de.cotto.lndmanagej.model; import java.util.List; public interface PaymentListener { + void forNewPaymentAttempt(List route); + void success(HexString preimage, List paymentAttemptHops); void failure(List paymentAttemptHops, FailureCode failureCode, int failureSourceIndex);