@onekeyfe/hd-core 1.2.0-alpha.45 → 1.2.0-alpha.46

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 (41) hide show
  1. package/__tests__/deviceUploadNft.test.ts +21 -0
  2. package/__tests__/open-wallet-session.test.ts +33 -1
  3. package/__tests__/protocol-v2-resources.test.ts +3 -38
  4. package/__tests__/protocol-v2.test.ts +7 -109
  5. package/__tests__/uiPromiseRegistry.test.ts +86 -0
  6. package/dist/api/FirmwareUpdateV4.d.ts +0 -2
  7. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  8. package/dist/core/index.d.ts.map +1 -1
  9. package/dist/core/uiPromiseRegistry.d.ts +4 -0
  10. package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
  11. package/dist/data-manager/DataManager.d.ts +0 -1
  12. package/dist/data-manager/DataManager.d.ts.map +1 -1
  13. package/dist/events/ui-promise.d.ts +4 -2
  14. package/dist/events/ui-promise.d.ts.map +1 -1
  15. package/dist/events/ui-request.d.ts +3 -0
  16. package/dist/events/ui-request.d.ts.map +1 -1
  17. package/dist/events/ui-response.d.ts +10 -2
  18. package/dist/events/ui-response.d.ts.map +1 -1
  19. package/dist/index.d.ts +52 -58
  20. package/dist/index.js +52 -109
  21. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  22. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  23. package/dist/types/api/firmwareUpdate.d.ts +1 -2
  24. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  25. package/dist/types/settings.d.ts +0 -10
  26. package/dist/types/settings.d.ts.map +1 -1
  27. package/dist/utils/pro2Nft.d.ts +2 -2
  28. package/dist/utils/pro2Nft.d.ts.map +1 -1
  29. package/package.json +4 -4
  30. package/src/api/FirmwareUpdateV4.ts +6 -76
  31. package/src/core/index.ts +20 -4
  32. package/src/core/uiPromiseRegistry.ts +41 -0
  33. package/src/data-manager/DataManager.ts +0 -4
  34. package/src/events/ui-promise.ts +4 -2
  35. package/src/events/ui-request.ts +3 -0
  36. package/src/events/ui-response.ts +12 -2
  37. package/src/protocols/protocol-v2/resources.ts +1 -33
  38. package/src/protocols/protocol-v2/walletSession.ts +5 -1
  39. package/src/types/api/firmwareUpdate.ts +0 -3
  40. package/src/types/settings.ts +0 -16
  41. package/src/utils/pro2Nft.ts +2 -2
@@ -3,8 +3,8 @@ export declare const PRO2_NFT_IMAGE_HEIGHT = 540;
3
3
  export declare const PRO2_NFT_THUMBNAIL_WIDTH = 263;
4
4
  export declare const PRO2_NFT_THUMBNAIL_HEIGHT = 263;
5
5
  export declare const PRO2_NFT_DIRECTORY = "vol1:/nft";
6
- export declare const PRO2_NFT_DEFAULT_CHUNK_SIZE = 512;
7
- export declare const PRO2_NFT_DEFAULT_PACE_MS = 20;
6
+ export declare const PRO2_NFT_DEFAULT_CHUNK_SIZE = 2048;
7
+ export declare const PRO2_NFT_DEFAULT_PACE_MS = 0;
8
8
  export declare const PRO2_NFT_DEFAULT_TIMEOUT_MS = 15000;
9
9
  export declare const PRO2_NFT_MIN_CHUNK_SIZE = 64;
10
10
  export declare const PRO2_NFT_MAX_CHUNK_SIZE = 2048;
@@ -1 +1 @@
1
- {"version":3,"file":"pro2Nft.d.ts","sourceRoot":"","sources":["../../src/utils/pro2Nft.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAC9C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAClD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,GAAG,WAAW,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,UAAU,CAAC;IACtB,QAAQ,EAAE,UAAU,CAAC;CACtB,CAAC;AAiCF,wBAAgB,2BAA2B,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAiB5E;AAsBD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,EAAE,YAAY,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,aAAa,CAkChB"}
1
+ {"version":3,"file":"pro2Nft.d.ts","sourceRoot":"","sources":["../../src/utils/pro2Nft.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAC9C,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAClD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,GAAG,WAAW,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,UAAU,CAAC;IACtB,QAAQ,EAAE,UAAU,CAAC;CACtB,CAAC;AAiCF,wBAAgB,2BAA2B,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAiB5E;AAsBD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,EAAE,YAAY,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,aAAa,CAkChB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-core",
3
- "version": "1.2.0-alpha.45",
3
+ "version": "1.2.0-alpha.46",
4
4
  "description": "Core processes and APIs for communicating with OneKey hardware devices.",
5
5
  "author": "OneKey",
6
6
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
@@ -25,8 +25,8 @@
25
25
  "url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
26
26
  },
27
27
  "dependencies": {
28
- "@onekeyfe/hd-shared": "1.2.0-alpha.45",
29
- "@onekeyfe/hd-transport": "1.2.0-alpha.45",
28
+ "@onekeyfe/hd-shared": "1.2.0-alpha.46",
29
+ "@onekeyfe/hd-transport": "1.2.0-alpha.46",
30
30
  "axios": "1.15.2",
31
31
  "bignumber.js": "^9.0.2",
32
32
  "bytebuffer": "^5.0.1",
@@ -44,5 +44,5 @@
44
44
  "@types/w3c-web-usb": "^1.0.10",
45
45
  "@types/web-bluetooth": "^0.0.21"
46
46
  },
47
- "gitHead": "8fc6c0d642bfaa268d23a2aa2a98e235d1a876f6"
47
+ "gitHead": "23bc93ba87e4f8557380c636b308aea06f569ae2"
48
48
  }
@@ -45,7 +45,6 @@ import type { TypedResponseMessage } from '../device/DeviceCommands';
45
45
  import type {
46
46
  Features,
47
47
  IFirmwareReleaseInfo,
48
- IProtocolV2BootResources,
49
48
  IProtocolV2FirmwareComponent,
50
49
  IProtocolV2Resource,
51
50
  IVersionArray,
@@ -75,7 +74,6 @@ const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
75
74
  const PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET = 0x200;
76
75
  const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
77
76
  const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
78
- const PROTOCOL_V2_BOOT_RESOURCES_FILE_NAME = 'boot_resources.crate.okpkg';
79
77
 
80
78
  const getProtocolV2DeviceTransferProgress = (
81
79
  bytesBeforeChunk: number,
@@ -110,7 +108,7 @@ type ProtocolV2FirmwareUpdateStartResponse = TypedResponseMessage<'Success'>;
110
108
 
111
109
  type ProtocolV2TargetBinary = { fileName: string; binary: ArrayBuffer; targetId: number };
112
110
  type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
113
- kind: ProtocolV2RemoteComponentTarget['kind'] | 'boot_resources';
111
+ kind: ProtocolV2RemoteComponentTarget['kind'];
114
112
  };
115
113
  type ProtocolV2InstallTarget = ProtocolV2InstallItem & {
116
114
  path: string;
@@ -371,11 +369,10 @@ export const assertProtocolV2ReconnectIdentity = (
371
369
  expectedSerialNumber?: string,
372
370
  actualSerialNumber?: string
373
371
  ) => {
372
+ // Some Pro2 loader builds omit serial_no. Reconnect still uses the same BLE
373
+ // descriptor or requires a uniquely enumerated USB device before this check.
374
374
  if (!expectedSerialNumber || !actualSerialNumber) {
375
- throw ERRORS.TypedError(
376
- HardwareErrorCode.DeviceNotFound,
377
- 'Protocol V2 reconnect physical identity is unavailable'
378
- );
375
+ return;
379
376
  }
380
377
  if (actualSerialNumber !== expectedSerialNumber) {
381
378
  throw ERRORS.TypedError(
@@ -426,7 +423,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
426
423
  { name: 'chunkSize', type: 'number' },
427
424
  { name: 'forcedUpdateRes', type: 'boolean' },
428
425
  { name: 'bootloaderBinary', type: 'buffer' },
429
- { name: 'bootResourcesBinary', type: 'buffer' },
430
426
  { name: 'romloaderBinary', type: 'buffer' },
431
427
  { name: 'applicationP1Binary', type: 'buffer' },
432
428
  { name: 'applicationP2Binary', type: 'buffer' },
@@ -445,7 +441,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
445
441
  chunkSize: payload.chunkSize,
446
442
  forcedUpdateRes: payload.forcedUpdateRes,
447
443
  bootloaderBinary: payload.bootloaderBinary,
448
- bootResourcesBinary: payload.bootResourcesBinary,
449
444
  romloaderBinary: payload.romloaderBinary,
450
445
  applicationP1Binary: payload.applicationP1Binary,
451
446
  applicationP2Binary: payload.applicationP2Binary,
@@ -494,7 +489,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
494
489
 
495
490
  let fwBinaryMap: ProtocolV2TargetBinary[] = [];
496
491
  let bootloaderBinary: ArrayBuffer | null = null;
497
- let bootResourcesInstallItem: ProtocolV2InstallItem | undefined;
498
492
  let installItems: ProtocolV2InstallItem[] | undefined;
499
493
  let resourceBundles: ProtocolV2ResourceBundleBinary[] | undefined;
500
494
  try {
@@ -505,10 +499,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
505
499
  const needsRemoteResources =
506
500
  !this.params.resourceBundleFiles?.length &&
507
501
  !!this.params.targetsToUpdate?.includes('resource');
508
- const needsRemoteBootResources =
509
- !this.params.bootResourcesBinary &&
510
- !!this.params.targetsToUpdate?.includes('boot_resources');
511
- if (needsRemoteFirmware || needsRemoteResources || needsRemoteBootResources) {
502
+ if (needsRemoteFirmware || needsRemoteResources) {
512
503
  // Remote updates must use a freshly fetched config before any reboot or file write.
513
504
  await DataManager.forceReloadData();
514
505
  }
@@ -521,25 +512,13 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
521
512
  fwBinaryMap = remoteBinaries.fwBinaryMap;
522
513
  installItems = remoteBinaries.installItems;
523
514
  }
524
- bootResourcesInstallItem = await this.prepareProtocolV2BootResources();
525
- if (bootResourcesInstallItem) {
526
- installItems = [
527
- bootResourcesInstallItem,
528
- ...(installItems ?? this.buildProtocolV2InstallItems({ bootloaderBinary, fwBinaryMap })),
529
- ];
530
- }
531
515
  resourceBundles = await this.prepareProtocolV2ResourceBundles(resourceRecoveryMode);
532
516
  this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
533
517
  } catch (err) {
534
518
  throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
535
519
  }
536
520
 
537
- if (
538
- !bootloaderBinary &&
539
- fwBinaryMap.length === 0 &&
540
- !installItems?.length &&
541
- !resourceBundles?.length
542
- ) {
521
+ if (!bootloaderBinary && fwBinaryMap.length === 0 && !resourceBundles?.length) {
543
522
  if (resourceBundles !== undefined) {
544
523
  this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
545
524
  return this.getProtocolV2VersionResult(deviceFeatures);
@@ -619,7 +598,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
619
598
  return (
620
599
  !!this.params.resourceBundleFiles?.length ||
621
600
  !!this.params.bootloaderBinary ||
622
- !!this.params.bootResourcesBinary ||
623
601
  fwBinaryMap.length > 0
624
602
  );
625
603
  }
@@ -769,54 +747,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
769
747
  };
770
748
  }
771
749
 
772
- private validateProtocolV2BootResourcesBinary(
773
- binary: ArrayBuffer,
774
- resource?: IProtocolV2BootResources
775
- ) {
776
- if (resource && !isProtocolV2ResourceFileValid(binary, resource)) {
777
- throw new Error('Pro2 boot resources file verification failed');
778
- }
779
- const header = parseProtocolV2OkppHeader(toProtocolV2Bytes(binary));
780
- if (!header || header.type !== 'CRAT') {
781
- throw new Error('Invalid Pro2 boot resources CRATE header');
782
- }
783
- if (resource) {
784
- const expectedPayloadHash = normalizeProtocolV2Hex(resource.payloadHash);
785
- const expectedHeaderHash = normalizeProtocolV2Hex(resource.headerHash);
786
- if (header.payloadHash !== expectedPayloadHash) {
787
- throw new Error('Pro2 boot resources payload hash mismatch');
788
- }
789
- if (header.headerHash !== expectedHeaderHash) {
790
- throw new Error('Pro2 boot resources header hash mismatch');
791
- }
792
- }
793
- }
794
-
795
- private async prepareProtocolV2BootResources(): Promise<ProtocolV2InstallItem | undefined> {
796
- let binary = this.params.bootResourcesBinary;
797
- let resource: IProtocolV2BootResources | undefined;
798
-
799
- if (!binary) {
800
- if (!this.params.targetsToUpdate?.includes('boot_resources')) {
801
- return undefined;
802
- }
803
- resource = DataManager.getProtocolV2BootResources();
804
- if (!resource) {
805
- throw new Error('Missing Pro2 boot resources configuration');
806
- }
807
- Log.log('[FirmwareUpdateV4] downloading Pro2 boot resources CRATE');
808
- ({ binary } = await getSysResourceBinary(resource.url));
809
- }
810
-
811
- this.validateProtocolV2BootResourcesBinary(binary, resource);
812
- return {
813
- fileName: PROTOCOL_V2_BOOT_RESOURCES_FILE_NAME,
814
- binary,
815
- targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_CRATE,
816
- kind: 'boot_resources',
817
- };
818
- }
819
-
820
750
  private async prepareProtocolV2ResourceBundles(
821
751
  recoveryMode: boolean
822
752
  ): Promise<ProtocolV2ResourceBundleBinary[] | undefined> {
package/src/core/index.ts CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  createRequestContext,
27
27
  createSdkTracingContext,
28
28
  formatRequestContext,
29
+ generateInstanceId,
29
30
  getActiveRequestsByDeviceInstance,
30
31
  updateRequestContext,
31
32
  } from '../utils/tracing';
@@ -53,6 +54,7 @@ import {
53
54
  import TransportManager from '../data-manager/TransportManager';
54
55
  import DeviceConnector from '../device/DeviceConnector';
55
56
  import RequestQueue from './RequestQueue';
57
+ import { findUiPromiseForResponse } from './uiPromiseRegistry';
56
58
  import { registerHardwareUiEventListeners } from './deviceEventRegistration';
57
59
  import { getSynchronize } from '../utils/getSynchronize';
58
60
  import { runMethodWithUnlockPolicy } from '../protocols/protocol-v2/unlockPolicyRunner';
@@ -1260,6 +1262,7 @@ const onDevicePinHandler = async (...[device, type, callback]: DeviceEvents['pin
1260
1262
  createUiMessage(UI_REQUEST.REQUEST_PIN, {
1261
1263
  device: device.toMessageObject() as unknown as KnownDevice,
1262
1264
  type,
1265
+ responseCorrelation: uiPromise.responseCorrelation,
1263
1266
  })
1264
1267
  );
1265
1268
  // wait for pin
@@ -1308,6 +1311,7 @@ const onDevicePassphraseHandler = async (
1308
1311
  reason: requestPayload.reason,
1309
1312
  expectedPassphraseState: requestPayload.expectedPassphraseState,
1310
1313
  ...(requestPayload.interaction ? { interaction: requestPayload.interaction } : {}),
1314
+ responseCorrelation: uiPromise.responseCorrelation,
1311
1315
  })
1312
1316
  );
1313
1317
  // wait for passphrase
@@ -1431,14 +1435,21 @@ const postMessage = (message: CoreMessage) => {
1431
1435
 
1432
1436
  const createUiPromise = <T extends UiPromiseResponse['type']>(promiseEvent: T, device?: Device) => {
1433
1437
  const uiPromise: UiPromise<T> = createDeferred(promiseEvent, device);
1438
+ if (
1439
+ device &&
1440
+ (promiseEvent === UI_RESPONSE.RECEIVE_PIN || promiseEvent === UI_RESPONSE.RECEIVE_PASSPHRASE)
1441
+ ) {
1442
+ const publicDeviceId = device.toMessageObject()?.deviceId;
1443
+ uiPromise.responseCorrelation = {
1444
+ interactionId: generateInstanceId('UiResponse', device.sdkInstanceId),
1445
+ deviceId: publicDeviceId || device.instanceId,
1446
+ };
1447
+ }
1434
1448
  _uiPromises.push(uiPromise as any);
1435
1449
 
1436
1450
  return uiPromise;
1437
1451
  };
1438
1452
 
1439
- const findUiPromise = <T extends UiPromiseResponse['type']>(promiseEvent: T) =>
1440
- _uiPromises.find(p => p.id === promiseEvent);
1441
-
1442
1453
  const removeUiPromise = (promise: Deferred<any>) => {
1443
1454
  _uiPromises = _uiPromises.filter(p => p !== promise);
1444
1455
  };
@@ -1490,11 +1501,16 @@ export default class Core extends EventEmitter {
1490
1501
  case UI_RESPONSE.RECEIVE_PASSPHRASE:
1491
1502
  case UI_RESPONSE.SELECT_DEVICE_IN_BOOTLOADER_FOR_WEB_DEVICE:
1492
1503
  case UI_RESPONSE.SELECT_DEVICE_FOR_SWITCH_FIRMWARE_WEB_DEVICE: {
1493
- const uiPromise = findUiPromise(message.type);
1504
+ const uiPromise = findUiPromiseForResponse(_uiPromises, message);
1494
1505
  if (uiPromise) {
1495
1506
  Log.log('receive UI Response: ', message.type);
1496
1507
  uiPromise.resolve(message);
1497
1508
  removeUiPromise(uiPromise);
1509
+ } else if (
1510
+ message.type === UI_RESPONSE.RECEIVE_PIN ||
1511
+ message.type === UI_RESPONSE.RECEIVE_PASSPHRASE
1512
+ ) {
1513
+ Log.warn('Ignored unmatched or ambiguous sensitive UI response:', message.type);
1498
1514
  }
1499
1515
  break;
1500
1516
  }
@@ -0,0 +1,41 @@
1
+ import { UI_RESPONSE } from '../events/ui-response';
2
+
3
+ import type { UiPromise, UiPromiseResponse } from '../events/ui-promise';
4
+ import type { UiResponseEvent } from '../events/ui-response';
5
+
6
+ const isSensitiveUiResponse = (
7
+ response: UiResponseEvent
8
+ ): response is Extract<
9
+ UiResponseEvent,
10
+ { type: typeof UI_RESPONSE.RECEIVE_PIN | typeof UI_RESPONSE.RECEIVE_PASSPHRASE }
11
+ > => response.type === UI_RESPONSE.RECEIVE_PIN || response.type === UI_RESPONSE.RECEIVE_PASSPHRASE;
12
+
13
+ export const findUiPromiseForResponse = (
14
+ uiPromises: UiPromise<UiPromiseResponse['type']>[],
15
+ response: UiResponseEvent
16
+ ) => {
17
+ const candidates = uiPromises.filter(promise => promise.id === response.type);
18
+ if (!isSensitiveUiResponse(response)) {
19
+ return candidates[0];
20
+ }
21
+
22
+ const hasInteractionId = typeof response.interactionId === 'string';
23
+ const hasDeviceId = typeof response.deviceId === 'string';
24
+ if (hasInteractionId !== hasDeviceId) {
25
+ return undefined;
26
+ }
27
+
28
+ if (hasInteractionId && hasDeviceId) {
29
+ return candidates.find(promise => {
30
+ const correlation = promise.responseCorrelation;
31
+ return (
32
+ correlation?.interactionId === response.interactionId &&
33
+ correlation?.deviceId === response.deviceId
34
+ );
35
+ });
36
+ }
37
+
38
+ // Legacy clients do not return correlation metadata. Preserve their single-request
39
+ // behavior, but never guess when multiple sensitive requests are pending.
40
+ return candidates.length === 1 ? candidates[0] : undefined;
41
+ };
@@ -509,10 +509,6 @@ export default class DataManager {
509
509
  return this.deviceMap[EDeviceType.Pro2]?.resources?.stable;
510
510
  }
511
511
 
512
- static getProtocolV2BootResources() {
513
- return this.deviceMap[EDeviceType.Pro2]?.resources?.boot;
514
- }
515
-
516
512
  static getProtobufMessages(schema: ProtobufMessageSchema = 'v1CurrentSchema'): JSON {
517
513
  return this.messages[schema];
518
514
  }
@@ -1,7 +1,7 @@
1
1
  import type { Deferred } from '@onekeyfe/hd-shared';
2
2
  import type { DEVICE } from './device';
3
3
  import type { Device } from '../device/Device';
4
- import type { UiResponseEvent } from './ui-response';
4
+ import type { UiResponseCorrelation, UiResponseEvent } from './ui-response';
5
5
 
6
6
  export type UiPromiseResponse =
7
7
  | UiResponseEvent
@@ -11,4 +11,6 @@ export type UiPromise<T extends UiPromiseResponse['type']> = Deferred<
11
11
  Extract<UiPromiseResponse, { type: T }>,
12
12
  T,
13
13
  Device
14
- >;
14
+ > & {
15
+ responseCorrelation?: UiResponseCorrelation;
16
+ };
@@ -1,6 +1,7 @@
1
1
  import type { PROTO } from '../constants';
2
2
  import type { Device } from '../types';
3
3
  import type { DeviceButtonRequest } from './device';
4
+ import type { UiResponseCorrelation } from './ui-response';
4
5
  import type { MessageFactoryFn } from './utils';
5
6
 
6
7
  export const UI_EVENT = 'UI_EVENT';
@@ -121,6 +122,7 @@ export type UiRequestDeviceAction = {
121
122
  payload: ProtocolV2UiEventMetadata & {
122
123
  device: Device;
123
124
  type?: PROTO.PinMatrixRequestType | 'ButtonRequest_PinEntry' | 'ButtonRequest_AttachPin';
125
+ responseCorrelation?: UiResponseCorrelation;
124
126
  };
125
127
  };
126
128
 
@@ -140,6 +142,7 @@ export interface UiRequestPassphrase {
140
142
  reason?: 'open-wallet' | 'session-recovery';
141
143
  expectedPassphraseState?: string;
142
144
  interaction?: HardwareUiInteractionMeta;
145
+ responseCorrelation?: UiResponseCorrelation;
143
146
  };
144
147
  }
145
148
 
@@ -11,12 +11,22 @@ export const UI_RESPONSE = {
11
11
  'ui-receive_select-device-for-switch-firmware-web-device',
12
12
  } as const;
13
13
 
14
- export interface UiResponsePin {
14
+ export interface UiResponseCorrelation {
15
+ interactionId: string;
16
+ deviceId: string;
17
+ }
18
+
19
+ export interface UiResponseCorrelationFields {
20
+ interactionId?: string;
21
+ deviceId?: string;
22
+ }
23
+
24
+ export interface UiResponsePin extends UiResponseCorrelationFields {
15
25
  type: typeof UI_RESPONSE.RECEIVE_PIN;
16
26
  payload: string;
17
27
  }
18
28
 
19
- export interface UiResponsePassphrase {
29
+ export interface UiResponsePassphrase extends UiResponseCorrelationFields {
20
30
  type: typeof UI_RESPONSE.RECEIVE_PASSPHRASE;
21
31
  payload: {
22
32
  value: string;
@@ -1,7 +1,6 @@
1
1
  import { sha256 } from '@noble/hashes/sha256';
2
2
 
3
3
  import type {
4
- IProtocolV2BootResources,
5
4
  IProtocolV2Resource,
6
5
  IProtocolV2ResourceType,
7
6
  IProtocolV2Resources,
@@ -150,34 +149,6 @@ function validateResource(value: unknown, index: number): IProtocolV2Resource {
150
149
  };
151
150
  }
152
151
 
153
- function validateBootResources(value: unknown): IProtocolV2BootResources {
154
- if (!value || typeof value !== 'object') {
155
- throw new Error('Invalid Pro2 boot resources config');
156
- }
157
- const resource = value as Partial<IProtocolV2BootResources>;
158
- if (resource.required !== false) {
159
- throw new Error('Invalid Pro2 boot resources required flag: expected false');
160
- }
161
- if (resource.target !== 'CRATE') {
162
- throw new Error('Invalid Pro2 boot resources target: expected CRATE');
163
- }
164
- if (typeof resource.url !== 'string' || !resource.url.startsWith('https://')) {
165
- throw new Error('Invalid Pro2 boot resources url');
166
- }
167
- if (!Number.isSafeInteger(resource.size) || Number(resource.size) <= 0) {
168
- throw new Error('Invalid Pro2 boot resources size');
169
- }
170
- return {
171
- required: false,
172
- target: 'CRATE',
173
- url: resource.url,
174
- size: Number(resource.size),
175
- fileHash: normalizeHex(resource.fileHash, SHA256_HEX_LENGTH, 'boot fileHash'),
176
- payloadHash: normalizeHex(resource.payloadHash, SHA3_512_HEX_LENGTH, 'boot payloadHash'),
177
- headerHash: normalizeHex(resource.headerHash, SHA3_512_HEX_LENGTH, 'boot headerHash'),
178
- };
179
- }
180
-
181
152
  /** Validate a complete Pro2 stable resource set from remote configuration. */
182
153
  export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources | undefined {
183
154
  if (value === undefined) return undefined;
@@ -189,8 +160,7 @@ export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources |
189
160
  throw new Error('Invalid Pro2 resources config: stable must be an array');
190
161
  }
191
162
 
192
- const config = value as { stable: unknown[]; boot?: unknown };
193
- const stable = config.stable.map(validateResource);
163
+ const stable = (value as { stable: unknown[] }).stable.map(validateResource);
194
164
  const types = new Set(stable.map(resource => resource.type));
195
165
  if (stable.length !== PROTOCOL_V2_RESOURCE_TYPES.length || types.size !== stable.length) {
196
166
  throw new Error('Invalid Pro2 resources config: stable must contain six unique resource types');
@@ -200,7 +170,6 @@ export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources |
200
170
  throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
201
171
  }
202
172
  }
203
- const boot = config.boot === undefined ? undefined : validateBootResources(config.boot);
204
173
  return {
205
174
  stable: PROTOCOL_V2_RESOURCE_TYPES.map(type => {
206
175
  const resource = stable.find(item => item.type === type);
@@ -209,7 +178,6 @@ export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources |
209
178
  }
210
179
  return resource;
211
180
  }),
212
- ...(boot ? { boot } : undefined),
213
181
  };
214
182
  }
215
183
 
@@ -164,9 +164,13 @@ const selectDeviceSession = async (
164
164
  return getDeviceSession(device, buildDeviceSessionGetRequest({ deriveCardano }));
165
165
  }
166
166
 
167
+ const passphraseOnDeviceInteraction = device.createProtocolV2UiPhaseMetadata?.(
168
+ 'passphrase-on-device',
169
+ 'start'
170
+ );
167
171
  device.emit(DEVICE.PASSPHRASE_ON_DEVICE, device, {
168
172
  ...metadata,
169
- ...(passphraseInteraction ? { interaction: passphraseInteraction } : {}),
173
+ ...(passphraseOnDeviceInteraction ? { interaction: passphraseOnDeviceInteraction } : {}),
170
174
  });
171
175
  await askDevicePassphrase(device, { on_device: true });
172
176
  return getDeviceSession(device, buildDeviceSessionGetRequest({ deriveCardano }));
@@ -63,7 +63,6 @@ export interface FirmwareUpdateV3Params {
63
63
  */
64
64
  export type FirmwareUpdateV4Target =
65
65
  | 'boot'
66
- | 'boot_resources'
67
66
  | 'app_v1'
68
67
  | 'app_v2'
69
68
  | 'coprocessor'
@@ -83,8 +82,6 @@ export interface FirmwareUpdateV4Params {
83
82
  romloaderBinary?: ArrayBuffer;
84
83
  /** FW_MGMT_TARGET_BOOTLOADER = 3 */
85
84
  bootloaderBinary?: ArrayBuffer;
86
- /** FW_MGMT_TARGET_CRATE = 1; optional Pro2 startup resources. */
87
- bootResourcesBinary?: ArrayBuffer;
88
85
  /** FW_MGMT_TARGET_APPLICATION_P1 = 4 */
89
86
  applicationP1Binary?: ArrayBuffer;
90
87
  /** FW_MGMT_TARGET_APPLICATION_P2 = 5 */
@@ -81,24 +81,8 @@ export type IProtocolV2Resource = {
81
81
  headerHash: string;
82
82
  };
83
83
 
84
- /** Optional Pro2 startup-resource CRATE installed by the bootloader updater. */
85
- export type IProtocolV2BootResources = {
86
- required: false;
87
- target: 'CRATE';
88
- url: string;
89
- /** Complete CRATE file size in bytes. */
90
- size: number;
91
- /** SHA-256 of the complete CRATE file. */
92
- fileHash: string;
93
- /** SHA3-512 payload_hash from the signed CRATE header. */
94
- payloadHash: string;
95
- /** SHA3-512 header_hash from the signed CRATE header. */
96
- headerHash: string;
97
- };
98
-
99
84
  export type IProtocolV2Resources = {
100
85
  stable: IProtocolV2Resource[];
101
- boot?: IProtocolV2BootResources;
102
86
  };
103
87
 
104
88
  /** Pro2 RESC bundle okpkg descriptor for incremental FileWrite synchronization. */
@@ -9,8 +9,8 @@ export const PRO2_NFT_IMAGE_HEIGHT = 540;
9
9
  export const PRO2_NFT_THUMBNAIL_WIDTH = 263;
10
10
  export const PRO2_NFT_THUMBNAIL_HEIGHT = 263;
11
11
  export const PRO2_NFT_DIRECTORY = 'vol1:/nft';
12
- export const PRO2_NFT_DEFAULT_CHUNK_SIZE = 512;
13
- export const PRO2_NFT_DEFAULT_PACE_MS = 20;
12
+ export const PRO2_NFT_DEFAULT_CHUNK_SIZE = 2048;
13
+ export const PRO2_NFT_DEFAULT_PACE_MS = 0;
14
14
  export const PRO2_NFT_DEFAULT_TIMEOUT_MS = 15_000;
15
15
  export const PRO2_NFT_MIN_CHUNK_SIZE = 64;
16
16
  export const PRO2_NFT_MAX_CHUNK_SIZE = 2048;