Skip to content

Commit 8c82b68

Browse files
committed
feat: support RN app migration
1 parent a043222 commit 8c82b68

File tree

12 files changed

+1499
-90
lines changed

12 files changed

+1499
-90
lines changed

app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ android {
4545
applicationId = "to.bitkit"
4646
minSdk = 28
4747
targetSdk = 36
48-
versionCode = 17
48+
versionCode = 160
4949
versionName = "0.0.17"
5050
testInstrumentationRunner = "to.bitkit.test.HiltTestRunner"
5151
vectorDrawables {

app/src/main/java/to/bitkit/repositories/ActivityRepo.kt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,14 @@ class ActivityRepo @Inject constructor(
110110
/**
111111
* Syncs `ldk-node` [PaymentDetails] list to `bitkit-core` [Activity] items.
112112
*/
113-
private suspend fun syncLdkNodePayments(payments: List<PaymentDetails>): Result<Unit> = runCatching {
114-
val channelIdsByTxId = findChannelsForPayments(payments)
115-
coreService.activity.syncLdkNodePaymentsToActivities(payments, channelIdsByTxId = channelIdsByTxId)
116-
}.onFailure { e ->
117-
Logger.error("Error syncing LDK payments:", e, context = TAG)
113+
suspend fun syncLdkNodePayments(payments: List<PaymentDetails>): Result<Unit> = withContext(bgDispatcher) {
114+
return@withContext runCatching {
115+
val channelIdsByTxId = findChannelsForPayments(payments)
116+
coreService.activity.syncLdkNodePaymentsToActivities(payments, channelIdsByTxId = channelIdsByTxId)
117+
notifyActivitiesChanged()
118+
}.onFailure { e ->
119+
Logger.error("Error syncing LDK payments:", e, context = TAG)
120+
}
118121
}
119122

120123
private suspend fun findChannelsForPayments(
@@ -666,6 +669,15 @@ class ActivityRepo @Inject constructor(
666669
}
667670
}
668671

672+
suspend fun markAllUnseenActivitiesAsSeen(): Result<Unit> = withContext(bgDispatcher) {
673+
return@withContext runCatching {
674+
coreService.activity.markAllUnseenActivitiesAsSeen()
675+
notifyActivitiesChanged()
676+
}.onFailure { e ->
677+
Logger.error("Failed to mark all activities as seen: $e", e, context = TAG)
678+
}
679+
}
680+
669681
// MARK: - Development/Testing Methods
670682

671683
/**

app/src/main/java/to/bitkit/repositories/LightningRepo.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,11 @@ class LightningRepo @Inject constructor(
167167
walletIndex: Int,
168168
customServerUrl: String? = null,
169169
customRgsServerUrl: String? = null,
170+
channelMigration: org.lightningdevkit.ldknode.ChannelDataMigration? = null,
170171
) = withContext(bgDispatcher) {
171172
return@withContext try {
172173
val trustedPeers = getTrustedPeersFromBlocktank()
173-
lightningService.setup(walletIndex, customServerUrl, customRgsServerUrl, trustedPeers)
174+
lightningService.setup(walletIndex, customServerUrl, customRgsServerUrl, trustedPeers, channelMigration)
174175
Result.success(Unit)
175176
} catch (e: Throwable) {
176177
Logger.error("Node setup error", e, context = TAG)
@@ -196,6 +197,7 @@ class LightningRepo @Inject constructor(
196197
customServerUrl: String? = null,
197198
customRgsServerUrl: String? = null,
198199
eventHandler: NodeEventHandler? = null,
200+
channelMigration: org.lightningdevkit.ldknode.ChannelDataMigration? = null,
199201
): Result<Unit> = withContext(bgDispatcher) {
200202
if (_isRecoveryMode.value) {
201203
return@withContext Result.failure(RecoveryModeException())
@@ -214,7 +216,7 @@ class LightningRepo @Inject constructor(
214216

215217
// Setup if needed
216218
if (lightningService.node == null) {
217-
val setupResult = setup(walletIndex, customServerUrl, customRgsServerUrl)
219+
val setupResult = setup(walletIndex, customServerUrl, customRgsServerUrl, channelMigration)
218220
if (setupResult.isFailure) {
219221
_lightningState.update {
220222
it.copy(
@@ -264,6 +266,7 @@ class LightningRepo @Inject constructor(
264266
shouldRetry = false,
265267
customServerUrl = customServerUrl,
266268
customRgsServerUrl = customRgsServerUrl,
269+
channelMigration = channelMigration,
267270
)
268271
} else {
269272
Logger.error("Node start error", e, context = TAG)
@@ -311,6 +314,19 @@ class LightningRepo @Inject constructor(
311314
}
312315
}
313316

317+
suspend fun restart(): Result<Unit> = withContext(bgDispatcher) {
318+
stop().onFailure {
319+
Logger.error("Failed to stop node during restart", it, context = TAG)
320+
return@withContext Result.failure(it)
321+
}
322+
delay(500)
323+
start(shouldRetry = false).onFailure {
324+
Logger.error("Failed to start node during restart", it, context = TAG)
325+
return@withContext Result.failure(it)
326+
}
327+
Result.success(Unit)
328+
}
329+
314330
suspend fun sync(): Result<Unit> = executeWhenNodeRunning("sync") {
315331
// If sync is in progress, mark pending and skip
316332
if (!syncMutex.tryLock()) {

app/src/main/java/to/bitkit/services/CoreService.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,35 @@ class ActivityService(
11121112
markActivityAsSeen(activity.id, seenAt)
11131113
}
11141114

1115+
suspend fun markAllUnseenActivitiesAsSeen() = ServiceQueue.CORE.background {
1116+
val timestamp = (System.currentTimeMillis() / 1000).toULong()
1117+
val activities = getActivities(
1118+
filter = ActivityFilter.ALL,
1119+
txType = null,
1120+
tags = null,
1121+
search = null,
1122+
minDate = null,
1123+
maxDate = null,
1124+
limit = null,
1125+
sortDirection = null,
1126+
)
1127+
1128+
for (activity in activities) {
1129+
val isSeen = when (activity) {
1130+
is Activity.Onchain -> activity.v1.seenAt != null
1131+
is Activity.Lightning -> activity.v1.seenAt != null
1132+
}
1133+
1134+
if (!isSeen) {
1135+
val activityId = when (activity) {
1136+
is Activity.Onchain -> activity.v1.id
1137+
is Activity.Lightning -> activity.v1.id
1138+
}
1139+
markActivityAsSeen(activityId, timestamp)
1140+
}
1141+
}
1142+
}
1143+
11151144
suspend fun getBoostTxDoesExist(boostTxIds: List<String>): Map<String, Boolean> {
11161145
return ServiceQueue.CORE.background {
11171146
val doesExistMap = mutableMapOf<String, Boolean>()

app/src/main/java/to/bitkit/services/LightningService.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import org.lightningdevkit.ldknode.Bolt11InvoiceDescription
1818
import org.lightningdevkit.ldknode.BuildException
1919
import org.lightningdevkit.ldknode.Builder
2020
import org.lightningdevkit.ldknode.ChannelConfig
21+
import org.lightningdevkit.ldknode.ChannelDataMigration
2122
import org.lightningdevkit.ldknode.ChannelDetails
2223
import org.lightningdevkit.ldknode.CoinSelectionAlgorithm
2324
import org.lightningdevkit.ldknode.Config
@@ -79,6 +80,7 @@ class LightningService @Inject constructor(
7980
customServerUrl: String? = null,
8081
customRgsServerUrl: String? = null,
8182
trustedPeers: List<PeerDetails>? = null,
83+
channelMigration: ChannelDataMigration? = null,
8284
) {
8385
Logger.debug("Building node…")
8486

@@ -88,6 +90,7 @@ class LightningService @Inject constructor(
8890
customServerUrl,
8991
customRgsServerUrl,
9092
config,
93+
channelMigration,
9194
)
9295

9396
Logger.info("LDK node setup")
@@ -123,11 +126,21 @@ class LightningService @Inject constructor(
123126
customServerUrl: String?,
124127
customRgsServerUrl: String?,
125128
config: Config,
129+
channelMigration: ChannelDataMigration? = null,
126130
): Node = ServiceQueue.LDK.background {
127131
val builder = Builder.fromConfig(config).apply {
128132
setCustomLogger(LdkLogWriter())
129133
configureChainSource(customServerUrl)
130134
configureGossipSource(customRgsServerUrl)
135+
136+
if (channelMigration != null) {
137+
setChannelDataMigration(channelMigration)
138+
Logger.info(
139+
"Applied channel migration: ${channelMigration.channelMonitors.size} monitors",
140+
context = "Migration"
141+
)
142+
}
143+
131144
setEntropyBip39Mnemonic(
132145
mnemonic = keychain.loadString(Keychain.Key.BIP39_MNEMONIC.name) ?: throw ServiceError.MnemonicNotFound,
133146
passphrase = keychain.loadString(Keychain.Key.BIP39_PASSPHRASE.name),

0 commit comments

Comments
 (0)