@onekeyfe/hd-core 1.2.0-alpha.16 → 1.2.0-alpha.18

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 (125) hide show
  1. package/__tests__/DeviceEventRegistration.test.ts +3 -0
  2. package/__tests__/deviceFeaturesUtils.test.ts +27 -0
  3. package/__tests__/evmLedgerLegacySafety.test.ts +3 -0
  4. package/__tests__/homescreen.test.ts +24 -0
  5. package/__tests__/kaspaSignTransaction.test.ts +591 -0
  6. package/__tests__/protocol-v2.test.ts +1006 -204
  7. package/__tests__/protocolV2FileWrite.test.ts +28 -3
  8. package/__tests__/protocolV2UiInteraction.test.ts +266 -0
  9. package/dist/api/BaseMethod.d.ts +4 -0
  10. package/dist/api/BaseMethod.d.ts.map +1 -1
  11. package/dist/api/FirmwareUpdateV4.d.ts +3 -3
  12. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  13. package/dist/api/GetDeviceInfo.d.ts.map +1 -1
  14. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  15. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  16. package/dist/api/device/DeviceCancel.d.ts.map +1 -1
  17. package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
  18. package/dist/api/device/DeviceLock.d.ts.map +1 -1
  19. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  20. package/dist/api/device/DeviceWipe.d.ts.map +1 -1
  21. package/dist/api/helpers/protocolV2FileWrite.d.ts +1 -0
  22. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  23. package/dist/api/index.d.ts +1 -1
  24. package/dist/api/index.d.ts.map +1 -1
  25. package/dist/api/kaspa/KaspaSignTransaction.d.ts +5 -1
  26. package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
  27. package/dist/api/kaspa/helpers/TransferSerialize.d.ts.map +1 -1
  28. package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
  29. package/dist/api/protocol-v2/DeviceSessionOpen.d.ts +28 -0
  30. package/dist/api/protocol-v2/DeviceSessionOpen.d.ts.map +1 -0
  31. package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts.map +1 -1
  32. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +1 -1
  33. package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -1
  34. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
  35. package/dist/core/deviceEventRegistration.d.ts +1 -0
  36. package/dist/core/deviceEventRegistration.d.ts.map +1 -1
  37. package/dist/core/index.d.ts.map +1 -1
  38. package/dist/device/Device.d.ts +3 -3
  39. package/dist/device/Device.d.ts.map +1 -1
  40. package/dist/device/DeviceCommands.d.ts +7 -5
  41. package/dist/device/DeviceCommands.d.ts.map +1 -1
  42. package/dist/device/DevicePool.d.ts +2 -2
  43. package/dist/device/DevicePool.d.ts.map +1 -1
  44. package/dist/deviceProfile/buildDeviceFeatures.d.ts +6 -2
  45. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  46. package/dist/deviceProfile/buildDeviceProfile.d.ts +3 -2
  47. package/dist/deviceProfile/buildDeviceProfile.d.ts.map +1 -1
  48. package/dist/events/device.d.ts +4 -0
  49. package/dist/events/device.d.ts.map +1 -1
  50. package/dist/events/ui-request.d.ts +18 -2
  51. package/dist/events/ui-request.d.ts.map +1 -1
  52. package/dist/index.d.ts +85 -15
  53. package/dist/index.js +1515 -554
  54. package/dist/protocols/protocol-v2/features.d.ts +6 -18
  55. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  56. package/dist/protocols/protocol-v2/index.d.ts +1 -1
  57. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  58. package/dist/protocols/protocol-v2/uiInteraction.d.ts +39 -0
  59. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -0
  60. package/dist/protocols/protocol-v2/unlockRetry.d.ts +5 -3
  61. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
  62. package/dist/protocols/protocol-v2/walletSession.d.ts +2 -1
  63. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  64. package/dist/types/api/getDeviceInfo.d.ts +2 -1
  65. package/dist/types/api/getDeviceInfo.d.ts.map +1 -1
  66. package/dist/types/api/index.d.ts +2 -2
  67. package/dist/types/api/index.d.ts.map +1 -1
  68. package/dist/types/api/kaspaSignTransaction.d.ts +27 -2
  69. package/dist/types/api/kaspaSignTransaction.d.ts.map +1 -1
  70. package/dist/types/api/protocolV2.d.ts +4 -2
  71. package/dist/types/api/protocolV2.d.ts.map +1 -1
  72. package/dist/types/device.d.ts +2 -1
  73. package/dist/types/device.d.ts.map +1 -1
  74. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  75. package/dist/utils/homescreen.d.ts.map +1 -1
  76. package/dist/utils/patch.d.ts +1 -1
  77. package/dist/utils/patch.d.ts.map +1 -1
  78. package/package.json +4 -4
  79. package/src/api/BaseMethod.ts +10 -2
  80. package/src/api/FirmwareUpdateV4.ts +27 -73
  81. package/src/api/GetDeviceInfo.ts +9 -2
  82. package/src/api/GetPassphraseState.ts +5 -2
  83. package/src/api/UploadPortfolio.ts +2 -0
  84. package/src/api/device/DeviceCancel.ts +1 -0
  85. package/src/api/device/DeviceChangePin.ts +23 -0
  86. package/src/api/device/DeviceLock.ts +1 -0
  87. package/src/api/device/DeviceUnlock.ts +9 -0
  88. package/src/api/device/DeviceWipe.ts +18 -0
  89. package/src/api/helpers/protocolV2FileWrite.ts +35 -11
  90. package/src/api/index.ts +1 -1
  91. package/src/api/kaspa/KaspaSignTransaction.ts +341 -22
  92. package/src/api/kaspa/helpers/TransferSerialize.ts +14 -0
  93. package/src/api/protocol-v2/DeviceInfoGet.ts +0 -1
  94. package/src/api/protocol-v2/DeviceSessionOpen.ts +100 -0
  95. package/src/api/protocol-v2/DeviceSettingsPageShow.ts +9 -1
  96. package/src/api/protocol-v2/DeviceSettingsSet.ts +11 -1
  97. package/src/api/protocol-v2/DeviceStatusGet.ts +1 -0
  98. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +2 -1
  99. package/src/core/deviceEventRegistration.ts +2 -0
  100. package/src/core/index.ts +36 -3
  101. package/src/data/messages/messages-protocol-v2.json +122 -23
  102. package/src/data/messages/messages.json +286 -1
  103. package/src/device/Device.ts +33 -41
  104. package/src/device/DeviceCommands.ts +22 -4
  105. package/src/device/DevicePool.ts +14 -6
  106. package/src/deviceProfile/buildDeviceFeatures.ts +21 -9
  107. package/src/deviceProfile/buildDeviceProfile.ts +27 -11
  108. package/src/events/device.ts +4 -0
  109. package/src/events/ui-request.ts +24 -2
  110. package/src/inject.ts +2 -2
  111. package/src/protocols/protocol-v2/features.ts +27 -20
  112. package/src/protocols/protocol-v2/index.ts +0 -1
  113. package/src/protocols/protocol-v2/uiInteraction.ts +209 -0
  114. package/src/protocols/protocol-v2/unlockRetry.ts +43 -3
  115. package/src/protocols/protocol-v2/walletSession.ts +122 -31
  116. package/src/types/api/getDeviceInfo.ts +6 -1
  117. package/src/types/api/index.ts +2 -2
  118. package/src/types/api/kaspaSignTransaction.ts +40 -2
  119. package/src/types/api/protocolV2.ts +5 -3
  120. package/src/types/device.ts +6 -1
  121. package/src/utils/deviceFeaturesUtils.ts +13 -0
  122. package/src/utils/homescreen.ts +10 -0
  123. package/dist/api/protocol-v2/DeviceSessionGet.d.ts +0 -6
  124. package/dist/api/protocol-v2/DeviceSessionGet.d.ts.map +0 -1
  125. package/src/api/protocol-v2/DeviceSessionGet.ts +0 -15
@@ -6082,7 +6082,6 @@
6082
6082
  }
6083
6083
  },
6084
6084
  "raw_message": {
6085
- "rule": "required",
6086
6085
  "type": "bytes",
6087
6086
  "id": 2
6088
6087
  },
@@ -6113,6 +6112,42 @@
6113
6112
  "options": {
6114
6113
  "default": true
6115
6114
  }
6115
+ },
6116
+ "output_count": {
6117
+ "type": "uint32",
6118
+ "id": 7
6119
+ },
6120
+ "version": {
6121
+ "type": "uint32",
6122
+ "id": 8,
6123
+ "options": {
6124
+ "default": 0
6125
+ }
6126
+ },
6127
+ "lock_time": {
6128
+ "type": "uint64",
6129
+ "id": 9,
6130
+ "options": {
6131
+ "default": 0
6132
+ }
6133
+ },
6134
+ "subnetwork_id": {
6135
+ "type": "bytes",
6136
+ "id": 10
6137
+ },
6138
+ "gas": {
6139
+ "type": "uint64",
6140
+ "id": 11,
6141
+ "options": {
6142
+ "default": 0
6143
+ }
6144
+ },
6145
+ "payload_length": {
6146
+ "type": "uint32",
6147
+ "id": 12,
6148
+ "options": {
6149
+ "default": 0
6150
+ }
6116
6151
  }
6117
6152
  }
6118
6153
  },
@@ -6146,6 +6181,249 @@
6146
6181
  }
6147
6182
  }
6148
6183
  },
6184
+ "KaspaOutpoint": {
6185
+ "fields": {
6186
+ "tx_id": {
6187
+ "rule": "required",
6188
+ "type": "bytes",
6189
+ "id": 1
6190
+ },
6191
+ "index": {
6192
+ "rule": "required",
6193
+ "type": "uint32",
6194
+ "id": 2
6195
+ }
6196
+ }
6197
+ },
6198
+ "KaspaInputScriptType": {
6199
+ "values": {
6200
+ "KASPA_SPEND_P2PK_SCHNORR": 0,
6201
+ "KASPA_SPEND_P2PK_ECDSA": 1
6202
+ }
6203
+ },
6204
+ "KaspaOutputScriptType": {
6205
+ "values": {
6206
+ "KASPA_PAYTOADDRESS": 0,
6207
+ "KASPA_PAYTOCHANGE": 1
6208
+ }
6209
+ },
6210
+ "KaspaRequestType": {
6211
+ "values": {
6212
+ "KASPA_TX_INPUT": 0,
6213
+ "KASPA_TX_OUTPUT": 1,
6214
+ "KASPA_TX_PAYLOAD": 2,
6215
+ "KASPA_TX_FINISHED": 3,
6216
+ "KASPA_TX_PREV_META": 4
6217
+ }
6218
+ },
6219
+ "KaspaTxRequestSignature": {
6220
+ "fields": {
6221
+ "signature_index": {
6222
+ "rule": "required",
6223
+ "type": "uint32",
6224
+ "id": 1
6225
+ },
6226
+ "signature": {
6227
+ "rule": "required",
6228
+ "type": "bytes",
6229
+ "id": 2
6230
+ }
6231
+ }
6232
+ },
6233
+ "KaspaTxRequest": {
6234
+ "fields": {
6235
+ "request_type": {
6236
+ "rule": "required",
6237
+ "type": "KaspaRequestType",
6238
+ "id": 1
6239
+ },
6240
+ "request_index": {
6241
+ "type": "uint32",
6242
+ "id": 2
6243
+ },
6244
+ "signature": {
6245
+ "type": "KaspaTxRequestSignature",
6246
+ "id": 3
6247
+ },
6248
+ "request_payload_length": {
6249
+ "type": "uint32",
6250
+ "id": 4
6251
+ },
6252
+ "prev_tx_id": {
6253
+ "type": "bytes",
6254
+ "id": 5
6255
+ }
6256
+ }
6257
+ },
6258
+ "KaspaTxAckInput": {
6259
+ "fields": {
6260
+ "address_n": {
6261
+ "rule": "repeated",
6262
+ "type": "uint32",
6263
+ "id": 1,
6264
+ "options": {
6265
+ "packed": false
6266
+ }
6267
+ },
6268
+ "previous_outpoint": {
6269
+ "rule": "required",
6270
+ "type": "KaspaOutpoint",
6271
+ "id": 2
6272
+ },
6273
+ "amount": {
6274
+ "rule": "required",
6275
+ "type": "uint64",
6276
+ "id": 3
6277
+ },
6278
+ "sequence": {
6279
+ "rule": "required",
6280
+ "type": "uint64",
6281
+ "id": 4
6282
+ },
6283
+ "sig_op_count": {
6284
+ "rule": "required",
6285
+ "type": "uint32",
6286
+ "id": 5
6287
+ },
6288
+ "script_type": {
6289
+ "type": "KaspaInputScriptType",
6290
+ "id": 6,
6291
+ "options": {
6292
+ "default": "KASPA_SPEND_P2PK_SCHNORR"
6293
+ }
6294
+ },
6295
+ "use_tweak": {
6296
+ "type": "bool",
6297
+ "id": 9,
6298
+ "options": {
6299
+ "default": true
6300
+ }
6301
+ }
6302
+ }
6303
+ },
6304
+ "KaspaTxAckOutput": {
6305
+ "fields": {
6306
+ "script_type": {
6307
+ "type": "KaspaOutputScriptType",
6308
+ "id": 1,
6309
+ "options": {
6310
+ "default": "KASPA_PAYTOADDRESS"
6311
+ }
6312
+ },
6313
+ "amount": {
6314
+ "rule": "required",
6315
+ "type": "uint64",
6316
+ "id": 2
6317
+ },
6318
+ "address_n": {
6319
+ "rule": "repeated",
6320
+ "type": "uint32",
6321
+ "id": 3,
6322
+ "options": {
6323
+ "packed": false
6324
+ }
6325
+ },
6326
+ "address": {
6327
+ "type": "string",
6328
+ "id": 4
6329
+ },
6330
+ "scheme": {
6331
+ "type": "string",
6332
+ "id": 7,
6333
+ "options": {
6334
+ "default": "schnorr"
6335
+ }
6336
+ },
6337
+ "use_tweak": {
6338
+ "type": "bool",
6339
+ "id": 8,
6340
+ "options": {
6341
+ "default": true
6342
+ }
6343
+ }
6344
+ }
6345
+ },
6346
+ "KaspaTxAckPayloadChunk": {
6347
+ "fields": {
6348
+ "payload_chunk": {
6349
+ "rule": "required",
6350
+ "type": "bytes",
6351
+ "id": 1
6352
+ }
6353
+ }
6354
+ },
6355
+ "KaspaTxAckPrevMeta": {
6356
+ "fields": {
6357
+ "version": {
6358
+ "rule": "required",
6359
+ "type": "uint32",
6360
+ "id": 1
6361
+ },
6362
+ "input_count": {
6363
+ "rule": "required",
6364
+ "type": "uint32",
6365
+ "id": 2
6366
+ },
6367
+ "output_count": {
6368
+ "rule": "required",
6369
+ "type": "uint32",
6370
+ "id": 3
6371
+ },
6372
+ "lock_time": {
6373
+ "rule": "required",
6374
+ "type": "uint64",
6375
+ "id": 4
6376
+ },
6377
+ "subnetwork_id": {
6378
+ "rule": "required",
6379
+ "type": "bytes",
6380
+ "id": 5
6381
+ },
6382
+ "gas": {
6383
+ "rule": "required",
6384
+ "type": "uint64",
6385
+ "id": 6
6386
+ },
6387
+ "payload_length": {
6388
+ "rule": "required",
6389
+ "type": "uint32",
6390
+ "id": 7
6391
+ }
6392
+ }
6393
+ },
6394
+ "KaspaTxAckPrevInput": {
6395
+ "fields": {
6396
+ "previous_outpoint": {
6397
+ "rule": "required",
6398
+ "type": "KaspaOutpoint",
6399
+ "id": 1
6400
+ },
6401
+ "sequence": {
6402
+ "rule": "required",
6403
+ "type": "uint64",
6404
+ "id": 2
6405
+ }
6406
+ }
6407
+ },
6408
+ "KaspaTxAckPrevOutput": {
6409
+ "fields": {
6410
+ "amount": {
6411
+ "rule": "required",
6412
+ "type": "uint64",
6413
+ "id": 1
6414
+ },
6415
+ "script_version": {
6416
+ "rule": "required",
6417
+ "type": "uint32",
6418
+ "id": 2
6419
+ },
6420
+ "script_public_key": {
6421
+ "rule": "required",
6422
+ "type": "bytes",
6423
+ "id": 3
6424
+ }
6425
+ }
6426
+ },
6149
6427
  "KaspaSignedTx": {
6150
6428
  "fields": {
6151
6429
  "signature": {
@@ -12559,6 +12837,13 @@
12559
12837
  "MessageType_KaspaSignedTx": 11303,
12560
12838
  "MessageType_KaspaTxInputRequest": 11304,
12561
12839
  "MessageType_KaspaTxInputAck": 11305,
12840
+ "MessageType_KaspaTxRequest": 11306,
12841
+ "MessageType_KaspaTxAckInput": 11307,
12842
+ "MessageType_KaspaTxAckOutput": 11308,
12843
+ "MessageType_KaspaTxAckPayloadChunk": 11309,
12844
+ "MessageType_KaspaTxAckPrevMeta": 11310,
12845
+ "MessageType_KaspaTxAckPrevInput": 11311,
12846
+ "MessageType_KaspaTxAckPrevOutput": 11312,
12562
12847
  "MessageType_NexaGetAddress": 11400,
12563
12848
  "MessageType_NexaAddress": 11401,
12564
12849
  "MessageType_NexaSignTx": 11402,
@@ -52,8 +52,8 @@ import TransportManager from '../data-manager/TransportManager';
52
52
  import { toHardened } from '../api/helpers/pathUtils';
53
53
  import { existCapability } from '../utils/capabilitieUtils';
54
54
  import {
55
- PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
56
55
  requestProtocolV2DeviceInfo,
56
+ requestProtocolV2DeviceStatus,
57
57
  } from '../protocols/protocol-v2/features';
58
58
  import { buildProtocolV1FeaturesPayload, buildProtocolV2FeaturesPayload } from '../deviceProfile';
59
59
 
@@ -96,7 +96,8 @@ const Log = getLogger(LoggerNames.Device);
96
96
 
97
97
  export interface DeviceEvents {
98
98
  [DEVICE.PIN]: [Device, PROTO.PinMatrixRequestType | undefined, (err: any, pin: string) => void];
99
- [DEVICE.PASSPHRASE_ON_DEVICE]: [Device, ((response: any) => void)?];
99
+ [DEVICE.PASSPHRASE_ON_DEVICE]: [Device, PassphraseRequestPayload?];
100
+ [DEVICE.ATTACH_PIN_ON_DEVICE]: [Device, PassphraseRequestPayload?];
100
101
  [DEVICE.BUTTON]: [Device, DeviceButtonRequestPayload];
101
102
  [DEVICE.FEATURES]: [Device, DeviceFeaturesPayload];
102
103
  [DEVICE.PASSPHRASE]: [
@@ -728,10 +729,8 @@ export class Device extends EventEmitter {
728
729
  if (this.features.bootloaderMode) {
729
730
  return;
730
731
  }
731
- // 不能直接信任缓存 features:设备端 wipe / 完成初始化 / 改 label 后
732
- // features 会永久陈旧。每次 run 做一次轻量 status 刷新(不含 fw/SE),
733
- // 用字段级合并保留已有版本和 SE 信息。
734
- await this._refreshProtocolV2Status(options);
732
+ // 普通业务调用复用缓存。动态状态由显式 getFeatures/deviceStatusGet、
733
+ // 解锁流程和设备事件刷新,避免每个 SDK 方法都额外轮询 DeviceStatus。
735
734
  return;
736
735
  }
737
736
  await this._initializeProtocolV2(options);
@@ -798,9 +797,15 @@ export class Device extends EventEmitter {
798
797
  commands: this.commands,
799
798
  timeoutMs: options?.protocolV2DeviceInfoTimeoutMs,
800
799
  });
800
+ const deviceStatus = await requestProtocolV2DeviceStatus({
801
+ commands: this.commands,
802
+ }).catch(error => {
803
+ Log.debug('Protocol V2 status unavailable during initialization:', error);
804
+ return undefined;
805
+ });
801
806
  // 默认请求不含 SE/hash 数据,scope 如实标注为 basic;
802
807
  // 完整数据由 getDeviceInfo(scope:'verify'|'full') 获取。
803
- const features = this.updateProtocolV2Features(deviceInfo);
808
+ const features = this.updateProtocolV2Features(deviceInfo, deviceStatus);
804
809
  Log.debug('Protocol V2 features:', features);
805
810
  this.featuresNeedsReload = false;
806
811
  } catch (error) {
@@ -809,34 +814,15 @@ export class Device extends EventEmitter {
809
814
  }
810
815
  }
811
816
 
812
- /**
813
- * Protocol V2 的轻量状态刷新(每次 run 前调用)。
814
- *
815
- * 请求 hw + coprocessor + status(不含 fw/SE target):status 提供 init_states / label /
816
- * passphrase_enabled 等会在设备端变化的字段;hw/coprocessor 提供 serialNo / bleName。
817
- * versions 为空时按字段级合并保留旧值,verify 数据不会被降级。
818
- */
819
- private async _refreshProtocolV2Status(options?: InitOptions) {
820
- try {
821
- const deviceInfo = await requestProtocolV2DeviceInfo({
822
- commands: this.commands,
823
- request: PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
824
- timeoutMs: options?.protocolV2DeviceInfoTimeoutMs,
825
- });
826
- const features = this.updateProtocolV2Features(deviceInfo);
827
- Log.debug('Protocol V2 features (status refresh):', features);
828
- } catch (error) {
829
- Log.error('Protocol V2 status refresh failed:', error);
830
- throw error;
831
- }
832
- }
833
-
834
817
  async getFeatures() {
835
818
  if (this.isProtocolV2()) {
836
819
  const deviceInfo = await requestProtocolV2DeviceInfo({
837
820
  commands: this.commands,
838
821
  });
839
- return this.updateProtocolV2Features(deviceInfo);
822
+ const deviceStatus = await requestProtocolV2DeviceStatus({
823
+ commands: this.commands,
824
+ }).catch(() => undefined);
825
+ return this.updateProtocolV2Features(deviceInfo, deviceStatus);
840
826
  }
841
827
 
842
828
  const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
@@ -871,10 +857,18 @@ export class Device extends EventEmitter {
871
857
  this.emit(DEVICE.FEATURES, this, feat);
872
858
  }
873
859
 
874
- updateProtocolV2Features(deviceInfo?: ProtocolV2DeviceInfo) {
860
+ updateProtocolV2Features(deviceInfo?: ProtocolV2DeviceInfo, deviceStatus?: DeviceStatus | null) {
875
861
  const previousCacheDeviceKey = this.getSessionCacheDeviceKey();
862
+ const resolvedDeviceStatus =
863
+ deviceStatus === undefined
864
+ ? this.features?.raw?.protocolV2DeviceStatus
865
+ : deviceStatus ?? undefined;
876
866
  const features = fixFeaturesFirmwareVersion(
877
- buildProtocolV2FeaturesPayload(deviceInfo, this.features)
867
+ buildProtocolV2FeaturesPayload({
868
+ deviceInfo,
869
+ deviceStatus: resolvedDeviceStatus,
870
+ previous: this.features,
871
+ })
878
872
  );
879
873
  this.features = features;
880
874
  const nextCacheDeviceKey = this.getSessionCacheDeviceKey();
@@ -888,14 +882,10 @@ export class Device extends EventEmitter {
888
882
 
889
883
  updateProtocolV2Status(status: DeviceStatus) {
890
884
  const previousDeviceInfo = this.features?.raw?.protocolV2DeviceInfo;
891
- const previousStatus = previousDeviceInfo?.status;
892
- return this.updateProtocolV2Features({
893
- ...(previousDeviceInfo ?? {}),
894
- protocol_version: previousDeviceInfo?.protocol_version ?? this.features?.protocolVersion ?? 2,
895
- status: {
896
- ...previousStatus,
897
- ...status,
898
- },
885
+ const previousStatus = this.features?.raw?.protocolV2DeviceStatus;
886
+ return this.updateProtocolV2Features(previousDeviceInfo, {
887
+ ...previousStatus,
888
+ ...status,
899
889
  });
900
890
  }
901
891
 
@@ -1190,7 +1180,9 @@ export class Device extends EventEmitter {
1190
1180
  async unlockDevice() {
1191
1181
  if (this.isProtocolV2()) {
1192
1182
  try {
1193
- await this.commands.typedCall('DeviceSessionAskPin', 'Success');
1183
+ await this.commands.typedCall('DeviceSessionAskPin', 'Success', undefined, {
1184
+ timeoutMs: 120_000,
1185
+ });
1194
1186
  } catch (error) {
1195
1187
  const errorText =
1196
1188
  error instanceof Error
@@ -663,7 +663,7 @@ export class DeviceCommands {
663
663
 
664
664
  if (res.type === 'PassphraseRequest') {
665
665
  const existsAttachPinUser = res.message.exists_attach_pin_user;
666
- return this._promptPassphrase({
666
+ return this.promptPassphrase({
667
667
  existsAttachPinUser,
668
668
  }).then(response => {
669
669
  const { passphrase, passphraseOnDevice, attachPinOnDevice } = response;
@@ -761,10 +761,27 @@ export class DeviceCommands {
761
761
  });
762
762
  }
763
763
 
764
- _promptPassphrase(options: PassphraseRequestPayload) {
764
+ promptPassphrase(
765
+ options: PassphraseRequestPayload,
766
+ promptOptions: { cancelDeviceOnReject?: boolean } = {}
767
+ ) {
765
768
  return new Promise<PassphrasePromptResponse>((resolve, reject) => {
766
- const cancelAndReject = (_error?: Error) =>
767
- cancelDeviceInPrompt(this.device, false)
769
+ const cancelAndReject = (_error?: Error) => {
770
+ const rejectWithCancelledError = () => {
771
+ reject(
772
+ ERRORS.TypedError(
773
+ HardwareErrorCode.CallQueueActionCancelled,
774
+ `${DEVICE.PASSPHRASE} canceled`
775
+ )
776
+ );
777
+ };
778
+
779
+ if (promptOptions.cancelDeviceOnReject === false) {
780
+ rejectWithCancelledError();
781
+ return Promise.resolve();
782
+ }
783
+
784
+ return cancelDeviceInPrompt(this.device, false)
768
785
  .then(onCancel => {
769
786
  const error = ERRORS.TypedError(
770
787
  HardwareErrorCode.CallQueueActionCancelled,
@@ -781,6 +798,7 @@ export class DeviceCommands {
781
798
  .catch(error => {
782
799
  reject(error);
783
800
  });
801
+ };
784
802
 
785
803
  if (this.device.listenerCount(DEVICE.PASSPHRASE) > 0) {
786
804
  this.device.setCancelableAction(cancelAndReject);
@@ -108,14 +108,19 @@ export class DevicePool extends EventEmitter {
108
108
  device.updateDescriptor(exist, true);
109
109
  devices[connectId] = device;
110
110
  deviceList.push(device);
111
- await this._checkDevicePool(initOptions);
111
+ await this._checkDevicePool(initOptions, connectId);
112
112
  return { devices, deviceList };
113
113
  }
114
114
  Log.debug('found device in cache, but path is different: ', connectId);
115
115
  }
116
116
  }
117
117
 
118
- for await (const descriptor of descriptorList) {
118
+ const matchedDescriptor = connectId
119
+ ? descriptorList.find(descriptor => descriptor.path === connectId)
120
+ : undefined;
121
+ const descriptorsToInitialize = matchedDescriptor ? [matchedDescriptor] : descriptorList;
122
+
123
+ for await (const descriptor of descriptorsToInitialize) {
119
124
  const device = await this._createDevice(descriptor, initOptions);
120
125
 
121
126
  const connectId = device.getConnectId();
@@ -133,7 +138,7 @@ export class DevicePool extends EventEmitter {
133
138
  // Log.debug('get devices result : ', devices, deviceList);
134
139
  // console.log('device poll -> connected: ', this.connectedPool);
135
140
  // console.log('device poll -> disconnected: ', this.disconnectPool);
136
- await this._checkDevicePool(initOptions);
141
+ await this._checkDevicePool(initOptions, matchedDescriptor ? connectId : undefined);
137
142
  return { devices, deviceList };
138
143
  }
139
144
 
@@ -157,14 +162,17 @@ export class DevicePool extends EventEmitter {
157
162
  return device;
158
163
  }
159
164
 
160
- static async _checkDevicePool(initOptions?: InitOptions) {
161
- await this._sendConnectMessage(initOptions);
165
+ static async _checkDevicePool(initOptions?: InitOptions, connectId?: string) {
166
+ await this._sendConnectMessage(initOptions, connectId);
162
167
  this._sendDisconnectMessage();
163
168
  }
164
169
 
165
- static async _sendConnectMessage(initOptions?: InitOptions) {
170
+ static async _sendConnectMessage(initOptions?: InitOptions, connectId?: string) {
166
171
  for (let i = this.connectedPool.length - 1; i >= 0; i--) {
167
172
  const descriptor = this.connectedPool[i];
173
+ if (connectId && descriptor.path !== connectId) {
174
+ continue;
175
+ }
168
176
  const device = await this._createDevice(descriptor, initOptions);
169
177
  Log.debug('emit DEVICE.CONNECT: ', device?.features);
170
178
  this.emitter.emit(DEVICE.CONNECT, device);
@@ -5,7 +5,11 @@ import {
5
5
  isProtocolV2RomloaderDeviceInfo,
6
6
  } from '../protocols/protocol-v2/features';
7
7
 
8
- import type { DeviceFirmwareImageInfo, ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
8
+ import type {
9
+ DeviceFirmwareImageInfo,
10
+ DeviceStatus,
11
+ ProtocolV2DeviceInfo,
12
+ } from '@onekeyfe/hd-transport';
9
13
  import type { PROTO } from '../constants';
10
14
  import type { DeviceFeaturesMode, Features } from '../types';
11
15
 
@@ -222,16 +226,21 @@ export const buildProtocolV1FeaturesPayload = (
222
226
  * 缓存的同名字段级合并;不存在协议等价语义的字段保持 null/空值,不再通过
223
227
  * DeviceProfile 或 transport path 做身份兜底。
224
228
  */
225
- export const buildProtocolV2FeaturesPayload = (
226
- deviceInfo?: ProtocolV2DeviceInfo,
227
- previous?: Features
228
- ): Features => {
229
+ export const buildProtocolV2FeaturesPayload = ({
230
+ deviceInfo,
231
+ deviceStatus,
232
+ previous,
233
+ }: {
234
+ deviceInfo?: ProtocolV2DeviceInfo;
235
+ deviceStatus?: DeviceStatus;
236
+ previous?: Features;
237
+ }): Features => {
229
238
  const info = deviceInfo;
230
239
  const fwApplication = info?.fw?.application;
231
240
  const fwBootloader = info?.fw?.bootloader;
232
241
  const fwBoard = info?.fw?.romloader;
233
242
  const bleApplication = info?.coprocessor?.application;
234
- const status = info?.status;
243
+ const status = deviceStatus;
235
244
 
236
245
  const firmwareVersion = firstMeaningfulVersion(
237
246
  getImageVersion(fwApplication),
@@ -248,15 +257,17 @@ export const buildProtocolV2FeaturesPayload = (
248
257
  const label = previous?.label ?? null;
249
258
  const bleName = firstValue(info?.coprocessor?.bt_adv_name, previous?.bleName);
250
259
  const initialized = firstValue(status?.init_states, previous?.initialized) ?? null;
251
- const passphraseProtection = status?.passphrase_enabled ?? null;
260
+ // Pro2 在锁定状态下返回的 passphrase_enabled 不是最终钱包状态。
261
+ // 只有完成 PIN 解锁后的 DeviceStatus 才能作为 passphrase 开关真值来源。
262
+ const passphraseProtection = status?.unlocked === true ? status.passphrase_enabled ?? null : null;
252
263
  const language = previous?.language ?? null;
253
264
  const backupRequired = firstValue(status?.backup_required, previous?.backupRequired) ?? null;
254
265
  const bleEnabled = previous?.bleEnabled;
255
266
  const unlocked = firstValue(status?.unlocked, previous?.unlocked) ?? null;
256
267
  const attachToPinEnabled = status?.attach_to_pin_enabled ?? null;
257
268
  const unlockedAttachPin = status?.unlocked_by_attach_to_pin ?? undefined;
258
- const romloaderMode = isProtocolV2RomloaderDeviceInfo(info);
259
- const bootloaderMode = isProtocolV2BootloaderDeviceInfo(info);
269
+ const romloaderMode = isProtocolV2RomloaderDeviceInfo(info, status);
270
+ const bootloaderMode = isProtocolV2BootloaderDeviceInfo(info, status);
260
271
  let mode: DeviceFeaturesMode = 'unknown';
261
272
  if (romloaderMode) {
262
273
  mode = 'romloader';
@@ -376,6 +387,7 @@ export const buildProtocolV2FeaturesPayload = (
376
387
  unlockedAttachPin,
377
388
  raw: {
378
389
  protocolV2DeviceInfo: deviceInfo,
390
+ protocolV2DeviceStatus: deviceStatus,
379
391
  },
380
392
  };
381
393
  };