mirror of
https://github.com/aljazceru/lnd-manageJ.git
synced 2026-01-20 14:34:24 +01:00
top-up: impose stricter fee limit for all hops (but not the ones from the top-up peer)
This commit is contained in:
@@ -9,8 +9,10 @@ import de.cotto.lndmanagej.model.DirectedChannelEdge;
|
||||
import de.cotto.lndmanagej.model.Edge;
|
||||
import de.cotto.lndmanagej.model.EdgeWithLiquidityInformation;
|
||||
import de.cotto.lndmanagej.model.LocalChannel;
|
||||
import de.cotto.lndmanagej.model.Policy;
|
||||
import de.cotto.lndmanagej.model.Pubkey;
|
||||
import de.cotto.lndmanagej.pickhardtpayments.model.EdgesWithLiquidityInformation;
|
||||
import de.cotto.lndmanagej.pickhardtpayments.model.PaymentOptions;
|
||||
import de.cotto.lndmanagej.service.BalanceService;
|
||||
import de.cotto.lndmanagej.service.ChannelService;
|
||||
import de.cotto.lndmanagej.service.LiquidityBoundsService;
|
||||
@@ -27,8 +29,6 @@ import java.util.function.Function;
|
||||
|
||||
@Component
|
||||
public class EdgeComputation {
|
||||
private static final long NO_FEE_RATE_LIMIT = -1L;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final GrpcGraph grpcGraph;
|
||||
@@ -57,11 +57,7 @@ public class EdgeComputation {
|
||||
this.routeHintService = routeHintService;
|
||||
}
|
||||
|
||||
public EdgesWithLiquidityInformation getEdges() {
|
||||
return getEdges(NO_FEE_RATE_LIMIT);
|
||||
}
|
||||
|
||||
public EdgesWithLiquidityInformation getEdges(long feeRateLimit) {
|
||||
public EdgesWithLiquidityInformation getEdges(PaymentOptions paymentOptions) {
|
||||
Set<DirectedChannelEdge> channelEdges = grpcGraph.getChannelEdges().orElse(null);
|
||||
if (channelEdges == null) {
|
||||
logger.warn("Unable to get graph");
|
||||
@@ -71,7 +67,7 @@ public class EdgeComputation {
|
||||
Pubkey ownPubkey = grpcGetInfo.getPubkey();
|
||||
Set<DirectedChannelEdge> edgesFromPaymentHints = routeHintService.getEdgesFromPaymentHints();
|
||||
for (DirectedChannelEdge channelEdge : Sets.union(channelEdges, edgesFromPaymentHints)) {
|
||||
if (shouldIgnore(channelEdge, feeRateLimit)) {
|
||||
if (shouldIgnore(channelEdge, paymentOptions, ownPubkey)) {
|
||||
continue;
|
||||
}
|
||||
ChannelId channelId = channelEdge.channelId();
|
||||
@@ -111,14 +107,31 @@ public class EdgeComputation {
|
||||
return withFeeReserve.subtract(Coins.ofSatoshis(1_000)).maximum(Coins.NONE);
|
||||
}
|
||||
|
||||
private boolean shouldIgnore(DirectedChannelEdge channelEdge, long feeRateLimit) {
|
||||
if (channelEdge.policy().disabled()) {
|
||||
private boolean shouldIgnore(DirectedChannelEdge channelEdge, PaymentOptions paymentOptions, Pubkey pubkey) {
|
||||
Policy policy = channelEdge.policy();
|
||||
if (policy.disabled()) {
|
||||
return true;
|
||||
}
|
||||
if (feeRateLimit == NO_FEE_RATE_LIMIT) {
|
||||
Long feeRateLimit = paymentOptions.feeRateLimit().orElse(null);
|
||||
if (feeRateLimit == null) {
|
||||
return false;
|
||||
}
|
||||
return channelEdge.policy().feeRate() >= feeRateLimit;
|
||||
long feeRate = policy.feeRate();
|
||||
if (feeRate >= feeRateLimit) {
|
||||
return true;
|
||||
}
|
||||
if (isIncomingEdge(channelEdge, pubkey)) {
|
||||
return false;
|
||||
}
|
||||
Long feeRateLimitFirstHops = paymentOptions.feeRateLimitExceptIncomingHops().orElse(null);
|
||||
if (feeRateLimitFirstHops == null) {
|
||||
return false;
|
||||
}
|
||||
return feeRate >= feeRateLimitFirstHops;
|
||||
}
|
||||
|
||||
private boolean isIncomingEdge(DirectedChannelEdge channelEdge, Pubkey ownPubkey) {
|
||||
return ownPubkey.equals(channelEdge.target());
|
||||
}
|
||||
|
||||
private Optional<Coins> getKnownLiquidity(Edge edge, Pubkey ownPubKey) {
|
||||
|
||||
@@ -38,7 +38,7 @@ public class FlowComputation {
|
||||
int quantization = getQuantization(amount);
|
||||
int piecewiseLinearApproximations = configurationService.getIntegerValue(PIECEWISE_LINEAR_APPROXIMATIONS)
|
||||
.orElse(DEFAULT_PIECEWISE_LINEAR_APPROXIMATIONS);
|
||||
EdgesWithLiquidityInformation edges = getEdges(paymentOptions);
|
||||
EdgesWithLiquidityInformation edges = edgeComputation.getEdges(paymentOptions);
|
||||
MinCostFlowSolver minCostFlowSolver = new MinCostFlowSolver(
|
||||
edges,
|
||||
Map.of(source, amount),
|
||||
@@ -52,16 +52,6 @@ public class FlowComputation {
|
||||
return minCostFlowSolver.solve();
|
||||
}
|
||||
|
||||
private EdgesWithLiquidityInformation getEdges(PaymentOptions paymentOptions) {
|
||||
EdgesWithLiquidityInformation edges;
|
||||
if (paymentOptions.feeRateLimit().isPresent()) {
|
||||
edges = edgeComputation.getEdges(paymentOptions.feeRateLimit().get());
|
||||
} else {
|
||||
edges = edgeComputation.getEdges();
|
||||
}
|
||||
return edges;
|
||||
}
|
||||
|
||||
private int getQuantization(Coins amount) {
|
||||
int quantization = configurationService.getIntegerValue(QUANTIZATION)
|
||||
.orElse(DEFAULT_QUANTIZATION);
|
||||
|
||||
@@ -88,7 +88,7 @@ public class TopUpService {
|
||||
String alias = nodeService.getAlias(pubkey);
|
||||
return PaymentStatus.createFailure("Unable to create payment request (%s, %s)".formatted(pubkey, alias));
|
||||
}
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(ourFeeRate, pubkey);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(ourFeeRate, peerFeeRate, pubkey);
|
||||
return multiPathPaymentSender.payPaymentRequest(paymentRequest, paymentOptions);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,20 +7,27 @@ import java.util.Optional;
|
||||
public record PaymentOptions(
|
||||
int feeRateWeight,
|
||||
Optional<Long> feeRateLimit,
|
||||
Optional<Long> feeRateLimitExceptIncomingHops,
|
||||
boolean ignoreFeesForOwnChannels,
|
||||
Optional<Pubkey> peer
|
||||
) {
|
||||
public static final PaymentOptions DEFAULT_PAYMENT_OPTIONS = forFeeRateWeight(0);
|
||||
|
||||
public static PaymentOptions forFeeRateWeight(int feeRateWeight) {
|
||||
return new PaymentOptions(feeRateWeight, Optional.empty(), true, Optional.empty());
|
||||
return new PaymentOptions(feeRateWeight, Optional.empty(), Optional.empty(), true, Optional.empty());
|
||||
}
|
||||
|
||||
public static PaymentOptions forFeeRateLimit(long feeRateLimit) {
|
||||
return new PaymentOptions(0, Optional.of(feeRateLimit), true, Optional.empty());
|
||||
return new PaymentOptions(0, Optional.of(feeRateLimit), Optional.of(feeRateLimit), true, Optional.empty());
|
||||
}
|
||||
|
||||
public static PaymentOptions forTopUp(long feeRateLimit, Pubkey peer) {
|
||||
return new PaymentOptions(5, Optional.of(feeRateLimit), false, Optional.of(peer));
|
||||
public static PaymentOptions forTopUp(long ourFeeRate, long peerFeeRate, Pubkey peer) {
|
||||
return new PaymentOptions(
|
||||
5,
|
||||
Optional.of(ourFeeRate),
|
||||
Optional.of(ourFeeRate - peerFeeRate),
|
||||
false,
|
||||
Optional.of(peer)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import de.cotto.lndmanagej.model.EdgeWithLiquidityInformation;
|
||||
import de.cotto.lndmanagej.model.Node;
|
||||
import de.cotto.lndmanagej.model.Policy;
|
||||
import de.cotto.lndmanagej.model.Pubkey;
|
||||
import de.cotto.lndmanagej.pickhardtpayments.model.PaymentOptions;
|
||||
import de.cotto.lndmanagej.service.BalanceService;
|
||||
import de.cotto.lndmanagej.service.ChannelService;
|
||||
import de.cotto.lndmanagej.service.LiquidityBoundsService;
|
||||
@@ -37,6 +38,7 @@ import static de.cotto.lndmanagej.model.PolicyFixtures.POLICY_WITH_BASE_FEE;
|
||||
import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY;
|
||||
import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_2;
|
||||
import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_4;
|
||||
import static de.cotto.lndmanagej.pickhardtpayments.model.PaymentOptions.DEFAULT_PAYMENT_OPTIONS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
@@ -79,23 +81,24 @@ class EdgeComputationTest {
|
||||
|
||||
@Test
|
||||
void no_graph() {
|
||||
assertThat(edgeComputation.getEdges().edges()).isEmpty();
|
||||
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS).edges()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void does_not_add_edge_for_disabled_channel() {
|
||||
DirectedChannelEdge edge = new DirectedChannelEdge(CHANNEL_ID, CAPACITY, PUBKEY, PUBKEY_2, POLICY_DISABLED);
|
||||
when(grpcGraph.getChannelEdges()).thenReturn(Optional.of(Set.of(edge)));
|
||||
assertThat(edgeComputation.getEdges().edges()).isEmpty();
|
||||
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS).edges()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void does_not_add_edge_with_fee_rate_at_or_above_limit() {
|
||||
int feeRateLimit = 199;
|
||||
Policy policyExpensive = new Policy(200, Coins.NONE, true, 40, Coins.ofSatoshis(0));
|
||||
PaymentOptions paymentOptions = PaymentOptions.forFeeRateLimit(feeRateLimit);
|
||||
Policy policyExpensive = policy(200);
|
||||
// needs to be excluded to avoid sending top-up payments in a tiny loop: S-X-S
|
||||
Policy policyAtLimit = new Policy(199, Coins.NONE, true, 40, Coins.ofSatoshis(0));
|
||||
Policy policyOk = new Policy(198, Coins.NONE, true, 40, Coins.ofSatoshis(0));
|
||||
Policy policyAtLimit = policy(199);
|
||||
Policy policyOk = policy(198);
|
||||
DirectedChannelEdge edgeExpensive =
|
||||
new DirectedChannelEdge(CHANNEL_ID, CAPACITY, PUBKEY, PUBKEY_2, policyExpensive);
|
||||
DirectedChannelEdge edgeAtLimit =
|
||||
@@ -103,8 +106,33 @@ class EdgeComputationTest {
|
||||
DirectedChannelEdge edgeOk =
|
||||
new DirectedChannelEdge(CHANNEL_ID_3, CAPACITY, PUBKEY, PUBKEY_2, policyOk);
|
||||
when(grpcGraph.getChannelEdges()).thenReturn(Optional.of(Set.of(edgeExpensive, edgeAtLimit, edgeOk)));
|
||||
assertThat(edgeComputation.getEdges(feeRateLimit).edges().stream().map(EdgeWithLiquidityInformation::channelId))
|
||||
.containsExactly(CHANNEL_ID_3);
|
||||
assertThat(
|
||||
edgeComputation.getEdges(paymentOptions).edges().stream().map(EdgeWithLiquidityInformation::channelId)
|
||||
).containsExactly(CHANNEL_ID_3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void does_not_add_first_hop_edge_with_fee_rate_at_or_above_limit_for_first_hops() {
|
||||
Pubkey ownPubkey = EDGE.startNode();
|
||||
Pubkey topUpPeer = PUBKEY_4;
|
||||
int feeRateLimit = 200;
|
||||
int feeRateLimitForFirstHops = 100;
|
||||
|
||||
when(grpcGetInfo.getPubkey()).thenReturn(ownPubkey);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(feeRateLimit, feeRateLimitForFirstHops, topUpPeer);
|
||||
Policy lastHopPolicy = policy(199);
|
||||
Policy firstHopPolicyExpensive = policy(100);
|
||||
Policy firstHopPolicyOk = policy(99);
|
||||
DirectedChannelEdge lastHop =
|
||||
new DirectedChannelEdge(CHANNEL_ID, CAPACITY, topUpPeer, ownPubkey, lastHopPolicy);
|
||||
DirectedChannelEdge firstHopExpensive =
|
||||
new DirectedChannelEdge(CHANNEL_ID_2, CAPACITY, ownPubkey, PUBKEY_2, firstHopPolicyExpensive);
|
||||
DirectedChannelEdge firstHopOk =
|
||||
new DirectedChannelEdge(CHANNEL_ID_3, CAPACITY, ownPubkey, PUBKEY_2, firstHopPolicyOk);
|
||||
when(grpcGraph.getChannelEdges()).thenReturn(Optional.of(Set.of(lastHop, firstHopExpensive, firstHopOk)));
|
||||
assertThat(
|
||||
edgeComputation.getEdges(paymentOptions).edges().stream().map(EdgeWithLiquidityInformation::channelId)
|
||||
).containsExactlyInAnyOrder(CHANNEL_ID, CHANNEL_ID_3);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -112,7 +140,7 @@ class EdgeComputationTest {
|
||||
DirectedChannelEdge edge =
|
||||
new DirectedChannelEdge(CHANNEL_ID, CAPACITY, PUBKEY, PUBKEY_2, POLICY_WITH_BASE_FEE);
|
||||
when(grpcGraph.getChannelEdges()).thenReturn(Optional.of(Set.of(edge)));
|
||||
assertThat(edgeComputation.getEdges().edges()).isNotEmpty();
|
||||
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS).edges()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,7 +152,7 @@ class EdgeComputationTest {
|
||||
Coins availableKnownLiquidity = getAvailableKnownLiquidity(knownLiquidity);
|
||||
when(balanceService.getAvailableLocalBalance(EDGE.channelId())).thenReturn(knownLiquidity);
|
||||
|
||||
assertThat(edgeComputation.getEdges().edges())
|
||||
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS).edges())
|
||||
.contains(EdgeWithLiquidityInformation.forKnownLiquidity(EDGE, availableKnownLiquidity));
|
||||
}
|
||||
|
||||
@@ -143,7 +171,7 @@ class EdgeComputationTest {
|
||||
edge.policy()
|
||||
)
|
||||
));
|
||||
assertThat(edgeComputation.getEdges().edges())
|
||||
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS).edges())
|
||||
.contains(EdgeWithLiquidityInformation.forKnownLiquidity(edge, fiftyCoins));
|
||||
}
|
||||
|
||||
@@ -152,7 +180,7 @@ class EdgeComputationTest {
|
||||
mockEdge();
|
||||
when(grpcGetInfo.getPubkey()).thenReturn(EDGE.startNode());
|
||||
|
||||
assertThat(edgeComputation.getEdges().edges())
|
||||
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS).edges())
|
||||
.contains(EdgeWithLiquidityInformation.forKnownLiquidity(EDGE, Coins.NONE));
|
||||
}
|
||||
|
||||
@@ -162,7 +190,7 @@ class EdgeComputationTest {
|
||||
mockOfflinePeer();
|
||||
when(grpcGetInfo.getPubkey()).thenReturn(EDGE.startNode());
|
||||
|
||||
assertThat(edgeComputation.getEdges().edges())
|
||||
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS).edges())
|
||||
.contains(EdgeWithLiquidityInformation.forKnownLiquidity(EDGE, Coins.NONE));
|
||||
verify(balanceService, never()).getAvailableLocalBalance(EDGE.channelId());
|
||||
}
|
||||
@@ -176,7 +204,7 @@ class EdgeComputationTest {
|
||||
Coins availableKnownLiquidity = getAvailableKnownLiquidity(knownLiquidity);
|
||||
when(balanceService.getAvailableRemoteBalance(EDGE.channelId())).thenReturn(knownLiquidity);
|
||||
|
||||
assertThat(edgeComputation.getEdges().edges())
|
||||
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS).edges())
|
||||
.contains(EdgeWithLiquidityInformation.forKnownLiquidity(EDGE, availableKnownLiquidity));
|
||||
}
|
||||
|
||||
@@ -185,7 +213,7 @@ class EdgeComputationTest {
|
||||
mockEdge();
|
||||
when(grpcGetInfo.getPubkey()).thenReturn(EDGE.endNode());
|
||||
|
||||
assertThat(edgeComputation.getEdges().edges())
|
||||
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS).edges())
|
||||
.contains(EdgeWithLiquidityInformation.forKnownLiquidity(EDGE, Coins.NONE));
|
||||
}
|
||||
|
||||
@@ -195,7 +223,7 @@ class EdgeComputationTest {
|
||||
mockOfflinePeer();
|
||||
when(grpcGetInfo.getPubkey()).thenReturn(EDGE.endNode());
|
||||
|
||||
assertThat(edgeComputation.getEdges().edges())
|
||||
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS).edges())
|
||||
.contains(EdgeWithLiquidityInformation.forKnownLiquidity(EDGE, Coins.NONE));
|
||||
verify(balanceService, never()).getAvailableLocalBalance(EDGE.channelId());
|
||||
}
|
||||
@@ -211,14 +239,14 @@ class EdgeComputationTest {
|
||||
mockEdge();
|
||||
Coins upperBound = Coins.ofSatoshis(100);
|
||||
mockUpperBound(upperBound);
|
||||
assertThat(edgeComputation.getEdges().edges())
|
||||
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS).edges())
|
||||
.contains(EdgeWithLiquidityInformation.forUpperBound(EDGE, upperBound));
|
||||
}
|
||||
|
||||
@Test
|
||||
void default_if_no_liquidity_information_is_known() {
|
||||
mockEdge();
|
||||
assertThat(edgeComputation.getEdges().edges())
|
||||
assertThat(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS).edges())
|
||||
.contains(EdgeWithLiquidityInformation.forUpperBound(EDGE, EDGE.capacity()));
|
||||
}
|
||||
|
||||
@@ -321,4 +349,8 @@ class EdgeComputationTest {
|
||||
Coins withOnChainReserve = withFeeReserve.subtract(Coins.ofSatoshis(1_000));
|
||||
return withOnChainReserve.maximum(Coins.NONE);
|
||||
}
|
||||
|
||||
private static Policy policy(int feeRate) {
|
||||
return new Policy(feeRate, Coins.NONE, true, 40, Coins.ofSatoshis(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_3;
|
||||
import static de.cotto.lndmanagej.model.PubkeyFixtures.PUBKEY_4;
|
||||
import static de.cotto.lndmanagej.pickhardtpayments.model.PaymentOptions.DEFAULT_PAYMENT_OPTIONS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -62,23 +61,24 @@ class FlowComputationTest {
|
||||
|
||||
@Test
|
||||
void solve_no_edge() {
|
||||
when(edgeComputation.getEdges()).thenReturn(EdgesWithLiquidityInformation.EMPTY);
|
||||
when(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS)).thenReturn(EdgesWithLiquidityInformation.EMPTY);
|
||||
assertThat(flowComputation.getOptimalFlows(PUBKEY, PUBKEY_2, Coins.ofSatoshis(1), DEFAULT_PAYMENT_OPTIONS))
|
||||
.isEqualTo(new Flows());
|
||||
}
|
||||
|
||||
@Test
|
||||
void passes_fee_rate_limit_to_get_edges() {
|
||||
when(edgeComputation.getEdges(anyLong())).thenReturn(EdgesWithLiquidityInformation.EMPTY);
|
||||
flowComputation.getOptimalFlows(PUBKEY, PUBKEY_2, Coins.ofSatoshis(1), PaymentOptions.forFeeRateLimit(123));
|
||||
verify(edgeComputation).getEdges(123);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forFeeRateLimit(123);
|
||||
when(edgeComputation.getEdges(paymentOptions)).thenReturn(EdgesWithLiquidityInformation.EMPTY);
|
||||
flowComputation.getOptimalFlows(PUBKEY, PUBKEY_2, Coins.ofSatoshis(1), paymentOptions);
|
||||
verify(edgeComputation).getEdges(paymentOptions);
|
||||
}
|
||||
|
||||
@Test
|
||||
void solve() {
|
||||
Coins amount = Coins.ofSatoshis(1);
|
||||
EdgeWithLiquidityInformation edge = EdgeWithLiquidityInformation.forUpperBound(EDGE, EDGE.capacity());
|
||||
when(edgeComputation.getEdges()).thenReturn(new EdgesWithLiquidityInformation(edge));
|
||||
when(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS)).thenReturn(new EdgesWithLiquidityInformation(edge));
|
||||
Flow expectedFlow = new Flow(EDGE, amount);
|
||||
assertThat(flowComputation.getOptimalFlows(PUBKEY, PUBKEY_2, amount, DEFAULT_PAYMENT_OPTIONS))
|
||||
.isEqualTo(new Flows(expectedFlow));
|
||||
@@ -89,7 +89,7 @@ class FlowComputationTest {
|
||||
when(configurationService.getIntegerValue(QUANTIZATION)).thenReturn(Optional.of(10));
|
||||
Coins amount = Coins.ofSatoshis(9);
|
||||
EdgeWithLiquidityInformation edge = EdgeWithLiquidityInformation.forUpperBound(EDGE, EDGE.capacity());
|
||||
when(edgeComputation.getEdges()).thenReturn(new EdgesWithLiquidityInformation(edge));
|
||||
when(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS)).thenReturn(new EdgesWithLiquidityInformation(edge));
|
||||
assertThat(flowComputation.getOptimalFlows(PUBKEY, PUBKEY_2, amount, DEFAULT_PAYMENT_OPTIONS))
|
||||
.isEqualTo(new Flows(new Flow(EDGE, amount)));
|
||||
}
|
||||
@@ -98,7 +98,7 @@ class FlowComputationTest {
|
||||
void solve_avoids_sending_from_depleted_local_channel() {
|
||||
Edge edge1 = new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, LARGE, POLICY_1);
|
||||
Edge edge2 = new Edge(CHANNEL_ID_2, PUBKEY, PUBKEY_2, SMALL, POLICY_2);
|
||||
when(edgeComputation.getEdges()).thenReturn(new EdgesWithLiquidityInformation(
|
||||
when(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS)).thenReturn(new EdgesWithLiquidityInformation(
|
||||
EdgeWithLiquidityInformation.forKnownLiquidity(edge1, Coins.NONE),
|
||||
EdgeWithLiquidityInformation.forUpperBound(edge2, SMALL)
|
||||
));
|
||||
@@ -114,7 +114,7 @@ class FlowComputationTest {
|
||||
Edge edge1a = new Edge(CHANNEL_ID, PUBKEY_2, PUBKEY_3, LARGE, POLICY_1);
|
||||
Edge edge1b = new Edge(CHANNEL_ID_2, PUBKEY_3, PUBKEY_4, LARGE, POLICY_1);
|
||||
Edge edge2 = new Edge(CHANNEL_ID_3, PUBKEY_2, PUBKEY_4, SMALL, POLICY_2);
|
||||
when(edgeComputation.getEdges()).thenReturn(new EdgesWithLiquidityInformation(
|
||||
when(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS)).thenReturn(new EdgesWithLiquidityInformation(
|
||||
EdgeWithLiquidityInformation.forUpperBound(edge1a, amount),
|
||||
EdgeWithLiquidityInformation.forUpperBound(edge1b, LARGE),
|
||||
EdgeWithLiquidityInformation.forUpperBound(edge2, SMALL)
|
||||
@@ -130,7 +130,7 @@ class FlowComputationTest {
|
||||
Edge edge1a = new Edge(CHANNEL_ID, PUBKEY_3, PUBKEY_4, LARGE, POLICY_2);
|
||||
Edge edge1b = new Edge(CHANNEL_ID_2, PUBKEY_4, PUBKEY, LARGE, POLICY_2);
|
||||
Edge edge2 = new Edge(CHANNEL_ID_3, PUBKEY_3, PUBKEY, SMALL, POLICY_1);
|
||||
when(edgeComputation.getEdges()).thenReturn(new EdgesWithLiquidityInformation(
|
||||
when(edgeComputation.getEdges(DEFAULT_PAYMENT_OPTIONS)).thenReturn(new EdgesWithLiquidityInformation(
|
||||
EdgeWithLiquidityInformation.forUpperBound(edge1a, Coins.ofSatoshis(5_000_000)),
|
||||
EdgeWithLiquidityInformation.forUpperBound(edge1b, LARGE),
|
||||
EdgeWithLiquidityInformation.forUpperBound(edge2, SMALL)
|
||||
|
||||
@@ -123,7 +123,7 @@ class MultiPathPaymentSplitterTest {
|
||||
int feeRate = 200;
|
||||
Coins amount = Coins.ofSatoshis(1_000_000);
|
||||
Policy policy = policyFor(feeRate);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(feeRate - 1, PUBKEY_2);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(feeRate - 1, 0, PUBKEY_2);
|
||||
mockFlow(amount, policy, paymentOptions);
|
||||
|
||||
MultiPathPayment multiPathPayment =
|
||||
@@ -137,7 +137,7 @@ class MultiPathPaymentSplitterTest {
|
||||
int feeRate = 200;
|
||||
Coins amount = Coins.ofSatoshis(2_000_000);
|
||||
Policy policy = policyFor(feeRate);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(feeRate, PUBKEY_2);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(feeRate, 0, PUBKEY_2);
|
||||
mockFlow(amount, policy, paymentOptions);
|
||||
|
||||
MultiPathPayment multiPathPayment =
|
||||
@@ -151,7 +151,7 @@ class MultiPathPaymentSplitterTest {
|
||||
mockExtensionEdge(PUBKEY_3, feeRate);
|
||||
Coins amount = Coins.ofSatoshis(2_000_000);
|
||||
Policy policy = policyFor(0);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(feeRate - 1, PUBKEY_2);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(feeRate - 1, 0, PUBKEY_2);
|
||||
mockFlow(amount, policy, paymentOptions);
|
||||
|
||||
MultiPathPayment multiPathPayment =
|
||||
@@ -197,11 +197,13 @@ class MultiPathPaymentSplitterTest {
|
||||
|
||||
@Test
|
||||
void one_flow_has_fee_rate_above_limit_but_average_fee_rate_is_below_limit_including_fees_from_first_hop() {
|
||||
// The cheaper flows might fail while the more expensive one settles. This is not what we want, so we have
|
||||
// to disregard all flows.
|
||||
mockExtensionEdge(PUBKEY_4, 0);
|
||||
int feeRate = 200;
|
||||
Coins halfOfAmount = Coins.ofSatoshis(500_000);
|
||||
Coins amount = halfOfAmount.add(halfOfAmount);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(feeRate - 1, PUBKEY_2);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(feeRate - 1, 0, PUBKEY_2);
|
||||
Edge edge1 = new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policyFor(0));
|
||||
Edge edge2 = new Edge(CHANNEL_ID, PUBKEY, PUBKEY_2, CAPACITY, policyFor(feeRate));
|
||||
Flow flow1 = new Flow(edge1, halfOfAmount);
|
||||
@@ -348,7 +350,7 @@ class MultiPathPaymentSplitterTest {
|
||||
}
|
||||
|
||||
private MultiPathPayment attemptTopUpPayment() {
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(500, PUBKEY_2);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(500, 123, PUBKEY_2);
|
||||
when(flowComputation.getOptimalFlows(PUBKEY, PUBKEY_2, AMOUNT, paymentOptions)).thenReturn(new Flows(FLOW));
|
||||
return multiPathPaymentSplitter.getMultiPathPayment(PUBKEY, PUBKEY_3, AMOUNT, paymentOptions);
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ class TopUpServiceTest {
|
||||
private void assertTopUp(Coins expectedTopUpAmount, Duration expiry) {
|
||||
PaymentStatus paymentStatus = topUpService.topUp(PUBKEY, AMOUNT);
|
||||
verify(grpcInvoices).createPaymentRequest(expectedTopUpAmount, DESCRIPTION, expiry);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(OUR_FEE_RATE, PUBKEY);
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(OUR_FEE_RATE, PEER_FEE_RATE, PUBKEY);
|
||||
verify(multiPathPaymentSender).payPaymentRequest(DECODED_PAYMENT_REQUEST, paymentOptions);
|
||||
assertThat(paymentStatus.isPending()).isTrue();
|
||||
}
|
||||
|
||||
@@ -12,19 +12,25 @@ class PaymentOptionsTest {
|
||||
@Test
|
||||
void forFeeRateWeight() {
|
||||
assertThat(PaymentOptions.forFeeRateWeight(12))
|
||||
.isEqualTo(new PaymentOptions(12, Optional.empty(), true, Optional.empty()));
|
||||
.isEqualTo(new PaymentOptions(12, Optional.empty(), Optional.empty(), true, Optional.empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void forFeeRateLimit() {
|
||||
assertThat(PaymentOptions.forFeeRateLimit(123))
|
||||
.isEqualTo(new PaymentOptions(0, Optional.of(123L), true, Optional.empty()));
|
||||
.isEqualTo(new PaymentOptions(0, Optional.of(123L), Optional.of(123L), true, Optional.empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void forTopUp() {
|
||||
assertThat(PaymentOptions.forTopUp(123, PUBKEY))
|
||||
.isEqualTo(new PaymentOptions(5, Optional.of(123L), false, Optional.of(PUBKEY)));
|
||||
assertThat(PaymentOptions.forTopUp(123, 100, PUBKEY))
|
||||
.isEqualTo(new PaymentOptions(5, Optional.of(123L), Optional.of(23L), false, Optional.of(PUBKEY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void feeRateLimitFirstHops() {
|
||||
PaymentOptions paymentOptions = PaymentOptions.forTopUp(200, 30, PUBKEY);
|
||||
assertThat(paymentOptions.feeRateLimitExceptIncomingHops()).contains(170L);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user