@onekeyfe/hd-core 1.2.0-alpha.77 → 1.2.0-alpha.79

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 (57) hide show
  1. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +12 -71
  2. package/__tests__/device-state-mapper.test.ts +16 -1
  3. package/__tests__/device-state-projector.test.ts +20 -0
  4. package/__tests__/device-state-store.test.ts +27 -0
  5. package/__tests__/homescreen.test.ts +17 -0
  6. package/__tests__/method-protocol-support.test.ts +13 -1
  7. package/__tests__/protocol-v2-resources.test.ts +113 -222
  8. package/__tests__/protocol-v2.test.ts +110 -380
  9. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  10. package/dist/api/FirmwareUpdateV4.d.ts +0 -6
  11. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  12. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  13. package/dist/api/utils.d.ts +2 -0
  14. package/dist/api/utils.d.ts.map +1 -1
  15. package/dist/data-manager/DataManager.d.ts +1 -2
  16. package/dist/data-manager/DataManager.d.ts.map +1 -1
  17. package/dist/device/DeviceStateMapper.d.ts.map +1 -1
  18. package/dist/device/DeviceStateProjector.d.ts.map +1 -1
  19. package/dist/device/DeviceStateStore.d.ts.map +1 -1
  20. package/dist/index.d.ts +96 -33
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +515 -597
  23. package/dist/inject.d.ts.map +1 -1
  24. package/dist/protocols/protocol-v2/resources.d.ts +31 -28
  25. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  26. package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -0
  27. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  28. package/dist/types/api/export.d.ts +1 -0
  29. package/dist/types/api/export.d.ts.map +1 -1
  30. package/dist/types/api/index.d.ts +2 -0
  31. package/dist/types/api/index.d.ts.map +1 -1
  32. package/dist/types/api/protocolV2ResourceManifest.d.ts +17 -0
  33. package/dist/types/api/protocolV2ResourceManifest.d.ts.map +1 -0
  34. package/dist/types/device.d.ts +8 -0
  35. package/dist/types/device.d.ts.map +1 -1
  36. package/dist/types/settings.d.ts +30 -21
  37. package/dist/types/settings.d.ts.map +1 -1
  38. package/dist/utils/homescreen.d.ts.map +1 -1
  39. package/package.json +4 -4
  40. package/src/api/CheckAllFirmwareRelease.ts +5 -34
  41. package/src/api/FirmwareUpdateV4.ts +48 -220
  42. package/src/api/UploadPortfolio.ts +18 -0
  43. package/src/api/utils.ts +25 -0
  44. package/src/data-manager/DataManager.ts +17 -6
  45. package/src/device/DeviceStateMapper.ts +42 -0
  46. package/src/device/DeviceStateProjector.ts +58 -0
  47. package/src/device/DeviceStateStore.ts +31 -0
  48. package/src/index.ts +8 -0
  49. package/src/inject.ts +2 -0
  50. package/src/protocols/protocol-v2/resources.ts +193 -324
  51. package/src/types/api/checkAllFirmwareRelease.ts +1 -0
  52. package/src/types/api/export.ts +4 -0
  53. package/src/types/api/index.ts +2 -0
  54. package/src/types/api/protocolV2ResourceManifest.ts +19 -0
  55. package/src/types/device.ts +16 -0
  56. package/src/types/settings.ts +30 -36
  57. package/src/utils/homescreen.ts +5 -1
@@ -5,7 +5,6 @@ import CheckAllFirmwareRelease, {
5
5
  } from '../src/api/CheckAllFirmwareRelease';
6
6
  import { DataManager } from '../src/data-manager';
7
7
  import { createCoreApi } from '../src/inject';
8
- import { PROTOCOL_V2_RESOURCE_DEVICE_PATHS } from '../src/protocols/protocol-v2/resources';
9
8
 
10
9
  import type { CoreApi } from '../src/types/api';
11
10
  import type { DeviceStateVersions, IFirmwareReleaseInfo } from '../src/types';
@@ -73,67 +72,8 @@ const release: IFirmwareReleaseInfo = {
73
72
  ],
74
73
  };
75
74
 
76
- const stableResources = ['images', 'animation', 'wallpaper', 'translations', 'roobert', 'noto'].map(
77
- (type, index) => ({
78
- type,
79
- url: `https://example.com/${type}.okpkg`,
80
- size: 0x52a0 + index + 1,
81
- fileHash: 'a'.repeat(64),
82
- headerHash: index.toString(16).padStart(128, '0'),
83
- })
84
- );
85
-
86
- const createFilesystemTypedCall = (missingAll = false) => {
87
- const resourceByPath = new Map(
88
- stableResources.map(resource => [
89
- PROTOCOL_V2_RESOURCE_DEVICE_PATHS[
90
- resource.type as keyof typeof PROTOCOL_V2_RESOURCE_DEVICE_PATHS
91
- ],
92
- resource,
93
- ])
94
- );
95
- return jest.fn((requestType: string, _responseType: string, payload: Record<string, any>) => {
96
- if (requestType === 'ResourceInventoryGet') {
97
- throw new Error('ResourceInventoryGet is unavailable on released firmware');
98
- }
99
- if (requestType === 'FilesystemPathInfoQuery') {
100
- const resource = resourceByPath.get(payload.path);
101
- return {
102
- message: {
103
- exist: Boolean(resource) && !missingAll,
104
- directory: false,
105
- size: missingAll ? 0 : resource?.size,
106
- },
107
- };
108
- }
109
- if (requestType === 'FilesystemFileRead') {
110
- const resource = resourceByPath.get(payload.file.path);
111
- if (!resource || missingAll) throw new Error('missing resource');
112
- const header = new Uint8Array(0x52a0);
113
- const view = new DataView(header.buffer);
114
- 'OKPP'.split('').forEach((char, index) => {
115
- header[index] = char.charCodeAt(0);
116
- });
117
- 'RESC'.split('').forEach((char, index) => {
118
- header[0x08 + index] = char.charCodeAt(0);
119
- });
120
- view.setUint32(0x0c, header.byteLength, true);
121
- for (let index = 0; index < resource.headerHash.length / 2; index++) {
122
- header[0x240 + index] = Number.parseInt(
123
- resource.headerHash.slice(index * 2, index * 2 + 2),
124
- 16
125
- );
126
- }
127
- const offset = Number(payload.file.offset);
128
- const chunkLength = Number(payload.chunk_len);
129
- return {
130
- message: {
131
- data: header.slice(offset, offset + chunkLength),
132
- },
133
- };
134
- }
135
- throw new Error(`Unexpected request: ${requestType}`);
136
- });
75
+ const resourceSource = {
76
+ manifestUrl: 'https://example.com/pro2-resource/manifest.json',
137
77
  };
138
78
 
139
79
  describe('checkAllFirmwareRelease Protocol V2 support', () => {
@@ -370,7 +310,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
370
310
  status: { mode: 'normal' },
371
311
  versions: currentVersions,
372
312
  });
373
- const typedCall = createFilesystemTypedCall(true);
313
+ const typedCall = jest.fn();
374
314
  method.device = {
375
315
  isProtocolV2: () => true,
376
316
  features: {
@@ -381,13 +321,14 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
381
321
  getCommands: () => ({ typedCall }),
382
322
  } as unknown as CheckAllFirmwareRelease['device'];
383
323
  jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
384
- jest.spyOn(DataManager, 'getProtocolV2Resources').mockReturnValue(stableResources as any);
324
+ jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(resourceSource);
385
325
 
386
326
  await expect(method.run()).resolves.toMatchObject({
387
327
  protocol: 'V2',
388
328
  deviceType: 'pro2',
389
329
  status: 'required',
390
330
  resourceStatus: 'unknown',
331
+ resourceManifestUrl: resourceSource.manifestUrl,
391
332
  targetsToUpdate: ['boot', 'app_v1'],
392
333
  firmware: {
393
334
  status: 'required',
@@ -437,7 +378,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
437
378
  getCommands: () => ({ typedCall: jest.fn() }),
438
379
  } as unknown as CheckAllFirmwareRelease['device'];
439
380
  jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(packageSet);
440
- jest.spyOn(DataManager, 'getProtocolV2Resources').mockReturnValue(undefined);
381
+ jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(undefined);
441
382
 
442
383
  await expect(method.run()).resolves.toMatchObject({
443
384
  status: 'outdated',
@@ -449,7 +390,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
449
390
  });
450
391
 
451
392
  test.each(['bootloader', 'romloader'] as const)(
452
- 'uses filesystem inventory in %s mode instead of forcing all resources',
393
+ 'does not read vol0 resource inventory in %s mode',
453
394
  async mode => {
454
395
  const method = new CheckAllFirmwareRelease({
455
396
  id: 1,
@@ -459,7 +400,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
459
400
  },
460
401
  });
461
402
  method.init();
462
- const typedCall = createFilesystemTypedCall();
403
+ const typedCall = jest.fn();
463
404
  method.device = {
464
405
  isProtocolV2: () => true,
465
406
  features: { deviceType: 'pro2', firmwareVersion: '1.0.0' },
@@ -471,15 +412,15 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
471
412
  getCommands: () => ({ typedCall }),
472
413
  } as unknown as CheckAllFirmwareRelease['device'];
473
414
  jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
474
- jest.spyOn(DataManager, 'getProtocolV2Resources').mockReturnValue(stableResources as any);
415
+ jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(resourceSource);
475
416
 
476
417
  await expect(method.run()).resolves.toMatchObject({
477
418
  protocol: 'V2',
478
- resourceStatus: 'valid',
419
+ resourceStatus: 'unknown',
420
+ resourceManifestUrl: resourceSource.manifestUrl,
479
421
  targetsToUpdate: ['boot', 'app_v1'],
480
422
  });
481
- expect(typedCall.mock.calls.some(call => call[0] === 'ResourceInventoryGet')).toBe(false);
482
- expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemFileRead')).toBe(true);
423
+ expect(typedCall).not.toHaveBeenCalled();
483
424
  }
484
425
  );
485
426
 
@@ -1,5 +1,10 @@
1
1
  import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
- import { DeviceType, Enum_SafetyCheckLevel } from '@onekeyfe/hd-transport';
2
+ import {
3
+ DeviceSEState,
4
+ DeviceSeType,
5
+ DeviceType,
6
+ Enum_SafetyCheckLevel,
7
+ } from '@onekeyfe/hd-transport';
3
8
 
4
9
  import {
5
10
  mapApplySettingsToState,
@@ -119,6 +124,11 @@ describe('DeviceStateMapper', () => {
119
124
  bt_adv_name: 'Pro2 1234',
120
125
  application: { version: '1.2.3' },
121
126
  },
127
+ se1: {
128
+ type: DeviceSeType.THD89,
129
+ state: DeviceSEState.APP,
130
+ application: { version: '3.0.0', build_id: 'se1-build', hash: [0x05, 0x06] },
131
+ },
122
132
  } as ProtocolV2DeviceInfo,
123
133
  'normal'
124
134
  );
@@ -140,6 +150,11 @@ describe('DeviceStateMapper', () => {
140
150
  applicationP1Hash: '0102',
141
151
  applicationP2BuildId: 'p2-build',
142
152
  applicationP2Hash: '0304',
153
+ se01BuildId: 'se1-build',
154
+ se01Hash: '0506',
155
+ });
156
+ expect(patch.securityElements).toMatchObject({
157
+ se01: { type: 'THD89', state: 'APP' },
143
158
  });
144
159
  expect(patch).toMatchObject({ protocolVersion: 2 });
145
160
  expect(patch.status?.unlocked).toBeUndefined();
@@ -33,6 +33,17 @@ describe('DeviceStateProjector', () => {
33
33
  firmware: '5.0.0',
34
34
  bootloader: '2.0.0',
35
35
  ble: '1.2.3',
36
+ se01: '3.1.0',
37
+ se01Boot: '1.0.2',
38
+ };
39
+ state.verification = {
40
+ firmwareBuildId: 'firmware-build',
41
+ firmwareHash: 'firmware-hash',
42
+ se01BuildId: 'se01-build',
43
+ se01Hash: 'se01-hash',
44
+ };
45
+ state.securityElements = {
46
+ se01: { type: 'THD89', state: 'APP' },
36
47
  };
37
48
 
38
49
  const features = projectFeatures(state);
@@ -43,6 +54,15 @@ describe('DeviceStateProjector', () => {
43
54
  unlocked: true,
44
55
  brightness: 80,
45
56
  firmwareVersion: '5.0.0',
57
+ onekey_firmware_version: '5.0.0',
58
+ onekey_firmware_build_id: 'firmware-build',
59
+ onekey_firmware_hash: 'firmware-hash',
60
+ onekey_se_type: 'THD89',
61
+ onekey_se01_version: '3.1.0',
62
+ onekey_se01_build_id: 'se01-build',
63
+ onekey_se01_hash: 'se01-hash',
64
+ onekey_se01_state: 'APP',
65
+ onekey_se01_boot_version: '1.0.2',
46
66
  });
47
67
  });
48
68
 
@@ -1,6 +1,7 @@
1
1
  import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
2
 
3
3
  import { DeviceStateStore, createEmptyDeviceState } from '../src/device/DeviceStateStore';
4
+ import { projectFeatures } from '../src/device/DeviceStateProjector';
4
5
 
5
6
  describe('DeviceStateStore', () => {
6
7
  test('merges identity patches and ignores undefined', () => {
@@ -79,4 +80,30 @@ describe('DeviceStateStore', () => {
79
80
  protocolV2DeviceInfo: deviceInfo,
80
81
  });
81
82
  });
83
+
84
+ test('merges partial secure-element patches without discarding existing metadata', () => {
85
+ const store = new DeviceStateStore(createEmptyDeviceState());
86
+
87
+ store.update(
88
+ {
89
+ securityElements: {
90
+ se01: { type: 'THD89', state: 'APP' },
91
+ se02: { type: 'SE608A', state: 'APP' },
92
+ },
93
+ },
94
+ 'device-info'
95
+ );
96
+ const result = store.update({ securityElements: { se01: { state: 'BOOT' } } }, 'device-status');
97
+
98
+ expect(result.state.securityElements).toEqual({
99
+ se01: { type: 'THD89', state: 'BOOT' },
100
+ se02: { type: 'SE608A', state: 'APP' },
101
+ });
102
+ expect(result.changedKeys).toContain('securityElements');
103
+ expect(projectFeatures(result.state)).toMatchObject({
104
+ onekey_se_type: 'THD89',
105
+ onekey_se01_state: 'BOOT',
106
+ onekey_se02_state: 'APP',
107
+ });
108
+ });
82
109
  });
@@ -22,6 +22,15 @@ describe('getHomeScreenSize', () => {
22
22
  ).toBeUndefined();
23
23
  });
24
24
 
25
+ it('uses the Protocol V2 wallpaper dimensions for Neo', () => {
26
+ expect(
27
+ getHomeScreenSize({
28
+ deviceType: EDeviceType.Neo,
29
+ homeScreenType: 'WallPaper',
30
+ })
31
+ ).toEqual({ width: 604, height: 1024 });
32
+ });
33
+
25
34
  it('does not reuse the wallpaper homeScreenType for Pro 2 NFT dimensions', () => {
26
35
  expect(
27
36
  getHomeScreenSize({
@@ -49,6 +58,14 @@ describe('getNftSize', () => {
49
58
  });
50
59
  });
51
60
 
61
+ it('uses the Protocol V2 NFT dimensions for Neo', () => {
62
+ expect(getNftSize({ deviceType: EDeviceType.Neo })).toEqual({ width: 540, height: 540 });
63
+ expect(getNftSize({ deviceType: EDeviceType.Neo, thumbnail: true })).toEqual({
64
+ width: 263,
65
+ height: 263,
66
+ });
67
+ });
68
+
52
69
  it('preserves the legacy NFT dimensions', () => {
53
70
  expect(getNftSize({ deviceType: EDeviceType.Touch })).toEqual({ width: 480, height: 800 });
54
71
  expect(getNftSize({ deviceType: EDeviceType.Touch, thumbnail: true })).toEqual({
@@ -1,7 +1,7 @@
1
1
  import { EFirmwareType, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
2
 
3
3
  import { BaseMethod } from '../src/api/BaseMethod';
4
- import { findMethod } from '../src/api/utils';
4
+ import { findMethod, getMethodSupportedProtocols } from '../src/api/utils';
5
5
  import BTCGetAddress from '../src/api/btc/BTCGetAddress';
6
6
  import DeviceReboot from '../src/api/protocol-v2/DeviceReboot';
7
7
  import SolGetAddress from '../src/api/solana/SolGetAddress';
@@ -129,4 +129,16 @@ describe('method protocol support', () => {
129
129
  expect(bitcoin.getVersionRange().pro2).toBeUndefined();
130
130
  expect(neurai.getVersionRange().pro2).toBeUndefined();
131
131
  });
132
+
133
+ test('exposes the dispatcher protocol contract for callers that need a preflight', () => {
134
+ expect(getMethodSupportedProtocols('solGetAddress')).toEqual(['V1', 'V2']);
135
+ expect(getMethodSupportedProtocols('stellarGetAddress')).toEqual(['V1']);
136
+ expect(
137
+ getMethodSupportedProtocols('btcGetAddress', {
138
+ path: "m/44'/1900'/0'/0/0",
139
+ coin: 'Neurai',
140
+ showOnOneKey: false,
141
+ })
142
+ ).toEqual(['V1']);
143
+ });
132
144
  });