@onekeyfe/hd-core 1.2.0-alpha.34 → 1.2.0-alpha.35

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 (65) hide show
  1. package/__tests__/DeviceCommands.test.ts +84 -0
  2. package/__tests__/device-settings.test.ts +3 -0
  3. package/__tests__/device-wallet-session-store.test.ts +80 -7
  4. package/__tests__/evmSignTransaction.test.ts +69 -0
  5. package/__tests__/get-device-state.test.ts +78 -30
  6. package/__tests__/open-wallet-session.test.ts +186 -51
  7. package/__tests__/protocol-v2.test.ts +490 -112
  8. package/dist/api/FirmwareUpdateV4.d.ts +1 -0
  9. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  10. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  11. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  12. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  13. package/dist/api/evm/protocol.d.ts +3 -0
  14. package/dist/api/evm/protocol.d.ts.map +1 -0
  15. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +1 -1
  16. package/dist/device/Device.d.ts +3 -2
  17. package/dist/device/Device.d.ts.map +1 -1
  18. package/dist/device/DeviceCommands.d.ts.map +1 -1
  19. package/dist/device/DeviceWalletSessionStore.d.ts +0 -1
  20. package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
  21. package/dist/index.d.ts +8 -6
  22. package/dist/index.js +606 -641
  23. package/dist/protocols/protocol-v2/features.d.ts +17 -6
  24. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  25. package/dist/protocols/protocol-v2/index.d.ts +2 -2
  26. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  27. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +1 -0
  28. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -1
  29. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
  30. package/dist/protocols/protocol-v2/walletSession.d.ts +1 -0
  31. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  32. package/dist/types/api/openWalletSession.d.ts +1 -1
  33. package/dist/types/api/openWalletSession.d.ts.map +1 -1
  34. package/dist/types/api/protocolV2.d.ts +2 -2
  35. package/dist/types/api/protocolV2.d.ts.map +1 -1
  36. package/dist/types/device.d.ts +2 -1
  37. package/dist/types/device.d.ts.map +1 -1
  38. package/dist/utils/patch.d.ts +1 -1
  39. package/dist/utils/patch.d.ts.map +1 -1
  40. package/package.json +4 -4
  41. package/src/api/FirmwareUpdateV4.ts +26 -8
  42. package/src/api/GetPassphraseState.ts +11 -0
  43. package/src/api/OpenWalletSession.ts +15 -7
  44. package/src/api/evm/EVMGetAddress.ts +2 -2
  45. package/src/api/evm/EVMGetPublicKey.ts +2 -2
  46. package/src/api/evm/EVMSignMessage.ts +2 -2
  47. package/src/api/evm/EVMSignTransaction.ts +2 -2
  48. package/src/api/evm/EVMSignTypedData.ts +35 -45
  49. package/src/api/evm/EVMVerifyMessage.ts +2 -2
  50. package/src/api/evm/protocol.ts +6 -0
  51. package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +2 -2
  52. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +1 -1
  53. package/src/data/messages/messages-protocol-v2.json +393 -485
  54. package/src/device/Device.ts +63 -37
  55. package/src/device/DeviceCommands.ts +12 -2
  56. package/src/device/DeviceWalletSessionStore.ts +0 -14
  57. package/src/deviceProfile/buildDeviceFeatures.ts +1 -1
  58. package/src/protocols/protocol-v2/features.ts +58 -16
  59. package/src/protocols/protocol-v2/index.ts +5 -0
  60. package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +8 -3
  61. package/src/protocols/protocol-v2/unlockRetry.ts +18 -7
  62. package/src/protocols/protocol-v2/walletSession.ts +35 -35
  63. package/src/types/api/openWalletSession.ts +1 -1
  64. package/src/types/api/protocolV2.ts +2 -2
  65. package/src/types/device.ts +2 -0
@@ -55,6 +55,7 @@ import { DataManager } from '../data-manager';
55
55
  import TransportManager from '../data-manager/TransportManager';
56
56
  import { toHardened } from '../api/helpers/pathUtils';
57
57
  import {
58
+ PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE,
58
59
  PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST,
59
60
  PROTOCOL_V2_FULL_DEVICE_INFO_REQUEST,
60
61
  PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
@@ -62,8 +63,11 @@ import {
62
63
  getProtocolV2RuntimeMode,
63
64
  requestProtocolV2DeviceInfo,
64
65
  requestProtocolV2DeviceStatus,
66
+ requestProtocolV2ProtocolInfo,
67
+ supportsProtocolV2Message,
65
68
  } from '../protocols/protocol-v2/features';
66
69
  import { buildProtocolV1FeaturesPayload } from '../deviceProfile';
70
+ import { resolveProtocolV2DeviceIdentity } from '../deviceProfile/protocolV2DeviceIdentity';
67
71
 
68
72
  import type { PROTO } from '../constants';
69
73
  import type { DeviceStateReadOptions } from '../types/api/getDeviceState';
@@ -77,6 +81,7 @@ import type { Deferred, HardwareConnectProtocol } from '@onekeyfe/hd-shared';
77
81
  import type {
78
82
  OneKeyDeviceInfo as DeviceDescriptor,
79
83
  DeviceStatus,
84
+ ProtocolInfo,
80
85
  ProtocolV2DeviceInfo,
81
86
  Success,
82
87
  } from '@onekeyfe/hd-transport';
@@ -835,8 +840,8 @@ export class Device extends EventEmitter {
835
840
 
836
841
  const expectedDeviceId = options?.deviceId;
837
842
  if (expectedDeviceId) {
838
- // Verify the physical device with a read-only Features request. An
839
- // empty-context Initialize would invalidate a cached V1 hidden-wallet session.
843
+ // 先只读校验物理设备身份;钱包上下文仍由下方携带完整参数的
844
+ // Initialize 选择,避免标准钱包请求复用此前的隐藏钱包上下文。
840
845
  this.passphraseState = undefined;
841
846
  const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
842
847
  this._updateFeatures(message);
@@ -864,15 +869,7 @@ export class Device extends EventEmitter {
864
869
  payload.derive_cardano = true;
865
870
  }
866
871
 
867
- const requiresWalletInitialize =
868
- !expectedDeviceId ||
869
- Boolean(internalState) ||
870
- Boolean(options?.passphraseState) ||
871
- options?.deriveCardano === true ||
872
- options?.initSession === true;
873
- if (requiresWalletInitialize) {
874
- await callInitialize(payload, options?.initSession);
875
- }
872
+ await callInitialize(payload, options?.initSession);
876
873
  } catch (error) {
877
874
  Log.error('Initialization failed:', error);
878
875
  throw error;
@@ -1048,40 +1045,60 @@ export class Device extends EventEmitter {
1048
1045
  }
1049
1046
 
1050
1047
  async probeProtocolV2RuntimeState(deviceInfo: ProtocolV2DeviceInfo, timeoutMs?: number) {
1051
- let deviceStatus: DeviceStatus;
1052
- try {
1053
- deviceStatus = await requestProtocolV2DeviceStatus({
1054
- commands: this.commands,
1055
- timeoutMs,
1056
- });
1057
- } catch (error) {
1058
- const runtimeMode = getProtocolV2RuntimeMode({
1059
- deviceInfo,
1060
- deviceStatusAvailable: false,
1061
- });
1062
- Log.debug('Protocol V2 DeviceStatusGet unavailable; use temporary loader fallback', {
1063
- runtimeMode,
1064
- error: error instanceof Error ? error.message : String(error),
1065
- });
1066
- return this.updateProtocolV2Features(deviceInfo, null, runtimeMode);
1048
+ const protocolInfo = await requestProtocolV2ProtocolInfo({
1049
+ commands: this.commands,
1050
+ timeoutMs,
1051
+ });
1052
+ const runtimeMode = getProtocolV2RuntimeMode(protocolInfo);
1053
+ const protocolV2DeviceType = resolveProtocolV2DeviceIdentity(
1054
+ deviceInfo.hw?.Device_type
1055
+ ).deviceType;
1056
+ if (runtimeMode === 'romloader' && protocolV2DeviceType !== EDeviceType.Pro2) {
1057
+ throw ERRORS.TypedError(
1058
+ HardwareErrorCode.DeviceInitializeFailed,
1059
+ 'Protocol V2 romloader mode is only supported for Pro2.'
1060
+ );
1061
+ }
1062
+ const deviceStatusSupported = supportsProtocolV2Message(
1063
+ protocolInfo,
1064
+ PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE
1065
+ );
1066
+
1067
+ if (runtimeMode === 'bootloader' || runtimeMode === 'romloader') {
1068
+ return this.updateProtocolV2Features(deviceInfo, null, runtimeMode, protocolInfo);
1069
+ }
1070
+
1071
+ if (!deviceStatusSupported) {
1072
+ if (runtimeMode === 'normal') {
1073
+ return this.updateProtocolV2Features(deviceInfo, null, runtimeMode, protocolInfo);
1074
+ }
1075
+ throw ERRORS.TypedError(
1076
+ HardwareErrorCode.DeviceInitializeFailed,
1077
+ `Unknown Protocol V2 build fingerprint without DeviceStatusGet capability: ${protocolInfo.build_fingerprint}`
1078
+ );
1067
1079
  }
1068
- return this.updateProtocolV2Features(deviceInfo, deviceStatus, 'normal');
1080
+
1081
+ const deviceStatus = await requestProtocolV2DeviceStatus({
1082
+ commands: this.commands,
1083
+ timeoutMs,
1084
+ });
1085
+ return this.updateProtocolV2Features(deviceInfo, deviceStatus, 'normal', protocolInfo);
1069
1086
  }
1070
1087
 
1071
1088
  updateProtocolV2Features(
1072
1089
  deviceInfo?: ProtocolV2DeviceInfo,
1073
1090
  deviceStatus?: DeviceStatus | null,
1074
- runtimeMode?: ProtocolV2RuntimeMode
1091
+ runtimeMode?: ProtocolV2RuntimeMode,
1092
+ protocolInfo?: ProtocolInfo
1075
1093
  ) {
1076
1094
  const previousDeviceId = this.getCurrentDeviceId();
1077
- const resolvedMode =
1078
- runtimeMode ??
1079
- (deviceStatus
1080
- ? getProtocolV2RuntimeMode({ deviceInfo, deviceStatusAvailable: true })
1081
- : this.state?.status.mode);
1095
+ const resolvedMode = runtimeMode ?? (deviceStatus ? 'normal' : this.state?.status.mode);
1082
1096
  if (deviceInfo) {
1083
1097
  this.updateState(mapProtocolV2DeviceInfoToState(deviceInfo, resolvedMode), 'device-info');
1084
1098
  }
1099
+ if (protocolInfo) {
1100
+ this.updateState({ raw: { protocolV2ProtocolInfo: protocolInfo } }, 'device-info');
1101
+ }
1085
1102
  if (deviceStatus) {
1086
1103
  this.updateState(mapProtocolV2DeviceStatusToState(deviceStatus), 'device-status');
1087
1104
  }
@@ -1150,7 +1167,7 @@ export class Device extends EventEmitter {
1150
1167
  attachToPinEnabled: null,
1151
1168
  unlockedAttachPin: null,
1152
1169
  },
1153
- raw: { protocolV2DeviceStatus: null },
1170
+ raw: { protocolV2ProtocolInfo: null, protocolV2DeviceStatus: null },
1154
1171
  },
1155
1172
  'transport-reconnect'
1156
1173
  );
@@ -1160,7 +1177,7 @@ export class Device extends EventEmitter {
1160
1177
  this.updateState(
1161
1178
  {
1162
1179
  status: { mode: 'normal', unlocked: null },
1163
- raw: { protocolV2DeviceStatus: null },
1180
+ raw: { protocolV2ProtocolInfo: null, protocolV2DeviceStatus: null },
1164
1181
  },
1165
1182
  'transport-reconnect'
1166
1183
  );
@@ -1406,7 +1423,16 @@ export class Device extends EventEmitter {
1406
1423
 
1407
1424
  isBootloader() {
1408
1425
  if (!this.state) return undefined;
1409
- return this.state.status.mode === 'bootloader' || this.state.status.mode === 'romloader';
1426
+ return this.state.status.mode === 'bootloader';
1427
+ }
1428
+
1429
+ isRomloader() {
1430
+ if (!this.state) return undefined;
1431
+ return (
1432
+ this.isProtocolV2() &&
1433
+ this.getCurrentDeviceType() === EDeviceType.Pro2 &&
1434
+ this.state.status.mode === 'romloader'
1435
+ );
1410
1436
  }
1411
1437
 
1412
1438
  isInitialized() {
@@ -6,6 +6,7 @@ import {
6
6
  type Messages,
7
7
  type Transport,
8
8
  type TransportCallOptions,
9
+ getSafeTransportLogPayload,
9
10
  } from '@onekeyfe/hd-transport';
10
11
 
11
12
  import TransportManager from '../data-manager/TransportManager';
@@ -282,7 +283,11 @@ export class DeviceCommands {
282
283
  ): Promise<DefaultMessageResponse> {
283
284
  const shouldReduceDebug = shouldReduceDebugForCall(type);
284
285
  if (!shouldReduceDebug) {
285
- Log.debug('[DeviceCommands] [call] Sending', type);
286
+ Log.debug(
287
+ '[DeviceCommands] [call] Sending',
288
+ type,
289
+ getSafeTransportLogPayload(msg ?? {}, type)
290
+ );
286
291
  }
287
292
 
288
293
  try {
@@ -290,13 +295,18 @@ export class DeviceCommands {
290
295
  this.callPromise = promise;
291
296
  const res = await promise;
292
297
  if (!shouldReduceDebug) {
293
- LogCore.debug('[DeviceCommands] [call] Received', res.type);
298
+ LogCore.debug(
299
+ '[DeviceCommands] [call] Received',
300
+ res.type,
301
+ getSafeTransportLogPayload(res.message, res.type)
302
+ );
294
303
  }
295
304
  return res;
296
305
  } catch (error) {
297
306
  LogCore.debug('[DeviceCommands] [call] Received error', {
298
307
  request: type,
299
308
  errorCode: error?.errorCode,
309
+ response: getSafeTransportLogPayload(error?.response?.data, type),
300
310
  });
301
311
  if (error.errorCode === HardwareErrorCode.BleDeviceBondError) {
302
312
  return {
@@ -1,6 +1,4 @@
1
1
  export class DeviceWalletSessionStore {
2
- private static readonly MAX_SESSIONS_PER_DEVICE = 3;
3
-
4
2
  private readonly walletSessions = new Map<string, Map<string, string>>();
5
3
 
6
4
  private readonly standardWalletSessions = new Map<
@@ -22,18 +20,6 @@ export class DeviceWalletSessionStore {
22
20
  deviceSessions = new Map<string, string>();
23
21
  this.walletSessions.set(deviceKey, deviceSessions);
24
22
  }
25
- if (
26
- !deviceSessions.has(passphraseState) &&
27
- deviceSessions.size >= DeviceWalletSessionStore.MAX_SESSIONS_PER_DEVICE
28
- ) {
29
- const oldestPassphraseState = deviceSessions.keys().next().value as string | undefined;
30
- if (oldestPassphraseState) {
31
- deviceSessions.delete(oldestPassphraseState);
32
- if (this.standardWalletSessions.get(deviceKey)?.passphraseState === oldestPassphraseState) {
33
- this.standardWalletSessions.delete(deviceKey);
34
- }
35
- }
36
- }
37
23
  deviceSessions.set(passphraseState, sessionId);
38
24
  }
39
25
 
@@ -260,7 +260,7 @@ export const buildProtocolV2FeaturesPayload = ({
260
260
  const attachToPinEnabled = status?.attach_to_pin_enabled ?? cached?.attachToPinEnabled ?? null;
261
261
  const unlockedAttachPin =
262
262
  status?.unlocked_by_attach_to_pin ?? cached?.unlockedAttachPin ?? undefined;
263
- const bootloaderMode = runtimeMode === 'bootloader';
263
+ const bootloaderMode = runtimeMode === 'bootloader' || runtimeMode === 'romloader';
264
264
  let mode: DeviceFeaturesMode = 'unknown';
265
265
  if (runtimeMode) {
266
266
  mode = runtimeMode;
@@ -4,6 +4,7 @@ import type {
4
4
  DeviceInfoGet,
5
5
  DeviceSEInfo,
6
6
  DeviceStatus,
7
+ ProtocolInfo,
7
8
  ProtocolV2DeviceInfo,
8
9
  } from '@onekeyfe/hd-transport';
9
10
  import type { DeviceCommands } from '../../device/DeviceCommands';
@@ -53,27 +54,54 @@ export const getProtocolV2SeState = (se?: DeviceSEInfo): ProtocolV2SeStateLabel
53
54
  export const getProtocolV2SeType = (se?: DeviceSEInfo): string | null =>
54
55
  normalizeEnumValue(DeviceSeType, se?.type);
55
56
 
56
- export type ProtocolV2RuntimeMode = 'normal' | 'bootloader';
57
+ export type ProtocolV2RuntimeMode = 'normal' | 'bootloader' | 'romloader';
57
58
 
58
59
  /**
59
- * TODO: Once firmware-pro2 exposes ProtocolInfo.build_fingerprint, parse the running
60
- * binary name and remove the temporary DeviceStatusGet-failure heuristic. romloader
61
- * is a separate recovery flow and is excluded until its contract is defined.
62
- *
63
- * The temporary contract has two states: DeviceStatusGet success means normal and
64
- * failure means bootloader. Do not infer mode from application/SE/romloader fields.
60
+ * Protocol V2 fingerprints use:
61
+ * <binary>__<version>__<commit>__<PROD|DEV>__<DEBUG|RELEASE>
65
62
  */
66
- export const getProtocolV2RuntimeMode = ({
67
- deviceInfo: _deviceInfo,
68
- deviceStatusAvailable,
69
- }: {
70
- deviceInfo?: ProtocolV2DeviceInfo | null;
71
- deviceStatusAvailable: boolean;
72
- }): ProtocolV2RuntimeMode => {
73
- if (deviceStatusAvailable) return 'normal';
74
- return 'bootloader';
63
+ export type ProtocolV2BuildFingerprint = {
64
+ binary: 'application' | 'bootloader' | 'romloader';
65
+ version: string;
66
+ commit: string;
67
+ environment: 'PROD' | 'DEV';
68
+ buildType: 'DEBUG' | 'RELEASE';
75
69
  };
76
70
 
71
+ export const parseProtocolV2BuildFingerprint = (
72
+ buildFingerprint: string | null | undefined
73
+ ): ProtocolV2BuildFingerprint | null => {
74
+ if (!buildFingerprint) return null;
75
+ const [binary, version, commit, environment, buildType, ...extra] = buildFingerprint.split('__');
76
+ if (
77
+ extra.length > 0 ||
78
+ (binary !== 'application' && binary !== 'bootloader' && binary !== 'romloader') ||
79
+ !version ||
80
+ !commit ||
81
+ (environment !== 'PROD' && environment !== 'DEV') ||
82
+ (buildType !== 'DEBUG' && buildType !== 'RELEASE')
83
+ ) {
84
+ return null;
85
+ }
86
+ return { binary, version, commit, environment, buildType };
87
+ };
88
+
89
+ export const getProtocolV2RuntimeMode = (
90
+ protocolInfo: ProtocolInfo
91
+ ): ProtocolV2RuntimeMode | undefined => {
92
+ const binary = parseProtocolV2BuildFingerprint(protocolInfo.build_fingerprint)?.binary;
93
+ if (binary === 'application') return 'normal';
94
+ return binary;
95
+ };
96
+
97
+ // MessageType_DeviceStatusGet in the Protocol V2 protobuf registry.
98
+ export const PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE = 60602;
99
+
100
+ export const supportsProtocolV2Message = (
101
+ protocolInfo: ProtocolInfo,
102
+ messageType: number
103
+ ): boolean => protocolInfo.supported_messages.includes(messageType);
104
+
77
105
  export const PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST = {
78
106
  targets: {
79
107
  hw: true,
@@ -131,6 +159,20 @@ export const PROTOCOL_V2_FULL_DEVICE_INFO_REQUEST = {
131
159
  export const PROTOCOL_V2_DEVICE_INFO_REQUEST = PROTOCOL_V2_FULL_DEVICE_INFO_REQUEST;
132
160
  export const PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS = 10 * 1000;
133
161
 
162
+ export async function requestProtocolV2ProtocolInfo({
163
+ commands,
164
+ timeoutMs,
165
+ }: {
166
+ commands: DeviceCommands;
167
+ timeoutMs?: number;
168
+ }): Promise<ProtocolInfo> {
169
+ const response =
170
+ timeoutMs === undefined
171
+ ? await commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {})
172
+ : await commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {}, { timeoutMs });
173
+ return response.message;
174
+ }
175
+
134
176
  export async function requestProtocolV2DeviceInfo({
135
177
  commands,
136
178
  timeoutMs = PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS,
@@ -1,13 +1,18 @@
1
1
  export {
2
2
  PROTOCOL_V2_DEVICE_INFO_REQUEST,
3
3
  PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS,
4
+ PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE,
4
5
  PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST,
5
6
  PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
6
7
  getProtocolV2RuntimeMode,
7
8
  getProtocolV2SeState,
8
9
  getProtocolV2SeType,
10
+ parseProtocolV2BuildFingerprint,
11
+ requestProtocolV2ProtocolInfo,
12
+ supportsProtocolV2Message,
9
13
  } from './features';
10
14
  export type {
15
+ ProtocolV2BuildFingerprint,
11
16
  ProtocolV2DeviceInfo,
12
17
  ProtocolV2FirmwareImageInfo,
13
18
  ProtocolV2SEInfo,
@@ -2,14 +2,19 @@ import type { DeviceSettings, DeviceSettingsPage } from '@onekeyfe/hd-transport'
2
2
  import type { UnlockPolicy } from '../../api/BaseMethod';
3
3
  import type { ProtocolV2InteractionDescriptor } from './uiInteraction';
4
4
 
5
- const LOCK_FREE_DEVICE_SETTINGS = new Set<keyof DeviceSettings>([
5
+ export const PROTOCOL_V2_LOCK_FREE_DEVICE_SETTINGS = [
6
6
  'language',
7
7
  'brightness',
8
+ 'animation_enable',
9
+ 'tap_to_wake',
8
10
  'haptic_feedback',
9
- ]);
11
+ 'device_name_display_enabled',
12
+ ] as const satisfies readonly (keyof DeviceSettings)[];
13
+
14
+ const lockFreeDeviceSettings = new Set<keyof DeviceSettings>(PROTOCOL_V2_LOCK_FREE_DEVICE_SETTINGS);
10
15
 
11
16
  export const getProtocolV2SettingsUnlockPolicy = (settings: DeviceSettings): UnlockPolicy =>
12
- Object.keys(settings).every(key => LOCK_FREE_DEVICE_SETTINGS.has(key as keyof DeviceSettings))
17
+ Object.keys(settings).every(key => lockFreeDeviceSettings.has(key as keyof DeviceSettings))
13
18
  ? 'none'
14
19
  : 'unlock-before-run';
15
20
 
@@ -1,7 +1,7 @@
1
1
  import { LoggerNames, getLogger } from '../../utils';
2
2
  import { isDeviceLockedError } from './lockedError';
3
3
  import { isProtocolV2UiEnabled, resolveProtocolV2UiInteraction } from './uiInteraction';
4
- import { restoreProtocolV2WalletSession } from './walletSession';
4
+ import { refreshProtocolV2DeviceStatus, restoreProtocolV2WalletSession } from './walletSession';
5
5
 
6
6
  import type { BaseMethod } from '../../api/BaseMethod';
7
7
  import type { Device } from '../../device/Device';
@@ -46,8 +46,23 @@ export async function runMethodWithUnlockRetry(
46
46
  uiCoordinator?: UiInteractionCoordinator
47
47
  ) {
48
48
  const shouldEmitUi = isProtocolV2UiEnabled(method);
49
+ const isProtocolV2 = device.isProtocolV2();
50
+ const requiresFreshStatus =
51
+ isProtocolV2 &&
52
+ method.unlockPolicy === 'unlock-before-run' &&
53
+ !device.isBootloader?.() &&
54
+ !device.isRomloader?.();
55
+
56
+ if (requiresFreshStatus) {
57
+ await refreshProtocolV2DeviceStatus(device);
58
+ }
59
+
49
60
  const shouldUnlockBeforeRun =
50
- device.isProtocolV2() && method.unlockPolicy !== 'none' && device.features?.unlocked === false;
61
+ isProtocolV2 &&
62
+ method.unlockPolicy !== 'none' &&
63
+ !device.isBootloader?.() &&
64
+ !device.isRomloader?.() &&
65
+ device.features?.unlocked === false;
51
66
 
52
67
  if (shouldUnlockBeforeRun) {
53
68
  if (shouldEmitUi) {
@@ -68,11 +83,7 @@ export async function runMethodWithUnlockRetry(
68
83
  try {
69
84
  return await method.run();
70
85
  } catch (error) {
71
- if (
72
- !device.isProtocolV2() ||
73
- method.unlockPolicy !== 'retry-on-locked' ||
74
- !isDeviceLockedError(error)
75
- ) {
86
+ if (!isProtocolV2 || method.unlockPolicy !== 'retry-on-locked' || !isDeviceLockedError(error)) {
76
87
  throw error;
77
88
  }
78
89
 
@@ -3,13 +3,18 @@ import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
3
3
 
4
4
  import { DEVICE } from '../../events';
5
5
  import { assertCompleteDeviceSession } from './deviceSession';
6
- import { isDeviceLockedError } from './lockedError';
7
6
 
8
- import type { DeviceSessionGet } from '@onekeyfe/hd-transport';
7
+ import type { DeviceSessionAskPassphrase, DeviceSessionGet } from '@onekeyfe/hd-transport';
9
8
  import type { Device } from '../../device/Device';
10
9
 
11
10
  const HOST_PASSPHRASE_MAX_BYTES = 50;
12
11
 
12
+ const isWalletSessionInvalidError = (error: unknown) =>
13
+ typeof error === 'object' &&
14
+ error !== null &&
15
+ 'errorCode' in error &&
16
+ (error as { errorCode?: unknown }).errorCode === HardwareErrorCode.WalletSessionInvalid;
17
+
13
18
  const utf8ByteLength = (value: string): number | undefined => {
14
19
  let length = 0;
15
20
  for (const character of value) {
@@ -39,34 +44,11 @@ const negotiateEventlessWalletSession = async (device: Device) => {
39
44
  });
40
45
  };
41
46
 
42
- const getDeviceSession = async (device: Device, request: DeviceSessionGet) => {
43
- try {
44
- return await device.commands.typedCall('DeviceSessionGet', 'DeviceSession', request);
45
- } catch (error) {
46
- if (!isDeviceLockedError(error) || !request.session_id) {
47
- throw error;
48
- }
49
- await device.unlockDevice(DeviceSessionPinType.Main);
50
- return device.commands.typedCall('DeviceSessionGet', 'DeviceSession', request);
51
- }
52
- };
47
+ const getDeviceSession = async (device: Device, request: DeviceSessionGet) =>
48
+ device.commands.typedCall('DeviceSessionGet', 'DeviceSession', request);
53
49
 
54
- const askDevicePassphrase = async (device: Device, passphrase?: string) => {
55
- const request = () =>
56
- device.commands.typedCall(
57
- 'DeviceSessionAskPassphrase',
58
- 'Success',
59
- passphrase ? { passphrase, on_device: false } : { on_device: true }
60
- );
61
- try {
62
- await request();
63
- } catch (error) {
64
- if (!isDeviceLockedError(error)) {
65
- throw error;
66
- }
67
- await device.unlockDevice(DeviceSessionPinType.Main);
68
- await request();
69
- }
50
+ const askDevicePassphrase = async (device: Device, requestPayload: DeviceSessionAskPassphrase) => {
51
+ await device.commands.typedCall('DeviceSessionAskPassphrase', 'Success', requestPayload);
70
52
  await refreshProtocolV2DeviceStatus(device);
71
53
  };
72
54
 
@@ -126,25 +108,33 @@ const selectDeviceSession = async (device: Device, expectedPassphraseState?: str
126
108
  }
127
109
 
128
110
  if (hasHostPassphrase) {
129
- await askDevicePassphrase(device, hostPassphrase);
111
+ await askDevicePassphrase(device, {
112
+ passphrase: hostPassphrase,
113
+ on_device: false,
114
+ });
130
115
  return getDeviceSession(device, {});
131
116
  }
132
117
 
133
118
  device.emit(DEVICE.PASSPHRASE_ON_DEVICE, device, metadata);
134
- await askDevicePassphrase(device);
119
+ await askDevicePassphrase(device, { on_device: true });
135
120
  return getDeviceSession(device, {});
136
121
  };
137
122
 
138
123
  export async function getProtocolV2WalletSession(
139
124
  device: Device,
140
125
  options?: {
126
+ forceWalletSelection?: boolean;
127
+ /** @deprecated 兼容旧 getPassphraseState;新流程使用 forceWalletSelection。 */
141
128
  initSession?: boolean;
142
129
  expectedPassphraseState?: string;
143
130
  onlyMainPin?: boolean;
144
131
  resumeOnly?: boolean;
145
132
  }
146
133
  ) {
147
- if (options?.initSession) {
134
+ const forceWalletSelection =
135
+ options?.forceWalletSelection === true || options?.initSession === true;
136
+
137
+ if (forceWalletSelection) {
148
138
  if (options.onlyMainPin) {
149
139
  device.clearStandardInternalState?.();
150
140
  } else {
@@ -185,7 +175,10 @@ export async function getProtocolV2WalletSession(
185
175
  resumed = true;
186
176
  } catch (error) {
187
177
  device.clearStandardInternalState?.();
188
- throw error;
178
+ if (!isWalletSessionInvalidError(error)) {
179
+ throw error;
180
+ }
181
+ resumed = false;
189
182
  }
190
183
  }
191
184
 
@@ -199,7 +192,10 @@ export async function getProtocolV2WalletSession(
199
192
  resumed = true;
200
193
  } catch (error) {
201
194
  device.clearInternalState();
202
- throw error;
195
+ if (options?.resumeOnly || !isWalletSessionInvalidError(error)) {
196
+ throw error;
197
+ }
198
+ resumed = false;
203
199
  }
204
200
  }
205
201
 
@@ -224,6 +220,10 @@ export async function getProtocolV2WalletSession(
224
220
  }
225
221
  if (expectedPassphraseState && expectedPassphraseState !== message.btc_test_address) {
226
222
  resumed = false;
223
+ if (options?.resumeOnly) {
224
+ device.clearInternalState();
225
+ throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
226
+ }
227
227
  if (options?.onlyMainPin) {
228
228
  device.clearStandardInternalState?.();
229
229
  await device.unlockDevice(DeviceSessionPinType.Main);
@@ -258,7 +258,7 @@ export async function getProtocolV2WalletSession(
258
258
  message.btc_test_address,
259
259
  device.getCurrentDeviceId(),
260
260
  message.session_id,
261
- options?.initSession ? null : cachedSessionId ?? null,
261
+ forceWalletSelection ? null : cachedSessionId ?? null,
262
262
  ] as const;
263
263
  if (options?.onlyMainPin) {
264
264
  device.updateInternalState(...internalStateArgs, 'standard');
@@ -36,7 +36,7 @@ export type OpenWalletSessionPayload = OpenWalletSessionPayloadBase &
36
36
  (
37
37
  | {
38
38
  walletType: 'standard';
39
- passphraseState: string | null;
39
+ passphraseState: null;
40
40
  }
41
41
  | {
42
42
  walletType: 'hidden';
@@ -1,5 +1,5 @@
1
1
  import type { CommonParams, Response } from '../params';
2
- import type { DevOnboardingStatus, Success } from '@onekeyfe/hd-transport';
2
+ import type { OnboardingStatus, Success } from '@onekeyfe/hd-transport';
3
3
  import type { DeviceRebootParams } from '../../api/protocol-v2/helpers';
4
4
  import type {
5
5
  DeviceUploadWallpaperParams,
@@ -45,7 +45,7 @@ export declare function deviceReboot(
45
45
  export declare function deviceGetOnboardingStatus(
46
46
  connectId: string,
47
47
  params?: CommonParams
48
- ): Response<DevOnboardingStatus>;
48
+ ): Response<OnboardingStatus>;
49
49
 
50
50
  export declare function deviceUploadWallpaper(
51
51
  connectId: string,
@@ -7,6 +7,7 @@ import type {
7
7
  OneKeyDeviceCommType,
8
8
  ProtocolV2DeviceInfo,
9
9
  DeviceStatus as ProtocolV2DeviceStatus,
10
+ ProtocolInfo as ProtocolV2ProtocolInfo,
10
11
  } from '@onekeyfe/hd-transport';
11
12
 
12
13
  export type DeviceStatus = 'available' | 'occupied' | 'used';
@@ -159,6 +160,7 @@ export type DeviceFeaturesVerify = {
159
160
  export type DeviceFeaturesRaw = {
160
161
  protocolV1Features?: PROTO.Features;
161
162
  protocolV1OneKeyFeatures?: OnekeyFeatures;
163
+ protocolV2ProtocolInfo?: ProtocolV2ProtocolInfo;
162
164
  protocolV2DeviceInfo?: ProtocolV2DeviceInfo;
163
165
  protocolV2DeviceStatus?: ProtocolV2DeviceStatus;
164
166
  };