feat: probe payment paths before LDK Node payments to improve routing
This commit is contained in:
@@ -1297,6 +1297,48 @@ class LdkNodeModule(reactContext: ReactApplicationContext) : ReactContextBaseJav
|
||||
}
|
||||
}
|
||||
|
||||
// Probe Methods
|
||||
|
||||
@ReactMethod
|
||||
fun sendBolt11Probes(invoice: String, maxTotalRoutingFeeMsat: Double, maxPathCount: Double, promise: Promise) {
|
||||
val routeParams = buildRouteParameters(maxTotalRoutingFeeMsat, maxPathCount)
|
||||
moduleScope.launch {
|
||||
try {
|
||||
val node = this@LdkNodeModule.node ?: throw Exception("Node not initialized")
|
||||
val bolt11 = node.bolt11Payment()
|
||||
val invoiceObj = Bolt11Invoice.fromStr(invoice)
|
||||
bolt11.sendProbes(invoiceObj, routeParams)
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(null)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("error", errorMessage(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun sendBolt11ProbesUsingAmount(invoice: String, amountMsat: Double, maxTotalRoutingFeeMsat: Double, maxPathCount: Double, promise: Promise) {
|
||||
val routeParams = buildRouteParameters(maxTotalRoutingFeeMsat, maxPathCount)
|
||||
moduleScope.launch {
|
||||
try {
|
||||
val node = this@LdkNodeModule.node ?: throw Exception("Node not initialized")
|
||||
val bolt11 = node.bolt11Payment()
|
||||
val invoiceObj = Bolt11Invoice.fromStr(invoice)
|
||||
bolt11.sendProbesUsingAmount(invoiceObj, amountMsat.toLong().toULong(), routeParams)
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(null)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("error", errorMessage(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Spontaneous Payment Methods
|
||||
|
||||
@ReactMethod
|
||||
|
||||
@@ -65,6 +65,10 @@ RCT_EXTERN_METHOD(receiveVariableAmountBolt11:(NSString *)invoiceDescription exp
|
||||
RCT_EXTERN_METHOD(sendBolt11:(NSString *)invoice maxTotalRoutingFeeMsat:(double)maxTotalRoutingFeeMsat maxPathCount:(double)maxPathCount resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
||||
RCT_EXTERN_METHOD(sendBolt11UsingAmount:(NSString *)invoice amountMsat:(double)amountMsat maxTotalRoutingFeeMsat:(double)maxTotalRoutingFeeMsat maxPathCount:(double)maxPathCount resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
// Probe Methods
|
||||
RCT_EXTERN_METHOD(sendBolt11Probes:(NSString *)invoice maxTotalRoutingFeeMsat:(double)maxTotalRoutingFeeMsat maxPathCount:(double)maxPathCount resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
||||
RCT_EXTERN_METHOD(sendBolt11ProbesUsingAmount:(NSString *)invoice amountMsat:(double)amountMsat maxTotalRoutingFeeMsat:(double)maxTotalRoutingFeeMsat maxPathCount:(double)maxPathCount resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
// Spontaneous Payment Methods
|
||||
RCT_EXTERN_METHOD(sendSpontaneousPayment:(NSString *)nodeId amountMsat:(double)amountMsat maxTotalRoutingFeeMsat:(double)maxTotalRoutingFeeMsat maxPathCount:(double)maxPathCount resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
|
||||
@@ -1157,6 +1157,50 @@ class LdkNodeModule: RCTEventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Probe Methods
|
||||
|
||||
@objc(sendBolt11Probes:maxTotalRoutingFeeMsat:maxPathCount:resolver:rejecter:)
|
||||
func sendBolt11Probes(_ invoice: String, maxTotalRoutingFeeMsat: Double, maxPathCount: Double, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
guard let node = self.getNode() else {
|
||||
reject("error", "Node not initialized", nil)
|
||||
return
|
||||
}
|
||||
|
||||
let routeParams = buildRouteParameters(maxTotalRoutingFeeMsat: maxTotalRoutingFeeMsat, maxPathCount: maxPathCount)
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
do {
|
||||
let bolt11 = node.bolt11Payment()
|
||||
let bolt11Invoice = try Bolt11Invoice.fromStr(invoiceStr: invoice)
|
||||
try bolt11.sendProbes(invoice: bolt11Invoice, routeParameters: routeParams)
|
||||
resolve(nil)
|
||||
} catch {
|
||||
reject("error", self.errorMessage(error), error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc(sendBolt11ProbesUsingAmount:amountMsat:maxTotalRoutingFeeMsat:maxPathCount:resolver:rejecter:)
|
||||
func sendBolt11ProbesUsingAmount(_ invoice: String, amountMsat: Double, maxTotalRoutingFeeMsat: Double, maxPathCount: Double, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
guard let node = self.getNode() else {
|
||||
reject("error", "Node not initialized", nil)
|
||||
return
|
||||
}
|
||||
|
||||
let routeParams = buildRouteParameters(maxTotalRoutingFeeMsat: maxTotalRoutingFeeMsat, maxPathCount: maxPathCount)
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
do {
|
||||
let bolt11 = node.bolt11Payment()
|
||||
let bolt11Invoice = try Bolt11Invoice.fromStr(invoiceStr: invoice)
|
||||
try bolt11.sendProbesUsingAmount(invoice: bolt11Invoice, amountMsat: UInt64(amountMsat), routeParameters: routeParams)
|
||||
resolve(nil)
|
||||
} catch {
|
||||
reject("error", self.errorMessage(error), error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Spontaneous Payment Methods
|
||||
|
||||
@objc(sendSpontaneousPayment:amountMsat:maxTotalRoutingFeeMsat:maxPathCount:resolver:rejecter:)
|
||||
|
||||
Vendored
+13
@@ -565,6 +565,19 @@ export interface ILdkNodeModule {
|
||||
maxPathCount: number
|
||||
): Promise<string>;
|
||||
|
||||
// Probe Methods
|
||||
sendBolt11Probes(
|
||||
invoice: string,
|
||||
maxTotalRoutingFeeMsat: number,
|
||||
maxPathCount: number
|
||||
): Promise<void>;
|
||||
sendBolt11ProbesUsingAmount(
|
||||
invoice: string,
|
||||
amountMsat: number,
|
||||
maxTotalRoutingFeeMsat: number,
|
||||
maxPathCount: number
|
||||
): Promise<void>;
|
||||
|
||||
// Spontaneous Payment Methods
|
||||
sendSpontaneousPayment(
|
||||
nodeId: string,
|
||||
|
||||
@@ -464,6 +464,45 @@ const sendBolt11UsingAmount = async ({
|
||||
return result.paymentId;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Probe Functions
|
||||
// ============================================================================
|
||||
|
||||
const sendBolt11Probes = async ({
|
||||
invoice,
|
||||
maxTotalRoutingFeeMsat,
|
||||
maxPathCount
|
||||
}: {
|
||||
invoice: string;
|
||||
maxTotalRoutingFeeMsat?: number;
|
||||
maxPathCount?: number;
|
||||
}): Promise<void> => {
|
||||
await LdkNodeModule.sendBolt11Probes(
|
||||
invoice,
|
||||
maxTotalRoutingFeeMsat ?? -1,
|
||||
maxPathCount ?? -1
|
||||
);
|
||||
};
|
||||
|
||||
const sendBolt11ProbesUsingAmount = async ({
|
||||
invoice,
|
||||
amountMsat,
|
||||
maxTotalRoutingFeeMsat,
|
||||
maxPathCount
|
||||
}: {
|
||||
invoice: string;
|
||||
amountMsat: number;
|
||||
maxTotalRoutingFeeMsat?: number;
|
||||
maxPathCount?: number;
|
||||
}): Promise<void> => {
|
||||
await LdkNodeModule.sendBolt11ProbesUsingAmount(
|
||||
invoice,
|
||||
amountMsat,
|
||||
maxTotalRoutingFeeMsat ?? -1,
|
||||
maxPathCount ?? -1
|
||||
);
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Spontaneous Payment Functions
|
||||
// ============================================================================
|
||||
@@ -1062,6 +1101,17 @@ export interface ILdkNodeInjections {
|
||||
maxTotalRoutingFeeMsat?: number;
|
||||
maxPathCount?: number;
|
||||
}) => Promise<string>;
|
||||
sendBolt11Probes: (params: {
|
||||
invoice: string;
|
||||
maxTotalRoutingFeeMsat?: number;
|
||||
maxPathCount?: number;
|
||||
}) => Promise<void>;
|
||||
sendBolt11ProbesUsingAmount: (params: {
|
||||
invoice: string;
|
||||
amountMsat: number;
|
||||
maxTotalRoutingFeeMsat?: number;
|
||||
maxPathCount?: number;
|
||||
}) => Promise<void>;
|
||||
};
|
||||
spontaneous: {
|
||||
sendSpontaneousPayment: (params: {
|
||||
@@ -1237,7 +1287,9 @@ const LdkNodeInjection: ILdkNodeInjections = {
|
||||
receiveBolt11,
|
||||
receiveVariableAmountBolt11,
|
||||
sendBolt11,
|
||||
sendBolt11UsingAmount
|
||||
sendBolt11UsingAmount,
|
||||
sendBolt11Probes,
|
||||
sendBolt11ProbesUsingAmount
|
||||
},
|
||||
spontaneous: {
|
||||
sendSpontaneousPayment
|
||||
|
||||
@@ -14,6 +14,7 @@ import LSPStore from './LSPStore';
|
||||
import BackendUtils from '../utils/BackendUtils';
|
||||
import { localeString } from '../utils/LocaleUtils';
|
||||
import { errorToUserFriendly } from '../utils/ErrorUtils';
|
||||
import LdkNodeInjection from '../ldknode/LdkNodeInjection';
|
||||
import ChannelsStore from './ChannelsStore';
|
||||
import NodeInfoStore from './NodeInfoStore';
|
||||
import WithdrawalRequest from '../models/WithdrawalRequest';
|
||||
@@ -70,6 +71,14 @@ export default class InvoicesStore {
|
||||
this.pay_req.getRequestAmount
|
||||
);
|
||||
}
|
||||
// Send probes for LDK Node to warm up the scorer
|
||||
// while the user reviews the payment request
|
||||
if (
|
||||
this.pay_req &&
|
||||
this.settingsStore.implementation === 'ldk-node'
|
||||
) {
|
||||
this.sendLdkProbes();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -760,4 +769,24 @@ export default class InvoicesStore {
|
||||
})
|
||||
.catch(() => this.getRoutesError());
|
||||
};
|
||||
|
||||
/**
|
||||
* Send probes along the payment path for LDK Node.
|
||||
* This warms up the probabilistic scorer so the actual
|
||||
* payment has better routing information. Fire-and-forget.
|
||||
* Only probes fixed-amount invoices — variable-amount
|
||||
* invoices can't be probed without knowing the amount.
|
||||
*/
|
||||
private sendLdkProbes = async () => {
|
||||
try {
|
||||
const amount = this.pay_req?.getRequestAmount;
|
||||
if (!amount || Number(amount) <= 0) return;
|
||||
|
||||
await LdkNodeInjection.bolt11.sendBolt11Probes({
|
||||
invoice: this.paymentRequest
|
||||
});
|
||||
} catch {
|
||||
// Probe failures are expected and non-fatal
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user