Skip to content

feat: bump core to 0.3.9 and hw reliability#1062

Open
piotr-iohk wants to merge 11 commits into
masterfrom
feat/wallet-scoped-core-0.3.9
Open

feat: bump core to 0.3.9 and hw reliability#1062
piotr-iohk wants to merge 11 commits into
masterfrom
feat/wallet-scoped-core-0.3.9

Conversation

@piotr-iohk

@piotr-iohk piotr-iohk commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Part of #1030

This PR:

  1. Bumps bitkit-core from 0.1.75 to 0.3.9 and migrates CoreService, hardware wallet, activity, and migration paths to wallet-scoped APIs.
  2. Improves BLE Trezor reconnect reliability for Transfer to Spending after app restart or device sleep.
  3. Suppresses empty error toasts when the user cancels signing on the Trezor.
  4. Serializes transfer reconnect with in-flight auto-reconnect and scopes sign-screen warm-up to the selected hardware device.

Description

The bitkit-core upgrade brings wallet-scoped storage and Trezor stack improvements from the 0.3.x line (device-busy handling, typed session errors, transport callback updates). Android wiring moves default wallet identity through WalletScope, routes CoreService calls through scoped wallet ids, and updates hardware wallet, activity, migration, and dev Trezor tooling to match.

On top of the core bump, this branch hardens hardware transfer UX:

  • Transfer to Spending retries BLE reconnect with backoff instead of failing immediately when the Trezor is asleep after restart.
  • Opening Trezor Connect on the sign screen warms up that specific BLE device instead of triggering a global foreground reconnect for any known wallet.
  • Connect paths wait for an in-flight auto-reconnect before starting another attempt, reducing "connection already in progress" races.
  • User cancellation during signing (UserCancelled, PinCancelled, PassphraseCancelled) is treated as a silent no-op: no error toast, no stale-session disconnect churn.

Out of scope for this PR (still tracked elsewhere):

Preview

Before (master / 0.1.75) After (this branch)
before.mov
after.mov

Upload your local recordings into the table and below when opening the draft PR on GitHub.

QA Notes

Manual Tests

  • 1. Pair BLE Trezor → force-stop app → reopen → Savings with HW balance → Transfer to Spending → Open Trezor Connect: reconnect succeeds, PIN/sign flow completes (compare with before video on master).
  • 2. Transfer to Spending → sign screen open with BLE Trezor asleep: warm-up reconnect targets the transfer device without spurious toasts for unrelated wallets.
  • 3. Transfer to Spending → start sign → cancel on Trezor: no error toast; tapping Open Trezor Connect again resumes normally.
  • 4a. regression: Settings → connect known USB Trezor → get address / watch balance: still works after core bump.
    • 4b. regression: Settings → connect known BLE Trezor: pairing, PIN, and balance watch still work.
  • 5. regression: Upgrade from existing wallet (Room migration path): app opens, balances, activity, and hardware wallet tiles unchanged.
  • 6. regression: Normal Lightning send/receive and on-chain activity: unaffected by wallet-scoped core migration.

Automated Checks

  • Unit tests added: BLE reconnect retry and warm-up behavior in TrezorRepoTest.kt; Trezor cancel detection in TrezorExceptionExtTest.kt; cancel handling in TransferViewModelTest.kt and HwWalletRepoTest.kt.
  • Unit tests modified: wallet-scoped hardware wallet and activity assertions in HwWalletRepoTest.kt, ActivityRepoTest.kt, PreActivityMetadataRepoTest.kt, TrezorViewModelTest.kt, and related view model tests.
  • Local: just test file "to.bitkit.repositories.TrezorRepoTest", just test file "to.bitkit.repositories.HwWalletRepoTest", just test file "to.bitkit.viewmodels.TransferViewModelTest", just test file "to.bitkit.ext.TrezorExceptionExtTest".
  • Changelog fragment: add one user-facing fragment before publish (hw transfer reliability fixes).
  • CI: standard compile, unit test, and detekt checks run by the PR bot.

@piotr-iohk piotr-iohk marked this pull request as ready for review July 3, 2026 18:42
@piotr-iohk piotr-iohk marked this pull request as draft July 3, 2026 18:42

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

detekt found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR bumps bitkit-core from 0.1.75 to 0.3.9 and migrates all activity, hardware-wallet, and backup APIs to wallet-scoped identifiers, while adding BLE Trezor reconnect reliability (retry with backoff, warm-up pre-connect, in-flight serialization) and silent suppression of user-cancellation toasts on the sign screen.

  • Wallet-scoped API migration: WalletScope.default and HwWalletId.derive thread a wallet ID through CoreService, ActivityService, MigrationService, LightningRepo, WalletRepo, and PreActivityMetadataRepo. BackupRestoreCompat injects the default wallet ID into legacy backup JSON so old backups load correctly under the new schema.
  • BLE reconnect hardening: TrezorRepo.ensureConnected now delegates BLE devices to reconnectKnownBluetoothDevice, which retries up to four times with growing delays, serializes with any in-flight auto-reconnect via awaitInFlightConnect, and guards disconnectStaleSession against clearing an unrelated connected device.
  • Cancel-suppression: TrezorExceptionExt.isTrezorUserCancellation walks the cause chain to detect UserCancelled/PinCancelled/PassphraseCancelled; TransferViewModel, HwWalletRepo, AppViewModel, and ToastEventBus all check it before showing an error toast or disconnecting the stale session.

Confidence Score: 5/5

Safe to merge. The wallet-scoped API migration is mechanical and comprehensive, the BLE retry logic is well-bounded (4 attempts, growing delays), and cancel suppression is guarded by a cause-chain walk rather than a simple type check.

The three main workstreams — wallet-scoped core migration, BLE reconnect hardening, and user-cancellation suppression — all have matching unit tests that exercise the new code paths, including edge cases like retry-until-scan-succeeds, cancel during reconnect, and disconnectStaleSession guarding against clearing an unrelated device. No logic errors were found in the retry loop bounds, the in-flight serialization, or the backup compat layer.

No files require special attention. The BackupRestoreCompat heuristics (field-presence checks for walletId injection) are worth keeping an eye on if the backup schema evolves, but they are correct for the current format.

Important Files Changed

Filename Overview
app/src/main/java/to/bitkit/repositories/TrezorRepo.kt Major expansion: adds BLE retry loop (reconnectKnownBluetoothDevice), in-flight connect serialization (awaitInFlightConnect / waitForConnectAttempt), warm-up pre-connect, resolveKnownReconnectDevice, HwWalletId-based wallet ID derivation, and a guard in disconnectStaleSession to avoid clearing an unrelated connected device. Well-tested with new TrezorRepoTest cases.
app/src/main/java/to/bitkit/repositories/HwWalletRepo.kt Drops HistoryTransaction in favour of Activity from WatcherEvent; adds walletId scoping to watchers; replaces multi-tx merge logic (received/sent aggregation) with a new mergedActivity() that works directly with OnchainActivity values from the core; cancel-guards disconnectStaleSession on signing failure.
app/src/main/java/to/bitkit/viewmodels/TransferViewModel.kt Adds warmUpHardwareConnection, isTrezorUserCancellation checks at two points in handleHardwareTransferFailure, and a BLE-aware error message (INFO toast instead of ERROR for known BLE devices). New TransferViewModelTest cases cover cancel and reconnect-cancel paths.
app/src/main/java/to/bitkit/ext/TrezorExceptionExt.kt New file: isTrezorUserCancellation() walks the full cause chain to detect UserCancelled, PinCancelled, PassphraseCancelled. Tested exhaustively including wrapped exceptions.
app/src/main/java/to/bitkit/models/WalletScope.kt New singleton that lazily resolves the default wallet ID via the core native call. testOverride allows unit tests to sidestep the real core. Production relies on DI graph ensuring core is initialised first.
app/src/main/java/to/bitkit/ext/BackupRestoreCompat.kt New file: normalizeLegacyWalletIdsInBackupJson injects WalletScope.default walletId into old backup JSON objects via structural heuristics (field presence checks). Only patches objects that don't already have walletId, ensuring idempotency on new-format backups.
app/src/main/java/to/bitkit/services/CoreService.kt ActivityService now holds a private walletId = WalletScope.default and passes it to every core API call. Mechanical but comprehensive migration.
app/src/main/java/to/bitkit/services/MigrationService.kt Adds WalletScope.default to ActivityTags, LightningActivity, and OnchainActivity constructors during migration paths, keeping Room upgrade state consistent with the new wallet-scoped schema.
app/src/main/java/to/bitkit/ui/shared/toast/ToastEventBus.kt send(Throwable) now silently drops user-cancellation errors before emitting a toast, and falls back to 'Unknown error' when the error message is blank.
app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt Adds LaunchedEffect(deviceId) that calls warmUpHardwareConnection to pre-connect the specific BLE device when the sign screen opens, avoiding a global foreground reconnect.
app/src/main/java/to/bitkit/services/TrezorTransport.kt Mechanical: adds errorCode = null to every TrezorTransportWriteResult / TrezorTransportReadResult construction site to match the updated core 0.3.9 struct signature. No logic changes.
app/src/main/java/to/bitkit/models/HwWalletId.kt New thin wrapper around core's deriveWalletId; requires non-empty xpubs and passes only the values (not keys) to the native function.
gradle/libs.versions.toml Bumps bitkit-core from 0.1.75 to 0.3.9; single-line change.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant UI as SpendingHwSignScreen
    participant TVM as TransferViewModel
    participant HWR as HwWalletRepo
    participant TR as TrezorRepo
    participant BLE as TrezorService (BLE)

    UI->>TVM: warmUpHardwareConnection(deviceId)
    TVM->>HWR: warmUpKnownDevice(deviceId)
    HWR->>TR: warmUpKnownDevice(deviceId)
    TR-->>TR: skip if connected / in-progress / non-BLE
    TR->>TR: ensureConnected(deviceId) [background]

    UI->>TVM: onTransferToSpendingHwConfirm(order, deviceId)
    TVM->>HWR: ensureConnected(deviceId)
    HWR->>TR: ensureConnected(deviceId)
    TR-->>TR: connectedFeatures? return early
    TR-->>TR: isConnectInProgress? awaitInFlightConnect
    TR->>TR: reconnectKnownBluetoothDevice(deviceId)

    loop up to 4 attempts (0s, 2s, 4s, 6s delays)
        TR->>BLE: scan()
        BLE-->>TR: devices list
        TR->>BLE: connectKnownDevice(deviceId)
        BLE-->>TR: TrezorFeatures or failure
        alt user cancelled
            TR-->>TVM: Result.failure(UserCancelled)
            TVM-->>UI: silent no-op (no toast)
        else success
            TR-->>TVM: Result.success(features)
        end
    end

    TVM->>HWR: signAndBroadcastFunding(tx, deviceId)
    alt user cancels on device
        HWR-->>TVM: Result.failure(UserCancelled)
        TVM-->>UI: silent no-op (no disconnectStaleSession)
    else success
        HWR-->>TVM: Result.success(broadcastResult)
        TVM-->>UI: transfer complete
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant UI as SpendingHwSignScreen
    participant TVM as TransferViewModel
    participant HWR as HwWalletRepo
    participant TR as TrezorRepo
    participant BLE as TrezorService (BLE)

    UI->>TVM: warmUpHardwareConnection(deviceId)
    TVM->>HWR: warmUpKnownDevice(deviceId)
    HWR->>TR: warmUpKnownDevice(deviceId)
    TR-->>TR: skip if connected / in-progress / non-BLE
    TR->>TR: ensureConnected(deviceId) [background]

    UI->>TVM: onTransferToSpendingHwConfirm(order, deviceId)
    TVM->>HWR: ensureConnected(deviceId)
    HWR->>TR: ensureConnected(deviceId)
    TR-->>TR: connectedFeatures? return early
    TR-->>TR: isConnectInProgress? awaitInFlightConnect
    TR->>TR: reconnectKnownBluetoothDevice(deviceId)

    loop up to 4 attempts (0s, 2s, 4s, 6s delays)
        TR->>BLE: scan()
        BLE-->>TR: devices list
        TR->>BLE: connectKnownDevice(deviceId)
        BLE-->>TR: TrezorFeatures or failure
        alt user cancelled
            TR-->>TVM: Result.failure(UserCancelled)
            TVM-->>UI: silent no-op (no toast)
        else success
            TR-->>TVM: Result.success(features)
        end
    end

    TVM->>HWR: signAndBroadcastFunding(tx, deviceId)
    alt user cancels on device
        HWR-->>TVM: Result.failure(UserCancelled)
        TVM-->>UI: silent no-op (no disconnectStaleSession)
    else success
        HWR-->>TVM: Result.success(broadcastResult)
        TVM-->>UI: transfer complete
    end
Loading

Reviews (4): Last reviewed commit: "fix: scope hw watchers to native segwit" | Re-trigger Greptile

Comment thread app/src/main/java/to/bitkit/repositories/TrezorRepo.kt
Comment thread app/src/main/java/to/bitkit/repositories/TrezorRepo.kt
Comment thread app/src/main/java/to/bitkit/models/WalletScope.kt

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c3a41d26a1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

addressType = addressType,
xpub = xpub,
electrumUrl = watcherSettings.electrumUrl,
walletId = device.walletId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Populate wallet ids before starting hardware watchers

For users upgrading with already-paired hardware wallets, KnownDevice.walletId deserializes as the new default "" until TrezorRepo.loadKnownDevices() migrates the store during startWatcher(). This spec is built from the raw HwWalletStore value before that migration, so the first watcher can be started with a blank wallet scope; when the migrated store emits, the watcher is considered active because only the Electrum URL is compared, so it is not restarted with the derived wallet id. That leaves upgraded devices running in the wrong/empty activity scope for the session; derive/fallback the id here or include walletId in the active watcher config and restart when it changes.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5ae87c021 + 25c3a7925:

  • resolvedWalletId() derives/fallback when store value is blank
  • activeWatcherWalletIds tracked; watcher restarts when wallet id changes (not just electrum URL)
  • Shared deriveHardwareWalletId() aligns connect, restore, and watcher paths

sortedBy { "${it.deviceId}|${it.addressType}" }
.flatMap { it.activities }
.forEach { activity ->
byId[activity.rawId()] = activity

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Merge duplicate watcher activities by amount

For transactions that touch two monitored xpubs for the same hardware wallet, such as receiving to both native SegWit and Taproot addresses, each address-type watcher can report an Activity.Onchain with the same txid and only that watcher's value. This assignment keeps whichever activity sorts last and drops the other value, so the wallet activity list undercounts while the balance still sums both watchers; keep the previous txid-level merge/summing behavior instead of overwriting by rawId().

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5ae87c021 — restored txid-level sum merge via mergedActivity() (group by rawId, sum received/sent). Tests expect merged 150uL, not last-writer 50uL. Also merged feeRate via maxOf in 25c3a7925.

allowBleFallback = allowBleFallback,
)
if (result.isSuccess) return result
lastFailure = result.exceptionOrNull()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop retrying BLE reconnect after user cancellation

When a BLE reconnect attempt fails because core returns a Trezor cancellation (for example the user cancels PIN, passphrase, or device approval), this loop treats it the same as a transient missing advertisement and continues through the remaining retry attempts. That can re-prompt the user several times after an explicit cancel before finally returning the failure; return cancellation failures immediately instead of storing them in lastFailure and continuing.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5ae87c021reconnectKnownBluetoothDevice returns immediately on isTrezorUserCancellation(). Covered by TrezorRepoTest (ensureConnected stops bluetooth retry after user cancellation).

@ovitrif ovitrif mentioned this pull request Jul 5, 2026
7 tasks
@piotr-iohk

Copy link
Copy Markdown
Collaborator Author

Addressed bot review feedback in 5ae87c021 and 25c3a7925:

  • Watcher merge — sum-merge duplicate tx activities across address-type watchers
  • Wallet id on upgrade — derive/fallback + restart watchers when wallet id changes
  • BLE cancel — stop reconnect retry loop on user cancellation
  • Connect cleanup — avoid double disconnectStaleSession on forceSession failure path
  • Wallet id derivation — shared deriveHardwareWalletId() for connect/restore/watcher paths

Deferred: WalletScope lazy-init ordering (no production issue; DI order + test override sufficient for 2.4.0).

@piotr-iohk piotr-iohk marked this pull request as ready for review July 6, 2026 10:36

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 25c3a79255

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 611 to +613
is HardwareReconnectError -> {
Logger.error("Failed to reconnect hardware device", e, context = TAG)
showHardwareReconnectError()
showHardwareReconnectError(deviceId)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Suppress cancellation before reconnect error

When the reconnect step itself is where the user cancels (PIN, passphrase, or on-device approval), ensureHardwareConnected wraps that TrezorException in HardwareReconnectError. This branch runs before the generic isTrezorUserCancellation() guard below, so the cancellation still gets logged as a reconnect failure and shows the hardware reconnect/connect error toast; check the cause chain before showing this error.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 32dbef704e:

  • ensureHardwareConnected() rethrows isTrezorUserCancellation() instead of wrapping in HardwareReconnectError
  • HardwareReconnectError branch also checks cancellation before showing toast

Covered by TransferViewModelTest (onTransferToSpendingHwConfirm does not toast when user cancels during reconnect).

Comment on lines 524 to 526
val value = when (txType) {
PaymentType.RECEIVED -> received.safe() - sent.safe()
PaymentType.SENT -> (sent.safe() - received.safe()).safe() - fee.safe()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not subtract fees from merged sent values

When a sent hardware transaction is reported by multiple address-type watchers, the values being merged are already OnchainActivity.value display amounts with the fee stored separately. This branch sums those net values into sent and then subtracts the max fee again, so a tx split across two watched xpubs displays too little while the watcher balances still include the correct amount; merge sent watcher activities by summing the reported values without applying the fee a second time.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 32dbef704e — SENT merge uses summed net watcher values (sent - received) without subtracting max(fee) again; fee is stored once separately.

Covered by HwWalletRepoTest (merges duplicate sent tx activities without subtracting fee twice).

Comment thread gradle/libs.versions.toml
barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version = "17.3.0" }
biometric = { module = "androidx.biometric:biometric", version = "1.4.0-alpha05" }
bitkit-core = { module = "com.synonym:bitkit-core-android", version = "0.1.75" }
bitkit-core = { module = "com.synonym:bitkit-core-android", version = "0.3.9" }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Default wallet ids when restoring old backups

This core bump makes serialized activity/metadata models include a required walletId field, and the rest of the diff had to add that value at every construction site. Existing cloud backups written by the previous core version do not contain walletId, but the restore path still decodes MetadataBackupV1 and ActivityBackupV1 directly before any migration/defaulting can run, so restoring those older backups fails with a missing-field decode error; add legacy backup normalization or serializers/defaults that fill WalletScope.default.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 32dbef704e:

  • Added BackupRestoreCompat.kt to inject WalletScope.default when legacy backup JSON lacks walletId
  • BackupRepo restore uses decodeActivityBackupV1Compat() / decodeMetadataBackupV1Compat()

Covered by BackupRestoreCompatTest.

Comment thread gradle/libs.versions.toml
barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version = "17.3.0" }
biometric = { module = "androidx.biometric:biometric", version = "1.4.0-alpha05" }
bitkit-core = { module = "com.synonym:bitkit-core-android", version = "0.1.75" }
bitkit-core = { module = "com.synonym:bitkit-core-android", version = "0.3.9" }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add a changelog fragment

This PR is a user-facing feat: change (core bump plus hardware wallet reliability), but the diff does not add any file under changelog.d/next/ or changelog.d/hotfix/. The repo instructions in AGENTS.md require exactly one changelog fragment for user-facing feat:/fix: PRs, so the release notes will omit these changes unless a fragment is added.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added changelog.d/next/1062.added.md in the latest commit on this branch.

@piotr-iohk piotr-iohk marked this pull request as draft July 6, 2026 12:05
@piotr-iohk piotr-iohk marked this pull request as ready for review July 6, 2026 12:06

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 32dbef704e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +398 to +400
if (txid in knownTxIds || !emittedReceivedTxIds.add(txid)) return@forEach
val sats = mergedActivities.findOnchain(txid)?.v1?.value ?: onchain.v1.value
_receivedTxs.emit(HwWalletReceivedTx(txid = txid, sats = sats))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Emit hardware receive sheets with merged amounts

When the same new inbound tx is reported by multiple address-type watchers, the first watcher to arrive adds the txid to emittedReceivedTxIds and emits the sheet using the amount currently in watcherData; the later watcher is then skipped by this guard. In that scenario the wallet balance/activity eventually merge both outputs, but AppViewModel has already shown the received sheet with only the first watcher’s value, so defer/dedupe the emission until the merged activity includes all watcher reports or update the emitted amount instead of suppressing the later event.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in scope for 2.4.0 Trezor v1 by locking hardware watchers to native segwit only (HwWalletRepo.SUPPORTED_WATCHER_ADDRESS_TYPES), matching product scope in #bitkit-native.

With a single native-segwit watcher per device, the multi-watcher receive-sheet race is not reachable on the planned v1 path. Multi-type HW monitoring and merged receive-sheet amounts remain follow-up work (#1044 / 2.5.0).

Comment on lines +683 to +684
if (!forceSession) {
disconnectStaleSession(deviceId)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the current Trezor on other-device failures

When a manual reconnect is attempted for a different known device and that scan/connect fails (for example the selected Trezor is not nearby), this new cleanup calls disconnectStaleSession(deviceId). That helper always invokes trezorService.disconnect() and clears _state.connected, so an unrelated Trezor that was already connected gets dropped just because another device failed to reconnect; only disconnect when the current session belongs to the failed deviceId or when forceSession requested it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in upcoming commit on this branch: disconnectStaleSession is now a no-op when another Trezor is already connected, so a failed reconnect for device A no longer clears the live session for device B.

See TrezorRepoTestdisconnectStaleSession should not disconnect unrelated connected device and connectKnownDevice failure should not disconnect unrelated connected device.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SHA: 1ba128d49

@piotr-iohk piotr-iohk marked this pull request as draft July 6, 2026 12:53
Lock Trezor watch-only sync to native segwit for v1, preserve
unrelated connected devices on reconnect failure, and add changelog.

Co-authored-by: Cursor <cursoragent@cursor.com>
@piotr-iohk piotr-iohk marked this pull request as ready for review July 6, 2026 13:24
Trigger a new store sync when retrying watcher stop; empty
knownDevices no longer matches the native-segwit-only path.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jvsena42

This comment was marked as resolved.

@piotr-iohk piotr-iohk requested a review from coreyphillips July 7, 2026 08:54

@jvsena42 jvsena42 Jul 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This compatibility layer should be handled by #1046 , gated by synonymdev/bitkit-core#113

I recommend drop this file and wait for Bitkit-core implementation

Summarizing, we need to implement wallet-scope backup migrations because of the new walletId. The logic must be in Bitkit-core for single source of true

@piotr-iohk piotr-iohk Jul 7, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree long-term: wallet-scoped backup migration belongs in Core (#113) and the app integration in #1046.

BackupRestoreCompat is a minimal bridge added after Codex flagged legacy VSS restore failing on missing walletId once we bumped to 0.3.9 (P1 on this PR). It keeps existing hot-wallet cloud backup restore working until Core exposes migration APIs — it does not add Trezor/HW tag product scope for 2.4.0.

Happy to drop it as soon as synonymdev/bitkit-core#113 lands and wire the proper flow in #1046.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: might also be patch-able by making the serializer more lenient on Kotlin's Json instance, maybe a derivation of our global di.json instance (if that wasn't already tried and deemed not applicable here); or perhaps by playing with an optional walletId on a domain replica of the original serialized model originating from core, which adds optional fields, given that the json instance is already configured with isLenient = true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants