@onekeyfe/hd-core 1.2.2-alpha.100 → 1.2.2-alpha.3

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 (69) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +20 -4
  2. package/__tests__/DeviceCommands.test.ts +13 -0
  3. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +0 -2
  4. package/__tests__/check-firmware-release-protocol-v2.test.ts +0 -2
  5. package/__tests__/device-lifecycle-events.test.ts +19 -0
  6. package/__tests__/deviceUploadNft.test.ts +68 -0
  7. package/__tests__/evmSignTypedData.test.ts +42 -0
  8. package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +825 -12
  9. package/__tests__/open-wallet-session.test.ts +144 -37
  10. package/__tests__/pro2HostAssetPackage.test.ts +58 -0
  11. package/__tests__/protocol-v2-resources.test.ts +7 -4
  12. package/__tests__/protocol-v2-unlock-policy.test.ts +35 -1
  13. package/__tests__/protocol-v2.test.ts +720 -51
  14. package/dist/api/FirmwareUpdateV4.d.ts +4 -0
  15. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  16. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  17. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  18. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  19. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +2 -2
  20. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  21. package/dist/api/firmware/getBinary.d.ts +2 -2
  22. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  23. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
  24. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
  25. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  26. package/dist/core/index.d.ts +1 -0
  27. package/dist/core/index.d.ts.map +1 -1
  28. package/dist/device/Device.d.ts +1 -1
  29. package/dist/device/Device.d.ts.map +1 -1
  30. package/dist/device/DeviceConnector.d.ts +1 -1
  31. package/dist/device/DeviceConnector.d.ts.map +1 -1
  32. package/dist/events/ui-request.d.ts +3 -0
  33. package/dist/events/ui-request.d.ts.map +1 -1
  34. package/dist/index.d.ts +21 -5
  35. package/dist/index.js +2288 -1439
  36. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  37. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +2 -0
  38. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
  39. package/dist/protocols/protocol-v2/walletSession.d.ts +3 -0
  40. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  41. package/dist/types/settings.d.ts +2 -2
  42. package/dist/types/settings.d.ts.map +1 -1
  43. package/dist/utils/deviceFeaturesUtils.d.ts +3 -0
  44. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  45. package/dist/utils/patch.d.ts +1 -1
  46. package/dist/utils/patch.d.ts.map +1 -1
  47. package/dist/utils/pro2HostAssetPackage.d.ts +8 -0
  48. package/dist/utils/pro2HostAssetPackage.d.ts.map +1 -0
  49. package/package.json +4 -4
  50. package/src/api/FirmwareUpdateV4.ts +434 -75
  51. package/src/api/OpenWalletSession.ts +39 -9
  52. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +5 -2
  53. package/src/api/evm/EVMSignTypedData.ts +1 -0
  54. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +9 -3
  55. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +7 -1
  56. package/src/api/protocol-v2/DeviceUploadNft.ts +31 -8
  57. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +27 -8
  58. package/src/api/protocol-v2/helpers.ts +8 -0
  59. package/src/core/index.ts +130 -14
  60. package/src/data/messages/messages-protocol-v2.json +1541 -1332
  61. package/src/device/Device.ts +3 -1
  62. package/src/device/DeviceConnector.ts +7 -3
  63. package/src/events/ui-request.ts +3 -0
  64. package/src/protocols/protocol-v2/resources.ts +9 -1
  65. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +8 -1
  66. package/src/protocols/protocol-v2/walletSession.ts +88 -50
  67. package/src/types/settings.ts +4 -2
  68. package/src/utils/deviceFeaturesUtils.ts +4 -0
  69. package/src/utils/pro2HostAssetPackage.ts +354 -0
@@ -95,7 +95,8 @@ describe('AllNetworkGetAddressBase tracing', () => {
95
95
  'hidden-state',
96
96
  false,
97
97
  undefined,
98
- false
98
+ undefined,
99
+ undefined
99
100
  );
100
101
  expect(calls).toEqual(['resume-hidden-session', 'run-chain-method']);
101
102
  expect(typedCall).not.toHaveBeenCalled();
@@ -173,7 +174,13 @@ describe('AllNetworkGetAddressBase tracing', () => {
173
174
  0
174
175
  );
175
176
 
176
- expect(checkPassphraseStateSafety).toHaveBeenCalledWith('hidden-state', false, undefined, true);
177
+ expect(checkPassphraseStateSafety).toHaveBeenCalledWith(
178
+ 'hidden-state',
179
+ false,
180
+ undefined,
181
+ true,
182
+ undefined
183
+ );
177
184
  expect(calls).toEqual(['resume-cardano-session', 'run-cardano-method']);
178
185
  expect(typedCall).not.toHaveBeenCalled();
179
186
  });
@@ -212,7 +219,10 @@ describe('AllNetworkGetAddressBase tracing', () => {
212
219
  bundle: [],
213
220
  },
214
221
  });
215
- method.protocolV2UnlockContext = { preflightCompleted: true };
222
+ method.protocolV2UnlockContext = {
223
+ preflightCompleted: true,
224
+ preflightMainPinSelected: true,
225
+ };
216
226
  const typedCall = jest.fn();
217
227
  method.device = {
218
228
  checkPassphraseStateSafety,
@@ -250,7 +260,13 @@ describe('AllNetworkGetAddressBase tracing', () => {
250
260
  0
251
261
  );
252
262
 
253
- expect(checkPassphraseStateSafety).toHaveBeenCalledWith(undefined, true, undefined, false);
263
+ expect(checkPassphraseStateSafety).toHaveBeenCalledWith(
264
+ undefined,
265
+ true,
266
+ undefined,
267
+ undefined,
268
+ true
269
+ );
254
270
  expect(calls).toEqual(['resume-standard-session', 'run-chain-method']);
255
271
  expect(typedCall).not.toHaveBeenCalled();
256
272
  });
@@ -126,6 +126,19 @@ describe('DeviceCommands failure mapping', () => {
126
126
  );
127
127
  });
128
128
 
129
+ it('preserves the Protocol V2 peer-removed pairing error code', async () => {
130
+ const commands = createCommands();
131
+ const transportError = Object.assign(new Error('Peer removed pairing information'), {
132
+ errorCode: HardwareErrorCode.BlePeerRemovedPairingInformation,
133
+ });
134
+ commands.mainId = 'main-id';
135
+ commands.transport = {
136
+ call: jest.fn().mockRejectedValue(transportError),
137
+ } as any;
138
+
139
+ await expect(commands._commonCall('DeviceInfoGet', {})).rejects.toBe(transportError);
140
+ });
141
+
129
142
  it('logs canonical DeviceStatus response fields without exposing the device ID', async () => {
130
143
  const commands = createCommands();
131
144
  const log = getLogger(LoggerNames.DeviceCommands);
@@ -33,8 +33,6 @@ const validComponentIntegrity = {
33
33
  const release: IFirmwareReleaseInfo = {
34
34
  required: true,
35
35
  version: [2, 0, 0],
36
- url: 'https://example.com/application-p1.bin',
37
- fingerprint: 'release-fingerprint',
38
36
  changelog: {
39
37
  'zh-CN': '更新',
40
38
  'en-US': 'Update',
@@ -16,8 +16,6 @@ jest.mock('../src/data/config', () => ({
16
16
  const release: IFirmwareReleaseInfo = {
17
17
  required: false,
18
18
  version: [2, 0, 0],
19
- url: 'https://example.com/application-p1.okpkg',
20
- fingerprint: 'release-fingerprint',
21
19
  changelog: {
22
20
  'zh-CN': '更新',
23
21
  'en-US': 'Update',
@@ -5,6 +5,7 @@ import {
5
5
  initConnector,
6
6
  initCore,
7
7
  isMissingDetectedProtocolV2Error,
8
+ isProtocolV2PeerRemovedPairingError,
8
9
  isRetryableBleConnectionError,
9
10
  isRetryableBleProtocolV2ProbeError,
10
11
  } from '../src/core';
@@ -278,6 +279,7 @@ describe('public device lifecycle events', () => {
278
279
  test.each([
279
280
  [HardwareErrorCode.RuntimeError, true],
280
281
  [HardwareErrorCode.BleDeviceBondError, false],
282
+ [HardwareErrorCode.BlePeerRemovedPairingInformation, false],
281
283
  ] as const)(
282
284
  'retries a Protocol V2 probe mismatch with error code %s: %s',
283
285
  (errorCode, expected) => {
@@ -297,6 +299,7 @@ describe('public device lifecycle events', () => {
297
299
  [HardwareErrorCode.BleTimeoutError, true],
298
300
  [HardwareErrorCode.PollingTimeout, false],
299
301
  [HardwareErrorCode.BleDeviceBondError, false],
302
+ [HardwareErrorCode.BlePeerRemovedPairingInformation, false],
300
303
  ] as const)('retries a BLE connection error with error code %s: %s', (errorCode, expected) => {
301
304
  const method = { payload: { connectProtocol: 'V2' } } as never;
302
305
  const error = {
@@ -307,6 +310,22 @@ describe('public device lifecycle events', () => {
307
310
  expect(isRetryableBleConnectionError(method, error)).toBe(expected);
308
311
  });
309
312
 
313
+ test.each([
314
+ ['V2', true],
315
+ ['V1', false],
316
+ [undefined, false],
317
+ ] as const)(
318
+ 'treats peer-removed pairing as terminal only for Protocol %s: %s',
319
+ (connectProtocol, expected) => {
320
+ const method = { payload: { connectProtocol } } as never;
321
+ const error = {
322
+ errorCode: HardwareErrorCode.BlePeerRemovedPairingInformation,
323
+ };
324
+
325
+ expect(isProtocolV2PeerRemovedPairingError(method, error)).toBe(expected);
326
+ }
327
+ );
328
+
310
329
  test('converts an internal transport disconnect into a public KnownDevice snapshot', () => {
311
330
  jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
312
331
  core = initCore();
@@ -32,10 +32,12 @@ const createMethod = ({
32
32
  typedCall,
33
33
  supportedMessages = [60802, 60805, 60808, 61500],
34
34
  useFullBundle = false,
35
+ firmwareVersion = '1.0.0',
35
36
  }: {
36
37
  typedCall: jest.Mock;
37
38
  supportedMessages?: number[];
38
39
  useFullBundle?: boolean;
40
+ firmwareVersion?: string;
39
41
  }) => {
40
42
  const method = new DeviceUploadNft({
41
43
  id: 1,
@@ -52,6 +54,7 @@ const createMethod = ({
52
54
  });
53
55
  (method as any).device = {
54
56
  commands: { typedCall },
57
+ state: { versions: { firmware: firmwareVersion } },
55
58
  ensureProtocolV2RuntimeContext: jest.fn(() =>
56
59
  Promise.resolve({
57
60
  version: 2,
@@ -60,6 +63,7 @@ const createMethod = ({
60
63
  })
61
64
  ),
62
65
  getCurrentFirmwareType: jest.fn(),
66
+ getCurrentFirmwareVersionString: jest.fn(() => firmwareVersion),
63
67
  };
64
68
  method.postMessage = jest.fn();
65
69
 
@@ -202,6 +206,70 @@ describe('DeviceUploadNft', () => {
202
206
  });
203
207
  });
204
208
 
209
+ test('uploads one host asset package on firmware 1.0.1', async () => {
210
+ const typedCall = jest.fn((request: string, _response: string, params: any) => {
211
+ if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
212
+ if (request === 'NftUpdate') return { message: { message: 'NFT updated' } };
213
+ throw new Error(`Unexpected request: ${request}`);
214
+ });
215
+ const method = createMethod({
216
+ typedCall,
217
+ supportedMessages: [60805, 61500],
218
+ firmwareVersion: '1.0.1',
219
+ });
220
+
221
+ const result = await method.run();
222
+
223
+ const requests = typedCall.mock.calls.map(call => call[0]);
224
+ expect(requests).not.toContain('FilesystemPathInfoQuery');
225
+ expect(requests).not.toContain('FilesystemDirList');
226
+ const fileWrites = typedCall.mock.calls.filter(call => call[0] === 'FilesystemFileWrite');
227
+ expect(new Set(fileWrites.map(call => call[2].file.path))).toEqual(
228
+ new Set(['vol1:/nft/nft-deadbeef-1760000000000.okpkg'])
229
+ );
230
+ expect(fileWrites[0][2].file.data.subarray(0, 4)).toEqual(
231
+ new Uint8Array([0x4f, 0x4b, 0x50, 0x50])
232
+ );
233
+ expect(typedCall).toHaveBeenLastCalledWith(
234
+ 'NftUpdate',
235
+ 'Success',
236
+ { file_name_no_ext: result.basename },
237
+ { timeoutMs: 15_000 }
238
+ );
239
+ expect(result).toMatchObject({
240
+ imagePath: 'vol1:/nft/nft-deadbeef-1760000000000.bin',
241
+ thumbnailPath: 'vol1:/nft/nft-deadbeef-1760000000000_m.bin',
242
+ metadataPath: 'vol1:/nft/nft-deadbeef-1760000000000.json',
243
+ nftUpdated: true,
244
+ });
245
+ });
246
+
247
+ test('keeps the legacy upload flow on a 1.0.1 prerelease', async () => {
248
+ const typedCall = jest.fn((request: string, _response: string, params: any) => {
249
+ if (request === 'FilesystemPathInfoQuery') {
250
+ return { message: { exist: true, directory: true } };
251
+ }
252
+ if (request === 'FilesystemDirList') {
253
+ return { message: { path: 'vol1:/nft', child_files: '' } };
254
+ }
255
+ if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
256
+ if (request === 'NftUpdate') return { message: {} };
257
+ throw new Error(`Unexpected request: ${request}`);
258
+ });
259
+ const method = createMethod({ typedCall, firmwareVersion: '1.0.1-beta.1' });
260
+
261
+ await method.run();
262
+
263
+ const requests = typedCall.mock.calls.map(call => call[0]);
264
+ expect(requests).toContain('FilesystemPathInfoQuery');
265
+ expect(requests).toContain('FilesystemDirList');
266
+ const paths = typedCall.mock.calls
267
+ .filter(call => call[0] === 'FilesystemFileWrite')
268
+ .map(call => call[2].file.path as string);
269
+ expect(paths.some(path => path.endsWith('.okpkg'))).toBe(false);
270
+ expect(paths).toContain('vol1:/nft/nft-deadbeef-1760000000000.bin');
271
+ });
272
+
205
273
  test('treats a missing NFT directory as empty before the first upload', async () => {
206
274
  const typedCall = jest.fn((request: string, _response: string, params: any) => {
207
275
  if (request === 'FilesystemPathInfoQuery') {
@@ -1,3 +1,5 @@
1
+ import { EDeviceType } from '@onekeyfe/hd-shared';
2
+
1
3
  import EVMSignTypedData from '../src/api/evm/EVMSignTypedData';
2
4
 
3
5
  import type { EthereumSignTypedDataMessage, EthereumSignTypedDataTypes } from '../src/types';
@@ -577,3 +579,43 @@ describe('EVMSignTypedData — OneKey Pro Safe Protocol V1', () => {
577
579
  });
578
580
  });
579
581
  });
582
+
583
+ describe('EVMSignTypedData — Protocol V2 data size routing', () => {
584
+ const buildSafeTxData = (
585
+ dataSize: number
586
+ ): EthereumSignTypedDataMessage<EthereumSignTypedDataTypes> =>
587
+ ({
588
+ types: {
589
+ EIP712Domain: [],
590
+ SafeTx: [{ name: 'data', type: 'bytes' }],
591
+ },
592
+ primaryType: 'SafeTx',
593
+ domain: {},
594
+ message: { data: `0x${'ab'.repeat(dataSize)}` },
595
+ } as EthereumSignTypedDataMessage<EthereumSignTypedDataTypes>);
596
+
597
+ test.each([EDeviceType.Pro2, EDeviceType.Neo])(
598
+ 'keeps SafeTx data up to 1536 bytes on the structured route for %s',
599
+ deviceType => {
600
+ const data = buildSafeTxData(1316);
601
+ const method = createMethod(data);
602
+ method.device.getCurrentDeviceType = jest.fn(() => deviceType);
603
+ method.device.getCurrentFirmwareVersionString = jest.fn(() => '1.0.0');
604
+
605
+ expect(method.hasBiggerData(data)).toBe(false);
606
+ expect(method.hasBiggerData(buildSafeTxData(1536))).toBe(false);
607
+ }
608
+ );
609
+
610
+ test.each([EDeviceType.Pro2, EDeviceType.Neo])(
611
+ 'falls back to the hash route above 1536 bytes for %s',
612
+ deviceType => {
613
+ const data = buildSafeTxData(1537);
614
+ const method = createMethod(data);
615
+ method.device.getCurrentDeviceType = jest.fn(() => deviceType);
616
+ method.device.getCurrentFirmwareVersionString = jest.fn(() => '1.0.0');
617
+
618
+ expect(method.hasBiggerData(data)).toBe(true);
619
+ }
620
+ );
621
+ });