Skip to content

Commit d2202da

Browse files
committed
fix: persist channel locally in case remote backup fails
1 parent 960d8f5 commit d2202da

File tree

3 files changed

+69
-15
lines changed

3 files changed

+69
-15
lines changed

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

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,52 @@ class LdkPersister {
2222
val file = File(LdkModule.channelStoragePath + "/" + channelId + ".bin")
2323

2424
val isNew = !file.exists()
25+
val serialized = data.write()
2526

27+
// If we're not remotely backing up, write locally and return
2628
if (BackupClient.skipRemoteBackup) {
27-
file.writeBytes(data.write())
29+
file.writeBytes(serialized)
2830
if (isNew) {
2931
LdkEventEmitter.send(EventTypes.new_channel, body)
3032
}
3133
return ChannelMonitorUpdateStatus.LDKChannelMonitorUpdateStatus_Completed
3234
}
3335

34-
BackupClient.addToPersistQueue(BackupClient.Label.CHANNEL_MONITOR(channelId=channelId), data.write()) { error ->
36+
// For new channels: write locally first to prevent loss if app is killed during backup
37+
// Then try remote backup asynchronously
38+
if (isNew) {
39+
file.writeBytes(serialized)
40+
41+
// Update chain monitor on main thread
42+
LdkModule.reactContext?.runOnUiThread {
43+
val res = LdkModule.chainMonitor?.channel_monitor_updated(channelFundingOutpoint, data._latest_update_id)
44+
if (res == null || !res.is_ok) {
45+
LdkEventEmitter.send(EventTypes.native_log, "Failed to update chain monitor with persisted channel (${channelId})")
46+
} else {
47+
LdkEventEmitter.send(EventTypes.native_log, "Persisted channel (${channelId}) to disk")
48+
LdkEventEmitter.send(EventTypes.new_channel, body)
49+
}
50+
}
51+
52+
// Kick off remote backup asynchronously (non-blocking)
53+
BackupClient.addToPersistQueue(BackupClient.Label.CHANNEL_MONITOR(channelId=channelId), serialized) { error ->
54+
if (error != null) {
55+
LdkEventEmitter.send(EventTypes.native_log, "Warning. Remote backup failed for new channel (${channelId}), but channel was persisted locally. $error")
56+
}
57+
}
58+
59+
return ChannelMonitorUpdateStatus.LDKChannelMonitorUpdateStatus_Completed
60+
}
61+
62+
// For updates: try remote backup first, then write locally on success (original behavior)
63+
BackupClient.addToPersistQueue(BackupClient.Label.CHANNEL_MONITOR(channelId=channelId), serialized) { error ->
3564
if (error != null) {
3665
LdkEventEmitter.send(EventTypes.native_log, "Failed to persist channel (${channelId}) to remote backup: $error")
3766
return@addToPersistQueue
3867
}
3968

4069
try {
41-
file.writeBytes(data.write())
70+
file.writeBytes(serialized)
4271
} catch (e: Exception) {
4372
//If this fails we can't do much but LDK will retry on startup
4473
LdkEventEmitter.send(EventTypes.native_log, "Failed to locally persist channel (${channelId}) to disk")
@@ -51,9 +80,6 @@ class LdkPersister {
5180
LdkEventEmitter.send(EventTypes.native_log, "Failed to update chain monitor with persisted channel (${channelId})")
5281
} else {
5382
LdkEventEmitter.send(EventTypes.native_log, "Persisted channel (${channelId}) to disk")
54-
if (isNew) {
55-
LdkEventEmitter.send(EventTypes.new_channel, body)
56-
}
5783
}
5884
}
5985

lib/ios/Classes/LdkPersist.swift

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,56 @@ class LdkPersister: Persist {
2727
}
2828

2929
let isNew = !FileManager().fileExists(atPath: channelStoragePath.path)
30+
let serialized = Data(data.write())
3031

3132
// If we're not remotely backing up no need to update status later
3233
if BackupClient.skipRemoteBackup {
33-
try Data(data.write()).write(to: channelStoragePath)
34+
try serialized.write(to: channelStoragePath)
3435
if isNew {
3536
LdkEventEmitter.shared.send(withEvent: .new_channel, body: body)
3637
}
3738
return ChannelMonitorUpdateStatus.Completed
3839
}
39-
40-
BackupClient.addToPersistQueue(.channelMonitor(id: channelIdHex), data.write()) { error in
40+
41+
// For new channels: write locally first to prevent loss if app is killed during backup
42+
// Then try remote backup asynchronously
43+
if isNew {
44+
try serialized.write(to: channelStoragePath)
45+
46+
// Notify chain monitor on main thread
47+
DispatchQueue.main.async {
48+
let res = Ldk.chainMonitor?.channelMonitorUpdated(
49+
fundingTxo: channelFundingOutpoint,
50+
completedUpdateId: data.getLatestUpdateId()
51+
)
52+
if let error = res?.getError() {
53+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Error. Failed to update chain monitor for channel (\(channelIdHex)) Error \(error.getValueType()).")
54+
} else {
55+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Persisted channel \(channelIdHex). Update ID: \(data.getLatestUpdateId())")
56+
LdkEventEmitter.shared.send(withEvent: .new_channel, body: body)
57+
}
58+
}
59+
60+
// Kick off remote backup asynchronously (non-blocking)
61+
BackupClient.addToPersistQueue(.channelMonitor(id: channelIdHex), [UInt8](serialized)) { error in
62+
if let error {
63+
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Warning. Remote backup failed for new channel (\(channelIdHex)), but channel was persisted locally. \(error.localizedDescription)")
64+
}
65+
}
66+
67+
return ChannelMonitorUpdateStatus.Completed
68+
}
69+
70+
// For updates: try remote backup first, then write locally on success (original behavior)
71+
BackupClient.addToPersistQueue(.channelMonitor(id: channelIdHex), [UInt8](serialized)) { error in
4172
if let error {
4273
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Error. Failed persist channel on remote server (\(channelIdHex)). \(error.localizedDescription)")
4374
return
4475
}
4576

46-
// Callback for when the persist queue queue entry is processed
77+
// Callback for when the persist queue entry is processed
4778
do {
48-
try Data(data.write()).write(to: channelStoragePath)
79+
try serialized.write(to: channelStoragePath)
4980
} catch {
5081
// If this fails we can't do much but LDK will retry on startup
5182
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Error. Failed to locally persist channel (\(channelIdHex)). \(error.localizedDescription)")
@@ -58,9 +89,6 @@ class LdkPersister: Persist {
5889
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Error. Failed to update chain monitor for channel (\(channelIdHex)) Error \(error.getValueType()).")
5990
} else {
6091
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Persisted channel \(channelIdHex). Update ID: \(data.getLatestUpdateId())")
61-
if isNew {
62-
LdkEventEmitter.shared.send(withEvent: .new_channel, body: body)
63-
}
6492
}
6593
}
6694

lib/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@synonymdev/react-native-ldk",
33
"title": "React Native LDK",
4-
"version": "0.0.160",
4+
"version": "0.0.161",
55
"description": "React Native wrapper for LDK",
66
"main": "./dist/index.js",
77
"types": "./dist/index.d.ts",

0 commit comments

Comments
 (0)