mirror of
https://github.com/aljazceru/lnd-manageJ.git
synced 2026-01-27 09:54:51 +01:00
use PaymentInformation, provide isFailure/isSettled
This commit is contained in:
@@ -7,6 +7,7 @@ import de.cotto.lndmanagej.model.FailureCode;
|
||||
import de.cotto.lndmanagej.model.HexString;
|
||||
import de.cotto.lndmanagej.model.PaymentAttemptHop;
|
||||
import de.cotto.lndmanagej.model.Route;
|
||||
import de.cotto.lndmanagej.pickhardtpayments.model.PaymentInformation;
|
||||
import de.cotto.lndmanagej.service.LiquidityInformationUpdater;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -15,11 +16,12 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Component
|
||||
public class MultiPathPaymentObserver {
|
||||
private final LiquidityInformationUpdater liquidityInformationUpdater;
|
||||
private final Map<HexString, Coins> inFlight = new ConcurrentHashMap<>();
|
||||
private final Map<HexString, PaymentInformation> map = new ConcurrentHashMap<>();
|
||||
|
||||
public MultiPathPaymentObserver(LiquidityInformationUpdater liquidityInformationUpdater) {
|
||||
this.liquidityInformationUpdater = liquidityInformationUpdater;
|
||||
@@ -30,11 +32,33 @@ public class MultiPathPaymentObserver {
|
||||
}
|
||||
|
||||
public Coins getInFlight(HexString paymentHash) {
|
||||
return inFlight.getOrDefault(paymentHash, Coins.NONE);
|
||||
return get(paymentHash).inFlight();
|
||||
}
|
||||
|
||||
public boolean isSettled(HexString paymentHash) {
|
||||
return get(paymentHash).settled();
|
||||
}
|
||||
|
||||
public boolean isFailed(HexString paymentHash) {
|
||||
return get(paymentHash).failed();
|
||||
}
|
||||
|
||||
private void addInFlight(HexString paymentHash, Coins amount) {
|
||||
inFlight.compute(paymentHash, (key, value) -> value == null ? amount : amount.add(value));
|
||||
update(paymentHash, value -> value.withAdditionalInFlight(amount));
|
||||
}
|
||||
|
||||
private PaymentInformation get(HexString paymentHash) {
|
||||
return map.getOrDefault(paymentHash, PaymentInformation.DEFAULT);
|
||||
}
|
||||
|
||||
private void update(HexString paymentHash, Function<PaymentInformation, PaymentInformation> updater) {
|
||||
map.compute(paymentHash, (key, value) -> {
|
||||
PaymentInformation newValue = updater.apply(value == null ? PaymentInformation.DEFAULT : value);
|
||||
if (PaymentInformation.DEFAULT.equals(newValue)) {
|
||||
return null;
|
||||
}
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
|
||||
private List<PaymentAttemptHop> topPaymentAttemptHops(Route route) {
|
||||
@@ -71,6 +95,13 @@ public class MultiPathPaymentObserver {
|
||||
|
||||
@Override
|
||||
public void onValue(HexString preimage, FailureCode failureCode) {
|
||||
if (preimage.equals(HexString.EMPTY)) {
|
||||
if (failureCode.isErrorFromFinalNode()) {
|
||||
update(paymentHash, PaymentInformation::withIsFailed);
|
||||
}
|
||||
} else {
|
||||
update(paymentHash, PaymentInformation::withIsSettled);
|
||||
}
|
||||
addInFlight(paymentHash, route.getAmount().negate());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class MultiPathPaymentObserverTest {
|
||||
private static final HexString PAYMENT_HASH = DECODED_PAYMENT_REQUEST.paymentHash();
|
||||
@InjectMocks
|
||||
private MultiPathPaymentObserver multiPathPaymentObserver;
|
||||
|
||||
@@ -35,25 +36,21 @@ class MultiPathPaymentObserverTest {
|
||||
|
||||
@Test
|
||||
void cancels_in_flight_on_error() {
|
||||
HexString paymentHash = DECODED_PAYMENT_REQUEST.paymentHash();
|
||||
SendToRouteObserver sendToRouteObserver =
|
||||
multiPathPaymentObserver.getFor(ROUTE, paymentHash);
|
||||
SendToRouteObserver sendToRouteObserver = multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
sendToRouteObserver.onError(new NullPointerException());
|
||||
assertThat(multiPathPaymentObserver.getInFlight(paymentHash)).isEqualTo(Coins.NONE);
|
||||
assertThat(multiPathPaymentObserver.getInFlight(PAYMENT_HASH)).isEqualTo(Coins.NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void cancels_in_flight_using_liquidity_information_updater_on_error() {
|
||||
SendToRouteObserver sendToRouteObserver =
|
||||
multiPathPaymentObserver.getFor(ROUTE, DECODED_PAYMENT_REQUEST.paymentHash());
|
||||
SendToRouteObserver sendToRouteObserver = multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
sendToRouteObserver.onError(new NullPointerException());
|
||||
verify(liquidityInformationUpdater).removeInFlight(hops());
|
||||
}
|
||||
|
||||
@Test
|
||||
void accepts_value() {
|
||||
SendToRouteObserver sendToRouteObserver =
|
||||
multiPathPaymentObserver.getFor(ROUTE, DECODED_PAYMENT_REQUEST.paymentHash());
|
||||
SendToRouteObserver sendToRouteObserver = multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
assertThatCode(
|
||||
() -> sendToRouteObserver.onValue(HexString.EMPTY, FailureCode.UNKNOWN_FAILURE)
|
||||
).doesNotThrowAnyException();
|
||||
@@ -61,41 +58,96 @@ class MultiPathPaymentObserverTest {
|
||||
|
||||
@Test
|
||||
void inFlight_initially_zero() {
|
||||
assertThat(multiPathPaymentObserver.getInFlight(DECODED_PAYMENT_REQUEST.paymentHash()))
|
||||
.isEqualTo(Coins.NONE);
|
||||
assertThat(multiPathPaymentObserver.getInFlight(PAYMENT_HASH)).isEqualTo(Coins.NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void inFlight_initialized_with_route_amount() {
|
||||
HexString paymentHash = DECODED_PAYMENT_REQUEST.paymentHash();
|
||||
multiPathPaymentObserver.getFor(ROUTE, paymentHash);
|
||||
assertThat(multiPathPaymentObserver.getInFlight(paymentHash)).isEqualTo(ROUTE.getAmount());
|
||||
multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
assertThat(multiPathPaymentObserver.getInFlight(PAYMENT_HASH)).isEqualTo(ROUTE.getAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void inFlight_reset_on_success() {
|
||||
HexString paymentHash = DECODED_PAYMENT_REQUEST.paymentHash();
|
||||
SendToRouteObserver observer = multiPathPaymentObserver.getFor(ROUTE, paymentHash);
|
||||
SendToRouteObserver observer = multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
observer.onValue(new HexString("AABBCC"), FailureCode.UNKNOWN_FAILURE);
|
||||
assertThat(multiPathPaymentObserver.getInFlight(paymentHash)).isEqualTo(Coins.NONE);
|
||||
assertThat(multiPathPaymentObserver.getInFlight(PAYMENT_HASH)).isEqualTo(Coins.NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isSettled_success() {
|
||||
SendToRouteObserver observer = multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
observer.onValue(new HexString("AABBCC"), FailureCode.UNKNOWN_FAILURE);
|
||||
assertThat(multiPathPaymentObserver.isSettled(PAYMENT_HASH)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isSettled_failure() {
|
||||
SendToRouteObserver observer = multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
observer.onValue(HexString.EMPTY, FailureCode.FEE_INSUFFICIENT);
|
||||
assertThat(multiPathPaymentObserver.isSettled(PAYMENT_HASH)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isSettled_error() {
|
||||
SendToRouteObserver observer = multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
observer.onError(new NullPointerException());
|
||||
assertThat(multiPathPaymentObserver.isSettled(PAYMENT_HASH)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isFailed_success() {
|
||||
SendToRouteObserver observer = multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
observer.onValue(new HexString("AABBCC"), FailureCode.UNKNOWN_FAILURE);
|
||||
assertThat(multiPathPaymentObserver.isFailed(PAYMENT_HASH)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isFailed_failure_from_final_node() {
|
||||
SendToRouteObserver observer = multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
observer.onValue(HexString.EMPTY, FailureCode.FINAL_INCORRECT_CLTV_EXPIRY);
|
||||
assertThat(multiPathPaymentObserver.isFailed(PAYMENT_HASH)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isFailed_failure_from_other_node() {
|
||||
SendToRouteObserver observer = multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
observer.onValue(HexString.EMPTY, FailureCode.PERMANENT_CHANNEL_FAILURE);
|
||||
assertThat(multiPathPaymentObserver.isFailed(PAYMENT_HASH)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isFailed_error() {
|
||||
SendToRouteObserver observer = multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
observer.onError(new NullPointerException());
|
||||
assertThat(multiPathPaymentObserver.isFailed(PAYMENT_HASH)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isSettled_initial() {
|
||||
multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
assertThat(multiPathPaymentObserver.isSettled(PAYMENT_HASH)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isSettled_unknown() {
|
||||
assertThat(multiPathPaymentObserver.isSettled(PAYMENT_HASH)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void inFlight_reset_on_failure() {
|
||||
HexString paymentHash = DECODED_PAYMENT_REQUEST.paymentHash();
|
||||
SendToRouteObserver observer = multiPathPaymentObserver.getFor(ROUTE, paymentHash);
|
||||
observer.onValue(HexString.EMPTY, FailureCode.PERMANENT_CHANNEL_FAILURE);
|
||||
assertThat(multiPathPaymentObserver.getInFlight(paymentHash)).isEqualTo(Coins.NONE);
|
||||
SendToRouteObserver observer = multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
observer.onValue(HexString.EMPTY, FailureCode.FEE_INSUFFICIENT);
|
||||
assertThat(multiPathPaymentObserver.getInFlight(PAYMENT_HASH)).isEqualTo(Coins.NONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void inFlight_updated_with_several_routes() {
|
||||
HexString paymentHash = DECODED_PAYMENT_REQUEST.paymentHash();
|
||||
multiPathPaymentObserver.getFor(ROUTE, paymentHash);
|
||||
multiPathPaymentObserver.getFor(ROUTE, PAYMENT_HASH);
|
||||
multiPathPaymentObserver.getFor(ROUTE_2, HexString.EMPTY);
|
||||
multiPathPaymentObserver.getFor(ROUTE_3, paymentHash);
|
||||
multiPathPaymentObserver.getFor(ROUTE_3, PAYMENT_HASH);
|
||||
Coins expectedAmount = ROUTE.getAmount().add(ROUTE_3.getAmount());
|
||||
assertThat(multiPathPaymentObserver.getInFlight(paymentHash)).isEqualTo(expectedAmount);
|
||||
assertThat(multiPathPaymentObserver.getInFlight(PAYMENT_HASH)).isEqualTo(expectedAmount);
|
||||
}
|
||||
|
||||
private List<PaymentAttemptHop> hops() {
|
||||
|
||||
Reference in New Issue
Block a user