Skip to content

Commit af0eb40

Browse files
committed
fix(ios): if fails to find route, reset graph reset scorer, sync both again and attempt payment one more time
1 parent 0a7c8bb commit af0eb40

File tree

1 file changed

+103
-12
lines changed

1 file changed

+103
-12
lines changed

lib/ios/Ldk.swift

Lines changed: 103 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ class Ldk: NSObject {
151151
var currentNetwork: NSString?
152152
var currentBlockchainTipHash: NSString?
153153
var currentBlockchainHeight: NSInteger?
154+
var currentScorerDownloadUrl: NSString?
155+
var currentRapidGossipSyncUrl: NSString?
154156

155157
// Peer connection checks
156158
var backgroundedAt: Date? = nil
@@ -259,6 +261,8 @@ class Ldk: NSObject {
259261
guard let accountStoragePath = Ldk.accountStoragePath else {
260262
return handleReject(reject, .init_storage_path)
261263
}
264+
265+
currentScorerDownloadUrl = scorerSyncUrl
262266

263267
let destinationFile = accountStoragePath.appendingPathComponent(LdkFileNames.scorer.rawValue)
264268

@@ -299,6 +303,8 @@ class Ldk: NSObject {
299303
guard let accountStoragePath = Ldk.accountStoragePath else {
300304
return handleReject(reject, .init_storage_path)
301305
}
306+
307+
currentRapidGossipSyncUrl = rapidGossipSyncUrl
302308

303309
let networkGraphStoragePath = accountStoragePath.appendingPathComponent(LdkFileNames.network_graph.rawValue).standardizedFileURL
304310

@@ -1062,29 +1068,110 @@ class Ldk: NSObject {
10621068

10631069
return resolve(invoice.asJson) // Invoice class extended in Helpers file
10641070
}
1071+
1072+
//Called when a payment fails but we want to reset graph and channel manager so if they try again it might work
1073+
func resetGraphAndScorerAndRetryPayment(orginalError: LdkErrors, paymentRequest: NSString, amountSats: NSInteger, timeoutSeconds: NSInteger, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
1074+
guard let accountStoragePath = Ldk.accountStoragePath else {
1075+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Failed to reset graph: account storage path not set")
1076+
return handleReject(reject, orginalError)
1077+
}
1078+
1079+
let fileManager = FileManager.default
1080+
let scorerPath = accountStoragePath.appendingPathComponent(LdkFileNames.scorer.rawValue)
1081+
let networkGraphPath = accountStoragePath.appendingPathComponent(LdkFileNames.network_graph.rawValue)
1082+
1083+
// Delete scorer if exists
1084+
if fileManager.fileExists(atPath: scorerPath.path) {
1085+
do {
1086+
try fileManager.removeItem(at: scorerPath)
1087+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Deleted scorer file")
1088+
} catch {
1089+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Failed to delete scorer file: \(error.localizedDescription)")
1090+
}
1091+
}
1092+
1093+
// Delete network graph if exists
1094+
if fileManager.fileExists(atPath: networkGraphPath.path) {
1095+
do {
1096+
try fileManager.removeItem(at: networkGraphPath)
1097+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Deleted network graph file")
1098+
networkGraph = nil
1099+
} catch {
1100+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Failed to delete network graph file: \(error.localizedDescription)")
1101+
}
1102+
}
1103+
1104+
guard let currentScorerDownloadUrl, let currentRapidGossipSyncUrl, let currentNetwork else {
1105+
return handleReject(reject, orginalError)
1106+
}
1107+
1108+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Deleted scorer and network graph, resyncing from scratch so we can retry payment")
1109+
1110+
//Download everything again and retry
1111+
self.downloadScorer(currentScorerDownloadUrl, skipHoursThreshold: 1) { _ in
1112+
self.initNetworkGraph(currentNetwork, rapidGossipSyncUrl: currentRapidGossipSyncUrl, skipHoursThreshold: 1, resolve: { _ in
1113+
self.restart { _ in
1114+
let (paymentId2, error2) = self.handlePayment(paymentRequest: paymentRequest, amountSats: amountSats, timeoutSeconds: timeoutSeconds)
1115+
if let error2 {
1116+
return handleReject(reject, error2)
1117+
}
1118+
1119+
//2nd attempt found a path with fresh graph
1120+
return resolve(paymentId2)
1121+
} reject: { _, _, _ in
1122+
return handleReject(reject, orginalError)
1123+
}
1124+
}, reject: { _, _, _ in
1125+
return handleReject(reject, orginalError)
1126+
})
1127+
} reject: { _, _, _ in
1128+
return handleReject(reject, orginalError)
1129+
}
1130+
}
10651131

10661132
@objc
10671133
func pay(_ paymentRequest: NSString, amountSats: NSInteger, timeoutSeconds: NSInteger, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
1134+
let (paymentId, error) = handlePayment(paymentRequest: paymentRequest, amountSats: amountSats, timeoutSeconds: timeoutSeconds)
1135+
if let error {
1136+
//If error is route not found, maybe a problem with the graph, so reset it, download all again and try payment one more time
1137+
if error == .invoice_payment_fail_route_not_found {
1138+
return resetGraphAndScorerAndRetryPayment(
1139+
orginalError: error,
1140+
paymentRequest: paymentRequest,
1141+
amountSats: amountSats,
1142+
timeoutSeconds: timeoutSeconds,
1143+
resolve: resolve,
1144+
reject: reject
1145+
)
1146+
}
1147+
1148+
return handleReject(reject, error)
1149+
}
1150+
1151+
return resolve(paymentId)
1152+
}
1153+
1154+
func handlePayment(paymentRequest: NSString, amountSats: NSInteger, timeoutSeconds: NSInteger) -> (String?, LdkErrors?) {
10681155
guard let channelManager = channelManager else {
1069-
return handleReject(reject, .init_channel_manager)
1156+
return (nil, .init_channel_manager)
10701157
}
1071-
1158+
10721159
guard let invoice = Bolt11Invoice.fromStr(s: String(paymentRequest)).getValue() else {
1073-
return handleReject(reject, .decode_invoice_fail)
1160+
return (nil, .decode_invoice_fail)
10741161
}
10751162

10761163
let isZeroValueInvoice = invoice.amountMilliSatoshis() == nil
10771164

10781165
// If it's a zero invoice and we don't have an amount then don't proceed
10791166
guard !(isZeroValueInvoice && amountSats == 0) else {
1080-
return handleReject(reject, .invoice_payment_fail_must_specify_amount)
1167+
return (nil, .invoice_payment_fail_must_specify_amount)
10811168
}
10821169

10831170
// Amount was set but not allowed to set own amount
10841171
guard !(amountSats > 0 && !isZeroValueInvoice) else {
1085-
return handleReject(reject, .invoice_payment_fail_must_not_specify_amount)
1172+
return (nil, .invoice_payment_fail_must_not_specify_amount)
10861173
}
1087-
1174+
10881175
let paymentId = invoice.paymentHash()!
10891176
let (paymentHash, recipientOnion, routeParameters) = isZeroValueInvoice ? Bindings.paymentParametersFromZeroAmountInvoice(invoice: invoice, amountMsat: UInt64(amountSats * 1000)).getValue()! : Bindings.paymentParametersFromInvoice(invoice: invoice).getValue()!
10901177

@@ -1101,22 +1188,26 @@ class Ldk: NSObject {
11011188
])
11021189

11031190
if res.isOk() {
1104-
return resolve(paymentId)
1191+
return (Data(paymentId).hexEncodedString(), nil)
11051192
}
11061193

11071194
guard let error = res.getError() else {
1108-
return handleReject(reject, .invoice_payment_fail_unknown)
1195+
return (nil, .invoice_payment_fail_unknown)
11091196
}
11101197

11111198
switch error {
11121199
case .DuplicatePayment:
1113-
return handleReject(reject, .invoice_payment_fail_duplicate_payment)
1200+
return (nil, .invoice_payment_fail_duplicate_payment)
11141201
case .PaymentExpired:
1115-
return handleReject(reject, .invoice_payment_fail_payment_expired)
1202+
return (nil, .invoice_payment_fail_payment_expired)
11161203
case .RouteNotFound:
1117-
return handleReject(reject, .invoice_payment_fail_route_not_found)
1204+
//Delete scorer
1205+
//Delete graph
1206+
//Download and update graph again
1207+
//Retry payment
1208+
return (nil, .invoice_payment_fail_route_not_found)
11181209
@unknown default:
1119-
return handleReject(reject, .invoice_payment_fail_unknown)
1210+
return (nil, .invoice_payment_fail_unknown)
11201211
}
11211212
}
11221213

0 commit comments

Comments
 (0)