Skip to content

fix: add wallet-scoped backup migrations #113

Description

@ovitrif

Problem

Android and iOS backup restore currently decode app-owned backup JSON directly into generated Core model types before calling Core.

That is brittle for wallet-scoped activity changes. If a backup was written before walletId existed, the app can fail while decoding Activity, ActivityTags, or PreActivityMetadata, before Core gets a chance to apply defaults or migrate the data.

Current app restore paths:

  • Android ActivityBackupV1 contains activities: List<Activity>, activityTags: List<ActivityTags>, and closedChannels: List<ClosedChannelDetails>. BackupRepo decodes that payload, then ActivityRepo.restoreFromBackup() calls Core upsertActivities, upsertTags, and upsertClosedChannels.
  • Android MetadataBackupV1 contains tagMetadata: List<PreActivityMetadata>. BackupRepo decodes that payload, then upserts the metadata through Core.
  • iOS has the same backup shapes in ActivityBackupV1 and MetadataBackupV1. BackupService decodes them with JSONDecoder, then calls Core upsertList, upsertTags, upsertClosedChannelList, and upsertPreActivityMetadata.
  • Android's normal wallet id is bitkit via ActivityWalletType.BITKIT.id; Trezor wallet ids are trezor:{sha256(sorted xpubs joined by \n)}.
  • iOS uses WalletScope.default = getDefaultWalletId() for the normal wallet; Trezor wallet ids are derived in HwWalletId with the same trezor:{sha256(sorted xpubs joined by \n)} shape when xpubs are available.
  • Both apps already pass wallet ids to Core for hardware-wallet activity reads/writes/deletes. The backup problem is the typed decode boundary, not a need for platform-specific JSON patching.

TransactionDetails is not in the current Android/iOS activity backup envelopes. It still needs DB migration coverage because Core persists it and scopes it by (walletId, txId).

Requested change

Add Core-owned migration support at both Core-owned boundaries:

  • migrate the activity DB on init, because Core owns the persisted activity layer.
  • migrate Core-owned JSON slices embedded in Android/iOS VSS backup envelopes, so the apps can rewrite those backups with current entries.

The apps should not hand-edit Core model JSON or decode legacy Core models before migration.

Required helpers:

  • migrate activity arrays from backup JSON into current Activity entries.
  • migrate activity tag arrays into current ActivityTags entries.
  • migrate pre-activity metadata arrays into current PreActivityMetadata entries.
  • migrate transaction details JSON if/when it is included in backups.
  • provide a way for apps to get current/migrated backup JSON for the Core-owned fields so VSS can be updated after migration.

Migration rules:

  • Missing walletId means DEFAULT_WALLET_ID / bitkit.
  • Empty walletId should normalize to DEFAULT_WALLET_ID only for legacy migration input. Normal current writes should still reject empty wallet ids.
  • Preserve non-default wallet ids such as trezor:{hash}.
  • Preserve activity ids, txids, tags, closed channel data, timestamps, contact fields, seen state, and pre-activity metadata fields.
  • Do not require Android or iOS to hand-edit Core model JSON.

DB migration

Core should own activity DB migration.

Required DB behavior:

  • On init, tables without wallet_id migrate to wallet-scoped primary keys.
  • Existing activity rows, tags, pre-activity metadata, and transaction details without a wallet scope migrate to DEFAULT_WALLET_ID.
  • activity_tags, pre_activity_metadata, and transaction_details use wallet-scoped keys after migration.
  • Migration preserves user metadata where possible: tags, contacts, seen state, and pre-activity metadata.
  • There is no separate cleanup/wipe requirement for dev-only pre-release DB states in this issue.

App/VSS responsibility

Android and iOS own the VSS backup envelopes. Core owns the activity data inside those envelopes.

Expected app-side flow:

  1. Read the app-owned VSS backup envelope as raw JSON.
  2. Keep app-owned fields, such as cache/settings/pubky session data, in the app layer.
  3. Pass the raw Core-owned activity/metadata JSON to Core migration/import APIs.
  4. Let Core decode, migrate, and persist Core-owned activity data into the activity DB.
  5. Ask Core for the migrated/current Core-owned backup entries.
  6. Write the updated app-owned VSS backup envelope with those migrated entries.

The app responsibility is to refresh VSS with v2/current Core entries after migration. The app should not own the Core model migration itself.

Search and scoping checks

After DB migration or backup migration:

  • walletId = DEFAULT_WALLET_ID returns only the normal Bitkit wallet rows.
  • walletId = trezor:{hash} returns only that hardware wallet's rows.
  • global list/search calls that intentionally pass no wallet id can merge wallets, but must not duplicate a logical row.
  • activity id and txid collisions across wallet ids stay scoped.
  • tags and transaction details are looked up with the same wallet scope as the activity.

Acceptance criteria

  • Android-shaped backup JSON with activities, activityTags, and tagMetadata missing walletId migrates without app-side JSON patching.
  • iOS-shaped backup JSON with the same Core-owned fields missing walletId migrates without app-side JSON patching.
  • Migrated legacy backup rows use DEFAULT_WALLET_ID.
  • Existing non-default wallet ids survive backup migration unchanged.
  • Core DB migration covers activities, onchain_activity, lightning_activity, activity_tags, pre_activity_metadata, and transaction_details.
  • Core exposes generated Android and iOS binding APIs for migrating/importing Core-owned backup JSON and exporting migrated/current backup entries.
  • Tests cover legacy backup JSON, current wallet-scoped backup JSON, DB migration from tables without wallet_id, VSS backup rewrite data, and scoped lookup/search/tag/detail behavior after migration.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions