Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions app/src/main/java/to/bitkit/services/MigrationService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class MigrationService @Inject constructor(
private const val RN_PENDING_METADATA_KEY = "rnPendingMetadata"
private const val RN_PENDING_TRANSFERS_KEY = "rnPendingTransfers"
private const val RN_PENDING_BOOSTS_KEY = "rnPendingBoosts"
private const val RN_DID_ATTEMPT_PEER_RECOVERY_KEY = "rnDidAttemptMigrationPeerRecovery"
private const val OPENING_CURLY_BRACE = "{"
private const val MMKV_ROOT = "persist:root"
private const val RN_WALLET_NAME = "wallet0"
Expand Down Expand Up @@ -1251,6 +1252,44 @@ class MigrationService @Inject constructor(
Logger.info("RN migration completed, marked for post-migration sync", context = TAG)
}

private suspend fun isRnMigrationCompleted(): Boolean {
val key = stringPreferencesKey(RN_MIGRATION_COMPLETED_KEY)
return rnMigrationStore.data.first()[key] == "true"
}

private suspend fun didAttemptPeerRecovery(): Boolean {
val key = stringPreferencesKey(RN_DID_ATTEMPT_PEER_RECOVERY_KEY)
return rnMigrationStore.data.first()[key] == "true"
}

private suspend fun setDidAttemptPeerRecovery() {
val key = stringPreferencesKey(RN_DID_ATTEMPT_PEER_RECOVERY_KEY)
rnMigrationStore.edit { it[key] = "true" }
}

suspend fun tryFetchMigrationPeersFromBackup(): List<String> {
if (!isRnMigrationCompleted()) return emptyList()
if (didAttemptPeerRecovery()) return emptyList()

setDidAttemptPeerRecovery()

return runCatching {
val data = rnBackupClient.retrieve("peers", fileGroup = "ldk") ?: return emptyList()
val peers = json.decodeFromString<List<BackupPeerEntry>>(String(data))
if (peers.isEmpty()) return emptyList()

val trustedIds = Env.trustedLnPeers.map { it.nodeId }.toSet()
val uris = peers
.filter { it.pubKey !in trustedIds }
.map { "${it.pubKey}@${it.address}:${it.port}" }

Logger.info("Migration peer recovery: fetched ${uris.size} peer(s) from remote backup", context = TAG)
uris
}.onFailure {
Logger.warn("Migration peer recovery failed (will not retry)", it, context = TAG)
}.getOrDefault(emptyList())
}

suspend fun cleanupAfterMigration() {
clearPersistedMigrationData()
setNeedsPostMigrationSync(false)
Expand Down Expand Up @@ -2076,6 +2115,13 @@ data class RNWidgetsWithOptions(
val widgetOptions: Map<String, ByteArray>,
)

@Serializable
data class BackupPeerEntry(
val pubKey: String,
val address: String,
val port: UShort,
)

private val Context.rnMigrationDataStore: DataStore<Preferences> by preferencesDataStore("rn_migration")
private val Context.rnKeychainDataStore: DataStore<Preferences> by preferencesDataStore("RN_KEYCHAIN")

Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/to/bitkit/viewmodels/WalletViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import org.lightningdevkit.ldknode.PeerDetails
import to.bitkit.R
import to.bitkit.data.SettingsStore
import to.bitkit.di.BgDispatcher
import to.bitkit.ext.of
import to.bitkit.models.Toast
import to.bitkit.repositories.BackupRepo
import to.bitkit.repositories.BlocktankRepo
Expand Down Expand Up @@ -291,6 +292,7 @@ class WalletViewModel @Inject constructor(
migrationService.consumePendingChannelMigration()
}
walletRepo.setWalletExistsState()
connectMigrationPeers()
walletRepo.syncBalances()
if (_restoreState.value.isIdle()) {
walletRepo.refreshBip21()
Expand All @@ -304,6 +306,18 @@ class WalletViewModel @Inject constructor(
}
}

private suspend fun connectMigrationPeers() {
val peerUris = migrationService.tryFetchMigrationPeersFromBackup()
for (uri in peerUris) {
runCatching {
val peer = PeerDetails.of(uri)
lightningRepo.connectPeer(peer)
}.onFailure {
Logger.error("Failed to connect migration peer: $uri", it, context = TAG)
}
}
}

fun stop() {
if (!walletExists) return

Expand Down
1 change: 1 addition & 0 deletions app/src/test/java/to/bitkit/ui/WalletViewModelTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class WalletViewModelTest : BaseUnitTest() {
whenever(walletRepo.walletState).thenReturn(walletState)
whenever(lightningRepo.lightningState).thenReturn(lightningState)
whenever(migrationService.isMigrationChecked()).thenReturn(true)
whenever(migrationService.tryFetchMigrationPeersFromBackup()).thenReturn(emptyList())
whenever(connectivityRepo.isOnline).thenReturn(isOnline)

sut = WalletViewModel(
Expand Down
Loading