@onekeyfe/hd-core 0.1.36 → 0.1.39

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 (88) hide show
  1. package/dist/api/BaseMethod.d.ts +1 -0
  2. package/dist/api/BaseMethod.d.ts.map +1 -1
  3. package/dist/api/CheckBLEFirmwareRelease.d.ts.map +1 -1
  4. package/dist/api/CheckBridgeStatus.d.ts.map +1 -1
  5. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  6. package/dist/api/CheckTransportRelease.d.ts.map +1 -1
  7. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  8. package/dist/api/GetFeatures.d.ts.map +1 -1
  9. package/dist/api/GetLogs.d.ts.map +1 -1
  10. package/dist/api/GetPassphraseState.d.ts +6 -0
  11. package/dist/api/GetPassphraseState.d.ts.map +1 -0
  12. package/dist/api/RequestWebUsbDevice.d.ts.map +1 -1
  13. package/dist/api/SearchDevices.d.ts.map +1 -1
  14. package/dist/api/device/DeviceBackup.d.ts.map +1 -1
  15. package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
  16. package/dist/api/device/DeviceFlags.d.ts.map +1 -1
  17. package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
  18. package/dist/api/device/DeviceRecovery.d.ts.map +1 -1
  19. package/dist/api/device/DeviceReset.d.ts.map +1 -1
  20. package/dist/api/device/DeviceSettings.d.ts +7 -0
  21. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  22. package/dist/api/device/DeviceSupportFeatures.d.ts +1 -1
  23. package/dist/api/device/DeviceSupportFeatures.d.ts.map +1 -1
  24. package/dist/api/device/DeviceUpdateReboot.d.ts.map +1 -1
  25. package/dist/api/device/DeviceVerify.d.ts.map +1 -1
  26. package/dist/api/device/DeviceWipe.d.ts.map +1 -1
  27. package/dist/api/index.d.ts +1 -0
  28. package/dist/api/index.d.ts.map +1 -1
  29. package/dist/core/index.d.ts.map +1 -1
  30. package/dist/device/Device.d.ts +15 -5
  31. package/dist/device/Device.d.ts.map +1 -1
  32. package/dist/device/DeviceCommands.d.ts.map +1 -1
  33. package/dist/device/DeviceList.d.ts +2 -2
  34. package/dist/device/DeviceList.d.ts.map +1 -1
  35. package/dist/device/DevicePool.d.ts +6 -5
  36. package/dist/device/DevicePool.d.ts.map +1 -1
  37. package/dist/events/ui-request.d.ts +9 -1
  38. package/dist/events/ui-request.d.ts.map +1 -1
  39. package/dist/index.d.ts +35 -8
  40. package/dist/index.js +442 -254
  41. package/dist/inject.d.ts.map +1 -1
  42. package/dist/types/api/getPassphraseState.d.ts +3 -0
  43. package/dist/types/api/getPassphraseState.d.ts.map +1 -0
  44. package/dist/types/api/index.d.ts +2 -0
  45. package/dist/types/api/index.d.ts.map +1 -1
  46. package/dist/types/device.d.ts +5 -1
  47. package/dist/types/device.d.ts.map +1 -1
  48. package/dist/types/params.d.ts +2 -0
  49. package/dist/types/params.d.ts.map +1 -1
  50. package/dist/utils/deviceFeaturesUtils.d.ts +5 -2
  51. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  52. package/dist/utils/patch.d.ts +1 -1
  53. package/package.json +4 -4
  54. package/src/api/BaseMethod.ts +5 -0
  55. package/src/api/CheckBLEFirmwareRelease.ts +1 -0
  56. package/src/api/CheckBridgeStatus.ts +1 -0
  57. package/src/api/CheckFirmwareRelease.ts +3 -1
  58. package/src/api/CheckTransportRelease.ts +1 -0
  59. package/src/api/FirmwareUpdate.ts +1 -0
  60. package/src/api/GetFeatures.ts +1 -0
  61. package/src/api/GetLogs.ts +1 -0
  62. package/src/api/GetPassphraseState.ts +31 -0
  63. package/src/api/RequestWebUsbDevice.ts +1 -0
  64. package/src/api/SearchDevices.ts +1 -0
  65. package/src/api/device/DeviceBackup.ts +3 -1
  66. package/src/api/device/DeviceChangePin.ts +2 -0
  67. package/src/api/device/DeviceFlags.ts +2 -0
  68. package/src/api/device/DeviceRebootToBootloader.ts +3 -1
  69. package/src/api/device/DeviceRecovery.ts +2 -0
  70. package/src/api/device/DeviceReset.ts +2 -0
  71. package/src/api/device/DeviceSettings.ts +13 -2
  72. package/src/api/device/DeviceSupportFeatures.ts +3 -1
  73. package/src/api/device/DeviceUpdateReboot.ts +3 -1
  74. package/src/api/device/DeviceVerify.ts +2 -0
  75. package/src/api/device/DeviceWipe.ts +3 -1
  76. package/src/api/index.ts +1 -0
  77. package/src/core/index.ts +98 -10
  78. package/src/device/Device.ts +120 -47
  79. package/src/device/DeviceCommands.ts +13 -9
  80. package/src/device/DeviceList.ts +7 -3
  81. package/src/device/DevicePool.ts +24 -12
  82. package/src/events/ui-request.ts +10 -0
  83. package/src/inject.ts +2 -0
  84. package/src/types/api/getPassphraseState.ts +6 -0
  85. package/src/types/api/index.ts +2 -0
  86. package/src/types/device.ts +3 -1
  87. package/src/types/params.ts +8 -0
  88. package/src/utils/deviceFeaturesUtils.ts +38 -5
package/src/core/index.ts CHANGED
@@ -1,8 +1,14 @@
1
1
  import semver from 'semver';
2
2
  import EventEmitter from 'events';
3
- import { OneKeyDeviceInfo } from '@onekeyfe/hd-transport';
3
+ import { Features, OneKeyDeviceInfo } from '@onekeyfe/hd-transport';
4
4
  import { createDeferred, Deferred, ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
5
- import { Device, DeviceEvents } from '../device/Device';
5
+ import {
6
+ getDeviceFirmwareVersion,
7
+ getDeviceModel,
8
+ getDeviceType,
9
+ supportNewPassphrase,
10
+ } from '../utils/deviceFeaturesUtils';
11
+ import { Device, DeviceEvents, InitOptions, RunOptions } from '../device/Device';
6
12
  import { DeviceList } from '../device/DeviceList';
7
13
  import { DevicePool } from '../device/DevicePool';
8
14
  import { findMethod } from '../api/utils';
@@ -26,14 +32,15 @@ import type { BaseMethod } from '../api/BaseMethod';
26
32
  import type { ConnectSettings, KnownDevice } from '../types';
27
33
  import TransportManager from '../data-manager/TransportManager';
28
34
  import DeviceConnector from '../device/DeviceConnector';
29
- import {
30
- getDeviceFirmwareVersion,
31
- getDeviceModel,
32
- getDeviceType,
33
- } from '../utils/deviceFeaturesUtils';
34
35
 
35
36
  const Log = getLogger(LoggerNames.Core);
36
37
 
38
+ const parseInitOptions = (method?: BaseMethod): InitOptions => ({
39
+ initSession: method?.payload.initSession,
40
+ passphraseState: method?.payload.passphraseState,
41
+ deviceId: method?.payload.deviceId,
42
+ });
43
+
37
44
  let _core: Core;
38
45
  let _deviceList: DeviceList | undefined;
39
46
  let _connector: DeviceConnector | undefined;
@@ -45,6 +52,12 @@ const deviceCacheMap = new Map<string, Device>();
45
52
  let pollingId = 1;
46
53
  const pollingState: Record<number, boolean> = {};
47
54
 
55
+ let preConnectCache: {
56
+ passphraseState: string | undefined;
57
+ } = {
58
+ passphraseState: undefined,
59
+ };
60
+
48
61
  export const callAPI = async (message: CoreMessage) => {
49
62
  if (!message.id || !message.payload || message.type !== IFRAME.CALL) {
50
63
  return Promise.reject(ERRORS.TypedError('on call: message.id or message.payload is missing'));
@@ -83,6 +96,17 @@ export const callAPI = async (message: CoreMessage) => {
83
96
  );
84
97
  }
85
98
 
99
+ const connectStateChange = preConnectCache.passphraseState !== method.payload.passphraseState;
100
+
101
+ preConnectCache = {
102
+ passphraseState: method.payload.passphraseState,
103
+ };
104
+
105
+ if (connectStateChange || method.payload.initSession) {
106
+ Log.debug('passphrase state change, clear device cache');
107
+ DevicePool.clearDeviceCache(method.payload.connectId);
108
+ }
109
+
86
110
  /**
87
111
  * Polling to ensure successful connection
88
112
  */
@@ -102,6 +126,7 @@ export const callAPI = async (message: CoreMessage) => {
102
126
 
103
127
  device.on(DEVICE.PIN, onDevicePinHandler);
104
128
  device.on(DEVICE.BUTTON, onDeviceButtonHandler);
129
+ device.on(DEVICE.PASSPHRASE_ON_DEVICE, onDevicePassphraseHandler);
105
130
  device.on(DEVICE.FEATURES, onDeviceFeaturesHandler);
106
131
 
107
132
  try {
@@ -179,6 +204,38 @@ export const callAPI = async (message: CoreMessage) => {
179
204
  await TransportManager.reconfigure(device.getFirmwareVersion());
180
205
  }
181
206
 
207
+ // Check to see if it is safe to use Passphrase
208
+ checkPassphraseSafety(method, device.features);
209
+
210
+ if (device.hasUsePassphrase() && method.useDevicePassphraseState) {
211
+ // check version
212
+ const support = supportNewPassphrase(device.features);
213
+ if (!support.support) {
214
+ return Promise.reject(
215
+ ERRORS.TypedError(
216
+ HardwareErrorCode.DeviceNotSupportPassphrase,
217
+ `Device not support passphrase, please update to ${support.require}`,
218
+ {
219
+ require: support.require,
220
+ }
221
+ )
222
+ );
223
+ }
224
+
225
+ // Check Device passphrase State
226
+ const passphraseState = await device.checkPassphraseState();
227
+
228
+ // Double check, handles the special case of Touch/Pro
229
+ checkPassphraseSafety(method, device.features);
230
+
231
+ if (passphraseState) {
232
+ DevicePool.clearDeviceCache(method.payload.connectId);
233
+ return Promise.reject(
234
+ ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError)
235
+ );
236
+ }
237
+ }
238
+
182
239
  try {
183
240
  const response: object = await method.run();
184
241
  Log.debug('Call API - Inner Method Run: ');
@@ -191,7 +248,12 @@ export const callAPI = async (message: CoreMessage) => {
191
248
  }
192
249
  };
193
250
  Log.debug('Call API - Device Run: ', device.mainId);
194
- const deviceRun = () => device.run(inner);
251
+
252
+ const runOptions: RunOptions = {
253
+ keepSession: method.payload.keepSession,
254
+ ...parseInitOptions(method),
255
+ };
256
+ const deviceRun = () => device.run(inner, runOptions);
195
257
  _callPromise = createDeferred(deviceRun);
196
258
 
197
259
  try {
@@ -247,7 +309,7 @@ async function initDeviceList(method: BaseMethod) {
247
309
  _deviceList.connector = _connector;
248
310
  }
249
311
 
250
- await _deviceList.getDeviceLists(method.connectId);
312
+ await _deviceList.getDeviceLists(method.connectId, parseInitOptions(method));
251
313
  }
252
314
 
253
315
  function initDevice(method: BaseMethod) {
@@ -359,12 +421,13 @@ const ensureConnected = async (method: BaseMethod, pollingId: number) => {
359
421
  if (timer) {
360
422
  clearTimeout(timer);
361
423
  }
424
+
362
425
  /**
363
426
  * Bluetooth should call initialize here
364
427
  */
365
428
  if (env === 'react-native') {
366
429
  await device.acquire();
367
- await device.initialize();
430
+ await device.initialize(parseInitOptions(method));
368
431
  }
369
432
  resolve(device);
370
433
  return;
@@ -378,6 +441,7 @@ const ensureConnected = async (method: BaseMethod, pollingId: number) => {
378
441
  HardwareErrorCode.BleDeviceNotBonded,
379
442
  HardwareErrorCode.BleCharacteristicNotifyError,
380
443
  HardwareErrorCode.BleWriteCharacteristicError,
444
+ HardwareErrorCode.BleAlreadyConnected,
381
445
  ].includes(error.errorCode)
382
446
  ) {
383
447
  reject(error);
@@ -420,6 +484,20 @@ export const cancel = (connectId?: string) => {
420
484
  closePopup();
421
485
  };
422
486
 
487
+ const checkPassphraseSafety = (method: BaseMethod, features?: Features) => {
488
+ if (!method.useDevicePassphraseState) return;
489
+
490
+ if (features?.passphrase_protection === true && !method.payload.passphraseState) {
491
+ DevicePool.clearDeviceCache(method.payload.connectId);
492
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceOpenedPassphrase);
493
+ }
494
+
495
+ if (features?.passphrase_protection === false && method.payload.passphraseState) {
496
+ DevicePool.clearDeviceCache(method.payload.connectId);
497
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceNotOpenedPassphrase);
498
+ }
499
+ };
500
+
423
501
  const cleanup = () => {
424
502
  _uiPromises = [];
425
503
  Log.debug('Cleanup...');
@@ -428,6 +506,7 @@ const cleanup = () => {
428
506
  const removeDeviceListener = (device: Device) => {
429
507
  device.removeListener(DEVICE.PIN, onDevicePinHandler);
430
508
  device.removeListener(DEVICE.BUTTON, onDeviceButtonHandler);
509
+ device.removeListener(DEVICE.PASSPHRASE_ON_DEVICE, onDevicePassphraseHandler);
431
510
  device.removeListener(DEVICE.FEATURES, onDeviceFeaturesHandler);
432
511
  DevicePool.emitter.removeListener(DEVICE.CONNECT, onDeviceConnectHandler);
433
512
  // DevicePool.emitter.removeListener(DEVICE.DISCONNECT, onDeviceDisconnectHandler);
@@ -488,6 +567,15 @@ const onDeviceFeaturesHandler = (...[_, features]: [...DeviceEvents['features']]
488
567
  postMessage(createDeviceMessage(DEVICE.FEATURES, { ...features }));
489
568
  };
490
569
 
570
+ const onDevicePassphraseHandler = (...[device]: [...DeviceEvents['passphrase_on_device']]) => {
571
+ postMessage(
572
+ createUiMessage(UI_REQUEST.REQUEST_PASSPHRASE_ON_DEVICE, {
573
+ device: device.toMessageObject() as KnownDevice,
574
+ passphraseState: device.passphraseState,
575
+ })
576
+ );
577
+ };
578
+
491
579
  /**
492
580
  * Emit message to listener (parent).
493
581
  * Clear method reference from _callMethods
@@ -7,11 +7,6 @@ import {
7
7
  ERRORS,
8
8
  HardwareError,
9
9
  } from '@onekeyfe/hd-shared';
10
-
11
- import type DeviceConnector from './DeviceConnector';
12
- // eslint-disable-next-line import/no-cycle
13
- import { DeviceCommands } from './DeviceCommands';
14
-
15
10
  import {
16
11
  getDeviceFirmwareVersion,
17
12
  getDeviceLabel,
@@ -19,7 +14,13 @@ import {
19
14
  getDeviceUUID,
20
15
  getDeviceBLEFirmwareVersion,
21
16
  getDeviceTypeOnBootloader,
17
+ getPassphraseState,
22
18
  } from '../utils/deviceFeaturesUtils';
19
+
20
+ import type DeviceConnector from './DeviceConnector';
21
+ // eslint-disable-next-line import/no-cycle
22
+ import { DeviceCommands } from './DeviceCommands';
23
+
23
24
  import type { Features, Device as DeviceTyped, UnavailableCapabilities } from '../types';
24
25
  import { DEVICE, DeviceButtonRequestPayload, DeviceFeaturesPayload } from '../events';
25
26
  import { UI_REQUEST } from '../constants/ui-request';
@@ -27,10 +28,16 @@ import { PROTO } from '../constants';
27
28
  import { getLogger, LoggerNames } from '../utils';
28
29
  import { DataManager } from '../data-manager';
29
30
 
30
- type RunOptions = {
31
- keepSession?: boolean;
31
+ export type InitOptions = {
32
+ initSession?: boolean;
33
+ deviceId?: string;
34
+ passphraseState?: string;
32
35
  };
33
36
 
37
+ export type RunOptions = {
38
+ keepSession?: boolean;
39
+ } & InitOptions;
40
+
34
41
  const parseRunOptions = (options?: RunOptions): RunOptions => {
35
42
  if (!options) options = {};
36
43
  return options;
@@ -51,6 +58,8 @@ export interface Device {
51
58
  emit<K extends keyof DeviceEvents>(type: K, ...args: DeviceEvents[K]): boolean;
52
59
  }
53
60
 
61
+ const deviceSessionCache: Record<string, string> = {};
62
+
54
63
  export class Device extends EventEmitter {
55
64
  /**
56
65
  * 设备标识对象
@@ -102,6 +111,8 @@ export class Device extends EventEmitter {
102
111
  */
103
112
  keepSession = false;
104
113
 
114
+ passphraseState: string | undefined = undefined;
115
+
105
116
  constructor(descriptor: DeviceDescriptor) {
106
117
  super();
107
118
  this.originalDescriptor = descriptor;
@@ -239,46 +250,95 @@ export class Device extends EventEmitter {
239
250
  return this.commands;
240
251
  }
241
252
 
242
- getInternalState() {
243
- return this.internalState[this.instance];
253
+ getInternalState(_deviceId?: string) {
254
+ Log.debug(
255
+ 'getInternalState session param: ',
256
+ `device_id: ${_deviceId}`,
257
+ `features.device_id: ${this.features?.device_id}`,
258
+ `passphraseState: ${this.passphraseState}`
259
+ );
260
+ Log.debug('getInternalState session cache: ', deviceSessionCache);
261
+
262
+ const deviceId = _deviceId || this.features?.device_id;
263
+ if (!deviceId) return undefined;
264
+
265
+ const key = `${deviceId}`;
266
+ const usePassKey = `${deviceId}@${this.passphraseState}`;
267
+ // When creating a wallet, use device_id as the key
268
+ const session = deviceSessionCache[key] ?? deviceSessionCache[usePassKey];
269
+ return this.passphraseState ? session : undefined;
244
270
  }
245
271
 
246
- async initialize() {
247
- let payload: any;
248
- if (this.features) {
249
- const internalState = this.getInternalState();
250
- payload = {};
251
- if (internalState) {
252
- payload.session_id = internalState;
253
- }
272
+ setInternalState(state: string, initSession?: boolean) {
273
+ Log.debug(
274
+ 'setInternalState session param: ',
275
+ `state: ${state}`,
276
+ `initSession: ${initSession}`,
277
+ `device_id: ${this.features?.device_id}`,
278
+ `passphraseState: ${this.passphraseState}`
279
+ );
280
+
281
+ if (!this.features) return;
282
+ if (!this.passphraseState && !initSession) return;
283
+
284
+ let key = `${this.features.device_id}`;
285
+ if (this.passphraseState) {
286
+ key += `@${this.passphraseState}`;
287
+ }
288
+ if (state) {
289
+ deviceSessionCache[key] = state;
254
290
  }
291
+ Log.debug('setInternalState done session cache: ', deviceSessionCache);
292
+ }
255
293
 
256
- const { message } = await this.commands.typedCall('Initialize', 'Features', payload);
257
- this._updateFeatures(message);
258
- if (message.passphrase_protection) {
259
- if (this.listenerCount(DEVICE.PIN) > 0) {
260
- Log.debug('try to close passpharse');
261
- try {
262
- await this.commands.typedCall('ApplySettings', 'Success', { use_passphrase: false });
263
- } catch (e) {
264
- await this.release();
265
- this.runPromise = null;
266
- throw e;
267
- }
268
- }
294
+ clearInternalState(_deviceId?: string) {
295
+ Log.debug('clearInternalState param: ', _deviceId);
296
+
297
+ const deviceId = _deviceId || this.features?.device_id;
298
+ if (!deviceId) return;
299
+ const key = `${deviceId}`;
300
+ delete deviceSessionCache[key];
301
+
302
+ if (this.passphraseState) {
303
+ const usePassKey = `${deviceId}@${this.passphraseState}`;
304
+ delete deviceSessionCache[usePassKey];
269
305
  }
270
306
  }
271
307
 
308
+ async initialize(options?: InitOptions) {
309
+ Log.debug('initialize param:', options);
310
+
311
+ this.passphraseState = options?.passphraseState;
312
+
313
+ if (options?.initSession) {
314
+ this.clearInternalState(options?.deviceId);
315
+ }
316
+
317
+ const internalState = this.getInternalState(options?.deviceId);
318
+ const payload: any = {};
319
+ if (internalState) {
320
+ payload.session_id = internalState;
321
+ }
322
+
323
+ Log.debug('initialize payload:', payload);
324
+
325
+ const { message } = await this.commands.typedCall('Initialize', 'Features', payload);
326
+ this._updateFeatures(message, options?.initSession);
327
+ }
328
+
272
329
  async getFeatures() {
273
330
  const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
274
331
  this._updateFeatures(message);
275
332
  }
276
333
 
277
- _updateFeatures(feat: Features) {
334
+ _updateFeatures(feat: Features, initSession?: boolean) {
278
335
  // GetFeatures doesn't return 'session_id'
279
336
  if (this.features && this.features.session_id && !feat.session_id) {
280
337
  feat.session_id = this.features.session_id;
281
338
  }
339
+ if (this.features && this.features.device_id && feat.session_id) {
340
+ this.setInternalState(feat.session_id, initSession);
341
+ }
282
342
  feat.unlocked = feat.unlocked || true;
283
343
 
284
344
  this.features = feat;
@@ -336,7 +396,7 @@ export class Device extends EventEmitter {
336
396
  await this.acquire();
337
397
  try {
338
398
  if (fn) {
339
- await this.initialize();
399
+ await this.initialize(options);
340
400
  }
341
401
  } catch (error) {
342
402
  this.runPromise = null;
@@ -350,21 +410,6 @@ export class Device extends EventEmitter {
350
410
  )
351
411
  );
352
412
  }
353
- } else if (env === 'react-native') {
354
- /**
355
- * The timing of the mobile initialization is different, so it needs to be closed here
356
- */
357
- if (this.features?.passphrase_protection) {
358
- if (this.listenerCount(DEVICE.PIN) > 0) {
359
- Log.debug('try to close passpharse for mobile');
360
- try {
361
- await this.commands.typedCall('ApplySettings', 'Success', { use_passphrase: false });
362
- } catch (e) {
363
- this.runPromise = null;
364
- return Promise.reject(e);
365
- }
366
- }
367
- }
368
413
  }
369
414
  }
370
415
 
@@ -490,12 +535,40 @@ export class Device extends EventEmitter {
490
535
  return null;
491
536
  }
492
537
 
538
+ hasUsePassphrase() {
539
+ const isModeT =
540
+ getDeviceType(this.features) === 'touch' || getDeviceType(this.features) === 'pro';
541
+ const preCheckTouch = isModeT && this.features?.unlocked === true;
542
+
543
+ return this.features && (!!this.features.passphrase_protection || preCheckTouch);
544
+ }
545
+
493
546
  checkDeviceId(deviceId: string) {
494
547
  if (this.features) {
495
548
  return this.features.device_id === deviceId;
496
549
  }
497
550
  return false;
498
551
  }
552
+
553
+ async checkPassphraseState() {
554
+ if (!this.features) return false;
555
+ const locked = this.features?.unlocked === true;
556
+ const isModeT =
557
+ getDeviceType(this.features) === 'touch' || getDeviceType(this.features) === 'pro';
558
+
559
+ const newState = await getPassphraseState(this.features, this.commands);
560
+
561
+ // if Touch/Pro was locked before, refresh the passphrase state
562
+ if (isModeT && locked) {
563
+ await this.getFeatures();
564
+ }
565
+
566
+ // When exists passphraseState, check passphraseState
567
+ if (this.passphraseState && this.passphraseState !== newState) {
568
+ this.clearInternalState();
569
+ return newState;
570
+ }
571
+ }
499
572
  }
500
573
 
501
574
  export default Device;
@@ -1,6 +1,5 @@
1
1
  import type { Transport, Messages } from '@onekeyfe/hd-transport';
2
2
  import { ERRORS, HardwareError, HardwareErrorCode } from '@onekeyfe/hd-shared';
3
- // eslint-disable-next-line import/no-cycle
4
3
  import TransportManager from '../data-manager/TransportManager';
5
4
  import DataManager from '../data-manager/DataManager';
6
5
  import { patchFeatures, getLogger, LoggerNames } from '../utils';
@@ -164,6 +163,10 @@ export class DeviceCommands {
164
163
  error = ERRORS.TypedError(HardwareErrorCode.PinCancelled);
165
164
  }
166
165
 
166
+ if (code === 'Failure_DataError' && message === 'Please confirm the BlindSign enabled') {
167
+ error = ERRORS.TypedError(HardwareErrorCode.BlindSignDisabled);
168
+ }
169
+
167
170
  if (error) {
168
171
  return Promise.reject(error);
169
172
  }
@@ -214,19 +217,20 @@ export class DeviceCommands {
214
217
  );
215
218
  }
216
219
 
217
- /**
218
- * Temporary, do not support passphrase
219
- */
220
- if (this.device.features?.passphrase_protection) {
221
- return Promise.reject(ERRORS.TypedError(HardwareErrorCode.DeviceNotSupportPassphrase));
222
- }
223
-
224
220
  if (res.type === 'PassphraseRequest') {
225
221
  /**
226
222
  * Temporary, do not support passphrase
227
223
  */
228
224
  // return this._commonCall('PassphraseAck', { passphrase: '' });
229
- return Promise.reject(ERRORS.TypedError(HardwareErrorCode.DeviceNotSupportPassphrase));
225
+ return Promise.reject(
226
+ ERRORS.TypedError(
227
+ HardwareErrorCode.DeviceNotSupportPassphrase,
228
+ 'Device not support passphrase',
229
+ {
230
+ require: '2.4.0',
231
+ }
232
+ )
233
+ );
230
234
  }
231
235
 
232
236
  // TT fw lower than 2.3.0, device send his current state
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'events';
2
2
  import DeviceConnector from './DeviceConnector';
3
- import { Device } from './Device';
3
+ import { Device, InitOptions } from './Device';
4
4
  import { DevicePool } from './DevicePool';
5
5
 
6
6
  export class DeviceList extends EventEmitter {
@@ -12,13 +12,17 @@ export class DeviceList extends EventEmitter {
12
12
  * 获取已连接的设备列表
13
13
  * @returns {OneKeyDeviceInfo[]}
14
14
  */
15
- async getDeviceLists(connectId?: string) {
15
+ async getDeviceLists(connectId?: string, initOptions?: InitOptions) {
16
16
  const deviceDiff = await this.connector?.enumerate();
17
17
  const descriptorList = deviceDiff?.descriptors ?? [];
18
18
 
19
19
  this.devices = {};
20
20
 
21
- const { deviceList, devices } = await DevicePool.getDevices(descriptorList, connectId);
21
+ const { deviceList, devices } = await DevicePool.getDevices(
22
+ descriptorList,
23
+ connectId,
24
+ initOptions
25
+ );
22
26
  this.devices = devices;
23
27
  return deviceList;
24
28
  }
@@ -1,7 +1,7 @@
1
1
  import EventEmitter from 'events';
2
2
  import { OneKeyDeviceInfo as DeviceDescriptor } from '@onekeyfe/hd-transport';
3
3
  // eslint-disable-next-line import/no-cycle
4
- import { Device } from './Device';
4
+ import { Device, InitOptions } from './Device';
5
5
  import { DEVICE } from '../events';
6
6
  import type DeviceConnector from './DeviceConnector';
7
7
  import { getDeviceUUID, getLogger, LoggerNames } from '../utils';
@@ -89,8 +89,12 @@ export class DevicePool extends EventEmitter {
89
89
  this.connector = connector;
90
90
  }
91
91
 
92
- static async getDevices(descriptorList: DeviceDescriptor[], connectId?: string) {
93
- Log.debug('get device list');
92
+ static async getDevices(
93
+ descriptorList: DeviceDescriptor[],
94
+ connectId?: string,
95
+ initOptions?: InitOptions
96
+ ) {
97
+ Log.debug('get device list: connectId: ', connectId);
94
98
 
95
99
  const devices: Record<string, Device> = {};
96
100
  const deviceList = [];
@@ -106,7 +110,7 @@ export class DevicePool extends EventEmitter {
106
110
  device.updateDescriptor(exist, true);
107
111
  devices[connectId] = device;
108
112
  deviceList.push(device);
109
- await this._checkDevicePool();
113
+ await this._checkDevicePool(initOptions);
110
114
  return { devices, deviceList };
111
115
  }
112
116
  Log.debug('found device in cache, but path is different: ', connectId);
@@ -114,7 +118,7 @@ export class DevicePool extends EventEmitter {
114
118
  }
115
119
 
116
120
  for await (const descriptor of descriptorList) {
117
- const device = await this._createDevice(descriptor);
121
+ const device = await this._createDevice(descriptor, initOptions);
118
122
 
119
123
  if (device.features) {
120
124
  const uuid = getDeviceUUID(device.features);
@@ -131,31 +135,39 @@ export class DevicePool extends EventEmitter {
131
135
  Log.debug('get devices result : ', devices, deviceList);
132
136
  console.log('device poll -> connected: ', this.connectedPool);
133
137
  console.log('device poll -> disconnected: ', this.disconnectPool);
134
- await this._checkDevicePool();
138
+ await this._checkDevicePool(initOptions);
135
139
  return { devices, deviceList };
136
140
  }
137
141
 
138
- static async _createDevice(descriptor: DeviceDescriptor) {
142
+ static clearDeviceCache(connectId?: string) {
143
+ Log.debug('clear device pool cache: connectId', connectId);
144
+ Log.debug('clear device pool cache: ', this.devicesCache);
145
+ if (connectId) {
146
+ delete this.devicesCache[connectId];
147
+ }
148
+ }
149
+
150
+ static async _createDevice(descriptor: DeviceDescriptor, initOptions?: InitOptions) {
139
151
  let device = this.getDeviceByPath(descriptor.path);
140
152
  if (!device) {
141
153
  device = Device.fromDescriptor(descriptor);
142
154
  device.deviceConnector = this.connector;
143
155
  await device.connect();
144
- await device.initialize();
156
+ await device.initialize(initOptions);
145
157
  await device.release();
146
158
  }
147
159
  return device;
148
160
  }
149
161
 
150
- static async _checkDevicePool() {
151
- await this._sendConnectMessage();
162
+ static async _checkDevicePool(initOptions?: InitOptions) {
163
+ await this._sendConnectMessage(initOptions);
152
164
  this._sendDisconnectMessage();
153
165
  }
154
166
 
155
- static async _sendConnectMessage() {
167
+ static async _sendConnectMessage(initOptions?: InitOptions) {
156
168
  for (let i = this.connectedPool.length - 1; i >= 0; i--) {
157
169
  const descriptor = this.connectedPool[i];
158
- const device = await this._createDevice(descriptor);
170
+ const device = await this._createDevice(descriptor, initOptions);
159
171
  Log.debug('emit DEVICE.CONNECT: ', device);
160
172
  this.emitter.emit(DEVICE.CONNECT, device);
161
173
  this.connectedPool.splice(i, 1);
@@ -9,6 +9,7 @@ export const UI_REQUEST = {
9
9
  REQUEST_PIN: 'ui-request_pin',
10
10
  INVALID_PIN: 'ui-invalid_pin',
11
11
  REQUEST_BUTTON: 'ui-button',
12
+ REQUEST_PASSPHRASE_ON_DEVICE: 'ui-request_passphrase_on_device',
12
13
 
13
14
  CLOSE_UI_WINDOW: 'ui-close_window',
14
15
 
@@ -41,6 +42,14 @@ export interface UiRequestButton {
41
42
  payload: DeviceButtonRequest['payload'];
42
43
  }
43
44
 
45
+ export interface UiRequestPassphrase {
46
+ type: typeof UI_REQUEST.REQUEST_PASSPHRASE_ON_DEVICE;
47
+ payload: {
48
+ device: Device;
49
+ passphraseState?: string;
50
+ };
51
+ }
52
+
44
53
  export interface FirmwareProgress {
45
54
  type: typeof UI_REQUEST.FIRMWARE_PROGRESS;
46
55
  payload: {
@@ -53,6 +62,7 @@ export type UiEvent =
53
62
  | UiRequestWithoutPayload
54
63
  | UiRequestDeviceAction
55
64
  | UiRequestButton
65
+ | UiRequestPassphrase
56
66
  | FirmwareProgress;
57
67
 
58
68
  export type UiEventMessage = UiEvent & { event: typeof UI_EVENT };
package/src/inject.ts CHANGED
@@ -88,6 +88,8 @@ export const inject = ({
88
88
  deviceSupportFeatures: connectId => call({ connectId, method: 'deviceSupportFeatures' }),
89
89
  deviceVerify: (connectId, params) => call({ ...params, connectId, method: 'deviceVerify' }),
90
90
  deviceWipe: connectId => call({ connectId, method: 'deviceWipe' }),
91
+ getPassphraseState: (connectId, params) =>
92
+ call({ ...params, connectId, method: 'getPassphraseState' }),
91
93
 
92
94
  evmGetAddress: (connectId, deviceId, params) =>
93
95
  call({ ...params, connectId, deviceId, method: 'evmGetAddress' }),
@@ -0,0 +1,6 @@
1
+ import type { CommonParams, Response } from '../params';
2
+
3
+ export declare function getPassphraseState(
4
+ connectId?: string,
5
+ params?: CommonParams
6
+ ): Response<string>;
@@ -45,6 +45,7 @@ import { firmwareUpdate } from './firmwareUpdate';
45
45
  import { getLogs } from './getLogs';
46
46
  import { deviceSupportFeatures } from './deviceSupportFeatures';
47
47
  import { requestWebUsbDevice } from './requestWebUsbDevice';
48
+ import { getPassphraseState } from './getPassphraseState';
48
49
 
49
50
  export * from './export';
50
51
 
@@ -92,6 +93,7 @@ export type CoreApi = {
92
93
  deviceSupportFeatures: typeof deviceSupportFeatures;
93
94
  deviceVerify: typeof deviceVerify;
94
95
  deviceWipe: typeof deviceWipe;
96
+ getPassphraseState: typeof getPassphraseState;
95
97
 
96
98
  evmGetAddress: typeof evmGetAddress;
97
99
  evmGetPublicKey: typeof evmGetPublicKey;