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

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 (141) hide show
  1. package/__tests__/device-features-state.test.ts +78 -0
  2. package/__tests__/device-identity.test.ts +30 -0
  3. package/__tests__/device-pool-state.test.ts +113 -0
  4. package/__tests__/device-settings.test.ts +108 -0
  5. package/__tests__/device-state-contract.test.ts +27 -0
  6. package/__tests__/device-state-events.test.ts +163 -0
  7. package/__tests__/device-state-mapper.test.ts +222 -0
  8. package/__tests__/device-state-projector.test.ts +94 -0
  9. package/__tests__/device-state-store.test.ts +115 -0
  10. package/__tests__/device-wallet-session-store.test.ts +46 -0
  11. package/__tests__/deviceFeaturesUtils.test.ts +13 -1
  12. package/__tests__/deviceSettings.test.ts +54 -0
  13. package/__tests__/get-device-state.test.ts +367 -0
  14. package/__tests__/logBlockEvent.test.ts +13 -0
  15. package/__tests__/protocol-v2.test.ts +358 -413
  16. package/__tests__/public-device-state-api.test.ts +153 -0
  17. package/__tests__/refresh-device-state.test.ts +101 -0
  18. package/__tests__/request-context-logging.test.ts +16 -0
  19. package/__tests__/search-devices.test.ts +93 -0
  20. package/dist/api/ClearSessionCache.d.ts.map +1 -1
  21. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  22. package/dist/api/GetDeviceState.d.ts +7 -0
  23. package/dist/api/GetDeviceState.d.ts.map +1 -0
  24. package/dist/api/GetFeatures.d.ts +1 -1
  25. package/dist/api/GetFeatures.d.ts.map +1 -1
  26. package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
  27. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  28. package/dist/api/SearchDevices.d.ts.map +1 -1
  29. package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
  30. package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
  31. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  32. package/dist/api/index.d.ts +1 -4
  33. package/dist/api/index.d.ts.map +1 -1
  34. package/dist/api/internal.d.ts +4 -0
  35. package/dist/api/internal.d.ts.map +1 -0
  36. package/dist/api/protocol-v2/DeviceInfoGet.d.ts +0 -1
  37. package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
  38. package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
  39. package/dist/api/protocol-v2/DeviceSettingsGet.d.ts.map +1 -1
  40. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +1 -1
  41. package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -1
  42. package/dist/api/utils.d.ts +2 -1
  43. package/dist/api/utils.d.ts.map +1 -1
  44. package/dist/constants/index.d.ts +1 -1
  45. package/dist/constants/index.d.ts.map +1 -1
  46. package/dist/core/index.d.ts.map +1 -1
  47. package/dist/device/Device.d.ts +22 -3
  48. package/dist/device/Device.d.ts.map +1 -1
  49. package/dist/device/DeviceFeaturesState.d.ts +4 -0
  50. package/dist/device/DeviceFeaturesState.d.ts.map +1 -0
  51. package/dist/device/DevicePool.d.ts +1 -0
  52. package/dist/device/DevicePool.d.ts.map +1 -1
  53. package/dist/device/DeviceStateMapper.d.ts +13 -0
  54. package/dist/device/DeviceStateMapper.d.ts.map +1 -0
  55. package/dist/device/DeviceStateProjector.d.ts +8 -0
  56. package/dist/device/DeviceStateProjector.d.ts.map +1 -0
  57. package/dist/device/DeviceStateStore.d.ts +23 -0
  58. package/dist/device/DeviceStateStore.d.ts.map +1 -0
  59. package/dist/device/deviceIdentity.d.ts +5 -0
  60. package/dist/device/deviceIdentity.d.ts.map +1 -0
  61. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  62. package/dist/deviceProfile/index.d.ts +0 -1
  63. package/dist/deviceProfile/index.d.ts.map +1 -1
  64. package/dist/events/device.d.ts +7 -2
  65. package/dist/events/device.d.ts.map +1 -1
  66. package/dist/index.d.ts +688 -639
  67. package/dist/index.js +1274 -942
  68. package/dist/inject.d.ts.map +1 -1
  69. package/dist/protocols/protocol-v2/features.d.ts +1 -0
  70. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  71. package/dist/protocols/protocol-v2/index.d.ts +1 -1
  72. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  73. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  74. package/dist/types/api/deviceSettings.d.ts +8 -0
  75. package/dist/types/api/deviceSettings.d.ts.map +1 -1
  76. package/dist/types/api/getDeviceState.d.ts +12 -0
  77. package/dist/types/api/getDeviceState.d.ts.map +1 -0
  78. package/dist/types/api/index.d.ts +4 -7
  79. package/dist/types/api/index.d.ts.map +1 -1
  80. package/dist/types/api/protocolV2.d.ts +1 -6
  81. package/dist/types/api/protocolV2.d.ts.map +1 -1
  82. package/dist/types/device.d.ts +121 -1
  83. package/dist/types/device.d.ts.map +1 -1
  84. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  85. package/dist/utils/deviceSettings.d.ts.map +1 -1
  86. package/dist/utils/patch.d.ts +1 -1
  87. package/dist/utils/tracing.d.ts.map +1 -1
  88. package/package.json +4 -4
  89. package/src/api/ClearSessionCache.ts +4 -0
  90. package/src/api/FirmwareUpdateV4.ts +15 -7
  91. package/src/api/GetDeviceState.ts +55 -0
  92. package/src/api/GetFeatures.ts +8 -3
  93. package/src/api/GetOnekeyFeatures.ts +3 -72
  94. package/src/api/GetPassphraseState.ts +3 -1
  95. package/src/api/SearchDevices.ts +25 -4
  96. package/src/api/device/DeviceRebootToBoardloader.ts +1 -0
  97. package/src/api/device/DeviceRebootToBootloader.ts +1 -0
  98. package/src/api/device/DeviceSettings.ts +28 -0
  99. package/src/api/index.ts +1 -4
  100. package/src/api/internal.ts +9 -0
  101. package/src/api/protocol-v2/DeviceInfoGet.ts +3 -5
  102. package/src/api/protocol-v2/DeviceReboot.ts +3 -1
  103. package/src/api/protocol-v2/DeviceSettingsGet.ts +3 -1
  104. package/src/api/protocol-v2/DeviceSettingsSet.ts +2 -0
  105. package/src/api/protocol-v2/DeviceStatusGet.ts +1 -0
  106. package/src/api/utils.ts +9 -3
  107. package/src/constants/index.ts +1 -4
  108. package/src/core/index.ts +7 -1
  109. package/src/data/messages/messages-protocol-v2.json +22 -13
  110. package/src/device/Device.ts +256 -78
  111. package/src/device/DeviceFeaturesState.ts +34 -0
  112. package/src/device/DevicePool.ts +35 -9
  113. package/src/device/DeviceStateMapper.ts +364 -0
  114. package/src/device/DeviceStateProjector.ts +92 -0
  115. package/src/device/DeviceStateStore.ts +242 -0
  116. package/src/device/deviceIdentity.ts +18 -0
  117. package/src/deviceProfile/buildDeviceFeatures.ts +166 -109
  118. package/src/deviceProfile/index.ts +0 -1
  119. package/src/events/device.ts +13 -1
  120. package/src/events/logBlockEvent.ts +1 -1
  121. package/src/inject.ts +4 -5
  122. package/src/protocols/protocol-v2/features.ts +18 -9
  123. package/src/protocols/protocol-v2/index.ts +1 -0
  124. package/src/protocols/protocol-v2/walletSession.ts +5 -7
  125. package/src/types/api/deviceSettings.ts +8 -0
  126. package/src/types/api/getDeviceState.ts +19 -0
  127. package/src/types/api/index.ts +3 -20
  128. package/src/types/api/protocolV2.ts +0 -24
  129. package/src/types/device.ts +148 -1
  130. package/src/utils/deviceFeaturesUtils.ts +5 -1
  131. package/src/utils/deviceSettings.ts +41 -0
  132. package/src/utils/tracing.ts +3 -1
  133. package/dist/api/GetDeviceInfo.d.ts +0 -9
  134. package/dist/api/GetDeviceInfo.d.ts.map +0 -1
  135. package/dist/deviceProfile/buildDeviceProfile.d.ts +0 -22
  136. package/dist/deviceProfile/buildDeviceProfile.d.ts.map +0 -1
  137. package/dist/types/api/getDeviceInfo.d.ts +0 -88
  138. package/dist/types/api/getDeviceInfo.d.ts.map +0 -1
  139. package/src/api/GetDeviceInfo.ts +0 -152
  140. package/src/deviceProfile/buildDeviceProfile.ts +0 -370
  141. package/src/types/api/getDeviceInfo.ts +0 -106
@@ -0,0 +1,364 @@
1
+ import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
+
3
+ import { buildProtocolV1FeaturesPayload } from '../deviceProfile/buildDeviceFeatures';
4
+ import {
5
+ isProtocolV2BootloaderDeviceInfo,
6
+ isProtocolV2RomloaderDeviceInfo,
7
+ } from '../protocols/protocol-v2/features';
8
+
9
+ import type { PROTO } from '../constants';
10
+ import type {
11
+ DeviceFeaturesVerify,
12
+ DeviceStateMode,
13
+ DeviceStatePatch,
14
+ Features,
15
+ OnekeyFeatures,
16
+ } from '../types';
17
+ import type { DeviceSettingsParams } from '../types/api/deviceSettings';
18
+ import type {
19
+ ApplySettings,
20
+ DeviceFirmwareImageInfo,
21
+ DeviceSettings,
22
+ DeviceStatus,
23
+ ProtocolV2DeviceInfo,
24
+ } from '@onekeyfe/hd-transport';
25
+
26
+ const definedEntries = <T extends Record<string, unknown>>(value: T): T =>
27
+ Object.fromEntries(
28
+ Object.entries(value).filter(([, fieldValue]) => fieldValue !== undefined)
29
+ ) as T;
30
+
31
+ const imageVersion = (image?: DeviceFirmwareImageInfo | null) => image?.version ?? undefined;
32
+
33
+ const bytesToHex = (value: unknown): string | undefined => {
34
+ if (!value) return undefined;
35
+ if (typeof value === 'string') return value;
36
+ if (value instanceof Uint8Array || Array.isArray(value)) {
37
+ return Array.from(value)
38
+ .map(byte => Number(byte).toString(16).padStart(2, '0'))
39
+ .join('');
40
+ }
41
+ return undefined;
42
+ };
43
+
44
+ const imageBuildId = (image?: DeviceFirmwareImageInfo | null) => image?.build_id ?? undefined;
45
+ const imageHash = (image?: DeviceFirmwareImageInfo | null) => bytesToHex(image?.hash);
46
+
47
+ const getFeaturesMode = (features: Features): DeviceStateMode | undefined => {
48
+ if (features.mode !== undefined && features.mode !== null) return features.mode;
49
+ if (features.bootloaderMode === true) return 'bootloader';
50
+ if (features.initialized === false) return 'notInitialized';
51
+ if (features.initialized === true) return 'normal';
52
+ return undefined;
53
+ };
54
+
55
+ const getProtocolV2StatusMode = (initialized?: boolean | null): DeviceStateMode | undefined => {
56
+ if (initialized === false) return 'notInitialized';
57
+ if (initialized === true) return 'normal';
58
+ return undefined;
59
+ };
60
+
61
+ export const mapFeaturesToState = (features: Features): DeviceStatePatch => ({
62
+ protocol: features.protocol,
63
+ protocolVersion: features.protocolVersion,
64
+ identity: {
65
+ deviceType: features.deviceType,
66
+ firmwareType: features.firmwareType,
67
+ model: features.model,
68
+ vendor: features.vendor,
69
+ deviceId: features.deviceId,
70
+ serialNo: features.serialNo,
71
+ label: features.label,
72
+ bleName: features.bleName,
73
+ },
74
+ status: {
75
+ mode: getFeaturesMode(features),
76
+ initialized: features.initialized,
77
+ unlocked: features.unlocked,
78
+ firmwarePresent: features.firmwarePresent,
79
+ backupRequired: features.backupRequired,
80
+ noBackup: features.noBackup,
81
+ unfinishedBackup: features.unfinishedBackup,
82
+ recoveryMode: features.recoveryMode,
83
+ passphraseProtection: features.passphraseProtection,
84
+ pinProtection: features.pinProtection,
85
+ attachToPinEnabled: features.attachToPinEnabled ?? null,
86
+ unlockedAttachPin: features.unlockedAttachPin ?? null,
87
+ },
88
+ settings: {
89
+ language: features.language,
90
+ bleEnabled: features.bleEnabled,
91
+ sdCardPresent: features.sdCardPresent,
92
+ sdProtection: features.sdProtection,
93
+ wipeCodeProtection: features.wipeCodeProtection,
94
+ passphraseAlwaysOnDevice: features.passphraseAlwaysOnDevice,
95
+ safetyChecks: features.safetyChecks,
96
+ autoLockDelayMs: features.autoLockDelayMs,
97
+ autoShutdownDelayMs: features.autoShutdownDelayMs,
98
+ displayRotation: features.displayRotation,
99
+ experimentalFeatures: features.experimentalFeatures,
100
+ wallpaperPath: features.wallpaperPath,
101
+ brightness: features.brightness,
102
+ animationEnabled: features.animationEnabled,
103
+ tapToWake: features.tapToWake,
104
+ hapticFeedback: features.hapticFeedback,
105
+ deviceNameDisplayEnabled: features.deviceNameDisplayEnabled,
106
+ airgapMode: features.airgapMode,
107
+ fidoEnabled: features.fidoEnabled,
108
+ usbLockEnabled: features.usbLockEnabled,
109
+ randomKeypad: features.randomKeypad,
110
+ },
111
+ versions: {
112
+ firmware: features.firmwareVersion,
113
+ bootloader: features.bootloaderVersion,
114
+ board: features.boardVersion,
115
+ ble: features.bleVersion,
116
+ se: features.seVersion,
117
+ se01: features.se01Version,
118
+ se02: features.se02Version,
119
+ se03: features.se03Version,
120
+ se04: features.se04Version,
121
+ se01Boot: features.se01BootVersion,
122
+ se02Boot: features.se02BootVersion,
123
+ se03Boot: features.se03BootVersion,
124
+ se04Boot: features.se04BootVersion,
125
+ },
126
+ capabilities: features.capabilities,
127
+ verification: definedEntries(features.verify ?? {}),
128
+ session: {
129
+ sessionId: features.sessionId,
130
+ passphraseState: features.passphraseState,
131
+ },
132
+ raw: features.raw,
133
+ });
134
+
135
+ export const mapProtocolV1FeaturesToState = (
136
+ protocolV1Features: PROTO.Features
137
+ ): DeviceStatePatch => mapFeaturesToState(buildProtocolV1FeaturesPayload(protocolV1Features));
138
+
139
+ const meaningfulVersion = (version?: string | null) => {
140
+ if (!version || version === '0.0.0') return undefined;
141
+ const parts = version.split('.');
142
+ while (parts.length < 3) parts.push('0');
143
+ return parts.map(part => (Number.isNaN(Number.parseInt(part, 10)) ? '0' : part)).join('.');
144
+ };
145
+
146
+ export const mapProtocolV1OnekeyFeaturesToState = (features: OnekeyFeatures): DeviceStatePatch => {
147
+ const verification = definedEntries<DeviceFeaturesVerify>({
148
+ firmwareBuildId: features.onekey_firmware_build_id,
149
+ firmwareHash: features.onekey_firmware_hash,
150
+ bootloaderBuildId: features.onekey_boot_build_id,
151
+ bootloaderHash: features.onekey_boot_hash,
152
+ boardBuildId: features.onekey_board_build_id,
153
+ boardHash: features.onekey_board_hash,
154
+ bleBuildId: features.onekey_ble_build_id,
155
+ bleHash: features.onekey_ble_hash,
156
+ se01BuildId: features.onekey_se01_build_id,
157
+ se01Hash: features.onekey_se01_hash,
158
+ se02BuildId: features.onekey_se02_build_id,
159
+ se02Hash: features.onekey_se02_hash,
160
+ se03BuildId: features.onekey_se03_build_id,
161
+ se03Hash: features.onekey_se03_hash,
162
+ se04BuildId: features.onekey_se04_build_id,
163
+ se04Hash: features.onekey_se04_hash,
164
+ se01BootBuildId: features.onekey_se01_boot_build_id,
165
+ se01BootHash: features.onekey_se01_boot_hash,
166
+ se02BootBuildId: features.onekey_se02_boot_build_id,
167
+ se02BootHash: features.onekey_se02_boot_hash,
168
+ se03BootBuildId: features.onekey_se03_boot_build_id,
169
+ se03BootHash: features.onekey_se03_boot_hash,
170
+ se04BootBuildId: features.onekey_se04_boot_build_id,
171
+ se04BootHash: features.onekey_se04_boot_hash,
172
+ });
173
+
174
+ return {
175
+ versions: definedEntries({
176
+ firmware: meaningfulVersion(features.onekey_firmware_version),
177
+ bootloader: meaningfulVersion(features.onekey_boot_version),
178
+ board: meaningfulVersion(features.onekey_board_version),
179
+ ble: meaningfulVersion(features.onekey_ble_version),
180
+ se01: meaningfulVersion(features.onekey_se01_version),
181
+ se02: meaningfulVersion(features.onekey_se02_version),
182
+ se03: meaningfulVersion(features.onekey_se03_version),
183
+ se04: meaningfulVersion(features.onekey_se04_version),
184
+ se01Boot: meaningfulVersion(features.onekey_se01_boot_version),
185
+ se02Boot: meaningfulVersion(features.onekey_se02_boot_version),
186
+ se03Boot: meaningfulVersion(features.onekey_se03_boot_version),
187
+ se04Boot: meaningfulVersion(features.onekey_se04_boot_version),
188
+ }),
189
+ ...(Object.keys(verification).length > 0 ? { verification } : {}),
190
+ raw: { protocolV1OneKeyFeatures: features },
191
+ };
192
+ };
193
+
194
+ export const mapProtocolV2DeviceInfoToState = (
195
+ info: ProtocolV2DeviceInfo,
196
+ currentMode?: DeviceStateMode
197
+ ): DeviceStatePatch => {
198
+ const romloader = isProtocolV2RomloaderDeviceInfo(info);
199
+ const bootloader = isProtocolV2BootloaderDeviceInfo(info);
200
+ const loader = romloader || bootloader;
201
+ let mode: DeviceStateMode = 'normal';
202
+ if (romloader) {
203
+ mode = 'romloader';
204
+ } else if (bootloader) {
205
+ mode = 'bootloader';
206
+ } else if (currentMode === 'notInitialized' || currentMode === 'backupMode') {
207
+ mode = currentMode;
208
+ }
209
+
210
+ return {
211
+ protocol: 'V2',
212
+ protocolVersion: info.protocol_version,
213
+ identity: definedEntries({
214
+ deviceType: EDeviceType.Pro2,
215
+ firmwareType: EFirmwareType.Universal,
216
+ model: 'pro2',
217
+ vendor: 'onekey.so',
218
+ serialNo: info.hw?.serial_no,
219
+ bleName: info.coprocessor?.bt_adv_name,
220
+ deviceId: loader ? null : undefined,
221
+ }),
222
+ status: loader
223
+ ? {
224
+ mode,
225
+ initialized: null,
226
+ unlocked: null,
227
+ firmwarePresent: null,
228
+ backupRequired: null,
229
+ noBackup: null,
230
+ unfinishedBackup: null,
231
+ recoveryMode: null,
232
+ passphraseProtection: null,
233
+ pinProtection: null,
234
+ attachToPinEnabled: null,
235
+ unlockedAttachPin: null,
236
+ }
237
+ : { mode },
238
+ versions: definedEntries({
239
+ firmware: imageVersion(info.fw?.application),
240
+ bootloader: imageVersion(info.fw?.bootloader),
241
+ board: imageVersion(info.fw?.romloader),
242
+ ble: imageVersion(info.coprocessor?.application),
243
+ se01: imageVersion(info.se1?.application),
244
+ se02: imageVersion(info.se2?.application),
245
+ se03: imageVersion(info.se3?.application),
246
+ se04: imageVersion(info.se4?.application),
247
+ se01Boot: imageVersion(info.se1?.bootloader),
248
+ se02Boot: imageVersion(info.se2?.bootloader),
249
+ se03Boot: imageVersion(info.se3?.bootloader),
250
+ se04Boot: imageVersion(info.se4?.bootloader),
251
+ }),
252
+ verification: definedEntries({
253
+ firmwareBuildId: imageBuildId(info.fw?.application),
254
+ firmwareHash: imageHash(info.fw?.application),
255
+ bootloaderBuildId: imageBuildId(info.fw?.bootloader),
256
+ bootloaderHash: imageHash(info.fw?.bootloader),
257
+ boardBuildId: imageBuildId(info.fw?.romloader),
258
+ boardHash: imageHash(info.fw?.romloader),
259
+ bleBuildId: imageBuildId(info.coprocessor?.application),
260
+ bleHash: imageHash(info.coprocessor?.application),
261
+ se01BuildId: imageBuildId(info.se1?.application),
262
+ se01Hash: imageHash(info.se1?.application),
263
+ se02BuildId: imageBuildId(info.se2?.application),
264
+ se02Hash: imageHash(info.se2?.application),
265
+ se03BuildId: imageBuildId(info.se3?.application),
266
+ se03Hash: imageHash(info.se3?.application),
267
+ se04BuildId: imageBuildId(info.se4?.application),
268
+ se04Hash: imageHash(info.se4?.application),
269
+ se01BootBuildId: imageBuildId(info.se1?.bootloader),
270
+ se01BootHash: imageHash(info.se1?.bootloader),
271
+ se02BootBuildId: imageBuildId(info.se2?.bootloader),
272
+ se02BootHash: imageHash(info.se2?.bootloader),
273
+ se03BootBuildId: imageBuildId(info.se3?.bootloader),
274
+ se03BootHash: imageHash(info.se3?.bootloader),
275
+ se04BootBuildId: imageBuildId(info.se4?.bootloader),
276
+ se04BootHash: imageHash(info.se4?.bootloader),
277
+ }),
278
+ raw: {
279
+ protocolV2DeviceInfo: info,
280
+ ...(loader ? { protocolV2DeviceStatus: null } : {}),
281
+ },
282
+ };
283
+ };
284
+
285
+ export const mapProtocolV2DeviceStatusToState = (status: DeviceStatus): DeviceStatePatch => ({
286
+ identity: definedEntries({ deviceId: status.device_id }),
287
+ status: definedEntries({
288
+ mode: getProtocolV2StatusMode(status.init_states),
289
+ initialized: status.init_states,
290
+ unlocked: status.unlocked,
291
+ passphraseProtection: status.unlocked === true ? status.passphrase_enabled ?? null : undefined,
292
+ backupRequired: status.backup_required,
293
+ attachToPinEnabled: status.attach_to_pin_enabled,
294
+ unlockedAttachPin: status.unlocked_by_attach_to_pin,
295
+ }),
296
+ raw: { protocolV2DeviceStatus: status },
297
+ });
298
+
299
+ export const mapApplySettingsToState = (settings: ApplySettings): DeviceStatePatch => {
300
+ const identity = definedEntries({ label: settings.label });
301
+ const status = definedEntries({ passphraseProtection: settings.use_passphrase });
302
+ const stateSettings = definedEntries({
303
+ language: settings.language,
304
+ autoLockDelayMs: settings.auto_lock_delay_ms,
305
+ autoShutdownDelayMs: settings.auto_shutdown_delay_ms,
306
+ displayRotation: settings.display_rotation,
307
+ passphraseAlwaysOnDevice: settings.passphrase_always_on_device,
308
+ safetyChecks: settings.safety_checks,
309
+ experimentalFeatures: settings.experimental_features,
310
+ hapticFeedback: settings.haptic_feedback,
311
+ });
312
+ return {
313
+ ...(Object.keys(identity).length ? { identity } : {}),
314
+ ...(Object.keys(status).length ? { status } : {}),
315
+ ...(Object.keys(stateSettings).length ? { settings: stateSettings } : {}),
316
+ };
317
+ };
318
+
319
+ export const mapDeviceSettingsToState = (settings: DeviceSettings): DeviceStatePatch => {
320
+ const settingsWithExperimental = settings as DeviceSettings & {
321
+ experimental_features?: boolean;
322
+ };
323
+ const identity = definedEntries({ label: settings.label });
324
+ const status = definedEntries({ passphraseProtection: settings.passphrase_enable });
325
+ const stateSettings = definedEntries({
326
+ bleEnabled: settings.bt_enable,
327
+ language: settings.language,
328
+ wallpaperPath: settings.wallpaper_path,
329
+ brightness: settings.brightness,
330
+ autoLockDelayMs: settings.autolock_delay_ms,
331
+ autoShutdownDelayMs: settings.autoshutdown_delay_ms,
332
+ animationEnabled: settings.animation_enable,
333
+ tapToWake: settings.tap_to_wake,
334
+ hapticFeedback: settings.haptic_feedback,
335
+ deviceNameDisplayEnabled: settings.device_name_display_enabled,
336
+ airgapMode: settings.airgap_mode,
337
+ fidoEnabled: settings.fido_enabled,
338
+ experimentalFeatures: settingsWithExperimental.experimental_features,
339
+ usbLockEnabled: settings.usb_lock_enable,
340
+ randomKeypad: settings.random_keypad,
341
+ });
342
+ return {
343
+ ...(Object.keys(identity).length ? { identity } : {}),
344
+ ...(Object.keys(status).length ? { status } : {}),
345
+ ...(Object.keys(stateSettings).length ? { settings: stateSettings } : {}),
346
+ };
347
+ };
348
+
349
+ export const mapCommonSettingsToProtocolV2 = (settings: DeviceSettingsParams): DeviceSettings =>
350
+ definedEntries({
351
+ label: settings.label,
352
+ bt_enable: settings.bluetoothEnabled,
353
+ language: settings.language,
354
+ brightness: settings.brightness,
355
+ autolock_delay_ms: settings.autoLockDelayMs,
356
+ autoshutdown_delay_ms: settings.autoShutdownDelayMs,
357
+ animation_enable: settings.animationEnabled,
358
+ tap_to_wake: settings.tapToWake,
359
+ haptic_feedback: settings.hapticFeedback,
360
+ device_name_display_enabled: settings.deviceNameDisplayEnabled,
361
+ fido_enabled: settings.fidoEnabled,
362
+ usb_lock_enable: settings.usbLockEnabled,
363
+ random_keypad: settings.randomKeypad,
364
+ });
@@ -0,0 +1,92 @@
1
+ import type { DeviceFeaturesRaw, DeviceState, DeviceStateSession, Features } from '../types';
2
+
3
+ type StoredDeviceState = DeviceState & {
4
+ raw?: DeviceFeaturesRaw;
5
+ session?: DeviceStateSession;
6
+ };
7
+
8
+ const getBootloaderMode = (state: StoredDeviceState) =>
9
+ state.status.mode === 'bootloader' || state.status.mode === 'romloader';
10
+
11
+ export const projectFeatures = (state: StoredDeviceState): Features => {
12
+ const snapshot = structuredClone(state);
13
+ const rawFeatures = snapshot.raw?.protocolV1Features ?? {};
14
+ const rawOneKeyFeatures = snapshot.raw?.protocolV1OneKeyFeatures ?? {};
15
+ const sessionId = snapshot.session?.sessionId ?? null;
16
+ const bootloaderMode =
17
+ snapshot.protocol === 'V1'
18
+ ? (rawFeatures as { bootloader_mode?: boolean | null }).bootloader_mode ?? null
19
+ : getBootloaderMode(snapshot);
20
+
21
+ return {
22
+ ...rawFeatures,
23
+ ...rawOneKeyFeatures,
24
+ protocol: snapshot.protocol,
25
+ protocolVersion: snapshot.protocolVersion ?? (snapshot.protocol === 'V1' ? 1 : null),
26
+ deviceType: snapshot.identity.deviceType,
27
+ firmwareType: snapshot.identity.firmwareType,
28
+ model: snapshot.identity.model,
29
+ vendor: snapshot.identity.vendor,
30
+ deviceId: snapshot.identity.deviceId,
31
+ serialNo: snapshot.identity.serialNo,
32
+ label: snapshot.identity.label,
33
+ bleName: snapshot.identity.bleName,
34
+ capabilities: snapshot.capabilities,
35
+ mode: snapshot.status.mode,
36
+ initialized: snapshot.status.initialized,
37
+ bootloaderMode,
38
+ unlocked: snapshot.status.unlocked,
39
+ firmwarePresent: snapshot.status.firmwarePresent,
40
+ passphraseProtection: snapshot.status.passphraseProtection,
41
+ pinProtection: snapshot.status.pinProtection,
42
+ backupRequired: snapshot.status.backupRequired,
43
+ noBackup: snapshot.status.noBackup,
44
+ unfinishedBackup: snapshot.status.unfinishedBackup,
45
+ recoveryMode: snapshot.status.recoveryMode,
46
+ attachToPinEnabled: snapshot.status.attachToPinEnabled,
47
+ unlockedAttachPin: snapshot.status.unlockedAttachPin ?? undefined,
48
+ language: snapshot.settings.language,
49
+ bleEnabled: snapshot.settings.bleEnabled,
50
+ sdCardPresent: snapshot.settings.sdCardPresent,
51
+ sdProtection: snapshot.settings.sdProtection,
52
+ wipeCodeProtection: snapshot.settings.wipeCodeProtection,
53
+ passphraseAlwaysOnDevice: snapshot.settings.passphraseAlwaysOnDevice,
54
+ safetyChecks: snapshot.settings.safetyChecks,
55
+ autoLockDelayMs: snapshot.settings.autoLockDelayMs,
56
+ autoShutdownDelayMs: snapshot.settings.autoShutdownDelayMs,
57
+ displayRotation: snapshot.settings.displayRotation,
58
+ experimentalFeatures: snapshot.settings.experimentalFeatures,
59
+ wallpaperPath: snapshot.settings.wallpaperPath,
60
+ brightness: snapshot.settings.brightness,
61
+ animationEnabled: snapshot.settings.animationEnabled,
62
+ tapToWake: snapshot.settings.tapToWake,
63
+ hapticFeedback: snapshot.settings.hapticFeedback,
64
+ deviceNameDisplayEnabled: snapshot.settings.deviceNameDisplayEnabled,
65
+ airgapMode: snapshot.settings.airgapMode,
66
+ fidoEnabled: snapshot.settings.fidoEnabled,
67
+ usbLockEnabled: snapshot.settings.usbLockEnabled,
68
+ randomKeypad: snapshot.settings.randomKeypad,
69
+ firmwareVersion: snapshot.versions.firmware,
70
+ bootloaderVersion: snapshot.versions.bootloader,
71
+ boardVersion: snapshot.versions.board,
72
+ bleVersion: snapshot.versions.ble,
73
+ se01Version: snapshot.versions.se01,
74
+ se02Version: snapshot.versions.se02,
75
+ se03Version: snapshot.versions.se03,
76
+ se04Version: snapshot.versions.se04,
77
+ se01BootVersion: snapshot.versions.se01Boot,
78
+ se02BootVersion: snapshot.versions.se02Boot,
79
+ se03BootVersion: snapshot.versions.se03Boot,
80
+ se04BootVersion: snapshot.versions.se04Boot,
81
+ seVersion: snapshot.versions.se ?? null,
82
+ verify: snapshot.verification,
83
+ sessionId,
84
+ passphraseState: snapshot.session?.passphraseState,
85
+ raw: snapshot.raw,
86
+ device_id: snapshot.identity.deviceId ?? undefined,
87
+ session_id: sessionId ?? undefined,
88
+ ble_name: snapshot.identity.bleName ?? undefined,
89
+ passphrase_protection: snapshot.status.passphraseProtection ?? undefined,
90
+ bootloader_mode: bootloaderMode,
91
+ } as unknown as Features;
92
+ };
@@ -0,0 +1,242 @@
1
+ import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
+
3
+ import type {
4
+ DeviceFeaturesRaw,
5
+ DeviceState,
6
+ DeviceStateIdentity,
7
+ DeviceStatePatch,
8
+ DeviceStateSession,
9
+ DeviceStateUpdateSource,
10
+ } from '../types';
11
+
12
+ type StoredDeviceState = DeviceState & {
13
+ raw?: DeviceFeaturesRaw;
14
+ session?: DeviceStateSession;
15
+ };
16
+
17
+ export type DeviceStateUpdateResult = {
18
+ state: StoredDeviceState;
19
+ revision: number;
20
+ source: DeviceStateUpdateSource;
21
+ changedKeys: string[];
22
+ };
23
+
24
+ const PRODUCT_DISPLAY_NAMES: Partial<Record<DeviceStateIdentity['deviceType'], string>> = {
25
+ [EDeviceType.Classic]: 'OneKey Classic',
26
+ [EDeviceType.Classic1s]: 'OneKey Classic 1S',
27
+ [EDeviceType.ClassicPure]: 'OneKey Classic 1S Pure',
28
+ [EDeviceType.Mini]: 'OneKey Mini',
29
+ [EDeviceType.Touch]: 'OneKey Touch',
30
+ [EDeviceType.Pro]: 'OneKey Pro',
31
+ [EDeviceType.Pro2]: 'OneKey Pro 2',
32
+ };
33
+
34
+ const getModelDisplayName = (deviceType: DeviceStateIdentity['deviceType']) =>
35
+ PRODUCT_DISPLAY_NAMES[deviceType] ?? 'OneKey';
36
+
37
+ const getDisplayName = (identity: DeviceStateIdentity) =>
38
+ identity.label || identity.bleName || getModelDisplayName(identity.deviceType);
39
+
40
+ export function createEmptyDeviceState(
41
+ identity: Partial<DeviceStateIdentity> = {}
42
+ ): StoredDeviceState {
43
+ const normalizedIdentity: DeviceStateIdentity = {
44
+ deviceType: EDeviceType.Unknown,
45
+ firmwareType: EFirmwareType.Universal,
46
+ model: null,
47
+ vendor: null,
48
+ deviceId: null,
49
+ serialNo: '',
50
+ label: null,
51
+ bleName: null,
52
+ displayName: '',
53
+ ...identity,
54
+ };
55
+ normalizedIdentity.displayName = getDisplayName(normalizedIdentity);
56
+
57
+ return {
58
+ schemaVersion: 1,
59
+ revision: 0,
60
+ updatedAt: Date.now(),
61
+ protocol: 'unknown',
62
+ protocolVersion: null,
63
+ identity: normalizedIdentity,
64
+ status: {
65
+ mode: 'unknown',
66
+ initialized: null,
67
+ unlocked: null,
68
+ firmwarePresent: null,
69
+ backupRequired: null,
70
+ noBackup: null,
71
+ unfinishedBackup: null,
72
+ recoveryMode: null,
73
+ passphraseProtection: null,
74
+ pinProtection: null,
75
+ attachToPinEnabled: null,
76
+ unlockedAttachPin: null,
77
+ },
78
+ settings: {
79
+ language: null,
80
+ bleEnabled: null,
81
+ sdCardPresent: null,
82
+ sdProtection: null,
83
+ wipeCodeProtection: null,
84
+ passphraseAlwaysOnDevice: null,
85
+ safetyChecks: null,
86
+ autoLockDelayMs: null,
87
+ autoShutdownDelayMs: null,
88
+ displayRotation: null,
89
+ experimentalFeatures: null,
90
+ wallpaperPath: null,
91
+ brightness: null,
92
+ animationEnabled: null,
93
+ tapToWake: null,
94
+ hapticFeedback: null,
95
+ deviceNameDisplayEnabled: null,
96
+ airgapMode: null,
97
+ fidoEnabled: null,
98
+ usbLockEnabled: null,
99
+ randomKeypad: null,
100
+ },
101
+ versions: {
102
+ firmware: null,
103
+ bootloader: null,
104
+ board: null,
105
+ ble: null,
106
+ },
107
+ capabilities: [],
108
+ };
109
+ }
110
+
111
+ const isEqual = (left: unknown, right: unknown) => JSON.stringify(left) === JSON.stringify(right);
112
+
113
+ const applySectionPatch = <T extends object>(
114
+ target: T,
115
+ patch: Partial<T>,
116
+ prefix: string,
117
+ changedKeys: string[]
118
+ ) => {
119
+ for (const [key, value] of Object.entries(patch) as [keyof T, T[keyof T] | undefined][]) {
120
+ if (value !== undefined && !isEqual(target[key], value)) {
121
+ target[key] = value;
122
+ changedKeys.push(`${prefix}.${String(key)}`);
123
+ }
124
+ }
125
+ };
126
+
127
+ export class DeviceStateStore {
128
+ private state: StoredDeviceState | undefined;
129
+
130
+ constructor(initial?: StoredDeviceState) {
131
+ this.state = initial ? structuredClone(initial) : undefined;
132
+ }
133
+
134
+ getState() {
135
+ return this.state;
136
+ }
137
+
138
+ replace(state: StoredDeviceState, source: DeviceStateUpdateSource): DeviceStateUpdateResult {
139
+ const revision = (this.state?.revision ?? 0) + 1;
140
+ this.state = {
141
+ ...structuredClone(state),
142
+ revision,
143
+ updatedAt: Date.now(),
144
+ };
145
+ return {
146
+ state: this.state,
147
+ revision,
148
+ source,
149
+ changedKeys: ['*'],
150
+ };
151
+ }
152
+
153
+ update(patch: DeviceStatePatch, source: DeviceStateUpdateSource): DeviceStateUpdateResult {
154
+ const next = structuredClone(this.state ?? createEmptyDeviceState());
155
+ const changedKeys: string[] = [];
156
+
157
+ if (patch.protocol !== undefined && patch.protocol !== next.protocol) {
158
+ next.protocol = patch.protocol;
159
+ changedKeys.push('protocol');
160
+ }
161
+ if (patch.protocolVersion !== undefined && patch.protocolVersion !== next.protocolVersion) {
162
+ next.protocolVersion = patch.protocolVersion;
163
+ changedKeys.push('protocolVersion');
164
+ }
165
+ if (patch.identity) applySectionPatch(next.identity, patch.identity, 'identity', changedKeys);
166
+ if (patch.status) applySectionPatch(next.status, patch.status, 'status', changedKeys);
167
+ if (patch.settings) applySectionPatch(next.settings, patch.settings, 'settings', changedKeys);
168
+ if (patch.versions) applySectionPatch(next.versions, patch.versions, 'versions', changedKeys);
169
+
170
+ if (patch.capabilities !== undefined && !isEqual(next.capabilities, patch.capabilities)) {
171
+ next.capabilities = structuredClone(patch.capabilities);
172
+ changedKeys.push('capabilities');
173
+ }
174
+ if (patch.verification !== undefined) {
175
+ const mergedVerification = {
176
+ ...structuredClone(next.verification ?? {}),
177
+ ...structuredClone(patch.verification),
178
+ };
179
+ if (!isEqual(next.verification, mergedVerification)) {
180
+ next.verification = mergedVerification;
181
+ changedKeys.push('verification');
182
+ }
183
+ }
184
+ if (patch.raw !== undefined) {
185
+ const mergedRaw = structuredClone(next.raw ?? {});
186
+ for (const [key, value] of Object.entries(patch.raw)) {
187
+ if (value === null) {
188
+ delete mergedRaw[key as keyof typeof mergedRaw];
189
+ } else if (value !== undefined) {
190
+ mergedRaw[key as keyof typeof mergedRaw] = structuredClone(value) as never;
191
+ }
192
+ }
193
+ if (!isEqual(next.raw, mergedRaw)) {
194
+ next.raw = mergedRaw;
195
+ changedKeys.push('raw');
196
+ }
197
+ }
198
+ if (patch.session === null) {
199
+ if (next.session !== undefined) {
200
+ delete next.session;
201
+ changedKeys.push('session');
202
+ }
203
+ } else if (patch.session) {
204
+ const session = next.session ?? { sessionId: null };
205
+ applySectionPatch(session, patch.session, 'session', changedKeys);
206
+ next.session = session;
207
+ }
208
+
209
+ const displayName = getDisplayName(next.identity);
210
+ if (displayName !== next.identity.displayName) {
211
+ next.identity.displayName = displayName;
212
+ changedKeys.push('identity.displayName');
213
+ }
214
+
215
+ if (changedKeys.length > 0) {
216
+ next.revision = (this.state?.revision ?? 0) + 1;
217
+ next.updatedAt = Date.now();
218
+ this.state = next;
219
+ } else if (!this.state) {
220
+ this.state = next;
221
+ }
222
+
223
+ return {
224
+ state: this.state,
225
+ revision: this.state.revision,
226
+ source,
227
+ changedKeys,
228
+ };
229
+ }
230
+
231
+ clearSession(source: DeviceStateUpdateSource) {
232
+ if (!this.state?.session) return undefined;
233
+ return this.update({ session: null }, source);
234
+ }
235
+ }
236
+
237
+ export const createPublicDeviceState = (state: StoredDeviceState): DeviceState => {
238
+ const snapshot = structuredClone(state);
239
+ delete snapshot.raw;
240
+ delete snapshot.session;
241
+ return snapshot;
242
+ };