@onekeyfe/hd-core 1.2.0-alpha.76 → 1.2.0-alpha.78

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 (33) hide show
  1. package/__tests__/device-state-mapper.test.ts +16 -1
  2. package/__tests__/device-state-projector.test.ts +20 -0
  3. package/__tests__/device-state-store.test.ts +27 -0
  4. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +27 -0
  5. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +57 -0
  6. package/__tests__/homescreen.test.ts +17 -0
  7. package/__tests__/method-protocol-support.test.ts +13 -1
  8. package/__tests__/protocol-v2-resources.test.ts +11 -0
  9. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  10. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  11. package/dist/api/utils.d.ts +2 -0
  12. package/dist/api/utils.d.ts.map +1 -1
  13. package/dist/data-manager/DataManager.d.ts.map +1 -1
  14. package/dist/device/DeviceStateMapper.d.ts.map +1 -1
  15. package/dist/device/DeviceStateProjector.d.ts.map +1 -1
  16. package/dist/device/DeviceStateStore.d.ts.map +1 -1
  17. package/dist/index.d.ts +23 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +269 -99
  20. package/dist/types/device.d.ts +8 -0
  21. package/dist/types/device.d.ts.map +1 -1
  22. package/dist/utils/homescreen.d.ts.map +1 -1
  23. package/package.json +4 -4
  24. package/src/api/FirmwareUpdateV2.ts +12 -0
  25. package/src/api/FirmwareUpdateV3.ts +2 -1
  26. package/src/api/utils.ts +25 -0
  27. package/src/data-manager/DataManager.ts +15 -0
  28. package/src/device/DeviceStateMapper.ts +42 -0
  29. package/src/device/DeviceStateProjector.ts +58 -0
  30. package/src/device/DeviceStateStore.ts +31 -0
  31. package/src/index.ts +2 -0
  32. package/src/types/device.ts +16 -0
  33. package/src/utils/homescreen.ts +5 -1
package/src/api/utils.ts CHANGED
@@ -4,6 +4,7 @@ import * as ApiMethods from './index';
4
4
 
5
5
  import type { BaseMethod } from './BaseMethod';
6
6
  import type { IFrameCallMessage } from '../events';
7
+ import type { ProtocolType } from '@onekeyfe/hd-transport';
7
8
 
8
9
  type MethodConstructor = new (message: IFrameCallMessage & { id?: number }) => BaseMethod<any>;
9
10
 
@@ -25,3 +26,27 @@ export function findMethod(message: IFrameCallMessage & { id?: number }): BaseMe
25
26
  `Method ${method} is not set`
26
27
  );
27
28
  }
29
+
30
+ /**
31
+ * Read the protocol contract from the same method instance used by the Core dispatcher.
32
+ * Passing a payload initializes the method first so parameter-dependent contracts, such
33
+ * as Bitcoin fork support, are evaluated with the actual request.
34
+ */
35
+ export function getMethodSupportedProtocols(
36
+ method: string,
37
+ payload?: Record<string, unknown>
38
+ ): readonly ProtocolType[] {
39
+ const instance = findMethod({
40
+ id: 0,
41
+ payload: {
42
+ ...payload,
43
+ method,
44
+ },
45
+ } as unknown as IFrameCallMessage);
46
+
47
+ if (payload !== undefined) {
48
+ instance.init();
49
+ }
50
+
51
+ return instance.getSupportedProtocols();
52
+ }
@@ -32,6 +32,8 @@ import type {
32
32
  RemoteConfigResponse,
33
33
  } from '../types';
34
34
 
35
+ const FIRMWARE_UPDATE_CONFIG_FRESHNESS_MS = 5 * 60 * 1000;
36
+
35
37
  const Log = getLogger(LoggerNames.Core);
36
38
 
37
39
  export const FIRMWARE_FIELDS = [
@@ -507,6 +509,7 @@ export default class DataManager {
507
509
  this.assets = {
508
510
  bridge: data.bridge,
509
511
  };
512
+ this.lastCheckTimestamp = getTimeStamp();
510
513
  }
511
514
 
512
515
  static updateEnv(newEnv: ConnectSettings['env']) {
@@ -538,6 +541,18 @@ export default class DataManager {
538
541
  if (!this.settings) {
539
542
  throw new Error('Remote config settings are not initialized');
540
543
  }
544
+ const hasFreshConfig =
545
+ this.lastCheckTimestamp > 0 &&
546
+ getTimeStamp() - this.lastCheckTimestamp <= FIRMWARE_UPDATE_CONFIG_FRESHNESS_MS;
547
+ if (hasFreshConfig) {
548
+ if (requireResources && this.protocolV2ResourcesConfigError) {
549
+ throw ERRORS.TypedError(
550
+ HardwareErrorCode.FirmwareUpdateDownloadFailed,
551
+ `Invalid Pro2 resources config: ${this.protocolV2ResourcesConfigError.message}`
552
+ );
553
+ }
554
+ return;
555
+ }
541
556
  const loaded = await this.load(this.settings);
542
557
  if (!loaded) {
543
558
  throw ERRORS.TypedError(
@@ -7,6 +7,7 @@ import {
7
7
  mapLanguageToProtocolV2,
8
8
  normalizeSafetyCheckLevel,
9
9
  } from '../utils/deviceSettings';
10
+ import { getProtocolV2SeState, getProtocolV2SeType } from '../protocols/protocol-v2/features';
10
11
 
11
12
  import type {
12
13
  ApplySettings,
@@ -159,6 +160,11 @@ const meaningfulVersion = (version?: string | null) => {
159
160
  return parts.map(part => (Number.isNaN(Number.parseInt(part, 10)) ? '0' : part)).join('.');
160
161
  };
161
162
 
163
+ const meaningfulText = (value: unknown): string | null => {
164
+ if (value === undefined || value === null || value === '') return null;
165
+ return String(value);
166
+ };
167
+
162
168
  export const mapProtocolV1OnekeyFeaturesToState = (features: OnekeyFeatures): DeviceStatePatch => {
163
169
  const verification = definedEntries<DeviceFeaturesVerify>({
164
170
  firmwareBuildId: features.onekey_firmware_build_id,
@@ -203,6 +209,24 @@ export const mapProtocolV1OnekeyFeaturesToState = (features: OnekeyFeatures): De
203
209
  se04Boot: meaningfulVersion(features.onekey_se04_boot_version),
204
210
  }),
205
211
  ...(Object.keys(verification).length > 0 ? { verification } : {}),
212
+ securityElements: {
213
+ se01: {
214
+ type: meaningfulText(features.onekey_se_type),
215
+ state: meaningfulText(features.onekey_se01_state),
216
+ },
217
+ se02: {
218
+ type: null,
219
+ state: meaningfulText(features.onekey_se02_state),
220
+ },
221
+ se03: {
222
+ type: null,
223
+ state: meaningfulText(features.onekey_se03_state),
224
+ },
225
+ se04: {
226
+ type: null,
227
+ state: meaningfulText(features.onekey_se04_state),
228
+ },
229
+ },
206
230
  raw: { protocolV1OneKeyFeatures: features },
207
231
  };
208
232
  };
@@ -288,6 +312,24 @@ export const mapProtocolV2DeviceInfoToState = (
288
312
  se04BootBuildId: imageBuildId(info.se4?.bootloader),
289
313
  se04BootHash: imageHash(info.se4?.bootloader),
290
314
  }),
315
+ securityElements: {
316
+ se01: {
317
+ type: getProtocolV2SeType(info.se1),
318
+ state: getProtocolV2SeState(info.se1),
319
+ },
320
+ se02: {
321
+ type: getProtocolV2SeType(info.se2),
322
+ state: getProtocolV2SeState(info.se2),
323
+ },
324
+ se03: {
325
+ type: getProtocolV2SeType(info.se3),
326
+ state: getProtocolV2SeState(info.se3),
327
+ },
328
+ se04: {
329
+ type: getProtocolV2SeType(info.se4),
330
+ state: getProtocolV2SeState(info.se4),
331
+ },
332
+ },
291
333
  raw: {
292
334
  protocolV2DeviceInfo: info,
293
335
  ...(loader ? { protocolV2DeviceStatus: null } : {}),
@@ -9,6 +9,62 @@ type StoredDeviceState = DeviceState & {
9
9
  const getBootloaderMode = (state: StoredDeviceState) =>
10
10
  state.status.mode === 'bootloader' || state.status.mode === 'romloader';
11
11
 
12
+ const projectLegacyAdvancedFields = (state: StoredDeviceState) => {
13
+ const verification = state.verification ?? {};
14
+ const securityElements = state.securityElements ?? {};
15
+
16
+ return {
17
+ onekey_device_type: state.identity.deviceType,
18
+ onekey_serial_no: state.identity.serialNo,
19
+ onekey_se_type:
20
+ securityElements.se01?.type ??
21
+ securityElements.se02?.type ??
22
+ securityElements.se03?.type ??
23
+ securityElements.se04?.type,
24
+ onekey_board_version: state.versions.board,
25
+ onekey_board_hash: verification.boardHash,
26
+ onekey_board_build_id: verification.boardBuildId,
27
+ onekey_boot_version: state.versions.bootloader,
28
+ onekey_boot_hash: verification.bootloaderHash,
29
+ onekey_boot_build_id: verification.bootloaderBuildId,
30
+ onekey_firmware_version: state.versions.firmware,
31
+ onekey_firmware_hash: verification.firmwareHash,
32
+ onekey_firmware_build_id: verification.firmwareBuildId,
33
+ onekey_ble_version: state.versions.ble,
34
+ onekey_ble_hash: verification.bleHash,
35
+ onekey_ble_build_id: verification.bleBuildId,
36
+ onekey_ble_name: state.identity.bleName,
37
+ onekey_se01_version: state.versions.se01,
38
+ onekey_se01_hash: verification.se01Hash,
39
+ onekey_se01_build_id: verification.se01BuildId,
40
+ onekey_se01_state: securityElements.se01?.state,
41
+ onekey_se01_boot_version: state.versions.se01Boot,
42
+ onekey_se01_boot_hash: verification.se01BootHash,
43
+ onekey_se01_boot_build_id: verification.se01BootBuildId,
44
+ onekey_se02_version: state.versions.se02,
45
+ onekey_se02_hash: verification.se02Hash,
46
+ onekey_se02_build_id: verification.se02BuildId,
47
+ onekey_se02_state: securityElements.se02?.state,
48
+ onekey_se02_boot_version: state.versions.se02Boot,
49
+ onekey_se02_boot_hash: verification.se02BootHash,
50
+ onekey_se02_boot_build_id: verification.se02BootBuildId,
51
+ onekey_se03_version: state.versions.se03,
52
+ onekey_se03_hash: verification.se03Hash,
53
+ onekey_se03_build_id: verification.se03BuildId,
54
+ onekey_se03_state: securityElements.se03?.state,
55
+ onekey_se03_boot_version: state.versions.se03Boot,
56
+ onekey_se03_boot_hash: verification.se03BootHash,
57
+ onekey_se03_boot_build_id: verification.se03BootBuildId,
58
+ onekey_se04_version: state.versions.se04,
59
+ onekey_se04_hash: verification.se04Hash,
60
+ onekey_se04_build_id: verification.se04BuildId,
61
+ onekey_se04_state: securityElements.se04?.state,
62
+ onekey_se04_boot_version: state.versions.se04Boot,
63
+ onekey_se04_boot_hash: verification.se04BootHash,
64
+ onekey_se04_boot_build_id: verification.se04BootBuildId,
65
+ };
66
+ };
67
+
12
68
  export const projectFeatures = (state: StoredDeviceState): Features => {
13
69
  const snapshot = cloneDeviceState(state);
14
70
  const rawFeatures = snapshot.raw?.protocolV1Features ?? {};
@@ -21,10 +77,12 @@ export const projectFeatures = (state: StoredDeviceState): Features => {
21
77
  snapshot.protocol === 'V1'
22
78
  ? (rawFeatures as { bootloader_mode?: boolean | null }).bootloader_mode ?? null
23
79
  : getBootloaderMode(snapshot);
80
+ const legacyAdvancedFields = projectLegacyAdvancedFields(snapshot);
24
81
 
25
82
  return {
26
83
  ...publicRawFeatures,
27
84
  ...rawOneKeyFeatures,
85
+ ...legacyAdvancedFields,
28
86
  protocol: snapshot.protocol,
29
87
  protocolVersion: snapshot.protocolVersion ?? (snapshot.protocol === 'V1' ? 1 : null),
30
88
  deviceType: snapshot.identity.deviceType,
@@ -106,6 +106,27 @@ const applySectionPatch = <T extends object>(
106
106
  }
107
107
  };
108
108
 
109
+ const mergeSecurityElements = (
110
+ current: StoredDeviceState['securityElements'],
111
+ patch: NonNullable<DeviceStatePatch['securityElements']>
112
+ ) => {
113
+ const merged = cloneDeviceState(current ?? {});
114
+
115
+ for (const key of Object.keys(patch) as Array<keyof typeof patch>) {
116
+ const elementPatch = patch[key];
117
+ if (elementPatch !== undefined) {
118
+ const currentElement = merged[key];
119
+ merged[key] = {
120
+ type: elementPatch.type !== undefined ? elementPatch.type : currentElement?.type ?? null,
121
+ state:
122
+ elementPatch.state !== undefined ? elementPatch.state : currentElement?.state ?? null,
123
+ };
124
+ }
125
+ }
126
+
127
+ return merged;
128
+ };
129
+
109
130
  export class DeviceStateStore {
110
131
  private state: StoredDeviceState | undefined;
111
132
 
@@ -148,6 +169,16 @@ export class DeviceStateStore {
148
169
  if (patch.status) applySectionPatch(next.status, patch.status, 'status', changedKeys);
149
170
  if (patch.settings) applySectionPatch(next.settings, patch.settings, 'settings', changedKeys);
150
171
  if (patch.versions) applySectionPatch(next.versions, patch.versions, 'versions', changedKeys);
172
+ if (patch.securityElements !== undefined) {
173
+ const mergedSecurityElements = mergeSecurityElements(
174
+ next.securityElements,
175
+ patch.securityElements
176
+ );
177
+ if (!isEqual(next.securityElements, mergedSecurityElements)) {
178
+ next.securityElements = mergedSecurityElements;
179
+ changedKeys.push('securityElements');
180
+ }
181
+ }
151
182
 
152
183
  if (patch.capabilities !== undefined && !isEqual(next.capabilities, patch.capabilities)) {
153
184
  next.capabilities = cloneDeviceState(patch.capabilities);
package/src/index.ts CHANGED
@@ -19,6 +19,8 @@ export * from './types';
19
19
  export { whitelist, whitelistExtension } from './data/config';
20
20
  export { executeCallback, cleanupCallback };
21
21
  export { preloadSessionCache } from './device/Device';
22
+ export { projectFeatures as projectDeviceStateFeatures } from './device/DeviceStateProjector';
23
+ export { getMethodSupportedProtocols } from './api/utils';
22
24
  export {
23
25
  getFirmwareUpdateHostBindingGeneration,
24
26
  registerFirmwareUpdateHostBinding,
@@ -259,6 +259,19 @@ export type DeviceStateVersions = {
259
259
  se04Boot?: string | null;
260
260
  };
261
261
 
262
+ export type DeviceStateSecurityElement = {
263
+ type: string | null;
264
+ state: string | null;
265
+ };
266
+
267
+ export type DeviceStateSecurityElements = Partial<
268
+ Record<'se01' | 'se02' | 'se03' | 'se04', DeviceStateSecurityElement>
269
+ >;
270
+
271
+ export type DeviceStateSecurityElementsPatch = Partial<
272
+ Record<keyof DeviceStateSecurityElements, Partial<DeviceStateSecurityElement>>
273
+ >;
274
+
262
275
  export type DeviceState = {
263
276
  schemaVersion: 1;
264
277
  revision: number;
@@ -270,6 +283,8 @@ export type DeviceState = {
270
283
  status: DeviceStateStatus;
271
284
  settings: DeviceStateSettings;
272
285
  versions: DeviceStateVersions;
286
+ /** Secure-element metadata exposed by the unified firmware information read. */
287
+ securityElements?: DeviceStateSecurityElements;
273
288
  capabilities: Array<number | string>;
274
289
  verification?: Partial<DeviceFeaturesVerify>;
275
290
  };
@@ -281,6 +296,7 @@ export type DeviceStatePatch = {
281
296
  status?: Partial<DeviceStateStatus>;
282
297
  settings?: Partial<DeviceStateSettings>;
283
298
  versions?: Partial<DeviceStateVersions>;
299
+ securityElements?: DeviceStateSecurityElementsPatch;
284
300
  capabilities?: Array<number | string>;
285
301
  verification?: DeviceFeaturesVerify;
286
302
  raw?: DeviceFeaturesRawPatch;
@@ -328,6 +328,10 @@ export const getNftSize = ({
328
328
  full: { width: PRO2_NFT_IMAGE_WIDTH, height: PRO2_NFT_IMAGE_HEIGHT },
329
329
  thumbnail: { width: PRO2_NFT_THUMBNAIL_WIDTH, height: PRO2_NFT_THUMBNAIL_HEIGHT },
330
330
  },
331
+ neo: {
332
+ full: { width: PRO2_NFT_IMAGE_WIDTH, height: PRO2_NFT_IMAGE_HEIGHT },
333
+ thumbnail: { width: PRO2_NFT_THUMBNAIL_WIDTH, height: PRO2_NFT_THUMBNAIL_HEIGHT },
334
+ },
331
335
  };
332
336
 
333
337
  return sizes[deviceType]?.[thumbnail ? 'thumbnail' : 'full'];
@@ -342,7 +346,7 @@ export const getHomeScreenSize = ({
342
346
  homeScreenType: 'WallPaper' | 'Nft';
343
347
  thumbnail?: boolean;
344
348
  }) => {
345
- if (deviceType === EDeviceType.Pro2) {
349
+ if (deviceType === EDeviceType.Pro2 || deviceType === EDeviceType.Neo) {
346
350
  return thumbnail ? undefined : { width: PRO2_WALLPAPER_WIDTH, height: PRO2_WALLPAPER_HEIGHT };
347
351
  }
348
352