@onekeyfe/hd-core 0.1.37 → 0.1.40

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 +447 -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 +97 -10
  78. package/src/device/Device.ts +126 -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;
@@ -421,6 +484,20 @@ export const cancel = (connectId?: string) => {
421
484
  closePopup();
422
485
  };
423
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
+
424
501
  const cleanup = () => {
425
502
  _uiPromises = [];
426
503
  Log.debug('Cleanup...');
@@ -429,6 +506,7 @@ const cleanup = () => {
429
506
  const removeDeviceListener = (device: Device) => {
430
507
  device.removeListener(DEVICE.PIN, onDevicePinHandler);
431
508
  device.removeListener(DEVICE.BUTTON, onDeviceButtonHandler);
509
+ device.removeListener(DEVICE.PASSPHRASE_ON_DEVICE, onDevicePassphraseHandler);
432
510
  device.removeListener(DEVICE.FEATURES, onDeviceFeaturesHandler);
433
511
  DevicePool.emitter.removeListener(DEVICE.CONNECT, onDeviceConnectHandler);
434
512
  // DevicePool.emitter.removeListener(DEVICE.DISCONNECT, onDeviceDisconnectHandler);
@@ -489,6 +567,15 @@ const onDeviceFeaturesHandler = (...[_, features]: [...DeviceEvents['features']]
489
567
  postMessage(createDeviceMessage(DEVICE.FEATURES, { ...features }));
490
568
  };
491
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
+
492
579
  /**
493
580
  * Emit message to listener (parent).
494
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,101 @@ 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
+ if (!this.passphraseState) return undefined;
265
+
266
+ const usePassKey = `${deviceId}@${this.passphraseState}`;
267
+
268
+ if (!deviceSessionCache[usePassKey]) {
269
+ const key = `${deviceId}`;
270
+ if (deviceSessionCache[key]) {
271
+ deviceSessionCache[usePassKey] = deviceSessionCache[key];
272
+ }
273
+ }
274
+
275
+ return deviceSessionCache[usePassKey];
244
276
  }
245
277
 
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
- }
278
+ setInternalState(state: string, initSession?: boolean) {
279
+ Log.debug(
280
+ 'setInternalState session param: ',
281
+ `state: ${state}`,
282
+ `initSession: ${initSession}`,
283
+ `device_id: ${this.features?.device_id}`,
284
+ `passphraseState: ${this.passphraseState}`
285
+ );
286
+
287
+ if (!this.features) return;
288
+ if (!this.passphraseState && !initSession) return;
289
+
290
+ let key = `${this.features.device_id}`;
291
+ if (this.passphraseState) {
292
+ key += `@${this.passphraseState}`;
293
+ }
294
+ if (state) {
295
+ deviceSessionCache[key] = state;
254
296
  }
297
+ Log.debug('setInternalState done session cache: ', deviceSessionCache);
298
+ }
255
299
 
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
- }
300
+ clearInternalState(_deviceId?: string) {
301
+ Log.debug('clearInternalState param: ', _deviceId);
302
+
303
+ const deviceId = _deviceId || this.features?.device_id;
304
+ if (!deviceId) return;
305
+ const key = `${deviceId}`;
306
+ delete deviceSessionCache[key];
307
+
308
+ if (this.passphraseState) {
309
+ const usePassKey = `${deviceId}@${this.passphraseState}`;
310
+ delete deviceSessionCache[usePassKey];
269
311
  }
270
312
  }
271
313
 
314
+ async initialize(options?: InitOptions) {
315
+ Log.debug('initialize param:', options);
316
+
317
+ this.passphraseState = options?.passphraseState;
318
+
319
+ if (options?.initSession) {
320
+ this.clearInternalState(options?.deviceId);
321
+ }
322
+
323
+ const internalState = this.getInternalState(options?.deviceId);
324
+ const payload: any = {};
325
+ if (internalState) {
326
+ payload.session_id = internalState;
327
+ }
328
+
329
+ Log.debug('initialize payload:', payload);
330
+
331
+ const { message } = await this.commands.typedCall('Initialize', 'Features', payload);
332
+ this._updateFeatures(message, options?.initSession);
333
+ }
334
+
272
335
  async getFeatures() {
273
336
  const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
274
337
  this._updateFeatures(message);
275
338
  }
276
339
 
277
- _updateFeatures(feat: Features) {
340
+ _updateFeatures(feat: Features, initSession?: boolean) {
278
341
  // GetFeatures doesn't return 'session_id'
279
342
  if (this.features && this.features.session_id && !feat.session_id) {
280
343
  feat.session_id = this.features.session_id;
281
344
  }
345
+ if (this.features && this.features.device_id && feat.session_id) {
346
+ this.setInternalState(feat.session_id, initSession);
347
+ }
282
348
  feat.unlocked = feat.unlocked || true;
283
349
 
284
350
  this.features = feat;
@@ -336,7 +402,7 @@ export class Device extends EventEmitter {
336
402
  await this.acquire();
337
403
  try {
338
404
  if (fn) {
339
- await this.initialize();
405
+ await this.initialize(options);
340
406
  }
341
407
  } catch (error) {
342
408
  this.runPromise = null;
@@ -350,21 +416,6 @@ export class Device extends EventEmitter {
350
416
  )
351
417
  );
352
418
  }
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
419
  }
369
420
  }
370
421
 
@@ -490,12 +541,40 @@ export class Device extends EventEmitter {
490
541
  return null;
491
542
  }
492
543
 
544
+ hasUsePassphrase() {
545
+ const isModeT =
546
+ getDeviceType(this.features) === 'touch' || getDeviceType(this.features) === 'pro';
547
+ const preCheckTouch = isModeT && this.features?.unlocked === true;
548
+
549
+ return this.features && (!!this.features.passphrase_protection || preCheckTouch);
550
+ }
551
+
493
552
  checkDeviceId(deviceId: string) {
494
553
  if (this.features) {
495
554
  return this.features.device_id === deviceId;
496
555
  }
497
556
  return false;
498
557
  }
558
+
559
+ async checkPassphraseState() {
560
+ if (!this.features) return false;
561
+ const locked = this.features?.unlocked === true;
562
+ const isModeT =
563
+ getDeviceType(this.features) === 'touch' || getDeviceType(this.features) === 'pro';
564
+
565
+ const newState = await getPassphraseState(this.features, this.commands);
566
+
567
+ // if Touch/Pro was locked before, refresh the passphrase state
568
+ if (isModeT && locked) {
569
+ await this.getFeatures();
570
+ }
571
+
572
+ // When exists passphraseState, check passphraseState
573
+ if (this.passphraseState && this.passphraseState !== newState) {
574
+ this.clearInternalState();
575
+ return newState;
576
+ }
577
+ }
499
578
  }
500
579
 
501
580
  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;