From 893404fe70700bf0eff76f61f4e0b1cfc2b64365 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Fri, 8 May 2026 12:43:57 +0200 Subject: [PATCH 1/3] fix: protect BitBox pairing against stale hash on re-pair `BitboxService` is a DI singleton, so a second pairing attempt in the same app session (re-open from welcome, KYC retry flow, or after an in-flight cancel) hits the same `BitboxManager` instance. Its `getChannelHash()` can still return the previous session's hash before the new `pair()` handshake lands the new one, which would then be shown to the user and not match the code on the device. Snapshot the hash before starting init() and require the polled value to differ from it. While in confirmPairing, drop the `?? Future.value(true)` fallback on `_pendingInit`: the state guard above guarantees it is non-null on this path, and silently substituting "init succeeded" hides any real state inconsistency. --- .../bloc/connect_bitbox_cubit.dart | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart b/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart index 11dd6a63..e91a8c57 100644 --- a/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart +++ b/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart @@ -39,6 +39,15 @@ class ConnectBitboxCubit extends Cubit { if (state is BitboxConnecting) return; emit(BitboxConnecting(device)); try { + // Snapshot any hash from a prior pairing on the same BitboxService + // instance so polling waits for *this* session's hash to land instead + // of accepting a stale value from an earlier pairing. + String? priorHash; + try { + priorHash = await _service.getChannelHash().timeout(const Duration(seconds: 2)); + } catch (_) {} + if (isClosed) return; + var initFailed = false; _pendingInit = _service .init(device) @@ -67,7 +76,7 @@ class ConnectBitboxCubit extends Cubit { if (initFailed) throw Exception('init failed'); try { final hash = await _service.getChannelHash().timeout(const Duration(seconds: 2)); - if (hash.isNotEmpty) channelHash = hash; + if (hash.isNotEmpty && hash != priorHash) channelHash = hash; } catch (_) {} } @@ -91,7 +100,7 @@ class ConnectBitboxCubit extends Cubit { try { emit(BitboxPairing(currentState.device)); - final initOk = await (_pendingInit ?? Future.value(true)).timeout( + final initOk = await _pendingInit!.timeout( const Duration(seconds: 120), onTimeout: () => false, ); From 65c89edd9e6ae9bd80fbbfaf92694e6369859c6f Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Fri, 8 May 2026 12:48:28 +0200 Subject: [PATCH 2/3] style: comment empty catch on prior-hash read --- .../hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart b/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart index e91a8c57..b5c167fc 100644 --- a/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart +++ b/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart @@ -45,7 +45,9 @@ class ConnectBitboxCubit extends Cubit { String? priorHash; try { priorHash = await _service.getChannelHash().timeout(const Duration(seconds: 2)); - } catch (_) {} + } catch (_) { + // no prior session — leave priorHash null, any non-empty new hash is accepted + } if (isClosed) return; var initFailed = false; From efe6f134fdccd39a6ce4e5c1f720e33647fb7ec2 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Fri, 8 May 2026 12:52:04 +0200 Subject: [PATCH 3/3] style: drop markdown emphasis and trim catch comment --- .../hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart b/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart index b5c167fc..57b48f82 100644 --- a/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart +++ b/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart @@ -40,13 +40,13 @@ class ConnectBitboxCubit extends Cubit { emit(BitboxConnecting(device)); try { // Snapshot any hash from a prior pairing on the same BitboxService - // instance so polling waits for *this* session's hash to land instead - // of accepting a stale value from an earlier pairing. + // instance so polling waits for the new session's hash instead of + // accepting a stale value from an earlier pairing. String? priorHash; try { priorHash = await _service.getChannelHash().timeout(const Duration(seconds: 2)); } catch (_) { - // no prior session — leave priorHash null, any non-empty new hash is accepted + // no prior session } if (isClosed) return;