@onekeyfe/hd-core 1.2.0-alpha.183 → 1.2.0-alpha.185

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +20 -4
  2. package/__tests__/DeviceCommands.test.ts +13 -0
  3. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +0 -2
  4. package/__tests__/check-firmware-release-protocol-v2.test.ts +0 -2
  5. package/__tests__/device-lifecycle-events.test.ts +19 -0
  6. package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +102 -2
  7. package/__tests__/open-wallet-session.test.ts +127 -1
  8. package/__tests__/protocol-v2-resources.test.ts +0 -4
  9. package/__tests__/protocol-v2-unlock-policy.test.ts +35 -1
  10. package/__tests__/protocol-v2.test.ts +155 -2
  11. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  12. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  13. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  14. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +2 -2
  15. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  16. package/dist/api/firmware/getBinary.d.ts +2 -2
  17. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  18. package/dist/core/index.d.ts +1 -0
  19. package/dist/core/index.d.ts.map +1 -1
  20. package/dist/device/Device.d.ts +1 -1
  21. package/dist/device/Device.d.ts.map +1 -1
  22. package/dist/events/ui-request.d.ts +3 -0
  23. package/dist/events/ui-request.d.ts.map +1 -1
  24. package/dist/index.d.ts +9 -4
  25. package/dist/index.js +644 -347
  26. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +2 -0
  27. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
  28. package/dist/protocols/protocol-v2/walletSession.d.ts +3 -0
  29. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  30. package/dist/types/settings.d.ts +2 -2
  31. package/dist/types/settings.d.ts.map +1 -1
  32. package/dist/utils/deviceFeaturesUtils.d.ts +3 -0
  33. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  34. package/dist/utils/patch.d.ts +1 -1
  35. package/dist/utils/patch.d.ts.map +1 -1
  36. package/package.json +4 -4
  37. package/src/api/FirmwareUpdateV4.ts +193 -21
  38. package/src/api/OpenWalletSession.ts +39 -9
  39. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +2 -1
  40. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +9 -3
  41. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +7 -1
  42. package/src/core/index.ts +16 -2
  43. package/src/data/messages/messages-protocol-v2.json +111 -1
  44. package/src/device/Device.ts +3 -1
  45. package/src/events/ui-request.ts +3 -0
  46. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +8 -1
  47. package/src/protocols/protocol-v2/walletSession.ts +65 -22
  48. package/src/types/settings.ts +4 -2
  49. package/src/utils/deviceFeaturesUtils.ts +4 -0
@@ -1,4 +1,11 @@
1
- import { EDeviceType, ERRORS, HardwareError, HardwareErrorCode, wait } from '@onekeyfe/hd-shared';
1
+ import {
2
+ EDeviceType,
3
+ ERRORS,
4
+ HardwareError,
5
+ HardwareErrorCode,
6
+ isBleStaleBondHardwareError,
7
+ wait,
8
+ } from '@onekeyfe/hd-shared';
2
9
  import JSZip from 'jszip';
3
10
  import {
4
11
  DeviceRebootType,
@@ -44,6 +51,7 @@ import {
44
51
  getProtocolV2UnknownErrorText,
45
52
  isProtocolV2DeviceDisconnectedError,
46
53
  } from './protocol-v2/helpers';
54
+ import { isProtocolV2ResponseTimeout } from './helpers/protocolV2FileWrite';
47
55
  import {
48
56
  openFirmwareByteSource,
49
57
  readFirmwareByteSourceFully,
@@ -55,6 +63,7 @@ import {
55
63
  assertFirmwareUpdatePreparedPlanDeviceIdentity,
56
64
  validateFirmwareUpdatePreparedPlan,
57
65
  } from './firmware/FirmwareUpdatePreparedPlan';
66
+
58
67
  import type {
59
68
  FirmwareArtifactReference,
60
69
  FirmwareUpdateV4Params,
@@ -84,6 +93,7 @@ const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
84
93
  const PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT = 15 * 1000;
85
94
  const PROTOCOL_V2_INSTALL_TIMEOUT = 5 * 60 * 1000;
86
95
  const PROTOCOL_V2_INSTALL_STATUS_INITIAL_DELAY = 1000;
96
+ const PROTOCOL_V2_INSTALL_FINISHED_AFTER_RECONNECT_POLLS = 4;
87
97
  const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
88
98
  const PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS = 1;
89
99
  const PROTOCOL_V2_TARGET_STATUS_FINISHED = 2;
@@ -182,6 +192,11 @@ const getProtocolV2DeviceTransferProgress = (
182
192
  type ProtocolV2FirmwareUpdateStatusTarget = {
183
193
  target_id: number | string;
184
194
  status?: number | string;
195
+ progress_percent?: number;
196
+ phase_info?: {
197
+ phase: number | string;
198
+ progress_percent: number;
199
+ };
185
200
  payload_version?: number;
186
201
  path?: string;
187
202
  };
@@ -360,11 +375,12 @@ const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
360
375
  };
361
376
 
362
377
  const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
363
- error instanceof HardwareError &&
364
- (error.errorCode === HardwareErrorCode.ActionCancelled ||
365
- error.errorCode === HardwareErrorCode.FirmwareError ||
366
- error.errorCode === HardwareErrorCode.FirmwareVerificationFailed ||
367
- error.params?.firmwareUpdateCode === PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE);
378
+ isBleStaleBondHardwareError(error) ||
379
+ (error instanceof HardwareError &&
380
+ (error.errorCode === HardwareErrorCode.ActionCancelled ||
381
+ error.errorCode === HardwareErrorCode.FirmwareError ||
382
+ error.errorCode === HardwareErrorCode.FirmwareVerificationFailed ||
383
+ error.params?.firmwareUpdateCode === PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE));
368
384
 
369
385
  const isProtocolV2TargetStatusFinished = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) =>
370
386
  normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_FINISHED;
@@ -401,6 +417,18 @@ const normalizeProtocolV2TargetStatus = (
401
417
  return undefined;
402
418
  };
403
419
 
420
+ const PROTOCOL_V2_INSTALL_PHASE_BY_DECODED_VALUE = new Map<
421
+ number | string,
422
+ 'prepare' | 'install' | 'verify'
423
+ >([
424
+ [0, 'prepare'],
425
+ [1, 'install'],
426
+ [2, 'verify'],
427
+ ['FW_MGMT_UPDATER_PHASE_PREPARE', 'prepare'],
428
+ ['FW_MGMT_UPDATER_PHASE_INSTALL', 'install'],
429
+ ['FW_MGMT_UPDATER_PHASE_VERIFY', 'verify'],
430
+ ]);
431
+
404
432
  const normalizeProtocolV2Hex = (value?: string) => value?.replace(/^0x/i, '').toLowerCase();
405
433
 
406
434
  const versionArrayToNumber = (version?: IVersionArray) => {
@@ -458,6 +486,11 @@ const toProtocolV2FiniteNumber = (value: unknown) => {
458
486
  return undefined;
459
487
  };
460
488
 
489
+ const normalizeProtocolV2ProgressPercent = (value: unknown) => {
490
+ const progress = toProtocolV2FiniteNumber(value);
491
+ return progress === undefined ? undefined : Math.min(100, Math.max(0, progress));
492
+ };
493
+
461
494
  export const isProtocolV2FirmwareFingerprintValid = (
462
495
  binary: ArrayBuffer | Uint8Array,
463
496
  fingerprint: string | undefined
@@ -1812,6 +1845,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1812
1845
  if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash) return false;
1813
1846
  return true;
1814
1847
  } catch (error) {
1848
+ if (isBleStaleBondHardwareError(error)) {
1849
+ throw error;
1850
+ }
1815
1851
  Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
1816
1852
  return false;
1817
1853
  }
@@ -1905,7 +1941,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1905
1941
  }
1906
1942
  lastError = new Error('Protocol V2 device is reachable but is not in bootloader mode');
1907
1943
  } catch (error) {
1908
- if (this.isProtocolV2ReconnectIdentityError(error)) {
1944
+ if (isBleStaleBondHardwareError(error) || this.isProtocolV2ReconnectIdentityError(error)) {
1909
1945
  throw error;
1910
1946
  }
1911
1947
  shouldReconnect = true;
@@ -2187,6 +2223,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2187
2223
  });
2188
2224
  return processedSize + source.size;
2189
2225
  } catch (error) {
2226
+ if (isBleStaleBondHardwareError(error)) {
2227
+ throw error;
2228
+ }
2190
2229
  lastError = error;
2191
2230
  if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
2192
2231
  await this.recoverProtocolV2FileTransfer();
@@ -2296,6 +2335,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2296
2335
  for (const target of matchingTargets) {
2297
2336
  const targetId = normalizeProtocolV2TargetId(target.target_id);
2298
2337
  const pathConflictsWithCurrentInstall =
2338
+ targetId !== undefined &&
2299
2339
  Boolean(target.path) &&
2300
2340
  Boolean(resolvedExpectedPaths.get(targetId)) &&
2301
2341
  target.path !== resolvedExpectedPaths.get(targetId) &&
@@ -2359,16 +2399,54 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2359
2399
  }
2360
2400
 
2361
2401
  if (expectedTargetIds.size > 0 && matchingTargets.length > 0) {
2362
- const hasInProgressTarget = matchingTargets.some(target =>
2363
- isProtocolV2TargetStatusInProgress(target.status)
2364
- );
2402
+ let detailedProgress = completedTargetIds.size * 100;
2403
+ let installProgressMetadata:
2404
+ | {
2405
+ installTargetId: number;
2406
+ installPhase: 'prepare' | 'install' | 'verify';
2407
+ installPhaseProgress: number;
2408
+ }
2409
+ | undefined;
2410
+ let hasInProgressTarget = false;
2411
+ matchingTargets.forEach(target => {
2412
+ const targetId = normalizeProtocolV2TargetId(target.target_id);
2413
+ if (
2414
+ targetId === undefined ||
2415
+ completedTargetIds.has(targetId) ||
2416
+ !isProtocolV2TargetStatusInProgress(target.status) ||
2417
+ (liveTargetIds && !liveTargetIds.has(targetId))
2418
+ ) {
2419
+ return;
2420
+ }
2421
+ hasInProgressTarget = true;
2422
+ detailedProgress += normalizeProtocolV2ProgressPercent(target.progress_percent) ?? 0;
2423
+ const installPhase = target.phase_info
2424
+ ? PROTOCOL_V2_INSTALL_PHASE_BY_DECODED_VALUE.get(target.phase_info.phase)
2425
+ : undefined;
2426
+ const installPhaseProgress = normalizeProtocolV2ProgressPercent(
2427
+ target.phase_info?.progress_percent
2428
+ );
2429
+ if (installPhase && installPhaseProgress !== undefined && !installProgressMetadata) {
2430
+ installProgressMetadata = {
2431
+ installTargetId: targetId,
2432
+ installPhase,
2433
+ installPhaseProgress,
2434
+ };
2435
+ }
2436
+ });
2365
2437
  const completedProgress = Math.floor(
2366
2438
  (completedTargetIds.size / expectedTargetIds.size) * 100
2367
2439
  );
2368
- // The protocol exposes no per-target percentage, so report coarse progress by
2369
- // completed targets and use 1% once work starts to keep the UI responsive.
2370
- const progress = Math.min(99, Math.max(completedProgress, hasInProgressTarget ? 1 : 0));
2371
- this.postProgressMessage(progress, 'installingFirmware');
2440
+ const reportedProgress = Math.floor(detailedProgress / expectedTargetIds.size);
2441
+ const progress = Math.min(
2442
+ 99,
2443
+ Math.max(completedProgress, reportedProgress, hasInProgressTarget ? 1 : 0)
2444
+ );
2445
+ if (installProgressMetadata) {
2446
+ this.postProgressMessage(progress, 'installingFirmware', installProgressMetadata);
2447
+ } else {
2448
+ this.postProgressMessage(progress, 'installingFirmware');
2449
+ }
2372
2450
  }
2373
2451
 
2374
2452
  return false;
@@ -2444,9 +2522,14 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2444
2522
  // The loader may release either USB or BLE as installation starts. Recovery reconnects
2445
2523
  // directly to status polling without generic Ping or DeviceInfo probes.
2446
2524
  let shouldReconnect = this.protocolV2InstallNeedsReconnect;
2525
+ let installTransportReleaseObserved = shouldReconnect;
2447
2526
  this.protocolV2InstallNeedsReconnect = false;
2448
2527
  let deviceInfo: ProtocolV2DeviceInfo | undefined;
2449
2528
  let bleInstallLinkReady = false;
2529
+ let installReconnectObserved = false;
2530
+ let finishedStatusSnapshotKey: string | undefined;
2531
+ let finishedStatusSnapshotPolls = 0;
2532
+ let fastFinishedInstallProgressReported = false;
2450
2533
  let installEvidenceObserved = this.protocolV2InstallTerminalSuccessObserved;
2451
2534
  let currentInstallStatusObserved = false;
2452
2535
  const liveTargetIds = new Set<number>();
@@ -2463,6 +2546,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2463
2546
  deviceInfo = isBleInstallReconnect
2464
2547
  ? undefined
2465
2548
  : await this.verifyProtocolV2ReconnectIdentity();
2549
+ if (installTransportReleaseObserved) {
2550
+ installReconnectObserved = true;
2551
+ }
2466
2552
  shouldReconnect = false;
2467
2553
  }
2468
2554
  let currentDeviceInfo = deviceInfo;
@@ -2473,6 +2559,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2473
2559
  {
2474
2560
  fields: {
2475
2561
  status: true,
2562
+ progress_percent: true,
2563
+ phase_info: true,
2476
2564
  payload_version: true,
2477
2565
  path: true,
2478
2566
  },
@@ -2502,8 +2590,73 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2502
2590
  const targetId = normalizeProtocolV2TargetId(target.target_id);
2503
2591
  return targetId !== undefined && expectedTargetIds.has(targetId);
2504
2592
  });
2593
+ const matchingFinishedTargetIds = new Set<number>();
2594
+ const matchingFinishedStatus = matchingStatusTargets
2595
+ .map(target => {
2596
+ const targetId = normalizeProtocolV2TargetId(target.target_id);
2597
+ const expectedPath = targetId === undefined ? undefined : expectedPaths.get(targetId);
2598
+ const expectedVersion =
2599
+ targetId === undefined
2600
+ ? undefined
2601
+ : this.getExpectedProtocolV2TargetVersion(targetId);
2602
+ const payloadVersion = toProtocolV2FiniteNumber(target.payload_version);
2603
+ if (
2604
+ targetId === undefined ||
2605
+ matchingFinishedTargetIds.has(targetId) ||
2606
+ !isProtocolV2TargetStatusFinished(target.status) ||
2607
+ target.path !== expectedPath ||
2608
+ (expectedVersion !== undefined && payloadVersion !== expectedVersion)
2609
+ ) {
2610
+ return undefined;
2611
+ }
2612
+ matchingFinishedTargetIds.add(targetId);
2613
+ return [targetId, target.path, payloadVersion ?? null] as const;
2614
+ })
2615
+ .filter((target): target is readonly [number, string, number | null] => !!target)
2616
+ .sort(([leftTargetId], [rightTargetId]) => leftTargetId - rightTargetId);
2617
+ const hasCompleteMatchingFinishedStatus =
2618
+ matchingFinishedStatus.length === expectedTargetIds.size &&
2619
+ matchingFinishedTargetIds.size === expectedTargetIds.size &&
2620
+ expectedTargetIds.size > 0;
2621
+ if (
2622
+ requireCurrentInstallStatus &&
2623
+ installReconnectObserved &&
2624
+ hasCompleteMatchingFinishedStatus
2625
+ ) {
2626
+ const nextSnapshotKey = JSON.stringify(matchingFinishedStatus);
2627
+ if (nextSnapshotKey === finishedStatusSnapshotKey) {
2628
+ finishedStatusSnapshotPolls += 1;
2629
+ } else {
2630
+ finishedStatusSnapshotKey = nextSnapshotKey;
2631
+ finishedStatusSnapshotPolls = 1;
2632
+ }
2633
+ } else {
2634
+ finishedStatusSnapshotKey = undefined;
2635
+ finishedStatusSnapshotPolls = 0;
2636
+ }
2637
+ if (
2638
+ requireCurrentInstallStatus &&
2639
+ installReconnectObserved &&
2640
+ hasCompleteMatchingFinishedStatus &&
2641
+ !currentInstallStatusObserved &&
2642
+ !this.protocolV2InstallTerminalSuccessObserved &&
2643
+ !fastFinishedInstallProgressReported
2644
+ ) {
2645
+ // A fast coprocessor install may finish while BLE is disconnected, so the
2646
+ // host never observes IN_PROGRESS. Enter the install UI while the matching
2647
+ // FINISHED snapshot is stabilized; completion still requires all polls.
2648
+ this.postProgressMessage(1, 'installingFirmware');
2649
+ fastFinishedInstallProgressReported = true;
2650
+ }
2651
+ const finishedAfterReconnectObserved =
2652
+ finishedStatusSnapshotPolls >= PROTOCOL_V2_INSTALL_FINISHED_AFTER_RECONNECT_POLLS;
2505
2653
  const hasCurrentInstallEvidence =
2506
- currentInstallStatusObserved || this.protocolV2InstallTerminalSuccessObserved;
2654
+ currentInstallStatusObserved ||
2655
+ this.protocolV2InstallTerminalSuccessObserved ||
2656
+ finishedAfterReconnectObserved;
2657
+ if (hasCurrentInstallEvidence) {
2658
+ installEvidenceObserved = true;
2659
+ }
2507
2660
  const shouldVerifyTargetCompletion =
2508
2661
  !requireCurrentInstallStatus || hasCurrentInstallEvidence;
2509
2662
  if (
@@ -2512,7 +2665,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2512
2665
  statusTargets,
2513
2666
  expectedTargetIds,
2514
2667
  expectedPaths,
2515
- requireCurrentInstallStatus && !this.protocolV2InstallTerminalSuccessObserved
2668
+ requireCurrentInstallStatus &&
2669
+ !this.protocolV2InstallTerminalSuccessObserved &&
2670
+ !finishedAfterReconnectObserved
2516
2671
  ? liveTargetIds
2517
2672
  : undefined
2518
2673
  )
@@ -2628,6 +2783,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2628
2783
  );
2629
2784
  }
2630
2785
  } else {
2786
+ if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ResponseTimeout(error)) {
2787
+ installTransportReleaseObserved = true;
2788
+ finishedStatusSnapshotKey = undefined;
2789
+ finishedStatusSnapshotPolls = 0;
2790
+ }
2631
2791
  shouldReconnect = true;
2632
2792
  deviceInfo = undefined;
2633
2793
  bleInstallLinkReady = false;
@@ -2683,6 +2843,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2683
2843
  return;
2684
2844
  }
2685
2845
  } catch (error) {
2846
+ if (isBleStaleBondHardwareError(error)) {
2847
+ throw error;
2848
+ }
2686
2849
  Log.log('[FirmwareUpdateV4] unable to confirm App mode before Normal reboot: ', error);
2687
2850
  }
2688
2851
  await this.protocolV2Reboot(DeviceRebootType.Normal);
@@ -2774,7 +2937,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2774
2937
  'Protocol V2 device is still in bootloader mode'
2775
2938
  );
2776
2939
  } catch (error) {
2777
- if (this.isProtocolV2ReconnectIdentityError(error)) {
2940
+ if (isBleStaleBondHardwareError(error) || this.isProtocolV2ReconnectIdentityError(error)) {
2778
2941
  throw error;
2779
2942
  }
2780
2943
  shouldReconnect = true;
@@ -3031,13 +3194,22 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
3031
3194
  private async protocolV2Reboot(rebootType: DeviceRebootType) {
3032
3195
  const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
3033
3196
  try {
3034
- const res = await typedCall('DeviceReboot', 'Success', {
3035
- reboot_type: rebootType,
3036
- });
3197
+ const res = await typedCall(
3198
+ 'DeviceReboot',
3199
+ 'Success',
3200
+ {
3201
+ reboot_type: rebootType,
3202
+ },
3203
+ { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT }
3204
+ );
3037
3205
  this.device.markProtocolV2Reboot(rebootType);
3038
3206
  return res.message;
3039
3207
  } catch (error) {
3040
- if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ReconnectProbeError(error)) {
3208
+ if (
3209
+ isProtocolV2DeviceDisconnectedError(error) ||
3210
+ isProtocolV2ReconnectProbeError(error) ||
3211
+ isProtocolV2ResponseTimeout(error)
3212
+ ) {
3041
3213
  this.device.markProtocolV2Reboot(rebootType);
3042
3214
  return { message: 'Device rebooted successfully' };
3043
3215
  }
@@ -29,6 +29,12 @@ const wasResumed = (session: unknown) =>
29
29
  'resumed' in session &&
30
30
  (session as { resumed?: unknown }).resumed === true;
31
31
 
32
+ const wasWalletStatusRefreshed = (session: unknown) =>
33
+ !!session &&
34
+ typeof session === 'object' &&
35
+ 'walletStatusRefreshed' in session &&
36
+ (session as { walletStatusRefreshed?: unknown }).walletStatusRefreshed === true;
37
+
32
38
  const requireHiddenWalletResponse = (session: { passphraseState?: string }) => {
33
39
  if (!session.passphraseState) {
34
40
  throw ERRORS.TypedError(
@@ -87,12 +93,20 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
87
93
  this.unlockPolicy = 'unlock-before-run';
88
94
  this.skipForceUpdateCheck = true;
89
95
  this.params = normalizeParams(this.payload as unknown as Record<string, unknown>);
96
+ this.protocolV2PreUnlockPinType =
97
+ this.params.mode === OpenWalletSessionMode.SelectHidden
98
+ ? DeviceSessionPinType.Any
99
+ : DeviceSessionPinType.Main;
90
100
  this.payload.useEmptyPassphrase = this.params.mode === OpenWalletSessionMode.Standard;
91
101
  }
92
102
 
93
103
  async run(): Promise<OpenWalletSessionPayload> {
94
104
  const isProtocolV2 = this.device.isProtocolV2();
95
- let state = await this.device.getDeviceState({ refreshSections: ['status'] });
105
+ const reusePreflightStatus =
106
+ isProtocolV2 && this.protocolV2UnlockContext?.preflightStatusRefreshed === true;
107
+ let state = reusePreflightStatus
108
+ ? await this.device.getDeviceState()
109
+ : await this.device.getDeviceState({ refreshSections: ['status'] });
96
110
  let currentDeviceId = state.identity.deviceId;
97
111
  const hasAuthoritativeProtocolV2WalletStatus = (candidate: typeof state) =>
98
112
  candidate.status.unlocked === true &&
@@ -115,14 +129,25 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
115
129
  currentDeviceId = state.identity.deviceId;
116
130
  return state;
117
131
  };
132
+ const resolveProtocolV2DeviceStateAfterSession = async (session: unknown) => {
133
+ if (!wasWalletStatusRefreshed(session)) {
134
+ return refreshProtocolV2DeviceState();
135
+ }
136
+ state = await this.device.getDeviceState();
137
+ currentDeviceId = state.identity.deviceId;
138
+ return state;
139
+ };
118
140
  const ensureProtocolV2WalletStatus = async () => {
119
141
  if (isProtocolV2 && !hasAuthoritativeProtocolV2WalletStatus(state)) {
120
- await this.device.unlockDevice(DeviceSessionPinType.Main, {
121
- source: 'unlock-coordinator',
122
- reason: 'device-locked',
123
- deviceOnly: true,
124
- method: 'openWalletSession',
125
- });
142
+ await this.device.unlockDevice(
143
+ this.protocolV2PreUnlockPinType ?? DeviceSessionPinType.Main,
144
+ {
145
+ source: 'unlock-coordinator',
146
+ reason: 'device-locked',
147
+ deviceOnly: true,
148
+ method: 'openWalletSession',
149
+ }
150
+ );
126
151
  requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState());
127
152
  }
128
153
  return state;
@@ -139,13 +164,16 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
139
164
  selectMainWalletBeforeRestore:
140
165
  !hasAuthoritativeProtocolV2WalletStatus(state) ||
141
166
  state.status.unlockedAttachPin === true,
167
+ mainPinSelected: this.protocolV2UnlockContext?.preflightMainPinSelected,
142
168
  })
143
169
  : await getPassphraseStateWithRefreshDeviceInfo(this.device, {
144
170
  onlyMainPin: true,
145
171
  initSession: this.payload.initSession,
146
172
  });
147
173
  const refreshedState = isProtocolV2
148
- ? requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState())
174
+ ? requireAuthoritativeProtocolV2WalletStatus(
175
+ await resolveProtocolV2DeviceStateAfterSession(session)
176
+ )
149
177
  : state;
150
178
  const deviceId = requireDeviceId();
151
179
  if (
@@ -233,7 +261,9 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
233
261
  })
234
262
  : await getPassphraseStateWithRefreshDeviceInfo(this.device, { initSession: true });
235
263
  const refreshedState = isProtocolV2
236
- ? requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState())
264
+ ? requireAuthoritativeProtocolV2WalletStatus(
265
+ await resolveProtocolV2DeviceStateAfterSession(session)
266
+ )
237
267
  : state;
238
268
  const deviceId = requireDeviceId();
239
269
  if (isProtocolV2 && refreshedState.status.passphraseProtection !== true) {
@@ -400,7 +400,8 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
400
400
  this.payload.passphraseState,
401
401
  useEmptyPassphrase,
402
402
  this.payload.skipPassphraseCheck,
403
- deriveCardano
403
+ deriveCardano,
404
+ this.protocolV2UnlockContext?.preflightMainPinSelected
404
405
  );
405
406
  if (!passphraseStateSafety) {
406
407
  throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
@@ -41,9 +41,15 @@ const FIRMWARE_UPDATE_CONFIRM = 'Firmware install confirmed';
41
41
  */
42
42
  export const BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS = 5000;
43
43
 
44
- type FirmwareTransferMetrics = Pick<
44
+ type FirmwareProgressMetadata = Pick<
45
45
  FirmwareProgress['payload'],
46
- 'transferredBytes' | 'totalBytes' | 'rateBytesPerSecond' | 'elapsedMs'
46
+ | 'transferredBytes'
47
+ | 'totalBytes'
48
+ | 'rateBytesPerSecond'
49
+ | 'elapsedMs'
50
+ | 'installTargetId'
51
+ | 'installPhase'
52
+ | 'installPhaseProgress'
47
53
  >;
48
54
 
49
55
  const isDeviceDisconnectedError = (error: unknown) => {
@@ -103,7 +109,7 @@ export class FirmwareUpdateBaseMethod<Params> extends BaseMethod<Params> {
103
109
  postProgressMessage = (
104
110
  progress: number,
105
111
  progressType: IFirmwareUpdateProgressType,
106
- metrics?: FirmwareTransferMetrics
112
+ metrics?: FirmwareProgressMetadata
107
113
  ) => {
108
114
  this.postMessage(
109
115
  createUiMessage(UI_REQUEST.FIRMWARE_PROGRESS, {
@@ -4,7 +4,13 @@ import { invalidParameter } from '../helpers/filesystemValidation';
4
4
  import type { DeviceFirmwareUpdateStatusGetParams } from './helpers';
5
5
  import type { DeviceFirmwareUpdateRecordFields } from '@onekeyfe/hd-transport';
6
6
 
7
- const DEVICE_FIRMWARE_UPDATE_STATUS_FIELDS = ['status', 'payload_version', 'path'] as const;
7
+ const DEVICE_FIRMWARE_UPDATE_STATUS_FIELDS = [
8
+ 'status',
9
+ 'progress_percent',
10
+ 'phase_info',
11
+ 'payload_version',
12
+ 'path',
13
+ ] as const;
8
14
 
9
15
  function normalizeStatusFields(
10
16
  fields: DeviceFirmwareUpdateStatusGetParams['fields']
package/src/core/index.ts CHANGED
@@ -651,7 +651,8 @@ const onCallDevice = async (
651
651
  method.payload?.passphraseState,
652
652
  method.payload?.useEmptyPassphrase,
653
653
  method.payload?.skipPassphraseCheck,
654
- hasDeriveCardano(method)
654
+ hasDeriveCardano(method),
655
+ method.protocolV2UnlockContext?.preflightMainPinSelected
655
656
  );
656
657
 
657
658
  // Double check, handles the special case of Touch/Pro
@@ -957,6 +958,14 @@ export function isMissingDetectedProtocolV2Error(method: BaseMethod, error: unkn
957
958
  );
958
959
  }
959
960
 
961
+ export function isProtocolV2PeerRemovedPairingError(method: BaseMethod, error: unknown) {
962
+ return (
963
+ method.payload.connectProtocol === 'V2' &&
964
+ (error as { errorCode?: unknown })?.errorCode ===
965
+ HardwareErrorCode.BlePeerRemovedPairingInformation
966
+ );
967
+ }
968
+
960
969
  /**
961
970
  * If the Bluetooth connection times out, retry up to 6 times
962
971
  * @param retryCount - Current retry count (default 0)
@@ -1258,7 +1267,8 @@ const ensureConnected = async (
1258
1267
  HardwareErrorCode.BridgeNeedsPermission,
1259
1268
  HardwareErrorCode.DeviceInterruptedFromUser,
1260
1269
  HardwareErrorCode.CallQueueActionCancelled,
1261
- ].includes(error.errorCode)
1270
+ ].includes(error.errorCode) ||
1271
+ isProtocolV2PeerRemovedPairingError(method, error)
1262
1272
  ) {
1263
1273
  reject(error);
1264
1274
  return;
@@ -1425,6 +1435,10 @@ const checkPassphraseEnableState = (method: BaseMethod, features?: Features) =>
1425
1435
  const shouldCheckPassphraseState = (method: BaseMethod, device: Device) => {
1426
1436
  if (!method.useDevicePassphraseState) return false;
1427
1437
 
1438
+ if (device.isProtocolV2() && method.payload?.useEmptyPassphrase === true) {
1439
+ return true;
1440
+ }
1441
+
1428
1442
  return device.hasUsePassphrase();
1429
1443
  };
1430
1444