-
Notifications
You must be signed in to change notification settings - Fork 1
feat: support RN app migration #561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements migration support for transitioning users from the React Native Bitkit app to the native Android version. The migration preserves critical wallet data including balances, channels, activities, settings, widgets, and tags/notes while maintaining a recovery path if issues occur.
Key changes:
- Adds comprehensive RN data migration logic handling MMKV storage, keychain data, and LDK channel state
- Implements migration loading UI with timeout protection and error handling
- Coordinates migration flow across wallet initialization, node startup, and data synchronization phases
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| gradle/libs.versions.toml | Updates ldk-node-android dependency to v0.7.0-rc.2 |
| app/build.gradle.kts | Bumps version code from 17 to 160 |
| MigrationService.kt | Core migration service handling RN data extraction, decryption, and native app data transformation |
| MmkvParser.kt | Binary parser for React Native MMKV storage format |
| MigrationLoadingScreen.kt | Loading screen UI displayed during migration process |
| WalletViewModel.kt | Orchestrates migration check and execution during wallet initialization |
| AppViewModel.kt | Handles migration completion, sync coordination, and timeout management |
| MainActivity.kt | Integrates migration loading screen into app navigation flow |
| LightningService.kt | Adds channel migration support to LDK node setup |
| LightningRepo.kt | Extends node start/setup to accept channel migration data and adds restart capability |
| CoreService.kt | Adds method to mark all unseen activities as seen during migration |
| ActivityRepo.kt | Exposes payment sync and bulk activity marking methods for migration |
781737a to
8c82b68
Compare
b30f0d8 to
db68175
Compare
| walletIndex: Int, | ||
| customServerUrl: String? = null, | ||
| customRgsServerUrl: String? = null, | ||
| channelMigration: org.lightningdevkit.ldknode.ChannelDataMigration? = null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could add org.lightningdevkit.ldknode to imports
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
| val bitkitFiles = rnBackupClient.listFiles(fileGroup = "bitkit")?.list ?: emptyList() | ||
| retrieveAndApplyBitkitBackups(bitkitFiles) | ||
| markMigrationCompleted() | ||
| } catch (e: Exception) { |
Check warning
Code scanning / detekt
The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled. Warning
| ) | ||
|
|
||
| var latestTimestamp: ULong? = null | ||
| for (label in labels) { |
Check warning
Code scanning / detekt
The loop contains more than one break or continue statement. The code should be refactored to increase readability. Warning
| val mnemonicBytes = mnemonic.toByteArray(Charsets.UTF_8) | ||
| val salt = ("mnemonic" + (passphrase ?: "")).toByteArray(Charsets.UTF_8) | ||
| val generator = PKCS5S2ParametersGenerator(SHA512Digest()) | ||
| generator.init(mnemonicBytes, salt, 2048) |
Check warning
Code scanning / detekt
Report magic numbers. Magic number is a numeric literal that is not defined as a constant and hence it's unclear what the purpose of this number is. It's better to declare such numbers as constants and give them a proper name. By default, -1, 0, 1, and 2 are not considered to be magic numbers. Warning
| val salt = ("mnemonic" + (passphrase ?: "")).toByteArray(Charsets.UTF_8) | ||
| val generator = PKCS5S2ParametersGenerator(SHA512Digest()) | ||
| generator.init(mnemonicBytes, salt, 2048) | ||
| return (generator.generateDerivedParameters(512) as KeyParameter).key |
Check warning
Code scanning / detekt
Report magic numbers. Magic number is a numeric literal that is not defined as a constant and hence it's unclear what the purpose of this number is. It's better to declare such numbers as constants and give them a proper name. By default, -1, 0, 1, and 2 are not considered to be magic numbers. Warning
| _restoreState.update { RestoreState.InProgress.Metadata } | ||
| try { | ||
| restoreFromMostRecentBackup() | ||
| } catch (e: Exception) { |
Check warning
Code scanning / detekt
The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled. Warning
| import javax.inject.Singleton | ||
|
|
||
| private val Context.rnMigrationDataStore: | ||
| androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> by preferencesDataStore( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import androidx.datastore.core.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
| val words = mnemonic.split(" ").filter { it.isNotBlank() } | ||
| if (words.size != MNEMONIC_WORD_COUNT_12 && words.size != MNEMONIC_WORD_COUNT_24) { | ||
| throw to.bitkit.utils.AppError( | ||
| "Recovery phrase format is invalid. Please use your 12 or 24 word recovery phrase to restore manually." | ||
| ) | ||
| } | ||
|
|
||
| keychain.saveString(Keychain.Key.BIP39_MNEMONIC.name, mnemonic) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could validate the mnemonics phrase too instead of just the count
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
app/build.gradle.kts
Outdated
| minSdk = 28 | ||
| targetSdk = 36 | ||
| versionCode = 17 | ||
| versionCode = 160 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the last version of RN is 161, so should be 162 here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
|
Failed restoring the channels on local migration, I'll try again, just in case Native data RN data |
Second attempt, still failed. also noticed that the PIN was not imported |
| ) | ||
| } | ||
|
|
||
| private suspend fun completeMigration() { |
Check warning
Code scanning / detekt
One method should have one responsibility. Long methods tend to handle many things at once. Prefer smaller methods to make them easier to understand. Warning
| import to.bitkit.repositories.SyncSource | ||
| import to.bitkit.repositories.WalletRepo | ||
| import to.bitkit.services.MigrationService | ||
| import to.bitkit.services.PendingChannelMigration |
Check warning
Code scanning / detekt
Detects unused imports Warning
| companion object { | ||
| private const val TAG = "WalletViewModel" | ||
| private val RESTORE_WAIT_TIMEOUT = 30.seconds | ||
| private const val NODE_RESTART_DELAY_MS = 500L |
Check warning
Code scanning / detekt
Property is unused and should be removed. Warning
|
Tested: On RN
On Android
From the warnings, the PR needs a cleanup. After that I'll do a last review |
This PR adds support for migrating an existing React Native Bitkit into the Bitkit native app.
The migration passes:
Balances
Channels
Activities
Settings
Widgets
Tags and notes
Closed Channels
It also adds support for restoring a wallet created in RN app which was later deleted. Ie. create wallet in RN, use it, delete the app, install native, recover using mnemonic.
What's still needed:
New design for the loading screen and success toast of migration