Skip to content

Commit 6187a9f

Browse files
authored
Merge pull request #1064 from synonymdev/fix/backup-wallet-id-migration
fix: migrate legacy backups missing wallet id on restore
2 parents cab49d4 + 9808d9e commit 6187a9f

2 files changed

Lines changed: 42 additions & 3 deletions

File tree

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

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package to.bitkit.repositories
22

33
import android.content.Context
4+
import com.synonym.bitkitcore.migrateBackupActivitiesJson
5+
import com.synonym.bitkitcore.migrateBackupActivityTagsJson
6+
import com.synonym.bitkitcore.migrateBackupPreActivityMetadataJson
47
import dagger.hilt.android.qualifiers.ApplicationContext
58
import kotlinx.coroutines.CoroutineDispatcher
69
import kotlinx.coroutines.CoroutineScope
@@ -26,6 +29,9 @@ import kotlinx.coroutines.launch
2629
import kotlinx.coroutines.withContext
2730
import kotlinx.coroutines.withTimeout
2831
import kotlinx.serialization.Serializable
32+
import kotlinx.serialization.json.JsonArray
33+
import kotlinx.serialization.json.JsonObject
34+
import kotlinx.serialization.json.jsonObject
2935
import to.bitkit.R
3036
import to.bitkit.data.AppDb
3137
import to.bitkit.data.CacheStore
@@ -558,7 +564,11 @@ class BackupRepo @Inject constructor(
558564

559565
val result = runCatching {
560566
performRestore(BackupCategory.METADATA) { dataBytes ->
561-
val parsed = json.decodeFromString<MetadataBackupV1>(String(dataBytes))
567+
val migrated = migrateCoreOwnedBackupFields(
568+
String(dataBytes),
569+
mapOf("tagMetadata" to ::migrateBackupPreActivityMetadataJson),
570+
)
571+
val parsed = json.decodeFromString<MetadataBackupV1>(migrated)
562572
val cleanCache = parsed.cache.resetBip21() // Force address rotation
563573
cacheStore.update { cleanCache }
564574
Logger.debug("Restored caches: ${jsonLogOf(parsed.cache.copy(cachedRates = emptyList()))}", TAG)
@@ -594,7 +604,14 @@ class BackupRepo @Inject constructor(
594604
parsed.createdAt
595605
}
596606
performRestore(BackupCategory.ACTIVITY) { dataBytes ->
597-
val parsed = json.decodeFromString<ActivityBackupV1>(String(dataBytes))
607+
val migrated = migrateCoreOwnedBackupFields(
608+
String(dataBytes),
609+
mapOf(
610+
"activities" to ::migrateBackupActivitiesJson,
611+
"activityTags" to ::migrateBackupActivityTagsJson,
612+
),
613+
)
614+
val parsed = json.decodeFromString<ActivityBackupV1>(migrated)
598615
activityRepo.restoreFromBackup(parsed)
599616
parsed.createdAt
600617
}
@@ -668,6 +685,28 @@ class BackupRepo @Inject constructor(
668685
}
669686
}
670687

688+
/**
689+
* Fill in wallet ids that predate wallet-scoped activity data before a backup
690+
* envelope is decoded. Each Core-owned array field is handed to the matching
691+
* Core migration helper as raw JSON, so the app never edits Core model JSON
692+
* itself. Records that already carry a wallet id are left unchanged, so this
693+
* is safe to run on current backups too.
694+
*/
695+
private fun migrateCoreOwnedBackupFields(
696+
raw: String,
697+
fieldMigrations: Map<String, (String) -> String>,
698+
): String {
699+
val root = json.parseToJsonElement(raw).jsonObject
700+
val patched = root.toMutableMap()
701+
for ((field, migrate) in fieldMigrations) {
702+
val element = root[field]
703+
if (element is JsonArray) {
704+
patched[field] = json.parseToJsonElement(migrate(element.toString()))
705+
}
706+
}
707+
return JsonObject(patched).toString()
708+
}
709+
671710
private suspend fun performRestore(
672711
category: BackupCategory,
673712
restoreAction: suspend (dataBytes: ByteArray) -> Long,

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ activity-compose = { module = "androidx.activity:activity-compose", version = "1
2121
appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.1" }
2222
barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version = "17.3.0" }
2323
biometric = { module = "androidx.biometric:biometric", version = "1.4.0-alpha05" }
24-
bitkit-core = { module = "com.synonym:bitkit-core-android", version = "0.3.9" }
24+
bitkit-core = { module = "com.synonym:bitkit-core-android", version = "0.4.0" }
2525
paykit = { module = "com.synonym:paykit-android", version = "0.1.0-rc31" }
2626
bouncycastle-provider-jdk = { module = "org.bouncycastle:bcprov-jdk18on", version = "1.83" }
2727
camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camera" }

0 commit comments

Comments
 (0)