@onekeyfe/hd-core 1.2.0-alpha.105 → 1.2.0-alpha.107

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 (67) hide show
  1. package/__tests__/DeviceCommands.test.ts +2 -10
  2. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +0 -2
  3. package/__tests__/device-lifecycle-events.test.ts +2 -113
  4. package/__tests__/device-pool-state.test.ts +0 -29
  5. package/__tests__/deviceUploadNft.test.ts +4 -33
  6. package/__tests__/get-device-state.test.ts +13 -52
  7. package/__tests__/protocol-v2.test.ts +18 -330
  8. package/__tests__/refresh-device-state.test.ts +1 -4
  9. package/__tests__/search-devices.test.ts +38 -34
  10. package/dist/api/FirmwareUpdateV4.d.ts +0 -3
  11. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  12. package/dist/api/GetDeviceState.d.ts.map +1 -1
  13. package/dist/api/SearchDevices.d.ts.map +1 -1
  14. package/dist/api/UploadPortfolio.d.ts +1 -1
  15. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  16. package/dist/api/firmware/protocolV2Release.d.ts.map +1 -1
  17. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +3 -2
  18. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
  19. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +3 -2
  20. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
  21. package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
  22. package/dist/core/index.d.ts.map +1 -1
  23. package/dist/device/Device.d.ts +2 -10
  24. package/dist/device/Device.d.ts.map +1 -1
  25. package/dist/device/DevicePool.d.ts +1 -1
  26. package/dist/device/DevicePool.d.ts.map +1 -1
  27. package/dist/index.d.ts +16 -18
  28. package/dist/index.js +99 -277
  29. package/dist/protocols/protocol-v2/features.d.ts +0 -8
  30. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  31. package/dist/protocols/protocol-v2/index.d.ts +2 -2
  32. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  33. package/dist/types/api/getDeviceState.d.ts +0 -1
  34. package/dist/types/api/getDeviceState.d.ts.map +1 -1
  35. package/dist/types/api/protocolV2.d.ts +1 -1
  36. package/dist/types/api/protocolV2.d.ts.map +1 -1
  37. package/dist/types/device.d.ts.map +1 -1
  38. package/dist/utils/pro2Nft.d.ts +0 -7
  39. package/dist/utils/pro2Nft.d.ts.map +1 -1
  40. package/package.json +4 -6
  41. package/src/api/FirmwareUpdateV4.ts +24 -72
  42. package/src/api/GetDeviceState.ts +0 -1
  43. package/src/api/SearchDevices.ts +2 -4
  44. package/src/api/UploadPortfolio.ts +2 -9
  45. package/src/api/firmware/protocolV2Release.ts +0 -1
  46. package/src/api/protocol-v2/DeviceUploadNft.ts +8 -56
  47. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +18 -37
  48. package/src/api/ton/TonSignMessage.ts +3 -5
  49. package/src/core/index.ts +0 -4
  50. package/src/device/Device.ts +31 -90
  51. package/src/device/DevicePool.ts +5 -17
  52. package/src/protocols/protocol-v2/features.ts +0 -10
  53. package/src/protocols/protocol-v2/index.ts +0 -2
  54. package/src/types/api/getDeviceState.ts +0 -2
  55. package/src/types/api/protocolV2.ts +1 -1
  56. package/src/types/device.ts +2 -1
  57. package/src/utils/pro2Nft.ts +8 -31
  58. package/__tests__/base64Data.test.ts +0 -70
  59. package/__tests__/protocol-v2-legacy-recovery.test.ts +0 -29
  60. package/__tests__/resourceBase64Boundary.test.ts +0 -48
  61. package/__tests__/ton-sign-message.test.ts +0 -84
  62. package/dist/api/helpers/base64Data.d.ts +0 -16
  63. package/dist/api/helpers/base64Data.d.ts.map +0 -1
  64. package/dist/core/protocolV2LegacyRecovery.d.ts +0 -5
  65. package/dist/core/protocolV2LegacyRecovery.d.ts.map +0 -1
  66. package/src/api/helpers/base64Data.ts +0 -85
  67. package/src/core/protocolV2LegacyRecovery.ts +0 -12
@@ -561,22 +561,17 @@ describe('DeviceCommands failure mapping', () => {
561
561
  });
562
562
 
563
563
  describe('DeviceCommands Protocol V2 interactive response compatibility', () => {
564
- it('auto-acks Pro2 ButtonRequest and registers Cancel instead of Initialize', async () => {
564
+ it('still auto-acks ButtonRequest when no hardware UI listener is registered', async () => {
565
565
  const commands = createCommands();
566
566
  const emit = jest.fn();
567
- const cancelDevice = jest.spyOn(commands, 'cancelDevice').mockResolvedValue(undefined);
568
- const cancelDeviceOnOneKeyDevice = jest
569
- .spyOn(commands, 'cancelDeviceOnOneKeyDevice')
570
- .mockResolvedValue(undefined);
571
567
  const commonCall = jest.fn().mockResolvedValue({
572
568
  type: 'Success',
573
569
  message: {},
574
570
  });
575
- const setCancelableAction = jest.fn();
576
571
  commands.device = {
577
572
  ...commands.device,
578
573
  getCurrentDeviceType: jest.fn(() => 'pro2'),
579
- setCancelableAction,
574
+ setCancelableAction: jest.fn(),
580
575
  emit,
581
576
  listenerCount: jest.fn(() => 0),
582
577
  } as any;
@@ -598,9 +593,6 @@ describe('DeviceCommands Protocol V2 interactive response compatibility', () =>
598
593
  expect.objectContaining({ code: 'ButtonRequest_PinEntry' })
599
594
  );
600
595
  expect(commonCall).toHaveBeenCalledWith('ButtonAck', {}, undefined);
601
- await setCancelableAction.mock.calls[0][0]();
602
- expect(cancelDevice).toHaveBeenCalledTimes(1);
603
- expect(cancelDeviceOnOneKeyDevice).not.toHaveBeenCalled();
604
596
  });
605
597
 
606
598
  it('rejects PassphraseRequest when the Pro2 UI listener is not registered', async () => {
@@ -346,7 +346,6 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
346
346
  expect(typedCall).not.toHaveBeenCalled();
347
347
  expect(getDeviceState).toHaveBeenCalledWith({
348
348
  refreshSections: ['identity', 'versions'],
349
- allowLegacyProtocolV2ProtocolInfo: true,
350
349
  });
351
350
  expect(method.getSupportedProtocols()).toEqual(['V1', 'V2']);
352
351
  });
@@ -726,7 +725,6 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
726
725
  });
727
726
  expect(getDeviceState).toHaveBeenCalledWith({
728
727
  refreshSections: ['identity', 'versions', 'verification'],
729
- allowLegacyProtocolV2ProtocolInfo: true,
730
728
  });
731
729
  });
732
730
 
@@ -1,14 +1,12 @@
1
- import { EDeviceType, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
- import { DeviceType, TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
1
+ import { HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+ import { TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
3
3
 
4
4
  import { initConnector, initCore } from '../src/core';
5
5
  import { DataManager } from '../src/data-manager';
6
6
  import TransportManager from '../src/data-manager/TransportManager';
7
7
  import { Device } from '../src/device/Device';
8
- import { cancelDeviceInPrompt, cancelDeviceWithInitialize } from '../src/device/DeviceCommands';
9
8
  import { DevicePool } from '../src/device/DevicePool';
10
9
  import { CORE_EVENT, DEVICE, IFRAME } from '../src/events';
11
- import { PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE } from '../src/protocols/protocol-v2';
12
10
 
13
11
  import type Core from '../src/core';
14
12
  import type { CoreMessage } from '../src/events';
@@ -273,115 +271,6 @@ describe('public device lifecycle events', () => {
273
271
  expect(device.isUsedHere()).toBe(false);
274
272
  });
275
273
 
276
- test.each([
277
- ['V2', 'Cancel', 'webusb'],
278
- ['V2', 'Cancel', 'desktop-web-ble'],
279
- ['V2', 'Cancel', 'react-native'],
280
- ['V1', 'Initialize', 'webusb'],
281
- ['V1', 'Cancel', 'desktop-web-ble'],
282
- ] as const)(
283
- 'sends Protocol %s %s without treating the acquired %s session as disconnected',
284
- async (protocol, messageType, env) => {
285
- jest.spyOn(DataManager, 'getSettings').mockReturnValue(env as never);
286
- const device = createInitializedDevice(protocol);
287
- const post = jest.fn().mockResolvedValue(undefined);
288
- const call = jest.fn().mockResolvedValue(undefined);
289
- const cancel = jest.fn().mockResolvedValue(undefined);
290
- device.originalDescriptor.session = device.mainId;
291
- (device as unknown as { deviceAcquired: boolean }).deviceAcquired = true;
292
- (device as any).commands = { transport: { post, call }, cancel };
293
- device.setCancelableAction(() =>
294
- messageType === 'Cancel'
295
- ? cancelDeviceInPrompt(device, false)
296
- : cancelDeviceWithInitialize(device)
297
- );
298
-
299
- await device.interruptionFromUser();
300
-
301
- if (messageType === 'Cancel') {
302
- expect(post).toHaveBeenCalledWith(device.mainId, 'Cancel', {});
303
- expect(call).not.toHaveBeenCalled();
304
- } else {
305
- expect(call).toHaveBeenCalledWith(device.mainId, 'Initialize', {});
306
- expect(post).not.toHaveBeenCalled();
307
- }
308
- expect(cancel).toHaveBeenCalledTimes(1);
309
- expect(device.hasDeviceAcquire()).toBe(true);
310
- }
311
- );
312
-
313
- test.each([
314
- [EDeviceType.Pro2, 'webusb', false, DeviceType.PRO2],
315
- [EDeviceType.Neo, 'webusb', false, DeviceType.NEO],
316
- [EDeviceType.Pro2, 'desktop-web-ble', true, DeviceType.PRO2],
317
- [EDeviceType.Neo, 'desktop-web-ble', true, DeviceType.NEO],
318
- [EDeviceType.Pro2, 'react-native', true, DeviceType.PRO2],
319
- [EDeviceType.Neo, 'react-native', true, DeviceType.NEO],
320
- [EDeviceType.Pro2, 'lowlevel', false, DeviceType.PRO2],
321
- ] as const)(
322
- 'refreshes %s runtime state after acquiring a fresh %s session',
323
- async (deviceType, env, isBle, protocolV2DeviceType) => {
324
- jest.spyOn(DataManager, 'getSettings').mockReturnValue(env as never);
325
- const device = createInitializedDevice('V2');
326
- const staleProtocolInfo = {
327
- version: 1,
328
- build_fingerprint: 'bootloader__1.0.0__abcdef0__PROD__RELEASE',
329
- supported_messages: [],
330
- };
331
- const freshProtocolInfo = {
332
- version: 1,
333
- build_fingerprint: 'application__5.0.0__abcdef0__PROD__RELEASE',
334
- supported_messages: [PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE],
335
- };
336
- device.updateState(
337
- {
338
- identity: { deviceType },
339
- status: { mode: 'bootloader' },
340
- raw: { protocolV2ProtocolInfo: staleProtocolInfo },
341
- },
342
- 'initialize'
343
- );
344
- const acquire = jest
345
- .fn()
346
- .mockResolvedValue(isBle ? { uuid: 'fresh-session', protocolType: 'V2' } : 'fresh-session');
347
- device.deviceConnector = { acquire } as never;
348
-
349
- await device.acquire('V2', { throwOnRunPromiseError: true });
350
- const typedCall = jest.fn().mockImplementation((requestType: string) => {
351
- if (requestType === 'DeviceInfoGet') {
352
- return {
353
- message: {
354
- protocol_version: 2,
355
- hw: { Device_type: protocolV2DeviceType, serial_no: 'SERIAL-001' },
356
- fw: { application: { version: '5.0.0' } },
357
- },
358
- };
359
- }
360
- if (requestType === 'ProtocolInfoRequest') {
361
- return { message: freshProtocolInfo };
362
- }
363
- if (requestType === 'DeviceStatusGet') {
364
- return {
365
- message: { init_states: true, unlocked: true, device_id: 'wallet-device-id' },
366
- };
367
- }
368
- throw new Error(`Unexpected request: ${requestType}`);
369
- });
370
- device.commands.typedCall = typedCall as never;
371
-
372
- await device.initialize();
373
-
374
- expect(typedCall.mock.calls.map(callArgs => callArgs[0])).toEqual([
375
- 'DeviceInfoGet',
376
- 'ProtocolInfoRequest',
377
- 'DeviceStatusGet',
378
- ]);
379
- expect(device.state?.status.mode).toBe('normal');
380
- expect(device.state?.identity.deviceType).toBe(deviceType);
381
- expect(device.hasDeviceAcquire()).toBe(true);
382
- }
383
- );
384
-
385
274
  test('exposes the current transport usage state in public device snapshots', () => {
386
275
  const getSettings = jest
387
276
  .spyOn(DataManager, 'getSettings')
@@ -38,35 +38,6 @@ describe('DevicePool state lifecycle', () => {
38
38
  expect(getDeviceState).toHaveBeenNthCalledWith(2, { refreshSections: ['settings'] });
39
39
  });
40
40
 
41
- test('keeps legacy ProtocolInfo compatibility scoped to a discovery refresh', async () => {
42
- const getDeviceState = jest.fn().mockResolvedValue({ status: { mode: 'bootloader' } });
43
- const run = jest.fn(async (callback: () => Promise<void>) => callback());
44
- const device = {
45
- isProtocolV2: () => true,
46
- getDeviceState,
47
- run,
48
- } as any;
49
-
50
- await (DevicePool as any)._refreshRuntimeState(device, {
51
- refreshRuntimeState: true,
52
- allowLegacyProtocolV2ProtocolInfo: true,
53
- });
54
-
55
- expect(run).toHaveBeenCalledWith(expect.any(Function), {
56
- connectProtocol: undefined,
57
- forceProtocolDetection: undefined,
58
- allowLegacyProtocolV2ProtocolInfo: true,
59
- });
60
- expect(getDeviceState).toHaveBeenNthCalledWith(1, {
61
- refreshSections: ['status'],
62
- allowLegacyProtocolV2ProtocolInfo: true,
63
- });
64
- expect(getDeviceState).toHaveBeenNthCalledWith(2, {
65
- refreshSections: ['settings'],
66
- allowLegacyProtocolV2ProtocolInfo: true,
67
- });
68
- });
69
-
70
41
  test('actively re-detects a cached device when protocol detection is forced', async () => {
71
42
  const descriptor = { path: 'cached-path', protocolType: 'V1' } as any;
72
43
  const device = Device.fromDescriptor(descriptor);
@@ -1,5 +1,4 @@
1
1
  import { HardwareErrorCode } from '@onekeyfe/hd-shared';
2
- import { encode as encodeJpeg } from 'jpeg-js';
3
2
 
4
3
  import DeviceUploadNft from '../src/api/protocol-v2/DeviceUploadNft';
5
4
 
@@ -14,19 +13,6 @@ const createRgba = (width: number, height: number) => {
14
13
  return data;
15
14
  };
16
15
 
17
- const jpegBase64Cache = new Map<string, string>();
18
-
19
- const createJpegBase64 = (width: number, height: number) => {
20
- const key = `${width}x${height}`;
21
- const cached = jpegBase64Cache.get(key);
22
- if (cached) return cached;
23
- const value = encodeJpeg({ width, height, data: createRgba(width, height) }, 80).data.toString(
24
- 'base64'
25
- );
26
- jpegBase64Cache.set(key, value);
27
- return value;
28
- };
29
-
30
16
  const createMethod = ({
31
17
  typedCall,
32
18
  supportedMessages = [60802, 60805, 60808, 61500],
@@ -40,8 +26,8 @@ const createMethod = ({
40
26
  id: 1,
41
27
  payload: {
42
28
  method: 'deviceUploadNft',
43
- imageJpegBase64: createJpegBase64(540, 540),
44
- thumbnailJpegBase64: createJpegBase64(263, 263),
29
+ image: { width: 540, height: 540, rgba: createRgba(540, 540) },
30
+ thumbnail: { width: 263, height: 263, rgba: createRgba(263, 263) },
45
31
  title: 'CryptoPunk #3100',
46
32
  subtitle: 'CryptoPunks',
47
33
  timestampMs: 1_760_000_000_000,
@@ -100,8 +86,8 @@ describe('DeviceUploadNft', () => {
100
86
  id: 1,
101
87
  payload: {
102
88
  method: 'deviceUploadNft',
103
- imageJpegBase64: createJpegBase64(540, 540),
104
- thumbnailJpegBase64: createJpegBase64(263, 263),
89
+ image: { width: 540, height: 540, rgba: createRgba(540, 540) },
90
+ thumbnail: { width: 263, height: 263, rgba: createRgba(263, 263) },
105
91
  title: 'CryptoPunk #3100',
106
92
  subtitle: 'CryptoPunks',
107
93
  timestampMs: 1_760_000_000_000,
@@ -116,21 +102,6 @@ describe('DeviceUploadNft', () => {
116
102
  });
117
103
  });
118
104
 
119
- test('rejects invalid image Base64 before device communication', () => {
120
- const method = new DeviceUploadNft({
121
- id: 1,
122
- payload: {
123
- method: 'deviceUploadNft',
124
- imageJpegBase64: 'not-base64',
125
- thumbnailJpegBase64: createJpegBase64(263, 263),
126
- title: 'NFT',
127
- subtitle: '',
128
- },
129
- });
130
-
131
- expect(() => method.init()).toThrow('canonical Base64');
132
- });
133
-
134
105
  test('uploads the triplet in order without creating the firmware-owned directory', async () => {
135
106
  const typedCall = jest.fn((request: string, _response: string, params: any) => {
136
107
  if (request === 'FilesystemPathInfoQuery') {
@@ -202,61 +202,25 @@ describe('getDeviceState', () => {
202
202
  expect(state.versions.se01).toBe('1.0.0');
203
203
  });
204
204
 
205
- test.each([
206
- ['bootloader', EDeviceType.Pro2, DeviceType.PRO2],
207
- ['romloader', EDeviceType.Pro2, DeviceType.PRO2],
208
- ['bootloader', EDeviceType.Neo, DeviceType.NEO],
209
- ['romloader', EDeviceType.Neo, DeviceType.NEO],
210
- ] as const)(
211
- 'refreshes cached %s state after %s reboots into the application',
212
- async (mode, deviceType, protocolV2DeviceType) => {
205
+ test.each(['bootloader', 'romloader'] as const)(
206
+ 'uses ProtocolInfo to preserve %s mode without DeviceStatusGet',
207
+ async mode => {
213
208
  const typedCall = jest.fn().mockImplementation((requestType: string) => {
214
- if (requestType === 'ProtocolInfoRequest') {
215
- return { message: protocolV2ApplicationInfo };
216
- }
217
- if (requestType === 'DeviceStatusGet') {
209
+ if (requestType === 'DeviceInfoGet') {
218
210
  return {
219
- message: { init_states: true, unlocked: true, device_id: 'wallet-1' },
220
- };
221
- }
222
- throw new Error(`Unexpected request: ${requestType}`);
223
- });
224
- const device = createV2Device(typedCall);
225
- device.updateState(
226
- {
227
- protocol: 'V2',
228
- identity: { deviceType },
229
- status: { mode },
230
- raw: {
231
- protocolV2DeviceInfo: {
232
- hw: { Device_type: protocolV2DeviceType, serial_no: 'SERIAL-1' },
211
+ message: {
212
+ hw: { Device_type: DeviceType.PRO2, serial_no: 'SERIAL-1' },
233
213
  fw:
234
214
  mode === 'romloader'
235
215
  ? { romloader: { version: '1.0.0' } }
236
216
  : { bootloader: { version: '1.0.0' } },
237
217
  },
238
- protocolV2ProtocolInfo: getProtocolV2LoaderInfo(mode),
239
- },
240
- },
241
- 'initialize'
242
- );
243
-
244
- const state = await device.getDeviceState({ refreshSections: ['status'] });
245
-
246
- expect(state.status.mode).toBe('normal');
247
- expect(state.identity.deviceId).toBe('wallet-1');
248
- expect(typedCall.mock.calls.map(call => call[0])).toEqual([
249
- 'ProtocolInfoRequest',
250
- 'DeviceStatusGet',
251
- ]);
252
- }
253
- );
254
-
255
- test.each(['bootloader', 'romloader'] as const)(
256
- 'keeps live %s mode after refreshing cached loader state',
257
- async mode => {
258
- const typedCall = jest.fn().mockResolvedValue({
259
- message: getProtocolV2LoaderInfo(mode),
218
+ };
219
+ }
220
+ if (requestType === 'ProtocolInfoRequest') {
221
+ return { message: getProtocolV2LoaderInfo(mode) };
222
+ }
223
+ throw new Error(`Unexpected request: ${requestType}`);
260
224
  });
261
225
  const device = createV2Device(typedCall);
262
226
  device.updateState(
@@ -272,10 +236,7 @@ describe('getDeviceState', () => {
272
236
  const state = await device.getDeviceState({ refreshSections: ['status'] });
273
237
 
274
238
  expect(state.status.mode).toBe(mode);
275
- expect(typedCall).toHaveBeenCalledTimes(1);
276
- expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
277
- eventless_wallet_session: true,
278
- });
239
+ expect(typedCall).not.toHaveBeenCalled();
279
240
  }
280
241
  );
281
242