@onekeyfe/hd-core 1.2.0-alpha.10 → 1.2.0-alpha.12

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 (122) hide show
  1. package/__tests__/DeviceCommands.test.ts +25 -0
  2. package/__tests__/RequestQueue.test.ts +34 -0
  3. package/__tests__/pro2Wallpaper.test.ts +50 -0
  4. package/__tests__/protocol-v2.test.ts +511 -61
  5. package/__tests__/protocolV2FileWrite.test.ts +55 -0
  6. package/dist/api/BaseMethod.d.ts +4 -0
  7. package/dist/api/BaseMethod.d.ts.map +1 -1
  8. package/dist/api/FileWrite.d.ts.map +1 -1
  9. package/dist/api/FirmwareUpdateV4.d.ts +0 -4
  10. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  11. package/dist/api/GetPassphraseState.d.ts +1 -6
  12. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  13. package/dist/api/UploadPortfolio.d.ts +18 -0
  14. package/dist/api/UploadPortfolio.d.ts.map +1 -0
  15. package/dist/api/helpers/protocolV2FileWrite.d.ts +32 -0
  16. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -0
  17. package/dist/api/index.d.ts +6 -0
  18. package/dist/api/index.d.ts.map +1 -1
  19. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +6 -0
  20. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +1 -0
  21. package/dist/api/protocol-v2/DeviceSessionGet.d.ts +1 -4
  22. package/dist/api/protocol-v2/DeviceSessionGet.d.ts.map +1 -1
  23. package/dist/api/protocol-v2/DeviceSettingsGet.d.ts +6 -0
  24. package/dist/api/protocol-v2/DeviceSettingsGet.d.ts.map +1 -0
  25. package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts +16 -0
  26. package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts.map +1 -0
  27. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts +12 -0
  28. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +1 -0
  29. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +26 -0
  30. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -0
  31. package/dist/core/RequestQueue.d.ts +2 -0
  32. package/dist/core/RequestQueue.d.ts.map +1 -1
  33. package/dist/core/index.d.ts +1 -0
  34. package/dist/core/index.d.ts.map +1 -1
  35. package/dist/device/Device.d.ts.map +1 -1
  36. package/dist/device/DeviceCommands.d.ts +4 -4
  37. package/dist/device/DeviceCommands.d.ts.map +1 -1
  38. package/dist/deviceProfile/buildDeviceFeatures.d.ts +2 -2
  39. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  40. package/dist/deviceProfile/buildDeviceProfile.d.ts.map +1 -1
  41. package/dist/events/call.d.ts +7 -0
  42. package/dist/events/call.d.ts.map +1 -1
  43. package/dist/events/core.d.ts +2 -2
  44. package/dist/events/core.d.ts.map +1 -1
  45. package/dist/events/iframe.d.ts +1 -0
  46. package/dist/events/iframe.d.ts.map +1 -1
  47. package/dist/index.d.ts +80 -23
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +922 -472
  50. package/dist/inject.d.ts +3 -2
  51. package/dist/inject.d.ts.map +1 -1
  52. package/dist/lowLevelInject.d.ts +2 -1
  53. package/dist/lowLevelInject.d.ts.map +1 -1
  54. package/dist/protocols/protocol-v2/features.d.ts +1 -0
  55. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  56. package/dist/protocols/protocol-v2/index.d.ts +1 -1
  57. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  58. package/dist/protocols/protocol-v2/unlockRetry.d.ts +9 -0
  59. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -0
  60. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  61. package/dist/topLevelInject.d.ts.map +1 -1
  62. package/dist/types/api/firmwareUpdate.d.ts +2 -0
  63. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  64. package/dist/types/api/getDeviceInfo.d.ts +1 -1
  65. package/dist/types/api/getDeviceInfo.d.ts.map +1 -1
  66. package/dist/types/api/getPassphraseState.d.ts +2 -10
  67. package/dist/types/api/getPassphraseState.d.ts.map +1 -1
  68. package/dist/types/api/index.d.ts +9 -2
  69. package/dist/types/api/index.d.ts.map +1 -1
  70. package/dist/types/api/protocolV2.d.ts +20 -4
  71. package/dist/types/api/protocolV2.d.ts.map +1 -1
  72. package/dist/types/device.d.ts +1 -1
  73. package/dist/types/device.d.ts.map +1 -1
  74. package/dist/utils/deviceFeaturesUtils.d.ts +0 -2
  75. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  76. package/dist/utils/index.d.ts +2 -0
  77. package/dist/utils/index.d.ts.map +1 -1
  78. package/dist/utils/patch.d.ts +1 -1
  79. package/dist/utils/patch.d.ts.map +1 -1
  80. package/dist/utils/pro2Wallpaper.d.ts +12 -0
  81. package/dist/utils/pro2Wallpaper.d.ts.map +1 -0
  82. package/package.json +4 -4
  83. package/src/api/BaseMethod.ts +18 -0
  84. package/src/api/FileWrite.ts +18 -178
  85. package/src/api/FirmwareUpdateV4.ts +21 -91
  86. package/src/api/GetPassphraseState.ts +9 -18
  87. package/src/api/UploadPortfolio.ts +37 -0
  88. package/src/api/helpers/protocolV2FileWrite.ts +164 -0
  89. package/src/api/index.ts +6 -0
  90. package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +18 -0
  91. package/src/api/protocol-v2/DeviceSessionGet.ts +3 -14
  92. package/src/api/protocol-v2/DeviceSettingsGet.ts +16 -0
  93. package/src/api/protocol-v2/DeviceSettingsPageShow.ts +59 -0
  94. package/src/api/protocol-v2/DeviceSettingsSet.ts +39 -0
  95. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +124 -0
  96. package/src/core/RequestQueue.ts +19 -0
  97. package/src/core/index.ts +10 -1
  98. package/src/data/messages/messages-protocol-v2.json +92 -8
  99. package/src/device/Device.ts +17 -3
  100. package/src/device/DeviceCommands.ts +9 -3
  101. package/src/deviceProfile/buildDeviceFeatures.ts +20 -12
  102. package/src/deviceProfile/buildDeviceProfile.ts +8 -6
  103. package/src/events/call.ts +6 -0
  104. package/src/events/core.ts +2 -0
  105. package/src/events/iframe.ts +1 -0
  106. package/src/index.ts +4 -0
  107. package/src/inject.ts +15 -0
  108. package/src/lowLevelInject.ts +3 -0
  109. package/src/protocols/protocol-v2/features.ts +19 -1
  110. package/src/protocols/protocol-v2/index.ts +2 -0
  111. package/src/protocols/protocol-v2/unlockRetry.ts +46 -0
  112. package/src/protocols/protocol-v2/walletSession.ts +14 -5
  113. package/src/topLevelInject.ts +2 -0
  114. package/src/types/api/firmwareUpdate.ts +12 -0
  115. package/src/types/api/getDeviceInfo.ts +1 -1
  116. package/src/types/api/getPassphraseState.ts +2 -11
  117. package/src/types/api/index.ts +14 -1
  118. package/src/types/api/protocolV2.ts +52 -3
  119. package/src/types/device.ts +1 -0
  120. package/src/utils/deviceFeaturesUtils.ts +3 -8
  121. package/src/utils/index.ts +6 -0
  122. package/src/utils/pro2Wallpaper.ts +111 -0
@@ -1,26 +1,15 @@
1
1
  import { BaseMethod } from '../BaseMethod';
2
2
 
3
- export type DeviceSessionGetParams = {
4
- sessionId?: string;
5
- };
6
-
7
- export default class DeviceSessionGet extends BaseMethod<DeviceSessionGetParams> {
3
+ export default class DeviceSessionGet extends BaseMethod {
8
4
  init() {
9
5
  this.requireProtocolV2 = true;
10
6
  this.useDevicePassphraseState = false;
11
7
  this.skipForceUpdateCheck = true;
12
- this.params = {
13
- sessionId: this.payload.sessionId,
14
- };
8
+ this.params = undefined;
15
9
  }
16
10
 
17
11
  async run() {
18
- const payload = this.params.sessionId ? { session_id: this.params.sessionId } : {};
19
- const { message } = await this.device.commands.typedCall(
20
- 'DeviceSessionGet',
21
- 'DeviceSession',
22
- payload
23
- );
12
+ const { message } = await this.device.commands.typedCall('DeviceSessionGet', 'DeviceSession', {});
24
13
  return message;
25
14
  }
26
15
  }
@@ -0,0 +1,16 @@
1
+ import { BaseMethod } from '../BaseMethod';
2
+
3
+ export default class DeviceSettingsGet extends BaseMethod {
4
+ init() {
5
+ this.requireProtocolV2 = true;
6
+ this.unlockPolicy = 'retry-on-locked';
7
+ this.skipForceUpdateCheck = true;
8
+ this.useDevicePassphraseState = false;
9
+ this.params = undefined;
10
+ }
11
+
12
+ async run() {
13
+ const res = await this.device.commands.typedCall('DeviceSettingsGet', 'DeviceSettings', {});
14
+ return Promise.resolve(res.message);
15
+ }
16
+ }
@@ -0,0 +1,59 @@
1
+ import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
2
+
3
+ import { BaseMethod } from '../BaseMethod';
4
+ import { invalidParameter } from '../helpers/filesystemValidation';
5
+
6
+ export type SupportedDeviceSettingsPage = DeviceSettingsPage;
7
+
8
+ export type DeviceSettingsPageShowParams = {
9
+ page?:
10
+ | SupportedDeviceSettingsPage
11
+ | 'DeviceReset'
12
+ | 'DevicePinChange'
13
+ | 'DevicePassphrase'
14
+ | 'DeviceAirgap';
15
+ fieldName?: string;
16
+ field_name?: string;
17
+ };
18
+
19
+ const SUPPORTED_PAGES = new Set<number>([
20
+ DeviceSettingsPage.DeviceReset,
21
+ DeviceSettingsPage.DevicePinChange,
22
+ DeviceSettingsPage.DevicePassphrase,
23
+ DeviceSettingsPage.DeviceAirgap,
24
+ ]);
25
+
26
+ function normalizePage(value: DeviceSettingsPageShowParams['page']): SupportedDeviceSettingsPage {
27
+ const page = typeof value === 'string' ? DeviceSettingsPage[value] : value;
28
+ if (page !== undefined && SUPPORTED_PAGES.has(page)) {
29
+ return page as SupportedDeviceSettingsPage;
30
+ }
31
+ throw invalidParameter(
32
+ 'Parameter [page] must be DeviceReset, DevicePinChange, DevicePassphrase, or DeviceAirgap.'
33
+ );
34
+ }
35
+
36
+ export default class DeviceSettingsPageShow extends BaseMethod<{
37
+ page: SupportedDeviceSettingsPage;
38
+ field_name?: string;
39
+ }> {
40
+ init() {
41
+ this.requireProtocolV2 = true;
42
+ this.unlockPolicy = 'retry-on-locked';
43
+ this.skipForceUpdateCheck = true;
44
+ this.useDevicePassphraseState = false;
45
+ this.params = {
46
+ page: normalizePage(this.payload.page),
47
+ field_name: this.payload.field_name ?? this.payload.fieldName,
48
+ };
49
+ }
50
+
51
+ async run() {
52
+ const res = await this.device.commands.typedCall(
53
+ 'DeviceSettingsPageShow',
54
+ 'Success',
55
+ this.params
56
+ );
57
+ return Promise.resolve(res.message);
58
+ }
59
+ }
@@ -0,0 +1,39 @@
1
+ import { BaseMethod } from '../BaseMethod';
2
+ import { invalidParameter } from '../helpers/filesystemValidation';
3
+
4
+ import type { DeviceSettings } from '@onekeyfe/hd-transport';
5
+
6
+ export type DeviceSettingsSetParams = {
7
+ settings?: Omit<DeviceSettings, 'passphrase_enable' | 'airgap_mode'>;
8
+ };
9
+
10
+ export default class DeviceSettingsSet extends BaseMethod<{
11
+ settings: Omit<DeviceSettings, 'passphrase_enable' | 'airgap_mode'>;
12
+ }> {
13
+ init() {
14
+ const settings = this.payload.settings;
15
+ if (!settings || typeof settings !== 'object' || Array.isArray(settings)) {
16
+ throw invalidParameter('Parameter [settings] must be an object.');
17
+ }
18
+
19
+ const {
20
+ passphrase_enable: _passphraseEnable,
21
+ airgap_mode: _airgapMode,
22
+ ...supported
23
+ } = settings as DeviceSettings;
24
+ if (Object.keys(supported).length === 0) {
25
+ throw invalidParameter('Parameter [settings] must contain at least one supported setting.');
26
+ }
27
+
28
+ this.requireProtocolV2 = true;
29
+ this.unlockPolicy = 'retry-on-locked';
30
+ this.skipForceUpdateCheck = true;
31
+ this.useDevicePassphraseState = false;
32
+ this.params = { settings: supported };
33
+ }
34
+
35
+ async run() {
36
+ const res = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', this.params);
37
+ return Promise.resolve(res.message);
38
+ }
39
+ }
@@ -0,0 +1,124 @@
1
+ import { blake2s } from '@noble/hashes/blake2s';
2
+ import { bytesToHex } from '@noble/hashes/utils';
3
+
4
+ import { BaseMethod } from '../BaseMethod';
5
+ import { invalidParameter } from '../helpers/filesystemValidation';
6
+ import { writeProtocolV2File } from '../helpers/protocolV2FileWrite';
7
+ import {
8
+ encodePro2Wallpaper,
9
+ PRO2_WALLPAPER_HEIGHT,
10
+ PRO2_WALLPAPER_WIDTH,
11
+ type Pro2WallpaperColorFormat,
12
+ } from '../../utils/pro2Wallpaper';
13
+
14
+ export type DeviceUploadWallpaperParams = {
15
+ width: number;
16
+ height: number;
17
+ rgba: Uint8Array | ArrayBuffer;
18
+ fileName?: string;
19
+ chunkSize?: number;
20
+ };
21
+
22
+ export type DeviceUploadWallpaperResponse = {
23
+ path: string;
24
+ size: number;
25
+ colorFormat: Pro2WallpaperColorFormat;
26
+ message?: string;
27
+ };
28
+
29
+ const WALLPAPER_DIRECTORY = 'vol0:/wallpapers/user';
30
+ const SAFE_FILE_NAME = /^[A-Za-z0-9_-]+(?:\.bin)?$/;
31
+
32
+ function normalizeFileName(fileName: string | undefined, data: Uint8Array): string {
33
+ if (fileName !== undefined && (!fileName || !SAFE_FILE_NAME.test(fileName))) {
34
+ throw invalidParameter(
35
+ 'Parameter [fileName] may only contain letters, numbers, underscores, hyphens and an optional .bin suffix.'
36
+ );
37
+ }
38
+ const baseName = fileName ?? `wallpaper-${bytesToHex(blake2s(data)).slice(0, 12)}`;
39
+ return baseName.endsWith('.bin') ? baseName : `${baseName}.bin`;
40
+ }
41
+
42
+ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpaperParams> {
43
+ private encoded?: { data: Uint8Array; colorFormat: Pro2WallpaperColorFormat };
44
+
45
+ private directoryReady = false;
46
+
47
+ private uploaded = false;
48
+
49
+ private path = '';
50
+
51
+ init() {
52
+ const { width, height, rgba, fileName, chunkSize } = this.payload;
53
+ if (width !== PRO2_WALLPAPER_WIDTH || height !== PRO2_WALLPAPER_HEIGHT) {
54
+ throw invalidParameter(
55
+ `Pro2 wallpaper dimensions must be ${PRO2_WALLPAPER_WIDTH}x${PRO2_WALLPAPER_HEIGHT}.`
56
+ );
57
+ }
58
+ if (!(rgba instanceof ArrayBuffer) && !ArrayBuffer.isView(rgba)) {
59
+ throw invalidParameter('Parameter [rgba] must be an ArrayBuffer or Uint8Array.');
60
+ }
61
+ if (chunkSize !== undefined && (!Number.isInteger(chunkSize) || chunkSize <= 0)) {
62
+ throw invalidParameter('Parameter [chunkSize] must be a positive integer.');
63
+ }
64
+
65
+ const rgbaBytes =
66
+ rgba instanceof ArrayBuffer
67
+ ? rgba
68
+ : new Uint8Array(rgba.buffer, rgba.byteOffset, rgba.byteLength);
69
+ this.encoded = encodePro2Wallpaper({ width, height, rgba: rgbaBytes });
70
+ this.path = `${WALLPAPER_DIRECTORY}/${normalizeFileName(fileName, this.encoded.data)}`;
71
+ this.params = { width, height, rgba: rgbaBytes, fileName, chunkSize };
72
+ this.requireProtocolV2 = true;
73
+ this.unlockPolicy = 'retry-on-locked';
74
+ this.skipForceUpdateCheck = true;
75
+ this.useDevicePassphraseState = false;
76
+ }
77
+
78
+ private async ensureDirectory() {
79
+ if (this.directoryReady) return;
80
+ try {
81
+ await this.device.commands.typedCall('FilesystemDirMake', 'Success', {
82
+ path: WALLPAPER_DIRECTORY,
83
+ });
84
+ } catch (error) {
85
+ const message = error instanceof Error ? error.message : String(error);
86
+ if (!/exist/i.test(message)) throw error;
87
+ }
88
+ this.directoryReady = true;
89
+ }
90
+
91
+ private async upload() {
92
+ if (this.uploaded) return;
93
+ const encoded = this.encoded;
94
+ if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
95
+
96
+ await writeProtocolV2File({
97
+ commands: this.device.commands,
98
+ path: this.path,
99
+ data: encoded.data,
100
+ totalSize: encoded.data.byteLength,
101
+ chunkSize: this.params.chunkSize,
102
+ overwrite: true,
103
+ append: false,
104
+ throwIfAborted: () => this.throwIfAborted(),
105
+ });
106
+ this.uploaded = true;
107
+ }
108
+
109
+ async run(): Promise<DeviceUploadWallpaperResponse> {
110
+ const encoded = this.encoded;
111
+ if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
112
+ await this.ensureDirectory();
113
+ await this.upload();
114
+ const response = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', {
115
+ settings: { wallpaper_path: this.path },
116
+ });
117
+ return {
118
+ path: this.path,
119
+ size: encoded.data.byteLength,
120
+ colorFormat: encoded.colorFormat,
121
+ message: response.message?.message,
122
+ };
123
+ }
124
+ }
@@ -14,6 +14,8 @@ export type RequestTask = {
14
14
  export default class RequestQueue {
15
15
  private requestQueue = new Map<number, RequestTask>();
16
16
 
17
+ private operationRequestIds = new Map<string, number>();
18
+
17
19
  private pendingCallbackTasks = new Map<string, Deferred<void>>();
18
20
 
19
21
  // 生成唯一请求ID
@@ -30,11 +32,21 @@ export default class RequestQueue {
30
32
  method.responseID = requestId;
31
33
  }
32
34
  const abortController = new AbortController();
35
+ method.abortSignal = abortController.signal;
33
36
  const task = { id: requestId, method, abortController };
34
37
  this.requestQueue.set(requestId, task);
38
+ const operationId = method.payload?.operationId;
39
+ if (typeof operationId === 'string' && operationId) {
40
+ this.operationRequestIds.set(operationId, requestId);
41
+ }
35
42
  return task;
36
43
  }
37
44
 
45
+ public abortOperation(operationId: string) {
46
+ const requestId = this.operationRequestIds.get(operationId);
47
+ return requestId === undefined ? false : this.abortRequest(requestId);
48
+ }
49
+
38
50
  public getTask(requestId: number): RequestTask | undefined {
39
51
  return this.requestQueue.get(requestId);
40
52
  }
@@ -105,6 +117,13 @@ export default class RequestQueue {
105
117
 
106
118
  // 删除请求
107
119
  public releaseTask(requestId: number) {
120
+ const operationId = this.requestQueue.get(requestId)?.method.payload?.operationId;
121
+ if (
122
+ typeof operationId === 'string' &&
123
+ this.operationRequestIds.get(operationId) === requestId
124
+ ) {
125
+ this.operationRequestIds.delete(operationId);
126
+ }
108
127
  this.requestQueue.delete(requestId);
109
128
  }
110
129
 
package/src/core/index.ts CHANGED
@@ -56,6 +56,7 @@ import TransportManager from '../data-manager/TransportManager';
56
56
  import DeviceConnector from '../device/DeviceConnector';
57
57
  import RequestQueue from './RequestQueue';
58
58
  import { getSynchronize } from '../utils/getSynchronize';
59
+ import { runMethodWithUnlockRetry } from '../protocols/protocol-v2/unlockRetry';
59
60
 
60
61
  import type { ConnectSettings, Features, KnownDevice } from '../types';
61
62
  import type { CoreMessage, IFrameCallMessage, UiPromise, UiPromiseResponse } from '../events';
@@ -616,7 +617,7 @@ const onCallDevice = async (
616
617
  method.device?.commands?.checkDisposed();
617
618
 
618
619
  try {
619
- const response: object = await method.run();
620
+ const response: object = await runMethodWithUnlockRetry(method, device);
620
621
  messageResponse = createResponseMessage(method.responseID, true, response);
621
622
  requestQueue.resolveRequest(method.responseID, messageResponse);
622
623
  completeMethodRequestContext(method);
@@ -1341,6 +1342,10 @@ export default class Core extends EventEmitter {
1341
1342
  Log.debug(`[Core] Created SDK instance: ${this.sdkInstanceId}`);
1342
1343
  }
1343
1344
 
1345
+ cancelOperation(operationId: string) {
1346
+ this.requestQueue.abortOperation(operationId);
1347
+ }
1348
+
1344
1349
  private getCoreContext() {
1345
1350
  return {
1346
1351
  sdkInstanceId: this.sdkInstanceId,
@@ -1409,6 +1414,10 @@ export default class Core extends EventEmitter {
1409
1414
  cancel(this.getCoreContext(), message.payload.connectId);
1410
1415
  break;
1411
1416
  }
1417
+ case IFRAME.CANCEL_OPERATION: {
1418
+ this.cancelOperation(message.payload.operationId);
1419
+ break;
1420
+ }
1412
1421
  case IFRAME.CALLBACK: {
1413
1422
  Log.log('callback message: ', message);
1414
1423
  postMessage(message);
@@ -467,6 +467,7 @@
467
467
  "MessageType_DeviceSettings": 60410,
468
468
  "MessageType_DeviceSettingsGet": 60411,
469
469
  "MessageType_DeviceSettingsSet": 60412,
470
+ "MessageType_DeviceSettingsPageShow": 60413,
470
471
  "MessageType_DeviceCertificate": 60420,
471
472
  "MessageType_DeviceCertificateWrite": 60421,
472
473
  "MessageType_DeviceCertificateRead": 60422,
@@ -7045,14 +7046,6 @@
7045
7046
  "passphrase_state": {
7046
7047
  "type": "string",
7047
7048
  "id": 1
7048
- },
7049
- "_only_main_pin": {
7050
- "type": "bool",
7051
- "id": 2
7052
- },
7053
- "allow_create_attach_pin": {
7054
- "type": "bool",
7055
- "id": 3
7056
7049
  }
7057
7050
  }
7058
7051
  },
@@ -11394,6 +11387,20 @@
11394
11387
  }
11395
11388
  }
11396
11389
  },
11390
+ "DeviceErrorCode": {
11391
+ "values": {
11392
+ "DeviceError_None": 0,
11393
+ "DeviceError_Busy": 1,
11394
+ "DeviceError_NotInitialized": 2,
11395
+ "DeviceError_ActionCancelled": 3,
11396
+ "DeviceError_PinAlreadyUsed": 4,
11397
+ "DeviceError_PersistFailed": 5,
11398
+ "DeviceError_SeError": 6,
11399
+ "DeviceError_InvalidLanguage": 7,
11400
+ "DeviceError_WallpaperNotUsable": 8,
11401
+ "DeviceError_DeviceLocked": 9
11402
+ }
11403
+ },
11397
11404
  "DeviceRebootType": {
11398
11405
  "values": {
11399
11406
  "Normal": 0,
@@ -11423,6 +11430,62 @@
11423
11430
  "language": {
11424
11431
  "type": "string",
11425
11432
  "id": 3
11433
+ },
11434
+ "wallpaper_path": {
11435
+ "type": "string",
11436
+ "id": 4
11437
+ },
11438
+ "passphrase_enable": {
11439
+ "type": "bool",
11440
+ "id": 6
11441
+ },
11442
+ "brightness": {
11443
+ "type": "uint32",
11444
+ "id": 7
11445
+ },
11446
+ "autolock_delay_ms": {
11447
+ "type": "uint32",
11448
+ "id": 8
11449
+ },
11450
+ "autoshutdown_delay_ms": {
11451
+ "type": "uint32",
11452
+ "id": 9
11453
+ },
11454
+ "animation_enable": {
11455
+ "type": "bool",
11456
+ "id": 10
11457
+ },
11458
+ "tap_to_wake": {
11459
+ "type": "bool",
11460
+ "id": 11
11461
+ },
11462
+ "haptic_feedback": {
11463
+ "type": "bool",
11464
+ "id": 12
11465
+ },
11466
+ "device_name_display_enabled": {
11467
+ "type": "bool",
11468
+ "id": 13
11469
+ },
11470
+ "airgap_mode": {
11471
+ "type": "bool",
11472
+ "id": 14
11473
+ },
11474
+ "fido_enabled": {
11475
+ "type": "bool",
11476
+ "id": 15
11477
+ },
11478
+ "experimental_features": {
11479
+ "type": "bool",
11480
+ "id": 16
11481
+ },
11482
+ "usb_lock_enable": {
11483
+ "type": "bool",
11484
+ "id": 17
11485
+ },
11486
+ "random_keypad": {
11487
+ "type": "bool",
11488
+ "id": 18
11426
11489
  }
11427
11490
  }
11428
11491
  },
@@ -11438,6 +11501,27 @@
11438
11501
  }
11439
11502
  }
11440
11503
  },
11504
+ "DeviceSettingsPage": {
11505
+ "values": {
11506
+ "DeviceReset": 0,
11507
+ "DevicePinChange": 1,
11508
+ "DevicePassphrase": 2,
11509
+ "DeviceAirgap": 3
11510
+ }
11511
+ },
11512
+ "DeviceSettingsPageShow": {
11513
+ "fields": {
11514
+ "page": {
11515
+ "rule": "required",
11516
+ "type": "DeviceSettingsPage",
11517
+ "id": 1
11518
+ },
11519
+ "field_name": {
11520
+ "type": "string",
11521
+ "id": 2
11522
+ }
11523
+ }
11524
+ },
11441
11525
  "DeviceCertificate": {
11442
11526
  "fields": {
11443
11527
  "cert_and_pubkey": {
@@ -55,7 +55,6 @@ import {
55
55
  PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
56
56
  requestProtocolV2DeviceInfo,
57
57
  } from '../protocols/protocol-v2/features';
58
- import { refreshProtocolV2DeviceStatus } from '../protocols/protocol-v2/walletSession';
59
58
  import { buildProtocolV1FeaturesPayload, buildProtocolV2FeaturesPayload } from '../deviceProfile';
60
59
 
61
60
  import type { PROTO } from '../constants';
@@ -68,6 +67,7 @@ import type { PassphrasePromptResponse } from './DeviceCommands';
68
67
  import type { Deferred, HardwareConnectProtocol } from '@onekeyfe/hd-shared';
69
68
  import type {
70
69
  OneKeyDeviceInfo as DeviceDescriptor,
70
+ DeviceSessionPinResult,
71
71
  DeviceStatus,
72
72
  ProtocolV2DeviceInfo,
73
73
  Success,
@@ -1190,8 +1190,12 @@ export class Device extends EventEmitter {
1190
1190
 
1191
1191
  async unlockDevice() {
1192
1192
  if (this.isProtocolV2()) {
1193
+ let pinResult: DeviceSessionPinResult;
1193
1194
  try {
1194
- await this.commands.typedCall('DeviceSessionAskPin', 'DeviceSessionPinResult');
1195
+ ({ message: pinResult } = await this.commands.typedCall(
1196
+ 'DeviceSessionAskPin',
1197
+ 'DeviceSessionPinResult'
1198
+ ));
1195
1199
  } catch (error) {
1196
1200
  const errorText =
1197
1201
  error instanceof Error
@@ -1203,7 +1207,17 @@ export class Device extends EventEmitter {
1203
1207
  throw error;
1204
1208
  }
1205
1209
 
1206
- return refreshProtocolV2DeviceStatus(this);
1210
+ const status: DeviceStatus = {};
1211
+ if (pinResult.unlocked != null) {
1212
+ status.unlocked = pinResult.unlocked;
1213
+ }
1214
+ if (pinResult.unlocked_attach_pin != null) {
1215
+ status.unlocked_by_attach_to_pin = pinResult.unlocked_attach_pin;
1216
+ }
1217
+ if (pinResult.passphrase_protection != null) {
1218
+ status.passphrase_enabled = pinResult.passphrase_protection;
1219
+ }
1220
+ return this.updateProtocolV2Status(status);
1207
1221
  }
1208
1222
 
1209
1223
  const firmwareVersion = this.getCurrentFirmwareVersionString() ?? '0.0.0';
@@ -532,8 +532,9 @@ export class DeviceCommands {
532
532
 
533
533
  this.device.clearCancelableAction();
534
534
  if (res.type === 'Failure') {
535
- const { code, message } = res.message as {
535
+ const { code, subcode, message } = res.message as {
536
536
  code?: string | FailureType;
537
+ subcode?: number;
537
538
  message?: string;
538
539
  };
539
540
  let error: HardwareError | null = null;
@@ -577,8 +578,13 @@ export class DeviceCommands {
577
578
  }
578
579
 
579
580
  if (code === 'Failure_ProcessError') {
580
- // Handle firmware verification failures
581
- if (
581
+ if (subcode === 9) {
582
+ error = ERRORS.TypedError(HardwareErrorCode.DeviceLocked, message, {
583
+ failureCode: code,
584
+ subcode,
585
+ firmwareMessage: message,
586
+ });
587
+ } else if (
582
588
  message?.includes('Bootloader file verify failed') ||
583
589
  message?.includes('verify failed')
584
590
  ) {
@@ -1,9 +1,13 @@
1
1
  import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
2
 
3
- import type { Features } from '../types';
4
- import type { PROTO } from '../constants';
3
+ import {
4
+ isProtocolV2BootloaderDeviceInfo,
5
+ isProtocolV2RomloaderDeviceInfo,
6
+ } from '../protocols/protocol-v2/features';
7
+
5
8
  import type { DeviceFirmwareImageInfo, ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
6
- import { isProtocolV2BootloaderDeviceInfo } from '../protocols/protocol-v2/features';
9
+ import type { PROTO } from '../constants';
10
+ import type { DeviceFeaturesMode, Features } from '../types';
7
11
 
8
12
  type ProtocolV1FeaturesCompat = PROTO.Features &
9
13
  Partial<PROTO.OnekeyFeatures> & {
@@ -204,7 +208,7 @@ export const buildProtocolV1FeaturesPayload = (
204
208
  },
205
209
  sessionId,
206
210
  raw: {
207
- protocolV1Features: protocolV1Features,
211
+ protocolV1Features,
208
212
  },
209
213
  };
210
214
  };
@@ -223,7 +227,7 @@ export const buildProtocolV2FeaturesPayload = (
223
227
  const info = deviceInfo;
224
228
  const fwApplication = info?.fw?.application;
225
229
  const fwBootloader = info?.fw?.bootloader;
226
- const fwBoard = firstValue(info?.fw?.application_data, info?.fw?.romloader);
230
+ const fwBoard = info?.fw?.romloader;
227
231
  const bleApplication = info?.coprocessor?.application;
228
232
  const status = info?.status;
229
233
 
@@ -249,14 +253,18 @@ export const buildProtocolV2FeaturesPayload = (
249
253
  const unlocked = firstValue(status?.unlocked, previous?.unlocked) ?? null;
250
254
  const attachToPinEnabled = status?.attach_to_pin_enabled ?? null;
251
255
  const unlockedAttachPin = status?.unlocked_by_attach_to_pin ?? undefined;
256
+ const romloaderMode = isProtocolV2RomloaderDeviceInfo(info);
252
257
  const bootloaderMode = isProtocolV2BootloaderDeviceInfo(info);
253
- const mode = bootloaderMode
254
- ? 'bootloader'
255
- : initialized === false
256
- ? 'notInitialized'
257
- : initialized === true
258
- ? 'normal'
259
- : 'unknown';
258
+ let mode: DeviceFeaturesMode = 'unknown';
259
+ if (romloaderMode) {
260
+ mode = 'romloader';
261
+ } else if (bootloaderMode) {
262
+ mode = 'bootloader';
263
+ } else if (initialized === false) {
264
+ mode = 'notInitialized';
265
+ } else if (initialized === true) {
266
+ mode = 'normal';
267
+ }
260
268
 
261
269
  return {
262
270
  protocol: 'V2',
@@ -13,6 +13,10 @@ import {
13
13
  getDeviceUUID,
14
14
  getFirmwareType,
15
15
  } from '../utils/deviceInfoUtils';
16
+ import {
17
+ isProtocolV2BootloaderDeviceInfo,
18
+ isProtocolV2RomloaderDeviceInfo,
19
+ } from '../protocols/protocol-v2/features';
16
20
 
17
21
  import type {
18
22
  DeviceInfoProtocol,
@@ -26,7 +30,6 @@ import type {
26
30
  } from '../types/api/getDeviceInfo';
27
31
  import type { Features, OnekeyFeatures } from '../types';
28
32
  import type { DeviceFirmwareImageInfo, ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
29
- import { isProtocolV2BootloaderDeviceInfo } from '../protocols/protocol-v2/features';
30
33
 
31
34
  type BuildProtocolV1ProfileParams = {
32
35
  protocol?: DeviceInfoProtocol;
@@ -87,6 +90,7 @@ const getDeviceMode = (features?: Features): DeviceInfoStatus['mode'] => {
87
90
  };
88
91
 
89
92
  const getProtocolV2Mode = (deviceInfo?: ProtocolV2DeviceInfo): DeviceInfoStatus['mode'] => {
93
+ if (isProtocolV2RomloaderDeviceInfo(deviceInfo)) return 'romloader';
90
94
  if (isProtocolV2BootloaderDeviceInfo(deviceInfo)) return 'bootloader';
91
95
  const initialized = deviceInfo?.status?.init_states;
92
96
  if (initialized === false) return 'notInitialized';
@@ -154,9 +158,7 @@ const normalizeV2Versions = (deviceInfo?: ProtocolV2DeviceInfo): DeviceProfileVe
154
158
  return {
155
159
  firmware: firstMeaningfulVersion(getImageVersion(info?.fw?.application)),
156
160
  bootloader: firstMeaningfulVersion(getImageVersion(info?.fw?.bootloader)),
157
- board: firstMeaningfulVersion(
158
- getImageVersion(info?.fw?.application_data ?? info?.fw?.romloader)
159
- ),
161
+ board: firstMeaningfulVersion(getImageVersion(info?.fw?.romloader)),
160
162
  ble: firstMeaningfulVersion(getImageVersion(info?.coprocessor?.application)),
161
163
  se01: firstMeaningfulVersion(getImageVersion(info?.se1?.application)),
162
164
  se02: firstMeaningfulVersion(getImageVersion(info?.se2?.application)),
@@ -245,8 +247,8 @@ const normalizeV2Verify = (deviceInfo?: ProtocolV2DeviceInfo): DeviceProfileVeri
245
247
  firmwareHash: getImageHash(info?.fw?.application),
246
248
  bootloaderBuildId: getImageBuildId(info?.fw?.bootloader),
247
249
  bootloaderHash: getImageHash(info?.fw?.bootloader),
248
- boardBuildId: getImageBuildId(info?.fw?.application_data ?? info?.fw?.romloader),
249
- boardHash: getImageHash(info?.fw?.application_data ?? info?.fw?.romloader),
250
+ boardBuildId: getImageBuildId(info?.fw?.romloader),
251
+ boardHash: getImageHash(info?.fw?.romloader),
250
252
  bleBuildId: getImageBuildId(info?.coprocessor?.application),
251
253
  bleHash: getImageHash(info?.coprocessor?.application),
252
254
  se01BuildId: getImageBuildId(info?.se1?.application),