Skip to content
This repository was archived by the owner on Feb 9, 2026. It is now read-only.

Commit 8645003

Browse files
authored
Merge pull request #183 from synonymdev/backup-retries
Remote persist retries
2 parents 50b625d + 1240657 commit 8645003

10 files changed

Lines changed: 85 additions & 85 deletions

File tree

lib/android/src/main/java/com/reactnativeldk/LdkModule.kt

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,10 @@ enum class EventTypes {
4545
channel_manager_channel_closed,
4646
channel_manager_discard_funding,
4747
channel_manager_payment_claimed,
48-
emergency_force_close_channel,
4948
new_channel,
5049
network_graph_updated,
5150
channel_manager_restarted,
52-
backup_failed
51+
backup_sync_persist_error
5352
}
5453
//*****************************************************************
5554

@@ -69,7 +68,6 @@ enum class LdkErrors {
6968
add_peer_fail,
7069
init_channel_manager,
7170
decode_invoice_fail,
72-
init_invoice_payer,
7371
invoice_payment_fail_unknown,
7472
invoice_payment_fail_must_specify_amount,
7573
invoice_payment_fail_must_not_specify_amount,
@@ -103,7 +101,6 @@ enum class LdkCallbackResponses {
103101
log_level_updated,
104102
log_path_updated,
105103
log_write_success,
106-
chain_monitor_init_success,
107104
keys_manager_init_success,
108105
channel_manager_init_success,
109106
config_init_success,
@@ -226,23 +223,6 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
226223
handleResolve(promise, LdkCallbackResponses.log_write_success)
227224
}
228225

229-
@ReactMethod
230-
fun initChainMonitor(promise: Promise) {
231-
if (chainMonitor !== null) {
232-
return handleReject(promise, LdkErrors.already_init)
233-
}
234-
235-
chainMonitor = ChainMonitor.of(
236-
Option_FilterZ.some(filter.filter),
237-
broadcaster.broadcaster,
238-
logger.logger,
239-
feeEstimator.feeEstimator,
240-
persister.persister
241-
)
242-
243-
handleResolve(promise, LdkCallbackResponses.chain_monitor_init_success)
244-
}
245-
246226
@ReactMethod
247227
fun initKeysManager(seed: String, promise: Promise) {
248228
if (keysManager !== null) {
@@ -359,7 +339,6 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
359339
return handleReject(promise, LdkErrors.already_init)
360340
}
361341

362-
chainMonitor ?: return handleReject(promise, LdkErrors.init_chain_monitor)
363342
keysManager ?: return handleReject(promise, LdkErrors.init_keys_manager)
364343
userConfig ?: return handleReject(promise, LdkErrors.init_user_config)
365344
networkGraph ?: return handleReject(promise, LdkErrors.init_network_graph)
@@ -393,6 +372,14 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
393372
val scoringParams = ProbabilisticScoringDecayParameters.with_default()
394373
val scoringFeeParams = ProbabilisticScoringFeeParameters.with_default()
395374

375+
chainMonitor = ChainMonitor.of(
376+
Option_FilterZ.some(filter.filter),
377+
broadcaster.broadcaster,
378+
logger.logger,
379+
feeEstimator.feeEstimator,
380+
persister.persister
381+
)
382+
396383
try {
397384
if (channelManagerSerialized != null) {
398385
//Restoring node

lib/android/src/main/java/com/reactnativeldk/classes/BackupClient.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,36 @@ class BackupClient {
174174
}
175175
}
176176

177+
@Throws(BackupError::class)
178+
fun persist(label: Label, bytes: ByteArray, retry: Int) {
179+
var attempts = 0
180+
var persistError: Exception? = null
181+
182+
while (attempts < retry) {
183+
try {
184+
persist(label, bytes)
185+
186+
LdkEventEmitter.send(
187+
EventTypes.native_log,
188+
"Remote persist success for ${label.string} after ${attempts+1} attempts"
189+
)
190+
return
191+
} catch (error: Exception) {
192+
persistError = error
193+
attempts += 1
194+
LdkEventEmitter.send(
195+
EventTypes.native_log,
196+
"Remote persist failed for ${label.string} ($attempts attempts)"
197+
)
198+
Thread.sleep(attempts.toLong()) // Ease off with each attempt
199+
}
200+
}
201+
202+
if (persistError != null) {
203+
throw persistError
204+
}
205+
}
206+
177207
@Throws(BackupError::class)
178208
fun persist(label: Label, bytes: ByteArray) {
179209
if (skipRemoteBackup) {
@@ -214,6 +244,10 @@ class BackupClient {
214244
EventTypes.native_log,
215245
"Remote persist failed for ${label.string} with response code ${urlConnection.responseCode}"
216246
)
247+
LdkEventEmitter.send(
248+
EventTypes.backup_sync_persist_error,
249+
"Response: ${urlConnection.responseMessage}"
250+
)
217251

218252
throw InvalidServerResponse(urlConnection.responseCode)
219253
}

lib/android/src/main/java/com/reactnativeldk/classes/LdkChannelManagerPersister.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,9 @@ class LdkChannelManagerPersister: ChannelManagerConstructor.EventHandler {
173173

174174
override fun persist_manager(channel_manager_bytes: ByteArray?) {
175175
if (channel_manager_bytes != null && LdkModule.accountStoragePath != "") {
176+
BackupClient.persist(BackupClient.Label.CHANNEL_MANAGER(), channel_manager_bytes, retry = 100)
176177
File(LdkModule.accountStoragePath + "/" + LdkFileNames.channel_manager.fileName).writeBytes(channel_manager_bytes)
177178

178-
BackupClient.persist(BackupClient.Label.CHANNEL_MANAGER(), channel_manager_bytes)
179-
180179
LdkEventEmitter.send(EventTypes.native_log, "Persisted channel manager to disk")
181180
LdkEventEmitter.send(EventTypes.backup, "")
182181
}

lib/android/src/main/java/com/reactnativeldk/classes/LdkPersister.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ class LdkPersister {
2424

2525
val isNew = !file.exists()
2626

27+
BackupClient.persist(BackupClient.Label.CHANNEL_MONITOR(channelId=channelId), data.write(), retry = 100)
2728
file.writeBytes(data.write())
28-
BackupClient.persist(BackupClient.Label.CHANNEL_MONITOR(channelId=channelId), data.write())
2929

3030
LdkEventEmitter.send(EventTypes.native_log, "Persisted channel (${id.to_channel_id().hexEncodedString()}) to disk")
3131
LdkEventEmitter.send(EventTypes.backup, "")

lib/ios/Classes/BackupClient.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,28 @@ class BackupClient {
186186
}
187187
}
188188
}
189+
190+
static func persist(_ label: Label, _ bytes: [UInt8], retry: Int) throws {
191+
var attempts: UInt32 = 0
192+
193+
var persistError: Error?
194+
while attempts < retry {
195+
do {
196+
try persist(label, bytes)
197+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Remote persist success for \(label.string) after \(attempts+1) attempts")
198+
return
199+
} catch {
200+
persistError = error
201+
attempts += 1
202+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Remote persist failed for \(label.string) (\(attempts) attempts)")
203+
sleep(attempts) //Ease off with each attempt
204+
}
205+
}
206+
207+
if let persistError {
208+
throw persistError
209+
}
210+
}
189211

190212
static func persist(_ label: Label, _ bytes: [UInt8]) throws {
191213
struct PersistResponse: Codable {

lib/ios/Classes/LdkChannelManagerPersister.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,10 +267,10 @@ class LdkChannelManagerPersister: Persister, ExtendedChannelManagerPersister {
267267
}
268268

269269
do {
270+
try BackupClient.persist(.channelManager, channelManager.write(), retry: 100)
271+
270272
try Data(channelManager.write()).write(to: managerStorage)
271-
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Persisted channel manager to disk")
272-
273-
try BackupClient.persist(.channelManager, channelManager.write())
273+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Persisted channel manager to disk")
274274

275275
LdkEventEmitter.shared.send(withEvent: .backup, body: "")
276276

lib/ios/Classes/LdkPersist.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,12 @@ class LdkPersister: Persist {
2323

2424
let isNew = !FileManager().fileExists(atPath: channelStoragePath.path)
2525

26+
try BackupClient.persist(.channelMonitor(id: channelId), data.write(), retry: 100)
2627
try Data(data.write()).write(to: channelStoragePath)
27-
try BackupClient.persist(.channelMonitor(id: channelId), data.write())
2828

2929
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Persisted channel (\(channelId)) to disk")
3030
LdkEventEmitter.shared.send(withEvent: .backup, body: "")
3131

32-
3332
if isNew {
3433
LdkEventEmitter.shared.send(
3534
withEvent: .new_channel,

lib/ios/Ldk.swift

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ enum EventTypes: String, CaseIterable {
2525
case new_channel = "new_channel"
2626
case network_graph_updated = "network_graph_updated"
2727
case channel_manager_restarted = "channel_manager_restarted"
28-
case backup_sync_persist_error = "backup_failed"
28+
case backup_sync_persist_error = "backup_sync_persist_error"
2929
}
3030
//*****************************************************************
3131

@@ -45,7 +45,6 @@ enum LdkErrors: String {
4545
case add_peer_fail = "add_peer_fail"
4646
case init_channel_manager = "init_channel_manager"
4747
case decode_invoice_fail = "decode_invoice_fail"
48-
case init_invoice_payer = "init_invoice_payer"
4948
case invoice_payment_fail_unknown = "invoice_payment_fail_unknown"
5049
case invoice_payment_fail_must_specify_amount = "invoice_payment_fail_must_specify_amount"
5150
case invoice_payment_fail_must_not_specify_amount = "invoice_payment_fail_must_not_specify_amount"
@@ -82,7 +81,6 @@ enum LdkCallbackResponses: String {
8281
case log_level_updated = "log_level_updated"
8382
case log_path_updated = "log_path_updated"
8483
case log_write_success = "log_write_success"
85-
case chain_monitor_init_success = "chain_monitor_init_success"
8684
case keys_manager_init_success = "keys_manager_init_success"
8785
case channel_manager_init_success = "channel_manager_init_success"
8886
case config_init_success = "config_init_success"
@@ -198,23 +196,6 @@ class Ldk: NSObject {
198196
return handleResolve(resolve, .log_write_success)
199197
}
200198

201-
@objc
202-
func initChainMonitor(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
203-
guard chainMonitor == nil else {
204-
return handleReject(reject, .already_init)
205-
}
206-
207-
chainMonitor = ChainMonitor(
208-
chainSource: filter,
209-
broadcaster: broadcaster,
210-
logger: logger,
211-
feeest: feeEstimator,
212-
persister: persister
213-
)
214-
215-
return handleResolve(resolve, .chain_monitor_init_success)
216-
}
217-
218199
@objc
219200
func initKeysManager(_ seed: NSString, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
220201
if keysManager != nil {
@@ -344,10 +325,6 @@ class Ldk: NSObject {
344325
return handleReject(reject, .already_init)
345326
}
346327

347-
guard let chainMonitor = chainMonitor else {
348-
return handleReject(reject, .init_chain_monitor)
349-
}
350-
351328
guard let keysManager = keysManager else {
352329
return handleReject(reject, .init_keys_manager)
353330
}
@@ -392,6 +369,14 @@ class Ldk: NSObject {
392369
let score = probabilisticScorer.asScore()
393370
let scorer = MultiThreadedLockableScore(score: score)
394371

372+
chainMonitor = ChainMonitor(
373+
chainSource: filter,
374+
broadcaster: broadcaster,
375+
logger: logger,
376+
feeest: feeEstimator,
377+
persister: persister
378+
)
379+
395380
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Enabled P2P gossip: \(enableP2PGossip)")
396381

397382
// print("\(String(cString: strerror(22)))")
@@ -402,7 +387,7 @@ class Ldk: NSObject {
402387
nodeSigner: keysManager.asNodeSigner(),
403388
signerProvider: keysManager.asSignerProvider(),
404389
feeEstimator: feeEstimator,
405-
chainMonitor: chainMonitor,
390+
chainMonitor: chainMonitor!,
406391
txBroadcaster: broadcaster,
407392
logger: logger,
408393
enableP2PGossip: enableP2PGossip,
@@ -500,14 +485,6 @@ class Ldk: NSObject {
500485
channelManager = nil
501486
peerManager = nil
502487
peerHandler = nil
503-
504-
chainMonitor = ChainMonitor(
505-
chainSource: filter,
506-
broadcaster: broadcaster,
507-
logger: logger,
508-
feeest: feeEstimator,
509-
persister: persister
510-
)
511488

512489
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Starting LDK background tasks again")
513490
initChannelManager(currentNetwork, blockHash: currentBlockchainTipHash, blockHeight: currentBlockchainHeight) { success in

lib/src/ldk.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,6 @@ class LDK {
8080
}
8181
}
8282

83-
/**
84-
* Connected and disconnected blocks must be provided
85-
* https://docs.rs/lightning/latest/lightning/chain/chainmonitor/struct.ChainMonitor.html
86-
* @returns {Promise<Err<unknown> | Ok<Ok<string> | Err<string>>>}
87-
*/
88-
async initChainMonitor(): Promise<Result<string>> {
89-
try {
90-
const res = await NativeLDK.initChainMonitor();
91-
this.writeDebugToLog('initChainMonitor');
92-
return ok(res);
93-
} catch (e) {
94-
this.writeErrorToLog('initChainMonitor', e);
95-
return err(e);
96-
}
97-
}
98-
9983
/**
10084
* Private key for node. Used to derive node public key. 32-byte entropy.
10185
* https://docs.rs/lightning/latest/lightning/chain/keysinterface/struct.KeysManager.html
@@ -1129,7 +1113,9 @@ class LDK {
11291113
this.writeDebugToLog('readFromFile', fileName);
11301114
return ok({ ...res, timestamp: Math.round(res.timestamp) });
11311115
} catch (e) {
1132-
this.writeErrorToLog('readFromFile', e);
1116+
if (JSON.stringify(e).indexOf('Could not locate file') < 0) {
1117+
this.writeErrorToLog('readFromFile', e);
1118+
}
11331119
return err(e);
11341120
}
11351121
}

lib/src/lightning-manager.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -435,11 +435,7 @@ class LightningManager {
435435
// Lazy loaded in native code
436436
// https://docs.rs/lightning/latest/lightning/chain/chainmonitor/trait.Persist.html
437437

438-
// Step 5: Initialize the ChainMonitor
439-
const chainMonitorRes = await ldk.initChainMonitor();
440-
if (chainMonitorRes.isErr()) {
441-
return this.handleStartError(chainMonitorRes);
442-
}
438+
// Step 5: Initialize the ChainMonitor (happens when we init the ChannelManager)
443439

444440
// Step 6: Initialize the KeysManager
445441
const keysManager = await ldk.initKeysManager(this.account.seed);

0 commit comments

Comments
 (0)