@onekeyfe/hd-core 1.2.0-alpha.32 → 1.2.0-alpha.34

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 (75) hide show
  1. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +31 -0
  2. package/__tests__/core-dispose.test.ts +3 -3
  3. package/__tests__/device-lifecycle-events.test.ts +52 -0
  4. package/__tests__/device-pool-state.test.ts +14 -2
  5. package/__tests__/device-settings.test.ts +22 -0
  6. package/__tests__/device-state-mapper.test.ts +13 -2
  7. package/__tests__/device-state-projector.test.ts +2 -2
  8. package/__tests__/device-utils.test.ts +1 -0
  9. package/__tests__/device-wallet-session-store.test.ts +64 -17
  10. package/__tests__/kaspaSignTransaction.test.ts +9 -0
  11. package/__tests__/known-device-public-types.type-test.ts +3 -0
  12. package/__tests__/logBlockEvent.test.ts +89 -5
  13. package/__tests__/open-wallet-session.test.ts +334 -30
  14. package/__tests__/pro2Wallpaper.test.ts +1 -1
  15. package/__tests__/protocol-v2.test.ts +708 -75
  16. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  17. package/dist/api/FirmwareUpdateV4.d.ts +1 -0
  18. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  19. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  20. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  21. package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
  22. package/dist/core/index.d.ts.map +1 -1
  23. package/dist/device/Device.d.ts +7 -2
  24. package/dist/device/Device.d.ts.map +1 -1
  25. package/dist/device/DeviceConnector.d.ts +1 -1
  26. package/dist/device/DeviceConnector.d.ts.map +1 -1
  27. package/dist/device/DevicePool.d.ts +1 -0
  28. package/dist/device/DevicePool.d.ts.map +1 -1
  29. package/dist/device/DeviceStateMapper.d.ts.map +1 -1
  30. package/dist/device/DeviceStateProjector.d.ts.map +1 -1
  31. package/dist/device/DeviceWalletSessionStore.d.ts +8 -0
  32. package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
  33. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  34. package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts +7 -0
  35. package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts.map +1 -0
  36. package/dist/events/logBlockEvent.d.ts +2 -0
  37. package/dist/events/logBlockEvent.d.ts.map +1 -1
  38. package/dist/index.d.ts +19 -7
  39. package/dist/index.js +456 -222
  40. package/dist/protocols/protocol-v2/walletSession.d.ts +0 -10
  41. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  42. package/dist/types/api/openWalletSession.d.ts +1 -1
  43. package/dist/types/api/openWalletSession.d.ts.map +1 -1
  44. package/dist/types/device.d.ts +4 -2
  45. package/dist/types/device.d.ts.map +1 -1
  46. package/dist/types/params.d.ts.map +1 -1
  47. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  48. package/dist/utils/deviceFeaturesUtils.d.ts +0 -5
  49. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  50. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  51. package/package.json +4 -4
  52. package/src/api/CheckAllFirmwareRelease.ts +11 -81
  53. package/src/api/FirmwareUpdateV4.ts +33 -17
  54. package/src/api/GetPassphraseState.ts +1 -3
  55. package/src/api/OpenWalletSession.ts +3 -3
  56. package/src/api/device/DeviceSettings.ts +8 -1
  57. package/src/api/kaspa/KaspaSignTransaction.ts +7 -0
  58. package/src/core/index.ts +7 -5
  59. package/src/device/Device.ts +37 -9
  60. package/src/device/DeviceConnector.ts +10 -7
  61. package/src/device/DevicePool.ts +5 -3
  62. package/src/device/DeviceStateMapper.ts +5 -3
  63. package/src/device/DeviceStateProjector.ts +0 -7
  64. package/src/device/DeviceWalletSessionStore.ts +57 -4
  65. package/src/deviceProfile/buildDeviceFeatures.ts +10 -3
  66. package/src/deviceProfile/protocolV2DeviceIdentity.ts +32 -0
  67. package/src/events/logBlockEvent.ts +84 -1
  68. package/src/protocols/protocol-v2/deviceSession.ts +1 -1
  69. package/src/protocols/protocol-v2/walletSession.ts +116 -34
  70. package/src/types/api/openWalletSession.ts +1 -1
  71. package/src/types/device.ts +6 -2
  72. package/src/types/params.ts +2 -1
  73. package/src/utils/deviceFeaturesCompat.ts +8 -1
  74. package/src/utils/pro2Wallpaper.ts +3 -9
  75. package/tsconfig.type-tests.json +5 -1
@@ -280,7 +280,7 @@ export class Device extends EventEmitter {
280
280
 
281
281
  // simplified object to pass via postMessage
282
282
  toMessageObject(): DeviceTyped | null {
283
- if (this.isUnacquired()) return null;
283
+ if (this.isUnacquired() || !this.features) return null;
284
284
 
285
285
  const env = DataManager.getSettings('env');
286
286
  const deviceType = this.getCurrentDeviceType();
@@ -297,6 +297,8 @@ export class Device extends EventEmitter {
297
297
  return {
298
298
  /** Android uses a MAC address, while iOS and USB use transport-specific identifiers. */
299
299
  connectId: DataManager.isBleConnect(env) ? this.mainId || null : connectId,
300
+ /** Persist this after the first active probe so later calls do not need to guess. */
301
+ connectProtocol: this.originalDescriptor.protocolType,
300
302
  /** Stable physical-device identity. */
301
303
  serialNo,
302
304
  /** @deprecated Use serialNo instead. */
@@ -364,12 +366,12 @@ export class Device extends EventEmitter {
364
366
  }
365
367
 
366
368
  async acquire(
367
- connectProtocol?: HardwareConnectProtocol,
369
+ expectedProtocol?: HardwareConnectProtocol,
368
370
  options?: { throwOnRunPromiseError?: boolean }
369
371
  ) {
370
372
  const env = DataManager.getSettings('env');
371
373
  const mainIdKey = DataManager.isBleConnect(env) ? 'id' : 'session';
372
- const expectedProtocol = connectProtocol ?? this.originalDescriptor.protocolType;
374
+ const protocolHint = expectedProtocol ? undefined : this.originalDescriptor.protocolType;
373
375
  try {
374
376
  let acquireResult: unknown;
375
377
  if (DataManager.isBleConnect(env)) {
@@ -381,7 +383,8 @@ export class Device extends EventEmitter {
381
383
  this.originalDescriptor.id,
382
384
  undefined,
383
385
  true,
384
- expectedProtocol
386
+ expectedProtocol,
387
+ protocolHint
385
388
  );
386
389
  this.mainId = (acquireResult as any)?.uuid ?? '';
387
390
  Log.debug('Expected uuid:', this.mainId);
@@ -390,7 +393,8 @@ export class Device extends EventEmitter {
390
393
  this.originalDescriptor.path,
391
394
  this.originalDescriptor.session,
392
395
  undefined,
393
- expectedProtocol
396
+ expectedProtocol,
397
+ protocolHint
394
398
  );
395
399
  this.mainId = acquireResult as string | undefined;
396
400
  Log.debug('Expected session id:', this.mainId);
@@ -717,13 +721,20 @@ export class Device extends EventEmitter {
717
721
  return deviceWalletSessionStore.get(deviceId, this.passphraseState);
718
722
  }
719
723
 
724
+ getStandardInternalState(_deviceId?: string) {
725
+ const deviceId = this.getSessionCacheDeviceKey(_deviceId);
726
+ if (!deviceId) return undefined;
727
+ return deviceWalletSessionStore.getStandard(deviceId);
728
+ }
729
+
720
730
  // attach to pin to fix internal state
721
731
  updateInternalState(
722
732
  enablePassphrase: boolean,
723
733
  passphraseState: string | undefined,
724
734
  deviceId: string | undefined,
725
735
  sessionId: string | null = null,
726
- featuresSessionId: string | null = null
736
+ featuresSessionId: string | null = null,
737
+ walletType: 'standard' | 'hidden' = 'hidden'
727
738
  ) {
728
739
  Log.debug(
729
740
  'updateInternalState session param: ',
@@ -740,7 +751,15 @@ export class Device extends EventEmitter {
740
751
  if (enablePassphrase) {
741
752
  const walletSessionId =
742
753
  sessionId || featuresSessionId || deviceWalletSessionStore.getPending(cacheDeviceKey);
743
- deviceWalletSessionStore.set(cacheDeviceKey, passphraseState, walletSessionId ?? undefined);
754
+ if (walletType === 'standard') {
755
+ deviceWalletSessionStore.setStandard(
756
+ cacheDeviceKey,
757
+ passphraseState,
758
+ walletSessionId ?? undefined
759
+ );
760
+ } else {
761
+ deviceWalletSessionStore.set(cacheDeviceKey, passphraseState, walletSessionId ?? undefined);
762
+ }
744
763
  }
745
764
 
746
765
  deviceWalletSessionStore.deletePending(cacheDeviceKey);
@@ -779,6 +798,12 @@ export class Device extends EventEmitter {
779
798
  }
780
799
  }
781
800
 
801
+ clearStandardInternalState(_deviceId?: string) {
802
+ const deviceId = this.getSessionCacheDeviceKey(_deviceId);
803
+ if (!deviceId) return;
804
+ deviceWalletSessionStore.deleteStandard(deviceId);
805
+ }
806
+
782
807
  async initialize(options?: InitOptions) {
783
808
  // Protocol V2 does not support legacy Initialize; use its dedicated flow.
784
809
  if (this.isProtocolV2()) {
@@ -810,9 +835,12 @@ export class Device extends EventEmitter {
810
835
 
811
836
  const expectedDeviceId = options?.deviceId;
812
837
  if (expectedDeviceId) {
813
- // Establish the live physical identity before sending wallet-bound state.
838
+ // Verify the physical device with a read-only Features request. An
839
+ // empty-context Initialize would invalidate a cached V1 hidden-wallet session.
814
840
  this.passphraseState = undefined;
815
- await callInitialize({ is_contains_attach: true });
841
+ const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
842
+ this._updateFeatures(message);
843
+ await TransportManager.reconfigure(this.features);
816
844
  if (!this.checkDeviceId(expectedDeviceId)) {
817
845
  throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
818
846
  }
@@ -82,9 +82,10 @@ export default class DeviceConnector {
82
82
  path: string,
83
83
  session?: string | null,
84
84
  forceCleanRunPromise?: boolean,
85
- connectProtocol?: HardwareConnectProtocol
85
+ expectedProtocol?: HardwareConnectProtocol,
86
+ protocolHint?: HardwareConnectProtocol
86
87
  ) {
87
- Log.debug('acquire', path, session, connectProtocol);
88
+ Log.debug('acquire', path, session, expectedProtocol, protocolHint);
88
89
  const env = DataManager.getSettings('env');
89
90
  try {
90
91
  let res;
@@ -92,21 +93,23 @@ export default class DeviceConnector {
92
93
  res = await this.transport.acquire({
93
94
  uuid: path,
94
95
  forceCleanRunPromise,
95
- expectedProtocol: connectProtocol,
96
+ expectedProtocol,
97
+ protocolHint,
96
98
  });
97
99
  } else {
98
100
  res = await this.transport.acquire({
99
101
  path,
100
102
  previous: session ?? null,
101
- expectedProtocol: connectProtocol,
103
+ expectedProtocol,
104
+ protocolHint,
102
105
  });
103
106
  }
104
- if (connectProtocol) {
107
+ if (expectedProtocol) {
105
108
  const detectedProtocol = this.transport.getProtocolType(path);
106
- if (detectedProtocol !== connectProtocol) {
109
+ if (detectedProtocol !== expectedProtocol) {
107
110
  throw ERRORS.TypedError(
108
111
  HardwareErrorCode.RuntimeError,
109
- `Device protocol mismatch: expected ${connectProtocol}, detected ${detectedProtocol}`
112
+ `Device protocol mismatch: expected ${expectedProtocol}, detected ${detectedProtocol}`
110
113
  );
111
114
  }
112
115
  }
@@ -306,9 +306,11 @@ export class DevicePool extends EventEmitter {
306
306
  this.disconnectPool = [];
307
307
  this.devicesCache = {};
308
308
 
309
- // Clear all event listeners but keep the emitter instance
310
- this.emitter.removeAllListeners();
311
-
312
309
  Log.debug('DevicePool state has been reset');
313
310
  }
311
+
312
+ static dispose() {
313
+ this.resetState();
314
+ this.emitter.removeAllListeners();
315
+ }
314
316
  }
@@ -1,6 +1,7 @@
1
- import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
1
+ import { EFirmwareType } from '@onekeyfe/hd-shared';
2
2
 
3
3
  import { buildProtocolV1FeaturesPayload } from '../deviceProfile/buildDeviceFeatures';
4
+ import { resolveProtocolV2DeviceIdentity } from '../deviceProfile/protocolV2DeviceIdentity';
4
5
  import {
5
6
  mapLanguageFromProtocolV2,
6
7
  mapLanguageToProtocolV2,
@@ -211,14 +212,15 @@ export const mapProtocolV2DeviceInfoToState = (
211
212
  mode: DeviceStateMode = 'unknown'
212
213
  ): DeviceStatePatch => {
213
214
  const loader = mode === 'bootloader' || mode === 'romloader';
215
+ const identity = resolveProtocolV2DeviceIdentity(info.hw?.Device_type);
214
216
 
215
217
  return {
216
218
  protocol: 'V2',
217
219
  protocolVersion: info.protocol_version,
218
220
  identity: definedEntries({
219
- deviceType: EDeviceType.Pro2,
221
+ deviceType: identity.deviceType,
220
222
  firmwareType: EFirmwareType.Universal,
221
- model: 'pro2',
223
+ model: identity.model,
222
224
  vendor: 'onekey.so',
223
225
  serialNo: info.hw?.serial_no,
224
226
  bleName: info.coprocessor?.bt_adv_name,
@@ -16,12 +16,6 @@ export const projectFeatures = (state: StoredDeviceState): Features => {
16
16
  delete publicRawFeatures.session_id;
17
17
  delete publicRawFeatures.sessionId;
18
18
  delete publicRawFeatures.passphraseState;
19
- const publicRaw = snapshot.raw
20
- ? {
21
- ...snapshot.raw,
22
- protocolV1Features: publicRawFeatures,
23
- }
24
- : undefined;
25
19
  const rawOneKeyFeatures = snapshot.raw?.protocolV1OneKeyFeatures ?? {};
26
20
  const bootloaderMode =
27
21
  snapshot.protocol === 'V1'
@@ -90,7 +84,6 @@ export const projectFeatures = (state: StoredDeviceState): Features => {
90
84
  se04BootVersion: snapshot.versions.se04Boot,
91
85
  seVersion: snapshot.versions.se ?? null,
92
86
  verify: snapshot.verification,
93
- raw: publicRaw,
94
87
  device_id: snapshot.identity.deviceId ?? undefined,
95
88
  ble_name: snapshot.identity.bleName ?? undefined,
96
89
  passphrase_protection: snapshot.status.passphraseProtection ?? undefined,
@@ -1,6 +1,13 @@
1
1
  export class DeviceWalletSessionStore {
2
+ private static readonly MAX_SESSIONS_PER_DEVICE = 3;
3
+
2
4
  private readonly walletSessions = new Map<string, Map<string, string>>();
3
5
 
6
+ private readonly standardWalletSessions = new Map<
7
+ string,
8
+ { passphraseState: string; sessionId: string }
9
+ >();
10
+
4
11
  private readonly pendingSessions = new Map<string, string>();
5
12
 
6
13
  get(deviceKey: string, passphraseState?: string) {
@@ -15,9 +22,32 @@ export class DeviceWalletSessionStore {
15
22
  deviceSessions = new Map<string, string>();
16
23
  this.walletSessions.set(deviceKey, deviceSessions);
17
24
  }
25
+ if (
26
+ !deviceSessions.has(passphraseState) &&
27
+ deviceSessions.size >= DeviceWalletSessionStore.MAX_SESSIONS_PER_DEVICE
28
+ ) {
29
+ const oldestPassphraseState = deviceSessions.keys().next().value as string | undefined;
30
+ if (oldestPassphraseState) {
31
+ deviceSessions.delete(oldestPassphraseState);
32
+ if (this.standardWalletSessions.get(deviceKey)?.passphraseState === oldestPassphraseState) {
33
+ this.standardWalletSessions.delete(deviceKey);
34
+ }
35
+ }
36
+ }
18
37
  deviceSessions.set(passphraseState, sessionId);
19
38
  }
20
39
 
40
+ getStandard(deviceKey: string) {
41
+ if (!deviceKey) return undefined;
42
+ return this.standardWalletSessions.get(deviceKey);
43
+ }
44
+
45
+ setStandard(deviceKey: string, passphraseState?: string, sessionId?: string) {
46
+ if (!deviceKey || !passphraseState || !sessionId) return;
47
+ this.set(deviceKey, passphraseState, sessionId);
48
+ this.standardWalletSessions.set(deviceKey, { passphraseState, sessionId });
49
+ }
50
+
21
51
  setPending(deviceKey: string, sessionId?: string) {
22
52
  if (!deviceKey || !sessionId) return;
23
53
  this.pendingSessions.set(deviceKey, sessionId);
@@ -30,6 +60,10 @@ export class DeviceWalletSessionStore {
30
60
 
31
61
  delete(deviceKey: string, passphraseState?: string) {
32
62
  if (!deviceKey || !passphraseState) return;
63
+ const standardWalletSession = this.standardWalletSessions.get(deviceKey);
64
+ if (standardWalletSession?.passphraseState === passphraseState) {
65
+ this.standardWalletSessions.delete(deviceKey);
66
+ }
33
67
  const deviceSessions = this.walletSessions.get(deviceKey);
34
68
  if (!deviceSessions) return;
35
69
  deviceSessions.delete(passphraseState);
@@ -38,6 +72,15 @@ export class DeviceWalletSessionStore {
38
72
  }
39
73
  }
40
74
 
75
+ deleteStandard(deviceKey: string) {
76
+ if (!deviceKey) return;
77
+ const standardWalletSession = this.standardWalletSessions.get(deviceKey);
78
+ this.standardWalletSessions.delete(deviceKey);
79
+ if (standardWalletSession) {
80
+ this.delete(deviceKey, standardWalletSession.passphraseState);
81
+ }
82
+ }
83
+
41
84
  deletePending(deviceKey: string) {
42
85
  if (!deviceKey) return;
43
86
  this.pendingSessions.delete(deviceKey);
@@ -46,6 +89,7 @@ export class DeviceWalletSessionStore {
46
89
  deleteDevice(deviceKey: string) {
47
90
  if (!deviceKey) return;
48
91
  this.walletSessions.delete(deviceKey);
92
+ this.standardWalletSessions.delete(deviceKey);
49
93
  this.pendingSessions.delete(deviceKey);
50
94
  }
51
95
 
@@ -54,16 +98,24 @@ export class DeviceWalletSessionStore {
54
98
 
55
99
  const sourceSessions = this.walletSessions.get(from);
56
100
  if (sourceSessions) {
57
- const targetSessions = this.walletSessions.get(to) ?? new Map<string, string>();
58
- this.walletSessions.set(to, targetSessions);
59
101
  sourceSessions.forEach((sessionId, passphraseState) => {
60
- if (!targetSessions.has(passphraseState)) {
61
- targetSessions.set(passphraseState, sessionId);
102
+ if (!this.get(to, passphraseState)) {
103
+ this.set(to, passphraseState, sessionId);
62
104
  }
63
105
  });
64
106
  this.walletSessions.delete(from);
65
107
  }
66
108
 
109
+ const standardWalletSession = this.standardWalletSessions.get(from);
110
+ if (
111
+ standardWalletSession &&
112
+ !this.standardWalletSessions.has(to) &&
113
+ this.get(to, standardWalletSession.passphraseState) === standardWalletSession.sessionId
114
+ ) {
115
+ this.standardWalletSessions.set(to, standardWalletSession);
116
+ }
117
+ this.standardWalletSessions.delete(from);
118
+
67
119
  const pendingSession = this.pendingSessions.get(from);
68
120
  if (pendingSession && !this.pendingSessions.has(to)) {
69
121
  this.pendingSessions.set(to, pendingSession);
@@ -98,6 +150,7 @@ export class DeviceWalletSessionStore {
98
150
 
99
151
  clear() {
100
152
  this.walletSessions.clear();
153
+ this.standardWalletSessions.clear();
101
154
  this.pendingSessions.clear();
102
155
  }
103
156
  }
@@ -1,4 +1,4 @@
1
- import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
1
+ import { EFirmwareType } from '@onekeyfe/hd-shared';
2
2
 
3
3
  import {
4
4
  resolveDeviceBleFirmwareVersion,
@@ -11,6 +11,7 @@ import {
11
11
  resolveDeviceType,
12
12
  } from '../utils/deviceFeaturesCompat';
13
13
  import { normalizeSafetyCheckLevel } from '../utils/deviceSettings';
14
+ import { resolveProtocolV2DeviceIdentity } from './protocolV2DeviceIdentity';
14
15
 
15
16
  import type {
16
17
  DeviceFirmwareImageInfo,
@@ -224,6 +225,12 @@ export const buildProtocolV2FeaturesPayload = ({
224
225
  const samePhysicalDevice =
225
226
  !incomingSerialNo || !previous?.serialNo || incomingSerialNo === previous.serialNo;
226
227
  const cached = samePhysicalDevice ? previous : undefined;
228
+ const incomingDeviceType = info?.hw?.Device_type;
229
+ const resolvedIdentity = resolveProtocolV2DeviceIdentity(incomingDeviceType);
230
+ const cachedIdentity =
231
+ incomingDeviceType === undefined || incomingDeviceType === null ? cached : undefined;
232
+ const deviceType = cachedIdentity ? cachedIdentity.deviceType : resolvedIdentity.deviceType;
233
+ const model = cachedIdentity ? cachedIdentity.model : resolvedIdentity.model;
227
234
 
228
235
  const firmwareVersion = firstMeaningfulVersion(
229
236
  getImageVersion(fwApplication),
@@ -270,9 +277,9 @@ export const buildProtocolV2FeaturesPayload = ({
270
277
  return {
271
278
  protocol: 'V2',
272
279
  protocolVersion: deviceInfo?.protocol_version ?? cached?.protocolVersion ?? null,
273
- deviceType: EDeviceType.Pro2,
280
+ deviceType,
274
281
  firmwareType: cached?.firmwareType ?? EFirmwareType.Universal,
275
- model: 'pro2',
282
+ model,
276
283
  vendor: 'onekey.so',
277
284
  deviceId,
278
285
  serialNo,
@@ -0,0 +1,32 @@
1
+ import { EDeviceType } from '@onekeyfe/hd-shared';
2
+ import { DeviceType } from '@onekeyfe/hd-transport';
3
+
4
+ export const resolveProtocolV2DeviceIdentity = (
5
+ deviceType?: DeviceType | keyof typeof DeviceType | null
6
+ ): { deviceType: EDeviceType; model: string | null } => {
7
+ switch (deviceType) {
8
+ case DeviceType.CLASSIC1:
9
+ case 'CLASSIC1':
10
+ return { deviceType: EDeviceType.Classic, model: 'classic' };
11
+ case DeviceType.CLASSIC1S:
12
+ case 'CLASSIC1S':
13
+ return { deviceType: EDeviceType.Classic1s, model: 'classic1s' };
14
+ case DeviceType.CLASSIC1S_PURE:
15
+ case 'CLASSIC1S_PURE':
16
+ return { deviceType: EDeviceType.ClassicPure, model: 'classicpure' };
17
+ case DeviceType.MINI:
18
+ case 'MINI':
19
+ return { deviceType: EDeviceType.Mini, model: 'mini' };
20
+ case DeviceType.TOUCH:
21
+ case 'TOUCH':
22
+ return { deviceType: EDeviceType.Touch, model: 'touch' };
23
+ case DeviceType.PRO:
24
+ case 'PRO':
25
+ return { deviceType: EDeviceType.Pro, model: 'pro' };
26
+ case DeviceType.PRO2:
27
+ case 'PRO2':
28
+ return { deviceType: EDeviceType.Pro2, model: 'pro2' };
29
+ default:
30
+ return { deviceType: EDeviceType.Unknown, model: null };
31
+ }
32
+ };
@@ -1,10 +1,68 @@
1
+ import { UI_REQUEST } from './ui-request';
1
2
  import { UI_RESPONSE } from './ui-response';
2
3
 
3
4
  export const LogBlockEvent: Set<string> = new Set([
5
+ UI_REQUEST.REQUEST_PASSPHRASE,
6
+ UI_REQUEST.REQUEST_PASSPHRASE_ON_DEVICE,
4
7
  UI_RESPONSE.RECEIVE_PIN,
5
8
  UI_RESPONSE.RECEIVE_PASSPHRASE,
6
9
  ]);
7
10
 
11
+ const LogLabelMethod: Set<string> = new Set([
12
+ 'openWalletSession',
13
+ 'deviceUploadWallpaper',
14
+ 'uploadPortfolio',
15
+ 'fileWrite',
16
+ 'fileRead',
17
+ ]);
18
+
19
+ const SensitiveLogKeys: Set<string> = new Set([
20
+ 'devicestate',
21
+ 'entropy',
22
+ 'expectedpassphrasestate',
23
+ 'mnemonic',
24
+ 'passphrase',
25
+ 'passphrasestate',
26
+ 'password',
27
+ 'pin',
28
+ 'privatekey',
29
+ 'seed',
30
+ 'session',
31
+ 'sessionid',
32
+ 'walletsessionid',
33
+ 'xprv',
34
+ ]);
35
+
36
+ const normalizeLogKey = (key: string) => key.replace(/[_-]/g, '').toLowerCase();
37
+
38
+ const isSigningMethod = (methodName: string) => /sign/i.test(methodName);
39
+
40
+ const redactLogValue = (value: unknown, seen: WeakSet<object>): unknown => {
41
+ if (ArrayBuffer.isView(value)) {
42
+ return `[BINARY:${value.byteLength}]`;
43
+ }
44
+ if (value instanceof ArrayBuffer) {
45
+ return `[BINARY:${value.byteLength}]`;
46
+ }
47
+ if (Array.isArray(value)) {
48
+ return value.map(item => redactLogValue(item, seen));
49
+ }
50
+ if (!value || typeof value !== 'object') return value;
51
+ if (seen.has(value)) return '[CIRCULAR]';
52
+
53
+ seen.add(value);
54
+ const redacted = Object.fromEntries(
55
+ Object.entries(value as Record<string, unknown>).map(([key, item]) => [
56
+ key,
57
+ SensitiveLogKeys.has(normalizeLogKey(key)) && item !== null && item !== undefined
58
+ ? '[REDACTED]'
59
+ : redactLogValue(item, seen),
60
+ ])
61
+ );
62
+ seen.delete(value);
63
+ return redacted;
64
+ };
65
+
8
66
  export function getLogBlockLabel(message: unknown): string | undefined {
9
67
  if (!message || typeof message !== 'object') return undefined;
10
68
 
@@ -19,5 +77,30 @@ export function getLogBlockLabel(message: unknown): string | undefined {
19
77
  }
20
78
 
21
79
  const methodName = method ?? payload?.method;
22
- return methodName;
80
+ if (methodName && (LogLabelMethod.has(methodName) || isSigningMethod(methodName))) {
81
+ return methodName;
82
+ }
83
+
84
+ return undefined;
85
+ }
86
+
87
+ export function getSafeLogPayload(value: unknown, blockLabel?: string): unknown {
88
+ if (blockLabel && (LogBlockEvent.has(blockLabel) || isSigningMethod(blockLabel))) {
89
+ return { method: blockLabel, payload: '[REDACTED]' };
90
+ }
91
+
92
+ const redactedValue = redactLogValue(value, new WeakSet());
93
+ if (
94
+ blockLabel &&
95
+ redactedValue &&
96
+ typeof redactedValue === 'object' &&
97
+ !Array.isArray(redactedValue)
98
+ ) {
99
+ return { ...redactedValue, method: blockLabel };
100
+ }
101
+ return redactedValue;
102
+ }
103
+
104
+ export function formatLogMethodLabel(label: string, methodName?: string): string {
105
+ return methodName ? `${label} [${methodName}]` : label;
23
106
  }
@@ -18,7 +18,7 @@ export function assertCompleteDeviceSession(
18
18
  ) {
19
19
  throw ERRORS.TypedError(
20
20
  HardwareErrorCode.RuntimeError,
21
- 'DeviceSessionGet returned an incomplete DeviceSession response.'
21
+ 'Device returned an incomplete DeviceSession response.'
22
22
  );
23
23
  }
24
24
  }